import test from 'node:test'; import assert from 'node:assert/strict'; import { readFileSync, mkdtempSync, writeFileSync, rmSync, mkdirSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { spawnSync } from 'node:child_process'; const release = readFileSync(new URL('./scripts/release-portal-runtime-prod.sh', import.meta.url), 'utf8'); const start = release.indexOf('remount_goosed_after_live_swap() {'); const end = release.indexOf('\n mkdir -p "${APP_DIR}"', release.indexOf('\n return 0\n fi', start)); const nativeFunction = release.slice(start, end) .replace('if [[ ! -x "${docker_bin}" || ! -f "${compose_file}" ]]; then', 'if true; then') + '\n}'; function run(targets, unavailable = '') { const root = mkdtempSync(join(tmpdir(), 'portal-targets-')); try { writeFileSync(join(root, '.env'), `TKMIND_API_TARGETS=${targets}\n`); writeFileSync(join(root, 'mindspace-sandbox-mcp.mjs'), ''); const result = spawnSync('/bin/bash', ['-c', ` set -euo pipefail APP_DIR="$1"; RELEASE_ID=test; unavailable="$2" say() { :; } launchctl() { printf 'restart=%s\\n' "$*" >> "$APP_DIR/restarts"; } sleep() { :; } curl() { case "$*" in *"$unavailable"*) return 1;; esac; printf ok; } # An empty unavailable marker must not match every request. if [[ -z "$2" ]]; then curl() { printf ok; }; fi ${nativeFunction} remount_goosed_after_live_swap `, 'test', root, unavailable], { encoding: 'utf8' }); try { result.stdout += readFileSync(join(root, 'restarts'), 'utf8'); } catch {} return result; } finally { rmSync(root, { recursive: true, force: true }); } } test('native remount restarts only the two configured targets', () => { const result = run('https://127.0.0.1:18006,https://127.0.0.1:18007'); assert.equal(result.status, 0, result.stderr); assert.match(result.stdout, /native-18006/); assert.match(result.stdout, /native-18007/); assert.doesNotMatch(result.stdout, /native-18008/); }); test('quoted targets and localhost are supported', () => { assert.equal(run('"https://127.0.0.1:18006,https://localhost:18007"').status, 0); }); for (const targets of ['', 'https://example.com:18006', 'https://127.0.0.1:99999']) { test(`invalid configured targets fail closed: ${targets || '(empty)'}`, () => { const result = run(targets); assert.notEqual(result.status, 0); assert.match(result.stderr, /goosed remount failed/); assert.doesNotMatch(result.stdout, /restart=/); }); } test('an unavailable configured target still blocks remount', () => { const result = run('https://127.0.0.1:18006,https://127.0.0.1:18007', '18007'); assert.notEqual(result.status, 0); assert.match(result.stderr, /did not become healthy/); }); test('release tar excludes transient skills locks while keeping user content', () => { const root = mkdtempSync(join(tmpdir(), 'portal-backup-')); try { const owner = join(root, 'Memind', 'MindSpace', 'user'); mkdirSync(join(owner, '.agents', '.skills-sync.lock'), { recursive: true }); writeFileSync(join(owner, '.agents', '.skills-sync.lock', 'owner'), 'transient'); writeFileSync(join(owner, 'page.html'), 'persistent page'); const archive = join(root, 'backup.tar'); const exclusion = release.match(/tar --exclude='Memind\/\.tailscale\/\*\.sock' --exclude='([^']+)'/); assert.ok(exclusion, 'full backup explicitly excludes transient skills lock'); const packed = spawnSync('tar', [`--exclude=${exclusion[1]}`, '-cf', archive, '-C', root, 'Memind'], { encoding: 'utf8' }); assert.equal(packed.status, 0, packed.stderr); const listed = spawnSync('tar', ['-tf', archive], { encoding: 'utf8' }); assert.equal(listed.status, 0, listed.stderr); assert.match(listed.stdout, /page\.html/); assert.doesNotMatch(listed.stdout, /skills-sync\.lock/); } finally { rmSync(root, { recursive: true, force: true }); } });