using JobTrackerApi.Models; using JobTrackerApi.Services; using Xunit; namespace JobTrackerApi.Tests; // Regression safety net for the renderer ahead of the theme-extraction work in // career-workspace-implementation-roadmap.md Phase F3. The renderer had zero test coverage; // these tests lock in "renders without throwing, is deterministic, and contains the content it // was given" for every shipped template so a future refactor (or accidental edit) that changes // output is caught immediately. public sealed class CvTemplateRendererTests { private static readonly string[] AllTemplateIds = { "ats-minimal", "harvard", "auckland", "edinburgh", "monarch", "fjord" }; private readonly CvTemplateRenderer _renderer = new(); private static TailoredCvDocument SampleDocument(string templateId) => new() { TemplateId = templateId, Headline = "Senior Backend Engineer", Summary = { "Builds reliable distributed systems." }, SelectedSkills = { "C#", ".NET", "SQL" }, Experience = { new TailoredCvExperienceItem { Title = "Backend Engineer", Company = "Acme Corp", Start = "2020", End = "Present", IsCurrent = true, Bullets = { "Shipped the payments service." }, }, }, Education = { new TailoredCvEducationItem { Qualification = "BSc Computer Science", Institution = "Example University" }, }, }; [Theory] [MemberData(nameof(TemplateIds))] public void Render_produces_html_containing_candidate_and_content(string templateId) { var result = _renderer.Render(SampleDocument(templateId), templateId, "Jamie Rivera", "Backend Engineer", "Acme Corp"); Assert.Equal(templateId, result.TemplateId); Assert.Contains("Jamie Rivera", result.Html); Assert.Contains("Acme Corp", result.Html); Assert.Contains("Shipped the payments service.", result.Html); Assert.Contains("", result.Html); } [Theory] [MemberData(nameof(TemplateIds))] public void Render_is_deterministic_for_identical_input(string templateId) { var a = _renderer.Render(SampleDocument(templateId), templateId, "Jamie Rivera", "Backend Engineer", "Acme Corp"); var b = _renderer.Render(SampleDocument(templateId), templateId, "Jamie Rivera", "Backend Engineer", "Acme Corp"); Assert.Equal(a.Html, b.Html); } [Fact] public void Unknown_template_id_falls_back_to_ats_minimal() { var result = _renderer.Render(SampleDocument("unknown"), "not-a-real-template", "Jamie Rivera", "Backend Engineer", null); Assert.Equal("ats-minimal", result.TemplateId); } [Fact] public void Html_encodes_user_supplied_content_to_prevent_injection() { var document = SampleDocument("ats-minimal"); document.Summary[0] = ""; var result = _renderer.Render(document, "ats-minimal", "Jamie Rivera", "Backend Engineer", null); Assert.DoesNotContain("", result.Html); Assert.Contains("<script>", result.Html); } public static IEnumerable TemplateIds() => AllTemplateIds.Select(id => new object[] { id }); }