From e0e2184b900144f25e3a9fbfd1d5289b3cf26e97 Mon Sep 17 00:00:00 2001 From: Sebastian <19554889+sebslight@users.noreply.github.com> Date: Tue, 17 Feb 2026 10:42:42 -0500 Subject: [PATCH] test(release): add appcast regression coverage --- CHANGELOG.md | 2 ++ test/appcast.test.ts | 27 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 test/appcast.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 4eaa4c8afb..a9987ea0f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,8 @@ Docs: https://docs.openclaw.ai - Agents: revert accidental per-model thinkingDefault override merge. (#19195) Thanks @sebslight. - Sessions: revert accidental session transcript permission hardening from PR #18288. (#19224) Thanks @sebslight. +- macOS/Update: correct the Sparkle appcast version for 2026.2.15 so updates are offered again. (#18201) +- Gateway/Auth: clear stale device-auth tokens after device token mismatch errors so re-paired clients can re-auth. (#18201) - Voice call/Gateway: prevent overlapping closed-loop turn races with per-call turn locking, route transcript dedupe via source-aware fingerprints with strict cache eviction bounds, and harden `voicecall latency` stats for large logs without spread-operator stack overflow. (#19140) Thanks @mbelinky. - iOS/Onboarding: stop auth Step 3 retry-loop churn by pausing reconnect attempts on unauthorized/missing-token gateway errors and keeping auth/pairing issue state sticky during manual retry. (#19153) Thanks @mbelinky. - Fix types in all tests. Typecheck the whole repository. diff --git a/test/appcast.test.ts b/test/appcast.test.ts new file mode 100644 index 0000000000..d8534c8744 --- /dev/null +++ b/test/appcast.test.ts @@ -0,0 +1,27 @@ +import { readFileSync } from "node:fs"; +import { describe, expect, it } from "vitest"; + +const APPCAST_URL = new URL("../appcast.xml", import.meta.url); + +function expectedSparkleVersion(shortVersion: string): string { + const [year, month, day] = shortVersion.split("."); + if (!year || !month || !day) { + throw new Error(`unexpected short version: ${shortVersion}`); + } + return `${year}${month.padStart(2, "0")}${day.padStart(2, "0")}0`; +} + +describe("appcast.xml", () => { + it("uses the expected Sparkle version for 2026.2.15", () => { + const appcast = readFileSync(APPCAST_URL, "utf8"); + const shortVersion = "2026.2.15"; + const items = Array.from(appcast.matchAll(/[\s\S]*?<\/item>/g)).map((match) => match[0]); + const matchingItem = items.find((item) => + item.includes(`${shortVersion}`), + ); + + expect(matchingItem).toBeDefined(); + const sparkleMatch = matchingItem?.match(/([^<]+)<\/sparkle:version>/); + expect(sparkleMatch?.[1]).toBe(expectedSparkleVersion(shortVersion)); + }); +});