Public Access
171 lines
7.4 KiB
C#
171 lines
7.4 KiB
C#
using MeetingAssistant.MeetingNotes;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using Microsoft.Extensions.Options;
|
|
|
|
namespace MeetingAssistant.Tests;
|
|
|
|
public sealed class MeetingNoteStoreTests
|
|
{
|
|
[Fact]
|
|
public async Task StoreWritesTemplateFrontmatterAndReadsUserNotesBody()
|
|
{
|
|
var (vaultRoot, store) = CreateStore();
|
|
var note = MeetingNoteTemplate.Create(
|
|
title: "Leadership Sync",
|
|
startTime: DateTimeOffset.Parse("2026-05-19T10:00:00+02:00"),
|
|
endTime: DateTimeOffset.Parse("2026-05-19T10:30:00+02:00"),
|
|
attendees:
|
|
[
|
|
"Mike <m.ike@ibm.com>",
|
|
"Ada"
|
|
],
|
|
projects:
|
|
[
|
|
"Meeting Assistant",
|
|
"Alaric"
|
|
],
|
|
transcriptPath: Path.Combine(vaultRoot, "Meetings", "Transcripts", "20260519-transcript.md"),
|
|
assistantContextPath: Path.Combine(vaultRoot, "Meetings", "Assistant Context", "20260519-context.md"),
|
|
summaryPath: Path.Combine(vaultRoot, "Meetings", "Summaries", "20260519-summary.md"),
|
|
userNotes: "Discuss rollout risks.");
|
|
|
|
var saved = await store.SaveAsync(note, CancellationToken.None);
|
|
var content = await File.ReadAllTextAsync(saved.Path);
|
|
var loaded = await store.ReadAsync(saved.Path, CancellationToken.None);
|
|
|
|
Assert.EndsWith(".md", saved.Path, StringComparison.Ordinal);
|
|
Assert.Contains("attendees:", content);
|
|
Assert.Contains("- Mike <m.ike@ibm.com>", content);
|
|
Assert.Contains("- Ada", content);
|
|
Assert.Contains("projects:", content);
|
|
Assert.Contains("- Meeting Assistant", content);
|
|
Assert.DoesNotContain("purpose:", content);
|
|
Assert.Contains("start_time: \"2026-05-19T10:00:00.0000000+02:00\"", content);
|
|
Assert.Contains("end_time: \"2026-05-19T10:30:00.0000000+02:00\"", content);
|
|
Assert.Contains("transcript: \"[[../Transcripts/20260519-transcript|Transcript]]\"", content);
|
|
Assert.Contains("assistant_context: \"[[../Assistant Context/20260519-context|Assistant Context]]\"", content);
|
|
Assert.Contains("summary: \"[[../Summaries/20260519-summary|Summary]]\"", content);
|
|
Assert.DoesNotContain("decisions:", content);
|
|
Assert.DoesNotContain("next_steps:", content);
|
|
Assert.Equal("Discuss rollout risks.", loaded.UserNotes);
|
|
Assert.Equal(["Mike <m.ike@ibm.com>", "Ada"], loaded.Frontmatter.Attendees);
|
|
Assert.Equal(["Meeting Assistant", "Alaric"], loaded.Frontmatter.Projects);
|
|
Assert.Equal(DateTimeOffset.Parse("2026-05-19T10:00:00+02:00"), loaded.Frontmatter.StartTime);
|
|
Assert.Equal(DateTimeOffset.Parse("2026-05-19T10:30:00+02:00"), loaded.Frontmatter.EndTime);
|
|
Assert.Equal("[[../Transcripts/20260519-transcript|Transcript]]", loaded.Frontmatter.Transcript);
|
|
Assert.Equal("[[../Assistant Context/20260519-context|Assistant Context]]", loaded.Frontmatter.AssistantContext);
|
|
Assert.Equal("[[../Summaries/20260519-summary|Summary]]", loaded.Frontmatter.Summary);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task StoreNamesGeneratedMeetingNoteWithMinutePrecision()
|
|
{
|
|
var (_, store) = CreateStore();
|
|
var saved = await store.SaveAsync(
|
|
MeetingNoteTemplate.Create(
|
|
title: "Leadership Sync",
|
|
startTime: DateTimeOffset.Parse("2026-05-19T10:03:42+02:00")),
|
|
CancellationToken.None);
|
|
|
|
Assert.Equal("20260519-1003-note.md", Path.GetFileName(saved.Path));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task FrontmatterUpdatePreservesExistingUserNotesBody()
|
|
{
|
|
var (vaultRoot, store) = CreateStore();
|
|
var userNotes = """
|
|
## User notes
|
|
|
|
- First point
|
|
- Second point
|
|
|
|
```text
|
|
Keep this block exactly.
|
|
```
|
|
|
|
""";
|
|
var saved = await store.SaveAsync(
|
|
MeetingNoteTemplate.Create(
|
|
title: "Body Preservation",
|
|
attendees: [],
|
|
projects: [],
|
|
transcriptPath: Path.Combine(vaultRoot, "Meetings", "Transcripts", "20260519-transcript.md"),
|
|
assistantContextPath: Path.Combine(vaultRoot, "Meetings", "Assistant Context", "20260519-context.md"),
|
|
summaryPath: Path.Combine(vaultRoot, "Meetings", "Summaries", "20260519-summary.md"),
|
|
userNotes: userNotes),
|
|
CancellationToken.None);
|
|
|
|
var loaded = await store.ReadAsync(saved.Path, CancellationToken.None);
|
|
loaded.Frontmatter.Title = "Updated title";
|
|
await store.SaveAsync(loaded, CancellationToken.None);
|
|
var reloaded = await store.ReadAsync(saved.Path, CancellationToken.None);
|
|
|
|
Assert.Equal("Updated title", reloaded.Frontmatter.Title);
|
|
Assert.Equal(userNotes, reloaded.UserNotes);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ReadConvertsScalarListFrontmatterToSingleItemLists()
|
|
{
|
|
var (vaultRoot, store) = CreateStore();
|
|
var notePath = Path.Combine(vaultRoot, "Meetings", "Notes", "manual.md");
|
|
Directory.CreateDirectory(Path.GetDirectoryName(notePath)!);
|
|
await File.WriteAllTextAsync(
|
|
notePath,
|
|
"""
|
|
---
|
|
title: Manual frontmatter
|
|
start_time: "2026-05-19T10:00:00.0000000+02:00"
|
|
end_time: ""
|
|
attendees: Ada
|
|
projects: Meeting Assistant
|
|
transcript: "[[../Transcripts/manual-transcript|Transcript]]"
|
|
assistant_context: "[[../Assistant Context/manual-context|Assistant Context]]"
|
|
summary: "[[../Summaries/manual-summary|Summary]]"
|
|
---
|
|
|
|
User notes.
|
|
""");
|
|
|
|
var loaded = await store.ReadAsync(notePath, CancellationToken.None);
|
|
|
|
Assert.Equal(["Ada"], loaded.Frontmatter.Attendees);
|
|
Assert.Equal(["Meeting Assistant"], loaded.Frontmatter.Projects);
|
|
Assert.Equal("User notes.", loaded.UserNotes);
|
|
}
|
|
|
|
[Fact]
|
|
public void ActionLinkEscapesSummaryFileName()
|
|
{
|
|
var link = MeetingNoteActionLinks.CreateSummaryRetryLink(
|
|
"http://localhost:5090/",
|
|
"C:\\Vault\\Meetings\\Summaries\\summary with spaces.md");
|
|
|
|
Assert.Equal(
|
|
"[Retry summary generation](http://localhost:5090/meetings/summary/retry?summaryPath=summary%20with%20spaces.md)",
|
|
link);
|
|
}
|
|
|
|
private static (string VaultRoot, MarkdownMeetingNoteStore Store) CreateStore()
|
|
{
|
|
var vaultRoot = Path.Combine(Path.GetTempPath(), "meeting-assistant-tests", Guid.NewGuid().ToString("N"));
|
|
var store = new MarkdownMeetingNoteStore(
|
|
Options.Create(new MeetingAssistantOptions
|
|
{
|
|
Vault = new VaultOptions
|
|
{
|
|
MeetingNotesFolder = Path.Combine(vaultRoot, "Meetings", "Notes"),
|
|
TranscriptsFolder = Path.Combine(vaultRoot, "Meetings", "Transcripts"),
|
|
AssistantContextFolder = Path.Combine(vaultRoot, "Meetings", "Assistant Context"),
|
|
SummariesFolder = Path.Combine(vaultRoot, "Meetings", "Summaries"),
|
|
ProjectsFolder = Path.Combine(vaultRoot, "Projects"),
|
|
DictationWordsPath = Path.Combine(vaultRoot, "Meetings", "dictation-words.md")
|
|
}
|
|
}),
|
|
NullLogger<MarkdownMeetingNoteStore>.Instance);
|
|
|
|
return (vaultRoot, store);
|
|
}
|
|
}
|