import { describe, it } from 'node:test'; import assert from 'node:assert/strict'; import { DEVICE_LOCAL_KEYS, SETTINGS_SYNC_VERSION, buildSyncedContent, isSyncedSettingsContent, mergeRemoteSettings, pickSyncable, shouldApplyRemote, syncableEqual, } from './settingsSync'; import { Settings } from '../state/settings'; // A representative slice of Settings; the helpers are generic over keys so a // partial object cast is enough to exercise every branch. const base = { settingsSync: true, pageZoom: 110, isMarkdown: true, quietHoursEnabled: false, quietHoursStart: '22:00', composerToolbarButtons: { showGif: true, order: ['showEmoji', 'showGif'] }, pttKey: 'KeyV', callDenoiseModel: 'rnnoise', } as unknown as Settings; describe('pickSyncable', () => { it('drops device-local keys and deep-copies objects', () => { const picked = pickSyncable(base); assert.equal('pageZoom' in picked, false); assert.equal('settingsSync' in picked, false); assert.equal('callDenoiseModel' in picked, false); assert.equal(picked.isMarkdown, true); assert.equal(picked.pttKey, 'KeyV'); assert.notEqual(picked.composerToolbarButtons, base.composerToolbarButtons); assert.deepEqual(picked.composerToolbarButtons, base.composerToolbarButtons); }); it('never syncs the sync toggle itself', () => { assert.equal(DEVICE_LOCAL_KEYS.has('settingsSync'), true); }); }); describe('syncableEqual', () => { it('is structural and key-order independent', () => { const a = pickSyncable(base); const b = Object.fromEntries(Object.entries(a).reverse()) as Partial; assert.equal(syncableEqual(a, b), true); assert.equal(syncableEqual(a, { ...a, isMarkdown: false }), false); assert.equal(syncableEqual(a, { ...a, extra: 1 } as Partial), false); }); }); describe('isSyncedSettingsContent', () => { it('accepts a well-formed event and rejects junk', () => { assert.equal(isSyncedSettingsContent(buildSyncedContent(base, 5)), true); assert.equal(isSyncedSettingsContent({}), false); assert.equal(isSyncedSettingsContent(null), false); assert.equal(isSyncedSettingsContent({ version: 1, updatedAt: 'x', settings: {} }), false); assert.equal(isSyncedSettingsContent({ version: 1, updatedAt: 1, settings: [] }), false); }); }); describe('mergeRemoteSettings', () => { it('applies syncable keys, ignores device-local and unknown keys and wrong shapes', () => { const remote = { version: SETTINGS_SYNC_VERSION, updatedAt: 10, settings: { isMarkdown: false, pageZoom: 200, // device-local: ignored quietHoursStart: 42, // wrong type: ignored composerToolbarButtons: 'nope', // wrong type: ignored futureKey: true, // unknown: ignored } as unknown as Partial, }; const merged = mergeRemoteSettings(base, remote); assert.equal(merged.isMarkdown, false); assert.equal(merged.pageZoom, 110); assert.equal(merged.quietHoursStart, '22:00'); assert.deepEqual(merged.composerToolbarButtons, base.composerToolbarButtons); assert.equal('futureKey' in merged, false); // Never mutates the input. assert.equal(base.isMarkdown, true); }); }); describe('shouldApplyRemote', () => { it('applies when never synced or strictly newer; ignores echoes', () => { const remote = buildSyncedContent(base, 100); assert.equal(shouldApplyRemote(remote, null), true); assert.equal(shouldApplyRemote(remote, 99), true); assert.equal(shouldApplyRemote(remote, 100), false); assert.equal(shouldApplyRemote(remote, 101), false); }); }); describe('buildSyncedContent', () => { it('stamps version and updatedAt around the syncable subset', () => { const c = buildSyncedContent(base, 7); assert.equal(c.version, SETTINGS_SYNC_VERSION); assert.equal(c.updatedAt, 7); assert.deepEqual(c.settings, pickSyncable(base)); }); });