influxdb/pkger/store_test.go

386 lines
8.9 KiB
Go

package pkger_test
import (
"context"
"testing"
"time"
"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/migration/all"
"github.com/influxdata/influxdb/v2/pkger"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
)
func TestStoreKV(t *testing.T) {
inMemStore := inmem.NewKVStore()
// run all migrations against store
if err := all.Up(context.Background(), zaptest.NewLogger(t), inMemStore); err != nil {
t.Fatal(err)
}
stackStub := func(id, orgID platform.ID) pkger.Stack {
now := time.Time{}.Add(10 * 365 * 24 * time.Hour)
urls := []string{
"http://example.com",
"http://abc.gov",
}
return pkger.Stack{
ID: id,
OrgID: orgID,
CreatedAt: now,
Events: []pkger.StackEvent{
{
EventType: pkger.StackEventCreate,
Name: "threeve",
Description: "desc",
UpdatedAt: now.Add(time.Hour),
Sources: urls,
TemplateURLs: urls,
Resources: []pkger.StackResource{
{
APIVersion: pkger.APIVersion,
ID: 9000,
Kind: pkger.KindBucket,
MetaName: "buzz lightyear",
Associations: []pkger.StackResourceAssociation{{
Kind: pkger.KindLabel,
MetaName: "foo_label",
}},
},
{
APIVersion: pkger.APIVersion,
ID: 333,
Kind: pkger.KindBucket,
MetaName: "beyond",
},
},
},
},
}
}
t.Run("create a stack", func(t *testing.T) {
defer inMemStore.Flush(context.Background())
storeKV := pkger.NewStoreKV(inMemStore)
const orgID = 333
seedEntities(t, storeKV, pkger.Stack{
ID: 1,
OrgID: orgID,
})
t.Run("with no ID collisions creates successfully", func(t *testing.T) {
expected := stackStub(3, orgID)
err := storeKV.CreateStack(context.Background(), expected)
require.NoError(t, err)
readStackEqual(t, storeKV, expected)
})
t.Run("with ID collisions fails with conflict error", func(t *testing.T) {
for _, id := range []platform.ID{2, 3} {
err := storeKV.CreateStack(context.Background(), pkger.Stack{
ID: 1,
OrgID: orgID,
})
require.Errorf(t, err, "id=%d", id)
assert.Equalf(t, errors.EConflict, errors.ErrorCode(err), "id=%d", id)
}
})
})
t.Run("list stacks", func(t *testing.T) {
defer inMemStore.Flush(context.Background())
storeKV := pkger.NewStoreKV(inMemStore)
const orgID1 = 1
const orgID2 = 2
seedEntities(t, storeKV,
pkger.Stack{
ID: 1,
OrgID: orgID1,
Events: []pkger.StackEvent{{
Name: "first_name",
}},
},
pkger.Stack{
ID: 2,
OrgID: orgID2,
Events: []pkger.StackEvent{{
Name: "first_name",
}},
},
pkger.Stack{
ID: 3,
OrgID: orgID1,
Events: []pkger.StackEvent{{
Name: "second_name",
}},
},
pkger.Stack{
ID: 4,
OrgID: orgID2,
Events: []pkger.StackEvent{{
Name: "second_name",
}},
},
)
tests := []struct {
name string
orgID platform.ID
filter pkger.ListFilter
expected []pkger.Stack
}{
{
name: "by org id",
orgID: orgID1,
expected: []pkger.Stack{
{
ID: 1,
OrgID: orgID1,
Events: []pkger.StackEvent{{
Name: "first_name",
}},
},
{
ID: 3,
OrgID: orgID1,
Events: []pkger.StackEvent{{
Name: "second_name",
}},
},
},
},
{
name: "by stack ids",
orgID: orgID1,
filter: pkger.ListFilter{
StackIDs: []platform.ID{1, 3},
},
expected: []pkger.Stack{
{
ID: 1,
OrgID: orgID1,
Events: []pkger.StackEvent{{
Name: "first_name",
}},
},
{
ID: 3,
OrgID: orgID1,
Events: []pkger.StackEvent{{
Name: "second_name",
}},
},
},
},
{
name: "by stack ids skips ids that belong to different organization",
orgID: orgID1,
filter: pkger.ListFilter{
StackIDs: []platform.ID{1, 2, 4},
},
expected: []pkger.Stack{{
ID: 1,
OrgID: orgID1,
Events: []pkger.StackEvent{{
Name: "first_name",
}},
}},
},
{
name: "stack ids that do not exist are skipped",
orgID: orgID1,
filter: pkger.ListFilter{
StackIDs: []platform.ID{1, 9000},
},
expected: []pkger.Stack{{
ID: 1,
OrgID: orgID1,
Events: []pkger.StackEvent{{
Name: "first_name",
}},
}},
},
{
name: "by name",
orgID: orgID1,
filter: pkger.ListFilter{
Names: []string{"first_name"},
},
expected: []pkger.Stack{{
ID: 1,
OrgID: orgID1,
Events: []pkger.StackEvent{{
Name: "first_name",
}},
}},
},
{
name: "by name and id",
orgID: orgID1,
filter: pkger.ListFilter{
StackIDs: []platform.ID{3},
Names: []string{"first_name"},
},
expected: []pkger.Stack{
{
ID: 1,
OrgID: orgID1,
Events: []pkger.StackEvent{{
Name: "first_name",
}},
},
{
ID: 3,
OrgID: orgID1,
Events: []pkger.StackEvent{{
Name: "second_name",
}},
},
},
},
}
for _, tt := range tests {
fn := func(t *testing.T) {
stacks, err := storeKV.ListStacks(context.Background(), tt.orgID, tt.filter)
require.NoError(t, err)
assert.Equal(t, tt.expected, stacks)
}
t.Run(tt.name, fn)
}
})
t.Run("read a stack", func(t *testing.T) {
defer inMemStore.Flush(context.Background())
storeKV := pkger.NewStoreKV(inMemStore)
expected := stackStub(1, 3)
seedEntities(t, storeKV, expected)
t.Run("with valid ID returns stack successfully", func(t *testing.T) {
readStackEqual(t, storeKV, expected)
})
t.Run("when no match found fails with not found error", func(t *testing.T) {
unmatchedID := platform.ID(3000)
_, err := storeKV.ReadStackByID(context.Background(), unmatchedID)
require.Error(t, err)
assert.Equal(t, errors.ENotFound, errors.ErrorCode(err))
})
})
t.Run("update a stack", func(t *testing.T) {
defer inMemStore.Flush(context.Background())
storeKV := pkger.NewStoreKV(inMemStore)
const orgID = 3
const id = 3
expected := stackStub(id, orgID)
seedEntities(t, storeKV, expected)
t.Run("with valid ID updates stack successfully", func(t *testing.T) {
expected := stackStub(id, orgID)
event := expected.LatestEvent()
event.EventType = pkger.StackEventUpdate
event.UpdatedAt = event.UpdatedAt.Add(time.Hour)
event.Resources = append(event.Resources, pkger.StackResource{
APIVersion: pkger.APIVersion,
ID: 333,
Kind: pkger.KindBucket,
MetaName: "beyond",
})
expected.Events = append(expected.Events, event)
err := storeKV.UpdateStack(context.Background(), expected)
require.NoError(t, err)
readStackEqual(t, storeKV, expected)
})
t.Run("when no match found fails with not found error", func(t *testing.T) {
unmatchedID := platform.ID(3000)
err := storeKV.UpdateStack(context.Background(), pkger.Stack{
ID: unmatchedID,
OrgID: orgID,
})
require.Error(t, err)
assert.Equalf(t, errors.ENotFound, errors.ErrorCode(err), "err: %s", err)
})
t.Run("when org id does not match fails with unprocessable entity error", func(t *testing.T) {
err := storeKV.UpdateStack(context.Background(), pkger.Stack{
ID: id,
OrgID: orgID + 9000,
})
require.Error(t, err)
assert.Equalf(t, errors.EUnprocessableEntity, errors.ErrorCode(err), "err: %s", err)
})
})
t.Run("delete a stack", func(t *testing.T) {
defer inMemStore.Flush(context.Background())
storeKV := pkger.NewStoreKV(inMemStore)
const orgID = 3
expected := stackStub(1, orgID)
seedEntities(t, storeKV, expected)
t.Run("with valid ID deletes stack successfully", func(t *testing.T) {
err := storeKV.DeleteStack(context.Background(), expected.ID)
require.NoError(t, err)
_, err = storeKV.ReadStackByID(context.Background(), expected.ID)
require.Error(t, err)
errCodeEqual(t, errors.ENotFound, err)
})
t.Run("when no match found fails with not found error", func(t *testing.T) {
unmatchedID := platform.ID(3000)
err := storeKV.DeleteStack(context.Background(), unmatchedID)
require.Error(t, err)
errCodeEqual(t, errors.ENotFound, err)
})
})
}
func readStackEqual(t *testing.T, store pkger.Store, expected pkger.Stack) {
t.Helper()
stack, err := store.ReadStackByID(context.Background(), expected.ID)
require.NoError(t, err)
assert.Equal(t, expected, stack)
}
func errCodeEqual(t *testing.T, expected string, actual error) {
t.Helper()
assert.Equalf(t, expected, errors.ErrorCode(actual), "err: %s", actual)
}
func seedEntities(t *testing.T, store pkger.Store, first pkger.Stack, rest ...pkger.Stack) {
t.Helper()
for _, st := range append(rest, first) {
err := store.CreateStack(context.Background(), st)
require.NoError(t, err)
}
}