package skillstore import ( "path/filepath" "testing" "sgg-ai-skill-manager/internal/domain" ) func TestStoreRoundTrip(t *testing.T) { dir := t.TempDir() store := New(filepath.Join(dir, "state.json"), filepath.Join(dir, "repos")) state := domain.State{ Skills: []domain.SkillState{ { Org: "skills", Repo: "demo", LocalPath: store.LocalRepoPath("skills", "demo"), }, }, } if err := store.Save(state); err != nil { t.Fatalf("Save returned error: %v", err) } got, err := store.Load() if err != nil { t.Fatalf("Load returned error: %v", err) } if len(got.Skills) != 1 || got.Skills[0].Repo != "demo" { t.Fatalf("state = %+v", got) } } func TestUpsertFindAndRemoveSkill(t *testing.T) { state := domain.State{} first := domain.SkillState{Org: "skills", Repo: "demo", CurrentCommit: "abc"} second := domain.SkillState{Org: "skills", Repo: "demo", CurrentCommit: "def"} state = UpsertSkill(state, first) state = UpsertSkill(state, second) if len(state.Skills) != 1 { t.Fatalf("skill count = %d, want 1", len(state.Skills)) } found, ok := FindSkill(state, "skills", "demo") if !ok { t.Fatal("FindSkill did not find demo") } if found.CurrentCommit != "def" { t.Fatalf("CurrentCommit = %q, want def", found.CurrentCommit) } state = RemoveSkill(state, "skills", "demo") if _, ok := FindSkill(state, "skills", "demo"); ok { t.Fatal("FindSkill found removed skill") } } func TestLocalRepoPathRejectsUnsafeNames(t *testing.T) { dir := t.TempDir() store := New(filepath.Join(dir, "state.json"), filepath.Join(dir, "repos")) if got := store.LocalRepoPath("skills", "demo"); got != filepath.Join(dir, "repos", "skills", "demo") { t.Fatalf("LocalRepoPath = %q", got) } if _, err := store.SafeLocalRepoPath("skills/evil", "demo"); err == nil { t.Fatal("SafeLocalRepoPath accepted org with path separator") } if _, err := store.SafeLocalRepoPath("skills", ".."); err == nil { t.Fatal("SafeLocalRepoPath accepted parent traversal") } }