Checkpoint: Audit et refactoring : centralisation robuste du suivi lu/non lu, suppression des captures SQL silencieuses, migration d’unicité avec déduplication des lectures existantes, correction du reclassement AAP via classifyAap, nettoyage de cinq composants template non utilisés, nettoyage des lectures lors des purges/fusions RSS et découpage dynamique du frontend. 27 tests Vitest, TypeScript et build de production validés.
This commit is contained in:
44
server/articleReads.test.ts
Normal file
44
server/articleReads.test.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
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]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user