mirror of
https://github.com/wu736139669/hapi.git
synced 2026-11-07 01:21:37 +00:00
Persist and backfill the last user-message timestamp, and use it for deterministic session ordering across web and native clients while preserving manual pins. Remove the fixed-order setting and guard ordering against transcript echoes, replayed messages, and stale REST/SSE updates. Add regression coverage and regenerate the SSE fixture.
48 lines
2.2 KiB
Swift
48 lines
2.2 KiB
Swift
import Foundation
|
|
import HapiProtocol
|
|
import Testing
|
|
|
|
@Suite("User-activity session sorting")
|
|
struct SessionSortingTests {
|
|
private func summary(_ id: String, createdAt: Int = 0, at: Int? = nil) -> SessionSummary {
|
|
SessionSummary(id: id, active: false, thinking: false, activeAt: 0, updatedAt: 0,
|
|
createdAt: createdAt, lastUserMessageAt: at, metadataVersion: 0,
|
|
agentStateVersion: 0, todosUpdatedAt: 0, pendingRequestsCount: 0)
|
|
}
|
|
|
|
@Test func outputAndStatusDoNotReorderButNewInputDoes() {
|
|
var old = summary("old", createdAt: 1, at: 100)
|
|
let new = summary("new", createdAt: 2, at: 200)
|
|
old.updatedAt = 90_000
|
|
old.active = true
|
|
old.thinking = true
|
|
old.pendingRequestsCount = 4
|
|
#expect(sortSessionSummaries([old, new]).map(\.id) == ["new", "old"])
|
|
old.lastUserMessageAt = 300
|
|
#expect(sortSessionSummaries([old, new]).map(\.id) == ["old", "new"])
|
|
}
|
|
|
|
@Test func manualPinsStillComeFirst() {
|
|
var global = summary("global", at: 1)
|
|
global.globalPinned = true
|
|
var project = summary("project", at: 2)
|
|
project.pinned = true
|
|
#expect(sortSessionSummaries([summary("new", at: 99), project, global]).map(\.id) == ["global", "project", "new"])
|
|
}
|
|
|
|
@Test func creationFallbackAndTimestampTiesAreDeterministic() {
|
|
let rows = [summary("b", createdAt: 2), summary("a", createdAt: 2), summary("old", createdAt: 1)]
|
|
#expect(sortSessionSummaries(rows).map(\.id) == ["a", "b", "old"])
|
|
#expect(sortSessionSummaries(rows.reversed()).map(\.id) == ["a", "b", "old"])
|
|
}
|
|
|
|
@Test func patchClockIsMonotonicAndRenderRelevant() throws {
|
|
let row = summary("a", at: 100)
|
|
let patch = try JSONDecoder().decode(SessionPatch.self, from: Data("{\"lastUserMessageAt\":200}".utf8))
|
|
let next = SummaryPatching.applySessionSummaryPatch(row, patch)
|
|
#expect(next.lastUserMessageAt == 200)
|
|
#expect(!SummaryPatching.isRenderIrrelevantPatch(current: row, next: next))
|
|
#expect(SummaryPatching.applySessionSummaryPatch(next, SessionPatch(lastUserMessageAt: 50)).lastUserMessageAt == 200)
|
|
}
|
|
}
|