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() { var root = Path.Combine(Path.GetTempPath(), "journal-vault-smoke", Guid.NewGuid().ToString("N")); var config = NewConfigService(root); var dbService = new JournalDatabaseService(config); var storage = new VaultStorageService(new VaultCryptoService(), dbService); try { var dbPath = dbService.GetDatabasePath(); Directory.CreateDirectory(Path.GetDirectoryName(dbPath)!); File.WriteAllText(dbPath, "db-bytes"); storage.RebuildAllVaults("vault-pass-123", config.Current.VaultDirectory, Path.GetDirectoryName(dbPath)!); var expectedName = $"_db_{Path.GetFileName(dbPath)}.vault"; Assert(File.Exists(Path.Combine(config.Current.VaultDirectory, expectedName)), "Expected DB vault snapshot filename with _db_ prefix and .vault suffix."); } finally { if (Directory.Exists(root)) Directory.Delete(root, recursive: true); } return Task.CompletedTask; } static Task TestVaultLoadClearsAndExtractsAsync() { var root = Path.Combine(Path.GetTempPath(), "journal-vault-smoke", Guid.NewGuid().ToString("N")); var config = NewConfigService(root); var dbService = new JournalDatabaseService(config); var storage = new VaultStorageService(new VaultCryptoService(), dbService); try { var dbPath = dbService.GetDatabasePath(); Directory.CreateDirectory(Path.GetDirectoryName(dbPath)!); var originalBytes = Guid.NewGuid().ToByteArray(); File.WriteAllBytes(dbPath, originalBytes); storage.RebuildAllVaults("vault-pass-123", config.Current.VaultDirectory, Path.GetDirectoryName(dbPath)!); File.WriteAllBytes(dbPath, [1, 2, 3, 4]); var ok = storage.LoadAllVaults("vault-pass-123", config.Current.VaultDirectory, Path.GetDirectoryName(dbPath)!); Assert(ok, "Expected vault load success with correct password."); var loaded = File.ReadAllBytes(dbPath); Assert(loaded.SequenceEqual(originalBytes), "Expected DB file bytes restored from vault snapshot."); } 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 config = NewConfigService(root); var dbService = new JournalDatabaseService(config); var storage = new VaultStorageService(new VaultCryptoService(), dbService); try { var dbPath = dbService.GetDatabasePath(); Directory.CreateDirectory(Path.GetDirectoryName(dbPath)!); File.WriteAllText(dbPath, "db payload"); storage.RebuildAllVaults("vault-pass-123", config.Current.VaultDirectory, Path.GetDirectoryName(dbPath)!); var vaultPath = Path.Combine(config.Current.VaultDirectory, $"_db_{Path.GetFileName(dbPath)}.vault"); var before = File.ReadAllBytes(vaultPath); var ok = storage.LoadAllVaults("wrong-password", config.Current.VaultDirectory, Path.GetDirectoryName(dbPath)!); 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 config = NewConfigService(root); var dbService = new JournalDatabaseService(config); var storage = new VaultStorageService(new VaultCryptoService(), dbService); try { var legacyPath = Path.Combine(config.Current.VaultDirectory, "_init_vault.vault"); File.WriteAllBytes(legacyPath, [1, 2, 3, 4]); var ok = storage.LoadAllVaults("vault-pass-123", config.Current.VaultDirectory, Path.GetDirectoryName(dbService.GetDatabasePath())!); Assert(ok, "Legacy-only vault directory should still be treated as successful load state."); Assert(File.Exists(legacyPath), "Legacy _init_vault.vault should be ignored in SQLCipher snapshot mode."); } 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 config = NewConfigService(root); var dbService = new JournalDatabaseService(config); var storage = new VaultStorageService(new VaultCryptoService(), dbService); try { var dbPath = dbService.GetDatabasePath(); Directory.CreateDirectory(Path.GetDirectoryName(dbPath)!); File.WriteAllText(dbPath, "initial db"); storage.RebuildAllVaults("vault-pass-123", config.Current.VaultDirectory, Path.GetDirectoryName(dbPath)!); var vaultPath = Path.Combine(config.Current.VaultDirectory, $"_db_{Path.GetFileName(dbPath)}.vault"); Assert(File.Exists(vaultPath), "Expected DB vault snapshot file to be created."); var firstLength = new FileInfo(vaultPath).Length; storage.RebuildAllVaults("vault-pass-123", config.Current.VaultDirectory, Path.GetDirectoryName(dbPath)!); var secondLength = new FileInfo(vaultPath).Length; Assert(firstLength > 0 && secondLength > 0, "Vault snapshot should remain non-empty across repeated rebuilds."); } 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 config = NewConfigService(root); var dbService = new JournalDatabaseService(config); var storage = new VaultStorageService(new VaultCryptoService(), dbService); try { var dbDir = Path.GetDirectoryName(dbService.GetDatabasePath())!; Directory.CreateDirectory(dbDir); File.WriteAllText(Path.Combine(dbDir, "journal_cache.db"), "primary"); File.WriteAllText(Path.Combine(dbDir, "analytics.db"), "secondary"); storage.RebuildAllVaults("vault-pass-123", config.Current.VaultDirectory, dbDir); Assert(File.Exists(Path.Combine(config.Current.VaultDirectory, "_db_journal_cache.db.vault")), "Expected primary DB snapshot vault."); Assert(File.Exists(Path.Combine(config.Current.VaultDirectory, "_db_analytics.db.vault")), "Expected secondary DB snapshot vault."); } 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 config = NewConfigService(root); var dbService = new JournalDatabaseService(config); var storage = new VaultStorageService(new VaultCryptoService(), dbService); var scratchDir = Path.Combine(root, "scratch-data"); Directory.CreateDirectory(scratchDir); Directory.CreateDirectory(Path.Combine(scratchDir, "nested")); try { File.WriteAllText(Path.Combine(scratchDir, "tmp.md"), "decrypted content"); File.WriteAllText(Path.Combine(scratchDir, "nested", "tmp.txt"), "temp"); storage.ClearDataDirectory(scratchDir); Assert(!Directory.Exists(scratchDir), "Non-db scratch directory should be deleted by clear_data_directory."); } 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")); Directory.CreateDirectory(root); try { var entry = NewLockedEntry(); var request = JsonSerializer.Serialize(new { action = "vault.load_all", payload = new { password = "vault-pass-123", vaultDirectory = Path.Combine(root, "vault") } }); Directory.CreateDirectory(Path.Combine(root, "vault")); 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."); } finally { if (Directory.Exists(root)) Directory.Delete(root, recursive: true); } } static async Task TestEntryVaultClearDataDirectoryAsync() { var entry = NewEntry(); var request = JsonSerializer.Serialize(new { action = "vault.clear_data_directory", payload = new { } }); 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."); } static Task TestSidecarVaultCliLoadAsync() { var root = Path.Combine(Path.GetTempPath(), "journal-sidecar-cli-smoke", Guid.NewGuid().ToString("N")); var config = NewConfigService(root); var dbService = new JournalDatabaseService(config); using var session = new DatabaseSessionService(dbService); session.SetPassword("vault-pass-123"); var repo = new SqliteEntryFileRepository(session); var entryFiles = new EntryFileService(repo); var searchService = new EntrySearchService(repo); var cli = new SidecarCli(new VaultStorageService(new VaultCryptoService(), dbService), searchService, config, entryFiles); var vaultDir = Path.Combine(root, "vault-cli"); Directory.CreateDirectory(vaultDir); try { var exitCode = cli.RunVaultCommand(["load", "--password", "vault-pass-123", "--vault-dir", vaultDir]); Assert(exitCode == 0, "Expected vault load CLI command to succeed on empty vault directory."); var dbDir = Path.Combine(config.Current.VaultDirectory, "db"); Assert(Directory.Exists(dbDir), "Expected db 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 config = NewConfigService(root); var dbService = new JournalDatabaseService(config); using var session = new DatabaseSessionService(dbService); session.SetPassword("vault-pass-123"); var repo = new SqliteEntryFileRepository(session); var entryFiles = new EntryFileService(repo); var searchService = new EntrySearchService(repo); var cli = new SidecarCli(new VaultStorageService(new VaultCryptoService(), dbService), searchService, config, entryFiles); var vaultDir = Path.Combine(root, "vault-cli"); Directory.CreateDirectory(vaultDir); try { entryFiles.SaveEntry(new EntrySavePayload("entry body", Mode: "Overwrite", FileName: "2026-02-22")); session.CloseConnection(); var exitCode = cli.RunVaultCommand(["save", "--password", "vault-pass-123", "--vault-dir", vaultDir]); Assert(exitCode == 0, "Expected vault save CLI command to succeed."); Assert(File.Exists(Path.Combine(vaultDir, "_db_journal_cache.db.vault")), "Expected DB vault snapshot 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 config = NewConfigService(root); var dbService = new JournalDatabaseService(config); var storage = new VaultStorageService(new VaultCryptoService(), dbService); try { using (var seedSession = new DatabaseSessionService(dbService)) { seedSession.SetPassword("vault-pass-123"); var seedRepo = new SqliteEntryFileRepository(seedSession); var seedEntryFiles = new EntryFileService(seedRepo); seedEntryFiles.SaveEntry(new EntrySavePayload("date entry", Mode: "Overwrite", FileName: "2026-02-01")); seedEntryFiles.SaveEntry(new EntrySavePayload("custom entry body", Mode: "Overwrite", FileName: "My Custom Entry")); seedEntryFiles.SaveEntry(new EntrySavePayload("work notes body", Mode: "Overwrite", FileName: "Work Notes")); seedSession.CloseConnection(); } var dbPath = dbService.GetDatabasePath(); var dbDir = Path.GetDirectoryName(dbPath)!; storage.RebuildAllVaults("vault-pass-123", config.Current.VaultDirectory, dbDir); Assert(File.Exists(Path.Combine(config.Current.VaultDirectory, "_db_journal_cache.db.vault")), "Expected DB vault snapshot to be created."); File.Delete(dbPath); Assert(!File.Exists(dbPath), "DB file should be deleted before restore."); var ok = storage.LoadAllVaults("vault-pass-123", config.Current.VaultDirectory, dbDir); Assert(ok, "Expected vault load to succeed."); using (var verifySession = new DatabaseSessionService(dbService)) { verifySession.SetPassword("vault-pass-123"); var verifyRepo = new SqliteEntryFileRepository(verifySession); var verifyEntryFiles = new EntryFileService(verifyRepo); var allEntries = verifyEntryFiles.ListEntries(); Assert(allEntries.Any(e => e.FileName == "2026-02-01.md"), "Date entry should be restored from vault DB snapshot."); Assert(allEntries.Any(e => e.FileName == "My Custom Entry.md"), "Custom entry should be restored from vault DB snapshot."); Assert(allEntries.Any(e => e.FileName == "Work Notes.md"), "Second custom entry should be restored from vault DB snapshot."); } } 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 config = NewConfigService(root); var dbService = new JournalDatabaseService(config); var vaultStorage = new VaultStorageService(new VaultCryptoService(), dbService); var session = new DatabaseSessionService(dbService); var repo = new SqliteEntryFileRepository(session); var entryFiles = new EntryFileService(repo); var entrySearch = new EntrySearchService(repo); var entry = new Entry( NewService(), entrySearch, vaultStorage, dbService, session, config, new DisabledAiService("none"), new DisabledSpeechBridgeService("none"), new DisabledS2TService(), entryFiles, new ListService(new SqliteListRepository(session)), new TodoService(new SqliteTodoRepository(session)), new DisabledCoachService(), new ConversationService(new SqliteConversationRepository(session)), new CommandLogger()); try { var dbDir = Path.GetDirectoryName(dbService.GetDatabasePath())!; Directory.CreateDirectory(dbDir); File.WriteAllBytes(dbService.GetDatabasePath(), Guid.NewGuid().ToByteArray()); vaultStorage.RebuildAllVaults("vault-pass-123", config.Current.VaultDirectory, dbDir); var loadRequest = JsonSerializer.Serialize(new { action = "vault.load_all", payload = new { password = "wrong-password", vaultDirectory = config.Current.VaultDirectory } }); 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 entry = NewEntry(root: root); var configResponse = await entry.HandleCommandAsync("""{"action":"config.get"}"""); using var configDoc = JsonDocument.Parse(configResponse); var configData = configDoc.RootElement.GetProperty("data"); var vaultDir = configData.GetProperty("VaultDirectory").GetString() ?? ""; var dbFilename = configData.GetProperty("DatabaseFilename").GetString() ?? "journal_cache.db"; var saveTemplateRequest = JsonSerializer.Serialize(new { action = "templates.save", payload = new { name = "Weekly Review", content = "## Wins\n- shipped feature" } }); var saveTemplateResponse = await entry.HandleCommandAsync(saveTemplateRequest); using var saveTemplateDoc = JsonDocument.Parse(saveTemplateResponse); Assert(saveTemplateDoc.RootElement.GetProperty("ok").GetBoolean(), "Expected templates.save to succeed."); var dbVaultPath = Path.Combine(vaultDir, $"_db_{dbFilename}.vault"); Assert(File.Exists(dbVaultPath), "Expected template save auto-sync to write DB vault snapshot."); if (Directory.Exists(root)) Directory.Delete(root, recursive: true); } }