Files
trackgo-fe/.agents/skills/archify/test/stable-update-manifest.test.mjs
T
shancheas 166e0d40ac feat: introduce archify skill for generating architecture diagrams
- Added a new Archify skill, enabling users to create polished architecture, workflow, sequence, data-flow, and lifecycle diagrams.
- Implemented comprehensive functionality including rendering, validation, and delivery of diagrams in various formats.
- Integrated a user-friendly command-line interface for generating and previewing diagrams.
- Developed supporting files including package.json, LICENSE, and SKILL.md for documentation and licensing.
- Added unit tests to ensure reliability and functionality of the new skill.

These changes enhance the application by providing a structured approach to visualizing system architecture and workflows, improving user experience and data representation.
2026-08-31 11:31:29 +07:00

168 lines
6.5 KiB
JavaScript

import assert from 'node:assert/strict';
import crypto from 'node:crypto';
import { spawnSync } from 'node:child_process';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import test from 'node:test';
import { fileURLToPath } from 'node:url';
const here = path.dirname(fileURLToPath(import.meta.url));
const repoRoot = path.resolve(here, '..', '..');
const checker = path.join(repoRoot, 'scripts', 'check-stable-update-manifest.mjs');
function git(root, args) {
return spawnSync('git', args, { cwd: root, encoding: 'utf8' });
}
function annotatedTaggerTime(root, tag) {
const result = git(root, [
'for-each-ref',
'--format=%(taggerdate:unix)',
`refs/tags/${tag}`,
]);
assert.equal(result.status, 0, result.stderr);
assert.match(result.stdout.trim(), /^\d+$/);
return new Date(Number(result.stdout.trim()) * 1_000)
.toISOString()
.replace('.000Z', 'Z');
}
function writeJson(target, value) {
fs.mkdirSync(path.dirname(target), { recursive: true });
fs.writeFileSync(target, `${JSON.stringify(value, null, 2)}\n`);
}
function runCheck(root, archive, extraArguments = []) {
return spawnSync(process.execPath, [
checker,
'--root', root,
'--archive', archive,
'--tag', 'v3.0.0',
...extraArguments,
], { encoding: 'utf8' });
}
test('stable release gate binds manifest tag, tree, and final archive digest', () => {
const fixture = fs.mkdtempSync(path.join(os.tmpdir(), 'archify-stable-manifest-'));
try {
writeJson(path.join(fixture, 'archify', 'package.json'), { version: '3.0.0' });
fs.writeFileSync(path.join(fixture, 'archify', 'SKILL.md'), 'stable fixture\n');
assert.equal(git(fixture, ['init']).status, 0);
assert.equal(git(fixture, ['add', 'archify']).status, 0);
assert.equal(git(fixture, [
'-c', 'user.name=Archify Test',
'-c', 'user.email=archify@example.invalid',
'commit', '-m', 'stable fixture',
]).status, 0);
assert.equal(git(fixture, [
'-c', 'user.name=Archify Test',
'-c', 'user.email=archify@example.invalid',
'tag', '-a', 'v3.0.0', '-m', 'stable v3.0.0',
]).status, 0);
const publishedAt = annotatedTaggerTime(fixture, 'v3.0.0');
const tree = git(fixture, ['rev-parse', 'HEAD:archify']);
assert.equal(tree.status, 0, tree.stderr);
const treeSha = tree.stdout.trim();
const archive = path.join(fixture, 'archify.zip');
const archiveBytes = Buffer.from('deterministic stable archive fixture');
fs.writeFileSync(archive, archiveBytes);
const artifactSha = crypto.createHash('sha256').update(archiveBytes).digest('hex');
const manifestPath = path.join(fixture, 'docs', 'skill-updates', 'archify', 'stable.json');
const manifest = {
schemaVersion: 1,
skillId: 'archify',
channel: 'stable',
version: '3.0.0',
publishedAt,
source: {
repository: 'https://github.com/tt-a1i/archify',
ref: 'v3.0.0',
treeSha,
},
artifact: { sha256: artifactSha },
summary: 'Stable release fixture.',
releaseNotes: 'https://github.com/tt-a1i/archify/releases/tag/v3.0.0',
severity: 'normal',
};
writeJson(manifestPath, manifest);
const passing = runCheck(fixture, archive);
assert.equal(passing.status, 0, passing.stderr);
assert.match(passing.stdout, /stable update manifest ok: v3\.0\.0/);
writeJson(path.join(fixture, 'archify', 'package.json'), { version: '4.0.0-dev.0' });
const historicalRelease = runCheck(fixture, archive, ['--source-ref', 'v3.0.0']);
assert.equal(historicalRelease.status, 0, historicalRelease.stderr);
const wrongHistoricalRef = runCheck(fixture, archive, ['--source-ref', 'v2.9.0']);
assert.notEqual(wrongHistoricalRef.status, 0);
assert.match(wrongHistoricalRef.stderr, /--source-ref must be HEAD or the exact release tag/);
writeJson(path.join(fixture, 'archify', 'package.json'), { version: '3.0.0' });
writeJson(manifestPath, {
...manifest,
publishedAt: '2026-08-28T08:00:00+08:00',
});
const nonUtcTimestamp = runCheck(fixture, archive);
assert.notEqual(nonUtcTimestamp.status, 0);
assert.match(nonUtcTimestamp.stderr, /stable update manifest identity/);
writeJson(manifestPath, manifest);
writeJson(manifestPath, {
...manifest,
publishedAt: new Date(Date.parse(publishedAt) + 1_000)
.toISOString()
.replace('.000Z', 'Z'),
});
const wrongTaggerTime = runCheck(fixture, archive);
assert.notEqual(wrongTaggerTime.status, 0);
assert.match(wrongTaggerTime.stderr, /publishedAt .* annotated tagger time/);
writeJson(manifestPath, manifest);
const invalidContracts = [
{ ...manifest, extra: true },
{ ...manifest, source: { ...manifest.source, extra: true } },
{ ...manifest, artifact: { ...manifest.artifact, extra: true } },
{ ...manifest, summary: '\u202eunsafe' },
{ ...manifest, severity: 'urgent' },
];
for (const invalid of invalidContracts) {
writeJson(manifestPath, invalid);
const rejected = runCheck(fixture, archive);
assert.notEqual(rejected.status, 0);
assert.match(rejected.stderr, /stable update manifest identity/);
}
writeJson(manifestPath, manifest);
writeJson(manifestPath, null);
const nullManifest = runCheck(fixture, archive);
assert.notEqual(nullManifest.status, 0);
assert.match(nullManifest.stderr, /stable update manifest identity/);
assert.doesNotMatch(nullManifest.stderr, /TypeError|check-stable-update-manifest\.mjs:\d+/);
writeJson(manifestPath, manifest);
fs.appendFileSync(archive, 'tampered');
const archiveMismatch = runCheck(fixture, archive);
assert.notEqual(archiveMismatch.status, 0);
assert.match(archiveMismatch.stderr, /archive sha256 .* does not match/);
fs.writeFileSync(archive, archiveBytes);
writeJson(manifestPath, {
...manifest,
source: { ...manifest.source, treeSha: 'c'.repeat(40) },
});
const treeMismatch = runCheck(fixture, archive);
assert.notEqual(treeMismatch.status, 0);
assert.match(treeMismatch.stderr, /treeSha .* does not match HEAD:archify/);
writeJson(manifestPath, manifest);
assert.equal(git(fixture, ['tag', '-d', 'v3.0.0']).status, 0);
assert.equal(git(fixture, ['tag', 'v3.0.0']).status, 0);
const lightweightTag = runCheck(fixture, archive);
assert.notEqual(lightweightTag.status, 0);
assert.match(lightweightTag.stderr, /must be an annotated tag/);
} finally {
fs.rmSync(fixture, { recursive: true, force: true });
}
});