Files
jobtrackingapp/JobTrackerApi.Tests/CoverLetterThemeEntitlementTests.cs
T

128 lines
7.2 KiB
C#

using System.Security.Claims;
using JobTrackerApi.Controllers;
using JobTrackerApi.Data;
using JobTrackerApi.Models;
using JobTrackerApi.Services;
using JobTrackerApi.Tests.TestSupport;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Moq;
using Xunit;
namespace JobTrackerApi.Tests;
public sealed class CoverLetterThemeEntitlementTests
{
[Theory]
[InlineData("save")]
[InlineData("preview")]
[InlineData("restore")]
public async Task Free_user_cannot_activate_a_premium_version_two_letter_through_any_design_writer(string action)
{
var (db, job, controller, _) = await Fixture();
await using var dispose = db;
var premium = new CoverLetterDocumentSettings { ThemeId = "executive", DesignVersion = 2 };
db.CoverLetterVersions.Add(new CoverLetterVersion { OwnerUserId = "owner", JobApplicationId = job.Id, Version = 1,
Text = "Historical premium", Source = CoverLetterSources.Manual, DocumentSettingsJson = CoverLetterDocumentSettingsJson.Serialize(premium) });
await db.SaveChangesAsync();
var response = action switch
{
"preview" => (await controller.PreviewCoverLetter(job.Id, new("Reviewed text", premium), default)).Result,
"restore" => (await controller.RestoreCoverLetter(job.Id, 1, new(job.Version), default)).Result,
_ => (await controller.SaveCoverLetter(job.Id, new("Reviewed text", "manual", null, job.Version, premium), default)).Result,
};
Assert.Equal(403, Assert.IsAssignableFrom<ObjectResult>(response).StatusCode);
Assert.Null(job.CoverLetterDocumentSettingsJson);
Assert.Null(job.CoverLetterText);
}
[Fact]
public async Task Shared_service_rejects_premium_design_before_changing_letter_or_history()
{
var (db, job, _, service) = await Fixture();
await using var dispose = db;
await Assert.ThrowsAsync<CoverLetterThemeAccessException>(() => service.SaveCoverLetterAsync("owner", job.Id, "Unsaved", "manual", null,
job.Version, default, new() { DesignVersion = 2, ThemeId = "technical" }));
Assert.Null(job.CoverLetterText);
Assert.Null(job.CoverLetterDocumentSettingsJson);
Assert.Empty(db.CoverLetterVersions);
}
[Theory]
[InlineData(null)]
[InlineData(1)]
public async Task Legacy_letter_metadata_remains_editable_and_previewable_for_free_users(int? designVersion)
{
var (db, job, controller, _) = await Fixture();
await using var dispose = db;
var legacy = new CoverLetterDocumentSettings { ThemeId = "executive", DesignVersion = designVersion };
Assert.IsType<OkObjectResult>((await controller.PreviewCoverLetter(job.Id, new("Legacy text", legacy), default)).Result);
Assert.IsType<OkObjectResult>((await controller.SaveCoverLetter(job.Id, new("Legacy text", "manual", null, job.Version, legacy), default)).Result);
}
[Fact]
public async Task Premium_account_can_save_and_preview_a_new_premium_letter_design()
{
var (db, job, controller, service) = await Fixture(premium: true);
await using var dispose = db;
var design = new CoverLetterDocumentSettings { ThemeId = "technical", DesignVersion = 2 };
Assert.IsType<OkObjectResult>((await controller.PreviewCoverLetter(job.Id, new("Reviewed text", design), default)).Result);
Assert.IsType<OkObjectResult>((await controller.SaveCoverLetter(job.Id, new("Reviewed text", "manual", null, job.Version, design), default)).Result);
Assert.NotNull(await service.RenderSavedCoverLetterAsync("owner", job.Id, job.Version, default));
}
[Fact]
public async Task Downgraded_account_keeps_current_premium_design_but_cannot_switch_to_another_premium_design()
{
var (db, job, controller, service) = await Fixture();
await using var dispose = db;
var stored = new CoverLetterDocumentSettings { ThemeId = "executive", DesignVersion = 2 };
job.CoverLetterDocumentSettingsJson = CoverLetterDocumentSettingsJson.Serialize(stored);
job.CoverLetterText = "Stored premium text";
await db.SaveChangesAsync();
Assert.NotNull(await service.GetCoverLetterAsync("owner", job.Id, default));
Assert.NotNull(await service.RenderSavedCoverLetterAsync("owner", job.Id, job.Version, default));
Assert.IsType<OkObjectResult>((await controller.PreviewCoverLetter(job.Id, new("Text edit", stored), default)).Result);
Assert.IsType<OkObjectResult>((await controller.SaveCoverLetter(job.Id, new("Text edit", "manual", null, job.Version, stored), default)).Result);
var different = new CoverLetterDocumentSettings { ThemeId = "technical", DesignVersion = 2 };
Assert.Equal(403, Assert.IsAssignableFrom<ObjectResult>((await controller.PreviewCoverLetter(job.Id, new("Text edit", different), default)).Result).StatusCode);
Assert.Equal(403, Assert.IsAssignableFrom<ObjectResult>((await controller.SaveCoverLetter(job.Id, new("Text edit", "manual", null, job.Version, different), default)).Result).StatusCode);
Assert.Equal("executive", CoverLetterDocumentSettingsJson.Deserialize(job.CoverLetterDocumentSettingsJson).ThemeId);
}
[Fact]
public async Task Foreign_application_remains_not_found_before_entitlement_checks()
{
var (db, job, controller, _) = await Fixture();
await using var dispose = db;
job.OwnerUserId = "other";
await db.SaveChangesAsync();
var design = new CoverLetterDocumentSettings { ThemeId = "executive", DesignVersion = 2 };
Assert.IsType<NotFoundResult>((await controller.PreviewCoverLetter(job.Id, new("Foreign", design), default)).Result);
Assert.IsType<NotFoundResult>((await controller.SaveCoverLetter(job.Id, new("Foreign", "manual", null, job.Version, design), default)).Result);
}
private static async Task<(JobTrackerContext db, JobApplication job, ApplicationAssetsController controller, ApplicationAssetsService service)> Fixture(bool premium = false)
{
var db = TestHostFactory.CreateInMemoryDb("owner");
var user = new ApplicationUser { Id = "owner", UserName = "owner" };
db.Users.Add(user);
if (premium)
{
db.Roles.Add(new IdentityRole("Premium") { Id = "premium-role" });
db.UserRoles.Add(new IdentityUserRole<string> { UserId = user.Id, RoleId = "premium-role" });
}
var company = new Company { OwnerUserId = user.Id, Name = "Synthetic" };
var job = new JobApplication { OwnerUserId = user.Id, Company = company, JobTitle = "Synthetic role" };
db.JobApplications.Add(job);
await db.SaveChangesAsync();
var service = new ApplicationAssetsService(db, Mock.Of<ICvVariantService>(), Mock.Of<IApplicationIntelligenceService>());
var users = TestHostFactory.CreateUserManager();
users.Setup(manager => manager.GetUserAsync(It.IsAny<ClaimsPrincipal>())).ReturnsAsync(user);
var controller = new ApplicationAssetsController(users.Object, service, Mock.Of<ISubmittedApplicationPackageService>())
{ ControllerContext = new ControllerContext { HttpContext = new DefaultHttpContext() } };
return (db, job, controller, service);
}
}