package store import ( "errors" "strings" "testing" ) func TestCreateSharedSecret_Validates(t *testing.T) { s := newTestStore(t) cases := []struct { name string in SharedSecret wantErr string }{ { name: "missing name", in: SharedSecret{Scope: SharedSecretScopeGlobal}, wantErr: "name is required", }, { name: "invalid scope", in: SharedSecret{Name: "FOO", Scope: "team"}, wantErr: "invalid scope", }, { name: "app scope without app_id", in: SharedSecret{Name: "FOO", Scope: SharedSecretScopeApp}, wantErr: "app_id is required", }, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { _, err := s.CreateSharedSecret(c.in) if err == nil { t.Fatalf("expected error containing %q, got nil", c.wantErr) } if !strings.Contains(err.Error(), c.wantErr) { t.Fatalf("error mismatch: got %q want substring %q", err.Error(), c.wantErr) } }) } } func TestCreateSharedSecret_GlobalForcesBlankAppID(t *testing.T) { s := newTestStore(t) got, err := s.CreateSharedSecret(SharedSecret{ Name: "GLOBAL_KEY", Value: "v", Scope: SharedSecretScopeGlobal, AppID: "should-be-cleared", Enabled: true, }) if err != nil { t.Fatalf("create: %v", err) } if got.AppID != "" { t.Errorf("global secret AppID = %q, want empty", got.AppID) } if got.ID == "" { t.Error("id should be set") } } func TestCreateAndGetSharedSecret(t *testing.T) { s := newTestStore(t) created, err := s.CreateSharedSecret(SharedSecret{ Name: "API_KEY", Value: "ciphertext", Encrypted: true, Scope: SharedSecretScopeApp, AppID: "app1", Description: "d", Enabled: true, }) if err != nil { t.Fatalf("create: %v", err) } got, err := s.GetSharedSecret(created.ID) if err != nil { t.Fatalf("get: %v", err) } if got.Name != "API_KEY" || got.Value != "ciphertext" || !got.Encrypted { t.Errorf("round-trip mismatch: %+v", got) } if got.Scope != SharedSecretScopeApp || got.AppID != "app1" { t.Errorf("scope/app mismatch: %+v", got) } if !got.Enabled { t.Error("enabled lost on round-trip") } } func TestGetSharedSecret_NotFound(t *testing.T) { s := newTestStore(t) if _, err := s.GetSharedSecret("nope"); !errors.Is(err, ErrNotFound) { t.Fatalf("expected ErrNotFound, got %v", err) } } func TestUpdateSharedSecret(t *testing.T) { s := newTestStore(t) created, _ := s.CreateSharedSecret(SharedSecret{ Name: "K", Value: "v1", Scope: SharedSecretScopeGlobal, Enabled: true, }) created.Value = "v2" created.Description = "updated" created.Enabled = false got, err := s.UpdateSharedSecret(created) if err != nil { t.Fatalf("update: %v", err) } if got.Value != "v2" { t.Errorf("value not updated: %q", got.Value) } if got.Description != "updated" { t.Errorf("description not updated: %q", got.Description) } if got.Enabled { t.Error("enabled=false not applied") } } func TestUpdateSharedSecret_NotFound(t *testing.T) { s := newTestStore(t) _, err := s.UpdateSharedSecret(SharedSecret{ ID: "missing", Name: "K", Scope: SharedSecretScopeGlobal, }) if !errors.Is(err, ErrNotFound) { t.Fatalf("expected ErrNotFound updating missing secret, got %v", err) } } func TestDeleteSharedSecret(t *testing.T) { s := newTestStore(t) created, _ := s.CreateSharedSecret(SharedSecret{ Name: "K", Value: "v", Scope: SharedSecretScopeGlobal, Enabled: true, }) if err := s.DeleteSharedSecret(created.ID); err != nil { t.Fatalf("delete: %v", err) } if _, err := s.GetSharedSecret(created.ID); !errors.Is(err, ErrNotFound) { t.Fatalf("expected ErrNotFound after delete, got %v", err) } if err := s.DeleteSharedSecret(created.ID); !errors.Is(err, ErrNotFound) { t.Fatalf("expected ErrNotFound deleting twice, got %v", err) } } func TestSharedSecret_UniquePerScopeAppName(t *testing.T) { s := newTestStore(t) if _, err := s.CreateSharedSecret(SharedSecret{ Name: "DUP", Value: "a", Scope: SharedSecretScopeGlobal, Enabled: true, }); err != nil { t.Fatalf("first create: %v", err) } // Same scope+name collides on the unique index. if _, err := s.CreateSharedSecret(SharedSecret{ Name: "DUP", Value: "b", Scope: SharedSecretScopeGlobal, Enabled: true, }); err == nil { t.Fatal("expected unique-index violation for duplicate global key") } // Same name under an app scope is a distinct row. if _, err := s.CreateSharedSecret(SharedSecret{ Name: "DUP", Value: "c", Scope: SharedSecretScopeApp, AppID: "app1", Enabled: true, }); err != nil { t.Fatalf("app-scoped same name should be allowed: %v", err) } } func TestListApplicableSharedSecrets(t *testing.T) { s := newTestStore(t) // Two globals (one disabled), one app1 secret, one app2 secret. mustCreate(t, s, SharedSecret{Name: "G_ONE", Value: "g1", Scope: SharedSecretScopeGlobal, Enabled: true}) mustCreate(t, s, SharedSecret{Name: "G_OFF", Value: "off", Scope: SharedSecretScopeGlobal, Enabled: false}) mustCreate(t, s, SharedSecret{Name: "A_ONE", Value: "a1", Scope: SharedSecretScopeApp, AppID: "app1", Enabled: true}) mustCreate(t, s, SharedSecret{Name: "A_TWO", Value: "a2", Scope: SharedSecretScopeApp, AppID: "app2", Enabled: true}) got, err := s.ListApplicableSharedSecrets("app1") if err != nil { t.Fatalf("applicable: %v", err) } // app1 sees the enabled global + its own; not the disabled global, not app2's. if len(got) != 2 { t.Fatalf("want 2 applicable secrets, got %d: %+v", len(got), got) } // Global must come first so callers can overlay app on top. if got[0].Name != "G_ONE" { t.Errorf("expected global first, got %q", got[0].Name) } if got[1].Name != "A_ONE" { t.Errorf("expected app1 secret second, got %q", got[1].Name) } for _, sec := range got { if sec.AppID == "app2" { t.Errorf("app1 must not see app2's secret: %+v", sec) } if !sec.Enabled { t.Errorf("disabled secret leaked into applicable set: %+v", sec) } } } func TestListApplicableSharedSecrets_NoAppOnlyGlobals(t *testing.T) { s := newTestStore(t) mustCreate(t, s, SharedSecret{Name: "G", Value: "g", Scope: SharedSecretScopeGlobal, Enabled: true}) mustCreate(t, s, SharedSecret{Name: "A", Value: "a", Scope: SharedSecretScopeApp, AppID: "app1", Enabled: true}) // An ungrouped workload (appID == "") sees only globals. got, err := s.ListApplicableSharedSecrets("") if err != nil { t.Fatalf("applicable: %v", err) } if len(got) != 1 || got[0].Name != "G" { t.Fatalf("ungrouped workload should see only the global, got %+v", got) } } func mustCreate(t *testing.T, s *Store, sec SharedSecret) SharedSecret { t.Helper() out, err := s.CreateSharedSecret(sec) if err != nil { t.Fatalf("create shared secret %q: %v", sec.Name, err) } return out }