Files
clawpanel/tests/kernel.test.js
晴天 328624cf03 chore: release v0.15.0
发布 0.15.0:
- 新增内核版本兼容层、特性门控、低版本阻断和升级提示
- 新增 PATH 中 OpenClaw CLI 冲突检测、隔离与恢复
- 修复 Hermes Gateway loopback 自动拉起与 /v1/runs 诊断
- 修复 standalone 一键安装包在 About/仪表盘显示未知版本
- 同步 OpenClaw 2026.5.6 推荐版本和热更新 minAppVersion
- 补齐本地 JS/Rust 测试与发布前检查说明

验证:
- npm run build
- node --test tests/*.test.js
- node --check src/scripts JS 文件
- cargo fmt --all -- --check
- cargo check
- cargo clippy --all-targets -- -D warnings
- cargo test
2026-05-08 04:39:36 +08:00

199 lines
7.9 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 内核版本与特性门控的单元测试
*
* 运行node --test tests/kernel.test.js
*
* 注意:本测试只覆盖 kernel.js 中的 **纯函数**parseVersion / versionGte / buildSnapshot
* 涉及 wsClient 订阅 / DOM 的状态函数无法在 node 环境直接测试,留给 e2e。
*/
import test from 'node:test'
import assert from 'node:assert/strict'
import { parseVersion, versionGte, buildSnapshot } from '../src/lib/kernel.js'
import { FEATURE_CATALOG, KERNEL_FLOOR } from '../src/lib/feature-catalog.js'
// ============================================================================
// parseVersion
// ============================================================================
test('parseVersion handles standard semver', () => {
assert.deepEqual(parseVersion('1.2.3'), [1, 2, 3])
assert.deepEqual(parseVersion('0.0.1'), [0, 0, 1])
assert.deepEqual(parseVersion('2026.5.6'), [2026, 5, 6])
})
test('parseVersion strips -zh / -beta suffix', () => {
assert.deepEqual(parseVersion('2026.5.6-zh.2'), [2026, 5, 6])
assert.deepEqual(parseVersion('2026.5.6-beta.1'), [2026, 5, 6])
assert.deepEqual(parseVersion('1.0.0-rc.1'), [1, 0, 0])
})
test('parseVersion pads short version', () => {
assert.deepEqual(parseVersion('1'), [1, 0, 0])
assert.deepEqual(parseVersion('1.2'), [1, 2, 0])
})
test('parseVersion returns null on invalid input', () => {
assert.equal(parseVersion(null), null)
assert.equal(parseVersion(''), null)
assert.equal(parseVersion(undefined), null)
assert.equal(parseVersion('not-a-version'), null)
assert.equal(parseVersion('a.b.c'), null)
})
test('parseVersion only takes first 3 segments', () => {
assert.deepEqual(parseVersion('1.2.3.4.5'), [1, 2, 3])
})
// ============================================================================
// versionGte
// ============================================================================
test('versionGte returns true for equal versions', () => {
assert.equal(versionGte('1.0.0', '1.0.0'), true)
assert.equal(versionGte('2026.5.6', '2026.5.6'), true)
})
test('versionGte returns true for higher major', () => {
assert.equal(versionGte('2.0.0', '1.99.99'), true)
assert.equal(versionGte('2026.0.0', '2025.99.99'), true)
})
test('versionGte returns true for higher minor when major equal', () => {
assert.equal(versionGte('1.5.0', '1.4.99'), true)
assert.equal(versionGte('2026.5.0', '2026.4.21'), true)
})
test('versionGte returns true for higher patch when major+minor equal', () => {
assert.equal(versionGte('1.0.5', '1.0.4'), true)
assert.equal(versionGte('2026.5.6', '2026.5.5'), true)
})
test('versionGte returns false for lower versions', () => {
assert.equal(versionGte('1.0.0', '2.0.0'), false)
assert.equal(versionGte('2026.4.9', '2026.5.6'), false)
assert.equal(versionGte('2026.3.2', '2026.5.6'), false)
})
test('versionGte ignores -zh suffix correctly', () => {
assert.equal(versionGte('2026.5.6-zh.2', '2026.5.6'), true)
assert.equal(versionGte('2026.5.6', '2026.5.6-zh.2'), true)
assert.equal(versionGte('2026.4.9-zh.2', '2026.5.6'), false)
})
test('versionGte returns false when input is unparseable', () => {
assert.equal(versionGte(null, '1.0.0'), false)
assert.equal(versionGte('1.0.0', null), false)
assert.equal(versionGte('foo', 'bar'), false)
})
// ============================================================================
// buildSnapshot
// ============================================================================
test('buildSnapshot constructs correct shape for known engine + version', () => {
const snap = buildSnapshot('openclaw', '2026.5.6')
assert.equal(snap.engine, 'openclaw')
assert.equal(snap.version, '2026.5.6')
assert.equal(snap.versionBase, '2026.5.6')
assert.equal(snap.variant, 'official')
assert.equal(snap.aboveFloor, true)
assert.equal(snap.floor, KERNEL_FLOOR.openclaw)
assert.ok(snap.features instanceof Set)
})
test('buildSnapshot detects chinese variant', () => {
const snap = buildSnapshot('openclaw', '2026.5.6-zh.2')
assert.equal(snap.variant, 'chinese')
assert.equal(snap.versionBase, '2026.5.6')
assert.equal(snap.versionLabel, '2026.5.6 汉化')
})
test('buildSnapshot.features contains 5.6 features for 5.6 kernel', () => {
const snap = buildSnapshot('openclaw', '2026.5.6')
assert.ok(snap.features.has('sessions.truncation'), 'sessions.truncation should be enabled on 5.6')
assert.ok(snap.features.has('agents.runtime'), 'agents.runtime should be enabled on 5.6')
assert.ok(snap.features.has('memory.statusDeepSplit'), 'memory.statusDeepSplit should be enabled on 5.6')
assert.ok(snap.features.has('doctor.deepSupervisor'), 'doctor.deepSupervisor should be enabled on 5.6')
})
test('buildSnapshot.features excludes 5.6 features on 4.9 kernel', () => {
const snap = buildSnapshot('openclaw', '2026.4.9')
assert.equal(snap.features.has('sessions.truncation'), false, 'sessions.truncation should NOT be enabled on 4.9')
assert.equal(snap.features.has('agents.runtime'), false, 'agents.runtime should NOT be enabled on 4.9')
assert.equal(snap.features.has('memory.statusDeepSplit'), false)
assert.equal(snap.features.has('doctor.deepSupervisor'), false)
})
test('buildSnapshot.aboveFloor is false for kernel below floor', () => {
const snap = buildSnapshot('openclaw', '2026.2.0')
assert.equal(snap.aboveFloor, false, '2026.2.0 should be below floor 2026.3.2')
})
test('buildSnapshot.aboveFloor is true at exactly floor', () => {
const snap = buildSnapshot('openclaw', KERNEL_FLOOR.openclaw)
assert.equal(snap.aboveFloor, true)
})
test('buildSnapshot returns null version when input is null', () => {
const snap = buildSnapshot('openclaw', null)
assert.equal(snap.version, null)
assert.equal(snap.aboveFloor, false)
assert.equal(snap.features.size, 0, 'no features enabled when version unknown')
})
test('buildSnapshot.features only includes current engine', () => {
const snap = buildSnapshot('hermes', '0.13.0')
// openclaw 特性应该全部被排除
for (const id of snap.features) {
const def = FEATURE_CATALOG[id]
assert.equal(def.engine, 'hermes', `${id} should belong to hermes engine`)
}
})
test('buildSnapshot edge case: version slightly below 5.6 feature requirement', () => {
// sessions.truncation requires 2026.5.4
const at = buildSnapshot('openclaw', '2026.5.4')
const below = buildSnapshot('openclaw', '2026.5.3')
assert.equal(at.features.has('sessions.truncation'), true)
assert.equal(below.features.has('sessions.truncation'), false)
})
test('buildSnapshot.isLatest works against KERNEL_TARGET', () => {
const at_target = buildSnapshot('openclaw', '2026.5.6')
const above_target = buildSnapshot('openclaw', '2026.6.0')
const below_target = buildSnapshot('openclaw', '2026.5.5')
assert.equal(at_target.isLatest, true)
assert.equal(above_target.isLatest, true)
assert.equal(below_target.isLatest, false)
})
// ============================================================================
// FEATURE_CATALOG sanity
// ============================================================================
test('FEATURE_CATALOG: every entry has engine and minVersion', () => {
for (const [id, def] of Object.entries(FEATURE_CATALOG)) {
assert.ok(def.engine, `${id} missing engine`)
assert.ok(def.minVersion, `${id} missing minVersion`)
assert.ok(parseVersion(def.minVersion), `${id} has unparseable minVersion: ${def.minVersion}`)
}
})
test('FEATURE_CATALOG: id format is <area>.<feature> camelCase', () => {
for (const id of Object.keys(FEATURE_CATALOG)) {
assert.match(id, /^[a-z]+\.[a-zA-Z0-9]+$/, `${id} should match <area>.<featureCamelCase>`)
}
})
test('FEATURE_CATALOG: all openclaw features minVersion >= floor', () => {
for (const [id, def] of Object.entries(FEATURE_CATALOG)) {
if (def.engine !== 'openclaw') continue
assert.equal(
versionGte(def.minVersion, KERNEL_FLOOR.openclaw),
true,
`${id} minVersion ${def.minVersion} is below floor ${KERNEL_FLOOR.openclaw}`,
)
}
})