refactor: initial moves
This commit is contained in:
208
shared/modules/class/class.service.test.ts
Normal file
208
shared/modules/class/class.service.test.ts
Normal file
@@ -0,0 +1,208 @@
|
||||
import { describe, it, expect, mock, beforeEach } from "bun:test";
|
||||
import { classService } from "@shared/modules/class/class.service";
|
||||
import { classes, users } from "@db/schema";
|
||||
|
||||
// Define mock functions
|
||||
const mockFindMany = mock();
|
||||
const mockFindFirst = mock();
|
||||
const mockInsert = mock();
|
||||
const mockUpdate = mock();
|
||||
const mockDelete = mock();
|
||||
const mockValues = mock();
|
||||
const mockReturning = mock();
|
||||
const mockSet = mock();
|
||||
const mockWhere = mock();
|
||||
|
||||
// Chainable mock setup
|
||||
mockInsert.mockReturnValue({ values: mockValues });
|
||||
mockValues.mockReturnValue({ returning: mockReturning });
|
||||
|
||||
mockUpdate.mockReturnValue({ set: mockSet });
|
||||
mockSet.mockReturnValue({ where: mockWhere });
|
||||
mockWhere.mockReturnValue({ returning: mockReturning });
|
||||
|
||||
mockDelete.mockReturnValue({ where: mockWhere }); // Fix for delete chaining if needed, usually delete(table).where(...)
|
||||
|
||||
// Mock DrizzleClient
|
||||
mock.module("@shared/db/DrizzleClient", () => {
|
||||
return {
|
||||
DrizzleClient: {
|
||||
query: {
|
||||
classes: {
|
||||
findMany: mockFindMany,
|
||||
findFirst: mockFindFirst,
|
||||
},
|
||||
},
|
||||
insert: mockInsert,
|
||||
update: mockUpdate,
|
||||
delete: mockDelete,
|
||||
transaction: async (cb: any) => {
|
||||
return cb({
|
||||
query: {
|
||||
classes: {
|
||||
findMany: mockFindMany,
|
||||
findFirst: mockFindFirst,
|
||||
},
|
||||
},
|
||||
insert: mockInsert,
|
||||
update: mockUpdate,
|
||||
delete: mockDelete,
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
describe("classService", () => {
|
||||
beforeEach(() => {
|
||||
mockFindMany.mockReset();
|
||||
mockFindFirst.mockReset();
|
||||
mockInsert.mockClear();
|
||||
mockUpdate.mockClear();
|
||||
mockDelete.mockClear();
|
||||
mockValues.mockClear();
|
||||
mockReturning.mockClear();
|
||||
mockSet.mockClear();
|
||||
mockWhere.mockClear();
|
||||
});
|
||||
|
||||
describe("getAllClasses", () => {
|
||||
it("should return all classes", async () => {
|
||||
const mockClasses = [{ id: 1n, name: "Warrior" }, { id: 2n, name: "Mage" }];
|
||||
mockFindMany.mockResolvedValue(mockClasses);
|
||||
|
||||
const result = await classService.getAllClasses();
|
||||
|
||||
expect(result).toEqual(mockClasses as any);
|
||||
expect(mockFindMany).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("assignClass", () => {
|
||||
it("should assign class to user if class exists", async () => {
|
||||
const mockClass = { id: 1n, name: "Warrior" };
|
||||
const mockUser = { id: 123n, classId: 1n };
|
||||
|
||||
mockFindFirst.mockResolvedValue(mockClass);
|
||||
mockReturning.mockResolvedValue([mockUser]);
|
||||
|
||||
const result = await classService.assignClass("123", 1n);
|
||||
|
||||
expect(result).toEqual(mockUser as any);
|
||||
// Verify class check
|
||||
expect(mockFindFirst).toHaveBeenCalledTimes(1);
|
||||
// Verify update
|
||||
expect(mockUpdate).toHaveBeenCalledWith(users);
|
||||
expect(mockSet).toHaveBeenCalledWith({ classId: 1n });
|
||||
});
|
||||
|
||||
it("should throw error if class not found", async () => {
|
||||
mockFindFirst.mockResolvedValue(undefined);
|
||||
|
||||
expect(classService.assignClass("123", 99n)).rejects.toThrow("Class not found");
|
||||
});
|
||||
});
|
||||
|
||||
describe("getClassBalance", () => {
|
||||
it("should return class balance", async () => {
|
||||
const mockClass = { id: 1n, balance: 100n };
|
||||
mockFindFirst.mockResolvedValue(mockClass);
|
||||
|
||||
const result = await classService.getClassBalance(1n);
|
||||
|
||||
expect(result).toBe(100n);
|
||||
});
|
||||
|
||||
it("should return 0n if class has no balance or not found", async () => {
|
||||
mockFindFirst.mockResolvedValue(null);
|
||||
const result = await classService.getClassBalance(1n);
|
||||
expect(result).toBe(0n);
|
||||
|
||||
mockFindFirst.mockResolvedValue({ id: 1n, balance: null });
|
||||
const result2 = await classService.getClassBalance(1n);
|
||||
expect(result2).toBe(0n);
|
||||
});
|
||||
});
|
||||
|
||||
describe("modifyClassBalance", () => {
|
||||
it("should modify class balance successfully", async () => {
|
||||
const mockClass = { id: 1n, balance: 100n };
|
||||
const updatedClass = { id: 1n, balance: 150n };
|
||||
|
||||
mockFindFirst.mockResolvedValue(mockClass);
|
||||
mockReturning.mockResolvedValue([updatedClass]);
|
||||
|
||||
const result = await classService.modifyClassBalance(1n, 50n);
|
||||
|
||||
expect(result).toEqual(updatedClass as any);
|
||||
expect(mockUpdate).toHaveBeenCalledWith(classes);
|
||||
// Note: sql template literal matching might be tricky, checking strict call might fail if not exact object ref
|
||||
// We verify at least mockSet was called
|
||||
expect(mockSet).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should throw if class not found", async () => {
|
||||
mockFindFirst.mockResolvedValue(undefined);
|
||||
expect(classService.modifyClassBalance(1n, 50n)).rejects.toThrow("Class not found");
|
||||
});
|
||||
|
||||
it("should throw if insufficient funds", async () => {
|
||||
const mockClass = { id: 1n, balance: 10n };
|
||||
mockFindFirst.mockResolvedValue(mockClass);
|
||||
|
||||
expect(classService.modifyClassBalance(1n, -20n)).rejects.toThrow("Insufficient class funds");
|
||||
});
|
||||
});
|
||||
|
||||
describe("updateClass", () => {
|
||||
it("should update class details", async () => {
|
||||
const updateData = { name: "Super Warrior" };
|
||||
const updatedClass = { id: 1n, name: "Super Warrior" };
|
||||
|
||||
mockReturning.mockResolvedValue([updatedClass]);
|
||||
|
||||
const result = await classService.updateClass(1n, updateData);
|
||||
|
||||
expect(result).toEqual(updatedClass as any);
|
||||
expect(mockUpdate).toHaveBeenCalledWith(classes);
|
||||
expect(mockSet).toHaveBeenCalledWith(updateData);
|
||||
});
|
||||
});
|
||||
|
||||
describe("createClass", () => {
|
||||
it("should create a new class", async () => {
|
||||
const newClassData = { name: "Archer", description: "Bow user" };
|
||||
const createdClass = { id: 3n, ...newClassData };
|
||||
|
||||
mockReturning.mockResolvedValue([createdClass]);
|
||||
|
||||
const result = await classService.createClass(newClassData as any);
|
||||
|
||||
expect(result).toEqual(createdClass as any);
|
||||
expect(mockInsert).toHaveBeenCalledWith(classes);
|
||||
expect(mockValues).toHaveBeenCalledWith(newClassData);
|
||||
});
|
||||
});
|
||||
|
||||
describe("deleteClass", () => {
|
||||
it("should delete a class", async () => {
|
||||
mockDelete.mockReturnValue({ where: mockWhere });
|
||||
// The chain is delete(table).where(...) for delete
|
||||
|
||||
// Wait, in user.service.test.ts:
|
||||
// mockDelete called without chain setup in the file provided?
|
||||
// "mockDelete = mock()"
|
||||
// And in mock: "delete: mockDelete"
|
||||
// And in usage: "await txFn.delete(users).where(...)"
|
||||
// So mockDelete must return an object with where.
|
||||
|
||||
mockDelete.mockReturnValue({ where: mockWhere });
|
||||
|
||||
await classService.deleteClass(1n);
|
||||
|
||||
expect(mockDelete).toHaveBeenCalledWith(classes);
|
||||
// We can't easily check 'where' arguments specifically without complex matcher if we don't return specific mock
|
||||
expect(mockWhere).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user