From 207d8ace079af338399f611d4f6fbec3a0f58fe3 Mon Sep 17 00:00:00 2001 From: Yaros Date: Wed, 25 Feb 2026 21:20:09 +0100 Subject: [PATCH] test(server): medium tests --- .../medium/specs/sync/sync-asset-ocr.spec.ts | 265 ++++++++++++++++++ 1 file changed, 265 insertions(+) create mode 100644 server/test/medium/specs/sync/sync-asset-ocr.spec.ts diff --git a/server/test/medium/specs/sync/sync-asset-ocr.spec.ts b/server/test/medium/specs/sync/sync-asset-ocr.spec.ts new file mode 100644 index 0000000000..db2aad33ae --- /dev/null +++ b/server/test/medium/specs/sync/sync-asset-ocr.spec.ts @@ -0,0 +1,265 @@ +import { Kysely } from 'kysely'; +import { SyncEntityType, SyncRequestType } from 'src/enum'; +import { OcrRepository } from 'src/repositories/ocr.repository'; +import { DB } from 'src/schema'; +import { SyncTestContext } from 'test/medium.factory'; +import { getKyselyDB } from 'test/utils'; + +let defaultDatabase: Kysely; + +const setup = async (db?: Kysely) => { + const ctx = new SyncTestContext(db || defaultDatabase); + const { auth, user, session } = await ctx.newSyncAuthUser(); + return { auth, user, session, ctx }; +}; + +beforeAll(async () => { + defaultDatabase = await getKyselyDB(); +}); + +describe(SyncEntityType.AssetOcrV1, () => { + it('should detect and sync new asset OCR', async () => { + const { auth, user, ctx } = await setup(); + + const ocrRepo = ctx.get(OcrRepository); + const { asset } = await ctx.newAsset({ ownerId: user.id }); + await ocrRepo.upsert( + asset.id, + [ + { + assetId: asset.id, + x1: 0.1, + y1: 0.2, + x2: 0.9, + y2: 0.2, + x3: 0.9, + y3: 0.8, + x4: 0.1, + y4: 0.8, + boxScore: 0.95, + textScore: 0.92, + text: 'Hello World', + isVisible: true, + }, + ], + 'Hello World', + ); + + const response = await ctx.syncStream(auth, [SyncRequestType.AssetOcrV1]); + expect(response).toEqual([ + { + ack: expect.any(String), + data: { + id: expect.any(String), + assetId: asset.id, + x1: 0.1, + y1: 0.2, + x2: 0.9, + y2: 0.2, + x3: 0.9, + y3: 0.8, + x4: 0.1, + y4: 0.8, + boxScore: 0.95, + textScore: 0.92, + text: 'Hello World', + isVisible: true, + }, + type: 'AssetOcrV1', + }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + + await ctx.syncAckAll(auth, response); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AssetOcrV1]); + }); + + it('should update asset OCR', async () => { + const { auth, user, ctx } = await setup(); + + const ocrRepo = ctx.get(OcrRepository); + const { asset } = await ctx.newAsset({ ownerId: user.id }); + await ocrRepo.upsert( + asset.id, + [ + { + assetId: asset.id, + x1: 0.1, + y1: 0.2, + x2: 0.9, + y2: 0.2, + x3: 0.9, + y3: 0.8, + x4: 0.1, + y4: 0.8, + boxScore: 0.95, + textScore: 0.92, + text: 'Hello World', + isVisible: true, + }, + ], + 'Hello World', + ); + + const response = await ctx.syncStream(auth, [SyncRequestType.AssetOcrV1]); + expect(response).toEqual([ + { + ack: expect.any(String), + data: { + id: expect.any(String), + assetId: asset.id, + x1: 0.1, + y1: 0.2, + x2: 0.9, + y2: 0.2, + x3: 0.9, + y3: 0.8, + x4: 0.1, + y4: 0.8, + boxScore: 0.95, + textScore: 0.92, + text: 'Hello World', + isVisible: true, + }, + type: 'AssetOcrV1', + }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + + await ctx.syncAckAll(auth, response); + + // Update OCR data (upsert deletes old entries first, then inserts new ones) + await ocrRepo.upsert( + asset.id, + [ + { + assetId: asset.id, + x1: 0.15, + y1: 0.25, + x2: 0.85, + y2: 0.25, + x3: 0.85, + y3: 0.75, + x4: 0.15, + y4: 0.75, + boxScore: 0.98, + textScore: 0.96, + text: 'Updated Text', + isVisible: true, + }, + ], + 'Updated Text', + ); + + const updatedResponse = await ctx.syncStream(auth, [SyncRequestType.AssetOcrV1]); + // upsert() deletes old entries first, so we expect both delete and upsert events + expect(updatedResponse).toEqual([ + { + ack: expect.any(String), + data: { + id: expect.any(String), + assetId: asset.id, + deletedAt: expect.any(String), + }, + type: 'AssetOcrDeleteV1', + }, + { + ack: expect.any(String), + data: { + id: expect.any(String), + assetId: asset.id, + x1: 0.15, + y1: 0.25, + x2: 0.85, + y2: 0.25, + x3: 0.85, + y3: 0.75, + x4: 0.15, + y4: 0.75, + boxScore: 0.98, + textScore: 0.96, + text: 'Updated Text', + isVisible: true, + }, + type: 'AssetOcrV1', + }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + + await ctx.syncAckAll(auth, updatedResponse); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AssetOcrV1]); + }); +}); + +describe(SyncEntityType.AssetOcrDeleteV1, () => { + it('should delete and sync asset OCR', async () => { + const { auth, user, ctx } = await setup(); + + const ocrRepo = ctx.get(OcrRepository); + const { asset } = await ctx.newAsset({ ownerId: user.id }); + await ocrRepo.upsert( + asset.id, + [ + { + assetId: asset.id, + x1: 0.1, + y1: 0.2, + x2: 0.9, + y2: 0.2, + x3: 0.9, + y3: 0.8, + x4: 0.1, + y4: 0.8, + boxScore: 0.95, + textScore: 0.92, + text: 'Hello World', + isVisible: true, + }, + ], + 'Hello World', + ); + + const response = await ctx.syncStream(auth, [SyncRequestType.AssetOcrV1]); + expect(response).toEqual([ + { + ack: expect.any(String), + data: { + id: expect.any(String), + assetId: asset.id, + x1: 0.1, + y1: 0.2, + x2: 0.9, + y2: 0.2, + x3: 0.9, + y3: 0.8, + x4: 0.1, + y4: 0.8, + boxScore: 0.95, + textScore: 0.92, + text: 'Hello World', + isVisible: true, + }, + type: 'AssetOcrV1', + }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + + await ctx.syncAckAll(auth, response); + + // Delete OCR data + await ocrRepo.upsert(asset.id, [], ''); + + await expect(ctx.syncStream(auth, [SyncRequestType.AssetOcrV1])).resolves.toEqual([ + { + ack: expect.any(String), + data: { + assetId: asset.id, + deletedAt: expect.any(String), + id: expect.any(String), + }, + type: 'AssetOcrDeleteV1', + }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + }); +});