refactor: move helper functions for setting up test stores into testing package (#22347)

* Move tenant.Service unit tests into its package
* Delete the top-level TenantService interface now that it's not used.
* Move helper funcs for setting up test stores into testing pkg
* Delete duplicate implementations scattered through the codebase
* Move error assertions into store-creation helpers
pull/22358/head
Daniel Moran 2021-08-31 16:43:45 -04:00 committed by GitHub
parent 99cfbfe8cf
commit 01355a068c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 1006 additions and 1631 deletions

View File

@ -21,26 +21,11 @@ import (
"github.com/influxdata/httprouter"
"github.com/influxdata/influxdb/v2"
icontext "github.com/influxdata/influxdb/v2/context"
"github.com/influxdata/influxdb/v2/inmem"
"github.com/influxdata/influxdb/v2/kv"
"github.com/influxdata/influxdb/v2/kv/migration/all"
"github.com/influxdata/influxdb/v2/mock"
itesting "github.com/influxdata/influxdb/v2/testing"
"go.uber.org/zap/zaptest"
)
func NewTestInmemStore(t *testing.T) (kv.Store, func(), error) {
t.Helper()
store := inmem.NewKVStore()
if err := all.Up(context.Background(), zaptest.NewLogger(t), store); err != nil {
t.Fatal(err)
}
return store, func() {}, nil
}
func TestService_handlePostAuthorization(t *testing.T) {
type fields struct {
AuthorizationService influxdb.AuthorizationService
@ -163,11 +148,7 @@ func TestService_handlePostAuthorization(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Helper()
s, _, err := NewTestInmemStore(t)
if err != nil {
t.Fatal(err)
}
s := itesting.NewTestInmemStore(t)
storage, err := NewStore(s)
if err != nil {
t.Fatal(err)
@ -738,11 +719,7 @@ func TestService_handleGetAuthorizations(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Helper()
s, _, err := NewTestInmemStore(t)
if err != nil {
t.Fatal(err)
}
s := itesting.NewTestInmemStore(t)
storage, err := NewStore(s)
if err != nil {
t.Fatal(err)

View File

@ -2,27 +2,17 @@ package authorization_test
import (
"context"
"errors"
"io/ioutil"
"os"
"testing"
"github.com/influxdata/influxdb/v2"
"github.com/influxdata/influxdb/v2/authorization"
"github.com/influxdata/influxdb/v2/bolt"
"github.com/influxdata/influxdb/v2/kv"
"github.com/influxdata/influxdb/v2/kv/migration/all"
"github.com/influxdata/influxdb/v2/tenant"
influxdbtesting "github.com/influxdata/influxdb/v2/testing"
"go.uber.org/zap/zaptest"
)
func initBoltAuthService(f influxdbtesting.AuthorizationFields, t *testing.T) (influxdb.AuthorizationService, string, func()) {
s, closeBolt, err := NewTestBoltStore(t)
if err != nil {
t.Fatalf("failed to create new kv store: %v", err)
}
s, closeBolt := influxdbtesting.NewTestBoltStore(t)
svc, closeSvc := initAuthService(s, f, t)
return svc, "service_auth", func() {
closeSvc()
@ -71,35 +61,6 @@ func initAuthService(s kv.Store, f influxdbtesting.AuthorizationFields, t *testi
}
}
func NewTestBoltStore(t *testing.T) (kv.Store, func(), error) {
f, err := ioutil.TempFile("", "influxdata-bolt-")
if err != nil {
return nil, nil, errors.New("unable to open temporary boltdb file")
}
f.Close()
path := f.Name()
ctx := context.Background()
logger := zaptest.NewLogger(t)
s := bolt.NewKVStore(logger, path, bolt.WithNoSync)
if err := s.Open(ctx); err != nil {
return nil, nil, err
}
if err := all.Up(ctx, logger, s); err != nil {
return nil, nil, err
}
close := func() {
s.Close()
os.Remove(path)
}
return s, close, nil
}
func TestBoltAuthService(t *testing.T) {
t.Parallel()
influxdbtesting.AuthorizationService(initBoltAuthService, t)

View File

@ -2,17 +2,13 @@ package dashboards
import (
"context"
"errors"
"io/ioutil"
"os"
"testing"
"github.com/influxdata/influxdb/v2"
"github.com/influxdata/influxdb/v2/bolt"
dashboardtesting "github.com/influxdata/influxdb/v2/dashboards/testing"
"github.com/influxdata/influxdb/v2/kv"
"github.com/influxdata/influxdb/v2/kv/migration/all"
"github.com/influxdata/influxdb/v2/mock"
itesting "github.com/influxdata/influxdb/v2/testing"
"go.uber.org/zap/zaptest"
)
@ -21,11 +17,7 @@ func TestBoltDashboardService(t *testing.T) {
}
func initBoltDashboardService(f dashboardtesting.DashboardFields, t *testing.T) (influxdb.DashboardService, string, func()) {
s, closeBolt, err := newTestBoltStore(t)
if err != nil {
t.Fatalf("failed to create new kv store: %v", err)
}
s, closeBolt := itesting.NewTestBoltStore(t)
svc, op, closeSvc := initDashboardService(s, f, t)
return svc, op, func() {
closeSvc()
@ -60,32 +52,3 @@ func initDashboardService(s kv.SchemaStore, f dashboardtesting.DashboardFields,
}
}
}
func newTestBoltStore(t *testing.T) (kv.SchemaStore, func(), error) {
f, err := ioutil.TempFile("", "influxdata-bolt-")
if err != nil {
return nil, nil, errors.New("unable to open temporary boltdb file")
}
f.Close()
ctx := context.Background()
logger := zaptest.NewLogger(t)
path := f.Name()
// skip fsync to improve test performance
s := bolt.NewKVStore(logger, path, bolt.WithNoSync)
if err := s.Open(context.Background()); err != nil {
return nil, nil, err
}
if err := all.Up(ctx, logger, s); err != nil {
return nil, nil, err
}
close := func() {
s.Close()
os.Remove(path)
}
return s, close, nil
}

View File

@ -12,21 +12,19 @@ import (
"testing"
"time"
"github.com/influxdata/influxdb/v2/kit/platform"
"github.com/influxdata/influxdb/v2/kit/platform/errors"
"github.com/go-chi/chi"
"github.com/google/go-cmp/cmp"
"github.com/influxdata/influxdb/v2"
"github.com/influxdata/influxdb/v2/dashboards"
dashboardstesting "github.com/influxdata/influxdb/v2/dashboards/testing"
ihttp "github.com/influxdata/influxdb/v2/http"
"github.com/influxdata/influxdb/v2/inmem"
"github.com/influxdata/influxdb/v2/kit/platform"
"github.com/influxdata/influxdb/v2/kit/platform/errors"
"github.com/influxdata/influxdb/v2/kv"
"github.com/influxdata/influxdb/v2/kv/migration/all"
"github.com/influxdata/influxdb/v2/label"
"github.com/influxdata/influxdb/v2/mock"
"github.com/influxdata/influxdb/v2/tenant"
itesting "github.com/influxdata/influxdb/v2/testing"
"github.com/yudai/gojsondiff"
"github.com/yudai/gojsondiff/formatter"
"go.uber.org/zap"
@ -1788,7 +1786,7 @@ func Test_dashboardCellIDPath(t *testing.T) {
func initDashboardService(f dashboardstesting.DashboardFields, t *testing.T) (influxdb.DashboardService, string, func()) {
t.Helper()
log := zaptest.NewLogger(t)
store := newTestInmemStore(t)
store := itesting.NewTestInmemStore(t)
kvsvc := kv.NewService(log, store, &mock.OrganizationService{})
kvsvc.IDGenerator = f.IDGenerator
@ -2000,15 +1998,3 @@ func withLabelService(svc influxdb.LabelService) option {
d.labelService = svc
}
}
func newTestInmemStore(t *testing.T) kv.Store {
t.Helper()
store := inmem.NewKVStore()
if err := all.Up(context.Background(), zaptest.NewLogger(t), store); err != nil {
t.Fatal(err)
}
return store
}

View File

@ -4,8 +4,6 @@ import (
"context"
"encoding/json"
"errors"
"github.com/influxdata/influxdb/v2/kit/platform"
errors2 "github.com/influxdata/influxdb/v2/kit/platform/errors"
"io"
"io/ioutil"
"net/http"
@ -16,6 +14,8 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/influxdata/influxdb/v2"
"github.com/influxdata/influxdb/v2/dbrp"
"github.com/influxdata/influxdb/v2/kit/platform"
errors2 "github.com/influxdata/influxdb/v2/kit/platform/errors"
"github.com/influxdata/influxdb/v2/mock"
influxdbtesting "github.com/influxdata/influxdb/v2/testing"
"github.com/stretchr/testify/assert"
@ -38,11 +38,7 @@ func initHttpService(t *testing.T) (influxdb.DBRPMappingService, *httptest.Serve
},
}
s, closeS, err := NewTestBoltStore(t)
if err != nil {
t.Fatal(err)
}
s, closeS := influxdbtesting.NewTestBoltStore(t)
svc := dbrp.NewService(ctx, bucketSvc, s)
server := httptest.NewServer(dbrp.NewHTTPHandler(zaptest.NewLogger(t), svc, orgSvc))

View File

@ -2,58 +2,18 @@ package dbrp_test
import (
"context"
"errors"
"fmt"
"io/ioutil"
"os"
"testing"
"github.com/influxdata/influxdb/v2"
"github.com/influxdata/influxdb/v2/bolt"
"github.com/influxdata/influxdb/v2/dbrp"
"github.com/influxdata/influxdb/v2/kit/platform"
"github.com/influxdata/influxdb/v2/kv"
"github.com/influxdata/influxdb/v2/kv/migration/all"
"github.com/influxdata/influxdb/v2/mock"
itesting "github.com/influxdata/influxdb/v2/testing"
"go.uber.org/zap/zaptest"
)
func NewTestBoltStore(t *testing.T) (kv.Store, func(), error) {
t.Helper()
f, err := ioutil.TempFile("", "influxdata-bolt-")
if err != nil {
return nil, nil, errors.New("unable to open temporary boltdb file")
}
f.Close()
ctx := context.Background()
logger := zaptest.NewLogger(t)
path := f.Name()
s := bolt.NewKVStore(logger, path, bolt.WithNoSync)
if err := s.Open(context.Background()); err != nil {
return nil, nil, err
}
if err := all.Up(ctx, logger, s); err != nil {
return nil, nil, err
}
close := func() {
s.Close()
os.Remove(path)
}
return s, close, nil
}
func initDBRPMappingService(f itesting.DBRPMappingFields, t *testing.T) (influxdb.DBRPMappingService, func()) {
s, closeStore, err := NewTestBoltStore(t)
if err != nil {
t.Fatalf("failed to create new bolt kv store: %v", err)
}
s, closeStore := itesting.NewTestBoltStore(t)
if f.BucketSvc == nil {
f.BucketSvc = &mock.BucketService{
FindBucketByIDFn: func(ctx context.Context, id platform.ID) (*influxdb.Bucket, error) {

View File

@ -873,7 +873,7 @@ func initAuthorizationService(f platformtesting.AuthorizationFields, t *testing.
t.Skip("HTTP authorization service does not required a user id on the authentication struct. We get the user from the session token.")
}
store := NewTestInmemStore(t)
store := platformtesting.NewTestInmemStore(t)
tenantStore := tenant.NewStore(store)
tenantStore.OrgIDGen = f.OrgIDGenerator
tenantService := tenant.NewService(tenantStore)

View File

@ -32,7 +32,7 @@ type fixture struct {
func setup(t *testing.T) (func(auth influxdb.Authorizer) *httptest.Server, func(serverUrl string) DocumentService, fixture) {
ctx := context.Background()
store := NewTestInmemStore(t)
store := itesting.NewTestInmemStore(t)
tenantStore := tenant.NewStore(store)
tenantService := tenant.NewService(tenantStore)
svc := kv.NewService(zaptest.NewLogger(t), store, tenantService)

View File

@ -597,8 +597,7 @@ func TestService_handlePatchLabel(t *testing.T) {
}
func initLabelService(f platformtesting.LabelFields, t *testing.T) (platform.LabelService, string, func()) {
store := NewTestInmemStore(t)
store := platformtesting.NewTestInmemStore(t)
labelStore, err := label.NewStore(store)
if err != nil {
t.Fatal(err)

View File

@ -1068,7 +1068,7 @@ func TestService_handlePostNotificationEndpointOwner(t *testing.T) {
func initNotificationEndpointService(f endpointTesting.NotificationEndpointFields, t *testing.T) (influxdb.NotificationEndpointService, influxdb.SecretService, func()) {
ctx := context.Background()
store := NewTestInmemStore(t)
store := influxTesting.NewTestInmemStore(t)
logger := zaptest.NewLogger(t)
tenantStore := tenant.NewStore(store)

View File

@ -15,9 +15,6 @@ import (
"testing"
"time"
"github.com/influxdata/influxdb/v2/kit/platform"
"github.com/influxdata/influxdb/v2/kit/platform/errors"
"github.com/google/go-cmp/cmp"
"github.com/influxdata/flux"
"github.com/influxdata/flux/csv"
@ -27,6 +24,8 @@ import (
"github.com/influxdata/influxdb/v2/http/metric"
"github.com/influxdata/influxdb/v2/kit/check"
"github.com/influxdata/influxdb/v2/kit/feature"
"github.com/influxdata/influxdb/v2/kit/platform"
"github.com/influxdata/influxdb/v2/kit/platform/errors"
tracetesting "github.com/influxdata/influxdb/v2/kit/tracing/testing"
kithttp "github.com/influxdata/influxdb/v2/kit/transport/http"
influxmock "github.com/influxdata/influxdb/v2/mock"
@ -34,6 +33,7 @@ import (
"github.com/influxdata/influxdb/v2/query/fluxlang"
"github.com/influxdata/influxdb/v2/query/mock"
"github.com/influxdata/influxdb/v2/tenant"
itesting "github.com/influxdata/influxdb/v2/testing"
"go.uber.org/zap/zaptest"
)
@ -330,7 +330,7 @@ var _ metric.EventRecorder = noopEventRecorder{}
func TestFluxHandler_PostQuery_Errors(t *testing.T) {
defer tracetesting.SetupInMemoryTracing(t.Name())()
store := NewTestInmemStore(t)
store := itesting.NewTestInmemStore(t)
orgSVC := tenant.NewService(tenant.NewStore(store))
b := &FluxBackend{
HTTPErrorHandler: kithttp.ErrorHandler(0),

View File

@ -820,7 +820,7 @@ func TestService_handlePatchScraperTarget(t *testing.T) {
func initScraperService(f platformtesting.TargetFields, t *testing.T) (influxdb.ScraperTargetStoreService, string, func()) {
t.Helper()
store := NewTestInmemStore(t)
store := platformtesting.NewTestInmemStore(t)
tenantStore := tenant.NewStore(store)
tenantService := tenant.NewService(tenantStore)

View File

@ -1,23 +0,0 @@
package http
import (
"context"
"testing"
"github.com/influxdata/influxdb/v2/inmem"
"github.com/influxdata/influxdb/v2/kv"
"github.com/influxdata/influxdb/v2/kv/migration/all"
"go.uber.org/zap/zaptest"
)
func NewTestInmemStore(t *testing.T) kv.Store {
t.Helper()
store := inmem.NewKVStore()
if err := all.Up(context.Background(), zaptest.NewLogger(t), store); err != nil {
t.Fatal(err)
}
return store
}

View File

@ -33,7 +33,7 @@ import (
// NewMockTaskBackend returns a TaskBackend with mock services.
func NewMockTaskBackend(t *testing.T) *TaskBackend {
t.Helper()
store := NewTestInmemStore(t)
store := influxdbtesting.NewTestInmemStore(t)
tenantService := tenant.NewService(tenant.NewStore(store))
return &TaskBackend{
@ -824,7 +824,7 @@ func TestTaskHandler_handleGetRuns(t *testing.T) {
func TestTaskHandler_NotFoundStatus(t *testing.T) {
// Ensure that the HTTP handlers return 404s for missing resources, and OKs for matching.
store := NewTestInmemStore(t)
store := influxdbtesting.NewTestInmemStore(t)
tenantService := tenant.NewService(tenant.NewStore(store))
labelStore, _ := label.NewStore(store)
@ -1219,7 +1219,7 @@ func TestService_handlePostTaskLabel(t *testing.T) {
// Test that org name to org ID translation happens properly in the HTTP layer.
// Regression test for https://github.com/influxdata/influxdb/issues/12089.
func TestTaskHandler_CreateTaskWithOrgName(t *testing.T) {
i := NewTestInmemStore(t)
i := influxdbtesting.NewTestInmemStore(t)
ts := tenant.NewService(tenant.NewStore(i))
aStore, _ := authorization.NewStore(i)
@ -1317,7 +1317,7 @@ func TestTaskHandler_CreateTaskWithOrgName(t *testing.T) {
func TestTaskHandler_Sessions(t *testing.T) {
t.Skip("rework these")
// Common setup to get a working base for using tasks.
st := NewTestInmemStore(t)
st := influxdbtesting.NewTestInmemStore(t)
tStore := tenant.NewStore(st)
tSvc := tenant.NewService(tStore)

View File

@ -33,7 +33,7 @@ func NewMockUserBackend(t *testing.T) *UserBackend {
func initUserService(f platformtesting.UserFields, t *testing.T) (platform.UserService, string, func()) {
t.Helper()
store := NewTestInmemStore(t)
store := platformtesting.NewTestInmemStore(t)
tenantStore := tenant.NewStore(store)
tenantStore.IDGen = f.IDGenerator
tenantService := tenant.NewService(tenantStore)

View File

@ -998,7 +998,7 @@ func TestService_handlePostVariableLabel(t *testing.T) {
}
func initVariableService(f itesting.VariableFields, t *testing.T) (platform.VariableService, string, func()) {
store := NewTestInmemStore(t)
store := itesting.NewTestInmemStore(t)
tenantService := tenant.NewService(tenant.NewStore(store))
svc := kv.NewService(zaptest.NewLogger(t), store, tenantService)

View File

@ -19,20 +19,12 @@ import (
)
func Test_Inmem_Index(t *testing.T) {
s, closeStore, err := NewTestInmemStore(t)
if err != nil {
t.Fatal(err)
}
defer closeStore()
s := influxdbtesting.NewTestInmemStore(t)
influxdbtesting.TestIndex(t, s)
}
func Test_Bolt_Index(t *testing.T) {
s, closeBolt, err := NewTestBoltStore(t)
if err != nil {
t.Fatalf("failed to create new kv store: %v", err)
}
s, closeBolt := influxdbtesting.NewTestBoltStore(t)
defer closeBolt()
influxdbtesting.TestIndex(t, s)

View File

@ -1,52 +0,0 @@
package kv_test
import (
"context"
"errors"
"io/ioutil"
"os"
"testing"
"github.com/influxdata/influxdb/v2/bolt"
"github.com/influxdata/influxdb/v2/inmem"
"github.com/influxdata/influxdb/v2/kv"
"github.com/influxdata/influxdb/v2/kv/migration/all"
"go.uber.org/zap/zaptest"
)
func NewTestBoltStore(t *testing.T) (kv.SchemaStore, func(), error) {
f, err := ioutil.TempFile("", "influxdata-bolt-")
if err != nil {
return nil, nil, errors.New("unable to open temporary boltdb file")
}
f.Close()
ctx := context.Background()
logger := zaptest.NewLogger(t)
path := f.Name()
// skip fsync to improve test performance
s := bolt.NewKVStore(logger, path, bolt.WithNoSync)
if err := s.Open(context.Background()); err != nil {
return nil, nil, err
}
if err := all.Up(ctx, logger, s); err != nil {
return nil, nil, err
}
close := func() {
s.Close()
os.Remove(path)
}
return s, close, nil
}
func NewTestInmemStore(t *testing.T) (kv.SchemaStore, func(), error) {
store := inmem.NewKVStore()
if err := all.Up(context.Background(), zaptest.NewLogger(t), store); err != nil {
return nil, nil, err
}
return store, func() {}, nil
}

View File

@ -16,11 +16,7 @@ func TestBoltKeyValueLog(t *testing.T) {
}
func initBoltKeyValueLog(f influxdbtesting.KeyValueLogFields, t *testing.T) (influxdb.KeyValueLog, func()) {
s, closeBolt, err := NewTestBoltStore(t)
if err != nil {
t.Fatalf("failed to create new kv store: %v", err)
}
s, closeBolt := influxdbtesting.NewTestBoltStore(t)
svc, closeSvc := initKeyValueLog(s, f, t)
return svc, func() {
closeSvc()

View File

@ -31,7 +31,7 @@ func Test_Inmem_Migrator(t *testing.T) {
}
func Test_Bolt_Migrator(t *testing.T) {
store, closeBolt, err := NewTestBoltStore(t)
store, closeBolt, err := newTestBoltStoreWithoutMigrations(t)
if err != nil {
t.Fatalf("failed to create new kv store: %v", err)
}
@ -40,7 +40,7 @@ func Test_Bolt_Migrator(t *testing.T) {
influxdbtesting.Migrator(t, store, newMigrator)
}
func NewTestBoltStore(t *testing.T) (kv.SchemaStore, func(), error) {
func newTestBoltStoreWithoutMigrations(t *testing.T) (kv.SchemaStore, func(), error) {
f, err := ioutil.TempFile("", "influxdata-bolt-")
if err != nil {
return nil, nil, errors.New("unable to open temporary boltdb file")

View File

@ -17,11 +17,7 @@ func TestBoltScraperTargetStoreService(t *testing.T) {
}
func initBoltTargetService(f influxdbtesting.TargetFields, t *testing.T) (influxdb.ScraperTargetStoreService, string, func()) {
s, closeFn, err := NewTestBoltStore(t)
if err != nil {
t.Fatalf("failed to create new kv store: %v", err)
}
s, closeFn := influxdbtesting.NewTestBoltStore(t)
svc, op, closeSvc := initScraperTargetStoreService(s, f, t)
return svc, op, func() {
closeSvc()

View File

@ -19,11 +19,7 @@ func TestBoltSourceService(t *testing.T) {
}
func initBoltSourceService(f influxdbtesting.SourceFields, t *testing.T) (influxdb.SourceService, string, func()) {
s, closeBolt, err := NewTestBoltStore(t)
if err != nil {
t.Fatalf("failed to create new kv store: %v", err)
}
s, closeBolt := influxdbtesting.NewTestBoltStore(t)
svc, op, closeSvc := initSourceService(s, f, t)
return svc, op, func() {
closeSvc()

View File

@ -4,13 +4,14 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/influxdata/influxdb/v2/kit/platform"
"github.com/influxdata/influxdb/v2/kit/platform/errors"
"testing"
"time"
"github.com/influxdata/influxdb/v2/kit/platform"
"github.com/influxdata/influxdb/v2/kit/platform/errors"
"github.com/influxdata/influxdb/v2/kv"
"github.com/influxdata/influxdb/v2/kv/migration"
itesting "github.com/influxdata/influxdb/v2/testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@ -19,8 +20,7 @@ func TestStoreBase(t *testing.T) {
newStoreBase := func(t *testing.T, bktSuffix string, encKeyFn, encBodyFn kv.EncodeEntFn, decFn kv.DecodeBucketValFn, decToEntFn kv.ConvertValToEntFn) (*kv.StoreBase, func(), kv.Store) {
t.Helper()
inmemSVC, done, err := NewTestBoltStore(t)
require.NoError(t, err)
svc, done := itesting.NewTestBoltStore(t)
bucket := []byte("foo_" + bktSuffix)
store := kv.NewStoreBase("foo", bucket, encKeyFn, encBodyFn, decFn, decToEntFn)
@ -29,10 +29,9 @@ func TestStoreBase(t *testing.T) {
defer cancel()
migrationName := fmt.Sprintf("create bucket %q", string(bucket))
migration.CreateBuckets(migrationName, bucket).Up(ctx, inmemSVC)
require.NoError(t, err)
migration.CreateBuckets(migrationName, bucket).Up(ctx, svc)
return store, done, inmemSVC
return store, done, svc
}
newFooStoreBase := func(t *testing.T, bktSuffix string) (*kv.StoreBase, func(), kv.Store) {

View File

@ -2,11 +2,12 @@ package kv_test
import (
"context"
"github.com/influxdata/influxdb/v2/kit/platform/errors"
"testing"
"github.com/influxdata/influxdb/v2/kit/platform/errors"
"github.com/influxdata/influxdb/v2/kv"
"github.com/influxdata/influxdb/v2/kv/migration"
itesting "github.com/influxdata/influxdb/v2/testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@ -19,8 +20,7 @@ func TestIndexStore(t *testing.T) {
newFooIndexStore := func(t *testing.T, bktSuffix string) (*kv.IndexStore, func(), kv.Store) {
t.Helper()
kvStoreStore, done, err := NewTestBoltStore(t)
require.NoError(t, err)
kvStoreStore, done := itesting.NewTestBoltStore(t)
const resource = "foo"

View File

@ -20,6 +20,7 @@ import (
"github.com/influxdata/influxdb/v2/task/servicetest"
"github.com/influxdata/influxdb/v2/task/taskmodel"
"github.com/influxdata/influxdb/v2/tenant"
itesting "github.com/influxdata/influxdb/v2/testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
@ -29,10 +30,7 @@ func TestBoltTaskService(t *testing.T) {
servicetest.TestTaskService(
t,
func(t *testing.T) (*servicetest.System, context.CancelFunc) {
store, close, err := NewTestBoltStore(t)
if err != nil {
t.Fatal(err)
}
store, close := itesting.NewTestBoltStore(t)
tenantStore := tenant.NewStore(store)
ts := tenant.NewService(tenantStore)
@ -72,12 +70,6 @@ type testService struct {
User influxdb.User
Auth influxdb.Authorization
Clock clock.Clock
storeCloseFn func()
}
func (s *testService) Close() {
s.storeCloseFn()
}
func newService(t *testing.T, ctx context.Context, c clock.Clock) *testService {
@ -93,7 +85,7 @@ func newService(t *testing.T, ctx context.Context, c clock.Clock) *testService {
store kv.SchemaStore
)
store, ts.storeCloseFn, err = NewTestInmemStore(t)
store = itesting.NewTestInmemStore(t)
if err != nil {
t.Fatal("failed to create InmemStore", err)
}
@ -147,7 +139,6 @@ func TestRetrieveTaskWithBadAuth(t *testing.T) {
defer cancelFunc()
ts := newService(t, ctx, nil)
defer ts.Close()
ctx = icontext.SetAuthorizer(ctx, &ts.Auth)
@ -224,7 +215,6 @@ func TestService_UpdateTask_InactiveToActive(t *testing.T) {
c.Set(time.Unix(1000, 0))
ts := newService(t, ctx, c)
defer ts.Close()
ctx = icontext.SetAuthorizer(ctx, &ts.Auth)
@ -267,11 +257,8 @@ func TestService_UpdateTask_InactiveToActive(t *testing.T) {
}
func TestTaskRunCancellation(t *testing.T) {
store, close, err := NewTestBoltStore(t)
if err != nil {
t.Fatal(err)
}
defer close()
store, closeSvc := itesting.NewTestBoltStore(t)
defer closeSvc()
ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc()
@ -352,7 +339,6 @@ func TestService_UpdateTask_RecordLatestSuccessAndFailure(t *testing.T) {
c.Set(time.Unix(1000, 0))
ts := newService(t, ctx, c)
defer ts.Close()
ctx = icontext.SetAuthorizer(ctx, &ts.Auth)

View File

@ -16,11 +16,7 @@ func TestBoltVariableService(t *testing.T) {
}
func initBoltVariableService(f influxdbtesting.VariableFields, t *testing.T) (influxdb.VariableService, string, func()) {
s, closeBolt, err := NewTestBoltStore(t)
if err != nil {
t.Fatalf("failed to create new kv store: %v", err)
}
s, closeBolt := influxdbtesting.NewTestBoltStore(t)
svc, op, closeSvc := initVariableService(s, f, t)
return svc, op, func() {
closeSvc()

View File

@ -2,60 +2,21 @@ package label_test
import (
"context"
"errors"
"io/ioutil"
"os"
"testing"
"github.com/influxdata/influxdb/v2"
"github.com/influxdata/influxdb/v2/bolt"
"github.com/influxdata/influxdb/v2/kv"
"github.com/influxdata/influxdb/v2/kv/migration/all"
"github.com/influxdata/influxdb/v2/label"
"github.com/influxdata/influxdb/v2/mock"
influxdbtesting "github.com/influxdata/influxdb/v2/testing"
"go.uber.org/zap/zaptest"
)
func TestBoltLabelService(t *testing.T) {
influxdbtesting.LabelService(initBoltLabelService, t)
}
func NewTestBoltStore(t *testing.T) (kv.Store, func(), error) {
t.Helper()
f, err := ioutil.TempFile("", "influxdata-bolt-")
if err != nil {
return nil, nil, errors.New("unable to open temporary boltdb file")
}
f.Close()
path := f.Name()
ctx := context.Background()
logger := zaptest.NewLogger(t)
s := bolt.NewKVStore(logger, path, bolt.WithNoSync)
if err := s.Open(ctx); err != nil {
return nil, nil, err
}
if err := all.Up(ctx, logger, s); err != nil {
return nil, nil, err
}
close := func() {
s.Close()
os.Remove(path)
}
return s, close, nil
}
func initBoltLabelService(f influxdbtesting.LabelFields, t *testing.T) (influxdb.LabelService, string, func()) {
s, closeBolt, err := NewTestBoltStore(t)
if err != nil {
t.Fatalf("failed to create new kv store: %v", err)
}
s, closeBolt := influxdbtesting.NewTestBoltStore(t)
svc, op, closeSvc := initLabelService(s, f, t)
return svc, op, func() {
closeSvc()

View File

@ -2,20 +2,17 @@ package service_test
import (
"context"
"io/ioutil"
"os"
"testing"
influxdb "github.com/influxdata/influxdb/v2"
"github.com/influxdata/influxdb/v2/bolt"
"github.com/influxdata/influxdb/v2"
"github.com/influxdata/influxdb/v2/inmem"
"github.com/influxdata/influxdb/v2/kit/errors"
"github.com/influxdata/influxdb/v2/kv"
"github.com/influxdata/influxdb/v2/kv/migration/all"
"github.com/influxdata/influxdb/v2/notification/endpoint/service"
endpointsTesting "github.com/influxdata/influxdb/v2/notification/endpoint/service/testing"
"github.com/influxdata/influxdb/v2/secret"
"github.com/influxdata/influxdb/v2/tenant"
itesting "github.com/influxdata/influxdb/v2/testing"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
)
@ -28,41 +25,8 @@ func TestNotificationEndpointService_WithBolt(t *testing.T) {
endpointsTesting.NotificationEndpointService(initBoltNotificationEndpointService, t)
}
func NewTestBoltStore(t *testing.T) (kv.SchemaStore, func(), error) {
f, err := ioutil.TempFile("", "influxdata-bolt-")
if err != nil {
return nil, nil, errors.New("unable to open temporary boltdb file")
}
f.Close()
ctx := context.Background()
logger := zaptest.NewLogger(t)
path := f.Name()
// skip fsync to improve test performance
s := bolt.NewKVStore(logger, path, bolt.WithNoSync)
if err := s.Open(context.Background()); err != nil {
return nil, nil, err
}
if err := all.Up(ctx, logger, s); err != nil {
return nil, nil, err
}
close := func() {
s.Close()
os.Remove(path)
}
return s, close, nil
}
func initBoltNotificationEndpointService(f endpointsTesting.NotificationEndpointFields, t *testing.T) (influxdb.NotificationEndpointService, influxdb.SecretService, func()) {
store, closeStore, err := NewTestBoltStore(t)
if err != nil {
t.Fatal(err)
}
store, closeStore := itesting.NewTestBoltStore(t)
svc, secretSVC, closeSvc := initNotificationEndpointService(store, f, t)
return svc, secretSVC, func() {
closeSvc()

View File

@ -2,24 +2,19 @@ package service
import (
"context"
"errors"
"io/ioutil"
"os"
"testing"
influxdb "github.com/influxdata/influxdb/v2"
"github.com/influxdata/influxdb/v2/bolt"
"github.com/influxdata/influxdb/v2"
_ "github.com/influxdata/influxdb/v2/fluxinit/static"
"github.com/influxdata/influxdb/v2/inmem"
"github.com/influxdata/influxdb/v2/kit/platform"
"github.com/influxdata/influxdb/v2/kv"
"github.com/influxdata/influxdb/v2/kv/migration/all"
"github.com/influxdata/influxdb/v2/mock"
endpointservice "github.com/influxdata/influxdb/v2/notification/endpoint/service"
"github.com/influxdata/influxdb/v2/query/fluxlang"
"github.com/influxdata/influxdb/v2/secret"
"github.com/influxdata/influxdb/v2/task/taskmodel"
"github.com/influxdata/influxdb/v2/tenant"
itesting "github.com/influxdata/influxdb/v2/testing"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
)
@ -29,23 +24,12 @@ func TestInmemNotificationRuleStore(t *testing.T) {
}
func initInmemNotificationRuleStore(f NotificationRuleFields, t *testing.T) (influxdb.NotificationRuleStore, taskmodel.TaskService, func()) {
store := inmem.NewKVStore()
if err := all.Up(context.Background(), zaptest.NewLogger(t), store); err != nil {
t.Fatal(err)
}
svc, tsvc, closeSvc := initNotificationRuleStore(store, f, t)
return svc, tsvc, func() {
closeSvc()
}
store := itesting.NewTestInmemStore(t)
return initNotificationRuleStore(store, f, t)
}
func initBoltNotificationRuleStore(f NotificationRuleFields, t *testing.T) (influxdb.NotificationRuleStore, taskmodel.TaskService, func()) {
store, closeBolt, err := newTestBoltStore(t)
if err != nil {
t.Fatal(err)
}
store, closeBolt := itesting.NewTestBoltStore(t)
svc, tsvc, closeSvc := initNotificationRuleStore(store, f, t)
return svc, tsvc, func() {
closeSvc()
@ -146,32 +130,3 @@ func withOrgID(store *tenant.Store, orgID platform.ID, fn func()) {
fn()
}
func newTestBoltStore(t *testing.T) (kv.SchemaStore, func(), error) {
f, err := ioutil.TempFile("", "influxdata-bolt-")
if err != nil {
return nil, nil, errors.New("unable to open temporary boltdb file")
}
f.Close()
ctx := context.Background()
logger := zaptest.NewLogger(t)
path := f.Name()
// skip fsync to improve test performance
s := bolt.NewKVStore(logger, path, bolt.WithNoSync)
if err := s.Open(context.Background()); err != nil {
return nil, nil, err
}
if err := all.Up(ctx, logger, s); err != nil {
return nil, nil, err
}
close := func() {
s.Close()
os.Remove(path)
}
return s, close, nil
}

View File

@ -2,59 +2,21 @@ package service_test
import (
"context"
"errors"
"io/ioutil"
"os"
"testing"
influxdb "github.com/influxdata/influxdb/v2"
"github.com/influxdata/influxdb/v2/bolt"
"github.com/influxdata/influxdb/v2"
"github.com/influxdata/influxdb/v2/kv"
"github.com/influxdata/influxdb/v2/kv/migration/all"
telegrafservice "github.com/influxdata/influxdb/v2/telegraf/service"
telegraftesting "github.com/influxdata/influxdb/v2/telegraf/service/testing"
"go.uber.org/zap/zaptest"
itesting "github.com/influxdata/influxdb/v2/testing"
)
func TestBoltTelegrafService(t *testing.T) {
telegraftesting.TelegrafConfigStore(initBoltTelegrafService, t)
}
func NewTestBoltStore(t *testing.T) (kv.SchemaStore, func(), error) {
f, err := ioutil.TempFile("", "influxdata-bolt-")
if err != nil {
return nil, nil, errors.New("unable to open temporary boltdb file")
}
f.Close()
ctx := context.Background()
logger := zaptest.NewLogger(t)
path := f.Name()
// skip fsync to improve test performance
s := bolt.NewKVStore(logger, path, bolt.WithNoSync)
if err := s.Open(context.Background()); err != nil {
return nil, nil, err
}
if err := all.Up(ctx, logger, s); err != nil {
return nil, nil, err
}
close := func() {
s.Close()
os.Remove(path)
}
return s, close, nil
}
func initBoltTelegrafService(f telegraftesting.TelegrafConfigFields, t *testing.T) (influxdb.TelegrafConfigStore, func()) {
s, closeBolt, err := NewTestBoltStore(t)
if err != nil {
t.Fatalf("failed to create new kv store: %v", err)
}
s, closeBolt := itesting.NewTestBoltStore(t)
svc, closeSvc := initTelegrafService(s, f, t)
return svc, func() {
closeSvc()

View File

@ -1,10 +0,0 @@
package influxdb
// TenantService is a service that exposes the functionality of the embedded services.
type TenantService interface {
UserService
PasswordsService
UserResourceMappingService
OrganizationService
BucketService
}

View File

@ -2,8 +2,6 @@ package tenant_test
import (
"context"
"github.com/influxdata/influxdb/v2/kit/platform"
"github.com/influxdata/influxdb/v2/kit/platform/errors"
"net/http/httptest"
"testing"
"time"
@ -13,6 +11,8 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/influxdata/influxdb/v2"
ihttp "github.com/influxdata/influxdb/v2/http"
"github.com/influxdata/influxdb/v2/kit/platform"
"github.com/influxdata/influxdb/v2/kit/platform/errors"
"github.com/influxdata/influxdb/v2/kv"
"github.com/influxdata/influxdb/v2/mock"
"github.com/influxdata/influxdb/v2/tenant"
@ -23,10 +23,7 @@ import (
func initBucketHttpService(f itesting.BucketFields, t *testing.T) (influxdb.BucketService, string, func()) {
t.Helper()
s, stCloser, err := NewTestInmemStore(t)
if err != nil {
t.Fatal(err)
}
s := itesting.NewTestInmemStore(t)
store := tenant.NewStore(s)
if f.IDGenerator != nil {
@ -75,10 +72,7 @@ func initBucketHttpService(f itesting.BucketFields, t *testing.T) (influxdb.Buck
Client: httpClient,
}
return &client, "http_tenant", func() {
server.Close()
stCloser()
}
return &client, "http_tenant", server.Close
}
func TestHTTPBucketService(t *testing.T) {

View File

@ -18,11 +18,7 @@ import (
func initOnboardHttpService(f itesting.OnboardingFields, t *testing.T) (influxdb.OnboardingService, func()) {
t.Helper()
s, stCloser, err := NewTestInmemStore(t)
if err != nil {
t.Fatal(err)
}
s := itesting.NewTestInmemStore(t)
storage := tenant.NewStore(s)
ten := tenant.NewService(storage)
@ -55,10 +51,7 @@ func initOnboardHttpService(f itesting.OnboardingFields, t *testing.T) (influxdb
Client: httpClient,
}
return &client, func() {
server.Close()
stCloser()
}
return &client, server.Close
}
func TestOnboardService(t *testing.T) {

View File

@ -17,11 +17,7 @@ import (
func initHttpOrgService(f itesting.OrganizationFields, t *testing.T) (influxdb.OrganizationService, string, func()) {
t.Helper()
s, stCloser, err := NewTestInmemStore(t)
if err != nil {
t.Fatal(err)
}
s := itesting.NewTestInmemStore(t)
storage := tenant.NewStore(s)
if f.OrgBucketIDs != nil {
@ -55,10 +51,7 @@ func initHttpOrgService(f itesting.OrganizationFields, t *testing.T) (influxdb.O
Client: httpClient,
}
return &orgClient, "http_tenant", func() {
server.Close()
stCloser()
}
return &orgClient, "http_tenant", server.Close
}
func TestHTTPOrgService(t *testing.T) {

View File

@ -16,10 +16,7 @@ import (
func initHttpUserService(f platformtesting.UserFields, t *testing.T) (platform.UserService, string, func()) {
t.Helper()
s, stCloser, err := NewTestInmemStore(t)
if err != nil {
t.Fatal(err)
}
s := platformtesting.NewTestInmemStore(t)
storage := tenant.NewStore(s)
svc := tenant.NewService(storage)
@ -46,10 +43,7 @@ func initHttpUserService(f platformtesting.UserFields, t *testing.T) (platform.U
Client: httpClient,
}
return &client, "http_tenant", func() {
server.Close()
stCloser()
}
return &client, "http_tenant", server.Close
}
func TestUserService(t *testing.T) {

View File

@ -15,16 +15,8 @@ func TestInmemBucketService(t *testing.T) {
}
func initInmemBucketService(f influxdbtesting.BucketFields, t *testing.T) (influxdb.BucketService, string, func()) {
s, closeBolt, err := NewTestInmemStore(t)
if err != nil {
t.Fatalf("failed to create new kv store: %v", err)
}
svc, op, closeSvc := initBucketService(s, f, t)
return svc, op, func() {
closeSvc()
closeBolt()
}
s := influxdbtesting.NewTestInmemStore(t)
return initBucketService(s, f, t)
}
func initBucketService(s kv.SchemaStore, f influxdbtesting.BucketFields, t *testing.T) (influxdb.BucketService, string, func()) {
@ -82,11 +74,7 @@ func initBucketService(s kv.SchemaStore, f influxdbtesting.BucketFields, t *test
}
func TestBucketFind(t *testing.T) {
s, close, err := NewTestInmemStore(t)
if err != nil {
t.Fatal(err)
}
defer close()
s := influxdbtesting.NewTestInmemStore(t)
storage := tenant.NewStore(s)
svc := tenant.NewService(storage)
@ -98,7 +86,7 @@ func TestBucketFind(t *testing.T) {
t.Fatal(err)
}
name := "thebucket"
_, _, err = svc.FindBuckets(context.Background(), influxdb.BucketFilter{
_, _, err := svc.FindBuckets(context.Background(), influxdb.BucketFilter{
Name: &name,
Org: &o.Name,
})
@ -108,11 +96,7 @@ func TestBucketFind(t *testing.T) {
}
func TestSystemBucketsInNameFind(t *testing.T) {
s, close, err := NewTestInmemStore(t)
if err != nil {
t.Fatal(err)
}
defer close()
s := influxdbtesting.NewTestInmemStore(t)
storage := tenant.NewStore(s)
svc := tenant.NewService(storage)

View File

@ -23,15 +23,9 @@ func TestBoltOnboardingService(t *testing.T) {
}
func initBoltOnboardingService(f influxdbtesting.OnboardingFields, t *testing.T) (influxdb.OnboardingService, func()) {
s, closeStore, err := NewTestInmemStore(t)
if err != nil {
t.Fatalf("failed to create new bolt kv store: %v", err)
}
s := influxdbtesting.NewTestInmemStore(t)
svc := initOnboardingService(s, f, t)
return svc, func() {
closeStore()
}
return svc, func() {}
}
func initOnboardingService(s kv.Store, f influxdbtesting.OnboardingFields, t *testing.T) influxdb.OnboardingService {
@ -60,7 +54,7 @@ func initOnboardingService(s kv.Store, f influxdbtesting.OnboardingFields, t *te
}
func TestOnboardURM(t *testing.T) {
s, _, _ := NewTestInmemStore(t)
s := influxdbtesting.NewTestInmemStore(t)
storage := tenant.NewStore(s)
ten := tenant.NewService(storage)
@ -98,7 +92,7 @@ func TestOnboardURM(t *testing.T) {
}
func TestOnboardAuth(t *testing.T) {
s, _, _ := NewTestInmemStore(t)
s := influxdbtesting.NewTestInmemStore(t)
storage := tenant.NewStore(s)
ten := tenant.NewService(storage)
@ -172,7 +166,7 @@ func TestOnboardAuth(t *testing.T) {
}
func TestOnboardService_RetentionPolicy(t *testing.T) {
s, _, _ := NewTestInmemStore(t)
s := influxdbtesting.NewTestInmemStore(t)
storage := tenant.NewStore(s)
ten := tenant.NewService(storage)
@ -204,7 +198,7 @@ func TestOnboardService_RetentionPolicy(t *testing.T) {
}
func TestOnboardService_RetentionPolicyDeprecated(t *testing.T) {
s, _, _ := NewTestInmemStore(t)
s := influxdbtesting.NewTestInmemStore(t)
storage := tenant.NewStore(s)
ten := tenant.NewService(storage)
@ -236,7 +230,7 @@ func TestOnboardService_RetentionPolicyDeprecated(t *testing.T) {
}
func TestOnboardService_WeakPassword(t *testing.T) {
s, _, _ := NewTestInmemStore(t)
s := influxdbtesting.NewTestInmemStore(t)
storage := tenant.NewStore(s)
ten := tenant.NewService(storage)

View File

@ -15,11 +15,7 @@ func TestBoltOrganizationService(t *testing.T) {
}
func initBoltOrganizationService(f influxdbtesting.OrganizationFields, t *testing.T) (influxdb.OrganizationService, string, func()) {
s, closeBolt, err := NewTestBoltStore(t)
if err != nil {
t.Fatalf("failed to create new kv store: %v", err)
}
s, closeBolt := influxdbtesting.NewTestBoltStore(t)
svc, op, closeSvc := initOrganizationService(s, f, t)
return svc, op, func() {
closeSvc()

View File

@ -2,69 +2,880 @@ package tenant_test
import (
"context"
"errors"
"io/ioutil"
"os"
"sort"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/influxdata/influxdb/v2"
"github.com/influxdata/influxdb/v2/bolt"
"github.com/influxdata/influxdb/v2/inmem"
"github.com/influxdata/influxdb/v2/kv"
"github.com/influxdata/influxdb/v2/kv/migration/all"
"github.com/influxdata/influxdb/v2/kit/platform"
"github.com/influxdata/influxdb/v2/mock"
"github.com/influxdata/influxdb/v2/tenant"
influxdbtesting "github.com/influxdata/influxdb/v2/testing"
"go.uber.org/zap/zaptest"
itesting "github.com/influxdata/influxdb/v2/testing"
)
func NewTestBoltStore(t *testing.T) (kv.SchemaStore, func(), error) {
f, err := ioutil.TempFile("", "influxdata-bolt-")
if err != nil {
return nil, nil, errors.New("unable to open temporary boltdb file")
}
f.Close()
path := f.Name()
s := bolt.NewKVStore(zaptest.NewLogger(t), path, bolt.WithNoSync)
if err := s.Open(context.Background()); err != nil {
return nil, nil, err
}
// apply all kv migrations
ctx := context.Background()
if err := all.Up(ctx, zaptest.NewLogger(t), s); err != nil {
return nil, nil, err
}
close := func() {
s.Close()
os.Remove(path)
}
return s, close, nil
}
func NewTestInmemStore(t *testing.T) (kv.SchemaStore, func(), error) {
s := inmem.NewKVStore()
// apply all kv migrations
ctx := context.Background()
if err := all.Up(ctx, zaptest.NewLogger(t), s); err != nil {
return nil, nil, err
}
return s, func() {}, nil
type tenantFields struct {
OrgIDGenerator platform.IDGenerator
BucketIDGenerator platform.IDGenerator
Users []*influxdb.User
Passwords []string // passwords are indexed against the Users field
UserResourceMappings []*influxdb.UserResourceMapping
Organizations []*influxdb.Organization
Buckets []*influxdb.Bucket
}
// TestBoltTenantService tests the tenant service functions.
// These tests stress the relation between the services embedded by the TenantService.
// The individual functionality of services is tested elsewhere.
func TestBoltTenantService(t *testing.T) {
influxdbtesting.TenantService(t, initBoltTenantService)
tests := []struct {
name string
fn func(t *testing.T, init func(*testing.T, tenantFields) (*tenant.Service, func()))
}{
{
name: "Create",
fn: Create,
},
{
name: "Delete",
fn: Delete,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.fn(t, initBoltTenantService)
})
}
}
func initBoltTenantService(t *testing.T, f influxdbtesting.TenantFields) (influxdb.TenantService, func()) {
s, closeBolt, err := NewTestBoltStore(t)
if err != nil {
t.Fatalf("failed to create new kv store: %v", err)
type bucketsByName []*influxdb.Bucket
func (b bucketsByName) Len() int {
return len(b)
}
func (b bucketsByName) Less(i, j int) bool {
return strings.Compare(b[i].Name, b[j].Name) < 0
}
func (b bucketsByName) Swap(i, j int) {
b[i], b[j] = b[j], b[i]
}
type urmByResourceID []*influxdb.UserResourceMapping
func (u urmByResourceID) Len() int {
return len(u)
}
func (u urmByResourceID) Less(i, j int) bool {
return u[i].ResourceID < u[j].ResourceID
}
func (u urmByResourceID) Swap(i, j int) {
u[i], u[j] = u[j], u[i]
}
type urmByUserID []*influxdb.UserResourceMapping
func (u urmByUserID) Len() int {
return len(u)
}
func (u urmByUserID) Less(i, j int) bool {
return u[i].UserID < u[j].UserID
}
func (u urmByUserID) Swap(i, j int) {
u[i], u[j] = u[j], u[i]
}
// Create tests various cases of creation for the services in the TenantService.
// For example, when you create a user, do you create system buckets? How are URMs organized?
func Create(t *testing.T, init func(*testing.T, tenantFields) (*tenant.Service, func())) {
t.Helper()
// Blank fields, we are testing creation.
fields := func() tenantFields {
return tenantFields{
OrgIDGenerator: mock.NewIncrementingIDGenerator(1),
BucketIDGenerator: mock.NewIncrementingIDGenerator(1),
}
}
// NOTE(affo)(*kv.Service): tests that contain s.CreateOrganization() generate error in logs:
// Failed to make user owner of organization: {"error": "could not find authorizer on context when adding user to resource type orgs"}.
// This happens because kv requires an authorization to be in context.
// This is a bad dependency pattern (store -> auth) and should not be there.
// Anyways this does not prevent the org to be created. If you add the urm manually you'll obtain the same result.
// NOTE(affo)(*kv.Service): it also creates urms for the non existing user found in context.
t.Run("creating an org creates system buckets", func(t *testing.T) {
data := fields()
s, done := init(t, data)
defer done()
ctx := context.Background()
o := &influxdb.Organization{
// ID(1)
Name: "org1",
}
if err := s.CreateOrganization(ctx, o); err != nil {
t.Fatal(err)
}
// Check existence
orgs, norgs, err := s.FindOrganizations(ctx, influxdb.OrganizationFilter{})
if err != nil {
t.Fatal(err)
}
if norgs != 1 {
t.Errorf("expected 1 org, got: %v", orgs)
}
usrs, nusrs, err := s.FindUsers(ctx, influxdb.UserFilter{})
if err != nil {
t.Fatal(err)
}
if nusrs > 0 {
t.Errorf("expected no user, got: %v", usrs)
}
urms, nurms, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{})
if err != nil {
t.Fatal(err)
}
if nurms > 0 {
t.Errorf("expected no urm, got: %+v", urms)
}
bs, nbs, err := s.FindBuckets(ctx, influxdb.BucketFilter{})
if err != nil {
t.Fatal(err)
}
if nbs != 2 {
t.Errorf("expected 2 buckets, got: %v", bs)
}
sort.Sort(bucketsByName(bs))
if name := bs[0].Name; name != "_monitoring" {
t.Errorf("unexpected nam for bucket: %s", name)
}
if name := bs[1].Name; name != "_tasks" {
t.Errorf("unexpected nam for bucket: %s", name)
}
})
// NOTE(affo)(*kv.Service): nope, it does create system buckets with invalid OrgIDs.
t.Run("creating user creates only the user", func(t *testing.T) {
data := fields()
s, done := init(t, data)
defer done()
ctx := context.Background()
// Number of buckets prior to user creation.
// This is because, for now, system buckets always get returned for compatibility with the old system.
_, nbs, err := s.FindBuckets(ctx, influxdb.BucketFilter{})
if err != nil {
t.Fatal(err)
}
u := &influxdb.User{
ID: 1,
Name: "user1",
}
if err := s.CreateUser(ctx, u); err != nil {
t.Fatal(err)
}
// Check existence
orgs, norgs, err := s.FindOrganizations(ctx, influxdb.OrganizationFilter{})
if err != nil {
t.Fatal(err)
}
if norgs != 0 {
t.Errorf("expected no org, got: %v", orgs)
}
usrs, nusrs, err := s.FindUsers(ctx, influxdb.UserFilter{})
if err != nil {
t.Fatal(err)
}
if nusrs != 1 {
t.Errorf("expected 1 user, got: %v", usrs)
}
urms, nurms, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{})
if err != nil {
t.Fatal(err)
}
if nurms > 0 {
t.Errorf("expected no urm, got: %v", urms)
}
bs, nnbs, err := s.FindBuckets(ctx, influxdb.BucketFilter{})
if err != nil {
t.Fatal(err)
}
// Compare new number of buckets with the one prior to user creation.
if nnbs != nbs {
t.Errorf("expected no bucket created, got: %+v", bs)
}
})
// NOTE(affo)(*kv.Service): nope, it does create a useless URM, no existence check.
// Apparently, system buckets are created too :thinking.
t.Run("creating urm pointing to non existing user fails", func(t *testing.T) {
data := fields()
s, done := init(t, data)
defer done()
ctx := context.Background()
// First create an org and a user.
u := &influxdb.User{
ID: 1,
Name: "user1",
}
if err := s.CreateUser(ctx, u); err != nil {
t.Fatal(err)
}
o := &influxdb.Organization{
// ID(1)
Name: "org1",
}
if err := s.CreateOrganization(ctx, o); err != nil {
t.Fatal(err)
}
checkInvariance := func(nurms int) {
orgs, norgs, err := s.FindOrganizations(ctx, influxdb.OrganizationFilter{})
if err != nil {
t.Fatal(err)
}
if norgs != 1 {
t.Errorf("expected 1 org, got: %v", orgs)
}
usrs, nusrs, err := s.FindUsers(ctx, influxdb.UserFilter{})
if err != nil {
t.Fatal(err)
}
if nusrs != 1 {
t.Errorf("expected 1 user, got: %v", usrs)
}
urms, nnurms, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{})
if err != nil {
t.Fatal(err)
}
if nnurms != nurms {
t.Errorf("expected %d urms got %d: %+v", nurms, nnurms, urms)
}
bs, nbs, err := s.FindBuckets(ctx, influxdb.BucketFilter{})
if err != nil {
t.Fatal(err)
}
if nbs != 2 {
t.Errorf("expected 2 buckets, got: %v", bs)
}
}
checkInvariance(0)
// Wrong userID.
urm := &influxdb.UserResourceMapping{
UserID: 2,
UserType: influxdb.Owner,
MappingType: influxdb.UserMappingType,
ResourceType: influxdb.OrgsResourceType,
ResourceID: 1,
}
if err := s.CreateUserResourceMapping(ctx, urm); err == nil {
t.Errorf("expected error got none")
}
checkInvariance(0)
// Wrong orgID. The URM gets created successfully.
urm = &influxdb.UserResourceMapping{
UserID: 1,
UserType: influxdb.Owner,
MappingType: influxdb.UserMappingType,
ResourceType: influxdb.OrgsResourceType,
ResourceID: 2,
}
if err := s.CreateUserResourceMapping(ctx, urm); err != nil {
t.Errorf("unexpected error: %v", err)
}
checkInvariance(1)
})
// NOTE(affo)(*kv.Service): errors on bucket creation.
// But, apparently, system buckets are created too :thinking.
t.Run("should not be possible to create bucket without org", func(t *testing.T) {
data := fields()
s, done := init(t, data)
defer done()
ctx := context.Background()
// Number of buckets prior to bucket creation.
// This is because, for now, system buckets always get returned for compatibility with the old system.
_, nbs, err := s.FindBuckets(ctx, influxdb.BucketFilter{})
if err != nil {
t.Fatal(err)
}
b := &influxdb.Bucket{
// ID(1)
OrgID: 1,
Name: "bucket1",
}
if err := s.CreateBucket(ctx, b); err == nil {
t.Errorf("expected error got none")
}
// Check existence
orgs, norgs, err := s.FindOrganizations(ctx, influxdb.OrganizationFilter{})
if err != nil {
t.Fatal(err)
}
if norgs != 0 {
t.Errorf("expected no org, got: %v", orgs)
}
usrs, nusrs, err := s.FindUsers(ctx, influxdb.UserFilter{})
if err != nil {
t.Fatal(err)
}
if nusrs != 0 {
t.Errorf("expected no user, got: %v", usrs)
}
urms, nurms, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{})
if err != nil {
t.Fatal(err)
}
if nurms > 0 {
t.Errorf("expected no urm, got: %v", urms)
}
bs, nnbs, err := s.FindBuckets(ctx, influxdb.BucketFilter{})
if err != nil {
t.Fatal(err)
}
// Compare new number of buckets with the one prior to bucket creation.
if nnbs != nbs {
t.Errorf("expected bucket created, got: %+v", bs)
}
})
t.Run("making user part of org creates mapping to org only", func(t *testing.T) {
for _, userType := range []influxdb.UserType{influxdb.Owner, influxdb.Member} {
t.Run(string(userType), func(t *testing.T) {
data := fields()
s, done := init(t, data)
defer done()
ctx := context.Background()
u := &influxdb.User{
ID: 1,
Name: "user1",
}
if err := s.CreateUser(ctx, u); err != nil {
t.Fatal(err)
}
o := &influxdb.Organization{
// ID(1)
Name: "org1",
}
if err := s.CreateOrganization(ctx, o); err != nil {
t.Fatal(err)
}
urm := &influxdb.UserResourceMapping{
UserID: u.ID,
UserType: userType,
MappingType: influxdb.UserMappingType,
ResourceType: influxdb.OrgsResourceType,
ResourceID: o.ID,
}
if err := s.CreateUserResourceMapping(ctx, urm); err != nil {
t.Fatal(err)
}
// Check existence
orgs, norgs, err := s.FindOrganizations(ctx, influxdb.OrganizationFilter{})
if err != nil {
t.Fatal(err)
}
if norgs != 1 {
t.Errorf("expected 1 org, got: %v", orgs)
}
usrs, nusrs, err := s.FindUsers(ctx, influxdb.UserFilter{})
if err != nil {
t.Fatal(err)
}
if nusrs != 1 {
t.Errorf("expected 1 user, got: %v", usrs)
}
bs, nbs, err := s.FindBuckets(ctx, influxdb.BucketFilter{})
if err != nil {
t.Fatal(err)
}
if nbs != 2 {
t.Errorf("expected 2 buckets, got: %v", bs)
}
sort.Sort(bucketsByName(bs))
if name := bs[0].Name; name != "_monitoring" {
t.Errorf("unexpected name for bucket: %s", name)
}
if name := bs[1].Name; name != "_tasks" {
t.Errorf("unexpected name for bucket: %v", name)
}
urms, _, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{})
if err != nil {
t.Fatal(err)
}
want := []*influxdb.UserResourceMapping{
{
UserID: u.ID,
UserType: userType,
MappingType: influxdb.UserMappingType,
ResourceType: influxdb.OrgsResourceType,
ResourceID: o.ID,
},
}
sort.Sort(urmByResourceID(want))
sort.Sort(urmByResourceID(urms))
if diff := cmp.Diff(want, urms); diff != "" {
t.Errorf("unexpected urms -want/+got:\n\t%s", diff)
}
// Now add a new bucket and check the URMs.
b := &influxdb.Bucket{
// ID(1)
OrgID: o.ID,
Name: "bucket1",
}
if err := s.CreateBucket(ctx, b); err != nil {
t.Fatal(err)
}
urms, _, err = s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{})
if err != nil {
t.Fatal(err)
}
sort.Sort(urmByResourceID(urms))
if diff := cmp.Diff(want, urms); diff != "" {
t.Errorf("unexpected urms -want/+got:\n\t%s", diff)
}
})
}
})
}
// Delete tests various cases of deletion for the services in the TenantService.
// An example: if you delete a bucket the corresponding user resource mapping is not present.
func Delete(t *testing.T, init func(*testing.T, tenantFields) (*tenant.Service, func())) {
t.Helper()
fields := func() tenantFields {
return tenantFields{
OrgIDGenerator: mock.NewIncrementingIDGenerator(1),
// URM are userID + resourceID (they do not include resource type)
// so same IDs across different resources leads to collisions
// therefore, we need to start bucket IDs at higher offset for
// test.
BucketIDGenerator: mock.NewIncrementingIDGenerator(10),
Users: []*influxdb.User{
{
ID: 1,
Name: "user1",
},
{
ID: 2,
Name: "user2",
},
},
Passwords: []string{"password1", "password2"},
Organizations: []*influxdb.Organization{
{
// ID(1)
Name: "org1",
},
{
// ID(2)
Name: "org2",
},
},
// 2 organizations create 2 system buckets each
// so start at 14
Buckets: []*influxdb.Bucket{
{
// ID(14)
OrgID: 1,
Name: "bucket1",
},
{
// ID(15)
OrgID: 2,
Name: "bucket2",
},
},
UserResourceMappings: []*influxdb.UserResourceMapping{
// NOTE(affo): bucket URMs should not be here, create them only for deletion purposes.
// user 1 owns org1 (and so bucket1)
{
UserID: 1,
UserType: influxdb.Owner,
MappingType: influxdb.UserMappingType,
ResourceType: influxdb.OrgsResourceType,
ResourceID: 1,
},
{
UserID: 1,
UserType: influxdb.Owner,
MappingType: influxdb.UserMappingType,
ResourceType: influxdb.BucketsResourceType,
ResourceID: 14,
},
// user 1 is member of org2 (and so bucket2)
{
UserID: 1,
UserType: influxdb.Member,
MappingType: influxdb.UserMappingType,
ResourceType: influxdb.OrgsResourceType,
ResourceID: 2,
},
{
UserID: 1,
UserType: influxdb.Member,
MappingType: influxdb.UserMappingType,
ResourceType: influxdb.BucketsResourceType,
ResourceID: 15,
},
// user 2 owns org2 (and so bucket2)
{
UserID: 2,
UserType: influxdb.Owner,
MappingType: influxdb.UserMappingType,
ResourceType: influxdb.OrgsResourceType,
ResourceID: 2,
},
{
UserID: 2,
UserType: influxdb.Owner,
MappingType: influxdb.UserMappingType,
ResourceType: influxdb.BucketsResourceType,
ResourceID: 15,
},
},
}
}
t.Run("deleting bucket deletes urm", func(t *testing.T) {
data := fields()
s, done := init(t, data)
defer done()
ctx := context.Background()
f := influxdb.UserResourceMappingFilter{
ResourceID: data.Buckets[0].ID,
ResourceType: influxdb.BucketsResourceType,
}
urms, n, err := s.FindUserResourceMappings(ctx, f)
if err != nil {
t.Fatal(err)
}
if n != 1 {
t.Fatalf("expected 1 urm, got: %v", urms)
}
if err := s.DeleteBucket(ctx, data.Buckets[0].ID); err != nil {
t.Fatal(err)
}
f = influxdb.UserResourceMappingFilter{
ResourceID: data.Buckets[0].ID,
ResourceType: influxdb.BucketsResourceType,
}
urms, n, err = s.FindUserResourceMappings(ctx, f)
if err != nil {
t.Fatal(err)
}
if n > 0 {
t.Fatalf("expected no urm, got: %v", urms)
}
})
// NOTE(affo): those resources could not be dangling (URM could be inferred from an user being in the owner org).
// We do not want to automatically propagate this kind of delete because an resource will always have an owner org.
t.Run("deleting bucket urm does create dangling bucket", func(t *testing.T) {
data := fields()
s, done := init(t, data)
defer done()
ctx := context.Background()
// Pre-check the current situation.
// bucket1 is owned by user1.
// Check it.
urms, _, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{
ResourceType: influxdb.BucketsResourceType,
ResourceID: data.Buckets[0].ID,
})
if err != nil {
t.Fatal(err)
}
want := []*influxdb.UserResourceMapping{
{
UserID: data.Users[0].ID,
UserType: influxdb.Owner,
MappingType: influxdb.UserMappingType,
ResourceType: influxdb.BucketsResourceType,
ResourceID: data.Buckets[0].ID,
},
}
sort.Sort(urmByUserID(want))
sort.Sort(urmByUserID(urms))
if diff := cmp.Diff(want, urms); diff != "" {
t.Fatalf("unexpected urms -want/+got:\n\t%s", diff)
}
// bucket2 is owned by user2.
// bucket2 is readable by user2.
// Check it.
urms, _, err = s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{
ResourceType: influxdb.BucketsResourceType,
ResourceID: data.Buckets[1].ID,
})
if err != nil {
t.Fatal(err)
}
want = []*influxdb.UserResourceMapping{
{
UserID: data.Users[1].ID,
UserType: influxdb.Owner,
MappingType: influxdb.UserMappingType,
ResourceType: influxdb.BucketsResourceType,
ResourceID: data.Buckets[1].ID,
},
{
UserID: data.Users[0].ID,
UserType: influxdb.Member,
MappingType: influxdb.UserMappingType,
ResourceType: influxdb.BucketsResourceType,
ResourceID: data.Buckets[1].ID,
},
}
sort.Sort(urmByUserID(want))
sort.Sort(urmByUserID(urms))
if diff := cmp.Diff(want, urms); diff != "" {
t.Fatalf("unexpected urms -want/+got:\n\t%s", diff)
}
// Now delete user2 -> bucket2.
// Still expect bucket2 to exist (user1 still points to it).
if err := s.DeleteUserResourceMapping(ctx, data.Buckets[1].ID, data.Users[1].ID); err != nil {
t.Fatal(err)
}
bs, nbs, err := s.FindBuckets(ctx, influxdb.BucketFilter{ID: &data.Buckets[1].ID})
if err != nil {
t.Fatal(err)
}
if nbs != 1 {
t.Errorf("expected 1 buckets, got: %v", bs)
}
// Now delete user1 -> bucket2.
// Still expect bucket2 to exist (nobody points to it).
if err := s.DeleteUserResourceMapping(ctx, data.Buckets[1].ID, data.Users[0].ID); err != nil {
t.Fatal(err)
}
bs, nbs, err = s.FindBuckets(ctx, influxdb.BucketFilter{ID: &data.Buckets[1].ID})
if err != nil {
t.Fatal(err)
}
if nbs != 1 {
t.Errorf("expected 1 buckets, got: %v", bs)
}
urms, nurms, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{
ResourceType: influxdb.BucketsResourceType,
ResourceID: data.Buckets[1].ID,
})
if err != nil {
t.Fatal(err)
}
if nurms != 0 {
t.Errorf("expected bucket2, to be dangling, got: %+v", urms)
}
// Now delete user1 -> bucket1.
// Still expect bucket1 to exist (nobody points to it).
if err := s.DeleteUserResourceMapping(ctx, data.Buckets[0].ID, data.Users[0].ID); err != nil {
t.Fatal(err)
}
bs, nbs, err = s.FindBuckets(ctx, influxdb.BucketFilter{ID: &data.Buckets[0].ID})
if err != nil {
t.Fatal(err)
}
if nbs != 1 {
t.Errorf("expected 1 buckets, got: %v", bs)
}
urms, nurms, err = s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{
ResourceType: influxdb.BucketsResourceType,
ResourceID: data.Buckets[0].ID,
})
if err != nil {
t.Fatal(err)
}
if nurms != 0 {
t.Errorf("expected bucket1, to be dangling, got: %+v", urms)
}
})
t.Run("deleting a user deletes every related urm and nothing else", func(t *testing.T) {
data := fields()
s, done := init(t, data)
defer done()
ctx := context.Background()
// bucket1 is owned by user1.
// Check it.
urms, _, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{
ResourceType: influxdb.BucketsResourceType,
ResourceID: data.Buckets[0].ID,
})
if err != nil {
t.Fatal(err)
}
want := []*influxdb.UserResourceMapping{
{
UserID: data.Users[0].ID,
UserType: influxdb.Owner,
MappingType: influxdb.UserMappingType,
ResourceType: influxdb.BucketsResourceType,
ResourceID: data.Buckets[0].ID,
},
}
sort.Sort(urmByUserID(want))
sort.Sort(urmByUserID(urms))
if diff := cmp.Diff(want, urms); diff != "" {
t.Fatalf("unexpected urms -want/+got:\n\t%s", diff)
}
// Delete user1.
// We expect his urms deleted but not bucket1.
if err := s.DeleteUser(ctx, data.Users[0].ID); err != nil {
t.Fatal(err)
}
urms, nurms, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{
UserID: data.Users[0].ID,
})
if err != nil {
t.Fatal(err)
}
if nurms > 0 {
t.Errorf("expected that user deletion would remove dangling urms, got: %+v", urms)
}
bs, nbs, err := s.FindBuckets(ctx, influxdb.BucketFilter{ID: &data.Buckets[0].ID})
if err != nil {
t.Fatal(err)
}
if nbs != 1 {
t.Errorf("expected 1 buckets, got: %v", bs)
}
})
t.Run("deleting a bucket deletes every related urm", func(t *testing.T) {
data := fields()
s, done := init(t, data)
defer done()
ctx := context.Background()
// Delete bucket2.
// We expect its urms deleted.
if err := s.DeleteBucket(ctx, data.Buckets[1].ID); err != nil {
t.Fatal(err)
}
urms, nurms, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{
ResourceType: influxdb.BucketsResourceType,
ResourceID: data.Buckets[1].ID,
})
if err != nil {
t.Fatal(err)
}
if nurms > 0 {
t.Errorf("expected that bucket deletion would remove dangling urms, got: %+v", urms)
}
})
// NOTE(affo)(*kv.Service): buckets, users, and urms survive.
t.Run("deleting an organization should delete everything that depends on it", func(t *testing.T) {
data := fields()
s, done := init(t, data)
defer done()
ctx := context.Background()
// Delete org1.
// We expect its buckets to be deleted.
// We expect urms to those buckets to be deleted too.
// No user should be deleted.
preDeletionBuckets, _, err := s.FindBuckets(ctx, influxdb.BucketFilter{OrganizationID: &data.Organizations[0].ID})
if err != nil {
t.Fatal(err)
}
if err := s.DeleteOrganization(ctx, data.Organizations[0].ID); err != nil {
t.Fatal(err)
}
bs, nbs, err := s.FindBuckets(ctx, influxdb.BucketFilter{OrganizationID: &data.Organizations[0].ID})
if err != nil {
t.Fatal(err)
}
if nbs != 0 {
t.Errorf("expected org buckets to be deleted, got: %+v", bs)
}
urms, _, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{
UserID: data.Users[0].ID,
ResourceType: influxdb.BucketsResourceType,
})
if err != nil {
t.Fatal(err)
}
for _, urm := range urms {
for _, b := range preDeletionBuckets {
if urm.ResourceID == b.ID {
t.Errorf("expected this urm to be deleted, got %+v instead", urm)
}
}
}
if _, err := s.FindUser(ctx, influxdb.UserFilter{ID: &data.Users[0].ID}); err != nil {
t.Fatal(err)
}
// Delete org2.
// Everything should disappear.
if err := s.DeleteOrganization(ctx, data.Organizations[1].ID); err != nil {
t.Fatal(err)
}
orgs, norgs, err := s.FindOrganizations(ctx, influxdb.OrganizationFilter{})
if err != nil {
t.Fatal(err)
}
if norgs != 0 {
t.Errorf("expected no org, got: %v", orgs)
}
usrs, nusrs, err := s.FindUsers(ctx, influxdb.UserFilter{})
if err != nil {
t.Fatal(err)
}
if nusrs != 2 {
t.Errorf("expected 2 users, got: %v", usrs)
}
urms, nurms, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{})
if err != nil {
t.Fatal(err)
}
if nurms > 0 {
t.Errorf("expected no urm, got: %v", urms)
}
bs, nbs, err = s.FindBuckets(ctx, influxdb.BucketFilter{})
if err != nil {
t.Fatal(err)
}
if nbs != 0 {
t.Errorf("expected buckets to be deleted, got: %+v", bs)
}
})
}
func initBoltTenantService(t *testing.T, f tenantFields) (*tenant.Service, func()) {
s, closeBolt := itesting.NewTestBoltStore(t)
store := tenant.NewStore(s)
if f.OrgIDGenerator != nil {

View File

@ -2,10 +2,10 @@ package tenant_test
import (
"context"
"github.com/influxdata/influxdb/v2/kit/platform"
"testing"
"github.com/influxdata/influxdb/v2"
"github.com/influxdata/influxdb/v2/kit/platform"
"github.com/influxdata/influxdb/v2/kv"
"github.com/influxdata/influxdb/v2/mock"
"github.com/influxdata/influxdb/v2/tenant"
@ -17,11 +17,7 @@ func TestBoltUserResourceMappingService(t *testing.T) {
}
func initBoltUserResourceMappingService(f influxdbtesting.UserResourceFields, t *testing.T) (influxdb.UserResourceMappingService, func()) {
s, closeBolt, err := NewTestBoltStore(t)
if err != nil {
t.Fatalf("failed to create new kv store: %v", err)
}
s, closeBolt := influxdbtesting.NewTestBoltStore(t)
svc, closeSvc := initUserResourceMappingService(s, f, t)
return svc, func() {
closeSvc()

View File

@ -17,11 +17,7 @@ func TestBoltUserService(t *testing.T) {
}
func initBoltUserService(f influxdbtesting.UserFields, t *testing.T) (influxdb.UserService, string, func()) {
s, closeBolt, err := NewTestBoltStore(t)
if err != nil {
t.Fatalf("failed to create new kv store: %v", err)
}
s, closeBolt := influxdbtesting.NewTestBoltStore(t)
svc, op, closeSvc := initUserService(s, f, t)
return svc, op, func() {
closeSvc()
@ -53,11 +49,7 @@ func TestBoltPasswordService(t *testing.T) {
}
func initBoltPasswordsService(f influxdbtesting.PasswordFields, t *testing.T) (influxdb.PasswordsService, func()) {
s, closeStore, err := NewTestBoltStore(t)
if err != nil {
t.Fatalf("failed to create new bolt kv store: %v", err)
}
s, closeStore := influxdbtesting.NewTestBoltStore(t)
svc, closeSvc := initPasswordsService(s, f, t)
return svc, func() {
closeSvc()
@ -91,10 +83,7 @@ func initPasswordsService(s kv.Store, f influxdbtesting.PasswordFields, t *testi
}
func TestFindPermissionsFromUser(t *testing.T) {
s, _, err := NewTestInmemStore(t)
if err != nil {
t.Fatal(err)
}
s := influxdbtesting.NewTestInmemStore(t)
storage := tenant.NewStore(s)
svc := tenant.NewService(storage)
@ -111,7 +100,7 @@ func TestFindPermissionsFromUser(t *testing.T) {
ctx := context.Background()
// createSomeURMS
err = svc.CreateUserResourceMapping(ctx, &influxdb.UserResourceMapping{
err := svc.CreateUserResourceMapping(ctx, &influxdb.UserResourceMapping{
UserID: u.ID,
UserType: influxdb.Member,
ResourceType: influxdb.OrgsResourceType,

View File

@ -3,15 +3,16 @@ package tenant_test
import (
"context"
"fmt"
"github.com/influxdata/influxdb/v2/kit/platform"
"reflect"
"testing"
"time"
"github.com/influxdata/influxdb/v2"
"github.com/influxdata/influxdb/v2/kit/platform"
"github.com/influxdata/influxdb/v2/kv"
"github.com/influxdata/influxdb/v2/mock"
"github.com/influxdata/influxdb/v2/tenant"
itesting "github.com/influxdata/influxdb/v2/testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@ -277,12 +278,7 @@ func TestBucket(t *testing.T) {
}
for _, testScenario := range st {
t.Run(testScenario.name, func(t *testing.T) {
s, closeS, err := NewTestInmemStore(t)
if err != nil {
t.Fatal(err)
}
defer closeS()
s := itesting.NewTestInmemStore(t)
ts := tenant.NewStore(s, tenant.WithNow(func() time.Time {
return aTime
}))

View File

@ -3,14 +3,15 @@ package tenant_test
import (
"context"
"fmt"
"github.com/influxdata/influxdb/v2/kit/platform"
"testing"
"time"
"github.com/influxdata/influxdb/v2"
"github.com/influxdata/influxdb/v2/kit/platform"
"github.com/influxdata/influxdb/v2/kv"
"github.com/influxdata/influxdb/v2/mock"
"github.com/influxdata/influxdb/v2/tenant"
itesting "github.com/influxdata/influxdb/v2/testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@ -206,12 +207,7 @@ func TestOrg(t *testing.T) {
}
for _, testScenario := range st {
t.Run(testScenario.name, func(t *testing.T) {
s, closeS, err := NewTestInmemStore(t)
if err != nil {
t.Fatal(err)
}
defer closeS()
s := itesting.NewTestInmemStore(t)
ts := tenant.NewStore(s, tenant.WithNow(func() time.Time {
return aTime
}))

View File

@ -3,16 +3,17 @@ package tenant_test
import (
"context"
"fmt"
"github.com/influxdata/influxdb/v2/kit/platform"
"github.com/influxdata/influxdb/v2/kit/platform/errors"
"reflect"
"sort"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/influxdata/influxdb/v2"
"github.com/influxdata/influxdb/v2/kit/platform"
"github.com/influxdata/influxdb/v2/kit/platform/errors"
"github.com/influxdata/influxdb/v2/kv"
"github.com/influxdata/influxdb/v2/tenant"
itesting "github.com/influxdata/influxdb/v2/testing"
)
func TestURM(t *testing.T) {
@ -333,12 +334,7 @@ func TestURM(t *testing.T) {
}
for _, testScenario := range st {
t.Run(testScenario.name, func(t *testing.T) {
s, closeS, err := NewTestInmemStore(t)
if err != nil {
t.Fatal(err)
}
defer closeS()
s := itesting.NewTestInmemStore(t)
ts := tenant.NewStore(s)
// setup

View File

@ -3,13 +3,14 @@ package tenant_test
import (
"context"
"fmt"
"github.com/influxdata/influxdb/v2/kit/platform"
"reflect"
"testing"
"github.com/influxdata/influxdb/v2"
"github.com/influxdata/influxdb/v2/kit/platform"
"github.com/influxdata/influxdb/v2/kv"
"github.com/influxdata/influxdb/v2/tenant"
itesting "github.com/influxdata/influxdb/v2/testing"
)
func TestUser(t *testing.T) {
@ -242,12 +243,7 @@ func TestUser(t *testing.T) {
}
for _, testScenario := range st {
t.Run(testScenario.name, func(t *testing.T) {
s, closeS, err := NewTestInmemStore(t)
if err != nil {
t.Fatal(err)
}
defer closeS()
s := itesting.NewTestInmemStore(t)
ts := tenant.NewStore(s)
// setup

View File

@ -1,874 +0,0 @@
package testing
import (
"context"
"sort"
"strings"
"testing"
"github.com/influxdata/influxdb/v2/kit/platform"
"github.com/google/go-cmp/cmp"
"github.com/influxdata/influxdb/v2"
"github.com/influxdata/influxdb/v2/mock"
)
type TenantFields struct {
OrgIDGenerator platform.IDGenerator
BucketIDGenerator platform.IDGenerator
Users []*influxdb.User
Passwords []string // passwords are indexed against the Users field
UserResourceMappings []*influxdb.UserResourceMapping
Organizations []*influxdb.Organization
Buckets []*influxdb.Bucket
}
// TenantService tests the tenant service functions.
// These tests stress the relation between the services embedded by the TenantService.
// The individual functionality of services is tested elsewhere.
func TenantService(t *testing.T, init func(*testing.T, TenantFields) (influxdb.TenantService, func())) {
tests := []struct {
name string
fn func(t *testing.T, init func(*testing.T, TenantFields) (influxdb.TenantService, func()))
}{
{
name: "Create",
fn: Create,
},
{
name: "Delete",
fn: Delete,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.fn(t, init)
})
}
}
type bucketsByName []*influxdb.Bucket
func (b bucketsByName) Len() int {
return len(b)
}
func (b bucketsByName) Less(i, j int) bool {
return strings.Compare(b[i].Name, b[j].Name) < 0
}
func (b bucketsByName) Swap(i, j int) {
b[i], b[j] = b[j], b[i]
}
type urmByResourceID []*influxdb.UserResourceMapping
func (u urmByResourceID) Len() int {
return len(u)
}
func (u urmByResourceID) Less(i, j int) bool {
return u[i].ResourceID < u[j].ResourceID
}
func (u urmByResourceID) Swap(i, j int) {
u[i], u[j] = u[j], u[i]
}
type urmByUserID []*influxdb.UserResourceMapping
func (u urmByUserID) Len() int {
return len(u)
}
func (u urmByUserID) Less(i, j int) bool {
return u[i].UserID < u[j].UserID
}
func (u urmByUserID) Swap(i, j int) {
u[i], u[j] = u[j], u[i]
}
// Create tests various cases of creation for the services in the TenantService.
// For example, when you create a user, do you create system buckets? How are URMs organized?
func Create(t *testing.T, init func(*testing.T, TenantFields) (influxdb.TenantService, func())) {
t.Helper()
// Blank fields, we are testing creation.
fields := func() TenantFields {
return TenantFields{
OrgIDGenerator: mock.NewIncrementingIDGenerator(1),
BucketIDGenerator: mock.NewIncrementingIDGenerator(1),
}
}
// NOTE(affo)(*kv.Service): tests that contain s.CreateOrganization() generate error in logs:
// Failed to make user owner of organization: {"error": "could not find authorizer on context when adding user to resource type orgs"}.
// This happens because kv requires an authorization to be in context.
// This is a bad dependency pattern (store -> auth) and should not be there.
// Anyways this does not prevent the org to be created. If you add the urm manually you'll obtain the same result.
// NOTE(affo)(*kv.Service): it also creates urms for the non existing user found in context.
t.Run("creating an org creates system buckets", func(t *testing.T) {
data := fields()
s, done := init(t, data)
defer done()
ctx := context.Background()
o := &influxdb.Organization{
// ID(1)
Name: "org1",
}
if err := s.CreateOrganization(ctx, o); err != nil {
t.Fatal(err)
}
// Check existence
orgs, norgs, err := s.FindOrganizations(ctx, influxdb.OrganizationFilter{})
if err != nil {
t.Fatal(err)
}
if norgs != 1 {
t.Errorf("expected 1 org, got: %v", orgs)
}
usrs, nusrs, err := s.FindUsers(ctx, influxdb.UserFilter{})
if err != nil {
t.Fatal(err)
}
if nusrs > 0 {
t.Errorf("expected no user, got: %v", usrs)
}
urms, nurms, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{})
if err != nil {
t.Fatal(err)
}
if nurms > 0 {
t.Errorf("expected no urm, got: %+v", urms)
}
bs, nbs, err := s.FindBuckets(ctx, influxdb.BucketFilter{})
if err != nil {
t.Fatal(err)
}
if nbs != 2 {
t.Errorf("expected 2 buckets, got: %v", bs)
}
sort.Sort(bucketsByName(bs))
if name := bs[0].Name; name != "_monitoring" {
t.Errorf("unexpected nam for bucket: %s", name)
}
if name := bs[1].Name; name != "_tasks" {
t.Errorf("unexpected nam for bucket: %s", name)
}
})
// NOTE(affo)(*kv.Service): nope, it does create system buckets with invalid OrgIDs.
t.Run("creating user creates only the user", func(t *testing.T) {
data := fields()
s, done := init(t, data)
defer done()
ctx := context.Background()
// Number of buckets prior to user creation.
// This is because, for now, system buckets always get returned for compatibility with the old system.
_, nbs, err := s.FindBuckets(ctx, influxdb.BucketFilter{})
if err != nil {
t.Fatal(err)
}
u := &influxdb.User{
ID: 1,
Name: "user1",
}
if err := s.CreateUser(ctx, u); err != nil {
t.Fatal(err)
}
// Check existence
orgs, norgs, err := s.FindOrganizations(ctx, influxdb.OrganizationFilter{})
if err != nil {
t.Fatal(err)
}
if norgs != 0 {
t.Errorf("expected no org, got: %v", orgs)
}
usrs, nusrs, err := s.FindUsers(ctx, influxdb.UserFilter{})
if err != nil {
t.Fatal(err)
}
if nusrs != 1 {
t.Errorf("expected 1 user, got: %v", usrs)
}
urms, nurms, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{})
if err != nil {
t.Fatal(err)
}
if nurms > 0 {
t.Errorf("expected no urm, got: %v", urms)
}
bs, nnbs, err := s.FindBuckets(ctx, influxdb.BucketFilter{})
if err != nil {
t.Fatal(err)
}
// Compare new number of buckets with the one prior to user creation.
if nnbs != nbs {
t.Errorf("expected no bucket created, got: %+v", bs)
}
})
// NOTE(affo)(*kv.Service): nope, it does create a useless URM, no existence check.
// Apparently, system buckets are created too :thinking.
t.Run("creating urm pointing to non existing user fails", func(t *testing.T) {
data := fields()
s, done := init(t, data)
defer done()
ctx := context.Background()
// First create an org and a user.
u := &influxdb.User{
ID: 1,
Name: "user1",
}
if err := s.CreateUser(ctx, u); err != nil {
t.Fatal(err)
}
o := &influxdb.Organization{
// ID(1)
Name: "org1",
}
if err := s.CreateOrganization(ctx, o); err != nil {
t.Fatal(err)
}
checkInvariance := func(nurms int) {
orgs, norgs, err := s.FindOrganizations(ctx, influxdb.OrganizationFilter{})
if err != nil {
t.Fatal(err)
}
if norgs != 1 {
t.Errorf("expected 1 org, got: %v", orgs)
}
usrs, nusrs, err := s.FindUsers(ctx, influxdb.UserFilter{})
if err != nil {
t.Fatal(err)
}
if nusrs != 1 {
t.Errorf("expected 1 user, got: %v", usrs)
}
urms, nnurms, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{})
if err != nil {
t.Fatal(err)
}
if nnurms != nurms {
t.Errorf("expected %d urms got %d: %+v", nurms, nnurms, urms)
}
bs, nbs, err := s.FindBuckets(ctx, influxdb.BucketFilter{})
if err != nil {
t.Fatal(err)
}
if nbs != 2 {
t.Errorf("expected 2 buckets, got: %v", bs)
}
}
checkInvariance(0)
// Wrong userID.
urm := &influxdb.UserResourceMapping{
UserID: 2,
UserType: influxdb.Owner,
MappingType: influxdb.UserMappingType,
ResourceType: influxdb.OrgsResourceType,
ResourceID: 1,
}
if err := s.CreateUserResourceMapping(ctx, urm); err == nil {
t.Errorf("expected error got none")
}
checkInvariance(0)
// Wrong orgID. The URM gets created successfully.
urm = &influxdb.UserResourceMapping{
UserID: 1,
UserType: influxdb.Owner,
MappingType: influxdb.UserMappingType,
ResourceType: influxdb.OrgsResourceType,
ResourceID: 2,
}
if err := s.CreateUserResourceMapping(ctx, urm); err != nil {
t.Errorf("unexpected error: %v", err)
}
checkInvariance(1)
})
// NOTE(affo)(*kv.Service): errors on bucket creation.
// But, apparently, system buckets are created too :thinking.
t.Run("should not be possible to create bucket without org", func(t *testing.T) {
data := fields()
s, done := init(t, data)
defer done()
ctx := context.Background()
// Number of buckets prior to bucket creation.
// This is because, for now, system buckets always get returned for compatibility with the old system.
_, nbs, err := s.FindBuckets(ctx, influxdb.BucketFilter{})
if err != nil {
t.Fatal(err)
}
b := &influxdb.Bucket{
// ID(1)
OrgID: 1,
Name: "bucket1",
}
if err := s.CreateBucket(ctx, b); err == nil {
t.Errorf("expected error got none")
}
// Check existence
orgs, norgs, err := s.FindOrganizations(ctx, influxdb.OrganizationFilter{})
if err != nil {
t.Fatal(err)
}
if norgs != 0 {
t.Errorf("expected no org, got: %v", orgs)
}
usrs, nusrs, err := s.FindUsers(ctx, influxdb.UserFilter{})
if err != nil {
t.Fatal(err)
}
if nusrs != 0 {
t.Errorf("expected no user, got: %v", usrs)
}
urms, nurms, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{})
if err != nil {
t.Fatal(err)
}
if nurms > 0 {
t.Errorf("expected no urm, got: %v", urms)
}
bs, nnbs, err := s.FindBuckets(ctx, influxdb.BucketFilter{})
if err != nil {
t.Fatal(err)
}
// Compare new number of buckets with the one prior to bucket creation.
if nnbs != nbs {
t.Errorf("expected bucket created, got: %+v", bs)
}
})
t.Run("making user part of org creates mapping to org only", func(t *testing.T) {
for _, userType := range []influxdb.UserType{influxdb.Owner, influxdb.Member} {
t.Run(string(userType), func(t *testing.T) {
data := fields()
s, done := init(t, data)
defer done()
ctx := context.Background()
u := &influxdb.User{
ID: 1,
Name: "user1",
}
if err := s.CreateUser(ctx, u); err != nil {
t.Fatal(err)
}
o := &influxdb.Organization{
// ID(1)
Name: "org1",
}
if err := s.CreateOrganization(ctx, o); err != nil {
t.Fatal(err)
}
urm := &influxdb.UserResourceMapping{
UserID: u.ID,
UserType: userType,
MappingType: influxdb.UserMappingType,
ResourceType: influxdb.OrgsResourceType,
ResourceID: o.ID,
}
if err := s.CreateUserResourceMapping(ctx, urm); err != nil {
t.Fatal(err)
}
// Check existence
orgs, norgs, err := s.FindOrganizations(ctx, influxdb.OrganizationFilter{})
if err != nil {
t.Fatal(err)
}
if norgs != 1 {
t.Errorf("expected 1 org, got: %v", orgs)
}
usrs, nusrs, err := s.FindUsers(ctx, influxdb.UserFilter{})
if err != nil {
t.Fatal(err)
}
if nusrs != 1 {
t.Errorf("expected 1 user, got: %v", usrs)
}
bs, nbs, err := s.FindBuckets(ctx, influxdb.BucketFilter{})
if err != nil {
t.Fatal(err)
}
if nbs != 2 {
t.Errorf("expected 2 buckets, got: %v", bs)
}
sort.Sort(bucketsByName(bs))
if name := bs[0].Name; name != "_monitoring" {
t.Errorf("unexpected name for bucket: %s", name)
}
if name := bs[1].Name; name != "_tasks" {
t.Errorf("unexpected name for bucket: %v", name)
}
urms, _, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{})
if err != nil {
t.Fatal(err)
}
want := []*influxdb.UserResourceMapping{
{
UserID: u.ID,
UserType: userType,
MappingType: influxdb.UserMappingType,
ResourceType: influxdb.OrgsResourceType,
ResourceID: o.ID,
},
}
sort.Sort(urmByResourceID(want))
sort.Sort(urmByResourceID(urms))
if diff := cmp.Diff(want, urms); diff != "" {
t.Errorf("unexpected urms -want/+got:\n\t%s", diff)
}
// Now add a new bucket and check the URMs.
b := &influxdb.Bucket{
// ID(1)
OrgID: o.ID,
Name: "bucket1",
}
if err := s.CreateBucket(ctx, b); err != nil {
t.Fatal(err)
}
urms, _, err = s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{})
if err != nil {
t.Fatal(err)
}
sort.Sort(urmByResourceID(urms))
if diff := cmp.Diff(want, urms); diff != "" {
t.Errorf("unexpected urms -want/+got:\n\t%s", diff)
}
})
}
})
}
// Delete tests various cases of deletion for the services in the TenantService.
// An example: if you delete a bucket the corresponding user resource mapping is not present.
func Delete(t *testing.T, init func(*testing.T, TenantFields) (influxdb.TenantService, func())) {
t.Helper()
fields := func() TenantFields {
return TenantFields{
OrgIDGenerator: mock.NewIncrementingIDGenerator(1),
// URM are userID + resourceID (they do not include resource type)
// so same IDs across different resources leads to collisions
// therefore, we need to start bucket IDs at higher offset for
// test.
BucketIDGenerator: mock.NewIncrementingIDGenerator(10),
Users: []*influxdb.User{
{
ID: 1,
Name: "user1",
},
{
ID: 2,
Name: "user2",
},
},
Passwords: []string{"password1", "password2"},
Organizations: []*influxdb.Organization{
{
// ID(1)
Name: "org1",
},
{
// ID(2)
Name: "org2",
},
},
// 2 organizations create 2 system buckets each
// so start at 14
Buckets: []*influxdb.Bucket{
{
// ID(14)
OrgID: 1,
Name: "bucket1",
},
{
// ID(15)
OrgID: 2,
Name: "bucket2",
},
},
UserResourceMappings: []*influxdb.UserResourceMapping{
// NOTE(affo): bucket URMs should not be here, create them only for deletion purposes.
// user 1 owns org1 (and so bucket1)
{
UserID: 1,
UserType: influxdb.Owner,
MappingType: influxdb.UserMappingType,
ResourceType: influxdb.OrgsResourceType,
ResourceID: 1,
},
{
UserID: 1,
UserType: influxdb.Owner,
MappingType: influxdb.UserMappingType,
ResourceType: influxdb.BucketsResourceType,
ResourceID: 14,
},
// user 1 is member of org2 (and so bucket2)
{
UserID: 1,
UserType: influxdb.Member,
MappingType: influxdb.UserMappingType,
ResourceType: influxdb.OrgsResourceType,
ResourceID: 2,
},
{
UserID: 1,
UserType: influxdb.Member,
MappingType: influxdb.UserMappingType,
ResourceType: influxdb.BucketsResourceType,
ResourceID: 15,
},
// user 2 owns org2 (and so bucket2)
{
UserID: 2,
UserType: influxdb.Owner,
MappingType: influxdb.UserMappingType,
ResourceType: influxdb.OrgsResourceType,
ResourceID: 2,
},
{
UserID: 2,
UserType: influxdb.Owner,
MappingType: influxdb.UserMappingType,
ResourceType: influxdb.BucketsResourceType,
ResourceID: 15,
},
},
}
}
t.Run("deleting bucket deletes urm", func(t *testing.T) {
data := fields()
s, done := init(t, data)
defer done()
ctx := context.Background()
f := influxdb.UserResourceMappingFilter{
ResourceID: data.Buckets[0].ID,
ResourceType: influxdb.BucketsResourceType,
}
urms, n, err := s.FindUserResourceMappings(ctx, f)
if err != nil {
t.Fatal(err)
}
if n != 1 {
t.Fatalf("expected 1 urm, got: %v", urms)
}
if err := s.DeleteBucket(ctx, data.Buckets[0].ID); err != nil {
t.Fatal(err)
}
f = influxdb.UserResourceMappingFilter{
ResourceID: data.Buckets[0].ID,
ResourceType: influxdb.BucketsResourceType,
}
urms, n, err = s.FindUserResourceMappings(ctx, f)
if err != nil {
t.Fatal(err)
}
if n > 0 {
t.Fatalf("expected no urm, got: %v", urms)
}
})
// NOTE(affo): those resources could not be dangling (URM could be inferred from an user being in the owner org).
// We do not want to automatically propagate this kind of delete because an resource will always have an owner org.
t.Run("deleting bucket urm does create dangling bucket", func(t *testing.T) {
data := fields()
s, done := init(t, data)
defer done()
ctx := context.Background()
// Pre-check the current situation.
// bucket1 is owned by user1.
// Check it.
urms, _, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{
ResourceType: influxdb.BucketsResourceType,
ResourceID: data.Buckets[0].ID,
})
if err != nil {
t.Fatal(err)
}
want := []*influxdb.UserResourceMapping{
{
UserID: data.Users[0].ID,
UserType: influxdb.Owner,
MappingType: influxdb.UserMappingType,
ResourceType: influxdb.BucketsResourceType,
ResourceID: data.Buckets[0].ID,
},
}
sort.Sort(urmByUserID(want))
sort.Sort(urmByUserID(urms))
if diff := cmp.Diff(want, urms); diff != "" {
t.Fatalf("unexpected urms -want/+got:\n\t%s", diff)
}
// bucket2 is owned by user2.
// bucket2 is readable by user2.
// Check it.
urms, _, err = s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{
ResourceType: influxdb.BucketsResourceType,
ResourceID: data.Buckets[1].ID,
})
if err != nil {
t.Fatal(err)
}
want = []*influxdb.UserResourceMapping{
{
UserID: data.Users[1].ID,
UserType: influxdb.Owner,
MappingType: influxdb.UserMappingType,
ResourceType: influxdb.BucketsResourceType,
ResourceID: data.Buckets[1].ID,
},
{
UserID: data.Users[0].ID,
UserType: influxdb.Member,
MappingType: influxdb.UserMappingType,
ResourceType: influxdb.BucketsResourceType,
ResourceID: data.Buckets[1].ID,
},
}
sort.Sort(urmByUserID(want))
sort.Sort(urmByUserID(urms))
if diff := cmp.Diff(want, urms); diff != "" {
t.Fatalf("unexpected urms -want/+got:\n\t%s", diff)
}
// Now delete user2 -> bucket2.
// Still expect bucket2 to exist (user1 still points to it).
if err := s.DeleteUserResourceMapping(ctx, data.Buckets[1].ID, data.Users[1].ID); err != nil {
t.Fatal(err)
}
bs, nbs, err := s.FindBuckets(ctx, influxdb.BucketFilter{ID: &data.Buckets[1].ID})
if err != nil {
t.Fatal(err)
}
if nbs != 1 {
t.Errorf("expected 1 buckets, got: %v", bs)
}
// Now delete user1 -> bucket2.
// Still expect bucket2 to exist (nobody points to it).
if err := s.DeleteUserResourceMapping(ctx, data.Buckets[1].ID, data.Users[0].ID); err != nil {
t.Fatal(err)
}
bs, nbs, err = s.FindBuckets(ctx, influxdb.BucketFilter{ID: &data.Buckets[1].ID})
if err != nil {
t.Fatal(err)
}
if nbs != 1 {
t.Errorf("expected 1 buckets, got: %v", bs)
}
urms, nurms, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{
ResourceType: influxdb.BucketsResourceType,
ResourceID: data.Buckets[1].ID,
})
if err != nil {
t.Fatal(err)
}
if nurms != 0 {
t.Errorf("expected bucket2, to be dangling, got: %+v", urms)
}
// Now delete user1 -> bucket1.
// Still expect bucket1 to exist (nobody points to it).
if err := s.DeleteUserResourceMapping(ctx, data.Buckets[0].ID, data.Users[0].ID); err != nil {
t.Fatal(err)
}
bs, nbs, err = s.FindBuckets(ctx, influxdb.BucketFilter{ID: &data.Buckets[0].ID})
if err != nil {
t.Fatal(err)
}
if nbs != 1 {
t.Errorf("expected 1 buckets, got: %v", bs)
}
urms, nurms, err = s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{
ResourceType: influxdb.BucketsResourceType,
ResourceID: data.Buckets[0].ID,
})
if err != nil {
t.Fatal(err)
}
if nurms != 0 {
t.Errorf("expected bucket1, to be dangling, got: %+v", urms)
}
})
t.Run("deleting a user deletes every related urm and nothing else", func(t *testing.T) {
data := fields()
s, done := init(t, data)
defer done()
ctx := context.Background()
// bucket1 is owned by user1.
// Check it.
urms, _, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{
ResourceType: influxdb.BucketsResourceType,
ResourceID: data.Buckets[0].ID,
})
if err != nil {
t.Fatal(err)
}
want := []*influxdb.UserResourceMapping{
{
UserID: data.Users[0].ID,
UserType: influxdb.Owner,
MappingType: influxdb.UserMappingType,
ResourceType: influxdb.BucketsResourceType,
ResourceID: data.Buckets[0].ID,
},
}
sort.Sort(urmByUserID(want))
sort.Sort(urmByUserID(urms))
if diff := cmp.Diff(want, urms); diff != "" {
t.Fatalf("unexpected urms -want/+got:\n\t%s", diff)
}
// Delete user1.
// We expect his urms deleted but not bucket1.
if err := s.DeleteUser(ctx, data.Users[0].ID); err != nil {
t.Fatal(err)
}
urms, nurms, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{
UserID: data.Users[0].ID,
})
if err != nil {
t.Fatal(err)
}
if nurms > 0 {
t.Errorf("expected that user deletion would remove dangling urms, got: %+v", urms)
}
bs, nbs, err := s.FindBuckets(ctx, influxdb.BucketFilter{ID: &data.Buckets[0].ID})
if err != nil {
t.Fatal(err)
}
if nbs != 1 {
t.Errorf("expected 1 buckets, got: %v", bs)
}
})
t.Run("deleting a bucket deletes every related urm", func(t *testing.T) {
data := fields()
s, done := init(t, data)
defer done()
ctx := context.Background()
// Delete bucket2.
// We expect its urms deleted.
if err := s.DeleteBucket(ctx, data.Buckets[1].ID); err != nil {
t.Fatal(err)
}
urms, nurms, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{
ResourceType: influxdb.BucketsResourceType,
ResourceID: data.Buckets[1].ID,
})
if err != nil {
t.Fatal(err)
}
if nurms > 0 {
t.Errorf("expected that bucket deletion would remove dangling urms, got: %+v", urms)
}
})
// NOTE(affo)(*kv.Service): buckets, users, and urms survive.
t.Run("deleting an organization should delete everything that depends on it", func(t *testing.T) {
data := fields()
s, done := init(t, data)
defer done()
ctx := context.Background()
// Delete org1.
// We expect its buckets to be deleted.
// We expect urms to those buckets to be deleted too.
// No user should be deleted.
preDeletionBuckets, _, err := s.FindBuckets(ctx, influxdb.BucketFilter{OrganizationID: &data.Organizations[0].ID})
if err != nil {
t.Fatal(err)
}
if err := s.DeleteOrganization(ctx, data.Organizations[0].ID); err != nil {
t.Fatal(err)
}
bs, nbs, err := s.FindBuckets(ctx, influxdb.BucketFilter{OrganizationID: &data.Organizations[0].ID})
if err != nil {
t.Fatal(err)
}
if nbs != 0 {
t.Errorf("expected org buckets to be deleted, got: %+v", bs)
}
urms, _, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{
UserID: data.Users[0].ID,
ResourceType: influxdb.BucketsResourceType,
})
if err != nil {
t.Fatal(err)
}
for _, urm := range urms {
for _, b := range preDeletionBuckets {
if urm.ResourceID == b.ID {
t.Errorf("expected this urm to be deleted, got %+v instead", urm)
}
}
}
if _, err := s.FindUser(ctx, influxdb.UserFilter{ID: &data.Users[0].ID}); err != nil {
t.Fatal(err)
}
// Delete org2.
// Everything should disappear.
if err := s.DeleteOrganization(ctx, data.Organizations[1].ID); err != nil {
t.Fatal(err)
}
orgs, norgs, err := s.FindOrganizations(ctx, influxdb.OrganizationFilter{})
if err != nil {
t.Fatal(err)
}
if norgs != 0 {
t.Errorf("expected no org, got: %v", orgs)
}
usrs, nusrs, err := s.FindUsers(ctx, influxdb.UserFilter{})
if err != nil {
t.Fatal(err)
}
if nusrs != 2 {
t.Errorf("expected 2 users, got: %v", usrs)
}
urms, nurms, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{})
if err != nil {
t.Fatal(err)
}
if nurms > 0 {
t.Errorf("expected no urm, got: %v", urms)
}
bs, nbs, err = s.FindBuckets(ctx, influxdb.BucketFilter{})
if err != nil {
t.Fatal(err)
}
if nbs != 0 {
t.Errorf("expected buckets to be deleted, got: %+v", bs)
}
})
}

View File

@ -2,22 +2,48 @@ package testing
import (
"context"
"io/ioutil"
"os"
"strings"
"testing"
"github.com/influxdata/influxdb/v2"
"github.com/influxdata/influxdb/v2/bolt"
"github.com/influxdata/influxdb/v2/inmem"
"github.com/influxdata/influxdb/v2/kit/platform"
"github.com/influxdata/influxdb/v2/kit/platform/errors"
"github.com/influxdata/influxdb/v2"
"github.com/influxdata/influxdb/v2/kv"
"github.com/influxdata/influxdb/v2/kv/migration/all"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
)
func strPtr(s string) *string {
return &s
func NewTestBoltStore(t *testing.T) (kv.SchemaStore, func()) {
f, err := ioutil.TempFile("", "influxdata-bolt-")
require.NoError(t, err, "unable to create temporary boltdb file")
require.NoError(t, f.Close())
path := f.Name()
s := bolt.NewKVStore(zaptest.NewLogger(t), path, bolt.WithNoSync)
require.NoError(t, s.Open(context.Background()))
// apply all kv migrations
require.NoError(t, all.Up(context.Background(), zaptest.NewLogger(t), s))
close := func() {
s.Close()
os.Remove(path)
}
return s, close
}
func boolPtr(b bool) *bool {
return &b
func NewTestInmemStore(t *testing.T) kv.SchemaStore {
s := inmem.NewKVStore()
// apply all kv migrations
require.NoError(t, all.Up(context.Background(), zaptest.NewLogger(t), s))
return s
}
// TODO(goller): remove opPrefix argument
@ -52,17 +78,17 @@ func ErrorsEqual(t *testing.T, actual, expected error) {
}
}
// FloatPtr takes the ref of a float number.
func FloatPtr(f float64) *float64 {
p := new(float64)
*p = f
return p
}
func idPtr(id platform.ID) *platform.ID {
return &id
}
func strPtr(s string) *string {
return &s
}
func boolPtr(b bool) *bool {
return &b
}
// MustIDBase16 is an helper to ensure a correct ID is built during testing.
func MustIDBase16(s string) platform.ID {
id, err := platform.IDFromString(s)
@ -86,14 +112,6 @@ func MustCreateOrgs(ctx context.Context, svc influxdb.OrganizationService, os ..
}
}
func MustCreateLabels(ctx context.Context, svc influxdb.LabelService, labels ...*influxdb.Label) {
for _, l := range labels {
if err := svc.CreateLabel(ctx, l); err != nil {
panic(err)
}
}
}
func MustCreateUsers(ctx context.Context, svc influxdb.UserService, us ...*influxdb.User) {
for _, u := range us {
if err := svc.CreateUser(ctx, u); err != nil {
@ -102,40 +120,6 @@ func MustCreateUsers(ctx context.Context, svc influxdb.UserService, us ...*influ
}
}
func MustCreateMappings(ctx context.Context, svc influxdb.UserResourceMappingService, ms ...*influxdb.UserResourceMapping) {
for _, m := range ms {
if err := svc.CreateUserResourceMapping(ctx, m); err != nil {
panic(err)
}
}
}
func MustMakeUsersOrgOwner(ctx context.Context, svc influxdb.UserResourceMappingService, oid platform.ID, uids ...platform.ID) {
ms := make([]*influxdb.UserResourceMapping, len(uids))
for i, uid := range uids {
ms[i] = &influxdb.UserResourceMapping{
UserID: uid,
UserType: influxdb.Owner,
ResourceType: influxdb.OrgsResourceType,
ResourceID: oid,
}
}
MustCreateMappings(ctx, svc, ms...)
}
func MustMakeUsersOrgMember(ctx context.Context, svc influxdb.UserResourceMappingService, oid platform.ID, uids ...platform.ID) {
ms := make([]*influxdb.UserResourceMapping, len(uids))
for i, uid := range uids {
ms[i] = &influxdb.UserResourceMapping{
UserID: uid,
UserType: influxdb.Member,
ResourceType: influxdb.OrgsResourceType,
ResourceID: oid,
}
}
MustCreateMappings(ctx, svc, ms...)
}
func MustNewPermissionAtID(id platform.ID, a influxdb.Action, rt influxdb.ResourceType, orgID platform.ID) *influxdb.Permission {
perm, err := influxdb.NewPermissionAtID(id, a, rt, orgID)
if err != nil {

View File

@ -20,26 +20,11 @@ import (
"github.com/influxdata/httprouter"
"github.com/influxdata/influxdb/v2"
icontext "github.com/influxdata/influxdb/v2/context"
"github.com/influxdata/influxdb/v2/inmem"
"github.com/influxdata/influxdb/v2/kv"
"github.com/influxdata/influxdb/v2/kv/migration/all"
"github.com/influxdata/influxdb/v2/mock"
itesting "github.com/influxdata/influxdb/v2/testing"
"go.uber.org/zap/zaptest"
)
func NewTestInmemStore(t *testing.T) (kv.Store, func(), error) {
t.Helper()
store := inmem.NewKVStore()
if err := all.Up(context.Background(), zaptest.NewLogger(t), store); err != nil {
t.Fatal(err)
}
return store, func() {}, nil
}
func TestService_handlePostAuthorization(t *testing.T) {
type fields struct {
AuthorizationService influxdb.AuthorizationService
@ -201,11 +186,7 @@ func TestService_handlePostAuthorization(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Helper()
s, _, err := NewTestInmemStore(t)
if err != nil {
t.Fatal(err)
}
s := itesting.NewTestInmemStore(t)
storage, err := NewStore(s)
if err != nil {
t.Fatal(err)
@ -745,11 +726,7 @@ func TestService_handleGetAuthorizations(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Helper()
s, _, err := NewTestInmemStore(t)
if err != nil {
t.Fatal(err)
}
s := itesting.NewTestInmemStore(t)
storage, err := NewStore(s)
if err != nil {
t.Fatal(err)