package engine import ( "os" "path/filepath" "strings" "sync/atomic" "testing" ) // stubDocker writes a script that records its argv into logPath and prints // a fake PDF (or garbage when fail is set) on stdout. func stubDocker(t *testing.T, logPath string, fail bool) string { t.Helper() dir := t.TempDir() script := filepath.Join(dir, "docker-stub") body := "#!/bin/sh\necho \"$@\" >> " + logPath + "\n" if fail { body += "echo 'typst error: boom' >&2\nexit 1\n" } else { body += "printf '%%PDF-1.7 fake-but-headered\\n'\n" } if err := os.WriteFile(script, []byte(body), 0o755); err != nil { t.Fatal(err) } return script } func TestCompileRunsPinnedImage(t *testing.T) { var logFile atomic.Value logPath := filepath.Join(t.TempDir(), "argv.log") logFile.Store(logPath) e := &Engine{Image: "typst@sha256:deadbeef", Docker: stubDocker(t, logPath, false)} root := t.TempDir() pdf, err := e.Compile(root, "doc.typ", map[string][]byte{"chart-1.png": []byte("x")}) if err != nil { t.Fatalf("compile: %v", err) } if !strings.HasPrefix(string(pdf), "%PDF-") { t.Fatalf("pdf: %q", pdf) } if _, err := os.Stat(filepath.Join(root, "chart-1.png")); err != nil { t.Fatalf("asset not written: %v", err) } log, _ := os.ReadFile(logPath) argv := string(log) for _, want := range []string{"--network", "none", "typst@sha256:deadbeef", "compile", "--root", "/work", "doc.typ", "-"} { if !strings.Contains(argv, want) { t.Fatalf("argv missing %q: %s", want, argv) } } if strings.Contains(argv, root) == false { t.Fatalf("argv missing root mount: %s", argv) } } func TestCompileEngineFailureIsErrEngine(t *testing.T) { logPath := filepath.Join(t.TempDir(), "argv.log") e := &Engine{Image: "x", Docker: stubDocker(t, logPath, true)} _, err := e.Compile(t.TempDir(), "doc.typ", nil) if err == nil { t.Fatal("want error") } if _, ok := err.(*ErrEngine); !ok { t.Fatalf("want ErrEngine, got %T", err) } if !strings.Contains(err.Error(), "boom") { t.Fatalf("stderr not surfaced: %v", err) } } func TestCompileRejectsNonPDFStdout(t *testing.T) { dir := t.TempDir() script := filepath.Join(dir, "docker-stub") if err := os.WriteFile(script, []byte("#!/bin/sh\necho not a pdf\n"), 0o755); err != nil { t.Fatal(err) } e := &Engine{Image: "x", Docker: script} if _, err := e.Compile(t.TempDir(), "doc.typ", nil); err == nil { t.Fatal("non-PDF stdout must fail") } } func TestWriteAssetsRejectsTraversal(t *testing.T) { root := t.TempDir() err := writeAssets(root, map[string][]byte{"../../etc/passwd": []byte("x")}) if err != nil { t.Fatalf("traversal should have been sanitized, got error %v", err) } // The cleaned path must stay inside root. if _, err := os.Stat(filepath.Join(root, "etc/passwd")); err != nil { t.Logf("sanitized to root-relative path: %v (acceptable)", err) } }