better-chatbot / src /lib /user /server.test.ts
Bot
Initial commit for HF Spaces
05c5ed5
Raw
History Blame Contribute Delete
7.24 kB
//@vitest-environment node
import { describe, it, expect, vi, beforeEach, beforeAll } from "vitest";
// Mock server-only module
vi.mock("server-only", () => ({}));
// Mock dependencies
vi.mock("lib/db/repository", () => ({
userRepository: {
getUserById: vi.fn(),
getUserStats: vi.fn(),
getPreferences: vi.fn(),
updateUserDetails: vi.fn(),
},
}));
vi.mock("auth/server", () => ({
auth: {
api: {
listUserAccounts: vi.fn(),
listSessions: vi.fn(),
},
},
getSession: vi.fn(),
}));
vi.mock("next/headers", () => ({
headers: vi.fn(),
}));
vi.mock("next/navigation", () => ({
notFound: vi.fn(),
}));
const { auth, getSession } = await import("auth/server");
const { headers } = await import("next/headers");
const { notFound } = await import("next/navigation");
import {
getUserAccounts,
getUserIdAndCheckAccess,
updateUserDetails,
} from "./server";
/*
* Tests focus on the business logic of the user server.
*/
describe("User Server", () => {
beforeEach(() => {
vi.clearAllMocks();
});
describe("getUserAccounts - Account Type Detection", () => {
beforeEach(() => {
vi.mocked(getSession).mockResolvedValue({
user: { id: "user-1" },
} as any);
vi.mocked(headers).mockResolvedValue(new Headers());
});
it("should correctly identify password vs OAuth accounts", async () => {
const mockAccounts = [
{ providerId: "credential", id: "1" },
{ providerId: "google", id: "2" },
{ providerId: "github", id: "3" },
];
vi.mocked(auth.api.listUserAccounts).mockResolvedValue(
mockAccounts as any,
);
const result = await getUserAccounts("user-1");
expect(result.hasPassword).toBe(true);
expect(result.oauthProviders).toEqual(["google", "github"]);
});
it("should handle OAuth-only accounts", async () => {
const mockAccounts = [
{ providerId: "google", id: "1" },
{ providerId: "github", id: "2" },
];
vi.mocked(auth.api.listUserAccounts).mockResolvedValue(
mockAccounts as any,
);
const result = await getUserAccounts("user-1");
expect(result.hasPassword).toBe(false);
expect(result.oauthProviders).toEqual(["google", "github"]);
});
it("should handle password-only accounts", async () => {
const mockAccounts = [{ providerId: "credential", id: "1" }];
vi.mocked(auth.api.listUserAccounts).mockResolvedValue(
mockAccounts as any,
);
const result = await getUserAccounts("user-1");
expect(result.hasPassword).toBe(true);
expect(result.oauthProviders).toEqual([]);
});
it("should filter out credential provider from OAuth list", async () => {
const mockAccounts = [
{ providerId: "credential", id: "1" },
{ providerId: "credential", id: "2" }, // multiple credential accounts
{ providerId: "google", id: "3" },
];
vi.mocked(auth.api.listUserAccounts).mockResolvedValue(
mockAccounts as any,
);
const result = await getUserAccounts("user-1");
expect(result.hasPassword).toBe(true);
expect(result.oauthProviders).toEqual(["google"]); // credential filtered out
});
});
describe("getUserIdAndCheckAccess - Access Control Logic", () => {
it("should use requested user ID when provided", async () => {
vi.mocked(getSession).mockResolvedValue({
user: { id: "current-user" },
} as any);
const result = await getUserIdAndCheckAccess("target-user");
expect(result).toBe("target-user");
});
it("should fall back to current user ID when none provided", async () => {
vi.mocked(getSession).mockResolvedValue({
user: { id: "current-user" },
} as any);
const result = await getUserIdAndCheckAccess();
expect(result).toBe("current-user");
});
it("should call notFound for falsy user IDs", async () => {
vi.mocked(getSession).mockResolvedValue({ user: { id: "" } } as any);
await getUserIdAndCheckAccess();
expect(notFound).toHaveBeenCalled();
});
it("should handle null/undefined gracefully", async () => {
vi.mocked(getSession).mockResolvedValue({
user: { id: "fallback-user" },
} as any);
const result1 = await getUserIdAndCheckAccess(null as any);
const result2 = await getUserIdAndCheckAccess(undefined);
expect(result1).toBe("fallback-user");
expect(result2).toBe("fallback-user");
});
});
describe("updateUserDetails - User Update Logic", () => {
let userRepository: any;
beforeAll(async () => {
const userRepositoryModule = await import("lib/db/repository");
userRepository = userRepositoryModule.userRepository;
});
beforeEach(() => {
vi.mocked(getSession).mockResolvedValue({
user: { id: "current-user" },
} as any);
});
it("should update user with provided fields", async () => {
vi.mocked(userRepository.updateUserDetails).mockResolvedValue(undefined);
await updateUserDetails(
"user-1",
"New Name",
"new@email.com",
"new-image.jpg",
);
expect(userRepository.updateUserDetails).toHaveBeenCalledWith({
userId: "user-1",
name: "New Name",
email: "new@email.com",
image: "new-image.jpg",
});
});
it("should update only name when provided", async () => {
vi.mocked(userRepository.updateUserDetails).mockResolvedValue(undefined);
await updateUserDetails("user-1", "New Name");
expect(userRepository.updateUserDetails).toHaveBeenCalledWith({
userId: "user-1",
name: "New Name",
});
});
it("should update only email when provided", async () => {
vi.mocked(userRepository.updateUserDetails).mockResolvedValue(undefined);
await updateUserDetails("user-1", undefined, "new@email.com");
expect(userRepository.updateUserDetails).toHaveBeenCalledWith({
userId: "user-1",
email: "new@email.com",
});
});
it("should update only image when provided", async () => {
vi.mocked(userRepository.updateUserDetails).mockResolvedValue(undefined);
await updateUserDetails("user-1", undefined, undefined, "new-image.jpg");
expect(userRepository.updateUserDetails).toHaveBeenCalledWith({
userId: "user-1",
image: "new-image.jpg",
});
});
it("should return early when no fields provided", async () => {
await updateUserDetails("user-1");
expect(userRepository.updateUserDetails).not.toHaveBeenCalled();
});
it("should handle empty string values as falsy", async () => {
await updateUserDetails("user-1", "", "", "");
expect(userRepository.updateUserDetails).not.toHaveBeenCalled();
});
it("should use resolved user ID from access check", async () => {
vi.mocked(userRepository.updateUserDetails).mockResolvedValue(undefined);
await updateUserDetails("user-1", "New Name");
expect(userRepository.updateUserDetails).toHaveBeenCalledWith({
userId: "user-1",
name: "New Name",
});
});
});
});