package exec import ( "os" "path/filepath" "strings" "testing" ) func TestMatchDeliverableRel_Aliases(t *testing.T) { workDir := filepath.Join(string(filepath.Separator), "app", "data", "workspaces", "bizdev", "prelim_x", "tasks", "01_draft") abs := filepath.Join(workDir, "output.md") cases := []struct { rel string want bool }{ {"output.md", true}, {"./output.md", true}, {abs, true}, {"tasks/01_draft/output.md", true}, {"execution.log", false}, {"../output.md", false}, {"../../output.md", false}, {"/etc/passwd", false}, {"tasks/02_review/output.md", false}, } for _, tc := range cases { if got := MatchDeliverableRel(workDir, tc.rel, "output.md"); got != tc.want { t.Errorf("MatchDeliverableRel(%q) = %v, want %v", tc.rel, got, tc.want) } } } func TestMaterializeTargets_MapsAbsoluteAndNested(t *testing.T) { workDir := filepath.Join(t.TempDir(), "tasks", "01_draft") if err := os.MkdirAll(workDir, 0o755); err != nil { t.Fatal(err) } body := []byte("# 系统初步设计方案\n\n真实 runner 产物\n") incoming := map[string][]byte{ filepath.Join(workDir, "output.md"): body, } got, err := MaterializeTargets(workDir, []string{"output.md"}, incoming) if err != nil { t.Fatalf("absolute rel_path must materialize: %v", err) } if string(got["output.md"]) != string(body) { t.Fatalf("canonical patch missing") } onDisk, err := os.ReadFile(filepath.Join(workDir, "output.md")) if err != nil { t.Fatal(err) } if string(onDisk) != string(body) { t.Fatalf("disk output.md mismatch") } nestedDir := filepath.Join(t.TempDir(), "ws", "tasks", "01_draft") if err := os.MkdirAll(nestedDir, 0o755); err != nil { t.Fatal(err) } if _, err := MaterializeTargets(nestedDir, []string{"output.md"}, map[string][]byte{ "tasks/01_draft/output.md": []byte("# nested alias\n"), }); err != nil { t.Fatalf("duplicated step prefix must map: %v", err) } } func TestMaterializeTargets_MissingAndEmptyFail(t *testing.T) { workDir := t.TempDir() if _, err := MaterializeTargets(workDir, []string{"output.md"}, map[string][]byte{ "execution.log": []byte("Outcome: SUCCESS\n"), }); err == nil || !strings.Contains(err.Error(), "missing target deliverable output.md") { t.Fatalf("must fail closed without inventing output.md, got %v", err) } if _, err := os.Stat(filepath.Join(workDir, "output.md")); err == nil { t.Fatal("must not write a stub output.md") } if _, err := MaterializeTargets(workDir, []string{"output.md"}, map[string][]byte{ "output.md": []byte(" \n"), }); err == nil { t.Fatal("whitespace-only body must not count as a deliverable") } } func TestMaterializeTargets_EmptyPatchKeepsExistingFile(t *testing.T) { workDir := t.TempDir() body := []byte("# 调研报告\n已写在盘上的正文\n") path := filepath.Join(workDir, "output.md") if err := os.WriteFile(path, body, 0o644); err != nil { t.Fatal(err) } got, err := MaterializeTargets(workDir, []string{"output.md"}, map[string][]byte{ "output.md": nil, }) if err != nil { t.Fatal(err) } if string(got["output.md"]) != string(body) { t.Fatalf("collected %q", got["output.md"]) } disk, err := os.ReadFile(path) if err != nil { t.Fatal(err) } if string(disk) != string(body) { t.Fatalf("disk overwritten with %q", disk) } } func TestMaterializeTargets_RejectsSymlinkEscape(t *testing.T) { root := t.TempDir() outside := t.TempDir() if err := os.WriteFile(filepath.Join(outside, "secret.md"), []byte("leak"), 0o644); err != nil { t.Fatal(err) } link := filepath.Join(root, "out") if err := os.Symlink(outside, link); err != nil { t.Fatal(err) } _, err := MaterializeTargets(root, []string{"out/secret.md"}, map[string][]byte{ "out/secret.md": []byte("overwrite"), }) if err == nil { t.Fatal("symlink escape must fail closed") } got, _ := os.ReadFile(filepath.Join(outside, "secret.md")) if string(got) != "leak" { t.Fatalf("must not write through symlink, got %q", got) } }