Files
2026-07-06 11:05:50 -04:00

157 lines
4.2 KiB
Go

package cv
import (
"context"
"database/sql"
"os"
"path/filepath"
"strings"
"testing"
_ "modernc.org/sqlite"
)
func setupProfileDB(t *testing.T, name, email, location string) *sql.DB {
t.Helper()
db, err := sql.Open("sqlite", ":memory:")
if err != nil {
t.Fatal(err)
}
if _, err := db.Exec(`CREATE TABLE profile (
id INTEGER PRIMARY KEY CHECK (id = 1),
name TEXT, email TEXT, location TEXT, timezone TEXT,
salary_target_min REAL, salary_target_max REAL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)`); err != nil {
t.Fatal(err)
}
if name != "" {
if _, err := db.Exec(`INSERT INTO profile (id, name, email, location) VALUES (1, ?, ?, ?)`,
name, email, location); err != nil {
t.Fatal(err)
}
}
return db
}
func TestLaTeXExporter_Export_EndToEnd(t *testing.T) {
db := setupProfileDB(t, "Ada Lovelace", "ada@example.com", "London, UK")
defer db.Close()
tmp := t.TempDir()
e := &LaTeXExporter{DB: db, OutputDir: tmp}
out, err := e.Export(context.Background())
if err != nil {
t.Fatalf("Export: %v", err)
}
if out.TeXPath == "" {
t.Fatal("TeXPath empty")
}
if filepath.Dir(out.TeXPath) != tmp {
t.Errorf("tex written outside OutputDir: %q", out.TeXPath)
}
data, err := os.ReadFile(out.TeXPath)
if err != nil {
t.Fatal(err)
}
s := string(data)
if !strings.Contains(s, "Ada Lovelace") {
t.Error("name not spliced")
}
if !strings.Contains(s, "ada@example.com") {
t.Error("email not spliced")
}
if strings.Contains(s, "{{NAME}}") || strings.Contains(s, "{{EXPERIENCE}}") {
t.Error("placeholders left unfilled in output")
}
if !strings.Contains(s, `\begin{document}`) || !strings.Contains(s, `\end{document}`) {
t.Error("document structure missing")
}
// pdflatex may or may not be present in CI; either way the tex must succeed.
// If present, PDFPath is populated; if absent, Issues notes the skip.
if out.PDFPath == "" && len(out.Issues) == 0 {
t.Log("pdflatex present but PDFPath unset — check compile path")
}
}
func TestLaTeXExporter_LaTeXEscapingOnContact(t *testing.T) {
db := setupProfileDB(t, "R&D Person", "x_y%z@example.com", "C#/OT")
defer db.Close()
e := &LaTeXExporter{DB: db, OutputDir: t.TempDir()}
out, err := e.Export(context.Background())
if err != nil {
t.Fatalf("Export: %v", err)
}
data, _ := os.ReadFile(out.TeXPath)
s := string(data)
// & should have been escaped to \&, % to \%, # to \#, _ to \_
for _, bad := range []string{"R&D Person", "C#/OT"} {
if strings.Contains(s, bad) {
t.Errorf("unescaped LaTeX special in output for %q", bad)
}
}
if !strings.Contains(s, `R\&D Person`) {
t.Error("missing escaped ampersand in name")
}
if !strings.Contains(s, `C\#/OT`) {
t.Error("missing escaped hash in location")
}
}
func TestValidate_CatchesStructuralGaps(t *testing.T) {
badTex := `\documentclass{article}
\begin{document}
Hello.
\end{document}
`
issues := validate(badTex)
if len(issues) == 0 {
t.Fatal("expected structural issues")
}
var sawEducation, sawSubheading bool
for _, i := range issues {
if strings.Contains(i, "Education") {
sawEducation = true
}
if strings.Contains(i, "resumeSubheading") {
sawSubheading = true
}
}
if !sawEducation || !sawSubheading {
t.Errorf("missing expected issues: got %v", issues)
}
}
func TestValidate_FlagsUnresolvedPlaceholders(t *testing.T) {
// Minimal file with required markers but an unresolved placeholder.
content := `\begin{document}
\section{Education}
\section{Work Experience}
\section{Personal Projects}
\section{Technical Skills}
\resumeSubheading\resumeItem\resumeProjectHeading
Name: {{NAME}}
\end{document}
`
issues := validate(content)
joined := strings.Join(issues, " | ")
if !strings.Contains(joined, "unresolved placeholders") {
t.Errorf("expected unresolved-placeholder flag, got: %s", joined)
}
}
func TestSanitizeFilename(t *testing.T) {
cases := map[string]string{
"Ada Lovelace": "ada-lovelace",
" Dr. María ": "dr-mara", // diacritics dropped (ASCII-only)
"../../evil": "evil",
"": "",
}
for in, want := range cases {
if got := sanitizeFilename(in); got != want {
t.Errorf("sanitizeFilename(%q) = %q, want %q", in, got, want)
}
}
}