using System.Security.Claims; using System.Text.Json; using JobTrackerApi.Controllers; using JobTrackerApi.Data; using JobTrackerApi.Models; using JobTrackerApi.Services; using JobTrackerApi.Tests.TestSupport; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; using Moq; using Xunit; namespace JobTrackerApi.Tests; public sealed class CvExportRevisionTests { [Fact] public async Task Explicit_blank_export_is_rejected_before_pdf_allocation() { var current = new Mock(); current.SetupGet(x => x.UserId).Returns("owner"); await using var db = new JobTrackerContext(new DbContextOptionsBuilder() .UseInMemoryDatabase(Guid.NewGuid().ToString()).Options, current.Object); var variants = new CvVariantService(db, new CareerProfileService(db), new ThemedCvRenderer()); var settings = CvVariantSettingsJson.Deserialize("{\"sectionsExplicit\":true,\"includeProfileContact\":false,\"sections\":[]}"); var cv = await variants.CreateAsync("owner", "Blank", null, settings, default); var user = new ApplicationUser { Id = "owner", FirstName = "Inherited name" }; var users = TestHostFactory.CreateUserManager(user); users.Setup(x => x.GetUserAsync(It.IsAny())).ReturnsAsync(user); var pdf = new Mock(MockBehavior.Strict); var controller = new CvVariantController(users.Object, variants, pdf.Object, Mock.Of()) { ControllerContext = new() { HttpContext = new DefaultHttpContext() } }; var result = Assert.IsType(await controller.ExportPdf(cv.Id, default, 1)); Assert.Equal("cv_empty_content", JsonSerializer.SerializeToElement(result.Value).GetProperty("code").GetString()); pdf.VerifyNoOtherCalls(); } [Theory] [InlineData(1, "owner", 409)] [InlineData(0, "owner", 409)] [InlineData(1, "other", 404)] [InlineData(2, "other", 404)] [InlineData(2, "owner", 200)] [InlineData(null, "owner", 200)] public async Task Export_checks_the_owned_render_revision_before_allocating_a_pdf(int? expectedVersion, string caller, int status) { await using var connection = new SqliteConnection("Data Source=:memory:"); await connection.OpenAsync(); var current = new Mock(); current.SetupGet(x => x.UserId).Returns("owner"); var options = new DbContextOptionsBuilder().UseSqlite(connection).Options; await using var seed = new JobTrackerContext(options, current.Object); await seed.Database.EnsureCreatedAsync(); var variants = new CvVariantService(seed, new CareerProfileService(seed), new ThemedCvRenderer()); var cv = await variants.CreateAsync("owner", "CV", null, new CvVariantSettings { Headline = "Old headline" }, default); await variants.SaveAsync("owner", cv.Id, null, new CvVariantSettings { Headline = "Saved headline" }, "manual", 1, default); current.SetupGet(x => x.UserId).Returns(caller); await using var db = new JobTrackerContext(options, current.Object); var renderer = new Mock(); renderer.Setup(x => x.Render(It.IsAny(), It.IsAny(), It.IsAny())) .Returns((CvRenderModel model, CvTheme theme, CvVariantSettings settings) => new ThemedCvRenderer().Render(model, theme, settings)); var service = new CvVariantService(db, new CareerProfileService(db), renderer.Object); var pdf = new Mock(); pdf.Setup(x => x.ExportAsync(caller, It.IsAny(), It.IsAny())) .ReturnsAsync(new CvPdfArtifact("CV.pdf", "unused", [1, 2, 3])); var user = new ApplicationUser { Id = caller }; var users = TestHostFactory.CreateUserManager(user); users.Setup(x => x.GetUserAsync(It.IsAny())).ReturnsAsync(user); var controller = new CvVariantController(users.Object, service, pdf.Object, Mock.Of()) { ControllerContext = new() { HttpContext = new DefaultHttpContext() } }; var result = await controller.ExportPdf(cv.Id, default, expectedVersion); if (status == 200) { Assert.Equal(new byte[] { 1, 2, 3 }, Assert.IsType(result).FileContents); pdf.Verify(x => x.ExportAsync(caller, It.Is(r => r.Html.Contains("Saved headline") && !r.Html.Contains("Old headline")), It.IsAny()), Times.Once); } else { if (status == 409) { var conflict = Assert.IsType(result); Assert.Equal(409, conflict.StatusCode); Assert.Equal("cv_revision_conflict", JsonSerializer.SerializeToElement(conflict.Value).GetProperty("code").GetString()); } else Assert.IsType(result); pdf.VerifyNoOtherCalls(); renderer.VerifyNoOtherCalls(); } } }