122 lines
3.6 KiB
Go
122 lines
3.6 KiB
Go
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)
|
||
|
|
}
|
||
|
|
}
|