Make meeting lifecycle stateful

This commit is contained in:
2026-05-27 12:55:17 +02:00
parent d607b957bb
commit e85274829a
91 changed files with 4076 additions and 479 deletions
@@ -17,13 +17,13 @@ public sealed class SpeakerIdentityMergeServiceTests
aliases: ["M. Schweigert"],
snippets: [[1], [2], [3]],
createdAt: DateTimeOffset.UtcNow.AddMonths(-2),
transcriptionCount: 5);
referenceCount: 5);
var recent = await fixture.AddIdentityAsync(
canonicalName: "Guest Manuel",
aliases: [],
snippets: [[4], [5]],
createdAt: DateTimeOffset.UtcNow.AddDays(-1),
transcriptionCount: 2);
referenceCount: 2);
fixture.Matcher.MatchIdentityIds.Enqueue(older.Id);
fixture.Matcher.MatchIdentityIds.Enqueue(older.Id);
var service = fixture.CreateService();
@@ -36,7 +36,9 @@ public sealed class SpeakerIdentityMergeServiceTests
Assert.Equal([5], fixture.Matcher.Requests[1].UnknownSnippet);
var savedOlder = await fixture.LoadIdentityAsync(older.Id);
Assert.Equal("Manuel", savedOlder.CanonicalName);
Assert.Equal(7, savedOlder.TranscriptionCount);
Assert.Equal(7, savedOlder.References.Count);
Assert.All(savedOlder.References, reference =>
Assert.Contains("Manuel and Guest Manuel were merged", File.ReadAllText(reference.TranscriptPath)));
Assert.True(savedOlder.UpdatedAt > older.UpdatedAt);
Assert.Equal(["Guest Manuel", "M. Schweigert"], savedOlder.Aliases.Select(alias => alias.Name).Order());
Assert.Equal(3, savedOlder.Snippets.Count);
@@ -74,10 +76,12 @@ public sealed class SpeakerIdentityMergeServiceTests
private sealed class SpeakerIdentityMergeFixture : IAsyncDisposable
{
private readonly string tempDirectory;
private readonly string dbPath;
private SpeakerIdentityMergeFixture(string dbPath, SpeakerIdentityDbContext context)
private SpeakerIdentityMergeFixture(string tempDirectory, string dbPath, SpeakerIdentityDbContext context)
{
this.tempDirectory = tempDirectory;
this.dbPath = dbPath;
Context = context;
}
@@ -88,13 +92,14 @@ public sealed class SpeakerIdentityMergeServiceTests
public static async Task<SpeakerIdentityMergeFixture> CreateAsync()
{
var dbPath = Path.Combine(Path.GetTempPath(), "meeting-assistant-tests", $"{Guid.NewGuid():N}.db");
Directory.CreateDirectory(Path.GetDirectoryName(dbPath)!);
var tempDirectory = Path.Combine(Path.GetTempPath(), "meeting-assistant-tests", Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(tempDirectory);
var dbPath = Path.Combine(tempDirectory, "speaker-identities.db");
var context = new SpeakerIdentityDbContext(new DbContextOptionsBuilder<SpeakerIdentityDbContext>()
.UseSqlite($"Data Source={dbPath};Pooling=False")
.Options);
await SpeakerIdentitySchema.EnsureCreatedOrUpdatedAsync(context, CancellationToken.None);
return new SpeakerIdentityMergeFixture(dbPath, context);
return new SpeakerIdentityMergeFixture(tempDirectory, dbPath, context);
}
public SpeakerIdentityMergeService CreateService()
@@ -118,14 +123,13 @@ public sealed class SpeakerIdentityMergeServiceTests
IReadOnlyList<string> aliases,
IReadOnlyList<byte[]> snippets,
DateTimeOffset createdAt,
int transcriptionCount)
int referenceCount)
{
var identity = new SpeakerIdentity
{
CanonicalName = canonicalName,
CreatedAt = createdAt,
UpdatedAt = createdAt,
TranscriptionCount = transcriptionCount,
Aliases = aliases.Select(alias => new SpeakerAlias { Name = alias }).ToList(),
Snippets = snippets
.Select((snippet, index) => new SpeakerSnippet
@@ -133,6 +137,19 @@ public sealed class SpeakerIdentityMergeServiceTests
WavBytes = snippet,
CreatedAt = createdAt.AddMinutes(index)
})
.ToList(),
References = Enumerable.Range(0, referenceCount)
.Select(index =>
{
var transcriptPath = Path.Combine(tempDirectory, $"reference-{Guid.NewGuid():N}-{index}.md");
File.WriteAllText(transcriptPath, "Transcript");
return new SpeakerIdentityReference
{
MeetingNotePath = Path.Combine(tempDirectory, $"reference-{Guid.NewGuid():N}-{index}.md"),
TranscriptPath = transcriptPath,
CreatedAt = createdAt.AddMinutes(index)
};
})
.ToList()
};
Context.SpeakerIdentities.Add(identity);
@@ -146,15 +163,16 @@ public sealed class SpeakerIdentityMergeServiceTests
return Context.SpeakerIdentities
.Include(identity => identity.Aliases)
.Include(identity => identity.Snippets)
.Include(identity => identity.References)
.SingleAsync(identity => identity.Id == id);
}
public async ValueTask DisposeAsync()
{
await Context.DisposeAsync();
if (File.Exists(dbPath))
if (Directory.Exists(tempDirectory))
{
File.Delete(dbPath);
Directory.Delete(tempDirectory, recursive: true);
}
}
}