feat(cv): qualify templates and reliable local import processing

This commit is contained in:
cesnimda
2026-10-28 14:36:12 +01:00
parent 73ea888375
commit 3f4db8ca81
69 changed files with 2597 additions and 180 deletions
@@ -4,6 +4,7 @@ using JobTrackerApi.Models;
using JobTrackerApi.Services;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
@@ -14,6 +15,42 @@ namespace JobTrackerApi.MariaDb.Tests;
public sealed partial class CoverLetterWriterTests
{
[Fact]
public async Task Premium_letter_entitlement_uses_actual_roles_and_preserves_downgraded_saved_design()
{
await using var connection = await MariaDbDatabase.CreateAsync();
await using var db = Context(connection);
db.Users.Add(new() { Id = Owner, UserName = "synthetic-letter-owner" });
var job = await Seed(db, "Original letter");
var design = new CoverLetterDocumentSettings { DesignVersion = 2, ThemeId = "technical" };
await Assert.ThrowsAsync<CoverLetterThemeAccessException>(() => Assets(db).SaveCoverLetterAsync(
Owner, job.Id, "Rejected", CoverLetterSources.Manual, null, 1, default, design));
Assert.Empty(await db.CoverLetterVersions.ToListAsync());
Assert.Equal("Original letter", (await db.JobApplications.AsNoTracking().SingleAsync()).CoverLetterText);
var role = new IdentityRole("Premium") { NormalizedName = "PREMIUM" };
db.Roles.Add(role);
db.UserRoles.Add(new IdentityUserRole<string> { UserId = Owner, RoleId = role.Id });
await db.SaveChangesAsync();
var saved = await Assets(db).SaveCoverLetterAsync(Owner, job.Id, "Approved premium letter", CoverLetterSources.Manual, null, 1, default, design);
Assert.Equal(2, saved!.Revision);
Assert.Equal("technical", saved.DocumentSettings!.ThemeId);
await db.UserRoles.Where(row => row.UserId == Owner).ExecuteDeleteAsync();
var retained = await Assets(db).SaveCoverLetterAsync(Owner, job.Id, "Approved after downgrade", CoverLetterSources.Manual, null, saved.Revision, default, design);
Assert.Equal(3, retained!.Revision);
var historyCount = await db.CoverLetterVersions.CountAsync();
await Assert.ThrowsAsync<CoverLetterThemeAccessException>(() => Assets(db).SaveCoverLetterAsync(Owner, job.Id,
"Rejected switch", CoverLetterSources.Manual, null, retained.Revision, default, new() { DesignVersion = 2, ThemeId = "executive" }));
await using var verify = Context(connection);
var actual = await verify.JobApplications.AsNoTracking().SingleAsync();
Assert.Equal("Approved after downgrade", actual.CoverLetterText);
Assert.Equal(3, actual.Version);
Assert.Equal("technical", CoverLetterDocumentSettingsJson.Deserialize(actual.CoverLetterDocumentSettingsJson).ThemeId);
Assert.Equal(historyCount, await verify.CoverLetterVersions.CountAsync());
Assert.Contains("Approved after downgrade", (await Assets(verify).RenderSavedCoverLetterAsync(Owner, job.Id, 3, default))!.Html);
}
[Fact]
public async Task Document_schema_upgrade_preserves_legacy_letter_history_and_frozen_rows_on_mariadb()
{