package repo import ( "errors" "path/filepath" "strings" "testing" "browser.local/platform/domain" ) func TestMemoryRepositoryRejectsDuplicateAndCopiesValues(t *testing.T) { store := NewMemoryStore() user := domain.User{ ID: "user-1", DisplayName: "Mary", Status: domain.UserStatusActive, Roles: []string{"admin"}, } if err := store.Users().Create(user); err != nil { t.Fatalf("create user: %v", err) } if err := store.Users().Create(user); !errors.Is(err, ErrDuplicate) { t.Fatalf("expected duplicate error, got %v", err) } got, err := store.Users().Get(user.ID) if err != nil { t.Fatalf("get user: %v", err) } got.Roles[0] = "mutated" again, err := store.Users().Get(user.ID) if err != nil { t.Fatalf("get user again: %v", err) } if again.Roles[0] != "admin" { t.Fatalf("expected stored roles to be isolated, got %+v", again.Roles) } } func TestMemoryRepositoryListFiltersAndSorts(t *testing.T) { store := NewMemoryStore() users := []domain.User{ {ID: "user-c", DisplayName: "C", Status: domain.UserStatusDisabled}, {ID: "user-b", DisplayName: "B", Status: domain.UserStatusActive}, {ID: "user-a", DisplayName: "A", Status: domain.UserStatusActive}, } for _, user := range users { if err := store.Users().Create(user); err != nil { t.Fatalf("create user %s: %v", user.ID, err) } } active, err := store.Users().List(domain.UserFilter{Status: domain.UserStatusActive}) if err != nil { t.Fatalf("list users: %v", err) } if len(active) != 2 || active[0].ID != "user-a" || active[1].ID != "user-b" { t.Fatalf("expected sorted active users, got %+v", active) } } func TestMemoryJobRepositoryFindsIdempotencyKey(t *testing.T) { store := NewMemoryStore() job := domain.Job{ ID: "job-1", RunEndpointID: "run-local", Capability: "process.start", IdempotencyKey: "idem-1", State: domain.JobStateQueued, } if err := store.Jobs().Create(job); err != nil { t.Fatalf("create job: %v", err) } got, err := store.Jobs().GetByIdempotency("run-local", "idem-1") if err != nil { t.Fatalf("get by idempotency: %v", err) } if got.ID != job.ID { t.Fatalf("expected job %q, got %q", job.ID, got.ID) } _, err = store.Jobs().GetByIdempotency("run-local", "missing") if !errors.Is(err, ErrNotFound) { t.Fatalf("expected not found for missing idempotency key, got %v", err) } } func TestFileStorePersistsAndReloadsResources(t *testing.T) { path := filepath.Join(t.TempDir(), "metadata.json") store, err := NewFileStore(path) if err != nil { t.Fatalf("create file store: %v", err) } user := domain.User{ ID: "user-1", DisplayName: "Durable User", Status: domain.UserStatusActive, Roles: []string{"platform-admin"}, } if err := store.Users().Create(user); err != nil { t.Fatalf("create user: %v", err) } job := domain.Job{ ID: "job-1", RunEndpointID: "run-local", ServerInstanceID: "server-1", Capability: "process.start", IdempotencyKey: "idem-1", State: domain.JobStateQueued, } if err := store.Jobs().Create(job); err != nil { t.Fatalf("create job: %v", err) } reloaded, err := NewFileStore(path) if err != nil { t.Fatalf("reload file store: %v", err) } got, err := reloaded.Users().Get("user-1") if err != nil { t.Fatalf("get reloaded user: %v", err) } if got.DisplayName != user.DisplayName || got.Roles[0] != "platform-admin" { t.Fatalf("unexpected reloaded user: %+v", got) } gotJob, err := reloaded.Jobs().GetByIdempotency("run-local", "idem-1") if err != nil { t.Fatalf("get reloaded job by idempotency: %v", err) } if gotJob.ID != "job-1" || gotJob.ServerInstanceID != "server-1" { t.Fatalf("unexpected reloaded job: %+v", gotJob) } } func TestMySQLStoreRequiresDSN(t *testing.T) { _, err := NewMySQLStore("") if err == nil || !strings.Contains(err.Error(), "PLATFORM_MYSQL_DSN") { t.Fatalf("expected missing MySQL DSN error, got %v", err) } }