Files
workinf_Blender_Wasm/web/tests/e2e/asset-catalog-indexeddb-migration.spec.ts
mes123456 5a11045ca5
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled
Advance Blender 5.2 web parity through M12-03D
2026-08-17 17:30:27 -04:00

115 lines
5.2 KiB
TypeScript

import { expect, test } from "@playwright/test";
import fs from "node:fs";
import path from "node:path";
const fixture = JSON.parse(fs.readFileSync(
path.resolve(import.meta.dirname, "../../../tests/golden/M12-01D/catalog-v1.json"),
"utf8",
)) as unknown;
test("M12-01G aborts catalog migration without changing the old IndexedDB index", async ({ page }) => {
await page.goto("/");
const result = await page.evaluate(async (source) => {
const storage = await import("/src/storage/asset-catalog-indexeddb.ts");
const open = (name: string): Promise<IDBDatabase> => new Promise((resolve, reject) => {
const request = indexedDB.open(name, 1);
request.onupgradeneeded = () => {
request.result.createObjectStore("setting", { keyPath: "id" });
request.result.createObjectStore("migration", { keyPath: "id" });
};
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
const complete = (transaction: IDBTransaction): Promise<void> => new Promise((resolve, reject) => {
transaction.oncomplete = () => resolve();
transaction.onerror = () => reject(transaction.error);
transaction.onabort = () => reject(transaction.error);
});
const seed = async (database: IDBDatabase): Promise<void> => {
const transaction = database.transaction(["setting", "migration"], "readwrite");
transaction.objectStore("setting").put({ id: storage.ASSET_CATALOG_INDEX_V1_ID, value: source });
transaction.objectStore("migration").put({ id: "schema-legacy", version: 1, marker: "unchanged" });
await complete(transaction);
};
const get = (store: IDBObjectStore, id: string): Promise<unknown> => new Promise((resolve, reject) => {
const request = store.get(id);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
const snapshot = async (database: IDBDatabase) => {
const transaction = database.transaction(["setting", "migration"], "readonly");
const settings = transaction.objectStore("setting");
const migrations = transaction.objectStore("migration");
const values = await Promise.all([
get(settings, storage.ASSET_CATALOG_INDEX_V1_ID),
get(settings, storage.ASSET_CATALOG_INDEX_V2_ID),
get(migrations, storage.ASSET_CATALOG_MIGRATION_ID),
get(migrations, "schema-legacy"),
]);
await complete(transaction);
return { version: database.version, source: values[0], target: values[1], receipt: values[2], legacy: values[3] };
};
const faultCode = async (database: IDBDatabase, faultAt: "AFTER_TARGET_PUT" | "AFTER_SOURCE_DELETE"): Promise<string> => {
try {
await storage.migrateAssetCatalogIndexedDB(database, { faultAt });
return "";
}
catch (error) {
return String((error as Error & { code?: string }).code ?? "");
}
};
const databaseName = `m12-01g-${crypto.randomUUID()}`;
let database = await open(databaseName);
await seed(database);
const baseline = await snapshot(database);
const afterTargetCode = await faultCode(database, "AFTER_TARGET_PUT");
database.close();
database = await open(databaseName);
const afterTargetFault = await snapshot(database);
const afterDeleteCode = await faultCode(database, "AFTER_SOURCE_DELETE");
database.close();
database = await open(databaseName);
const afterDeleteFault = await snapshot(database);
const migrated = await storage.migrateAssetCatalogIndexedDB(database);
const committed = await snapshot(database);
database.close();
indexedDB.deleteDatabase(databaseName);
return {
baseline,
afterTargetCode,
afterTargetFault,
afterDeleteCode,
afterDeleteFault,
migrated: {
status: migrated.status,
revision: migrated.manifest.revision,
sourceHash: migrated.receipt.sourceManifestSha256,
targetHash: migrated.receipt.targetManifestSha256,
},
committed,
};
}, fixture);
expect(result.afterTargetCode).toBe("STORAGE_TRANSACTION");
expect(result.afterDeleteCode).toBe("STORAGE_TRANSACTION");
expect(result.afterTargetFault).toEqual(result.baseline);
expect(result.afterDeleteFault).toEqual(result.baseline);
expect(result.baseline.version).toBe(1);
expect(result.baseline.source).toEqual({ id: "asset-catalog:index:v1", value: fixture });
expect(result.baseline.target).toBeUndefined();
expect(result.baseline.receipt).toBeUndefined();
expect(result.baseline.legacy).toEqual({ id: "schema-legacy", version: 1, marker: "unchanged" });
expect(result.migrated).toEqual({
status: "MIGRATED",
revision: 7,
sourceHash: "cbdc8b03dc9ce1f38b44bae1c7459eb273126cbd6c7dd33115d2b1966e975881",
targetHash: "0c08ed1af1dd0998c809f76d969fd21d8fdae28ad37cf5571bdc99b16bf3ec90",
});
expect(result.committed.version).toBe(1);
expect(result.committed.source).toBeUndefined();
expect(result.committed.target).toMatchObject({ id: "asset-catalog:index:v2", value: { schemaVersion: 2, revision: 7 } });
expect(result.committed.receipt).toMatchObject({ id: "asset-catalog:migration:v1-to-v2", task: "M12-01G", status: "MIGRATED" });
expect(result.committed.legacy).toEqual(result.baseline.legacy);
});