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