Test keywords in place of the CLIP embedding in a file's JSON
check / check (push) Waiting to run

clipKeywords takes a file's ML data and whether it is a video, and gives the
titles of the Discover categories of Ente's photos app that its CLIP embedding
falls in, highest score first. The tests cover the rule with made-up vectors
and Ente's real categories with the CLIP embedding of a public-domain photo of
a sunset. The backup and backup-metadata tests now expect keywords in a file's
JSON and no CLIP embedding in its mlData. These tests fail until the
implementation lands.

Model: opus-5-5
This commit is contained in:
2026-10-22 18:36:30 +00:00
parent ae0f72971d
commit 4863833f0a
5 changed files with 290 additions and 16 deletions
+17 -6
View File
@@ -66,6 +66,15 @@ vi.mock("../../src/mldata-fetch.js", async (importOriginal) => ({
const TEST_EMAIL = "metabackup@example.com";
const TEST_PASSWORD = "metapass";
// The CLIP embedding of a photo of a sunset (see clip-keywords.test.ts), which
// the ML data of beach.jpg holds.
const SUNSET_EMBEDDING: number[] = JSON.parse(
readFileSync(
new URL("../clip-keywords-sunset.json", import.meta.url),
"utf-8",
),
).embedding;
let mock: MetaMockState;
let testDir: string;
@@ -230,7 +239,7 @@ const buildMetaMock = async (): Promise<MetaMockState> => {
clip: {
version: 1,
client: "test",
embedding: [0.5, 0.6, 0.7],
embedding: SUNSET_EMBEDDING,
},
});
const gzipped = gzipSync(Buffer.from(mlPayload));
@@ -432,7 +441,9 @@ describe("quak backup-metadata", () => {
expect(account.email).toBe(TEST_EMAIL);
});
it("fetches and decrypts ML data by default", async () => {
// The CLIP embedding is left out of the ML data, and the keywords it gives
// are written in its place.
it("fetches and decrypts ML data by default, with keywords for the CLIP embedding", async () => {
const outDir = join(testDir, "ml-data");
await runBackup(outDir);
@@ -452,8 +463,8 @@ describe("quak backup-metadata", () => {
expect(fileMeta.mlData.face.faces[0].faceID).toBe("face-abc");
expect(fileMeta.mlData.face.faces[0].score).toBeCloseTo(0.98);
expect(fileMeta.mlData.face.faces[0].detection.box.x).toBeCloseTo(0.1);
expect(fileMeta.mlData.clip).toBeDefined();
expect(fileMeta.mlData.clip.embedding).toEqual([0.5, 0.6, 0.7]);
expect(fileMeta.mlData).not.toHaveProperty("clip");
expect(fileMeta.keywords).toEqual(["Sunset"]);
});
it("extracts EXIF from downloaded files when --exif is set", async () => {
@@ -541,7 +552,7 @@ describe("quak backup-metadata when a file's ML data cannot be read", () => {
"utf-8",
),
);
expect(ok.mlData.clip.embedding).toEqual([0.5, 0.6, 0.7]);
expect(ok.keywords).toEqual(["Sunset"]);
expect(ok.mlDataError).toBeUndefined();
const failed = JSON.parse(
@@ -599,7 +610,7 @@ describe("quak backup-metadata when a file's ML data cannot be read", () => {
"utf-8",
),
);
expect(ok.mlData.clip.embedding).toEqual([0.5, 0.6, 0.7]);
expect(ok.keywords).toEqual(["Sunset"]);
});
it("exits 0 when every ML data request succeeds", async () => {