104 lines
7.9 KiB
C#
104 lines
7.9 KiB
C#
internal static partial class Program
|
|
{
|
|
private static async Task<int> Main()
|
|
{
|
|
var tests = new List<(string Name, Func<Task> Run)>
|
|
{
|
|
("CreateAsync trims fields", TestCreateTrimsAsync),
|
|
("UpdateAsync accepts valid type updates", TestUpdateAcceptsTypeAsync),
|
|
("UpdateAsync rejects whitespace type", TestUpdateRejectsWhitespaceTypeAsync),
|
|
("JournalEntry model stores parity fields", TestJournalEntryModelAsync),
|
|
("MergeWith overwrites section when new content is meaningful", TestMergeOverwritesMeaningfulSectionAsync),
|
|
("MergeWith ignores whitespace-only section updates", TestMergeIgnoresWhitespaceOnlySectionAsync),
|
|
("MergeWith appends non-duplicate fragments by description", TestMergeAppendsNonDuplicateFragmentsAsync),
|
|
("ToMarkdown writes canonical section order", TestToMarkdownCanonicalSectionOrderAsync),
|
|
("ToMarkdown writes fragment blocks", TestToMarkdownFragmentFormattingAsync),
|
|
("Vault crypto roundtrip preserves data and layout", TestVaultCryptoRoundtripAsync),
|
|
("Vault crypto decrypts Python payload fixture", TestVaultCryptoDecryptsPythonFixtureAsync),
|
|
("Vault key derivation matches Python fixture", TestVaultKeyDerivationMatchesPythonAsync),
|
|
("Vault monthly filename matches parity format", TestVaultMonthlyFilenameParityAsync),
|
|
("Vault load clears workspace and extracts decrypted files", TestVaultLoadClearsAndExtractsAsync),
|
|
("Vault load wrong password does not modify vault files", TestVaultLoadWrongPasswordPreservesVaultAsync),
|
|
("Vault load ignores and removes legacy _init_vault.vault", TestVaultLoadLegacyInitVaultHandlingAsync),
|
|
("Vault current-month save writes only current month and skips unchanged state", TestVaultCurrentMonthSaveOptimizedAsync),
|
|
("Vault rebuild saves grouped monthly archives from decrypted files", TestVaultRebuildAllVaultsAsync),
|
|
("Vault clear data directory removes decrypted workspace artifacts", TestVaultClearDataDirectoryAsync),
|
|
("Parser extracts date from **Date:** marker", TestParserExtractsBoldDateAsync),
|
|
("Parser extracts date from Date: marker", TestParserExtractsPlainDateAsync),
|
|
("Parser falls back to file stem when date missing", TestParserFallsBackToFileStemAsync),
|
|
("Parser captures canonical sections and content", TestParserCapturesSectionsAsync),
|
|
("Parser ignores non-canonical section headers", TestParserIgnoresNonCanonicalHeadersAsync),
|
|
("Parser captures checkbox states per section", TestParserCapturesCheckboxStatesAsync),
|
|
("Parser captures multiline fragment blocks", TestParserCapturesMultilineFragmentsAsync),
|
|
("Parser fragment boundary follows header lines", TestParserFragmentBoundaryBehaviorAsync),
|
|
("File repository persists fragments", TestFileRepositoryPersistsAsync),
|
|
("Entry invalid JSON returns error envelope", TestEntryInvalidJsonAsync),
|
|
("Entry unknown action returns error envelope", TestEntryUnknownActionAsync),
|
|
("Entry get missing id returns ok with null data", TestEntryGetMissingReturnsNullDataAsync),
|
|
("Entry create without payload returns error envelope", TestEntryCreateMissingPayloadAsync),
|
|
("Entry entries.save writes and merges content", TestEntryEntriesSaveMergeAsync),
|
|
("Entry entries.load returns raw content payload", TestEntryEntriesLoadAsync),
|
|
("Entry entries.list returns markdown files", TestEntryEntriesListAsync),
|
|
("Entry templates CRUD stores .template.md and entries.list excludes templates", TestEntryTemplatesCrudExcludesFromEntriesListAsync),
|
|
("Entry search.entries matches query against full raw content", TestEntrySearchEntriesMatchesRawContentAsync),
|
|
("Entry search.entries without query returns all markdown entries", TestEntrySearchEntriesWithoutQueryReturnsAllAsync),
|
|
("Entry search.entries applies date range filter", TestEntrySearchEntriesDateRangeFilterAsync),
|
|
("Entry search.entries applies section-scoped query filter", TestEntrySearchEntriesSectionFilterAsync),
|
|
("Entry search.entries applies fragment tag and type filters", TestEntrySearchEntriesTagTypeFilterAsync),
|
|
("Entry search.entries applies checkbox checked and unchecked filters", TestEntrySearchEntriesCheckboxFilterAsync),
|
|
("Entry search.entries rejects invalid date filter format", TestEntrySearchEntriesRejectsInvalidDateAsync),
|
|
("Database key derivation matches Python fixture", TestDatabaseKeyDerivationMatchesPythonAsync),
|
|
("Database schema parity tables are created", TestDatabaseSchemaParityAsync),
|
|
("Entry db.status returns database compatibility payload", TestEntryDatabaseStatusAsync),
|
|
("Entry db.initialize_schema creates schema in data directory", TestEntryDatabaseInitializeSchemaAsync),
|
|
("Entry db.hydrate_workspace returns hydration metadata", TestEntryDatabaseHydrateWorkspaceAsync),
|
|
("Config service exposes parity path, vault, AI, and speech settings", TestConfigServiceParityKeysAsync),
|
|
("Entry config.get returns config payload", TestEntryConfigGetAsync),
|
|
("Log redactor scrubs sensitive payload fields", TestLogRedactorScrubsSensitiveFieldsAsync),
|
|
("Log redactor preserves non-sensitive payload fields", TestLogRedactorPreservesNonSensitiveFieldsAsync),
|
|
("Entry ai.health returns disabled by default", TestEntryAiHealthDefaultAsync),
|
|
("Entry ai.summarize_entry succeeds when disabled", TestEntryAiSummarizeEntryDisabledAsync),
|
|
("Entry ai.summarize_all succeeds when disabled", TestEntryAiSummarizeAllDisabledAsync),
|
|
("Entry ai.chat succeeds when disabled", TestEntryAiChatDisabledAsync),
|
|
("Entry ai.embed returns empty vector when disabled", TestEntryAiEmbedDisabledAsync),
|
|
("Entry speech.devices.list returns envelope when disabled", TestEntrySpeechDevicesListDisabledAsync),
|
|
("Entry speech.transcribe returns envelope when disabled", TestEntrySpeechTranscribeDisabledAsync),
|
|
("Python sidecar AI service parses last JSON line", TestPythonSidecarAiServiceJsonLineAsync),
|
|
("Python sidecar AI service surfaces sidecar errors", TestPythonSidecarAiServiceErrorAsync),
|
|
("Python sidecar speech service handles empty devices payload", TestPythonSidecarSpeechServiceNoDevicesAsync),
|
|
("Python sidecar speech service surfaces unavailable engine errors", TestPythonSidecarSpeechServiceErrorAsync),
|
|
("Python sidecar speech service times out deterministically", TestPythonSidecarSpeechServiceTimeoutAsync),
|
|
("Entry vault.load_all succeeds for empty vault directory", TestEntryVaultLoadAllEmptyAsync),
|
|
("Entry vault.load_all wrong password keeps database session locked", TestEntryVaultLoadWrongPasswordKeepsSessionLockedAsync),
|
|
("Entry vault.clear_data_directory removes files", TestEntryVaultClearDataDirectoryAsync),
|
|
("Entry templates.save auto-syncs vault and survives reload", TestEntryTemplateSaveAutoSyncsVaultAsync),
|
|
("Sidecar vault CLI load succeeds with --password", TestSidecarVaultCliLoadAsync),
|
|
("Sidecar vault CLI save writes monthly vault with --password", TestSidecarVaultCliSaveAsync),
|
|
("Sidecar search CLI returns matching entries with filters", TestSidecarSearchCliFilteredAsync),
|
|
("Sidecar search CLI warns when no decrypted entries exist", TestSidecarSearchCliEmptyDataAsync),
|
|
("Transport fixtures produce stable envelopes", TestTransportFixturesAsync),
|
|
("EntrySavePayload deserializes camelCase fileName from JsonElement", TestEntrySavePayloadFileNameDeserializationAsync),
|
|
("entries.save with fileName creates custom-named file", TestEntrySaveWithFileNameAsync),
|
|
("Vault rebuild and load preserves custom-named entries", TestVaultCustomEntryRoundtripAsync),
|
|
};
|
|
|
|
var passed = 0;
|
|
foreach (var (name, run) in tests)
|
|
{
|
|
try
|
|
{
|
|
await run();
|
|
Console.WriteLine($"PASS {name}");
|
|
passed++;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"FAIL {name}: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
Console.WriteLine($"Summary: {passed}/{tests.Count} passed.");
|
|
return passed == tests.Count ? 0 : 1;
|
|
}
|
|
}
|