package testing import ( "bytes" "context" "sort" "testing" "github.com/google/go-cmp/cmp" platform "github.com/influxdata/influxdb/v2" ) var secretCmpOptions = cmp.Options{ cmp.Comparer(func(x, y []byte) bool { return bytes.Equal(x, y) }), cmp.Transformer("Sort", func(in []string) []string { out := append([]string(nil), in...) // Copy input to avoid mutating it sort.Strings(out) return out }), } // A secret is a comparable data structure that is used for testing type Secret struct { OrganizationID platform.ID Env map[string]string } // SecretServiceFields contain the type SecretServiceFields struct { Secrets []Secret } // SecretService will test all methods for the secrets service. func SecretService( init func(SecretServiceFields, *testing.T) (platform.SecretService, func()), t *testing.T, ) { tests := []struct { name string fn func( init func(SecretServiceFields, *testing.T) (platform.SecretService, func()), t *testing.T, ) }{ { name: "LoadSecret", fn: LoadSecret, }, { name: "PutSecret", fn: PutSecret, }, { name: "PutSecrets", fn: PutSecrets, }, { name: "PatchSecrets", fn: PatchSecrets, }, { name: "GetSecretKeys", fn: GetSecretKeys, }, { name: "DeleteSecrets", fn: DeleteSecrets, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { tt.fn(init, t) }) } } // LoadSecret tests the LoadSecret method for the SecretService interface. func LoadSecret( init func(f SecretServiceFields, t *testing.T) (platform.SecretService, func()), t *testing.T, ) { type args struct { orgID platform.ID key string } type wants struct { value string err error } tests := []struct { name string fields SecretServiceFields args args wants wants }{ { name: "load secret field", fields: SecretServiceFields{ Secrets: []Secret{ { OrganizationID: platform.ID(1), Env: map[string]string{ "api_key": "abc123xyz", }, }, }, }, args: args{ orgID: platform.ID(1), key: "api_key", }, wants: wants{ value: "abc123xyz", }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { s, done := init(tt.fields, t) defer done() ctx := context.Background() val, err := s.LoadSecret(ctx, tt.args.orgID, tt.args.key) if (err != nil) != (tt.wants.err != nil) { t.Fatalf("expected error '%v' got '%v'", tt.wants.err, err) } if err != nil && tt.wants.err != nil { if err.Error() != tt.wants.err.Error() { t.Fatalf("expected error messages to match '%v' got '%v'", tt.wants.err, err.Error()) } } if want, got := tt.wants.value, val; want != got { t.Errorf("expected value to be %s, got %s", want, got) } }) } } // PutSecret tests the PutSecret method for the SecretService interface. func PutSecret( init func(f SecretServiceFields, t *testing.T) (platform.SecretService, func()), t *testing.T, ) { type args struct { orgID platform.ID key string value string } type wants struct { err error } tests := []struct { name string fields SecretServiceFields args args wants wants }{ { name: "put secret", fields: SecretServiceFields{}, args: args{ orgID: platform.ID(1), key: "api_key", value: "abc123xyz", }, wants: wants{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { s, done := init(tt.fields, t) defer done() ctx := context.Background() err := s.PutSecret(ctx, tt.args.orgID, tt.args.key, tt.args.value) if (err != nil) != (tt.wants.err != nil) { t.Fatalf("expected error '%v' got '%v'", tt.wants.err, err) } if err != nil && tt.wants.err != nil { if err.Error() != tt.wants.err.Error() { t.Fatalf("expected error messages to match '%v' got '%v'", tt.wants.err, err.Error()) } } val, err := s.LoadSecret(ctx, tt.args.orgID, tt.args.key) if err != nil { t.Fatalf("unexpected error %v", err) } if want, got := tt.args.value, val; want != got { t.Errorf("expected value to be %s, got %s", want, got) } }) } } // PutSecrets tests the PutSecrets method for the SecretService interface. func PutSecrets( init func(f SecretServiceFields, t *testing.T) (platform.SecretService, func()), t *testing.T, ) { type args struct { orgID platform.ID secrets map[string]string } type wants struct { err error keys []string } tests := []struct { name string fields SecretServiceFields args args wants wants }{ { name: "put secrets", fields: SecretServiceFields{ Secrets: []Secret{ { OrganizationID: platform.ID(1), Env: map[string]string{ "api_key": "abc123xyz", }, }, }, }, args: args{ orgID: platform.ID(1), secrets: map[string]string{ "api_key2": "abc123xyz", "batman": "potato", }, }, wants: wants{ keys: []string{"api_key2", "batman"}, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { s, done := init(tt.fields, t) defer done() ctx := context.Background() err := s.PutSecrets(ctx, tt.args.orgID, tt.args.secrets) if (err != nil) != (tt.wants.err != nil) { t.Fatalf("expected error '%v' got '%v'", tt.wants.err, err) } if err != nil && tt.wants.err != nil { if err.Error() != tt.wants.err.Error() { t.Fatalf("expected error messages to match '%v' got '%v'", tt.wants.err, err.Error()) } } for k, v := range tt.args.secrets { val, err := s.LoadSecret(ctx, tt.args.orgID, k) if err != nil { t.Fatalf("unexpected error %v", err) } if want, got := v, val; want != got { t.Errorf("expected value to be %s, got %s", want, got) } } keys, err := s.GetSecretKeys(ctx, tt.args.orgID) if err != nil { t.Fatalf("unexpected error %v", err) } if diff := cmp.Diff(keys, tt.wants.keys, secretCmpOptions...); diff != "" { t.Errorf("keys are different -got/+want\ndiff %s", diff) } }) } } // PatchSecrets tests the PatchSecrets method for the SecretService interface. func PatchSecrets( init func(f SecretServiceFields, t *testing.T) (platform.SecretService, func()), t *testing.T, ) { type args struct { orgID platform.ID secrets map[string]string } type wants struct { err error keys []string } tests := []struct { name string fields SecretServiceFields args args wants wants }{ { name: "patch secrets", fields: SecretServiceFields{ Secrets: []Secret{ { OrganizationID: platform.ID(1), Env: map[string]string{ "api_key": "abc123xyz", }, }, }, }, args: args{ orgID: platform.ID(1), secrets: map[string]string{ "api_key2": "abc123xyz", "batman": "potato", }, }, wants: wants{ keys: []string{"api_key", "api_key2", "batman"}, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { s, done := init(tt.fields, t) defer done() ctx := context.Background() err := s.PatchSecrets(ctx, tt.args.orgID, tt.args.secrets) if (err != nil) != (tt.wants.err != nil) { t.Fatalf("expected error '%v' got '%v'", tt.wants.err, err) } if err != nil && tt.wants.err != nil { if err.Error() != tt.wants.err.Error() { t.Fatalf("expected error messages to match '%v' got '%v'", tt.wants.err, err.Error()) } } for k, v := range tt.args.secrets { val, err := s.LoadSecret(ctx, tt.args.orgID, k) if err != nil { if platform.ErrorCode(err) == platform.EMethodNotAllowed { // skip value checking for http service testing break } t.Fatalf("unexpected error %v", err) } if want, got := v, val; want != got { t.Errorf("expected value to be %s, got %s", want, got) } } keys, err := s.GetSecretKeys(ctx, tt.args.orgID) if err != nil { t.Fatalf("unexpected error %v", err) } if diff := cmp.Diff(keys, tt.wants.keys, secretCmpOptions...); diff != "" { t.Errorf("keys are different -got/+want\ndiff %s", diff) } }) } } // GetSecretKeys tests the GetSecretKeys method for the SecretService interface. func GetSecretKeys( init func(f SecretServiceFields, t *testing.T) (platform.SecretService, func()), t *testing.T, ) { type args struct { orgID platform.ID } type wants struct { keys []string err error } tests := []struct { name string fields SecretServiceFields args args wants wants }{ { name: "get secret keys for one org", fields: SecretServiceFields{ Secrets: []Secret{ { OrganizationID: platform.ID(1), Env: map[string]string{ "api_key": "abc123xyz", }, }, { OrganizationID: platform.ID(2), Env: map[string]string{ "api_key": "zyx321cba", }, }, }, }, args: args{ orgID: platform.ID(1), }, wants: wants{ keys: []string{"api_key"}, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { s, done := init(tt.fields, t) defer done() ctx := context.Background() keys, err := s.GetSecretKeys(ctx, tt.args.orgID) if (err != nil) != (tt.wants.err != nil) { t.Fatalf("expected error '%v' got '%v'", tt.wants.err, err) } if err != nil && tt.wants.err != nil { if err.Error() != tt.wants.err.Error() { t.Fatalf("expected error messages to match '%v' got '%v'", tt.wants.err, err.Error()) } } if diff := cmp.Diff(keys, tt.wants.keys, secretCmpOptions...); diff != "" { t.Errorf("keys are different -got/+want\ndiff %s", diff) } }) } } // DeleteSecrets tests the DeleteSecrets method for the SecretService interface. func DeleteSecrets( init func(f SecretServiceFields, t *testing.T) (platform.SecretService, func()), t *testing.T, ) { type args struct { orgID platform.ID keys []string } type wants struct { keys []string err error } tests := []struct { name string fields SecretServiceFields args args wants wants }{ { name: "delete secret keys", fields: SecretServiceFields{ Secrets: []Secret{ { OrganizationID: platform.ID(1), Env: map[string]string{ "api_key": "abc123xyz", "api_key2": "potato", "batman": "foo", }, }, }, }, args: args{ orgID: platform.ID(1), keys: []string{"api_key2", "batman"}, }, wants: wants{ keys: []string{"api_key"}, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { s, done := init(tt.fields, t) defer done() ctx := context.Background() err := s.DeleteSecret(ctx, tt.args.orgID, tt.args.keys...) if (err != nil) != (tt.wants.err != nil) { t.Fatalf("expected error '%v' got '%v'", tt.wants.err, err) } if err != nil && tt.wants.err != nil { if err.Error() != tt.wants.err.Error() { t.Fatalf("expected error messages to match '%v' got '%v'", tt.wants.err, err.Error()) } } keys, err := s.GetSecretKeys(ctx, tt.args.orgID) if err != nil { t.Fatalf("unexpected error %v", err) } if diff := cmp.Diff(keys, tt.wants.keys, secretCmpOptions...); diff != "" { t.Errorf("keys are different -got/+want\ndiff %s", diff) } }) } }