Improve local backup permissions and change tracking
All checks were successful
Deploy Sokko G / deploy (push) Successful in 19s

This commit is contained in:
Shinuwa 2026-09-25 10:45:12 +02:00
parent 34e542be28
commit ee823d2c37
13 changed files with 372 additions and 37 deletions

View file

@ -1,7 +1,16 @@
// Rôle : vérifie les règles de nommage, d’écriture et de rétention des sauvegardes locales.
import { test } from "node:test";
import assert from "node:assert/strict";
import { backupContentsEqual, getBackupFileName, getLocalDateKey, isFileSystemAccessSupported, saveBackup } from "../website/src/features/toolboxes/storage/autoBackup.js";
import {
backupContentsEqual,
getBackupContentSignature,
getBackupFileName,
getLocalDateKey,
hasBackupDataChanged,
isFileSystemAccessSupported,
normalizeAutoBackupConfig,
saveBackup
} from "../website/src/features/toolboxes/storage/autoBackup.js";
class FakeDirectory {
constructor(names = []) {
@ -48,6 +57,12 @@ test("feature detection does not rely on the user agent", () => {
assert.equal(isFileSystemAccessSupported({}), false);
});
test("legacy enabled configurations start dirty while explicit backup state is preserved", () => {
assert.equal(normalizeAutoBackupConfig().hasUnsavedChanges, false);
assert.equal(normalizeAutoBackupConfig({ enabled: true, directoryHandle: {} }).hasUnsavedChanges, true);
assert.equal(normalizeAutoBackupConfig({ enabled: true, directoryHandle: {}, hasUnsavedChanges: false }).hasUnsavedChanges, false);
});
test("backup names use the browser local date", () => {
const date = new Date(2026, 8, 24, 0, 5);
assert.equal(getLocalDateKey(date), "2026-09-24");
@ -79,6 +94,19 @@ test("automatic backup skips a new day when the export is unchanged", async () =
assert.equal(directory.files.has("sokkog-global-2026-09-24.json"), false);
});
test("content signatures ignore the export timestamp and detect toolbox changes", async () => {
const previous = JSON.stringify({ version: 1, exportedAt: "2026-09-23T08:00:00.000Z", toolboxes: [{ id: "one", name: "Avant" }], modules: {}, links: {} });
const sameData = JSON.stringify({ version: 1, exportedAt: "2026-09-24T08:00:00.000Z", toolboxes: [{ id: "one", name: "Avant" }], modules: {}, links: {} });
const changedData = JSON.stringify({ version: 1, exportedAt: "2026-09-24T08:00:00.000Z", toolboxes: [{ id: "one", name: "Après" }], modules: {}, links: {} });
const previousSignature = await getBackupContentSignature(previous, {});
const sameSignature = await getBackupContentSignature(sameData, {});
const changedSignature = await getBackupContentSignature(changedData, {});
assert.equal(previousSignature, sameSignature);
assert.equal(hasBackupDataChanged({ lastBackupSignature: previousSignature, currentSignature: sameSignature }), false);
assert.equal(hasBackupDataChanged({ lastBackupSignature: previousSignature, currentSignature: changedSignature }), true);
});
test("manual backup replaces the day and retains ten non-consecutive versions", async () => {
const names = [
"sokkog-global-2024-01-01.json", "sokkog-global-2024-06-01.json", "sokkog-global-2025-01-01.json",

View file

@ -227,6 +227,10 @@ export function validateSiteContent(site) {
"toolboxes.autoBackup.title",
"toolboxes.autoBackup.description",
"toolboxes.autoBackup.compatibleLabel",
"toolboxes.autoBackup.permissionTitle",
"toolboxes.autoBackup.permissionDescription",
"toolboxes.autoBackup.reminderMessage",
"toolboxes.autoBackup.reminderAction",
"toolboxes.autoBackup.folderLabel",
"toolboxes.autoBackup.enabled",
"toolboxes.autoBackup.toggleHelp",
@ -905,6 +909,7 @@ export function validateSiteContent(site) {
assertStringArray(site, "toolboxes.storageHelp.items", 3);
assertStringArray(site, "toolboxes.autoBackup.browsers", 2);
assertStringArray(site, "toolboxes.autoBackup.permissionSteps", 3);
const librarySummaryItems = valueAt(site, "library.summaryItems");
assert.ok(Array.isArray(librarySummaryItems), "library.summaryItems must be an array");