Implements GET/PATCH/PUT /users/me/preferences. Fixes profile page 'Preferences unavailable' error by correcting the /api prefix in frontend calls and adding PATCH handler to controller. Co-authored-by: Jason Woltje <jason@diversecanvas.com> Co-committed-by: Jason Woltje <jason@diversecanvas.com>
142 lines
4.9 KiB
TypeScript
142 lines
4.9 KiB
TypeScript
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
import { PreferencesService } from "./preferences.service";
|
|
import type { PrismaService } from "../prisma/prisma.service";
|
|
import type { UpdatePreferencesDto } from "./dto";
|
|
|
|
describe("PreferencesService", () => {
|
|
let service: PreferencesService;
|
|
|
|
const mockPrisma = {
|
|
userPreference: {
|
|
findUnique: vi.fn(),
|
|
create: vi.fn(),
|
|
update: vi.fn(),
|
|
},
|
|
};
|
|
|
|
const mockUserId = "user-uuid-123";
|
|
|
|
const mockDbPreference = {
|
|
id: "pref-uuid-456",
|
|
userId: mockUserId,
|
|
theme: "system",
|
|
locale: "en",
|
|
timezone: null,
|
|
settings: {},
|
|
updatedAt: new Date("2026-01-01T00:00:00Z"),
|
|
};
|
|
|
|
beforeEach(() => {
|
|
service = new PreferencesService(mockPrisma as unknown as PrismaService);
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
describe("getPreferences", () => {
|
|
it("should return existing preferences", async () => {
|
|
mockPrisma.userPreference.findUnique.mockResolvedValue(mockDbPreference);
|
|
|
|
const result = await service.getPreferences(mockUserId);
|
|
|
|
expect(result).toMatchObject({
|
|
id: mockDbPreference.id,
|
|
userId: mockUserId,
|
|
theme: "system",
|
|
locale: "en",
|
|
timezone: null,
|
|
settings: {},
|
|
});
|
|
expect(mockPrisma.userPreference.findUnique).toHaveBeenCalledWith({
|
|
where: { userId: mockUserId },
|
|
});
|
|
expect(mockPrisma.userPreference.create).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("should create default preferences when none exist", async () => {
|
|
mockPrisma.userPreference.findUnique.mockResolvedValue(null);
|
|
mockPrisma.userPreference.create.mockResolvedValue(mockDbPreference);
|
|
|
|
const result = await service.getPreferences(mockUserId);
|
|
|
|
expect(result).toMatchObject({
|
|
id: mockDbPreference.id,
|
|
userId: mockUserId,
|
|
theme: "system",
|
|
locale: "en",
|
|
});
|
|
expect(mockPrisma.userPreference.create).toHaveBeenCalledWith({
|
|
data: expect.objectContaining({
|
|
userId: mockUserId,
|
|
theme: "system",
|
|
locale: "en",
|
|
}),
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("updatePreferences", () => {
|
|
it("should update existing preferences", async () => {
|
|
const updateDto: UpdatePreferencesDto = { theme: "dark", locale: "fr" };
|
|
const updatedPreference = { ...mockDbPreference, theme: "dark", locale: "fr" };
|
|
|
|
mockPrisma.userPreference.findUnique.mockResolvedValue(mockDbPreference);
|
|
mockPrisma.userPreference.update.mockResolvedValue(updatedPreference);
|
|
|
|
const result = await service.updatePreferences(mockUserId, updateDto);
|
|
|
|
expect(result).toMatchObject({ theme: "dark", locale: "fr" });
|
|
expect(mockPrisma.userPreference.update).toHaveBeenCalledWith({
|
|
where: { userId: mockUserId },
|
|
data: expect.objectContaining({ theme: "dark", locale: "fr" }),
|
|
});
|
|
expect(mockPrisma.userPreference.create).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("should create preferences when updating non-existent record", async () => {
|
|
const updateDto: UpdatePreferencesDto = { theme: "light" };
|
|
const createdPreference = { ...mockDbPreference, theme: "light" };
|
|
|
|
mockPrisma.userPreference.findUnique.mockResolvedValue(null);
|
|
mockPrisma.userPreference.create.mockResolvedValue(createdPreference);
|
|
|
|
const result = await service.updatePreferences(mockUserId, updateDto);
|
|
|
|
expect(result).toMatchObject({ theme: "light" });
|
|
expect(mockPrisma.userPreference.create).toHaveBeenCalledWith({
|
|
data: expect.objectContaining({
|
|
userId: mockUserId,
|
|
theme: "light",
|
|
}),
|
|
});
|
|
expect(mockPrisma.userPreference.update).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("should handle timezone update", async () => {
|
|
const updateDto: UpdatePreferencesDto = { timezone: "America/New_York" };
|
|
const updatedPreference = { ...mockDbPreference, timezone: "America/New_York" };
|
|
|
|
mockPrisma.userPreference.findUnique.mockResolvedValue(mockDbPreference);
|
|
mockPrisma.userPreference.update.mockResolvedValue(updatedPreference);
|
|
|
|
const result = await service.updatePreferences(mockUserId, updateDto);
|
|
|
|
expect(result.timezone).toBe("America/New_York");
|
|
expect(mockPrisma.userPreference.update).toHaveBeenCalledWith({
|
|
where: { userId: mockUserId },
|
|
data: expect.objectContaining({ timezone: "America/New_York" }),
|
|
});
|
|
});
|
|
|
|
it("should handle settings update", async () => {
|
|
const updateDto: UpdatePreferencesDto = { settings: { notifications: true } };
|
|
const updatedPreference = { ...mockDbPreference, settings: { notifications: true } };
|
|
|
|
mockPrisma.userPreference.findUnique.mockResolvedValue(mockDbPreference);
|
|
mockPrisma.userPreference.update.mockResolvedValue(updatedPreference);
|
|
|
|
const result = await service.updatePreferences(mockUserId, updateDto);
|
|
|
|
expect(result.settings).toEqual({ notifications: true });
|
|
});
|
|
});
|
|
});
|