Advance Blender 5.2 web parity through M12-03D
This commit is contained in:
113
web/tests/e2e/asset-preview-dedup.spec.ts
Normal file
113
web/tests/e2e/asset-preview-dedup.spec.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
const catalog = JSON.parse(fs.readFileSync(
|
||||
path.resolve(import.meta.dirname, "../../../tests/golden/M12-01D/catalog-v2.json"),
|
||||
"utf8",
|
||||
)) as Record<string, unknown>;
|
||||
const identity = JSON.parse(fs.readFileSync(
|
||||
path.resolve(import.meta.dirname, "../../../tests/golden/M12-02B/identity.json"),
|
||||
"utf8",
|
||||
)) as Record<string, unknown>;
|
||||
const preview = fs.readFileSync(path.resolve(import.meta.dirname, "../../../tests/golden/M12-02B/preview.png"));
|
||||
|
||||
test("M12-02E deduplicates preview content without merging asset metadata", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
const result = await page.evaluate(async ({ sourceCatalog, sourceIdentity, bytes }) => {
|
||||
const storage = await import("/src/storage/asset-preview-opfs-commit.ts");
|
||||
const encoded = new Uint8Array(bytes).buffer;
|
||||
const base = await storage.parseAssetCatalogManifestV2({
|
||||
...sourceCatalog,
|
||||
assets: (sourceCatalog.assets as Array<Record<string, unknown>>).map((asset) => ({ ...asset, preview: null })),
|
||||
});
|
||||
const identities = [];
|
||||
for (const asset of base.assets) {
|
||||
const input = { ...sourceIdentity, assetId: asset.assetId } as Record<string, unknown>;
|
||||
delete input.identitySha256;
|
||||
identities.push(await storage.createAssetPreviewIdentity(input as never));
|
||||
}
|
||||
const databaseName = `m12-02e-${crypto.randomUUID()}`;
|
||||
const projectId = `m12-02e-${crypto.randomUUID()}`;
|
||||
const open = (): Promise<IDBDatabase> => new Promise((resolve, reject) => {
|
||||
const request = indexedDB.open(databaseName, 1);
|
||||
request.onupgradeneeded = () => request.result.createObjectStore("setting", { 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);
|
||||
});
|
||||
let database = await open();
|
||||
const seed = database.transaction("setting", "readwrite");
|
||||
seed.objectStore("setting").put({ id: "asset-catalog:index:v2", value: base });
|
||||
await complete(seed);
|
||||
const first = await storage.commitAssetPreviewToOPFS(database, projectId, 7, identities[0], encoded.slice(0));
|
||||
const second = await storage.commitAssetPreviewToOPFS(database, projectId, 8, identities[1], encoded.slice(0));
|
||||
database.close();
|
||||
database = await open();
|
||||
const reopened = await storage.loadCommittedAssetPreviewCatalog(database);
|
||||
|
||||
const root = await navigator.storage.getDirectory();
|
||||
let contentDirectory = root;
|
||||
for (const segment of ["projects", projectId, "assets", "sha256", identities[0].content.sha256.slice(0, 2)]) {
|
||||
contentDirectory = await contentDirectory.getDirectoryHandle(segment);
|
||||
}
|
||||
const files: string[] = [];
|
||||
for await (const [name, handle] of (contentDirectory as unknown as { entries: () => AsyncIterable<[string, FileSystemHandle]> }).entries()) {
|
||||
if (handle.kind === "file") files.push(name);
|
||||
}
|
||||
files.sort();
|
||||
const stored = await (await contentDirectory.getFileHandle(identities[0].content.sha256)).getFile();
|
||||
const storedBytes = await stored.arrayBuffer();
|
||||
const storedSha256 = Array.from(new Uint8Array(await crypto.subtle.digest("SHA-256", storedBytes)),
|
||||
(byte) => byte.toString(16).padStart(2, "0")).join("");
|
||||
const withoutPreview = (asset: Record<string, unknown>) => {
|
||||
const copy = { ...asset };
|
||||
delete copy.preview;
|
||||
return copy;
|
||||
};
|
||||
const resultValue = {
|
||||
first: { revision: first.manifest.revision, deduplicated: first.deduplicated, identity: identities[0].identitySha256 },
|
||||
second: { revision: second.manifest.revision, deduplicated: second.deduplicated, identity: identities[1].identitySha256 },
|
||||
contentSha256: identities[0].content.sha256,
|
||||
files,
|
||||
storedBytes: stored.size,
|
||||
storedSha256,
|
||||
baseMetadata: base.assets.map((asset) => withoutPreview(asset as unknown as Record<string, unknown>)),
|
||||
committedMetadata: second.manifest.assets.map((asset) => withoutPreview(asset as unknown as Record<string, unknown>)),
|
||||
previews: second.manifest.assets.map((asset) => asset.preview),
|
||||
reopened: {
|
||||
revision: reopened.manifest.revision,
|
||||
receiptAssetId: reopened.receipt.assetId,
|
||||
preview: reopened.preview,
|
||||
},
|
||||
assetIds: base.assets.map((asset) => asset.assetId),
|
||||
};
|
||||
database.close();
|
||||
indexedDB.deleteDatabase(databaseName);
|
||||
const projects = await root.getDirectoryHandle("projects");
|
||||
await projects.removeEntry(projectId, { recursive: true });
|
||||
return resultValue;
|
||||
}, { sourceCatalog: catalog, sourceIdentity: identity, bytes: [...preview] });
|
||||
|
||||
expect(result.first).toMatchObject({ revision: 8, deduplicated: false });
|
||||
expect(result.second).toMatchObject({ revision: 9, deduplicated: true });
|
||||
expect(result.first.identity).not.toBe(result.second.identity);
|
||||
expect(result.files).toEqual([result.contentSha256]);
|
||||
expect(result.storedBytes).toBe(513);
|
||||
expect(result.storedSha256).toBe(result.contentSha256);
|
||||
expect(result.committedMetadata).toEqual(result.baseMetadata);
|
||||
expect(result.committedMetadata[0]).not.toEqual(result.committedMetadata[1]);
|
||||
expect(result.previews).toEqual([
|
||||
{ sha256: result.contentSha256, mimeType: "image/png", width: 8, height: 8, byteLength: 513 },
|
||||
{ sha256: result.contentSha256, mimeType: "image/png", width: 8, height: 8, byteLength: 513 },
|
||||
]);
|
||||
expect(result.reopened).toEqual({
|
||||
revision: 9,
|
||||
receiptAssetId: result.assetIds[1],
|
||||
preview: result.previews[1],
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user