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:
+21
-7
@@ -946,9 +946,19 @@ describe("the backup lock", () => {
|
||||
});
|
||||
});
|
||||
|
||||
// Also serves ML data: file 100 has `ML_PAYLOAD`, the other two have none.
|
||||
// While `mlError` is set, every ML data request fails with it.
|
||||
const ML_PAYLOAD = { face: { faces: [] }, clip: { embedding: [0.5, 0.25] } };
|
||||
// Also serves ML data: file 100 has `ML_PAYLOAD`, whose CLIP embedding is that
|
||||
// of a photo of a sunset (see clip-keywords.test.ts), and the other two have
|
||||
// none. While `mlError` is set, every ML data request fails with it.
|
||||
const SUNSET_EMBEDDING: number[] = JSON.parse(
|
||||
readFileSync(
|
||||
new URL("../clip-keywords-sunset.json", import.meta.url),
|
||||
"utf-8",
|
||||
),
|
||||
).embedding;
|
||||
const ML_PAYLOAD = {
|
||||
face: { faces: [] },
|
||||
clip: { embedding: SUNSET_EMBEDDING },
|
||||
};
|
||||
|
||||
class MLClient extends MockClient {
|
||||
mlError?: string;
|
||||
@@ -968,16 +978,20 @@ const fileJSON = (outDir: string, fileID: number): Record<string, unknown> =>
|
||||
JSON.parse(readFileSync(saved(outDir, `${fileID}.json`), "utf-8"));
|
||||
|
||||
describe("ML data in each file's JSON", () => {
|
||||
it("writes a file's ML data, and no ML field for a file that has none", async () => {
|
||||
// The JSON holds the ML data without its CLIP embedding, and in its place
|
||||
// the keywords the embedding gives.
|
||||
it("writes a file's ML data and keywords, and no ML field for a file that has none", async () => {
|
||||
const lib = await openLibrary(stubSource(), new MLClient());
|
||||
const outDir = join(root, "backup");
|
||||
|
||||
const result = await lib.backup({ downloadDirectory: outDir });
|
||||
|
||||
expect(result.failed).toBe(0);
|
||||
expect(fileJSON(outDir, 100).mlData).toEqual(ML_PAYLOAD);
|
||||
expect(fileJSON(outDir, 100).mlData).toEqual({ face: { faces: [] } });
|
||||
expect(fileJSON(outDir, 100).keywords).toEqual(["Sunset"]);
|
||||
expect(fileJSON(outDir, 101)).not.toHaveProperty("mlData");
|
||||
expect(fileJSON(outDir, 101)).not.toHaveProperty("mlDataError");
|
||||
expect(fileJSON(outDir, 101)).not.toHaveProperty("keywords");
|
||||
await lib.close();
|
||||
});
|
||||
|
||||
@@ -993,7 +1007,7 @@ describe("ML data in each file's JSON", () => {
|
||||
const result = await lib.backup({ downloadDirectory: outDir });
|
||||
|
||||
// File 100's ML data was cached by the first run and is kept.
|
||||
expect(fileJSON(outDir, 100).mlData).toEqual(ML_PAYLOAD);
|
||||
expect(fileJSON(outDir, 100).mlData).toEqual({ face: { faces: [] } });
|
||||
expect(fileJSON(outDir, 100)).not.toHaveProperty("mlDataError");
|
||||
for (const fileID of [101, 200]) {
|
||||
expect(fileJSON(outDir, fileID).mlDataError).toBe(
|
||||
@@ -1026,7 +1040,7 @@ describe("ML data in each file's JSON", () => {
|
||||
|
||||
expect(result.failed).toBe(0);
|
||||
expect(result.skipped).toBe(3);
|
||||
expect(fileJSON(outDir, 100).mlData).toEqual(ML_PAYLOAD);
|
||||
expect(fileJSON(outDir, 100).mlData).toEqual({ face: { faces: [] } });
|
||||
for (const fileID of [100, 101, 200]) {
|
||||
expect(fileJSON(outDir, fileID)).not.toHaveProperty("mlDataError");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user