Enhance branch management with foreign key relation handling
- Updated `BranchesModule` to include foreign key relations in list and write responses, ensuring they are represented as nested objects using `pickRelation`. - Introduced new `relation-response.mdc` file to define guidelines for embedding foreign key relations. - Modified `BranchesRepository` to support fetching related `division`, `createdByUser`, and `updatedByUser` data. - Updated DTOs and service methods to reflect changes in response structure, removing direct foreign key IDs. - Added unit tests to validate the new relation handling in branches service and repository. - Enhanced e2e tests to verify the correct structure of branch responses with nested relations.
This commit is contained in:
@@ -26,6 +26,7 @@ describe('BranchesRepository', () => {
|
||||
const del = jest.fn();
|
||||
const transaction = jest.fn();
|
||||
const $dynamic = jest.fn();
|
||||
const leftJoin = jest.fn();
|
||||
|
||||
const db = {
|
||||
select,
|
||||
@@ -56,6 +57,13 @@ describe('BranchesRepository', () => {
|
||||
updatedBy: 'user-1',
|
||||
};
|
||||
|
||||
const joinedRow = {
|
||||
branch: row,
|
||||
division: { id: 'div-1', code: 'JKT', name: 'Jakarta' },
|
||||
createdByUser: { id: 'user-1', username: 'admin' },
|
||||
updatedByUser: { id: 'user-1', username: 'admin' },
|
||||
};
|
||||
|
||||
const createInput = {
|
||||
code: 'JKT_01',
|
||||
name: 'Jakarta Pusat',
|
||||
@@ -68,16 +76,34 @@ describe('BranchesRepository', () => {
|
||||
userId: 'user-1',
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks();
|
||||
where.mockImplementation(() => ({ limit, orderBy }));
|
||||
orderBy.mockImplementation(() => ({ limit }));
|
||||
limit.mockImplementation(() => ({ offset }));
|
||||
offset.mockResolvedValue([row]);
|
||||
from.mockImplementation(() => ({
|
||||
const joinChain = () => {
|
||||
const chain: {
|
||||
leftJoin: jest.Mock;
|
||||
where: typeof where;
|
||||
$dynamic: typeof $dynamic;
|
||||
} = {
|
||||
leftJoin: jest.fn(),
|
||||
where,
|
||||
$dynamic,
|
||||
};
|
||||
chain.leftJoin.mockReturnValue(chain);
|
||||
return chain;
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks();
|
||||
where.mockImplementation(() => ({ limit, orderBy, returning }));
|
||||
orderBy.mockImplementation(() => ({ limit }));
|
||||
limit.mockImplementation(() => ({
|
||||
offset,
|
||||
then: (
|
||||
resolve: (value: (typeof joinedRow)[]) => unknown,
|
||||
reject?: (reason: unknown) => unknown,
|
||||
) => Promise.resolve([joinedRow]).then(resolve, reject),
|
||||
}));
|
||||
offset.mockResolvedValue([joinedRow]);
|
||||
from.mockImplementation(() => joinChain());
|
||||
leftJoin.mockImplementation(() => joinChain());
|
||||
$dynamic.mockReturnValue({ where });
|
||||
select.mockImplementation(() => ({ from }));
|
||||
values.mockReturnValue({ returning });
|
||||
@@ -86,7 +112,6 @@ describe('BranchesRepository', () => {
|
||||
update.mockReturnValue({ set });
|
||||
del.mockReturnValue({ where });
|
||||
returning.mockResolvedValue([row]);
|
||||
where.mockImplementation(() => ({ limit, orderBy, returning }));
|
||||
|
||||
const moduleRef: TestingModule = await Test.createTestingModule({
|
||||
providers: [BranchesRepository, { provide: DRIZZLE, useValue: db }],
|
||||
@@ -95,13 +120,15 @@ describe('BranchesRepository', () => {
|
||||
});
|
||||
|
||||
it('findById maps a row to domain Branch', async () => {
|
||||
limit.mockResolvedValueOnce([row]);
|
||||
const branch = await repository.findById('br-1');
|
||||
expect(branch).toMatchObject({
|
||||
id: 'br-1',
|
||||
code: 'JKT_01',
|
||||
name: 'Jakarta Pusat',
|
||||
createdBy: 'user-1',
|
||||
division: { id: 'div-1', code: 'JKT', name: 'Jakarta' },
|
||||
createdByUser: { id: 'user-1', username: 'admin' },
|
||||
updatedByUser: { id: 'user-1', username: 'admin' },
|
||||
});
|
||||
expect(branch?.phone.value).toBe('+6281234567890');
|
||||
expect(branch?.status.value).toBe('draft');
|
||||
@@ -109,12 +136,11 @@ describe('BranchesRepository', () => {
|
||||
});
|
||||
|
||||
it('findById returns null when missing', async () => {
|
||||
limit.mockResolvedValueOnce([]);
|
||||
limit.mockImplementationOnce(() => Promise.resolve([]));
|
||||
await expect(repository.findById('missing')).resolves.toBeNull();
|
||||
});
|
||||
|
||||
it('findByCode maps a row', async () => {
|
||||
limit.mockResolvedValueOnce([row]);
|
||||
const branch = await repository.findByCode('JKT_01');
|
||||
expect(branch?.code).toBe('JKT_01');
|
||||
});
|
||||
@@ -127,17 +153,10 @@ describe('BranchesRepository', () => {
|
||||
}),
|
||||
}))
|
||||
.mockImplementationOnce(() => ({
|
||||
from: () => ({
|
||||
$dynamic: () => ({
|
||||
where: () => ({
|
||||
orderBy: () => ({
|
||||
limit: () => ({
|
||||
offset: () => Promise.resolve([row]),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
from: () => {
|
||||
const chain = joinChain();
|
||||
return chain;
|
||||
},
|
||||
}));
|
||||
|
||||
const result = await repository.list({
|
||||
@@ -158,12 +177,18 @@ describe('BranchesRepository', () => {
|
||||
});
|
||||
expect(result.total).toBe(1);
|
||||
expect(result.data[0].code).toBe('JKT_01');
|
||||
expect(result.data[0].division).toEqual({
|
||||
id: 'div-1',
|
||||
code: 'JKT',
|
||||
name: 'Jakarta',
|
||||
});
|
||||
});
|
||||
|
||||
it('create inserts and maps unique violations', async () => {
|
||||
returning.mockResolvedValueOnce([row]);
|
||||
const created = await repository.create(createInput);
|
||||
expect(created.code).toBe('JKT_01');
|
||||
expect(created.createdByUser).toEqual({ id: 'user-1', username: 'admin' });
|
||||
|
||||
returning.mockRejectedValueOnce({ code: '23505' });
|
||||
await expect(repository.create(createInput)).rejects.toBeInstanceOf(
|
||||
@@ -209,17 +234,17 @@ describe('BranchesRepository', () => {
|
||||
'user-1',
|
||||
);
|
||||
expect(updated.id).toBe('br-1');
|
||||
expect(updated.updatedByUser).toEqual({ id: 'user-1', username: 'admin' });
|
||||
});
|
||||
|
||||
it('update throws when missing', async () => {
|
||||
limit.mockResolvedValueOnce([]);
|
||||
limit.mockImplementationOnce(() => Promise.resolve([]));
|
||||
await expect(
|
||||
repository.update('missing', { userId: 'user-1' }),
|
||||
).rejects.toBeInstanceOf(NotFoundException);
|
||||
});
|
||||
|
||||
it('update maps a row when present', async () => {
|
||||
limit.mockResolvedValueOnce([row]);
|
||||
returning.mockResolvedValueOnce([row]);
|
||||
const updated = await repository.update('br-1', {
|
||||
name: 'Jakarta Selatan',
|
||||
|
||||
Reference in New Issue
Block a user