147 lines
8.0 KiB
C#
147 lines
8.0 KiB
C#
using JobTrackerApi.Data;
|
|
using JobTrackerApi.Models;
|
|
using JobTrackerApi.Services;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Xunit;
|
|
|
|
namespace JobTrackerApi.MariaDb.Tests;
|
|
|
|
public sealed class CvBlankPersistenceTests
|
|
{
|
|
private const string Owner = "rd21-owner";
|
|
private static CvVariantService Service(JobTrackerContext db) => new(db, new CareerProfileService(db), new ThemedCvRenderer());
|
|
private static CvVariantSettings Blank() => new() { SectionsExplicit = true, IncludeProfileContact = false };
|
|
|
|
private static async Task SeedProfile(JobTrackerContext db)
|
|
{
|
|
db.Users.Add(new() { Id = Owner, UserName = "synthetic-cv-blank" });
|
|
await db.SaveChangesAsync();
|
|
await new CareerProfileService(db).SaveVersionAsync(Owner, new()
|
|
{
|
|
Contact = new() { FullName = "Canonical Person", Headline = "Canonical headline", Email = "synthetic@example.test" },
|
|
Summary = { "Canonical summary" },
|
|
Jobs = { new() { Id = "canonical-role", Title = "Canonical role", Company = "Synthetic company", Bullets = { "Canonical evidence" } } },
|
|
Skills = { "Canonical skill" },
|
|
}, "manual", 0, default);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Blank_json_readback_duplicate_design_and_restore_preserve_canonical_profile_on_mariadb()
|
|
{
|
|
await using var database = await MariaDbDatabase.CreateAsync();
|
|
int id;
|
|
string initialJson;
|
|
string canonicalJson;
|
|
string? identityJson;
|
|
await using (var seed = database.Context())
|
|
{
|
|
await SeedProfile(seed);
|
|
canonicalJson = (await seed.CareerProfiles.SingleAsync()).ProfileJson;
|
|
identityJson = (await seed.Users.SingleAsync()).ProfileCvStructureJson;
|
|
var created = await Service(seed).CreateAsync(Owner, "Blank", null, Blank(), default);
|
|
id = created.Id;
|
|
initialJson = created.SettingsJson;
|
|
}
|
|
|
|
int copyId;
|
|
await using (var edit = database.Context())
|
|
{
|
|
var service = Service(edit);
|
|
var read = (await service.GetAsync(Owner, id, default))!;
|
|
Assert.Equal(initialJson, read.SettingsJson);
|
|
var settings = CvVariantSettingsJson.Deserialize(read.SettingsJson);
|
|
Assert.True(settings.SectionsExplicit);
|
|
Assert.False(settings.IncludeProfileContact);
|
|
Assert.Empty(settings.Sections);
|
|
var rendered = await service.RenderAsync(Owner, id, new("Fallback identity", "data:image/png;base64,AA=="), default, 1);
|
|
Assert.False(rendered!.HasContent);
|
|
Assert.DoesNotContain("Canonical", rendered.Html);
|
|
Assert.DoesNotContain("Fallback identity", rendered.Html);
|
|
|
|
var copy = (await service.DuplicateAsync(Owner, id, "Blank copy", default))!;
|
|
copyId = copy.Id;
|
|
Assert.Equal(initialJson, copy.SettingsJson);
|
|
settings.ThemeId = "minimal";
|
|
settings.AccentColor = "#123456";
|
|
await service.SaveAsync(Owner, id, null, settings, "manual", 1, default);
|
|
}
|
|
|
|
await using (var reopen = database.Context())
|
|
{
|
|
var service = Service(reopen);
|
|
var settings = CvVariantSettingsJson.Deserialize((await service.GetAsync(Owner, id, default))!.SettingsJson);
|
|
Assert.Equal("minimal", settings.ThemeId);
|
|
Assert.True(settings.SectionsExplicit);
|
|
Assert.False(settings.IncludeProfileContact);
|
|
Assert.False((await service.RenderAsync(Owner, id, new("Fallback", null), default, 2))!.HasContent);
|
|
settings.Headline = "Authored CV-only headline";
|
|
await service.SaveAsync(Owner, id, null, settings, "manual", 2, default);
|
|
var restored = (await service.RestoreVersionAsync(Owner, id, 1, 3, default))!;
|
|
Assert.Equal(4, restored.Version);
|
|
Assert.Equal(initialJson, restored.SettingsJson);
|
|
}
|
|
|
|
await using var verify = database.Context();
|
|
Assert.Equal(initialJson, (await verify.CvVariants.SingleAsync(cv => cv.Id == id)).SettingsJson);
|
|
Assert.Equal(initialJson, (await verify.CvVariants.SingleAsync(cv => cv.Id == copyId)).SettingsJson);
|
|
Assert.Equal(new[] { 1, 2, 3, 4 }, await verify.CvVariantVersions.Where(row => row.CvVariantId == id).OrderBy(row => row.Version).Select(row => row.Version).ToArrayAsync());
|
|
Assert.Single(await verify.CvVariantVersions.Where(row => row.CvVariantId == copyId).ToListAsync());
|
|
Assert.Equal(canonicalJson, (await verify.CareerProfiles.SingleAsync()).ProfileJson);
|
|
Assert.Equal(identityJson, (await verify.Users.SingleAsync()).ProfileCvStructureJson);
|
|
Assert.Equal(1, (await verify.CareerProfiles.SingleAsync()).Version);
|
|
Assert.Single(await verify.CareerProfileVersions.ToListAsync());
|
|
Assert.Equal("Canonical role", (await verify.CareerExperiences.SingleAsync()).Title);
|
|
var profile = await new CareerProfileService(verify).LoadStructuredAsync(Owner, default);
|
|
Assert.Equal("Canonical summary", Assert.Single(profile.Summary));
|
|
Assert.Equal("Canonical skill", Assert.Single(profile.Skills));
|
|
Assert.Equal("Canonical evidence", Assert.Single(Assert.Single(profile.Jobs).Bullets));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Competing_blank_edits_and_foreign_access_cannot_overwrite_winner_or_append_history_on_mariadb()
|
|
{
|
|
await using var database = await MariaDbDatabase.CreateAsync();
|
|
int id;
|
|
await using (var seed = database.Context())
|
|
{
|
|
await SeedProfile(seed);
|
|
id = (await Service(seed).CreateAsync(Owner, "Blank", null, Blank(), default)).Id;
|
|
}
|
|
await using var winner = database.Context();
|
|
await using var staleSave = database.Context();
|
|
await using var staleRestore = database.Context();
|
|
Assert.Equal(1, (await Service(staleSave).GetAsync(Owner, id, default))!.Version);
|
|
Assert.Equal(1, (await Service(staleRestore).GetAsync(Owner, id, default))!.Version);
|
|
var settings = Blank();
|
|
settings.ThemeId = "minimal";
|
|
settings.Headline = "Acknowledged CV-only winner";
|
|
await Service(winner).SaveAsync(Owner, id, "Winner", settings, "manual", 1, default);
|
|
await Assert.ThrowsAsync<DbUpdateConcurrencyException>(() => Service(staleSave).SaveAsync(Owner, id, "Stale name", Blank(), "autosave", 1, default));
|
|
await Assert.ThrowsAsync<DbUpdateConcurrencyException>(() => Service(staleRestore).RestoreVersionAsync(Owner, id, 1, 1, default));
|
|
|
|
await using (var foreign = database.Context("foreign"))
|
|
{
|
|
var service = Service(foreign);
|
|
Assert.Null(await service.GetAsync("foreign", id, default));
|
|
Assert.Null(await service.SaveAsync("foreign", id, "Foreign", Blank(), "manual", 2, default));
|
|
Assert.Null(await service.DuplicateAsync("foreign", id, null, default));
|
|
Assert.Null(await service.RestoreVersionAsync("foreign", id, 1, 2, default));
|
|
Assert.Null(await service.RenderAsync("foreign", id, new("Foreign", null), default, 2));
|
|
Assert.Empty(await service.ListVersionsAsync("foreign", id, default));
|
|
}
|
|
|
|
await using var verify = database.Context();
|
|
var saved = await verify.CvVariants.SingleAsync();
|
|
Assert.Equal(2, saved.Version);
|
|
Assert.Equal("Winner", saved.Name);
|
|
Assert.Equal(CvVariantSettingsJson.Serialize(settings), saved.SettingsJson);
|
|
var history = await verify.CvVariantVersions.OrderBy(row => row.Version).ToListAsync();
|
|
Assert.Equal(new[] { 1, 2 }, history.Select(row => row.Version));
|
|
Assert.Equal(CvVariantSettingsJson.Serialize(Blank()), history[0].SettingsJson);
|
|
Assert.Equal(saved.SettingsJson, history[1].SettingsJson);
|
|
Assert.Equal(1, (await verify.CareerProfiles.SingleAsync()).Version);
|
|
Assert.Single(await verify.CareerProfileVersions.ToListAsync());
|
|
Assert.Equal("Canonical role", (await verify.CareerExperiences.SingleAsync()).Title);
|
|
}
|
|
}
|