using MeetingAssistant.MeetingNotes; using MeetingAssistant.Summary; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; namespace MeetingAssistant.Tests; public sealed class MeetingSummaryArtifactResolverTests { [Fact] public async Task ResolverFindsMeetingArtifactsForSummaryPath() { var root = Path.Combine(Path.GetTempPath(), "meeting-assistant-tests", Guid.NewGuid().ToString("N")); var notes = Path.Combine(root, "Meetings", "Notes"); var summaries = Path.Combine(root, "Meetings", "Summaries"); Directory.CreateDirectory(notes); Directory.CreateDirectory(summaries); var notePath = Path.Combine(notes, "meeting.md"); var assistantContextPath = Path.Combine(root, "Meetings", "Assistant Context", "context.md"); Directory.CreateDirectory(Path.GetDirectoryName(assistantContextPath)!); await File.WriteAllTextAsync(assistantContextPath, "# Assistant Context"); await File.WriteAllTextAsync( notePath, """ --- title: Meeting attendees: projects: transcript: "[[../Transcripts/transcript|Transcript]]" assistant_context: "[[../Assistant Context/context|Assistant Context]]" summary: "[[../Summaries/summary|Summary]]" --- User notes. """); var options = Options.Create(new MeetingAssistantOptions { Vault = new VaultOptions { MeetingNotesFolder = notes, SummariesFolder = summaries } }); var resolver = new MeetingSummaryArtifactResolver( options, new MarkdownMeetingNoteStore(options, NullLogger.Instance)); var artifacts = await resolver.ResolveBySummaryPathAsync("summary.md", CancellationToken.None); Assert.NotNull(artifacts); Assert.Equal(notePath, artifacts.MeetingNotePath); Assert.Equal(Path.Combine(root, "Meetings", "Transcripts", "transcript.md"), artifacts.TranscriptPath); Assert.Equal(assistantContextPath, artifacts.AssistantContextPath); Assert.Equal(Path.Combine(summaries, "summary.md"), artifacts.SummaryPath); } [Fact] public async Task ResolverPrefersArtifactLinksFromRequestedSummaryFile() { var root = Path.Combine(Path.GetTempPath(), "meeting-assistant-tests", Guid.NewGuid().ToString("N")); var notes = Path.Combine(root, "Meetings", "Notes"); var summaries = Path.Combine(root, "Meetings", "Summaries"); Directory.CreateDirectory(notes); Directory.CreateDirectory(summaries); var notePath = Path.Combine(notes, "meeting.md"); var transcriptPath = Path.Combine(root, "Meetings", "Transcripts", "transcript.md"); var summaryContextPath = Path.Combine(root, "Meetings", "Assistant Context", "summary-context.md"); Directory.CreateDirectory(Path.GetDirectoryName(summaryContextPath)!); await File.WriteAllTextAsync(summaryContextPath, "# Summary-linked Assistant Context"); await File.WriteAllTextAsync( notePath, """ --- title: Meeting transcript: "[[../Transcripts/transcript|Transcript]]" assistant_context: "[[../Assistant Context/stale-context|Assistant Context]]" summary: "[[../Summaries/summary|Summary]]" --- """); await File.WriteAllTextAsync( Path.Combine(summaries, "summary.md"), """ --- meeting: "[[../Notes/meeting|Meeting Note]]" transcript: "[[../Transcripts/transcript|Transcript]]" assistant_context: "[[../Assistant Context/summary-context|Assistant Context]]" --- """); var options = Options.Create(new MeetingAssistantOptions { Vault = new VaultOptions { MeetingNotesFolder = notes, SummariesFolder = summaries } }); var resolver = new MeetingSummaryArtifactResolver( options, new MarkdownMeetingNoteStore(options, NullLogger.Instance)); var artifacts = await resolver.ResolveBySummaryPathAsync("summary.md", CancellationToken.None); Assert.NotNull(artifacts); Assert.Equal(notePath, artifacts.MeetingNotePath); Assert.Equal(transcriptPath, artifacts.TranscriptPath); Assert.Equal(summaryContextPath, artifacts.AssistantContextPath); Assert.Equal(Path.Combine(summaries, "summary.md"), artifacts.SummaryPath); } [Fact] public async Task ResolverRejectsSummaryWhenLinkedAssistantContextIsMissing() { var root = Path.Combine(Path.GetTempPath(), "meeting-assistant-tests", Guid.NewGuid().ToString("N")); var notes = Path.Combine(root, "Meetings", "Notes"); var summaries = Path.Combine(root, "Meetings", "Summaries"); Directory.CreateDirectory(notes); Directory.CreateDirectory(summaries); await File.WriteAllTextAsync( Path.Combine(notes, "meeting.md"), """ --- title: Meeting transcript: "[[../Transcripts/transcript|Transcript]]" assistant_context: "[[../Assistant Context/missing-context|Assistant Context]]" summary: "[[../Summaries/summary|Summary]]" --- """); var options = Options.Create(new MeetingAssistantOptions { Vault = new VaultOptions { MeetingNotesFolder = notes, SummariesFolder = summaries } }); var resolver = new MeetingSummaryArtifactResolver( options, new MarkdownMeetingNoteStore(options, NullLogger.Instance)); var artifacts = await resolver.ResolveBySummaryPathAsync("summary.md", CancellationToken.None); Assert.Null(artifacts); } [Fact] public async Task ResolverRejectsSummaryPathOutsideConfiguredSummaryFolder() { var root = Path.Combine(Path.GetTempPath(), "meeting-assistant-tests", Guid.NewGuid().ToString("N")); var options = Options.Create(new MeetingAssistantOptions { Vault = new VaultOptions { MeetingNotesFolder = Path.Combine(root, "Notes"), SummariesFolder = Path.Combine(root, "Summaries") } }); var resolver = new MeetingSummaryArtifactResolver( options, new MarkdownMeetingNoteStore(options, NullLogger.Instance)); var artifacts = await resolver.ResolveBySummaryPathAsync( Path.Combine(root, "Other", "summary.md"), CancellationToken.None); Assert.Null(artifacts); } [Fact] public async Task ResolverUsesSuppliedProfileOptionsForSummaryLookup() { var root = Path.Combine(Path.GetTempPath(), "meeting-assistant-tests", Guid.NewGuid().ToString("N")); var defaultRoot = Path.Combine(root, "default"); var englishRoot = Path.Combine(root, "english"); var englishNotes = Path.Combine(englishRoot, "Notes"); var englishSummaries = Path.Combine(englishRoot, "Summaries"); Directory.CreateDirectory(englishNotes); Directory.CreateDirectory(englishSummaries); var notePath = Path.Combine(englishNotes, "meeting.md"); var assistantContextPath = Path.Combine(englishRoot, "Assistant Context", "context.md"); Directory.CreateDirectory(Path.GetDirectoryName(assistantContextPath)!); await File.WriteAllTextAsync(assistantContextPath, "# Assistant Context"); await File.WriteAllTextAsync( notePath, """ --- title: Meeting attendees: projects: transcript: "[[../Transcripts/transcript|Transcript]]" assistant_context: "[[../Assistant Context/context|Assistant Context]]" summary: "[[../Summaries/summary|Summary]]" --- User notes. """); var defaultOptions = Options.Create(new MeetingAssistantOptions { Vault = new VaultOptions { MeetingNotesFolder = Path.Combine(defaultRoot, "Notes"), SummariesFolder = Path.Combine(defaultRoot, "Summaries") } }); var profileOptions = new MeetingAssistantOptions { Vault = new VaultOptions { MeetingNotesFolder = englishNotes, SummariesFolder = englishSummaries } }; var resolver = new MeetingSummaryArtifactResolver( defaultOptions, new MarkdownMeetingNoteStore(defaultOptions, NullLogger.Instance)); var artifacts = await resolver.ResolveBySummaryPathAsync("summary.md", profileOptions, CancellationToken.None); Assert.NotNull(artifacts); Assert.Equal(notePath, artifacts.MeetingNotePath); Assert.Equal(Path.Combine(englishSummaries, "summary.md"), artifacts.SummaryPath); } }