mirror of
https://github.com/zhu1090093659/dsh-web.git
synced 2026-09-29 17:08:39 +08:00
- task-board: pick the newest SETTLED execution for session reuse; the launch path appends this run's open record before the rule reads it (#1587) - plugin-manager: resolve the packaged desktop runtime's own lib/bin.js and run it through the host interpreter when no PATH entry or .bin shim exists (#1588) - plugin cards: contribute to whichever plugin-card seat the host declares (family list seat, else the official keyed settings.plugin.item seat) and log a refused registration instead of swallowing it (#1589) - usage: sidebar entry + collapsible panel over the same overview document, with the family pairing fence on the host routes (#1592) - usage: serve the zai route, key plan windows by the provider unit, use the exact credit ratio, and skip TIME_LIMIT/unknown units (#1597) - plugin-manager + remote-web-ui: accumulate child output as bytes and decode once, so a Windows CP936 console renders readable text (#1600)
90 lines
5.7 KiB
JavaScript
90 lines
5.7 KiB
JavaScript
import { mkdtemp, mkdir, readFile, rm, writeFile } from 'node:fs/promises'
|
|
import { tmpdir } from 'node:os'
|
|
import { dirname, join } from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
import assert from 'node:assert/strict'
|
|
import { test } from 'node:test'
|
|
import { REPO_ROOT, applySync, checkSync, copyEntries, headerFor, renderCopy, stripHeader } from './sync-shared.mjs'
|
|
|
|
test('header/strip round-trips every file kind', () => {
|
|
for (const file of ['settings-form.ts', 'PluginSettingsCard.tsx', 'settings-card.module.css']) {
|
|
const source = 'export const x = 1' + String.fromCharCode(10)
|
|
const sourceRel = 'shared/client/settings/' + file
|
|
const rendered = renderCopy(source, file, sourceRel)
|
|
assert.ok(rendered.startsWith(headerFor(file, sourceRel)))
|
|
assert.equal(stripHeader(rendered, file, sourceRel), source)
|
|
assert.equal(stripHeader('mangled ' + rendered.slice(10), file, sourceRel), undefined)
|
|
}
|
|
})
|
|
|
|
test('copies cover the settings trio for all consumers plus host and http helpers', () => {
|
|
// Normalize separators: node:path join yields backslashes on Windows, and
|
|
// the copy-count buckets below match on forward slashes.
|
|
const entries = copyEntries().map(entry => ({ ...entry, target: entry.target.replaceAll('\\', '/') }))
|
|
// The total is every generated copy in the manifest; the single-instance
|
|
// guard alone contributes one mount-once.ts per host half (17 today). The
|
|
// buckets below partition the same set by target location.
|
|
assert.equal(entries.length, 127)
|
|
const clientTrio = entries.filter(entry => entry.target.includes('/src/client/'))
|
|
assert.equal(clientTrio.length, 51)
|
|
const hostCopies = entries.filter(entry => entry.target.includes('/src/host/')
|
|
|| entry.target.includes('/src/dsh-home.ts')
|
|
|| entry.target.includes('/src/mount-once.ts')
|
|
|| entry.target.includes('/src/loopback.ts')
|
|
|| entry.target.includes('/src/pair-access.ts')
|
|
|| entry.target.includes('/src/agent/')
|
|
|| entry.target.endsWith('/packages/dsh-task-board/src/http.ts'))
|
|
assert.equal(hostCopies.length, 63)
|
|
})
|
|
|
|
test('checkSync detects drift and applySync repairs it', async () => {
|
|
const root = await mkdtemp(join(tmpdir(), 'sync-shared-test-'))
|
|
try {
|
|
// Fake tree: shared source + one consumer copy with wrong content.
|
|
const sourceDir = join(root, 'shared', 'client', 'settings')
|
|
await mkdir(sourceDir, { recursive: true })
|
|
await writeFile(join(sourceDir, 'settings-form.ts'), 'export const good = 1' + String.fromCharCode(10))
|
|
await writeFile(join(sourceDir, 'PluginSettingsCard.tsx'), 'export const card = 1' + String.fromCharCode(10))
|
|
await writeFile(join(sourceDir, 'settings-card.module.css'), '.card { color: red }' + String.fromCharCode(10))
|
|
await writeFile(join(sourceDir, 'plugin-card-seat.ts'), 'export const seat = 1' + String.fromCharCode(10))
|
|
await writeFile(join(root, 'shared', 'client', 'telemetry.ts'), 'export const beat = 1' + String.fromCharCode(10))
|
|
await writeFile(join(root, 'shared', 'client', 'sse-leader.ts'), 'export const leader = 1' + String.fromCharCode(10))
|
|
await writeFile(join(root, 'shared', 'client', 'sidebar-entry-core.ts'), 'export const sidecore = 1' + String.fromCharCode(10))
|
|
await writeFile(join(root, 'shared', 'client', 'panel-mount-core.ts'), 'export const panelcore = 1' + String.fromCharCode(10))
|
|
await writeFile(join(root, 'shared', 'client', 'body-mutations.ts'), 'export const hub = 1' + String.fromCharCode(10))
|
|
const hostDir = join(root, 'shared', 'host')
|
|
await mkdir(hostDir, { recursive: true })
|
|
await writeFile(join(hostDir, 'poll-guard.ts'), 'export const guard = 1' + String.fromCharCode(10))
|
|
await writeFile(join(hostDir, 'dsh-home.ts'), 'export const home = 1' + String.fromCharCode(10))
|
|
await writeFile(join(hostDir, 'loopback.ts'), 'export const loop = 1' + String.fromCharCode(10))
|
|
await writeFile(join(hostDir, 'pair-access.ts'), 'export const fence = 1' + String.fromCharCode(10))
|
|
await writeFile(join(root, 'shared', 'vitest.setup.ts'), 'export {}' + String.fromCharCode(10))
|
|
await writeFile(join(hostDir, 'git-runner.ts'), 'export const runner = 1' + String.fromCharCode(10))
|
|
await writeFile(join(hostDir, 'mount-once.ts'), 'export const once = 1' + String.fromCharCode(10))
|
|
await writeFile(join(hostDir, 'http.ts'), 'export const http = 1' + String.fromCharCode(10))
|
|
await writeFile(join(hostDir, 'legacy-migration.ts'), 'export const legacy = 1' + String.fromCharCode(10))
|
|
await writeFile(join(hostDir, 'run-guarded.ts'), 'export const guard2 = 1' + String.fromCharCode(10))
|
|
await writeFile(join(hostDir, 'console-output.ts'), 'export const capture = 1' + String.fromCharCode(10))
|
|
const targetDir = join(root, 'packages', 'dsh-pet', 'src', 'client')
|
|
await mkdir(targetDir, { recursive: true })
|
|
await writeFile(join(targetDir, 'settings-form.ts'), renderCopy('export const bad = 2' + String.fromCharCode(10), 'settings-form.ts', 'shared/client/settings/settings-form.ts'))
|
|
|
|
// checkSync compares all consumers; the missing files count as drift too.
|
|
const before = await checkSync(root)
|
|
assert.ok(before.some(entry => entry.reason === 'content drifted from shared source'))
|
|
assert.ok(before.some(entry => entry.reason === 'missing'))
|
|
|
|
await applySync(root)
|
|
const after = await checkSync(root)
|
|
assert.deepEqual(after, [])
|
|
const fixed = await readFile(join(targetDir, 'settings-form.ts'), 'utf8')
|
|
assert.equal(stripHeader(fixed, 'settings-form.ts', 'shared/client/settings/settings-form.ts'), 'export const good = 1' + String.fromCharCode(10))
|
|
} finally {
|
|
await rm(root, { recursive: true, force: true })
|
|
}
|
|
})
|
|
|
|
test('the live tree is in sync', async () => {
|
|
const drift = await checkSync()
|
|
assert.deepEqual(drift, [])
|
|
}) |