[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:
258
domain/pluginSyncSidecar.test.ts
Normal file
258
domain/pluginSyncSidecar.test.ts
Normal file
@@ -0,0 +1,258 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { describe, it } from 'node:test';
|
||||
import {
|
||||
collectPluginSyncSidecars,
|
||||
excludeSecretPluginSettingsFromSidecars,
|
||||
isCloudSyncablePluginSetting,
|
||||
mergePluginSyncSidecars,
|
||||
mergePluginSyncSidecarsThreeWay,
|
||||
parseSettingsSidecarKey,
|
||||
type PluginSettingFieldDescriptor,
|
||||
type PluginSyncSidecarEntry,
|
||||
} from './pluginSyncSidecar';
|
||||
|
||||
const pluginId = 'com.example.sidecar';
|
||||
|
||||
const declared = new Map<string, PluginSettingFieldDescriptor[]>([
|
||||
[pluginId, [
|
||||
{ id: `${pluginId}.theme`, sync: true, secret: false },
|
||||
{ id: `${pluginId}.token`, sync: true, secret: true },
|
||||
{ id: `${pluginId}.localOnly`, sync: false, secret: false },
|
||||
]],
|
||||
]);
|
||||
|
||||
describe('pluginSyncSidecar', () => {
|
||||
it('rejects secret fields from cloud sync even when sync is true', () => {
|
||||
assert.equal(isCloudSyncablePluginSetting({ sync: true, secret: true }), false);
|
||||
assert.equal(isCloudSyncablePluginSetting({ sync: true, secret: false }), true);
|
||||
assert.equal(isCloudSyncablePluginSetting({ sync: false, secret: false }), false);
|
||||
});
|
||||
|
||||
it('collects only non-secret sync:true settings into the sidecar bundle', () => {
|
||||
const bundle = collectPluginSyncSidecars({
|
||||
declaredSettingsByPlugin: declared,
|
||||
storedSettings: [
|
||||
{
|
||||
pluginId,
|
||||
settingId: `${pluginId}.theme`,
|
||||
scope: 'application',
|
||||
scopeId: 'application',
|
||||
value: 'dark',
|
||||
updatedAt: 10,
|
||||
},
|
||||
{
|
||||
pluginId,
|
||||
settingId: `${pluginId}.token`,
|
||||
scope: 'application',
|
||||
scopeId: 'application',
|
||||
value: 'should-not-sync',
|
||||
updatedAt: 10,
|
||||
},
|
||||
{
|
||||
pluginId,
|
||||
settingId: `${pluginId}.localOnly`,
|
||||
scope: 'application',
|
||||
scopeId: 'application',
|
||||
value: true,
|
||||
updatedAt: 10,
|
||||
},
|
||||
],
|
||||
now: 20,
|
||||
});
|
||||
|
||||
assert.equal(bundle.version, 1);
|
||||
assert.equal(bundle.entries.length, 1);
|
||||
assert.equal(bundle.entries[0].kind, 'settings');
|
||||
assert.equal(bundle.entries[0].value, 'dark');
|
||||
assert.deepEqual(parseSettingsSidecarKey(bundle.entries[0].key), {
|
||||
settingId: `${pluginId}.theme`,
|
||||
scope: 'application',
|
||||
scopeId: 'application',
|
||||
});
|
||||
});
|
||||
|
||||
it('preserves account and CRDT baselines when the plugin is missing', () => {
|
||||
const existing: PluginSyncSidecarEntry[] = [
|
||||
{
|
||||
pluginId: 'com.missing.plugin',
|
||||
kind: 'account_baseline',
|
||||
key: 'account',
|
||||
value: { accountId: 'acct-1' },
|
||||
updatedAt: 5,
|
||||
},
|
||||
{
|
||||
pluginId: 'com.missing.plugin',
|
||||
kind: 'crdt_baseline',
|
||||
key: 'replica',
|
||||
value: { clock: 3 },
|
||||
updatedAt: 5,
|
||||
},
|
||||
{
|
||||
pluginId: 'com.missing.plugin',
|
||||
kind: 'settings',
|
||||
key: 'com.missing.plugin.theme\0application\0application',
|
||||
value: 'light',
|
||||
updatedAt: 5,
|
||||
},
|
||||
];
|
||||
|
||||
const bundle = collectPluginSyncSidecars({
|
||||
declaredSettingsByPlugin: new Map(), // plugin not installed
|
||||
storedSettings: [],
|
||||
existingSidecars: existing,
|
||||
now: 100,
|
||||
});
|
||||
|
||||
assert.equal(bundle.entries.length, 3);
|
||||
assert.ok(bundle.entries.some((entry) => entry.kind === 'account_baseline'));
|
||||
assert.ok(bundle.entries.some((entry) => entry.kind === 'crdt_baseline'));
|
||||
assert.ok(bundle.entries.some((entry) => entry.kind === 'settings' && entry.value === 'light'));
|
||||
});
|
||||
|
||||
it('merges remote sidecars without dropping local baselines for missing remote plugins', () => {
|
||||
const local: PluginSyncSidecarEntry[] = [
|
||||
{
|
||||
pluginId: 'com.local.only',
|
||||
kind: 'account_baseline',
|
||||
key: 'account',
|
||||
value: { id: 'local' },
|
||||
updatedAt: 1,
|
||||
},
|
||||
];
|
||||
const remote = {
|
||||
version: 1 as const,
|
||||
entries: [
|
||||
{
|
||||
pluginId,
|
||||
kind: 'settings' as const,
|
||||
key: `${pluginId}.theme\0application\0application`,
|
||||
value: 'dark',
|
||||
updatedAt: 2,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const merged = mergePluginSyncSidecars({ local, remote, declaredSettingsByPlugin: declared });
|
||||
assert.equal(merged.length, 2);
|
||||
assert.ok(merged.some((entry) => entry.pluginId === 'com.local.only'));
|
||||
assert.ok(merged.some((entry) => entry.value === 'dark'));
|
||||
});
|
||||
|
||||
it('excludes secret settings when stripping sidecars with known declarations', () => {
|
||||
const entries: PluginSyncSidecarEntry[] = [
|
||||
{
|
||||
pluginId,
|
||||
kind: 'settings',
|
||||
key: `${pluginId}.token\0application\0application`,
|
||||
value: 'secret',
|
||||
updatedAt: 1,
|
||||
},
|
||||
{
|
||||
pluginId,
|
||||
kind: 'settings',
|
||||
key: `${pluginId}.theme\0application\0application`,
|
||||
value: 'dark',
|
||||
updatedAt: 1,
|
||||
},
|
||||
];
|
||||
const filtered = excludeSecretPluginSettingsFromSidecars(entries, declared);
|
||||
assert.equal(filtered.length, 1);
|
||||
assert.equal(filtered[0].value, 'dark');
|
||||
});
|
||||
|
||||
it('collect prefers newer retained sidecars over older stored settings', () => {
|
||||
const bundle = collectPluginSyncSidecars({
|
||||
declaredSettingsByPlugin: declared,
|
||||
storedSettings: [{
|
||||
pluginId,
|
||||
settingId: `${pluginId}.theme`,
|
||||
scope: 'application',
|
||||
scopeId: 'application',
|
||||
value: 'local-old',
|
||||
updatedAt: 100,
|
||||
}],
|
||||
existingSidecars: [{
|
||||
pluginId,
|
||||
kind: 'settings',
|
||||
key: `${pluginId}.theme\0application\0application`,
|
||||
value: 'remote-new',
|
||||
updatedAt: 200,
|
||||
}],
|
||||
now: 300,
|
||||
});
|
||||
assert.equal(bundle.entries.length, 1);
|
||||
assert.equal(bundle.entries[0].value, 'remote-new');
|
||||
assert.equal(bundle.entries[0].updatedAt, 200);
|
||||
});
|
||||
|
||||
it('three-way merge propagates local settings deletions against an unchanged remote', () => {
|
||||
const entry: PluginSyncSidecarEntry = {
|
||||
pluginId,
|
||||
kind: 'settings',
|
||||
key: `${pluginId}.theme\0application\0application`,
|
||||
value: 'dark',
|
||||
updatedAt: 10,
|
||||
};
|
||||
const merged = mergePluginSyncSidecarsThreeWay({
|
||||
base: [entry],
|
||||
local: [],
|
||||
remote: [entry],
|
||||
});
|
||||
assert.equal(merged.some((item) => item.key === entry.key), false);
|
||||
});
|
||||
|
||||
it('three-way merge keeps a newer remote edit over a local deletion', () => {
|
||||
const base: PluginSyncSidecarEntry = {
|
||||
pluginId,
|
||||
kind: 'settings',
|
||||
key: `${pluginId}.theme\0application\0application`,
|
||||
value: 'dark',
|
||||
updatedAt: 10,
|
||||
};
|
||||
const remote: PluginSyncSidecarEntry = {
|
||||
...base,
|
||||
value: 'light',
|
||||
updatedAt: 20,
|
||||
};
|
||||
const merged = mergePluginSyncSidecarsThreeWay({
|
||||
base: [base],
|
||||
local: [],
|
||||
remote: [remote],
|
||||
});
|
||||
assert.equal(merged.length, 1);
|
||||
assert.equal(merged[0].value, 'light');
|
||||
});
|
||||
|
||||
it('preferCloud keeps local-only settings and baseline orphans', () => {
|
||||
const localOnly: PluginSyncSidecarEntry = {
|
||||
pluginId,
|
||||
kind: 'settings',
|
||||
key: `${pluginId}.theme\0application\0application`,
|
||||
value: 'local-new',
|
||||
updatedAt: 50,
|
||||
};
|
||||
const baseline: PluginSyncSidecarEntry = {
|
||||
pluginId,
|
||||
kind: 'account_baseline',
|
||||
key: 'account',
|
||||
value: { id: 'local-acct' },
|
||||
updatedAt: 40,
|
||||
};
|
||||
const remoteBaseline: PluginSyncSidecarEntry = {
|
||||
pluginId,
|
||||
kind: 'crdt_baseline',
|
||||
key: 'crdt',
|
||||
value: { clock: 1 },
|
||||
updatedAt: 30,
|
||||
};
|
||||
const merged = mergePluginSyncSidecarsThreeWay({
|
||||
base: [],
|
||||
local: [localOnly, baseline],
|
||||
remote: [remoteBaseline],
|
||||
strategy: 'preferCloud',
|
||||
});
|
||||
assert.equal(merged.some((item) => item.key === localOnly.key), true);
|
||||
assert.equal(merged.some((item) => item.kind === 'account_baseline'), true);
|
||||
assert.equal(merged.some((item) => item.kind === 'crdt_baseline'), true);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user