fix(housekeeping): harden preference persistence
This commit is contained in:
@@ -1,4 +1,16 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
const drizzleMocks = vi.hoisted(() => ({
|
||||
eq: vi.fn((column: unknown, value: unknown) => ({ column, value })),
|
||||
}));
|
||||
|
||||
vi.mock("drizzle-orm", async (importOriginal) => ({
|
||||
...(await importOriginal<typeof import("drizzle-orm")>()),
|
||||
eq: drizzleMocks.eq,
|
||||
}));
|
||||
|
||||
import { HousekeepingUserPreferences } from "@/lib/db";
|
||||
import { createDrizzleHousekeepingPreferencesStorage } from "@/lib/housekeeping-preferences-repository";
|
||||
import {
|
||||
createHousekeepingPreferencesRepository,
|
||||
type HousekeepingPreferencesStorage,
|
||||
@@ -39,4 +51,90 @@ describe("housekeeping preferences repository", () => {
|
||||
payload: JSON.stringify(value),
|
||||
});
|
||||
});
|
||||
|
||||
it("returns a fresh default for malformed or incompatible stored values", async () => {
|
||||
const storage: HousekeepingPreferencesStorage = {
|
||||
findByUserId: vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce({ schemaVersion: 1, payload: "{broken" })
|
||||
.mockResolvedValueOnce({ schemaVersion: 2, payload: "{}" })
|
||||
.mockResolvedValueOnce({
|
||||
schemaVersion: 1,
|
||||
payload: JSON.stringify({ schemaVersion: 1 }),
|
||||
}),
|
||||
upsert: vi.fn(),
|
||||
};
|
||||
const repository = createHousekeepingPreferencesRepository(storage);
|
||||
|
||||
await expect(repository.read(42)).resolves.toEqual(
|
||||
defaultHousekeepingPreferences(),
|
||||
);
|
||||
await expect(repository.read(42)).resolves.toEqual(
|
||||
defaultHousekeepingPreferences(),
|
||||
);
|
||||
await expect(repository.read(42)).resolves.toEqual(
|
||||
defaultHousekeepingPreferences(),
|
||||
);
|
||||
});
|
||||
|
||||
it("preserves genuine storage failures instead of treating them as corrupt preferences", async () => {
|
||||
const storage: HousekeepingPreferencesStorage = {
|
||||
findByUserId: vi
|
||||
.fn()
|
||||
.mockRejectedValue(new Error("database unavailable")),
|
||||
upsert: vi.fn(),
|
||||
};
|
||||
|
||||
await expect(
|
||||
createHousekeepingPreferencesRepository(storage).read(42),
|
||||
).rejects.toThrow("database unavailable");
|
||||
});
|
||||
|
||||
it("uses the preferences table and user ID for the typed Drizzle read and upsert", async () => {
|
||||
const limit = vi.fn().mockResolvedValue([]);
|
||||
const where = vi.fn().mockReturnValue({ limit });
|
||||
const from = vi.fn().mockReturnValue({ where });
|
||||
const select = vi.fn().mockReturnValue({ from });
|
||||
const onDuplicateKeyUpdate = vi.fn().mockResolvedValue(undefined);
|
||||
const values = vi.fn().mockReturnValue({ onDuplicateKeyUpdate });
|
||||
const insert = vi.fn().mockReturnValue({ values });
|
||||
const storage = createDrizzleHousekeepingPreferencesStorage({
|
||||
select,
|
||||
insert,
|
||||
} as never);
|
||||
const value = defaultHousekeepingPreferences();
|
||||
|
||||
await storage.findByUserId(42);
|
||||
await storage.upsert({
|
||||
userId: 42,
|
||||
schemaVersion: 1,
|
||||
payload: JSON.stringify(value),
|
||||
});
|
||||
|
||||
expect(select).toHaveBeenCalledWith({
|
||||
schemaVersion: HousekeepingUserPreferences.schemaVersion,
|
||||
payload: HousekeepingUserPreferences.payload,
|
||||
});
|
||||
expect(from).toHaveBeenCalledWith(HousekeepingUserPreferences);
|
||||
expect(drizzleMocks.eq).toHaveBeenCalledWith(
|
||||
HousekeepingUserPreferences.userId,
|
||||
42,
|
||||
);
|
||||
expect(where).toHaveBeenCalledWith({
|
||||
column: HousekeepingUserPreferences.userId,
|
||||
value: 42,
|
||||
});
|
||||
expect(values).toHaveBeenCalledWith({
|
||||
userId: 42,
|
||||
schemaVersion: 1,
|
||||
payload: JSON.stringify(value),
|
||||
});
|
||||
expect(onDuplicateKeyUpdate).toHaveBeenCalledWith({
|
||||
set: expect.objectContaining({
|
||||
schemaVersion: 1,
|
||||
payload: JSON.stringify(value),
|
||||
updatedAt: expect.any(Date),
|
||||
}),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user