package cockpit import ( "os" "path/filepath" "strings" "testing" ) // TestGenerateOff_RestoresForeignFile proves the reversibility contract for // a pre-existing foreign artifact: generate backs it up and overwrites; // off removes eeco's emit and restores the original byte-for-byte. func TestGenerateOff_RestoresForeignFile(t *testing.T) { cfg := testConfig(t) pb := loadHandover(t) dst := filepath.Join(cfg.UserDir, ".claude", "skills", "handover", "SKILL.md") foreign := "---\nname: handover\ndescription: someone else's skill\nallowed-tools: Read\n---\n# Theirs\n" if err := os.MkdirAll(filepath.Dir(dst), 0o755); err != nil { t.Fatal(err) } if err := os.WriteFile(dst, []byte(foreign), 0o644); err != nil { t.Fatal(err) } res, err := Generate(cfg, pb, "claude") if err != nil { t.Fatalf("Generate: %v", err) } if res.Action != "updated" || res.Backup == "" { t.Fatalf("expected an updated action with a backup, got action=%q backup=%q", res.Action, res.Backup) } if _, err := os.Stat(res.Backup); err != nil { t.Fatalf("backup file not present: %v", err) } cur, _ := os.ReadFile(dst) if string(cur) == foreign { t.Error("generate did not overwrite the foreign file") } off, err := Off(cfg, pb, "claude") if err != nil { t.Fatalf("Off: %v", err) } if !off.Changed { t.Error("Off reported no change") } restored, err := os.ReadFile(dst) if err != nil { t.Fatalf("foreign file not restored: %v", err) } if string(restored) != foreign { t.Errorf("restored content != original foreign file:\n%s", string(restored)) } } // TestVerify_ParityPath exercises the optional --parity branch of Verify // against a synthetic answer key matching the emitted shape. func TestVerify_ParityPath(t *testing.T) { cfg := testConfig(t) pb := loadHandover(t) if _, err := Generate(cfg, pb, "claude"); err != nil { t.Fatal(err) } // A synthetic answer key in the skill layout whose allowlist core is a // subset of the emitted one and carries no write-git verb. keyRoot := t.TempDir() key := filepath.Join(keyRoot, ".claude", "skills", "handover", "SKILL.md") if err := os.MkdirAll(filepath.Dir(key), 0o755); err != nil { t.Fatal(err) } answer := "---\nname: handover\ndescription: x\nallowed-tools: Read, Write, Bash(git status:*)\n---\n" + "# Handover\n## Step 0 — a\n## Step 1 — b\n## Step 2 — c\n## Step 3 — d\n## Step 4 — e\n## Output\nx\n" if err := os.WriteFile(key, []byte(answer), 0o644); err != nil { t.Fatal(err) } vr, err := Verify(cfg, pb, "claude", key) if err != nil { t.Fatalf("Verify --parity: %v", err) } if !vr.Clean || !strings.Contains(vr.Detail, "parity OK") { t.Errorf("expected clean + parity OK, got clean=%v detail=%q", vr.Clean, vr.Detail) } } func TestAccessors(t *testing.T) { if (claudeRenderer{}).Target() != "claude" { t.Error("claudeRenderer.Target") } if got := Targets(); len(got) == 0 || got[0] != "claude" { t.Errorf("Targets() = %v (want claude first)", got) } if _, ok := rendererFor("nosuchharness"); ok { t.Error("rendererFor returned a renderer for an unknown target") } r := GenerateResult{Path: "/p", Action: "generated", Backup: "/b"} if msg := r.Message(); !strings.Contains(msg, "/p") || !strings.Contains(msg, "/b") { t.Errorf("GenerateResult.Message = %q", msg) } }