[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:
209
application/pluginSyncSidecarBridge.test.ts
Normal file
209
application/pluginSyncSidecarBridge.test.ts
Normal file
@@ -0,0 +1,209 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
|
||||
type LocalStorageMock = {
|
||||
clear(): void;
|
||||
getItem(key: string): string | null;
|
||||
setItem(key: string, value: string): void;
|
||||
removeItem(key: string): void;
|
||||
};
|
||||
|
||||
function installLocalStorage(): LocalStorageMock {
|
||||
const store = new Map<string, string>();
|
||||
const localStorage: LocalStorageMock = {
|
||||
clear() { store.clear(); },
|
||||
getItem(key) { return store.has(key) ? store.get(key)! : null; },
|
||||
setItem(key, value) { store.set(key, String(value)); },
|
||||
removeItem(key) { store.delete(key); },
|
||||
};
|
||||
Object.defineProperty(globalThis, 'localStorage', {
|
||||
value: localStorage,
|
||||
configurable: true,
|
||||
});
|
||||
return localStorage;
|
||||
}
|
||||
|
||||
const localStorage = installLocalStorage();
|
||||
|
||||
const { SYNC_STORAGE_KEYS } = await import('../domain/sync.ts');
|
||||
const { localStorageAdapter } = await import('../infrastructure/persistence/localStorageAdapter.ts');
|
||||
const {
|
||||
collectPluginSyncSidecarsFromHost,
|
||||
commitPluginSidecarsLastKnown,
|
||||
isPluginSidecarHostReady,
|
||||
} = await import('./pluginSyncSidecarBridge.ts');
|
||||
const { hasMeaningfulCloudSyncData } = await import('./syncPayload.ts');
|
||||
|
||||
test.beforeEach(() => {
|
||||
localStorage.clear();
|
||||
delete (globalThis as { window?: unknown }).window;
|
||||
});
|
||||
|
||||
test('isPluginSidecarHostReady follows pluginHostReady probe when present', () => {
|
||||
(globalThis as { window: unknown }).window = {
|
||||
netcatty: {
|
||||
pluginHostReady: () => true,
|
||||
async collectPluginSyncSidecars() {
|
||||
return { version: 1, entries: [] };
|
||||
},
|
||||
},
|
||||
};
|
||||
assert.equal(isPluginSidecarHostReady(), true);
|
||||
|
||||
(globalThis as { window: unknown }).window = {
|
||||
netcatty: {
|
||||
pluginHostReady: () => false,
|
||||
async collectPluginSyncSidecars() {
|
||||
return null;
|
||||
},
|
||||
},
|
||||
};
|
||||
assert.equal(isPluginSidecarHostReady(), false);
|
||||
});
|
||||
|
||||
test('collect defers empty last-known until commit (empty-vault guard ignores last-known alone)', async () => {
|
||||
localStorageAdapter.write(SYNC_STORAGE_KEYS.PLUGIN_SIDECARS_LAST_KNOWN, {
|
||||
version: 1,
|
||||
entries: [{
|
||||
pluginId: 'com.example.p',
|
||||
kind: 'settings',
|
||||
key: 'k',
|
||||
value: 1,
|
||||
updatedAt: 1,
|
||||
}],
|
||||
});
|
||||
(globalThis as { window: unknown }).window = {
|
||||
netcatty: {
|
||||
async collectPluginSyncSidecars() {
|
||||
return { version: 1, entries: [] };
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const collected = await collectPluginSyncSidecarsFromHost();
|
||||
assert.deepEqual(collected, { version: 1, entries: [] });
|
||||
|
||||
// Last-known still holds prior entries until commit, but that alone must not
|
||||
// bypass the empty-vault upload guard.
|
||||
const lastKnown = localStorageAdapter.read<{ entries: unknown[] }>(
|
||||
SYNC_STORAGE_KEYS.PLUGIN_SIDECARS_LAST_KNOWN,
|
||||
);
|
||||
assert.equal(lastKnown?.entries?.length, 1);
|
||||
|
||||
assert.equal(
|
||||
hasMeaningfulCloudSyncData({
|
||||
hosts: [],
|
||||
keys: [],
|
||||
identities: [],
|
||||
snippets: [],
|
||||
customGroups: [],
|
||||
syncedAt: 1,
|
||||
pluginSidecars: { version: 1, entries: [] },
|
||||
}),
|
||||
false,
|
||||
);
|
||||
|
||||
commitPluginSidecarsLastKnown({ version: 1, entries: [] });
|
||||
const committed = localStorageAdapter.read<{ entries: unknown[] }>(
|
||||
SYNC_STORAGE_KEYS.PLUGIN_SIDECARS_LAST_KNOWN,
|
||||
);
|
||||
assert.deepEqual(committed?.entries, []);
|
||||
});
|
||||
|
||||
test('liveOnly collect returns null instead of last-known when host is gated off', async () => {
|
||||
localStorageAdapter.write(SYNC_STORAGE_KEYS.PLUGIN_SIDECARS_LAST_KNOWN, {
|
||||
version: 1,
|
||||
entries: [{
|
||||
pluginId: 'com.example.p',
|
||||
kind: 'settings',
|
||||
key: 'k',
|
||||
value: 1,
|
||||
updatedAt: 1,
|
||||
}],
|
||||
});
|
||||
(globalThis as { window: unknown }).window = {
|
||||
netcatty: {
|
||||
async collectPluginSyncSidecars() {
|
||||
return null;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
assert.equal(await collectPluginSyncSidecarsFromHost({ liveOnly: true }), null);
|
||||
const fallback = await collectPluginSyncSidecarsFromHost();
|
||||
assert.equal(fallback?.entries?.length, 1);
|
||||
});
|
||||
|
||||
test('liveOnly collect omits pending remote when replay cannot apply', async () => {
|
||||
localStorageAdapter.write(SYNC_STORAGE_KEYS.PLUGIN_SIDECARS_PENDING_REMOTE, {
|
||||
version: 1,
|
||||
entries: [{
|
||||
pluginId: 'com.example.remote',
|
||||
kind: 'settings',
|
||||
key: 'k',
|
||||
value: 'remote',
|
||||
updatedAt: 2,
|
||||
}],
|
||||
});
|
||||
(globalThis as { window: unknown }).window = {
|
||||
netcatty: {
|
||||
async collectPluginSyncSidecars() {
|
||||
return { version: 1, entries: [] };
|
||||
},
|
||||
// no applyPluginSyncSidecars → pending cannot replay
|
||||
},
|
||||
};
|
||||
|
||||
assert.equal(await collectPluginSyncSidecarsFromHost({ liveOnly: true }), null);
|
||||
const uploadPath = await collectPluginSyncSidecarsFromHost();
|
||||
assert.equal(uploadPath?.entries?.[0]?.value, 'remote');
|
||||
});
|
||||
|
||||
test('commitPluginSidecarsAfterSuccessfulSync prefers merged payload sidecars', async () => {
|
||||
const { commitPluginSidecarsAfterSuccessfulSync } = await import('./pluginSyncSidecarBridge.ts');
|
||||
localStorageAdapter.write(SYNC_STORAGE_KEYS.PLUGIN_SIDECARS_LAST_KNOWN, {
|
||||
version: 1,
|
||||
entries: [{
|
||||
pluginId: 'com.example.old',
|
||||
kind: 'settings',
|
||||
key: 'k',
|
||||
value: 'old',
|
||||
updatedAt: 1,
|
||||
}],
|
||||
});
|
||||
|
||||
commitPluginSidecarsAfterSuccessfulSync(
|
||||
{
|
||||
pluginSidecars: {
|
||||
version: 1,
|
||||
entries: [{
|
||||
pluginId: 'com.example.local',
|
||||
kind: 'settings',
|
||||
key: 'k',
|
||||
value: 'local',
|
||||
updatedAt: 2,
|
||||
}],
|
||||
},
|
||||
},
|
||||
[{
|
||||
success: true,
|
||||
mergedPayload: {
|
||||
pluginSidecars: {
|
||||
version: 1,
|
||||
entries: [{
|
||||
pluginId: 'com.example.merged',
|
||||
kind: 'settings',
|
||||
key: 'k',
|
||||
value: 'merged',
|
||||
updatedAt: 3,
|
||||
}],
|
||||
},
|
||||
},
|
||||
}],
|
||||
);
|
||||
|
||||
const committed = localStorageAdapter.read<{ entries: Array<{ value: string }> }>(
|
||||
SYNC_STORAGE_KEYS.PLUGIN_SIDECARS_LAST_KNOWN,
|
||||
);
|
||||
assert.equal(committed?.entries?.[0]?.value, 'merged');
|
||||
});
|
||||
Reference in New Issue
Block a user