crossmate

A collaborative crossword app for iOS
Log | Files | Refs | LICENSE

commit d71aee9b866565002fb14122be9b965694741a9f
parent b23aa20c561bf9b83097c8af7dd09e50cb0c7299
Author: Michael Camilleri <[email protected]>
Date:   Thu, 21 May 2026 13:35:33 +0900

Use consistent puzzle title

Diffstat:
MCrossmate/Sync/SessionMonitor.swift | 2+-
MTests/Unit/Sync/SessionMonitorTests.swift | 42++++++++++++++++++++++++++++++++++++++++++
2 files changed, 43 insertions(+), 1 deletion(-)

diff --git a/Crossmate/Sync/SessionMonitor.swift b/Crossmate/Sync/SessionMonitor.swift @@ -240,7 +240,7 @@ actor SessionMonitor { gameReq.predicate = NSPredicate(format: "id == %@", gameID as CVarArg) gameReq.fetchLimit = 1 let game = try? ctx.fetch(gameReq).first - let title = game?.title ?? "" + let title = PuzzleNotificationText.title(for: game) var name = "" if let game { diff --git a/Tests/Unit/Sync/SessionMonitorTests.swift b/Tests/Unit/Sync/SessionMonitorTests.swift @@ -217,6 +217,48 @@ struct SessionMonitorTests { )) } + @Test("End-of-session summary uses the same puzzle title as session start") + @MainActor func endSummaryUsesFormattedPuzzleTitle() async throws { + let persistence = makeTestPersistence() + let context = persistence.viewContext + let gameID = UUID() + let date = try #require(Calendar(identifier: .gregorian).date( + from: DateComponents(year: 2001, month: 1, day: 1) + )) + + let game = GameEntity(context: context) + game.id = gameID + game.title = "Saturday Puzzle" + game.cachedPublisher = "The Daily Crossword" + game.cachedPuzzleDate = date + game.puzzleSource = "" + game.createdAt = date + game.updatedAt = date + game.ckRecordName = "game-\(gameID.uuidString)" + + let player = PlayerEntity(context: context) + player.authorID = "alice" + player.ckRecordName = "player-\(gameID.uuidString)-alice" + player.name = "Alice" + player.updatedAt = date + player.game = game + + try context.save() + + let scheduler = RecordingNotificationScheduler() + let monitor = SessionMonitor( + persistence: persistence, + localAuthorIDProvider: { nil }, + notificationCenter: scheduler + ) + + await monitor.ingest([makeDelta(gameID: gameID, authorID: "alice", added: 1)]) + + let body = try #require(scheduler.added.first?.content.body) + let expectedTitle = PuzzleNotificationText.title(for: game) + #expect(body == "Alice added 1 letter in the puzzle '\(expectedTitle)'") + } + @Test("consumeOnOpen returns hydrated summaries and clears the buckets") @MainActor func consumeOnOpenReturnsAndClears() async { let scheduler = RecordingNotificationScheduler()