Files
Agent-Native/adapters/pitransport/resultv1_test.go
T
weichenandCursor e65c6ba604 Align execution errors with the shared catalog and add optional logs.
Contract codes now use the agent.* values, and runtime can emit structured logs without changing results when no logger is configured.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-29 14:24:11 +00:00

127 lines
3.6 KiB
Go

package pitransport
import (
"encoding/json"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"testing"
)
func TestSafeRelPathRejectsEscape(t *testing.T) {
if SafeRelPath("/etc/passwd") || SafeRelPath("../x") || SafeRelPath("a/../../b") || SafeRelPath("./../x") {
t.Fatal("escape")
}
if !SafeRelPath("output.md") || !SafeRelPath("tasks/01_draft/output.md") {
t.Fatal("relative deliverable")
}
}
func TestResultV1FailClosedOnBadDigest(t *testing.T) {
body := []byte("hello")
r := BuildResultV1("run-1", "success", false, "", []struct {
RelPath string
Content []byte
}{{"output.md", body}})
patches := map[string][]byte{"output.md": []byte("tampered")}
if err := r.Validate("run-1", patches); err == nil {
t.Fatal("tamper must fail closed")
}
patches["output.md"] = body
if err := r.Validate("run-1", patches); err != nil {
t.Fatal(err)
}
if err := r.Validate("other", patches); err == nil {
t.Fatal("run mismatch")
}
}
func TestResultV1RejectsUnknownMajor(t *testing.T) {
r := ResultV1{SchemaVersion: 99, RunID: "r", Outcome: "success", ContentDigest: "x"}
if err := r.Validate("r", nil); err == nil {
t.Fatal("unknown major")
}
}
func TestResultV1RejectsUnknownErrorCode(t *testing.T) {
r := BuildResultV1("run-1", "error", true, "DROP TABLE users", nil)
if err := r.Validate("run-1", map[string][]byte{}); err == nil {
t.Fatal("arbitrary error_code must fail closed")
}
}
func TestResultV1RejectsMissingMemberBody(t *testing.T) {
r := BuildResultV1("run-1", "success", false, "", []struct {
RelPath string
Content []byte
}{{"output.md", []byte("hello")}})
if err := r.Validate("run-1", map[string][]byte{}); err == nil {
t.Fatal("member without patch body must fail closed")
}
}
func TestTypedRetryableAllowlist(t *testing.T) {
ok := BuildResultV1("r", "error", true, "agent.transient", nil)
if !TypedRetryable(ok) {
t.Fatal("agent.transient retryable")
}
denied := BuildResultV1("r", "error", true, "agent.prompt_empty", nil)
if TypedRetryable(denied) {
t.Fatal("agent.prompt_empty must not retry")
}
}
func TestMembersDigestMatchesNodeCanonicalJSON(t *testing.T) {
r := BuildResultV1("run-js", "success", false, "", []struct {
RelPath string
Content []byte
}{{"output.md", []byte("hello")}})
script := `import { membersDigest } from './adapters/pitransport/testdata/result-v1.mjs';
const members = ` + mustJSON(t, r.Members) + `;
process.stdout.write(membersDigest(members));
`
_, thisFile, _, _ := runtime.Caller(0)
root := filepath.Join(filepath.Dir(thisFile), "..", "..")
cmd := exec.Command("node", "--input-type=module", "-e", script)
cmd.Dir = root
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("node digest: %v %s", err, out)
}
if got := strings.TrimSpace(string(out)); got != r.ContentDigest {
t.Fatalf("js digest %s != go %s", got, r.ContentDigest)
}
}
func mustJSON(t *testing.T, v any) string {
t.Helper()
b, err := json.Marshal(v)
if err != nil {
t.Fatal(err)
}
return string(b)
}
func TestGoldenResultV1Fixture(t *testing.T) {
raw, err := os.ReadFile(filepath.Join("testdata", "pi-run-result-v1.golden.json"))
if err != nil {
t.Fatal(err)
}
var rv ResultV1
if err := json.Unmarshal(raw, &rv); err != nil {
t.Fatal(err)
}
if err := rv.Validate("golden-run-1", map[string][]byte{"output.md": []byte("hello")}); err != nil {
t.Fatal(err)
}
built := BuildResultV1("golden-run-1", "success", false, "", []struct {
RelPath string
Content []byte
}{{"output.md", []byte("hello")}})
if built.ContentDigest != rv.ContentDigest {
t.Fatalf("golden digest drift go=%s file=%s", built.ContentDigest, rv.ContentDigest)
}
}