internal static partial class Program { static Task TestVaultCryptoRoundtripAsync() { var crypto = new VaultCryptoService(); var plaintext = "sample vault payload"; var payload = crypto.EncryptData(System.Text.Encoding.UTF8.GetBytes(plaintext), "vault-pass-123"); Assert(payload.Length == VaultCryptoService.SaltSize + VaultCryptoService.NonceSize + VaultCryptoService.TagSize + plaintext.Length, "Vault payload length should match salt+nonce+tag+ciphertext layout."); var decrypted = crypto.DecryptData(payload, "vault-pass-123"); Assert(System.Text.Encoding.UTF8.GetString(decrypted) == plaintext, "Vault roundtrip decrypt should return original plaintext."); return Task.CompletedTask; } static Task TestVaultCryptoDecryptsPythonFixtureAsync() { var crypto = new VaultCryptoService(); var payload = Convert.FromBase64String("AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKC6AErhDEMERBl7OFkG4L4oZ2JZckS0VzhxaZoVLckF7VXE+NIYXILsJ8f1I="); var expectedPlaintext = Convert.FromBase64String("dmF1bHQgcGF5bG9hZCBleGFtcGxlCmxpbmUy"); var decrypted = crypto.DecryptData(payload, "vault-pass-123"); Assert(decrypted.SequenceEqual(expectedPlaintext), "C# decrypt should match Python-generated payload plaintext."); return Task.CompletedTask; } static Task TestVaultKeyDerivationMatchesPythonAsync() { var crypto = new VaultCryptoService(); var salt = Enumerable.Range(0, VaultCryptoService.SaltSize).Select(i => (byte)i).ToArray(); var key = crypto.DeriveKey("vault-pass-123", salt); var expectedKeyHex = "b29f523f28bf178f6815c6ca9ee2a588d79b3bd9a822c92a2f0dde5bc853bb52"; var actualKeyHex = Convert.ToHexString(key).ToLowerInvariant(); Assert(actualKeyHex == expectedKeyHex, "Derived key should match Python PBKDF2 fixture key."); return Task.CompletedTask; } static Task TestVaultMonthlyFilenameParityAsync() { IVaultStorageService vaultStorage = new VaultStorageService(new VaultCryptoService()); var name = vaultStorage.GetMonthlyVaultFileName(new DateTime(2026, 2, 7)); Assert(name == "2026-02.vault", "Monthly vault filename must match yyyy-MM.vault format."); return Task.CompletedTask; } static Task TestVaultLoadClearsAndExtractsAsync() { var root = Path.Combine(Path.GetTempPath(), "journal-vault-smoke", Guid.NewGuid().ToString("N")); var vaultDir = Path.Combine(root, "vault"); var dataDir = Path.Combine(root, "data"); Directory.CreateDirectory(vaultDir); Directory.CreateDirectory(dataDir); try { File.WriteAllText(Path.Combine(dataDir, "old_file.md"), "stale"); var zipBytes = CreateZipBytes(new Dictionary { ["2026-02-01.md"] = "hello from vault" }); var crypto = new VaultCryptoService(); var encrypted = crypto.EncryptData(zipBytes, "vault-pass-123"); File.WriteAllBytes(Path.Combine(vaultDir, "2026-02.vault"), encrypted); IVaultStorageService storage = new VaultStorageService(crypto); var ok = storage.LoadAllVaults("vault-pass-123", vaultDir, dataDir); Assert(ok, "Expected vault load success with correct password."); Assert(!File.Exists(Path.Combine(dataDir, "old_file.md")), "Data directory should be cleared before extraction."); var extractedPath = Path.Combine(dataDir, "2026-02-01.md"); Assert(File.Exists(extractedPath), "Expected markdown file extracted from vault archive."); Assert(File.ReadAllText(extractedPath) == "hello from vault", "Extracted file content mismatch."); } finally { if (Directory.Exists(root)) Directory.Delete(root, recursive: true); } return Task.CompletedTask; } static Task TestVaultLoadWrongPasswordPreservesVaultAsync() { var root = Path.Combine(Path.GetTempPath(), "journal-vault-smoke", Guid.NewGuid().ToString("N")); var vaultDir = Path.Combine(root, "vault"); var dataDir = Path.Combine(root, "data"); Directory.CreateDirectory(vaultDir); Directory.CreateDirectory(dataDir); try { var zipBytes = CreateZipBytes(new Dictionary { ["2026-02-01.md"] = "hello from vault" }); var crypto = new VaultCryptoService(); var encrypted = crypto.EncryptData(zipBytes, "vault-pass-123"); var vaultPath = Path.Combine(vaultDir, "2026-02.vault"); File.WriteAllBytes(vaultPath, encrypted); var before = File.ReadAllBytes(vaultPath); IVaultStorageService storage = new VaultStorageService(crypto); var ok = storage.LoadAllVaults("wrong-password", vaultDir, dataDir); var after = File.ReadAllBytes(vaultPath); Assert(!ok, "Expected vault load failure with wrong password."); Assert(before.SequenceEqual(after), "Vault file bytes should remain unchanged on wrong password."); } finally { if (Directory.Exists(root)) Directory.Delete(root, recursive: true); } return Task.CompletedTask; } static Task TestVaultLoadLegacyInitVaultHandlingAsync() { var root = Path.Combine(Path.GetTempPath(), "journal-vault-smoke", Guid.NewGuid().ToString("N")); var vaultDir = Path.Combine(root, "vault"); var dataDir = Path.Combine(root, "data"); Directory.CreateDirectory(vaultDir); Directory.CreateDirectory(dataDir); try { var legacyPath = Path.Combine(vaultDir, "_init_vault.vault"); File.WriteAllBytes(legacyPath, [1, 2, 3, 4]); IVaultStorageService storage = new VaultStorageService(new VaultCryptoService()); var ok = storage.LoadAllVaults("vault-pass-123", vaultDir, dataDir); Assert(ok, "Legacy-only vault directory should still be treated as successful load state."); Assert(!File.Exists(legacyPath), "Legacy _init_vault.vault should be removed during load."); Assert(Directory.Exists(dataDir), "Data directory should exist after load workflow."); } finally { if (Directory.Exists(root)) Directory.Delete(root, recursive: true); } return Task.CompletedTask; } static Task TestVaultCurrentMonthSaveOptimizedAsync() { var root = Path.Combine(Path.GetTempPath(), "journal-vault-smoke", Guid.NewGuid().ToString("N")); var vaultDir = Path.Combine(root, "vault"); var dataDir = Path.Combine(root, "data"); Directory.CreateDirectory(vaultDir); Directory.CreateDirectory(dataDir); try { File.WriteAllText(Path.Combine(dataDir, "2026-02-01.md"), "feb one"); File.WriteAllText(Path.Combine(dataDir, "2026-02-18.md"), "feb two"); File.WriteAllText(Path.Combine(dataDir, "2026-01-31.md"), "jan one"); IVaultStorageService storage = new VaultStorageService(new VaultCryptoService()); var now = new DateTime(2026, 2, 22, 12, 0, 0, DateTimeKind.Utc); var firstSaved = storage.SaveCurrentMonthVault("vault-pass-123", vaultDir, dataDir, now); Assert(firstSaved, "Expected first current-month save to write vault data."); var febVaultPath = Path.Combine(vaultDir, "2026-02.vault"); var janVaultPath = Path.Combine(vaultDir, "2026-01.vault"); Assert(File.Exists(febVaultPath), "Expected current-month vault file to be created."); Assert(!File.Exists(janVaultPath), "Current-month save should not write non-current month vault files."); var entries = ReadVaultEntryTexts(febVaultPath, "vault-pass-123"); Assert(entries.Count == 2, "Current-month vault should include only current-month markdown files."); Assert(entries.ContainsKey("2026-02-01.md"), "Missing first current-month entry in vault archive."); Assert(entries.ContainsKey("2026-02-18.md"), "Missing second current-month entry in vault archive."); Assert(!entries.ContainsKey("2026-01-31.md"), "Current-month vault must not include previous-month files."); var beforeSkipBytes = File.ReadAllBytes(febVaultPath); var secondSaved = storage.SaveCurrentMonthVault("vault-pass-123", vaultDir, dataDir, now); var afterSkipBytes = File.ReadAllBytes(febVaultPath); Assert(!secondSaved, "Expected unchanged current-month save to skip write."); Assert(beforeSkipBytes.SequenceEqual(afterSkipBytes), "Vault bytes should remain unchanged when save is skipped."); File.WriteAllText(Path.Combine(dataDir, "2026-02-18.md"), "feb two changed"); var thirdSaved = storage.SaveCurrentMonthVault("vault-pass-123", vaultDir, dataDir, now); Assert(thirdSaved, "Expected save to run after current-month file change."); } finally { if (Directory.Exists(root)) Directory.Delete(root, recursive: true); } return Task.CompletedTask; } static Task TestVaultRebuildAllVaultsAsync() { var root = Path.Combine(Path.GetTempPath(), "journal-vault-smoke", Guid.NewGuid().ToString("N")); var vaultDir = Path.Combine(root, "vault"); var dataDir = Path.Combine(root, "data"); Directory.CreateDirectory(vaultDir); Directory.CreateDirectory(dataDir); try { File.WriteAllText(Path.Combine(dataDir, "2026-01-31.md"), "jan body"); File.WriteAllText(Path.Combine(dataDir, "2026-02-01.md"), "feb body"); File.WriteAllText(Path.Combine(dataDir, "not-a-journal.md"), "should be ignored"); IVaultStorageService storage = new VaultStorageService(new VaultCryptoService()); storage.RebuildAllVaults("vault-pass-123", vaultDir, dataDir); var janVaultPath = Path.Combine(vaultDir, "2026-01.vault"); var febVaultPath = Path.Combine(vaultDir, "2026-02.vault"); Assert(File.Exists(janVaultPath), "Expected January vault from rebuild flow."); Assert(File.Exists(febVaultPath), "Expected February vault from rebuild flow."); var janEntries = ReadVaultEntryTexts(janVaultPath, "vault-pass-123"); var febEntries = ReadVaultEntryTexts(febVaultPath, "vault-pass-123"); Assert(janEntries.Count == 1 && janEntries.ContainsKey("2026-01-31.md"), "January vault contents mismatch."); Assert(febEntries.Count == 1 && febEntries.ContainsKey("2026-02-01.md"), "February vault contents mismatch."); } finally { if (Directory.Exists(root)) Directory.Delete(root, recursive: true); } return Task.CompletedTask; } static Task TestVaultClearDataDirectoryAsync() { var root = Path.Combine(Path.GetTempPath(), "journal-vault-smoke", Guid.NewGuid().ToString("N")); var dataDir = Path.Combine(root, "data"); Directory.CreateDirectory(dataDir); Directory.CreateDirectory(Path.Combine(dataDir, "nested")); try { File.WriteAllText(Path.Combine(dataDir, "2026-02-01.md"), "decrypted content"); File.WriteAllText(Path.Combine(dataDir, "journal_cache.db"), "cache"); File.WriteAllText(Path.Combine(dataDir, "nested", "tmp.txt"), "temp"); IVaultStorageService storage = new VaultStorageService(new VaultCryptoService()); storage.ClearDataDirectory(dataDir); Assert(Directory.Exists(dataDir), "Data directory should be recreated after cleanup."); Assert(!Directory.EnumerateFileSystemEntries(dataDir).Any(), "Data directory should be empty after cleanup."); } finally { if (Directory.Exists(root)) Directory.Delete(root, recursive: true); } return Task.CompletedTask; } static async Task TestEntryVaultLoadAllEmptyAsync() { var root = Path.Combine(Path.GetTempPath(), "journal-sidecar-smoke", Guid.NewGuid().ToString("N")); var vaultDir = Path.Combine(root, "vault"); var dataDir = Path.Combine(root, "data"); Directory.CreateDirectory(vaultDir); try { var entry = NewEntry(); var request = JsonSerializer.Serialize(new { action = "vault.load_all", payload = new { password = "vault-pass-123", vaultDirectory = vaultDir, dataDirectory = dataDir, } }); var response = await entry.HandleCommandAsync(request); using var doc = JsonDocument.Parse(response); Assert(doc.RootElement.GetProperty("ok").GetBoolean(), "Expected ok=true for empty vault directory load."); Assert(doc.RootElement.GetProperty("data").GetBoolean(), "Expected vault.load_all data=true for empty vault directory."); Assert(Directory.Exists(dataDir), "Expected data directory to be created by load workflow."); } finally { if (Directory.Exists(root)) Directory.Delete(root, recursive: true); } } static async Task TestEntryVaultClearDataDirectoryAsync() { var root = Path.Combine(Path.GetTempPath(), "journal-sidecar-smoke", Guid.NewGuid().ToString("N")); var dataDir = Path.Combine(root, "data"); Directory.CreateDirectory(dataDir); File.WriteAllText(Path.Combine(dataDir, "tmp.md"), "x"); try { var entry = NewEntry(); var request = JsonSerializer.Serialize(new { action = "vault.clear_data_directory", payload = new { dataDirectory = dataDir, } }); var response = await entry.HandleCommandAsync(request); using var doc = JsonDocument.Parse(response); Assert(doc.RootElement.GetProperty("ok").GetBoolean(), "Expected ok=true for clear_data_directory."); Assert(doc.RootElement.GetProperty("data").GetBoolean(), "Expected clear_data_directory result=true."); Assert(Directory.Exists(dataDir), "Expected data directory to exist after clear."); Assert(!Directory.EnumerateFileSystemEntries(dataDir).Any(), "Expected data directory to be empty after clear."); } finally { if (Directory.Exists(root)) Directory.Delete(root, recursive: true); } } static Task TestSidecarVaultCliLoadAsync() { var root = Path.Combine(Path.GetTempPath(), "journal-sidecar-cli-smoke", Guid.NewGuid().ToString("N")); var vaultDir = Path.Combine(root, "vault"); var dataDir = Path.Combine(root, "data"); Directory.CreateDirectory(vaultDir); try { var cli = new SidecarCli(new VaultStorageService(new VaultCryptoService()), new EntrySearchService(), new JournalConfigService()); var exitCode = cli.RunVaultCommand(["load", "--password", "vault-pass-123", "--vault-dir", vaultDir, "--data-dir", dataDir]); Assert(exitCode == 0, "Expected vault load CLI command to succeed on empty vault directory."); Assert(Directory.Exists(dataDir), "Expected data directory to be created by vault load CLI command."); } finally { if (Directory.Exists(root)) Directory.Delete(root, recursive: true); } return Task.CompletedTask; } static Task TestSidecarVaultCliSaveAsync() { var root = Path.Combine(Path.GetTempPath(), "journal-sidecar-cli-smoke", Guid.NewGuid().ToString("N")); var vaultDir = Path.Combine(root, "vault"); var dataDir = Path.Combine(root, "data"); Directory.CreateDirectory(vaultDir); Directory.CreateDirectory(dataDir); try { File.WriteAllText(Path.Combine(dataDir, "2026-02-22.md"), "entry body"); var cli = new SidecarCli(new VaultStorageService(new VaultCryptoService()), new EntrySearchService(), new JournalConfigService()); var exitCode = cli.RunVaultCommand(["save", "--password", "vault-pass-123", "--vault-dir", vaultDir, "--data-dir", dataDir]); Assert(exitCode == 0, "Expected vault save CLI command to succeed."); Assert(File.Exists(Path.Combine(vaultDir, "2026-02.vault")), "Expected monthly vault file to be written by save CLI command."); } finally { if (Directory.Exists(root)) Directory.Delete(root, recursive: true); } return Task.CompletedTask; } static Task TestVaultCustomEntryRoundtripAsync() { var root = Path.Combine(Path.GetTempPath(), "journal-vault-smoke", Guid.NewGuid().ToString("N")); var vaultDir = Path.Combine(root, "vault"); var dataDir = Path.Combine(root, "data"); Directory.CreateDirectory(vaultDir); Directory.CreateDirectory(dataDir); try { // Create both date-named and custom-named entries File.WriteAllText(Path.Combine(dataDir, "2026-02-01.md"), "date entry"); File.WriteAllText(Path.Combine(dataDir, "My Custom Entry.md"), "custom entry body"); File.WriteAllText(Path.Combine(dataDir, "Work Notes.md"), "work notes body"); // Rebuild vaults (simulates app close) IVaultStorageService storage = new VaultStorageService(new VaultCryptoService()); storage.RebuildAllVaults("vault-pass-123", vaultDir, dataDir); // Verify custom vault was created var customVaultPath = Path.Combine(vaultDir, "_custom_entries.vault"); Assert(File.Exists(customVaultPath), "Expected _custom_entries.vault to be created."); Assert(File.Exists(Path.Combine(vaultDir, "2026-02.vault")), "Expected monthly vault for date entry."); // Clear data directory (simulates app close step 2) storage.ClearDataDirectory(dataDir); Assert(!Directory.EnumerateFileSystemEntries(dataDir).Any(), "Data directory should be empty after clear."); // Load vaults (simulates app restart) var ok = storage.LoadAllVaults("vault-pass-123", vaultDir, dataDir); Assert(ok, "Expected vault load to succeed."); // Verify all entries are restored Assert(File.Exists(Path.Combine(dataDir, "2026-02-01.md")), "Date entry should be restored from vault."); Assert(File.Exists(Path.Combine(dataDir, "My Custom Entry.md")), "Custom entry should be restored from vault."); Assert(File.Exists(Path.Combine(dataDir, "Work Notes.md")), "Second custom entry should be restored from vault."); Assert(File.ReadAllText(Path.Combine(dataDir, "My Custom Entry.md")) == "custom entry body", "Custom entry content mismatch."); Assert(File.ReadAllText(Path.Combine(dataDir, "Work Notes.md")) == "work notes body", "Second custom entry content mismatch."); } finally { if (Directory.Exists(root)) Directory.Delete(root, recursive: true); } return Task.CompletedTask; } static async Task TestEntryVaultLoadWrongPasswordKeepsSessionLockedAsync() { var root = Path.Combine(Path.GetTempPath(), "journal-vault-smoke", Guid.NewGuid().ToString("N")); var vaultDir = Path.Combine(root, "vault"); var dataDir = Path.Combine(root, "data"); Directory.CreateDirectory(vaultDir); Directory.CreateDirectory(dataDir); try { var zipBytes = CreateZipBytes(new Dictionary { ["2026-02-01.md"] = "hello from vault" }); var crypto = new VaultCryptoService(); var encrypted = crypto.EncryptData(zipBytes, "vault-pass-123"); File.WriteAllBytes(Path.Combine(vaultDir, "2026-02.vault"), encrypted); var entry = NewEntry(); var loadRequest = JsonSerializer.Serialize(new { action = "vault.load_all", payload = new { password = "wrong-password", vaultDirectory = vaultDir, dataDirectory = dataDir } }); var loadResponse = await entry.HandleCommandAsync(loadRequest); using var loadDoc = JsonDocument.Parse(loadResponse); Assert(loadDoc.RootElement.GetProperty("ok").GetBoolean(), "Expected vault.load_all response envelope to be ok=true."); Assert(!loadDoc.RootElement.GetProperty("data").GetBoolean(), "Expected vault.load_all data=false with wrong password."); var listRequest = JsonSerializer.Serialize(new { action = "lists.list" }); var listResponse = await entry.HandleCommandAsync(listRequest); using var listDoc = JsonDocument.Parse(listResponse); Assert(!listDoc.RootElement.GetProperty("ok").GetBoolean(), "Expected lists.list to fail while locked."); var error = listDoc.RootElement.GetProperty("error").GetString() ?? ""; Assert(error.Contains("database is locked", StringComparison.OrdinalIgnoreCase), "Expected locked-session error after failed vault.load_all."); } finally { if (Directory.Exists(root)) Directory.Delete(root, recursive: true); } } static async Task TestEntryTemplateSaveAutoSyncsVaultAsync() { var root = Path.Combine(Path.GetTempPath(), "journal-entry-vault-sync-smoke", Guid.NewGuid().ToString("N")); var projectRoot = Path.Combine(root, "project"); var appDirectory = Path.Combine(projectRoot, "journal"); var vaultDir = Path.Combine(appDirectory, "vault"); var dataDir = Path.Combine(appDirectory, "data"); Directory.CreateDirectory(vaultDir); Directory.CreateDirectory(dataDir); var previousProjectRoot = Environment.GetEnvironmentVariable("JOURNAL_PROJECT_ROOT"); var previousDataDir = Environment.GetEnvironmentVariable("JOURNAL_DATA_DIR"); var previousVaultDir = Environment.GetEnvironmentVariable("JOURNAL_VAULT_DIR"); Environment.SetEnvironmentVariable("JOURNAL_PROJECT_ROOT", projectRoot); Environment.SetEnvironmentVariable("JOURNAL_DATA_DIR", dataDir); Environment.SetEnvironmentVariable("JOURNAL_VAULT_DIR", vaultDir); try { var entry = NewEntry(); var password = "vault-pass-123"; var unlockRequest = JsonSerializer.Serialize(new { action = "vault.load_all", payload = new { password, vaultDirectory = vaultDir, dataDirectory = dataDir } }); var unlockResponse = await entry.HandleCommandAsync(unlockRequest); using var unlockDoc = JsonDocument.Parse(unlockResponse); Assert(unlockDoc.RootElement.GetProperty("ok").GetBoolean(), "Expected vault.load_all envelope to succeed."); Assert(unlockDoc.RootElement.GetProperty("data").GetBoolean(), "Expected vault.load_all data=true for empty vault."); var saveTemplateRequest = JsonSerializer.Serialize(new { action = "templates.save", payload = new { name = "Weekly Review", content = "## Wins\n- shipped feature", dataDirectory = dataDir } }); var saveTemplateResponse = await entry.HandleCommandAsync(saveTemplateRequest); using var saveTemplateDoc = JsonDocument.Parse(saveTemplateResponse); Assert(saveTemplateDoc.RootElement.GetProperty("ok").GetBoolean(), "Expected templates.save to succeed."); var customVaultPath = Path.Combine(vaultDir, "_custom_entries.vault"); Assert(File.Exists(customVaultPath), "Expected template save to auto-sync custom entries vault."); var entries = ReadVaultEntryTexts(customVaultPath, password); Assert(entries.ContainsKey("Weekly Review.template.md"), "Expected template file in custom vault archive."); var clearRequest = JsonSerializer.Serialize(new { action = "vault.clear_data_directory", payload = new { dataDirectory = dataDir } }); var clearResponse = await entry.HandleCommandAsync(clearRequest); using var clearDoc = JsonDocument.Parse(clearResponse); Assert(clearDoc.RootElement.GetProperty("ok").GetBoolean(), "Expected vault.clear_data_directory to succeed."); var reloadResponse = await entry.HandleCommandAsync(unlockRequest); using var reloadDoc = JsonDocument.Parse(reloadResponse); Assert(reloadDoc.RootElement.GetProperty("ok").GetBoolean(), "Expected second vault.load_all envelope to succeed."); Assert(reloadDoc.RootElement.GetProperty("data").GetBoolean(), "Expected second vault.load_all data=true."); var restoredTemplatePath = Path.Combine(dataDir, "Weekly Review.template.md"); Assert(File.Exists(restoredTemplatePath), "Expected template to be restored from vault after reload."); } finally { Environment.SetEnvironmentVariable("JOURNAL_PROJECT_ROOT", previousProjectRoot); Environment.SetEnvironmentVariable("JOURNAL_DATA_DIR", previousDataDir); Environment.SetEnvironmentVariable("JOURNAL_VAULT_DIR", previousVaultDir); if (Directory.Exists(root)) Directory.Delete(root, recursive: true); } } }