[Init] Initial commit - NetMesh terminal manager
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
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
This commit is contained in:
111
application/state/pluginImporterTransaction.test.ts
Normal file
111
application/state/pluginImporterTransaction.test.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { STORAGE_KEY_PLUGIN_IMPORT_TRANSACTION } from '../../infrastructure/config/storageKeys';
|
||||
import {
|
||||
commitPluginImporterTransaction,
|
||||
recoverPluginImporterTransaction,
|
||||
} from './pluginImporterTransaction';
|
||||
|
||||
const storage = (initial: Record<string, string> = {}) => {
|
||||
const values = new Map(Object.entries(initial));
|
||||
return {
|
||||
values,
|
||||
read<T>(key: string): T | null {
|
||||
const value = values.get(key);
|
||||
return value === undefined ? null : JSON.parse(value) as T;
|
||||
},
|
||||
readString: (key: string) => values.get(key) ?? null,
|
||||
write<T>(key: string, value: T) { values.set(key, JSON.stringify(value)); return true; },
|
||||
writeString(key: string, value: string) { values.set(key, value); return true; },
|
||||
remove(key: string) { values.delete(key); },
|
||||
};
|
||||
};
|
||||
|
||||
test('plugin importer transaction commits all keys and removes its journal', () => {
|
||||
const target = storage({ hosts: JSON.stringify(['old']) });
|
||||
commitPluginImporterTransaction(target, [
|
||||
['hosts', ['new']],
|
||||
['keys', ['key']],
|
||||
]);
|
||||
assert.deepEqual(target.read('hosts'), ['new']);
|
||||
assert.deepEqual(target.read('keys'), ['key']);
|
||||
assert.equal(target.readString(STORAGE_KEY_PLUGIN_IMPORT_TRANSACTION), null);
|
||||
});
|
||||
|
||||
test('plugin importer recovery rolls back a crash during the prepared phase', () => {
|
||||
const target = storage({ hosts: JSON.stringify(['partial']), keys: JSON.stringify(['old-key']) });
|
||||
target.write(STORAGE_KEY_PLUGIN_IMPORT_TRANSACTION, {
|
||||
version: 1,
|
||||
phase: 'prepared',
|
||||
previous: [
|
||||
{ key: 'hosts', value: JSON.stringify(['old-host']) },
|
||||
{ key: 'keys', value: JSON.stringify(['old-key']) },
|
||||
],
|
||||
});
|
||||
assert.equal(recoverPluginImporterTransaction(target, new Set(['hosts', 'keys'])), 'rolled-back');
|
||||
assert.deepEqual(target.read('hosts'), ['old-host']);
|
||||
assert.deepEqual(target.read('keys'), ['old-key']);
|
||||
assert.equal(target.readString(STORAGE_KEY_PLUGIN_IMPORT_TRANSACTION), null);
|
||||
});
|
||||
|
||||
test('Vault importer recovery accepts a safe subset of transaction keys', () => {
|
||||
const target = storage({ groups: JSON.stringify(['partial']), sources: JSON.stringify(['partial-source']) });
|
||||
target.write(STORAGE_KEY_PLUGIN_IMPORT_TRANSACTION, {
|
||||
version: 1,
|
||||
phase: 'prepared',
|
||||
previous: [
|
||||
{ key: 'groups', value: JSON.stringify(['old-group']) },
|
||||
{ key: 'sources', value: JSON.stringify(['old-source']) },
|
||||
],
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
recoverPluginImporterTransaction(target, new Set(['hosts', 'groups', 'sources'])),
|
||||
'rolled-back',
|
||||
);
|
||||
assert.deepEqual(target.read('groups'), ['old-group']);
|
||||
assert.deepEqual(target.read('sources'), ['old-source']);
|
||||
});
|
||||
|
||||
test('Vault importer transaction rejects writes that do not actually persist', () => {
|
||||
const target = storage({ hosts: JSON.stringify(['old']) });
|
||||
const lyingStorage = {
|
||||
...target,
|
||||
write<T>(key: string, value: T) {
|
||||
if (key === 'hosts') return true;
|
||||
return target.write(key, value);
|
||||
},
|
||||
};
|
||||
|
||||
assert.throws(
|
||||
() => commitPluginImporterTransaction(lyingStorage, [['hosts', ['new']]]),
|
||||
/rejected importer transaction/,
|
||||
);
|
||||
assert.deepEqual(target.read('hosts'), ['old']);
|
||||
});
|
||||
|
||||
test('Vault importer transaction preserves an unfinished recovery record', () => {
|
||||
const recoveryRecord = JSON.stringify({
|
||||
version: 1,
|
||||
phase: 'prepared',
|
||||
previous: [{ key: 'hosts', value: JSON.stringify(['old']) }],
|
||||
});
|
||||
const target = storage({
|
||||
hosts: JSON.stringify(['partial']),
|
||||
[STORAGE_KEY_PLUGIN_IMPORT_TRANSACTION]: recoveryRecord,
|
||||
});
|
||||
|
||||
assert.throws(
|
||||
() => commitPluginImporterTransaction(target, [['hosts', ['new']]]),
|
||||
/unfinished Vault import recovery record/,
|
||||
);
|
||||
assert.equal(target.readString(STORAGE_KEY_PLUGIN_IMPORT_TRANSACTION), recoveryRecord);
|
||||
assert.deepEqual(target.read('hosts'), ['partial']);
|
||||
});
|
||||
|
||||
test('plugin importer recovery keeps fully committed values', () => {
|
||||
const target = storage({ hosts: JSON.stringify(['new']) });
|
||||
target.write(STORAGE_KEY_PLUGIN_IMPORT_TRANSACTION, { version: 1, phase: 'committed' });
|
||||
assert.equal(recoverPluginImporterTransaction(target, new Set(['hosts'])), 'committed');
|
||||
assert.deepEqual(target.read('hosts'), ['new']);
|
||||
});
|
||||
Reference in New Issue
Block a user