mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-28 09:01:12 +00:00
* fix(apps): address native Skills review feedback * feat(apps): add native Automations parity * fix(ios): preserve automation editor selection * fix(android): page automation discovery * chore(apps): refresh native source inventory * fix(ios): refresh invalid automation diagnostics * chore(apps): sync native localization * fix(cron): stabilize paginated snapshots * refactor(cron): split pagination helpers * fix(apps): address final native review feedback * chore(apps): refresh native source inventory * fix(ios): dedupe automation list actions * chore: keep release note in PR context * fix(ci): repair current main architecture gates * fix(ios): default new skill requirement fields * fix(ios): retain queued automation reservations
135 lines
5.7 KiB
Swift
135 lines
5.7 KiB
Swift
import Foundation
|
|
import OpenClawProtocol
|
|
import Testing
|
|
@testable import OpenClawKit
|
|
|
|
struct SkillManagementTests {
|
|
@Test func `detail review uses exact detail version and publisher`() throws {
|
|
let data = Data(
|
|
#"{"skill":{"displayName":"Weather","summary":"Forecasts"},"latestVersion":{"version":"2.0.0"},"owner":{"handle":"molly","displayName":"Molly"}}"#
|
|
.utf8)
|
|
let fallbackData = Data(
|
|
#"{"slug":"weather","displayName":"Old Weather","summary":null,"version":"1.0.0"}"#.utf8)
|
|
let detail = try JSONDecoder().decode(ClawHubSkillDetail.self, from: data)
|
|
let fallback = try JSONDecoder().decode(ClawHubSkillSummary.self, from: fallbackData)
|
|
let review = try #require(ClawHubSkillInstallReview(
|
|
detail: detail,
|
|
fallback: fallback))
|
|
|
|
#expect(review.slug == "@molly/weather")
|
|
#expect(review.displayName == "Weather")
|
|
#expect(review.version == "2.0.0")
|
|
#expect(review.author == "Molly")
|
|
}
|
|
|
|
@Test func `risk acknowledgement stays bound to reviewed version`() {
|
|
let matching = GatewayResponseError(
|
|
method: "skills.install",
|
|
code: "UNAVAILABLE",
|
|
message: "Review warning",
|
|
details: [
|
|
"clawhubTrustCode": AnyCodable("clawhub_risk_acknowledgement_required"),
|
|
"version": AnyCodable("2.0.0"),
|
|
"warning": AnyCodable("Automated analysis found risky behavior."),
|
|
])
|
|
let accepted = SkillManagementContract.rejection(from: matching, attemptedVersion: "2.0.0")
|
|
#expect(accepted.requiresAcknowledgement)
|
|
#expect(accepted.acknowledgeVersion == "2.0.0")
|
|
|
|
let stale = SkillManagementContract.rejection(from: matching, attemptedVersion: "1.0.0")
|
|
#expect(!stale.requiresAcknowledgement)
|
|
#expect(stale.acknowledgeVersion == nil)
|
|
#expect(stale.message.contains("different ClawHub release"))
|
|
}
|
|
|
|
@Test func `missing requirements preserve alternatives and platforms`() throws {
|
|
let data = Data(#"{"bins":[],"anyBins":["rg","grep"],"env":[],"config":[],"os":["darwin"]}"#.utf8)
|
|
let missing = try JSONDecoder().decode(SkillMissing.self, from: data)
|
|
|
|
#expect(missing.anyBins == ["rg", "grep"])
|
|
#expect(missing.os == ["darwin"])
|
|
}
|
|
|
|
@Test func `legacy requirements default new fields to empty`() throws {
|
|
let data = Data(#"{"bins":["rg"],"env":[],"config":[]}"#.utf8)
|
|
let requirements = try JSONDecoder().decode(SkillRequirements.self, from: data)
|
|
let missing = try JSONDecoder().decode(SkillMissing.self, from: data)
|
|
|
|
#expect(requirements.anyBins.isEmpty)
|
|
#expect(requirements.os.isEmpty)
|
|
#expect(missing.anyBins.isEmpty)
|
|
#expect(missing.os.isEmpty)
|
|
}
|
|
|
|
@Test func `qualified install remains busy for unqualified browse row`() {
|
|
#expect(SkillManagementContract.sameClawHubSkill("@molly/weather", "weather"))
|
|
#expect(!SkillManagementContract.sameClawHubSkill("@molly/weather", "@alice/weather"))
|
|
}
|
|
|
|
@Test func `installed readback requires valid provenance and exact version`() {
|
|
let linked = Self.skill(
|
|
clawhub: ClawHubInstalledSkillLink(
|
|
status: "linked",
|
|
valid: true,
|
|
slug: "@molly/weather",
|
|
ownerHandle: "molly",
|
|
installedVersion: "2.0.0",
|
|
reason: nil))
|
|
#expect(SkillManagementContract.installed([linked], slug: "weather", version: "2.0.0"))
|
|
#expect(!SkillManagementContract.installed([linked], slug: "weather", version: "2.0.1"))
|
|
#expect(SkillManagementContract.installed([linked], slug: "weather"))
|
|
}
|
|
|
|
@Test func `owner qualified readback matches split provenance identity`() {
|
|
let linked = Self.skill(
|
|
clawhub: ClawHubInstalledSkillLink(
|
|
status: "linked",
|
|
valid: true,
|
|
slug: "weather",
|
|
ownerHandle: "molly",
|
|
installedVersion: "2.0.0",
|
|
reason: nil))
|
|
#expect(SkillManagementContract.installed([linked], slug: "@molly/weather", version: "2.0.0"))
|
|
#expect(!SkillManagementContract.installed([linked], slug: "@other/weather", version: "2.0.0"))
|
|
}
|
|
|
|
@Test func `agent filtered skills need setup instead of reporting ready`() {
|
|
let blocked = Self.skill(clawhub: nil, blockedByAgentFilter: true)
|
|
#expect(!SkillManagementContract.ready(blocked))
|
|
#expect(SkillManagementContract.needsSetup(blocked))
|
|
}
|
|
|
|
@Test func `platform incompatible skills need setup instead of reporting ready`() {
|
|
let blocked = Self.skill(clawhub: nil, platformIncompatible: true)
|
|
#expect(!SkillManagementContract.ready(blocked))
|
|
#expect(SkillManagementContract.needsSetup(blocked))
|
|
}
|
|
|
|
private static func skill(
|
|
clawhub: ClawHubInstalledSkillLink?,
|
|
blockedByAgentFilter: Bool? = nil,
|
|
platformIncompatible: Bool? = nil) -> SkillStatus
|
|
{
|
|
SkillStatus(
|
|
name: "Weather",
|
|
description: "Forecasts",
|
|
source: "openclaw-managed",
|
|
filePath: "/tmp/weather/SKILL.md",
|
|
baseDir: "/tmp/weather",
|
|
skillKey: "weather",
|
|
primaryEnv: nil,
|
|
emoji: "☀️",
|
|
homepage: nil,
|
|
always: false,
|
|
disabled: false,
|
|
blockedByAgentFilter: blockedByAgentFilter,
|
|
platformIncompatible: platformIncompatible,
|
|
eligible: true,
|
|
requirements: SkillRequirements(bins: [], env: [], config: []),
|
|
missing: SkillMissing(bins: [], env: [], config: []),
|
|
configChecks: [],
|
|
install: [],
|
|
clawhub: clawhub)
|
|
}
|
|
}
|