Publish curated Knowledge Hub source from 2ed833f290b439a7bc91e5fd19b042c269e3bee9
This commit is contained in:
1 parent
10f2edcd29
commit
59f15b2d08
218 files changed
+23995
-1849
No files matched your search
+80
-2
@@ -1,7 +1,10 @@
|
||||
using System.Net.Http.Json;
|
||||
using ItWorx.KnowledgeHub.Application.Operations;
|
||||
using ItWorx.KnowledgeHub.Contracts.Knowledge;
|
||||
using ItWorx.KnowledgeHub.Infrastructure.Operations;
|
||||
using ItWorx.KnowledgeHub.Infrastructure.Persistence;
|
||||
using ItWorx.KnowledgeHub.IntegrationTests.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace ItWorx.KnowledgeHub.IntegrationTests.Operations;
|
||||
@@ -10,9 +13,16 @@ namespace ItWorx.KnowledgeHub.IntegrationTests.Operations;
|
||||
/// integrity hashes") against a real running application and real PostgreSQL, not a
|
||||
/// mock.</summary>
|
||||
[Collection(PostgresTestGroup.Name)]
|
||||
public sealed class BackupServiceTests(PostgresContainerFixture postgres)
|
||||
: OwnerSetupTestBase(postgres, new Dictionary<string, string?> { ["KnowledgeHub:Backup:RetainedCount"] = "2" })
|
||||
public sealed class BackupServiceTests : OwnerSetupTestBase
|
||||
{
|
||||
private readonly PostgresContainerFixture _postgres;
|
||||
|
||||
public BackupServiceTests(PostgresContainerFixture postgres)
|
||||
: base(postgres, new Dictionary<string, string?> { ["KnowledgeHub:Backup:RetainedCount"] = "2" })
|
||||
{
|
||||
_postgres = postgres;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task A_backup_captures_every_table_and_verifies_its_own_hashes()
|
||||
{
|
||||
@@ -23,6 +33,10 @@ public sealed class BackupServiceTests(PostgresContainerFixture postgres)
|
||||
|
||||
using var scope = Factory.Services.CreateScope();
|
||||
var backups = scope.ServiceProvider.GetRequiredService<IBackupService>();
|
||||
var database = scope.ServiceProvider.GetRequiredService<KnowledgeHubDbContext>();
|
||||
var appliedMigrations = await database.Database.GetAppliedMigrationsAsync();
|
||||
Assert.NotEmpty(appliedMigrations);
|
||||
var latestAppliedMigration = appliedMigrations.Last();
|
||||
|
||||
var summary = await backups.CreateBackupAsync(CancellationToken.None);
|
||||
|
||||
@@ -31,12 +45,31 @@ public sealed class BackupServiceTests(PostgresContainerFixture postgres)
|
||||
|
||||
var manifest = await backups.GetManifestAsync(summary.BackupId, CancellationToken.None);
|
||||
Assert.NotNull(manifest);
|
||||
Assert.Equal(latestAppliedMigration, manifest.SchemaVersion);
|
||||
var pagesTable = Assert.Single(manifest.Tables, table => table.TableName == "pages");
|
||||
Assert.True(pagesTable.RowCount >= 1);
|
||||
Assert.False(string.IsNullOrWhiteSpace(pagesTable.Sha256));
|
||||
// Never a migration-bookkeeping table — that is recreated by the target's own
|
||||
// startup migration, not restored as user data (ADR-016 decision 2).
|
||||
Assert.DoesNotContain(manifest.Tables, table => table.TableName == "__EFMigrationsHistory");
|
||||
var receipt = await scope.ServiceProvider.GetRequiredService<IBackupEvidenceStore>().GetAsync(summary.BackupId, CancellationToken.None);
|
||||
Assert.NotNull(receipt!.IntegrityVerifiedAt);
|
||||
Assert.Null(receipt.RestoreVerifiedAt);
|
||||
AssertBackupFilesArePrivate(
|
||||
scope.ServiceProvider.GetRequiredService<BackupPaths>(), summary.BackupId);
|
||||
|
||||
// Restore the binary dump into a separate migrated database and verify every
|
||||
// manifest row count against the rows actually written by PostgreSQL COPY.
|
||||
await using var target = await RestoreTargetDatabase.CreateAsync(_postgres);
|
||||
var restore = scope.ServiceProvider.GetRequiredService<IRestoreService>();
|
||||
var report = await restore.RestoreAsync(
|
||||
summary.BackupId,
|
||||
new RestoreTarget(target.ConnectionString, target.AttachmentsDirectory),
|
||||
CancellationToken.None);
|
||||
Assert.True(report.Success, string.Join("; ", report.Warnings));
|
||||
Assert.All(report.Tables, table => Assert.True(
|
||||
table.Matches,
|
||||
$"{table.TableName}: restored {table.RestoredRowCount}, manifest expected {table.ExpectedRowCount}"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -85,4 +118,49 @@ public sealed class BackupServiceTests(PostgresContainerFixture postgres)
|
||||
Assert.Equal(second.BackupId, list[1].BackupId);
|
||||
Assert.DoesNotContain(list, entry => entry.BackupId == first.BackupId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Protected_backup_survives_retention_of_newer_unprotected_copies()
|
||||
{
|
||||
using var scope = Factory.Services.CreateScope();
|
||||
var backups = scope.ServiceProvider.GetRequiredService<IBackupService>();
|
||||
var evidence = scope.ServiceProvider.GetRequiredService<IBackupEvidenceStore>();
|
||||
var protectedBackup = await backups.CreateBackupAsync(CancellationToken.None);
|
||||
Assert.Equal(BackupProtectionResult.Updated,
|
||||
await evidence.SetProtectionAsync(protectedBackup.BackupId, true, 0, Guid.NewGuid(), CancellationToken.None));
|
||||
await backups.CreateBackupAsync(CancellationToken.None);
|
||||
await backups.CreateBackupAsync(CancellationToken.None);
|
||||
var latest = await backups.CreateBackupAsync(CancellationToken.None);
|
||||
var list = await backups.ListBackupsAsync(CancellationToken.None);
|
||||
Assert.Contains(list, backup => backup.BackupId == protectedBackup.BackupId);
|
||||
Assert.Contains(list, backup => backup.BackupId == latest.BackupId);
|
||||
Assert.Equal(3, list.Count);
|
||||
}
|
||||
|
||||
private static void AssertBackupFilesArePrivate(BackupPaths paths, string backupId)
|
||||
{
|
||||
if (OperatingSystem.IsWindows()) return;
|
||||
|
||||
var backupDirectory = Path.Combine(paths.BackupsRoot, backupId);
|
||||
Assert.Equal(
|
||||
UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute,
|
||||
File.GetUnixFileMode(paths.BackupsRoot));
|
||||
var directories = Directory.EnumerateDirectories(backupDirectory, "*", SearchOption.AllDirectories)
|
||||
.Prepend(backupDirectory);
|
||||
foreach (var directory in directories)
|
||||
{
|
||||
Assert.Equal(
|
||||
UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute,
|
||||
File.GetUnixFileMode(directory));
|
||||
}
|
||||
|
||||
foreach (var file in Directory.EnumerateFiles(backupDirectory, "*", SearchOption.AllDirectories))
|
||||
{
|
||||
Assert.Equal(UnixFileMode.UserRead | UnixFileMode.UserWrite, File.GetUnixFileMode(file));
|
||||
}
|
||||
|
||||
Assert.Equal(
|
||||
UnixFileMode.UserRead | UnixFileMode.UserWrite,
|
||||
File.GetUnixFileMode(Path.Combine(paths.BackupsRoot, ".backup.lock")));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user