168 lines
5.7 KiB
Go
168 lines
5.7 KiB
Go
package continuity
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"orchestra/internal/store"
|
|
)
|
|
|
|
func TestScratchCommitCapturesAllGitStatesExceptProtocolMarkers(t *testing.T) {
|
|
repo := t.TempDir()
|
|
run := func(args ...string) {
|
|
t.Helper()
|
|
if out, err := exec.Command("git", append([]string{"-C", repo}, args...)...).CombinedOutput(); err != nil {
|
|
t.Fatalf("git %v: %v: %s", args, err, out)
|
|
}
|
|
}
|
|
run("init")
|
|
run("config", "user.email", "t@t")
|
|
run("config", "user.name", "t")
|
|
for _, name := range []string{"TASK.md", "deleted.txt", "renamed.txt", "staged.txt"} {
|
|
if err := os.WriteFile(filepath.Join(repo, name), []byte(name), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
run("add", "-A")
|
|
run("commit", "-m", "base")
|
|
if err := os.WriteFile(filepath.Join(repo, "staged.txt"), []byte("staged change"), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
run("add", "staged.txt")
|
|
if err := os.Remove(filepath.Join(repo, "deleted.txt")); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
run("mv", "renamed.txt", "renamed-new.txt")
|
|
if err := os.WriteFile(filepath.Join(repo, "untracked.txt"), []byte("new"), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(repo, ".orchestra-handoff-report.md"), []byte("protocol"), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := ScratchCommit(repo, "orchestra/scratch/test", "checkpoint"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, want := range []string{"staged.txt", "renamed-new.txt", "untracked.txt"} {
|
|
if err := exec.Command("git", "-C", repo, "cat-file", "-e", "HEAD:"+want).Run(); err != nil {
|
|
t.Fatalf("checkpoint omitted %s: %v", want, err)
|
|
}
|
|
}
|
|
if err := exec.Command("git", "-C", repo, "cat-file", "-e", "HEAD:deleted.txt").Run(); err == nil {
|
|
t.Fatal("checkpoint retained deleted file")
|
|
}
|
|
if err := exec.Command("git", "-C", repo, "cat-file", "-e", "HEAD:.orchestra-handoff-report.md").Run(); err == nil {
|
|
t.Fatal("checkpoint committed protocol marker")
|
|
}
|
|
}
|
|
|
|
func TestHandoffCASAndPickup(t *testing.T) {
|
|
root := t.TempDir()
|
|
run := func(a ...string) {
|
|
c := exec.Command("git", append([]string{"-C", root}, a...)...)
|
|
c.Env = append(os.Environ(), "GIT_AUTHOR_NAME=test", "GIT_AUTHOR_EMAIL=test@example", "GIT_COMMITTER_NAME=test", "GIT_COMMITTER_EMAIL=test@example")
|
|
if b, e := c.CombinedOutput(); e != nil {
|
|
t.Fatalf("git: %s %v", b, e)
|
|
}
|
|
}
|
|
os.WriteFile(filepath.Join(root, "TASK.md"), []byte("original"), 0644)
|
|
run("init")
|
|
run("add", "TASK.md")
|
|
run("commit", "-m", "init")
|
|
head := runOut(t, root, "rev-parse", "HEAD")
|
|
task := sha256.Sum256([]byte("original"))
|
|
s, _ := store.Open(t.TempDir())
|
|
h := Handoff{Meta: Meta{ID: "h1", Reason: "manual"}, Anchor: Anchor{GitSHA: head, Branch: "main"}, Action: "run the focused tests", Command: "go test ./..."}
|
|
ref, e := Save(h, s)
|
|
if e != nil {
|
|
t.Fatal(e)
|
|
}
|
|
got, e := Load(ref, s)
|
|
if e != nil || got.Meta.ID != "h1" {
|
|
t.Fatalf("load: %v", e)
|
|
}
|
|
if e = ValidatePickup(root, got, hex.EncodeToString(task[:])); e != nil {
|
|
t.Fatal(e)
|
|
}
|
|
}
|
|
func runOut(t *testing.T, root string, a ...string) string {
|
|
b, e := exec.Command("git", append([]string{"-C", root}, a...)...).Output()
|
|
if e != nil {
|
|
t.Fatal(e)
|
|
}
|
|
return string(b[:len(b)-1])
|
|
}
|
|
|
|
func TestDecodeRejectsUnknownKnowledgeFields(t *testing.T) {
|
|
_, e := Decode([]byte(`{"meta":{"id":"x","reason":"manual","rotation_index":0},"unknown":1}`))
|
|
if e == nil {
|
|
t.Fatal("expected strict schema error")
|
|
}
|
|
}
|
|
|
|
func TestHandoffRejectsFabricatedOrProseAuthoredFields(t *testing.T) {
|
|
base := Handoff{Meta: Meta{ID: "h", Reason: "manual"}, Anchor: Anchor{GitSHA: strings.Repeat("a", 40), Branch: "main"}, Action: "run the focused tests"}
|
|
if err := base.Validate(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
badAction := base
|
|
badAction.Action = "Continue the task from the handoff"
|
|
if err := badAction.Validate(); err == nil {
|
|
t.Fatal("expected circular action rejection")
|
|
}
|
|
badProse := base
|
|
badProse.Remaining = []string{"short\n# markdown heading"}
|
|
if err := badProse.Validate(); err == nil {
|
|
t.Fatal("expected prose-in-list rejection")
|
|
}
|
|
if _, err := Decode([]byte(`{"meta":{"id":"h","reason":"manual","rotation_index":0},"anchor":{"git_sha":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","branch":"main"},"goal":"fabricated","action":"run tests"}`)); err == nil {
|
|
t.Fatal("expected goal rejection")
|
|
}
|
|
}
|
|
|
|
func TestScratchCommitProtectsTask(t *testing.T) {
|
|
root := t.TempDir()
|
|
run := func(a ...string) {
|
|
c := exec.Command("git", append([]string{"-C", root}, a...)...)
|
|
c.Env = append(os.Environ(), "GIT_AUTHOR_NAME=test", "GIT_AUTHOR_EMAIL=test@example", "GIT_COMMITTER_NAME=test", "GIT_COMMITTER_EMAIL=test@example")
|
|
if b, e := c.CombinedOutput(); e != nil {
|
|
t.Fatalf("git: %s %v", b, e)
|
|
}
|
|
}
|
|
os.WriteFile(filepath.Join(root, "TASK.md"), []byte("fixed"), 0644)
|
|
run("init")
|
|
run("add", ".")
|
|
run("commit", "-m", "init")
|
|
os.WriteFile(filepath.Join(root, "wip.txt"), []byte("wip"), 0644)
|
|
if err := ScratchCommit(root, "scratch/task", "wip"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(root, "TASK.md"), []byte("changed"), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := ScratchCommit(root, "scratch/other", "bad"); err == nil {
|
|
t.Fatal("expected immutable TASK.md rejection")
|
|
}
|
|
}
|
|
|
|
func TestVerifyTaskFileRejectsMutation(t *testing.T) {
|
|
root := t.TempDir()
|
|
if err := os.WriteFile(filepath.Join(root, "TASK.md"), []byte("task"), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
sum := sha256.Sum256([]byte("task"))
|
|
if err := VerifyTaskFile(root, hex.EncodeToString(sum[:])); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(root, "TASK.md"), []byte("changed"), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := VerifyTaskFile(root, hex.EncodeToString(sum[:])); err == nil {
|
|
t.Fatal("expected immutable task check to fail")
|
|
}
|
|
}
|