mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-05 19:11:38 +00:00
* feat(ui): gateway-owned sidebar section order with hover-only drag grips * fix(ui): repair sidebar section order CI gates * fix(protocol): keep sectionOrder optional for older gateways Native Swift clients ship separately from gateways, so new clients must decode older gateway responses that omit sectionOrder. * fix(ui): reconcile gateway section ordering with main * fix(state): allow lazy tables in v5 maintenance * refactor(ui): retire prefs session section order in favor of gateway-owned order Supersedes #113948 by deleting the unreleased ui.prefs.sessionSectionOrder key and its browser plumbing. Gateway SQLite sidebar_sections remains the single canonical store.
104 lines
3.6 KiB
Swift
104 lines
3.6 KiB
Swift
import Foundation
|
|
import OpenClawProtocol
|
|
import Testing
|
|
|
|
struct GatewayModelsCompatibilityTests {
|
|
@Test
|
|
func `optional fields stay additive around required fields`() {
|
|
let params = PluginApprovalRequestParams(
|
|
title: "Install plugin",
|
|
description: "Review requested")
|
|
|
|
#expect(params.pluginid == nil)
|
|
#expect(params.approvalreviewerdeviceids == nil)
|
|
}
|
|
|
|
@Test
|
|
func `optional fields stay additive before trailing required fields`() {
|
|
let params = MessageActionParams(
|
|
channel: "slack",
|
|
action: "member-info",
|
|
params: [:],
|
|
idempotencykey: "test")
|
|
|
|
#expect(params.accountid == nil)
|
|
#expect(params.requesteraccountid == nil)
|
|
}
|
|
|
|
@Test
|
|
func `strict literal model optional fields default to nil`() {
|
|
let result = PluginsSessionActionSuccessResult()
|
|
|
|
#expect(result.ok)
|
|
#expect(result.result == nil)
|
|
}
|
|
|
|
@Test
|
|
func `session group results decode older gateway payloads`() throws {
|
|
let list = try JSONDecoder().decode(
|
|
SessionsGroupsListResult.self,
|
|
from: Data(#"{"groups":[]}"#.utf8))
|
|
let mutation = try JSONDecoder().decode(
|
|
SessionsGroupsMutationResult.self,
|
|
from: Data(#"{"ok":true,"groups":[]}"#.utf8))
|
|
|
|
#expect(list.sectionorder == nil)
|
|
#expect(mutation.sectionorder == nil)
|
|
}
|
|
|
|
@Test
|
|
func `chat send canonical initializer stays unambiguous`() {
|
|
let params = ChatSendParams(
|
|
sessionkey: "main",
|
|
message: "hello",
|
|
idempotencykey: "test")
|
|
let legacyParams = ChatSendParams(
|
|
sessionkey: "main",
|
|
message: "hello",
|
|
fastmode: true,
|
|
idempotencykey: "test")
|
|
|
|
#expect(params.agentid == nil)
|
|
#expect(params.fastmodevalue == nil)
|
|
#expect(legacyParams.fastmode == true)
|
|
}
|
|
|
|
@Test
|
|
func `agent update model keeps legacy source compatibility and nullable wire semantics`() throws {
|
|
let legacyParams = AgentsUpdateParams(agentid: "work", model: "openai/gpt-5.6")
|
|
let omittedParams = AgentsUpdateParams(agentid: "work")
|
|
let clearedParams = AgentsUpdateParams(agentid: "work", modelvalue: AnyCodable(NSNull()))
|
|
|
|
#expect(legacyParams.model == "openai/gpt-5.6")
|
|
#expect(omittedParams.modelvalue == nil)
|
|
|
|
let legacyJSON = try #require(
|
|
JSONSerialization.jsonObject(with: JSONEncoder().encode(legacyParams))
|
|
as? [String: Any])
|
|
let omittedJSON = try #require(
|
|
JSONSerialization.jsonObject(with: JSONEncoder().encode(omittedParams))
|
|
as? [String: Any])
|
|
let clearedJSON = try #require(
|
|
JSONSerialization.jsonObject(with: JSONEncoder().encode(clearedParams))
|
|
as? [String: Any])
|
|
|
|
#expect(legacyJSON["model"] as? String == "openai/gpt-5.6")
|
|
#expect(omittedJSON["model"] == nil)
|
|
#expect(clearedJSON["model"] is NSNull)
|
|
|
|
let decodedOmitted = try JSONDecoder().decode(
|
|
AgentsUpdateParams.self,
|
|
from: Data(#"{"agentId":"work"}"#.utf8))
|
|
let decodedCleared = try JSONDecoder().decode(
|
|
AgentsUpdateParams.self,
|
|
from: Data(#"{"agentId":"work","model":null}"#.utf8))
|
|
let reencodedCleared = try #require(
|
|
JSONSerialization.jsonObject(with: JSONEncoder().encode(decodedCleared))
|
|
as? [String: Any])
|
|
|
|
#expect(decodedOmitted.modelvalue == nil)
|
|
#expect(decodedCleared.modelvalue?.value is NSNull)
|
|
#expect(reencodedCleared["model"] is NSNull)
|
|
}
|
|
}
|