diff --git a/modules/nextcloud/client.ts b/modules/nextcloud/client.ts index a3d4bc3b..f0461db8 100644 --- a/modules/nextcloud/client.ts +++ b/modules/nextcloud/client.ts @@ -62,6 +62,10 @@ const ONLYOFFICE_KEYS: ReadonlyArray { } // Keeps Nextcloud's onlyoffice settings matched to the document server the mesh bound (novox/hq -// issue 1830). The client never logs; this says what it did. +// issue 1830). The client never logs; this says what it did. A failed connection check is run +// again on every later tick until it passes (issue 1832): every failing tick is logged, and one +// line marks it passing again; nothing is logged while the last check passed and nothing changed. +let onlyOfficeCheckWasFailing = false; async function applyOnlyOffice(client: NextcloudClient): Promise { const result = client.applyOnlyOffice(); - if (result.changed.length === 0) return; - console.log(`[nextcloud] onlyoffice: set ${result.changed.join(", ")}`); - if (result.checkOk === false) console.error("[nextcloud] onlyoffice: connection check failed after applying settings"); + if (result.changed.length > 0) console.log(`[nextcloud] onlyoffice: set ${result.changed.join(", ")}`); + if (result.checkOk === false) { + onlyOfficeCheckWasFailing = true; + console.error("[nextcloud] onlyoffice: connection check failed" + (result.changed.length > 0 ? " after applying settings" : "")); + } else if (result.checkOk === true && onlyOfficeCheckWasFailing) { + onlyOfficeCheckWasFailing = false; + console.log("[nextcloud] onlyoffice: connection check passes again"); + } } if (nextcloud) { diff --git a/modules/nextcloud/test/onlyoffice.test.ts b/modules/nextcloud/test/onlyoffice.test.ts index 52d4896d..5c90b9d8 100644 --- a/modules/nextcloud/test/onlyoffice.test.ts +++ b/modules/nextcloud/test/onlyoffice.test.ts @@ -110,6 +110,46 @@ test("the jwt_secret is set by stdin of config:import, never in an argv", () => assert.equal(current.jwt_secret, SECRET, "the secret was actually applied"); }); +// 1832/AC1: a failed check keeps running on every later tick, even with nothing changed, reporting +// checkOk false each time, until it passes — then nothing runs again while nothing changes +// (novox/hq issue 1832). +test("a failed check is re-run every tick until it passes, then nothing runs while unchanged", () => { + const c = client(); + const current: Record = {}; // every key unset, so tick 1 changes all four + const checkOpts: { checkFails?: boolean } = { checkFails: true }; + const stand = standInOcc(current, checkOpts); + mock.method(c, "occ", stand.impl); + const checkCalls = () => stand.calls.filter((a) => a[0] === "onlyoffice:documentserver").length; + const file = configFile(FULL_CONFIG); + + // Tick 1: everything changes, the check runs and fails (1832/AC1c: reported on a failing tick). + const tick1 = c.applyOnlyOffice(file); + assert.deepEqual(tick1.changed.sort(), ["DocumentServerInternalUrl", "DocumentServerUrl", "StorageUrl", "jwt_secret"].sort()); + assert.equal(tick1.checkOk, false); + assert.equal(checkCalls(), 1); + + // Tick 2: nothing changed, but the last check failed — it is run again (1832/AC1a), and fails + // again, reported again (1832/AC1c). + const tick2 = c.applyOnlyOffice(file); + assert.deepEqual(tick2.changed, []); + assert.equal(tick2.checkOk, false); + assert.equal(checkCalls(), 2, "the check ran again though nothing changed"); + + // Tick 3: the document server now answers; still nothing changed, but the last check had + // failed, so it is run again — and this time it passes. + checkOpts.checkFails = false; + const tick3 = c.applyOnlyOffice(file); + assert.deepEqual(tick3.changed, []); + assert.equal(tick3.checkOk, true); + assert.equal(checkCalls(), 3); + + // Tick 4: nothing changed, and the last check passed — no check runs (1832/AC1b). + const tick4 = c.applyOnlyOffice(file); + assert.deepEqual(tick4.changed, []); + assert.equal(tick4.checkOk, undefined); + assert.equal(checkCalls(), 3, "no further check ran once the last one passed"); +}); + // 1830/AC3b: when they match, it changes nothing and runs no check. test("changes nothing and runs no check when every value already matches", () => { const c = client();