package testing import ( "bytes" "context" "sort" "testing" "github.com/google/go-cmp/cmp" "github.com/influxdata/influxdb/v2" "github.com/influxdata/influxdb/v2/mock" ) const ( labelOneID = "41a9f7288d4e2d64" labelTwoID = "b7c5355e1134b11c" labelThreeID = "c8d6466f2245c22d" ) var labelCmpOptions = cmp.Options{ cmp.Comparer(func(x, y []byte) bool { return bytes.Equal(x, y) }), cmp.Transformer("Sort", func(in []*influxdb.Label) []*influxdb.Label { out := append([]*influxdb.Label(nil), in...) // Copy input to avoid mutating it sort.Slice(out, func(i, j int) bool { return out[i].ID.String() > out[j].ID.String() }) return out }), } // LabelFields include the IDGenerator, labels and their mappings type LabelFields struct { Labels []*influxdb.Label Mappings []*influxdb.LabelMapping IDGenerator influxdb.IDGenerator } type labelServiceF func( init func(LabelFields, *testing.T) (influxdb.LabelService, string, func()), t *testing.T, ) // LabelService tests all the service functions. func LabelService( init func(LabelFields, *testing.T) (influxdb.LabelService, string, func()), t *testing.T, ) { tests := []struct { name string fn labelServiceF }{ { name: "CreateLabel", fn: CreateLabel, }, { name: "CreateLabelMapping", fn: CreateLabelMapping, }, { name: "FindLabels", fn: FindLabels, }, { name: "FindLabelByID", fn: FindLabelByID, }, { name: "UpdateLabel", fn: UpdateLabel, }, { name: "DeleteLabel", fn: DeleteLabel, }, { name: "DeleteLabelMapping", fn: DeleteLabelMapping, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { tt.fn(init, t) }) } } func CreateLabel( init func(LabelFields, *testing.T) (influxdb.LabelService, string, func()), t *testing.T, ) { type args struct { label *influxdb.Label } type wants struct { err error labels []*influxdb.Label } tests := []struct { name string fields LabelFields args args wants wants }{ { name: "names should be unique", fields: LabelFields{ IDGenerator: mock.NewMockIDGenerator(), Labels: []*influxdb.Label{ { ID: MustIDBase16(labelOneID), Name: "label_1", OrgID: MustIDBase16(orgOneID), Properties: map[string]string{ "color": "fff000", }, }, }, }, args: args{ label: &influxdb.Label{ ID: MustIDBase16(labelTwoID), Name: "label_1", OrgID: MustIDBase16(orgOneID), Properties: map[string]string{ "color": "fff000", }, }, }, wants: wants{ labels: []*influxdb.Label{ { ID: MustIDBase16(labelOneID), Name: "label_1", OrgID: MustIDBase16(orgOneID), Properties: map[string]string{ "color": "fff000", }, }, }, err: &influxdb.Error{ Code: influxdb.EConflict, Op: influxdb.OpCreateLabel, Msg: "label with name label_1 already exists", }, }, }, { name: "names should be trimmed of spacing", fields: LabelFields{ IDGenerator: mock.NewMockIDGenerator(), Labels: []*influxdb.Label{ { ID: MustIDBase16(labelOneID), Name: "tag_1", OrgID: MustIDBase16(orgOneID), Properties: map[string]string{ "color": "fff000", }, }, }, }, args: args{ label: &influxdb.Label{ ID: MustIDBase16(labelOneID), Name: " tag_1 ", OrgID: MustIDBase16(orgOneID), Properties: map[string]string{ "color": "fff000", }, }, }, wants: wants{ labels: []*influxdb.Label{ { ID: MustIDBase16(labelOneID), Name: "tag_1", OrgID: MustIDBase16(orgOneID), Properties: map[string]string{ "color": "fff000", }, }, }, err: &influxdb.Error{ Code: influxdb.EConflict, Op: influxdb.OpCreateLabel, Msg: "label with name tag_1 already exists", }, }, }, { name: "labels should be unique and case-agnostic", fields: LabelFields{ IDGenerator: mock.NewMockIDGenerator(), Labels: []*influxdb.Label{ { ID: MustIDBase16(labelOneID), Name: "tag_1", OrgID: MustIDBase16(orgOneID), Properties: map[string]string{ "color": "fff000", }, }, }, }, args: args{ label: &influxdb.Label{ ID: MustIDBase16(labelOneID), Name: "TAG_1", OrgID: MustIDBase16(orgOneID), Properties: map[string]string{ "color": "fff000", }, }, }, wants: wants{ labels: []*influxdb.Label{ { ID: MustIDBase16(labelOneID), Name: "tag_1", OrgID: MustIDBase16(orgOneID), Properties: map[string]string{ "color": "fff000", }, }, }, err: &influxdb.Error{ Code: influxdb.EConflict, Op: influxdb.OpCreateLabel, Msg: "label with name TAG_1 already exists", }, }, }, { name: "basic create label", fields: LabelFields{ IDGenerator: mock.NewIDGenerator(labelOneID, t), Labels: []*influxdb.Label{}, }, args: args{ label: &influxdb.Label{ Name: "Tag2", ID: MustIDBase16(labelOneID), OrgID: MustIDBase16(orgOneID), Properties: map[string]string{ "color": "fff000", }, }, }, wants: wants{ labels: []*influxdb.Label{ { ID: MustIDBase16(labelOneID), Name: "Tag2", OrgID: MustIDBase16(orgOneID), Properties: map[string]string{ "color": "fff000", }, }, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { s, opPrefix, done := init(tt.fields, t) defer done() ctx := context.Background() err := s.CreateLabel(ctx, tt.args.label) diffPlatformErrors(tt.name, err, tt.wants.err, opPrefix, t) defer s.DeleteLabel(ctx, tt.args.label.ID) labels, err := s.FindLabels(ctx, influxdb.LabelFilter{}) if err != nil { t.Fatalf("failed to retrieve labels: %v", err) } if diff := cmp.Diff(labels, tt.wants.labels, labelCmpOptions...); diff != "" { t.Errorf("labels are different -got/+want\ndiff %s", diff) } }) } } func FindLabels( init func(LabelFields, *testing.T) (influxdb.LabelService, string, func()), t *testing.T, ) { type args struct { filter influxdb.LabelFilter } type wants struct { err error labels []*influxdb.Label } tests := []struct { name string fields LabelFields args args wants wants }{ { name: "basic find labels", fields: LabelFields{ Labels: []*influxdb.Label{ { ID: MustIDBase16(labelOneID), Name: "Tag1", OrgID: MustIDBase16(orgOneID), }, { ID: MustIDBase16(labelTwoID), Name: "Tag2", OrgID: MustIDBase16(orgOneID), }, }, }, args: args{ filter: influxdb.LabelFilter{}, }, wants: wants{ labels: []*influxdb.Label{ { ID: MustIDBase16(labelOneID), Name: "Tag1", OrgID: MustIDBase16(orgOneID), }, { ID: MustIDBase16(labelTwoID), Name: "Tag2", OrgID: MustIDBase16(orgOneID), }, }, }, }, { name: "find labels filtering", fields: LabelFields{ Labels: []*influxdb.Label{ { ID: MustIDBase16(labelOneID), Name: "Tag1", OrgID: MustIDBase16(orgOneID), }, { ID: MustIDBase16(labelTwoID), Name: "Tag2", OrgID: MustIDBase16(orgOneID), }, { ID: MustIDBase16(labelThreeID), Name: "Tag1", OrgID: MustIDBase16(orgTwoID), }, }, }, args: args{ filter: influxdb.LabelFilter{ Name: "Tag1", OrgID: idPtr(MustIDBase16(orgOneID)), }, }, wants: wants{ labels: []*influxdb.Label{ { ID: MustIDBase16(labelOneID), Name: "Tag1", OrgID: MustIDBase16(orgOneID), }, }, }, }, { name: "find a label by name is case-agnostic", fields: LabelFields{ Labels: []*influxdb.Label{ { ID: MustIDBase16(labelOneID), Name: "tag1", OrgID: MustIDBase16(orgOneID), }, }, }, args: args{ filter: influxdb.LabelFilter{ Name: "TAG1", OrgID: idPtr(MustIDBase16(orgOneID)), }, }, wants: wants{ labels: []*influxdb.Label{ { ID: MustIDBase16(labelOneID), Name: "tag1", OrgID: MustIDBase16(orgOneID), }, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { s, opPrefix, done := init(tt.fields, t) defer done() ctx := context.Background() labels, err := s.FindLabels(ctx, tt.args.filter) diffPlatformErrors(tt.name, err, tt.wants.err, opPrefix, t) if diff := cmp.Diff(labels, tt.wants.labels, labelCmpOptions...); diff != "" { t.Errorf("labels are different -got/+want\ndiff %s", diff) } }) } } func FindLabelByID( init func(LabelFields, *testing.T) (influxdb.LabelService, string, func()), t *testing.T, ) { type args struct { id influxdb.ID } type wants struct { err error label *influxdb.Label } tests := []struct { name string fields LabelFields args args wants wants }{ { name: "find label by ID", fields: LabelFields{ Labels: []*influxdb.Label{ { ID: MustIDBase16(labelOneID), Name: "Tag1", OrgID: MustIDBase16(orgOneID), }, { ID: MustIDBase16(labelTwoID), Name: "Tag2", OrgID: MustIDBase16(orgOneID), }, }, }, args: args{ id: MustIDBase16(labelOneID), }, wants: wants{ label: &influxdb.Label{ ID: MustIDBase16(labelOneID), Name: "Tag1", OrgID: MustIDBase16(orgOneID), }, }, }, { name: "label does not exist", fields: LabelFields{ Labels: []*influxdb.Label{}, }, args: args{ id: MustIDBase16(labelOneID), }, wants: wants{ err: &influxdb.Error{ Code: influxdb.ENotFound, Op: influxdb.OpFindLabelByID, Msg: influxdb.ErrLabelNotFound, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { s, opPrefix, done := init(tt.fields, t) defer done() ctx := context.Background() label, err := s.FindLabelByID(ctx, tt.args.id) diffPlatformErrors(tt.name, err, tt.wants.err, opPrefix, t) if diff := cmp.Diff(label, tt.wants.label, labelCmpOptions...); diff != "" { t.Errorf("labels are different -got/+want\ndiff %s", diff) } }) } } func UpdateLabel( init func(LabelFields, *testing.T) (influxdb.LabelService, string, func()), t *testing.T, ) { type args struct { labelID influxdb.ID update influxdb.LabelUpdate } type wants struct { err error labels []*influxdb.Label } tests := []struct { name string fields LabelFields args args wants wants }{ { name: "update label name", fields: LabelFields{ Labels: []*influxdb.Label{ { ID: MustIDBase16(labelOneID), OrgID: MustIDBase16(orgOneID), Name: "Tag1", }, }, }, args: args{ labelID: MustIDBase16(labelOneID), update: influxdb.LabelUpdate{ Name: "NotTag1", }, }, wants: wants{ labels: []*influxdb.Label{ { ID: MustIDBase16(labelOneID), OrgID: MustIDBase16(orgOneID), Name: "NotTag1", }, }, }, }, { name: "cant update a label with a name that already exists", fields: LabelFields{ Labels: []*influxdb.Label{ { ID: MustIDBase16(labelOneID), OrgID: MustIDBase16(orgOneID), Name: "tag_1", }, { ID: MustIDBase16(labelTwoID), OrgID: MustIDBase16(orgOneID), Name: "tag_2", }, }, }, args: args{ labelID: MustIDBase16(labelTwoID), update: influxdb.LabelUpdate{ Name: "tag_1", }, }, wants: wants{ labels: []*influxdb.Label{ { ID: MustIDBase16(labelOneID), OrgID: MustIDBase16(orgOneID), Name: "tag_1", }, { ID: MustIDBase16(labelTwoID), OrgID: MustIDBase16(orgOneID), Name: "tag_2", }, }, err: &influxdb.Error{ Code: influxdb.EConflict, Op: influxdb.OpCreateLabel, Msg: "label with name tag_1 already exists", }, }, }, { name: "should trim space but fails to update existing label", fields: LabelFields{ Labels: []*influxdb.Label{ { ID: MustIDBase16(labelOneID), OrgID: MustIDBase16(orgOneID), Name: "tag_1", }, { ID: MustIDBase16(labelTwoID), OrgID: MustIDBase16(orgOneID), Name: "tag_2", }, }, }, args: args{ labelID: MustIDBase16(labelTwoID), update: influxdb.LabelUpdate{ Name: " tag_1 ", }, }, wants: wants{ labels: []*influxdb.Label{ { ID: MustIDBase16(labelOneID), OrgID: MustIDBase16(orgOneID), Name: "tag_1", }, { ID: MustIDBase16(labelTwoID), OrgID: MustIDBase16(orgOneID), Name: "tag_2", }, }, err: &influxdb.Error{ Code: influxdb.EConflict, Op: influxdb.OpCreateLabel, Msg: "label with name tag_1 already exists", }, }, }, { name: "update label properties", fields: LabelFields{ Labels: []*influxdb.Label{ { ID: MustIDBase16(labelOneID), OrgID: MustIDBase16(orgOneID), Name: "Tag1", }, }, }, args: args{ labelID: MustIDBase16(labelOneID), update: influxdb.LabelUpdate{ Properties: map[string]string{ "color": "fff000", }, }, }, wants: wants{ labels: []*influxdb.Label{ { ID: MustIDBase16(labelOneID), OrgID: MustIDBase16(orgOneID), Name: "Tag1", Properties: map[string]string{ "color": "fff000", }, }, }, }, }, { name: "replacing a label property", fields: LabelFields{ Labels: []*influxdb.Label{ { ID: MustIDBase16(labelOneID), OrgID: MustIDBase16(orgOneID), Name: "Tag1", Properties: map[string]string{ "color": "fff000", "description": "description", }, }, }, }, args: args{ labelID: MustIDBase16(labelOneID), update: influxdb.LabelUpdate{ Properties: map[string]string{ "color": "abc123", }, }, }, wants: wants{ labels: []*influxdb.Label{ { ID: MustIDBase16(labelOneID), OrgID: MustIDBase16(orgOneID), Name: "Tag1", Properties: map[string]string{ "color": "abc123", "description": "description", }, }, }, }, }, { name: "deleting a label property", fields: LabelFields{ Labels: []*influxdb.Label{ { ID: MustIDBase16(labelOneID), OrgID: MustIDBase16(orgOneID), Name: "Tag1", Properties: map[string]string{ "color": "fff000", "description": "description", }, }, }, }, args: args{ labelID: MustIDBase16(labelOneID), update: influxdb.LabelUpdate{ Properties: map[string]string{ "description": "", }, }, }, wants: wants{ labels: []*influxdb.Label{ { ID: MustIDBase16(labelOneID), OrgID: MustIDBase16(orgOneID), Name: "Tag1", Properties: map[string]string{ "color": "fff000", }, }, }, }, }, { name: "updating a non-existent label", fields: LabelFields{ Labels: []*influxdb.Label{}, }, args: args{ labelID: MustIDBase16(labelOneID), update: influxdb.LabelUpdate{ Properties: map[string]string{ "color": "fff000", }, }, }, wants: wants{ labels: []*influxdb.Label{}, err: &influxdb.Error{ Code: influxdb.ENotFound, Op: influxdb.OpUpdateLabel, Msg: influxdb.ErrLabelNotFound, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { s, opPrefix, done := init(tt.fields, t) defer done() ctx := context.Background() _, err := s.UpdateLabel(ctx, tt.args.labelID, tt.args.update) diffPlatformErrors(tt.name, err, tt.wants.err, opPrefix, t) labels, err := s.FindLabels(ctx, influxdb.LabelFilter{}) if err != nil { t.Fatalf("failed to retrieve labels: %v", err) } if diff := cmp.Diff(labels, tt.wants.labels, labelCmpOptions...); diff != "" { t.Errorf("labels are different -got/+want\ndiff %s", diff) } }) } } func DeleteLabel( init func(LabelFields, *testing.T) (influxdb.LabelService, string, func()), t *testing.T, ) { type args struct { labelID influxdb.ID } type wants struct { err error labels []*influxdb.Label } tests := []struct { name string fields LabelFields args args wants wants }{ { name: "basic delete label", fields: LabelFields{ Labels: []*influxdb.Label{ { ID: MustIDBase16(labelOneID), OrgID: MustIDBase16(orgOneID), Name: "Tag1", }, { ID: MustIDBase16(labelTwoID), OrgID: MustIDBase16(orgOneID), Name: "Tag2", }, }, }, args: args{ labelID: MustIDBase16(labelOneID), }, wants: wants{ labels: []*influxdb.Label{ { ID: MustIDBase16(labelTwoID), OrgID: MustIDBase16(orgOneID), Name: "Tag2", }, }, }, }, { name: "deleting a non-existent label", fields: LabelFields{ Labels: []*influxdb.Label{ { ID: MustIDBase16(labelOneID), OrgID: MustIDBase16(orgOneID), Name: "Tag1", }, }, }, args: args{ labelID: MustIDBase16(labelTwoID), }, wants: wants{ labels: []*influxdb.Label{ { ID: MustIDBase16(labelOneID), OrgID: MustIDBase16(orgOneID), Name: "Tag1", }, }, err: &influxdb.Error{ Code: influxdb.ENotFound, Op: influxdb.OpDeleteLabel, Msg: influxdb.ErrLabelNotFound, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { s, opPrefix, done := init(tt.fields, t) defer done() ctx := context.Background() err := s.DeleteLabel(ctx, tt.args.labelID) diffPlatformErrors(tt.name, err, tt.wants.err, opPrefix, t) labels, err := s.FindLabels(ctx, influxdb.LabelFilter{}) if err != nil { t.Fatalf("failed to retrieve labels: %v", err) } if diff := cmp.Diff(labels, tt.wants.labels, labelCmpOptions...); diff != "" { t.Errorf("labels are different -got/+want\ndiff %s", diff) } }) } } func CreateLabelMapping( init func(LabelFields, *testing.T) (influxdb.LabelService, string, func()), t *testing.T, ) { type args struct { mapping *influxdb.LabelMapping filter *influxdb.LabelMappingFilter } type wants struct { err error labels []*influxdb.Label } tests := []struct { name string fields LabelFields args args wants wants }{ { name: "create label mapping", fields: LabelFields{ Labels: []*influxdb.Label{ { ID: MustIDBase16(labelOneID), OrgID: MustIDBase16(orgOneID), Name: "Tag1", }, }, }, args: args{ mapping: &influxdb.LabelMapping{ LabelID: MustIDBase16(labelOneID), ResourceID: MustIDBase16(bucketOneID), }, filter: &influxdb.LabelMappingFilter{ ResourceID: MustIDBase16(bucketOneID), }, }, wants: wants{ labels: []*influxdb.Label{ { ID: MustIDBase16(labelOneID), Name: "Tag1", OrgID: MustIDBase16(orgOneID), }, }, }, }, { name: "mapping to a nonexistent label", fields: LabelFields{ IDGenerator: mock.NewIDGenerator(labelOneID, t), Labels: []*influxdb.Label{}, }, args: args{ mapping: &influxdb.LabelMapping{ LabelID: MustIDBase16(labelOneID), ResourceID: MustIDBase16(bucketOneID), }, }, wants: wants{ err: &influxdb.Error{ Code: influxdb.ENotFound, Op: influxdb.OpDeleteLabel, Msg: influxdb.ErrLabelNotFound, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { s, opPrefix, done := init(tt.fields, t) defer done() ctx := context.Background() err := s.CreateLabelMapping(ctx, tt.args.mapping) diffPlatformErrors(tt.name, err, tt.wants.err, opPrefix, t) defer s.DeleteLabelMapping(ctx, tt.args.mapping) if tt.args.filter == nil { return } labels, err := s.FindResourceLabels(ctx, *tt.args.filter) if err != nil { t.Fatalf("failed to retrieve labels: %v", err) } if diff := cmp.Diff(labels, tt.wants.labels, labelCmpOptions...); diff != "" { t.Errorf("labels are different -got/+want\ndiff %s", diff) } }) } } func DeleteLabelMapping( init func(LabelFields, *testing.T) (influxdb.LabelService, string, func()), t *testing.T, ) { type args struct { mapping *influxdb.LabelMapping filter influxdb.LabelMappingFilter } type wants struct { err error labels []*influxdb.Label } tests := []struct { name string fields LabelFields args args wants wants }{ { name: "delete label mapping", fields: LabelFields{ Labels: []*influxdb.Label{ { ID: MustIDBase16(labelOneID), Name: "Tag1", OrgID: MustIDBase16(orgOneID), }, }, Mappings: []*influxdb.LabelMapping{ { LabelID: MustIDBase16(labelOneID), ResourceID: MustIDBase16(bucketOneID), }, }, }, args: args{ mapping: &influxdb.LabelMapping{ LabelID: MustIDBase16(labelOneID), ResourceID: MustIDBase16(bucketOneID), }, filter: influxdb.LabelMappingFilter{ ResourceID: MustIDBase16(bucketOneID), }, }, wants: wants{ labels: []*influxdb.Label{}, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { s, opPrefix, done := init(tt.fields, t) defer done() ctx := context.Background() err := s.DeleteLabelMapping(ctx, tt.args.mapping) diffPlatformErrors(tt.name, err, tt.wants.err, opPrefix, t) labels, err := s.FindResourceLabels(ctx, tt.args.filter) if err != nil { t.Fatalf("failed to retrieve labels: %v", err) } if diff := cmp.Diff(labels, tt.wants.labels, labelCmpOptions...); diff != "" { t.Errorf("labels are different -got/+want\ndiff %s", diff) } }) } }