Files
jobtrackingapp/JobTrackerApi.Tests/BackupControllerTests.cs
T

134 lines
7.1 KiB
C#

using System.Reflection;
using JobTrackerApi.Controllers;
using JobTrackerApi.Data;
using JobTrackerApi.Models;
using JobTrackerApi.Services;
using JobTrackerApi.Tests.TestSupport;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging.Abstractions;
using Xunit;
namespace JobTrackerApi.Tests;
public sealed class BackupControllerTests
{
[Fact]
public void Backup_controller_requires_local_authorization()
{
var attribute = typeof(BackupController).GetCustomAttribute<AuthorizeAttribute>();
Assert.NotNull(attribute);
Assert.Equal("local", attribute!.AuthenticationSchemes);
}
[Fact]
public void Export_controller_requires_local_authorization()
{
var attribute = typeof(ExportController).GetCustomAttribute<AuthorizeAttribute>();
Assert.NotNull(attribute);
Assert.Equal("local", attribute!.AuthenticationSchemes);
}
[Fact]
public async Task Encrypted_returns_file_payload_on_non_windows_platforms_too()
{
await using var db = CreateDb();
db.Companies.Add(new Company { Name = "Acme", OwnerUserId = "user-1" });
var job = new JobApplication { JobTitle = "Backend Developer", OwnerUserId = "user-1" };
var otherJob = new JobApplication { JobTitle = "Other tenant role", OwnerUserId = "user-2" };
db.JobApplications.AddRange(job, otherJob);
await db.SaveChangesAsync();
db.CoverLetterVersions.AddRange(
new CoverLetterVersion { OwnerUserId = "user-1", JobApplicationId = job.Id, Version = 1, Text = "Original letter", Source = CoverLetterSources.Manual },
new CoverLetterVersion { OwnerUserId = "user-1", JobApplicationId = job.Id, Version = 2, Text = "Approved letter", Source = CoverLetterSources.Ai, AiAction = "improve", DocumentSettingsJson = "{\"senderName\":\"Historical sender\"}" },
new CoverLetterVersion { OwnerUserId = "user-2", JobApplicationId = otherJob.Id, Version = 1, Text = "Other tenant private letter" });
db.SubmittedApplicationPackages.AddRange(
new SubmittedApplicationPackage { OwnerUserId = "user-1", JobApplicationId = job.Id, Version = 1, CoverLetterText = "Frozen letter", RenderedCoverLetterHtml = "<p>Exact frozen HTML</p>", CoverLetterDocumentSettingsJson = "{\"subject\":\"Frozen subject\"}" },
new SubmittedApplicationPackage { OwnerUserId = "user-2", JobApplicationId = otherJob.Id, Version = 1, RenderedCoverLetterHtml = "Other tenant private frozen HTML" });
db.EmailSendAttempts.Add(new EmailSendAttempt
{
Id = Guid.NewGuid(),
OwnerUserId = "user-1",
JobApplicationId = job.Id,
Provider = "gmail",
ClientRequestId = Guid.NewGuid().ToString(),
PayloadHash = new string('a', 64),
Status = EmailSendStatuses.Sent,
ProviderMessageId = "synthetic-message-id",
CreatedAtUtc = DateTime.UtcNow,
CompletedAtUtc = DateTime.UtcNow,
});
db.EmailDrafts.Add(new EmailDraft
{
Id = Guid.NewGuid(),
OwnerUserId = "user-1",
JobApplicationId = job.Id,
Provider = "gmail",
To = "recipient@example.test",
Subject = "Synthetic export subject",
BodyText = "Synthetic readable draft body.",
ThreadId = "synthetic-thread",
ClientRequestId = "00000000-0000-4000-8000-000000000123",
Revision = 3,
CreatedAtUtc = DateTime.UtcNow.AddMinutes(-5),
UpdatedAtUtc = DateTime.UtcNow,
});
db.EmailDrafts.Add(new EmailDraft
{
Id = Guid.NewGuid(),
OwnerUserId = "user-2",
JobApplicationId = otherJob.Id,
Provider = "microsoft",
To = "other@example.test",
Subject = "Other tenant subject",
BodyText = "Other tenant private body.",
ClientRequestId = "00000000-0000-4000-8000-000000000456",
CreatedAtUtc = DateTime.UtcNow,
UpdatedAtUtc = DateTime.UtcNow,
});
await db.SaveChangesAsync();
var provider = DataProtectionProvider.Create(new DirectoryInfo(Path.Combine(Path.GetTempPath(), $"jobtracker-tests-{Guid.NewGuid():N}")));
var controller = new BackupController(db, new NullLogger<BackupController>(), provider);
var result = await controller.Encrypted(CancellationToken.None);
var file = Assert.IsType<FileContentResult>(result);
Assert.Equal("application/octet-stream", file.ContentType);
Assert.EndsWith(".jtbackup", file.FileDownloadName);
Assert.NotEmpty(file.FileContents);
var protectedText = System.Text.Encoding.UTF8.GetString(file.FileContents);
var json = Convert.FromBase64String(provider.CreateProtector("JobTrackerApi.Backup.v1").Unprotect(protectedText));
using var document = System.Text.Json.JsonDocument.Parse(json);
var letters = document.RootElement.GetProperty("Data").GetProperty("CoverLetterVersions").EnumerateArray().ToArray();
Assert.Equal(2, letters.Length);
Assert.Equal("Original letter", letters[0].GetProperty("Text").GetString());
Assert.Equal(2, letters[1].GetProperty("Version").GetInt32());
Assert.Equal("Approved letter", letters[1].GetProperty("Text").GetString());
Assert.Equal(CoverLetterSources.Ai, letters[1].GetProperty("Source").GetString());
Assert.Equal("improve", letters[1].GetProperty("AiAction").GetString());
Assert.Equal("{\"senderName\":\"Historical sender\"}", letters[1].GetProperty("DocumentSettingsJson").GetString());
var frozen = Assert.Single(document.RootElement.GetProperty("Data").GetProperty("SubmittedApplicationPackages").EnumerateArray());
Assert.Equal("<p>Exact frozen HTML</p>", frozen.GetProperty("RenderedCoverLetterHtml").GetString());
Assert.Equal("{\"subject\":\"Frozen subject\"}", frozen.GetProperty("CoverLetterDocumentSettingsJson").GetString());
var attempt = Assert.Single(document.RootElement.GetProperty("Data").GetProperty("EmailSendAttempts").EnumerateArray());
Assert.Equal("gmail", attempt.GetProperty("Provider").GetString());
Assert.Equal("sent", attempt.GetProperty("Status").GetString());
Assert.False(attempt.TryGetProperty("PayloadHash", out _));
var draft = Assert.Single(document.RootElement.GetProperty("Data").GetProperty("EmailDrafts").EnumerateArray());
Assert.Equal("recipient@example.test", draft.GetProperty("To").GetString());
Assert.Equal("Synthetic export subject", draft.GetProperty("Subject").GetString());
Assert.Equal("Synthetic readable draft body.", draft.GetProperty("BodyText").GetString());
Assert.Equal("synthetic-thread", draft.GetProperty("ThreadId").GetString());
Assert.Equal("00000000-0000-4000-8000-000000000123", draft.GetProperty("ClientRequestId").GetString());
Assert.Equal(3, draft.GetProperty("Revision").GetInt64());
}
private static JobTrackerContext CreateDb()
{
return TestHostFactory.CreateInMemoryDb();
}
}