658 lines
27 KiB
C#
658 lines
27 KiB
C#
internal static partial class Program
|
|
{
|
|
static async Task TestEntryUnknownActionAsync()
|
|
{
|
|
var entry = NewEntry();
|
|
var response = await entry.HandleCommandAsync("""{"action":"unknown.action"}""");
|
|
using var doc = JsonDocument.Parse(response);
|
|
|
|
Assert(!doc.RootElement.GetProperty("ok").GetBoolean(), "Expected ok=false.");
|
|
Assert(doc.RootElement.GetProperty("error").GetString()!.Contains("Unknown action"), "Expected unknown action error.");
|
|
}
|
|
|
|
static async Task TestEntryInvalidJsonAsync()
|
|
{
|
|
var entry = NewEntry();
|
|
var response = await entry.HandleCommandAsync("{\"action\":\"fragments.list\"");
|
|
using var doc = JsonDocument.Parse(response);
|
|
|
|
Assert(!doc.RootElement.GetProperty("ok").GetBoolean(), "Expected ok=false.");
|
|
Assert(doc.RootElement.GetProperty("error").GetString()!.Contains("Invalid command JSON"), "Expected invalid JSON error.");
|
|
}
|
|
|
|
static async Task TestEntryGetMissingReturnsNullDataAsync()
|
|
{
|
|
var entry = NewEntry();
|
|
var request = JsonSerializer.Serialize(new
|
|
{
|
|
action = "fragments.get",
|
|
id = Guid.NewGuid().ToString(),
|
|
});
|
|
var response = await entry.HandleCommandAsync(request);
|
|
using var doc = JsonDocument.Parse(response);
|
|
|
|
Assert(doc.RootElement.GetProperty("ok").GetBoolean(), "Expected ok=true.");
|
|
Assert(doc.RootElement.GetProperty("data").ValueKind == JsonValueKind.Null, "Expected data=null for missing fragment.");
|
|
}
|
|
|
|
static async Task TestEntryCreateMissingPayloadAsync()
|
|
{
|
|
var entry = NewEntry();
|
|
var response = await entry.HandleCommandAsync("""{"action":"fragments.create"}""");
|
|
using var doc = JsonDocument.Parse(response);
|
|
|
|
Assert(!doc.RootElement.GetProperty("ok").GetBoolean(), "Expected ok=false.");
|
|
Assert(doc.RootElement.GetProperty("error").GetString()!.Contains("payload", StringComparison.OrdinalIgnoreCase), "Expected payload validation error.");
|
|
}
|
|
|
|
static async Task TestEntryEntriesSaveMergeAsync()
|
|
{
|
|
var root = Path.Combine(Path.GetTempPath(), "journal-entry-smoke", Guid.NewGuid().ToString("N"));
|
|
Directory.CreateDirectory(root);
|
|
|
|
try
|
|
{
|
|
var filePath = Path.Combine(root, "2026-02-22.md");
|
|
File.WriteAllText(filePath, """
|
|
Date: 2026-02-22
|
|
## Summary
|
|
old summary text
|
|
""");
|
|
|
|
var entry = NewEntry();
|
|
var request = JsonSerializer.Serialize(new
|
|
{
|
|
action = "entries.save",
|
|
payload = new
|
|
{
|
|
filePath,
|
|
mode = "Daily",
|
|
content = """
|
|
Date: 2026-02-22
|
|
## Summary
|
|
new summary text
|
|
## Reflection
|
|
new reflection text
|
|
"""
|
|
}
|
|
});
|
|
|
|
var response = await entry.HandleCommandAsync(request);
|
|
using var doc = JsonDocument.Parse(response);
|
|
Assert(doc.RootElement.GetProperty("ok").GetBoolean(), "Expected ok=true for entries.save.");
|
|
|
|
var saved = File.ReadAllText(filePath);
|
|
Assert(saved.Contains("new summary text", StringComparison.Ordinal), "Expected merged file to contain new summary text.");
|
|
Assert(!saved.Contains("old summary text", StringComparison.Ordinal), "Expected merged file to replace old summary section.");
|
|
Assert(saved.Contains("new reflection text", StringComparison.Ordinal), "Expected merged file to contain new reflection section.");
|
|
|
|
var fragmentSaveRequest = JsonSerializer.Serialize(new
|
|
{
|
|
action = "entries.save",
|
|
payload = new
|
|
{
|
|
filePath,
|
|
mode = "Fragment",
|
|
content = "!NOTE\nfragment append text"
|
|
}
|
|
});
|
|
|
|
var fragmentResponse = await entry.HandleCommandAsync(fragmentSaveRequest);
|
|
using var fragmentDoc = JsonDocument.Parse(fragmentResponse);
|
|
Assert(fragmentDoc.RootElement.GetProperty("ok").GetBoolean(), "Expected ok=true for entries.save fragment mode.");
|
|
var appended = File.ReadAllText(filePath);
|
|
Assert(appended.Contains("fragment append text", StringComparison.Ordinal), "Expected fragment append text in saved file.");
|
|
}
|
|
finally
|
|
{
|
|
if (Directory.Exists(root))
|
|
Directory.Delete(root, recursive: true);
|
|
}
|
|
}
|
|
|
|
static async Task TestEntryEntriesLoadAsync()
|
|
{
|
|
var root = Path.Combine(Path.GetTempPath(), "journal-entry-smoke", Guid.NewGuid().ToString("N"));
|
|
Directory.CreateDirectory(root);
|
|
|
|
try
|
|
{
|
|
var filePath = Path.Combine(root, "2026-02-22.md");
|
|
var content = """
|
|
Date: 2026-02-22
|
|
## Summary
|
|
hello world
|
|
""";
|
|
File.WriteAllText(filePath, content);
|
|
|
|
var entry = NewEntry();
|
|
var request = JsonSerializer.Serialize(new
|
|
{
|
|
action = "entries.load",
|
|
payload = new
|
|
{
|
|
filePath
|
|
}
|
|
});
|
|
|
|
var response = await entry.HandleCommandAsync(request);
|
|
using var doc = JsonDocument.Parse(response);
|
|
Assert(doc.RootElement.GetProperty("ok").GetBoolean(), "Expected ok=true for entries.load.");
|
|
|
|
var data = doc.RootElement.GetProperty("data");
|
|
var entryDto = data.GetProperty("Entry");
|
|
Assert(entryDto.GetProperty("Date").GetString() == "2026-02-22", "Expected parsed date from entries.load.");
|
|
Assert(entryDto.GetProperty("RawContent").GetString() == content, "Expected raw content from entries.load.");
|
|
Assert(data.GetProperty("FileName").GetString() == "2026-02-22.md", "Expected file name from entries.load.");
|
|
}
|
|
finally
|
|
{
|
|
if (Directory.Exists(root))
|
|
Directory.Delete(root, recursive: true);
|
|
}
|
|
}
|
|
|
|
static async Task TestEntryEntriesListAsync()
|
|
{
|
|
var root = Path.Combine(Path.GetTempPath(), "journal-entry-smoke", Guid.NewGuid().ToString("N"));
|
|
Directory.CreateDirectory(root);
|
|
|
|
try
|
|
{
|
|
File.WriteAllText(Path.Combine(root, "2026-02-03.md"), "c");
|
|
File.WriteAllText(Path.Combine(root, "2026-02-01.md"), "a");
|
|
File.WriteAllText(Path.Combine(root, "ignore.txt"), "x");
|
|
|
|
var entry = NewEntry();
|
|
var request = JsonSerializer.Serialize(new
|
|
{
|
|
action = "entries.list",
|
|
payload = new
|
|
{
|
|
dataDirectory = root
|
|
}
|
|
});
|
|
|
|
var response = await entry.HandleCommandAsync(request);
|
|
using var doc = JsonDocument.Parse(response);
|
|
Assert(doc.RootElement.GetProperty("ok").GetBoolean(), "Expected ok=true for entries.list.");
|
|
|
|
var data = doc.RootElement.GetProperty("data");
|
|
Assert(data.ValueKind == JsonValueKind.Array, "Expected entries.list data array.");
|
|
Assert(data.GetArrayLength() == 2, "Expected entries.list to return only markdown files.");
|
|
Assert(data[0].GetProperty("FileName").GetString() == "2026-02-01.md", "Expected entries.list sort order by file name.");
|
|
Assert(data[1].GetProperty("FileName").GetString() == "2026-02-03.md", "Expected entries.list sort order by file name.");
|
|
}
|
|
finally
|
|
{
|
|
if (Directory.Exists(root))
|
|
Directory.Delete(root, recursive: true);
|
|
}
|
|
}
|
|
|
|
static async Task TestEntryTemplatesCrudExcludesFromEntriesListAsync()
|
|
{
|
|
var root = Path.Combine(Path.GetTempPath(), "journal-template-smoke", Guid.NewGuid().ToString("N"));
|
|
Directory.CreateDirectory(root);
|
|
|
|
try
|
|
{
|
|
File.WriteAllText(Path.Combine(root, "2026-02-03.md"), "daily entry");
|
|
|
|
var entry = NewEntry();
|
|
var saveRequest = JsonSerializer.Serialize(new
|
|
{
|
|
action = "templates.save",
|
|
payload = new
|
|
{
|
|
name = "Weekly Review",
|
|
content = "# Weekly Review\n\n## Wins\n- one",
|
|
dataDirectory = root
|
|
}
|
|
});
|
|
var saveResponse = await entry.HandleCommandAsync(saveRequest);
|
|
using var saveDoc = JsonDocument.Parse(saveResponse);
|
|
Assert(saveDoc.RootElement.GetProperty("ok").GetBoolean(), "Expected ok=true for templates.save.");
|
|
var templatePath = saveDoc.RootElement.GetProperty("data").GetProperty("FilePath").GetString() ?? "";
|
|
Assert(templatePath.EndsWith(".template.md", StringComparison.OrdinalIgnoreCase), "Template file should end with .template.md.");
|
|
Assert(File.Exists(templatePath), "Template file should exist.");
|
|
|
|
var listTemplatesRequest = JsonSerializer.Serialize(new
|
|
{
|
|
action = "templates.list",
|
|
payload = new
|
|
{
|
|
dataDirectory = root
|
|
}
|
|
});
|
|
var listTemplatesResponse = await entry.HandleCommandAsync(listTemplatesRequest);
|
|
using var listTemplatesDoc = JsonDocument.Parse(listTemplatesResponse);
|
|
Assert(listTemplatesDoc.RootElement.GetProperty("ok").GetBoolean(), "Expected ok=true for templates.list.");
|
|
var templateItems = listTemplatesDoc.RootElement.GetProperty("data");
|
|
Assert(templateItems.GetArrayLength() == 1, "Expected one template in templates.list.");
|
|
Assert(templateItems[0].GetProperty("FileName").GetString() == "Weekly Review.template.md", "Template file name mismatch.");
|
|
|
|
var listEntriesRequest = JsonSerializer.Serialize(new
|
|
{
|
|
action = "entries.list",
|
|
payload = new
|
|
{
|
|
dataDirectory = root
|
|
}
|
|
});
|
|
var listEntriesResponse = await entry.HandleCommandAsync(listEntriesRequest);
|
|
using var listEntriesDoc = JsonDocument.Parse(listEntriesResponse);
|
|
Assert(listEntriesDoc.RootElement.GetProperty("ok").GetBoolean(), "Expected ok=true for entries.list.");
|
|
var entryItems = listEntriesDoc.RootElement.GetProperty("data");
|
|
Assert(entryItems.GetArrayLength() == 1, "Expected entries.list to exclude template files.");
|
|
Assert(entryItems[0].GetProperty("FileName").GetString() == "2026-02-03.md", "Expected only daily entry file in entries.list.");
|
|
|
|
var loadTemplateRequest = JsonSerializer.Serialize(new
|
|
{
|
|
action = "templates.load",
|
|
payload = new
|
|
{
|
|
filePath = templatePath
|
|
}
|
|
});
|
|
var loadTemplateResponse = await entry.HandleCommandAsync(loadTemplateRequest);
|
|
using var loadTemplateDoc = JsonDocument.Parse(loadTemplateResponse);
|
|
Assert(loadTemplateDoc.RootElement.GetProperty("ok").GetBoolean(), "Expected ok=true for templates.load.");
|
|
var content = loadTemplateDoc.RootElement.GetProperty("data").GetProperty("Content").GetString() ?? "";
|
|
Assert(content.Contains("## Wins", StringComparison.Ordinal), "Expected template content in templates.load result.");
|
|
|
|
var deleteTemplateRequest = JsonSerializer.Serialize(new
|
|
{
|
|
action = "templates.delete",
|
|
payload = new
|
|
{
|
|
filePath = templatePath
|
|
}
|
|
});
|
|
var deleteTemplateResponse = await entry.HandleCommandAsync(deleteTemplateRequest);
|
|
using var deleteTemplateDoc = JsonDocument.Parse(deleteTemplateResponse);
|
|
Assert(deleteTemplateDoc.RootElement.GetProperty("ok").GetBoolean(), "Expected ok=true for templates.delete.");
|
|
Assert(deleteTemplateDoc.RootElement.GetProperty("data").GetBoolean(), "Expected templates.delete to return true.");
|
|
Assert(!File.Exists(templatePath), "Template file should be deleted.");
|
|
}
|
|
finally
|
|
{
|
|
if (Directory.Exists(root))
|
|
Directory.Delete(root, recursive: true);
|
|
}
|
|
}
|
|
|
|
static async Task TestEntrySearchEntriesMatchesRawContentAsync()
|
|
{
|
|
var root = Path.Combine(Path.GetTempPath(), "journal-search-smoke", Guid.NewGuid().ToString("N"));
|
|
Directory.CreateDirectory(root);
|
|
|
|
try
|
|
{
|
|
File.WriteAllText(Path.Combine(root, "2026-02-01.md"), "## Summary\nAlpha line\ncommon token");
|
|
File.WriteAllText(Path.Combine(root, "2026-02-02.md"), "## Summary\nbeta line\nCOMMON token");
|
|
File.WriteAllText(Path.Combine(root, "2026-02-03.md"), "## Summary\ngamma only");
|
|
|
|
var entry = NewEntry();
|
|
var request = JsonSerializer.Serialize(new
|
|
{
|
|
action = "search.entries",
|
|
payload = new
|
|
{
|
|
dataDirectory = root,
|
|
query = "common token",
|
|
}
|
|
});
|
|
|
|
var response = await entry.HandleCommandAsync(request);
|
|
using var doc = JsonDocument.Parse(response);
|
|
|
|
Assert(doc.RootElement.GetProperty("ok").GetBoolean(), "Expected ok=true for search.entries.");
|
|
var data = doc.RootElement.GetProperty("data");
|
|
Assert(data.ValueKind == JsonValueKind.Array, "Expected data array from search.entries.");
|
|
Assert(data.GetArrayLength() == 2, "Expected two entries matching query across raw content.");
|
|
}
|
|
finally
|
|
{
|
|
if (Directory.Exists(root))
|
|
Directory.Delete(root, recursive: true);
|
|
}
|
|
}
|
|
|
|
static async Task TestEntrySearchEntriesWithoutQueryReturnsAllAsync()
|
|
{
|
|
var root = Path.Combine(Path.GetTempPath(), "journal-search-smoke", Guid.NewGuid().ToString("N"));
|
|
Directory.CreateDirectory(root);
|
|
|
|
try
|
|
{
|
|
File.WriteAllText(Path.Combine(root, "2026-02-01.md"), "one");
|
|
File.WriteAllText(Path.Combine(root, "2026-02-02.md"), "two");
|
|
File.WriteAllText(Path.Combine(root, "ignore.txt"), "not markdown");
|
|
|
|
var entry = NewEntry();
|
|
var request = JsonSerializer.Serialize(new
|
|
{
|
|
action = "search.entries",
|
|
payload = new
|
|
{
|
|
dataDirectory = root,
|
|
}
|
|
});
|
|
|
|
var response = await entry.HandleCommandAsync(request);
|
|
using var doc = JsonDocument.Parse(response);
|
|
|
|
Assert(doc.RootElement.GetProperty("ok").GetBoolean(), "Expected ok=true for search.entries without query.");
|
|
var data = doc.RootElement.GetProperty("data");
|
|
Assert(data.ValueKind == JsonValueKind.Array, "Expected data array from search.entries.");
|
|
Assert(data.GetArrayLength() == 2, "Expected all markdown files to be returned when query is omitted.");
|
|
}
|
|
finally
|
|
{
|
|
if (Directory.Exists(root))
|
|
Directory.Delete(root, recursive: true);
|
|
}
|
|
}
|
|
|
|
static async Task TestEntrySearchEntriesDateRangeFilterAsync()
|
|
{
|
|
var root = Path.Combine(Path.GetTempPath(), "journal-search-smoke", Guid.NewGuid().ToString("N"));
|
|
Directory.CreateDirectory(root);
|
|
|
|
try
|
|
{
|
|
WriteSearchFixtureFiles(root);
|
|
var entry = NewEntry();
|
|
var request = JsonSerializer.Serialize(new
|
|
{
|
|
action = "search.entries",
|
|
payload = new
|
|
{
|
|
dataDirectory = root,
|
|
startDate = "2026-02-02",
|
|
endDate = "2026-02-28",
|
|
}
|
|
});
|
|
|
|
var response = await entry.HandleCommandAsync(request);
|
|
using var doc = JsonDocument.Parse(response);
|
|
|
|
Assert(doc.RootElement.GetProperty("ok").GetBoolean(), "Expected ok=true for date-range filtered search.");
|
|
var data = doc.RootElement.GetProperty("data");
|
|
Assert(data.GetArrayLength() == 1, "Expected one result for filtered date range.");
|
|
Assert(data[0].GetProperty("FileName").GetString() == "2026-02-05.md", "Date-range result mismatch.");
|
|
}
|
|
finally
|
|
{
|
|
if (Directory.Exists(root))
|
|
Directory.Delete(root, recursive: true);
|
|
}
|
|
}
|
|
|
|
static async Task TestEntrySearchEntriesSectionFilterAsync()
|
|
{
|
|
var root = Path.Combine(Path.GetTempPath(), "journal-search-smoke", Guid.NewGuid().ToString("N"));
|
|
Directory.CreateDirectory(root);
|
|
|
|
try
|
|
{
|
|
WriteSearchFixtureFiles(root);
|
|
var entry = NewEntry();
|
|
var request = JsonSerializer.Serialize(new
|
|
{
|
|
action = "search.entries",
|
|
payload = new
|
|
{
|
|
dataDirectory = root,
|
|
query = "focus area",
|
|
section = "Reflection",
|
|
}
|
|
});
|
|
|
|
var response = await entry.HandleCommandAsync(request);
|
|
using var doc = JsonDocument.Parse(response);
|
|
|
|
Assert(doc.RootElement.GetProperty("ok").GetBoolean(), "Expected ok=true for section-scoped search.");
|
|
var data = doc.RootElement.GetProperty("data");
|
|
Assert(data.GetArrayLength() == 1, "Expected one section-scoped result.");
|
|
Assert(data[0].GetProperty("FileName").GetString() == "2026-02-01.md", "Section filter result mismatch.");
|
|
}
|
|
finally
|
|
{
|
|
if (Directory.Exists(root))
|
|
Directory.Delete(root, recursive: true);
|
|
}
|
|
}
|
|
|
|
static async Task TestEntrySearchEntriesTagTypeFilterAsync()
|
|
{
|
|
var root = Path.Combine(Path.GetTempPath(), "journal-search-smoke", Guid.NewGuid().ToString("N"));
|
|
Directory.CreateDirectory(root);
|
|
|
|
try
|
|
{
|
|
WriteSearchFixtureFiles(root);
|
|
var entry = NewEntry();
|
|
var request = JsonSerializer.Serialize(new
|
|
{
|
|
action = "search.entries",
|
|
payload = new
|
|
{
|
|
dataDirectory = root,
|
|
tags = new[] { "stress" },
|
|
types = new[] { "!TRIGGER" },
|
|
}
|
|
});
|
|
|
|
var response = await entry.HandleCommandAsync(request);
|
|
using var doc = JsonDocument.Parse(response);
|
|
|
|
Assert(doc.RootElement.GetProperty("ok").GetBoolean(), "Expected ok=true for fragment tag/type filtered search.");
|
|
var data = doc.RootElement.GetProperty("data");
|
|
Assert(data.GetArrayLength() == 1, "Expected one result for fragment tag/type filters.");
|
|
Assert(data[0].GetProperty("FileName").GetString() == "2026-02-01.md", "Tag/type filter result mismatch.");
|
|
}
|
|
finally
|
|
{
|
|
if (Directory.Exists(root))
|
|
Directory.Delete(root, recursive: true);
|
|
}
|
|
}
|
|
|
|
static async Task TestEntrySearchEntriesCheckboxFilterAsync()
|
|
{
|
|
var root = Path.Combine(Path.GetTempPath(), "journal-search-smoke", Guid.NewGuid().ToString("N"));
|
|
Directory.CreateDirectory(root);
|
|
|
|
try
|
|
{
|
|
WriteSearchFixtureFiles(root);
|
|
var entry = NewEntry();
|
|
var request = JsonSerializer.Serialize(new
|
|
{
|
|
action = "search.entries",
|
|
payload = new
|
|
{
|
|
dataDirectory = root,
|
|
@checked = new[] { "med taken" },
|
|
@unchecked = new[] { "drink water" },
|
|
}
|
|
});
|
|
|
|
var response = await entry.HandleCommandAsync(request);
|
|
using var doc = JsonDocument.Parse(response);
|
|
|
|
Assert(doc.RootElement.GetProperty("ok").GetBoolean(), "Expected ok=true for checkbox filtered search.");
|
|
var data = doc.RootElement.GetProperty("data");
|
|
Assert(data.GetArrayLength() == 2, "Expected OR-style checkbox match across checked/unchecked filters.");
|
|
}
|
|
finally
|
|
{
|
|
if (Directory.Exists(root))
|
|
Directory.Delete(root, recursive: true);
|
|
}
|
|
}
|
|
|
|
static async Task TestEntrySearchEntriesRejectsInvalidDateAsync()
|
|
{
|
|
var root = Path.Combine(Path.GetTempPath(), "journal-search-smoke", Guid.NewGuid().ToString("N"));
|
|
Directory.CreateDirectory(root);
|
|
|
|
try
|
|
{
|
|
WriteSearchFixtureFiles(root);
|
|
var entry = NewEntry();
|
|
var request = JsonSerializer.Serialize(new
|
|
{
|
|
action = "search.entries",
|
|
payload = new
|
|
{
|
|
dataDirectory = root,
|
|
startDate = "2026/02/01",
|
|
}
|
|
});
|
|
|
|
var response = await entry.HandleCommandAsync(request);
|
|
using var doc = JsonDocument.Parse(response);
|
|
|
|
Assert(!doc.RootElement.GetProperty("ok").GetBoolean(), "Expected ok=false for invalid date format.");
|
|
var error = doc.RootElement.GetProperty("error").GetString() ?? "";
|
|
Assert(error.Contains("invalid startdate value", StringComparison.OrdinalIgnoreCase), "Expected invalid startDate error.");
|
|
}
|
|
finally
|
|
{
|
|
if (Directory.Exists(root))
|
|
Directory.Delete(root, recursive: true);
|
|
}
|
|
}
|
|
|
|
static Task TestSidecarSearchCliFilteredAsync()
|
|
{
|
|
var root = Path.Combine(Path.GetTempPath(), "journal-sidecar-cli-smoke", Guid.NewGuid().ToString("N"));
|
|
var dataDir = Path.Combine(root, "data");
|
|
Directory.CreateDirectory(dataDir);
|
|
|
|
try
|
|
{
|
|
WriteSearchFixtureFiles(dataDir);
|
|
var cli = new SidecarCli(new VaultStorageService(new VaultCryptoService()), new EntrySearchService(), new JournalConfigService());
|
|
|
|
var (exitCode, stdout, stderr) = CaptureConsole(() => cli.RunSearchCommand(
|
|
[
|
|
"common",
|
|
"--data-dir", dataDir,
|
|
"--start-date", "2026-02-01",
|
|
"--end-date", "2026-02-28",
|
|
"--tag", "stress",
|
|
"--type", "!TRIGGER",
|
|
"--checked", "med taken",
|
|
"--section", "Summary"
|
|
]));
|
|
|
|
Assert(exitCode == 0, "Expected search CLI command to succeed.");
|
|
Assert(string.IsNullOrWhiteSpace(stderr), "Expected no stderr output for successful search CLI command.");
|
|
Assert(stdout.Contains("--- 2026-02-01 ---", StringComparison.Ordinal), "Expected matching entry header in search CLI output.");
|
|
Assert(!stdout.Contains("--- 2026-02-05 ---", StringComparison.Ordinal), "Unexpected non-matching entry in filtered search CLI output.");
|
|
}
|
|
finally
|
|
{
|
|
if (Directory.Exists(root))
|
|
Directory.Delete(root, recursive: true);
|
|
}
|
|
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
static Task TestSidecarSearchCliEmptyDataAsync()
|
|
{
|
|
var root = Path.Combine(Path.GetTempPath(), "journal-sidecar-cli-smoke", Guid.NewGuid().ToString("N"));
|
|
var dataDir = Path.Combine(root, "data");
|
|
Directory.CreateDirectory(dataDir);
|
|
|
|
try
|
|
{
|
|
var cli = new SidecarCli(new VaultStorageService(new VaultCryptoService()), new EntrySearchService(), new JournalConfigService());
|
|
var (exitCode, stdout, _) = CaptureConsole(() => cli.RunSearchCommand(["--data-dir", dataDir]));
|
|
|
|
Assert(exitCode == 0, "Expected search CLI command to return success for empty data directory.");
|
|
Assert(stdout.Contains("No decrypted journal entries found", StringComparison.OrdinalIgnoreCase), "Expected empty-data guidance message.");
|
|
}
|
|
finally
|
|
{
|
|
if (Directory.Exists(root))
|
|
Directory.Delete(root, recursive: true);
|
|
}
|
|
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
static Task TestEntrySavePayloadFileNameDeserializationAsync()
|
|
{
|
|
// Simulate what DeserializePayload does: parse JSON to JsonElement, then Deserialize<T>
|
|
var json = """{"content":"hello","mode":"Overwrite","fileName":"My Custom Name"}""";
|
|
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
|
|
var element = JsonSerializer.Deserialize<JsonElement>(json);
|
|
var payload = element.Deserialize<EntrySavePayload>(options);
|
|
|
|
Assert(payload is not null, "Payload should not be null.");
|
|
Assert(payload!.Content == "hello", "Content should be deserialized.");
|
|
Assert(payload.Mode == "Overwrite", "Mode should be deserialized.");
|
|
Assert(payload.FileName == "My Custom Name", "FileName should be deserialized from camelCase JSON via JsonElement.");
|
|
Assert(payload.FilePath is null, "FilePath should be null when not provided.");
|
|
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
static async Task TestEntrySaveWithFileNameAsync()
|
|
{
|
|
var root = Path.Combine(Path.GetTempPath(), "journal-entry-smoke", Guid.NewGuid().ToString("N"));
|
|
Directory.CreateDirectory(root);
|
|
|
|
try
|
|
{
|
|
// Use EntryFileService directly to test the full save path with fileName
|
|
var service = new EntryFileService(new DiskEntryFileRepository());
|
|
var payload = new EntrySavePayload(
|
|
Content: "# Custom Entry\n\nHello world",
|
|
FilePath: null,
|
|
Mode: "Overwrite",
|
|
FileName: "My Custom Name");
|
|
|
|
var result = service.SaveEntry(payload, root);
|
|
|
|
var expectedPath = Path.GetFullPath(Path.Combine(root, "My Custom Name.md"));
|
|
Assert(result.FilePath == expectedPath, $"Expected path '{expectedPath}' but got '{result.FilePath}'.");
|
|
Assert(File.Exists(expectedPath), "Custom-named file should exist on disk.");
|
|
Assert(File.ReadAllText(expectedPath).Contains("Hello world"), "File content should match.");
|
|
|
|
// Also test via Entry.HandleCommandAsync with JSON to verify full deserialization chain
|
|
var entry = NewEntry();
|
|
var request = JsonSerializer.Serialize(new
|
|
{
|
|
action = "entries.save",
|
|
payload = new
|
|
{
|
|
content = "# Second Entry",
|
|
mode = "Overwrite",
|
|
fileName = "Another Custom Name"
|
|
}
|
|
});
|
|
|
|
var response = await entry.HandleCommandAsync(request);
|
|
using var doc = JsonDocument.Parse(response);
|
|
Assert(doc.RootElement.GetProperty("ok").GetBoolean(), "Expected ok=true for entries.save with fileName.");
|
|
|
|
var savedFilePath = doc.RootElement.GetProperty("data").GetProperty("FilePath").GetString() ?? "";
|
|
Assert(savedFilePath.Contains("Another Custom Name.md", StringComparison.OrdinalIgnoreCase),
|
|
$"Expected file path to contain 'Another Custom Name.md' but got '{savedFilePath}'.");
|
|
}
|
|
finally
|
|
{
|
|
if (Directory.Exists(root))
|
|
Directory.Delete(root, recursive: true);
|
|
}
|
|
}
|
|
}
|
|
|