import Foundation import Testing @testable import OpenClawChatUI struct ChatWorkingProgressTests { private let session = "agent:main:main" private let previousEndedAt = 900_000.0 private let runEndedAt = 1_000_000.0 @Test func `stance selection is deterministic for a run and salt`() { let salt: UInt32 = 0xA1B2_C3D4 let run = "run-4c71b7e8" let first = ChatWorkingClawStance.seeded(run, salt: salt) #expect(first == ChatWorkingClawStance.seeded(run, salt: salt)) #expect(first == .southpaw) #expect(ChatWorkingClawStance.seeded("run-a", salt: salt) == .standard) #expect(ChatWorkingClawStance.seeded("run-6", salt: salt) == .spin) } @Test func `stance weights total one hundred and match the allowlist`() { let weightedStances = ChatWorkingClawStance.weightedStances #expect(weightedStances.reduce(0) { $0 + $1.weight } == 100) #expect(weightedStances.map(\.weight) == [55, 18, 5, 4, 3, 2, 2, 2, 2, 2, 2, 2, 1]) #expect(weightedStances.map(\.stance) == [ .standard, .southpaw, .flurry, .spin, .shadowbox, .backflip, .zen, .drummer, .peekaboo, .nodOff, .curious, .omNom, .fakeOut, ]) #expect(weightedStances.map(\.stance) == ChatWorkingClawStance.allCases) } @Test func `rare stances are selectable`() { let salt: UInt32 = 0xA1B2_C3D4 #expect(ChatWorkingClawStance.seeded("run-38", salt: salt) == .zen) #expect(ChatWorkingClawStance.seeded("run-21", salt: salt) == .drummer) #expect(ChatWorkingClawStance.seeded("run-8", salt: salt) == .peekaboo) #expect(ChatWorkingClawStance.seeded("run-15", salt: salt) == .nodOff) #expect(ChatWorkingClawStance.seeded("run-10", salt: salt) == .curious) #expect(ChatWorkingClawStance.seeded("run-24", salt: salt) == .omNom) #expect(ChatWorkingClawStance.seeded("run-22", salt: salt) == .fakeOut) } @Test func `zen samples the breath and deliberate snip keyframes`() { for (elapsed, scale) in [(0.0, 1.0), (1.8, 1.08), (3.3, 1.0), (6.0, 1.0)] { let pose = ChatWorkingClawMotion.pose(stance: .zen, elapsed: elapsed) self.expectClose(pose.bodyScale, scale) #expect(pose.bodyRotation == 0) #expect(pose.xOffset == 0) #expect(pose.yOffset == 0) } for (elapsed, jaw) in [(3.6, -10.0), (4.2, -24.0), (4.56, 2.0), (5.16, -10.0)] { let pose = ChatWorkingClawMotion.pose(stance: .zen, elapsed: elapsed) self.expectClose(pose.jawRotation, jaw) } } @Test func `drummer samples the two tilt and jaw hit keyframes`() { for (elapsed, rotation) in [(0.0, 0.0), (0.18, -8.0), (0.36, 0.0), (0.66, 8.0), (0.84, 0.0)] { let pose = ChatWorkingClawMotion.pose(stance: .drummer, elapsed: elapsed) self.expectClose(pose.bodyRotation, rotation) #expect(pose.bodyScale == 1) #expect(pose.xOffset == 0) #expect(pose.yOffset == 0) } for (elapsed, jaw) in [ (0.12, -20.0), (0.18, 2.0), (0.30, -10.0), (0.60, -20.0), (0.66, 2.0), (0.78, -10.0), ] { let pose = ChatWorkingClawMotion.pose(stance: .drummer, elapsed: elapsed) self.expectClose(pose.jawRotation, jaw) } } @Test func `peekaboo samples the vertical duck pop and boo keyframes`() { for (elapsed, yOffset, scale) in [ (1.32, 0.0, 1.0), (1.488, 5.0, 0.72), (1.728, 5.0, 0.72), (1.872, -1.5, 1.06), (2.016, 0.0, 1.0), ] { let pose = ChatWorkingClawMotion.pose(stance: .peekaboo, elapsed: elapsed) self.expectClose(pose.yOffset, yOffset) self.expectClose(pose.bodyScale, scale) #expect(pose.bodyRotation == 0) #expect(pose.xOffset == 0) } for (elapsed, jaw) in [ (1.32, -10.0), (1.488, -2.0), (1.728, -2.0), (1.872, -28.0), (2.064, -10.0), ] { let pose = ChatWorkingClawMotion.pose(stance: .peekaboo, elapsed: elapsed) self.expectClose(pose.jawRotation, jaw) } } @Test func `latest rare stances sample their defining beats`() { let samples: [( stance: ChatWorkingClawStance, elapsed: TimeInterval, bodyRotation: CGFloat, xOffset: CGFloat, yOffset: CGFloat, jawRotation: CGFloat )] = [ (.nodOff, 2.304, -3, 0, -0.5, -6), (.curious, 0.96, -14, 0, 0, -16), (.omNom, 0.864, 0, 2.5, 0, 8), (.fakeOut, 1.392, 3, 0, 0, 4), ] for sample in samples { let pose = ChatWorkingClawMotion.pose(stance: sample.stance, elapsed: sample.elapsed) self.expectClose(pose.bodyRotation, sample.bodyRotation) self.expectClose(pose.xOffset, sample.xOffset) self.expectClose(pose.yOffset, sample.yOffset) self.expectClose(pose.jawRotation, sample.jawRotation) #expect(pose.bodyScale == 1) } } @Test func `working identity survives run rekey but resets for turn and session`() throws { let messageID = try #require(UUID(uuidString: "AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE")) let nextMessageID = try #require(UUID(uuidString: "11111111-2222-3333-4444-555555555555")) let local = ChatWorkingIdentity.resolve( sessionKey: self.session, pendingRunIDs: ["local-run"], localUserMessageIDsByRunID: ["local-run": messageID], fallbackGeneration: 1) let remote = ChatWorkingIdentity.resolve( sessionKey: self.session, pendingRunIDs: ["remote-run"], localUserMessageIDsByRunID: ["remote-run": messageID], fallbackGeneration: 3) let nextTurn = ChatWorkingIdentity.resolve( sessionKey: self.session, pendingRunIDs: ["next-run"], localUserMessageIDsByRunID: ["next-run": nextMessageID], fallbackGeneration: 4) let nextSession = ChatWorkingIdentity.resolve( sessionKey: "agent:other:main", pendingRunIDs: ["remote-run"], localUserMessageIDsByRunID: ["remote-run": messageID], fallbackGeneration: 3) #expect(local == remote) #expect(local != nextTurn) #expect(local != nextSession) } @Test func `phrase rotation keeps adjacent buckets distinct and visits the full list`() { let indices = (0.. ChatTurnRecapSessionRow { ChatTurnRecapSessionRow( status: "done", endedAt: endedAt, runtimeMs: runtimeMs, outputTokens: outputTokens) } private func expectClose(_ actual: CGFloat, _ expected: CGFloat) { #expect(abs(actual - expected) < 0.0001) } }