package e2e import ( "os" "path/filepath" "testing" "github.com/m3tam3re/agent-lib/internal/lockfile" ) func ownSkillFixture(h *harness, t *testing.T) { t.Helper() dir := filepath.Join(h.workDir, "skills", "good-skill") if err := os.MkdirAll(dir, 0o755); err != nil { t.Fatal(err) } os.WriteFile(filepath.Join(dir, "SKILL.md"), []byte("---\nname: Own Good\n---\n"), 0o644) } func TestCollisionOwnVsVendored(t *testing.T) { h := newHarness(t) ownSkillFixture(h, t) standardFixture(h, t) out, err := h.run(t, "vendor", "add", "superpowers", h.upstreamDir) if err == nil { t.Fatalf("own-vs-vendored collision must fail:\n%s", out) } for _, want := range []string{"collides", "own", "good-skill"} { if !contains(out, want) { t.Errorf("error must contain %q:\n%s", want, out) } } if _, err := os.Stat(filepath.Join(h.workDir, "external")); err == nil { t.Error("collision must abort before any tree mutation") } if _, err := os.Stat(filepath.Join(h.workDir, lockfile.FileName)); err == nil { t.Error("collision must not write a lockfile") } } func TestCollisionVendoredVsVendored(t *testing.T) { h := newHarness(t) standardFixture(h, t) h.mustRun(t, "vendor", "add", "first", h.upstreamDir) out, err := h.run(t, "vendor", "add", "second", h.upstreamDir) if err == nil { t.Fatalf("vendored-vs-vendored collision must fail:\n%s", out) } for _, want := range []string{"collides", "first"} { if !contains(out, want) { t.Errorf("error must name both parties (%q missing):\n%s", want, out) } } } func TestCollisionResolvedByRename(t *testing.T) { h := newHarness(t) ownSkillFixture(h, t) standardFixture(h, t) out := h.mustRun(t, "vendor", "add", "superpowers", h.upstreamDir, "--rename", "good-skill=superpowers-good-skill") if !contains(out, "renamed") && !contains(out, "materialized") { t.Errorf("rename resolution should report success:\n%s", out) } renamed := filepath.Join(h.workDir, "external/superpowers/skills/superpowers-good-skill/SKILL.md") if _, err := os.Stat(renamed); err != nil { t.Fatal("renamed folder must carry the deployed name") } original := filepath.Join(h.workDir, "external/superpowers/skills/good-skill") if _, err := os.Stat(original); err == nil { t.Error("upstream folder name must not appear after rename") } lock := readFile(t, filepath.Join(h.workDir, lockfile.FileName)) if !contains(lock, `"good-skill": "superpowers-good-skill"`) { t.Errorf("rename map must be pinned in the lockfile:\n%s", lock) } if out := h.mustRun(t, "validate"); !contains(out, "valid") { t.Errorf("renamed state must validate:\n%s", out) } } func TestCollisionRenameToAnotherCollision(t *testing.T) { h := newHarness(t) ownSkillFixture(h, t) standardFixture(h, t) out, err := h.run(t, "vendor", "add", "superpowers", h.upstreamDir, "--rename", "good-skill=broken-skill") if err == nil { t.Fatalf("renaming onto an existing deployed name must fail:\n%s", out) } if !contains(out, "collides") { t.Errorf("error must report the collision:\n%s", out) } } func TestValidateDetectsHandEditedCollision(t *testing.T) { h := newHarness(t) standardFixture(h, t) h.mustRun(t, "vendor", "add", "superpowers", h.upstreamDir) h.mustRun(t, "vendor", "add", "second-source", h.upstreamDir, "--include", "good-skill", "--rename", "good-skill=second-good") mutateLockfile(t, h, func(lf *lockfile.Lockfile) { lf.Sources["second-source"].Renames["good-skill"] = "broken-skill" }) out, err := h.run(t, "validate") if err == nil { t.Fatalf("hand-edited rename collision must fail validation:\n%s", out) } if !contains(out, "collision") { t.Errorf("rule must be reported as collision:\n%s", out) } }