Files
NetMesh/domain/snippetAgentOps.test.ts
zhaolei 3c72efcb7f
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
[Init] Initial commit - NetMesh terminal manager
2026-09-13 18:24:01 +08:00

253 lines
7.7 KiB
TypeScript

import assert from 'node:assert/strict';
import test from 'node:test';
import type { Host, Snippet } from './models';
import { getScriptApiReference } from './scriptApiReference.ts';
import {
applySnippetAgentPatch,
applySnippetCreateToVault,
applySnippetUpdateToVault,
buildSnippetFromAgentDraft,
deleteSnippetFromVault,
filterScriptSnippets,
serializeSnippetForAgentGet,
setHostConnectScriptIds,
} from './snippetAgentOps.ts';
const host: Host = {
id: 'host-a',
label: 'A',
hostname: 'a.example',
username: 'root',
os: 'linux',
protocol: 'ssh',
tags: [],
};
test('getScriptApiReference includes nct API and wrapper rules', () => {
const ref = getScriptApiReference();
assert.match(ref, /nct\.screen\.waitForPrompt/);
assert.match(ref, /nct\.session\.name/);
assert.match(ref, /Only JavaScript is executed/);
assert.match(ref, /onConnect/);
});
test('filterScriptSnippets returns only script kind', () => {
const snippets: Snippet[] = [
{ id: '1', label: 'cmd', command: 'ls', kind: 'snippet' },
{ id: '2', label: 'auto', command: 'nct.log(1)', kind: 'script' },
];
assert.deepEqual(filterScriptSnippets(snippets).map((s) => s.id), ['2']);
});
test('serializeSnippetForAgentGet includes script metadata', () => {
const snippet: Snippet = {
id: 's1',
label: 'Deploy',
command: 'await main();',
kind: 'script',
trigger: 'onConnect',
targets: ['host-a'],
targetGroups: ['Production'],
language: 'javascript',
};
const serialized = serializeSnippetForAgentGet(snippet);
assert.equal(serialized.kind, 'script');
assert.equal(serialized.trigger, 'onConnect');
assert.deepEqual(serialized.targets, ['host-a']);
assert.deepEqual(serialized.targetGroups, ['Production']);
});
test('agent serialization preserves missing and explicit empty group scopes', () => {
const unscoped = serializeSnippetForAgentGet({
id: 'legacy',
label: 'Legacy',
command: 'nct.log(1)',
kind: 'script',
trigger: 'onOutput',
triggerPattern: 'READY',
});
const disabled = serializeSnippetForAgentGet({
id: 'disabled',
label: 'Disabled',
command: 'nct.log(1)',
kind: 'script',
trigger: 'onOutput',
triggerPattern: 'READY',
targetGroups: [],
});
assert.equal(unscoped.targetGroups, undefined);
assert.deepEqual(disabled.targetGroups, []);
});
test('buildSnippetFromAgentDraft validates onOutput triggerPattern', () => {
const bad = buildSnippetFromAgentDraft(
{ label: 'x', command: 'nct.log(1)', trigger: 'onOutput' },
[],
{ forceKind: 'script' },
);
assert.equal(bad.ok, false);
if (bad.ok) return;
assert.match(bad.error, /triggerPattern/);
const good = buildSnippetFromAgentDraft(
{ label: 'x', command: 'nct.log(1)', trigger: 'onOutput', triggerPattern: 'ERROR' },
[],
{ forceKind: 'script' },
);
assert.equal(good.ok, true);
});
test('buildSnippetFromAgentDraft accepts multi-line run mode', () => {
const result = buildSnippetFromAgentDraft(
{ label: 'login', command: 'user\npass', multiLineRunMode: 'lineDelay' },
[],
);
assert.equal(result.ok, true);
if (!result.ok) return;
assert.equal(result.snippet.multiLineRunMode, 'lineDelay');
});
test('deleteSnippetFromVault removes snippet and host connect references', () => {
const snippets: Snippet[] = [
{
id: 'run',
label: 'Run',
command: 'nct.log(1)',
kind: 'script',
trigger: 'onConnect',
targets: ['host-a'],
},
];
const hosts = [{ ...host, connectScriptIds: ['run'] }];
const result = deleteSnippetFromVault(snippets, hosts, 'run');
if ('error' in result) {
assert.fail(result.error);
}
assert.equal(result.snippets.length, 0);
assert.deepEqual(result.hosts[0].connectScriptIds, []);
});
test('applySnippetCreateToVault syncs onConnect targets to host queue', () => {
const snippet: Snippet = {
id: 'run',
label: 'Run',
command: 'nct.log(1)',
kind: 'script',
trigger: 'onConnect',
targets: ['host-a'],
};
const result = applySnippetCreateToVault([], [host], snippet);
assert.deepEqual(result.hosts[0].connectScriptIds, ['run']);
});
test('applySnippetUpdateToVault syncs target changes', () => {
const snippet: Snippet = {
id: 'run',
label: 'Run',
command: 'nct.log(1)',
kind: 'script',
trigger: 'onConnect',
targets: ['host-a'],
};
const created = applySnippetCreateToVault([snippet], [host], snippet);
const updatedSnippet = { ...snippet, targets: [] as string[] | undefined, targetsAllHosts: true };
const updated = applySnippetUpdateToVault(created.snippets, created.hosts, updatedSnippet, snippet, ['host-a']);
assert.equal(updated.hosts[0].connectScriptIds?.includes('run'), false);
});
test('applySnippetUpdateToVault clears host queue when onConnect is demoted', () => {
const snippet: Snippet = {
id: 'run',
label: 'Run',
command: 'nct.log(1)',
kind: 'script',
trigger: 'onConnect',
targets: ['host-a'],
};
const host: Host = {
id: 'host-a',
label: 'A',
hostname: 'a.example',
username: 'root',
os: 'linux',
protocol: 'ssh',
tags: [],
connectScriptIds: ['run'],
};
const created = applySnippetCreateToVault([snippet], [host], snippet);
const manual = { ...snippet, trigger: 'manual' as const };
const updated = applySnippetUpdateToVault(created.snippets, created.hosts, manual, snippet);
assert.equal(updated.hosts[0].connectScriptIds?.includes('run'), false);
});
test('applySnippetAgentPatch clears targetsAllHosts when explicit targets are set', () => {
const existing: Snippet = {
id: 'run',
label: 'Run',
command: 'nct.log(1)',
kind: 'script',
trigger: 'onConnect',
targetsAllHosts: true,
};
const patched = applySnippetAgentPatch(existing, { targets: ['host-a'] });
assert.equal(patched.ok, true);
if (!patched.ok) return;
assert.equal(patched.snippet.targetsAllHosts, undefined);
assert.deepEqual(patched.snippet.targets, ['host-a']);
});
test('applySnippetAgentPatch supports dynamic groups alongside explicit hosts', () => {
const existing: Snippet = {
id: 'run',
label: 'Run',
command: 'nct.log(1)',
kind: 'script',
trigger: 'onConnect',
targets: ['host-a'],
};
const patched = applySnippetAgentPatch(existing, { targetGroups: ['Production'] });
assert.equal(patched.ok, true);
if (!patched.ok) return;
assert.deepEqual(patched.snippet.targets, ['host-a']);
assert.deepEqual(patched.snippet.targetGroups, ['Production']);
});
test('agent group targets normalize paths and preserve an explicit empty scope', () => {
const built = buildSnippetFromAgentDraft({
label: 'Run',
command: 'nct.log(1)',
kind: 'script',
targetGroups: [' Production\\Web ', 'Production//Web'],
}, []);
assert.equal(built.ok, true);
if (!built.ok) return;
assert.deepEqual(built.snippet.targetGroups, ['Production/Web']);
const patched = applySnippetAgentPatch(built.snippet, { targetGroups: [] });
assert.equal(patched.ok, true);
if (!patched.ok) return;
assert.deepEqual(patched.snippet.targetGroups, []);
});
test('applySnippetAgentPatch updates multi-line run mode', () => {
const existing: Snippet = {
id: 'login',
label: 'Login',
command: 'user\npass',
multiLineRunMode: 'lineDelay',
};
const patched = applySnippetAgentPatch(existing, { multiLineRunMode: 'paste' });
assert.equal(patched.ok, true);
if (!patched.ok) return;
assert.equal(patched.snippet.multiLineRunMode, 'paste');
});
test('setHostConnectScriptIds rejects non-onConnect scripts', () => {
const snippets: Snippet[] = [
{ id: 'manual', label: 'M', command: 'x', kind: 'script', trigger: 'manual' },
];
const result = setHostConnectScriptIds(host, ['manual'], snippets);
assert.equal(result.ok, false);
});