45 lines
1.8 KiB
TypeScript
45 lines
1.8 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
|
import { isDuplicateEntryError, persistArticleReads, readArticleIdsFromDb } from "./db";
|
|
|
|
function createReadDb(existingIds: number[] = []) {
|
|
const insertValues = vi.fn().mockResolvedValue(undefined);
|
|
const insert = vi.fn(() => ({ values: insertValues }));
|
|
const where = vi.fn().mockResolvedValue(existingIds.map((articleId) => ({ articleId })));
|
|
const from = vi.fn(() => ({ where }));
|
|
const select = vi.fn(() => ({ from }));
|
|
return { db: { select, insert }, insertValues };
|
|
}
|
|
|
|
describe("isDuplicateEntryError", () => {
|
|
it("identifie les erreurs de contrainte unique MySQL", () => {
|
|
expect(isDuplicateEntryError({ code: "ER_DUP_ENTRY" })).toBe(true);
|
|
expect(isDuplicateEntryError({ cause: { code: "ER_DUP_ENTRY" } })).toBe(true);
|
|
});
|
|
|
|
it("ne masque jamais une erreur SQL non liée à un doublon", () => {
|
|
expect(isDuplicateEntryError(new Error("Field 'readAt' doesn't have a default value"))).toBe(false);
|
|
expect(isDuplicateEntryError({ code: "ER_NO_DEFAULT_FOR_FIELD" })).toBe(false);
|
|
expect(isDuplicateEntryError(null)).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("persistance des articles lus", () => {
|
|
it("insère uniquement les articles encore non lus et déduplique la demande", async () => {
|
|
const { db, insertValues } = createReadDb([10]);
|
|
|
|
const inserted = await persistArticleReads(db, 2, "veille", [10, 12, 12, 13]);
|
|
|
|
expect(inserted).toBe(2);
|
|
expect(insertValues).toHaveBeenCalledWith([
|
|
{ userId: 2, articleType: "veille", articleId: 12 },
|
|
{ userId: 2, articleType: "veille", articleId: 13 },
|
|
]);
|
|
});
|
|
|
|
it("restaure les identifiants lus stockés pour le bon utilisateur et le bon flux", async () => {
|
|
const { db } = createReadDb([4, 9]);
|
|
|
|
await expect(readArticleIdsFromDb(db, 2, "aap")).resolves.toEqual([4, 9]);
|
|
});
|
|
});
|