Skip to content

Commit 9c5ea29

Browse files
authored
test: stabilize OpenClawKit async tests (#100243)
1 parent d467ac5 commit 9c5ea29

2 files changed

Lines changed: 57 additions & 25 deletions

File tree

apps/shared/OpenClawKit/Tests/OpenClawKitTests/ChatViewModelTests.swift

Lines changed: 55 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -767,6 +767,7 @@ extension TestChatTransportState {
767767
}
768768
}
769769

770+
@Suite(.serialized)
770771
struct ChatViewModelTests {
771772
@Test func `keeps distinct idempotent user turns with identical timestamps and content`() async throws {
772773
let history = historyPayload(
@@ -3432,17 +3433,22 @@ struct ChatViewModelTests {
34323433

34333434
await gate.open()
34343435
try await waitUntil("history reloaded after compact") {
3435-
await MainActor.run { vm.messages.first?.content.first?.text == "after compact" }
3436+
await MainActor.run {
3437+
vm.messages.first?.content.first?.text == "after compact" && !vm.isLoading
3438+
}
34363439
}
34373440

34383441
await MainActor.run {
34393442
vm.input = "/compact"
34403443
vm.send()
34413444
}
34423445

3443-
try await Task.sleep(for: .milliseconds(50))
3446+
try await waitUntil("compact cooldown rejects immediate retry") {
3447+
await MainActor.run {
3448+
vm.errorText == "Please wait before compacting this session again."
3449+
}
3450+
}
34443451
#expect(await transport.compactSessionKeys() == ["main"])
3445-
#expect(await MainActor.run { vm.errorText } == "Please wait before compacting this session again.")
34463452
}
34473453

34483454
@Test func `compact trigger allows immediate retry after failure`() async throws {
@@ -3615,6 +3621,13 @@ struct ChatViewModelTests {
36153621
modelResponses: [models])
36163622

36173623
try await loadAndWaitBootstrap(vm: vm)
3624+
try await waitUntil("model metadata bootstrap") {
3625+
await MainActor.run {
3626+
vm.showsModelPicker
3627+
&& vm.modelSelectionID == "anthropic/claude-opus-4-6"
3628+
&& vm.defaultModelLabel == "Default: openai/gpt-4.1-mini"
3629+
}
3630+
}
36183631

36193632
#expect(await MainActor.run { vm.showsModelPicker })
36203633
#expect(await MainActor.run { vm.modelSelectionID } == "anthropic/claude-opus-4-6")
@@ -3748,15 +3761,17 @@ struct ChatViewModelTests {
37483761

37493762
try await loadAndWaitBootstrap(vm: vm)
37503763

3751-
await MainActor.run {
3752-
vm.selectModel("openai/gpt-5.4")
3753-
vm.selectModel("openai/gpt-5.4-pro")
3764+
await MainActor.run { vm.selectModel("openai/gpt-5.4") }
3765+
try await waitUntil("older model patch starts") {
3766+
await transport.patchedModels() == ["openai/gpt-5.4"]
37543767
}
3768+
await MainActor.run { vm.selectModel("openai/gpt-5.4-pro") }
37553769

3756-
try await waitUntil("two model patches complete") {
3757-
let patched = await transport.patchedModels()
3758-
return patched == ["openai/gpt-5.4", "openai/gpt-5.4-pro"]
3770+
try await waitUntil("two model patches issued") {
3771+
await transport.patchedModels() == ["openai/gpt-5.4", "openai/gpt-5.4-pro"]
37593772
}
3773+
await sendUserMessage(vm, text: "after model patches")
3774+
_ = try await waitForLastSentRunId(transport)
37603775

37613776
#expect(await MainActor.run { vm.modelSelectionID } == "openai/gpt-5.4-pro")
37623777
#expect(await MainActor.run { vm.sessions.first(where: { $0.key == "main" })?.model } == "gpt-5.4-pro")
@@ -3848,10 +3863,11 @@ struct ChatViewModelTests {
38483863

38493864
try await loadAndWaitBootstrap(vm: vm)
38503865

3851-
await MainActor.run {
3852-
vm.selectModel("openai/gpt-5.4")
3853-
vm.selectModel("openai/gpt-5.4-pro")
3866+
await MainActor.run { vm.selectModel("openai/gpt-5.4") }
3867+
try await waitUntil("older model patch starts") {
3868+
await transport.patchedModels() == ["openai/gpt-5.4"]
38543869
}
3870+
await MainActor.run { vm.selectModel("openai/gpt-5.4-pro") }
38553871

38563872
try await waitUntil("older model completion wins after latest failure") {
38573873
await MainActor.run {
@@ -3899,10 +3915,11 @@ struct ChatViewModelTests {
38993915

39003916
try await loadAndWaitBootstrap(vm: vm)
39013917

3902-
await MainActor.run {
3903-
vm.selectModel("openai/gpt-5.4")
3904-
vm.selectModel("openai/gpt-5.4-pro")
3918+
await MainActor.run { vm.selectModel("openai/gpt-5.4") }
3919+
try await waitUntil("earlier model patch starts") {
3920+
await transport.patchedModels() == ["openai/gpt-5.4"]
39053921
}
3922+
await MainActor.run { vm.selectModel("openai/gpt-5.4-pro") }
39063923

39073924
try await waitUntil("latest failure restores prior successful model") {
39083925
await MainActor.run {
@@ -4971,6 +4988,8 @@ struct ChatViewModelTests {
49714988
historyResponses: [
49724989
historyPayload(sessionKey: "main", sessionId: "sess-main"),
49734990
historyPayload(sessionKey: "other", sessionId: "sess-other"),
4991+
historyPayload(sessionKey: "main", sessionId: "sess-main"),
4992+
historyPayload(sessionKey: "other", sessionId: "sess-other"),
49744993
],
49754994
sessionsResponses: [sessions, sessions],
49764995
modelResponses: [models, models],
@@ -4983,14 +5002,25 @@ struct ChatViewModelTests {
49835002
try await loadAndWaitBootstrap(vm: vm, sessionId: "sess-main")
49845003

49855004
await MainActor.run { vm.selectModel("openai/gpt-5.4") }
5005+
try await waitUntil("main session model patch starts") {
5006+
await transport.patchedModels() == ["openai/gpt-5.4"]
5007+
}
49865008
await MainActor.run { vm.switchSession(to: "other") }
49875009

49885010
try await waitUntil("switched sessions") {
49895011
await MainActor.run { vm.sessionKey == "other" && vm.sessionId == "sess-other" }
49905012
}
4991-
try await waitUntil("late model patch finished") {
4992-
let patched = await transport.patchedModels()
4993-
return patched == ["openai/gpt-5.4"]
5013+
5014+
await MainActor.run { vm.switchSession(to: "main") }
5015+
try await waitUntil("returned to original session") {
5016+
await MainActor.run { vm.sessionKey == "main" && vm.sessionId == "sess-main" }
5017+
}
5018+
await sendUserMessage(vm, text: "after late model patch")
5019+
_ = try await waitForLastSentRunId(transport)
5020+
5021+
await MainActor.run { vm.switchSession(to: "other") }
5022+
try await waitUntil("reopened other session") {
5023+
await MainActor.run { vm.sessionKey == "other" && vm.sessionId == "sess-other" }
49945024
}
49955025

49965026
#expect(await MainActor.run { vm.modelSelectionID } == OpenClawChatViewModel.defaultModelSelectionID)
@@ -5039,6 +5069,9 @@ struct ChatViewModelTests {
50395069
try await loadAndWaitBootstrap(vm: vm, sessionId: "sess-main")
50405070

50415071
await MainActor.run { vm.selectModel("openai/gpt-5.4") }
5072+
try await waitUntil("main session model patch starts") {
5073+
await transport.patchedModels() == ["openai/gpt-5.4"]
5074+
}
50425075
await MainActor.run { vm.switchSession(to: "other") }
50435076
try await waitUntil("switched to other session") {
50445077
await MainActor.run { vm.sessionKey == "other" && vm.sessionId == "sess-other" }
@@ -5399,10 +5432,11 @@ struct ChatViewModelTests {
53995432

54005433
try await loadAndWaitBootstrap(vm: vm, sessionId: "sess-main")
54015434

5402-
await MainActor.run {
5403-
vm.selectThinkingLevel("medium")
5404-
vm.selectThinkingLevel("high")
5435+
await MainActor.run { vm.selectThinkingLevel("medium") }
5436+
try await waitUntil("older thinking patch starts") {
5437+
await transport.patchedThinkingLevels() == ["medium"]
54055438
}
5439+
await MainActor.run { vm.selectThinkingLevel("high") }
54065440

54075441
try await waitUntil("thinking patch replayed latest selection") {
54085442
let patched = await transport.patchedThinkingLevels()

apps/shared/OpenClawKit/Tests/OpenClawKitTests/TalkConfigContractTests.swift

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ private struct TalkConfigContractFixture: Decodable {
99
struct SelectionCase: Decodable {
1010
let id: String
1111
let defaultProvider: String
12-
let payloadValid: Bool
1312
let expectedSelection: ExpectedSelection?
1413
let talk: [String: AnyCodable]
1514

@@ -69,7 +68,7 @@ private enum TalkConfigContractFixtureLoader {
6968
}
7069

7170
struct TalkConfigContractTests {
72-
@Test func selectionFixtures() throws {
71+
@Test func `selection fixtures`() throws {
7372
for fixture in try TalkConfigContractFixtureLoader.load().selectionCases {
7473
let selection = TalkConfigParsing.selectProviderConfig(
7574
fixture.gatewayResponseTalk,
@@ -83,11 +82,10 @@ struct TalkConfigContractTests {
8382
} else {
8483
#expect(selection == nil)
8584
}
86-
#expect(fixture.payloadValid == (selection != nil))
8785
}
8886
}
8987

90-
@Test func timeoutFixtures() throws {
88+
@Test func `timeout fixtures`() throws {
9189
for fixture in try TalkConfigContractFixtureLoader.load().timeoutCases {
9290
#expect(
9391
TalkConfigParsing.resolvedSilenceTimeoutMs(

0 commit comments

Comments
 (0)