Skip to content

Commit ec8dfea

Browse files
committed
chore: fixed tests
1 parent b1c27c7 commit ec8dfea

3 files changed

Lines changed: 208 additions & 0 deletions

File tree

admin/backend/test/resource-asset/resource-asset.controller.spec.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { describe, beforeEach, it, expect, vi } from 'vitest';
33
import { RecreationAssetController } from '@/resource-asset/resource-asset.controller';
44
import { RecreationAssetService } from '@/resource-asset/service/resource-asset.service';
55
import {
6+
BulkCreateRecreationAssetsDto,
67
CreateRecreationAssetDto,
78
CreateRecreationAssetRepairDto,
89
FindAllAssetsQueryDto,
@@ -21,6 +22,7 @@ describe('RecreationAssetController', () => {
2122

2223
let serviceMock: {
2324
createAsset: ReturnType<typeof vi.fn>;
25+
bulkCreateAssets: ReturnType<typeof vi.fn>;
2426
findAllAssets: ReturnType<typeof vi.fn>;
2527
findAssetById: ReturnType<typeof vi.fn>;
2628
findAllAssetCodes: ReturnType<typeof vi.fn>;
@@ -38,6 +40,7 @@ describe('RecreationAssetController', () => {
3840
beforeEach(async () => {
3941
serviceMock = {
4042
createAsset: vi.fn(),
43+
bulkCreateAssets: vi.fn(),
4144
findAllAssets: vi.fn(),
4245
findAssetById: vi.fn(),
4346
findAllAssetCodes: vi.fn(),
@@ -99,6 +102,37 @@ describe('RecreationAssetController', () => {
99102
});
100103
});
101104

105+
describe('bulkCreateAssets', () => {
106+
it('should delegate to service.bulkCreateAssets with the provided DTO', async () => {
107+
const dto: BulkCreateRecreationAssetsDto = {
108+
assets: [
109+
{ asset_code: 1, rec_resource_id: 'REC-1' },
110+
{ asset_code: 2, rec_resource_id: 'REC-1' },
111+
],
112+
};
113+
114+
const expectedResponse: RecreationAssetDto[] = [
115+
{
116+
asset_id: 10,
117+
rec_resource_id: 'REC-1',
118+
asset_code: 1,
119+
} as RecreationAssetDto,
120+
{
121+
asset_id: 11,
122+
rec_resource_id: 'REC-1',
123+
asset_code: 2,
124+
} as RecreationAssetDto,
125+
];
126+
127+
serviceMock.bulkCreateAssets.mockResolvedValue(expectedResponse);
128+
129+
const result = await controller.bulkCreateAssets(dto);
130+
131+
expect(serviceMock.bulkCreateAssets).toHaveBeenCalledWith(dto);
132+
expect(result).toEqual(expectedResponse);
133+
});
134+
});
135+
102136
describe('findAllAssets', () => {
103137
it('should delegate to service.findAllAssets and return an array', async () => {
104138
const query: FindAllAssetsQueryDto = {};

admin/backend/test/resource-asset/service/resource-asset.service.spec.ts

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,176 @@ describe('RecreationAssetService', () => {
421421
});
422422
});
423423

424+
describe('bulkCreateAssets', () => {
425+
it('should validate all unique asset codes then create all assets in a transaction', async () => {
426+
vi.spyOn(service, 'findAssetCodeById').mockResolvedValue({
427+
asset_code: 1,
428+
description: 'Bridge',
429+
});
430+
431+
const createdAssets = [
432+
{
433+
asset_id: 10n,
434+
parent_id: null,
435+
asset_tag: null,
436+
rec_resource_id: 'REC-1',
437+
asset_code: 1,
438+
asset_name: 'Asset A',
439+
asset_comment: null,
440+
legacy_structure_id: null,
441+
asset_length: null,
442+
asset_width: null,
443+
asset_area: null,
444+
actual_value: null,
445+
installation_date: null,
446+
},
447+
{
448+
asset_id: 11n,
449+
parent_id: null,
450+
asset_tag: null,
451+
rec_resource_id: 'REC-1',
452+
asset_code: 1,
453+
asset_name: 'Asset B',
454+
asset_comment: null,
455+
legacy_structure_id: null,
456+
asset_length: null,
457+
asset_width: null,
458+
asset_area: null,
459+
actual_value: null,
460+
installation_date: null,
461+
},
462+
];
463+
464+
prismaMock.$transaction.mockResolvedValue(createdAssets);
465+
466+
const dto = {
467+
assets: [
468+
{ asset_code: 1, rec_resource_id: 'REC-1', asset_name: 'Asset A' },
469+
{ asset_code: 1, rec_resource_id: 'REC-1', asset_name: 'Asset B' },
470+
],
471+
};
472+
473+
const result = await service.bulkCreateAssets(dto as any);
474+
475+
// Only one unique code (1), so findAssetCodeById called once
476+
expect(service.findAssetCodeById).toHaveBeenCalledTimes(1);
477+
expect(service.findAssetCodeById).toHaveBeenCalledWith(1);
478+
479+
expect(prismaMock.$transaction).toHaveBeenCalledTimes(1);
480+
481+
expect(result).toHaveLength(2);
482+
expect(result[0].asset_id).toBe(10);
483+
expect(result[1].asset_id).toBe(11);
484+
});
485+
486+
it('should call findAssetCodeById for each unique asset_code', async () => {
487+
vi.spyOn(service, 'findAssetCodeById').mockResolvedValue({
488+
asset_code: 1,
489+
description: 'Bridge',
490+
});
491+
492+
prismaMock.$transaction.mockResolvedValue([
493+
{
494+
asset_id: 20n,
495+
rec_resource_id: 'REC-2',
496+
asset_code: 2,
497+
parent_id: null,
498+
asset_tag: null,
499+
asset_name: null,
500+
asset_comment: null,
501+
legacy_structure_id: null,
502+
asset_length: null,
503+
asset_width: null,
504+
asset_area: null,
505+
actual_value: null,
506+
installation_date: null,
507+
},
508+
]);
509+
510+
const dto = {
511+
assets: [
512+
{ asset_code: 1, rec_resource_id: 'REC-2' },
513+
{ asset_code: 2, rec_resource_id: 'REC-2' },
514+
{ asset_code: 1, rec_resource_id: 'REC-2' }, // duplicate
515+
],
516+
};
517+
518+
await service.bulkCreateAssets(dto as any);
519+
520+
// 2 unique codes: 1 and 2
521+
expect(service.findAssetCodeById).toHaveBeenCalledTimes(2);
522+
});
523+
524+
it('should throw NotFoundException if any asset code is invalid', async () => {
525+
vi.spyOn(service, 'findAssetCodeById').mockRejectedValue(
526+
new NotFoundException('Asset code not found'),
527+
);
528+
529+
const dto = {
530+
assets: [{ asset_code: 999, rec_resource_id: 'REC-1' }],
531+
};
532+
533+
await expect(service.bulkCreateAssets(dto as any)).rejects.toThrow();
534+
});
535+
536+
it('should map all returned assets including null optional fields', async () => {
537+
vi.spyOn(service, 'findAssetCodeById').mockResolvedValue({
538+
asset_code: 5,
539+
description: 'Bench',
540+
});
541+
542+
prismaMock.$transaction.mockResolvedValue([
543+
{
544+
asset_id: 30n,
545+
parent_id: 5n,
546+
asset_tag: 'T-30',
547+
rec_resource_id: 'REC-3',
548+
asset_code: 5,
549+
asset_name: 'My Bench',
550+
asset_comment: 'A comment',
551+
legacy_structure_id: 'LEG-30',
552+
asset_length: 2.5,
553+
asset_width: 1.2,
554+
asset_area: 3.0,
555+
actual_value: 750,
556+
installation_date: new Date('2022-06-15T00:00:00.000Z'),
557+
},
558+
]);
559+
560+
const dto = {
561+
assets: [
562+
{
563+
asset_code: 5,
564+
rec_resource_id: 'REC-3',
565+
parent_id: 5,
566+
asset_tag: 'T-30',
567+
asset_name: 'My Bench',
568+
asset_comment: 'A comment',
569+
legacy_structure_id: 'LEG-30',
570+
asset_length: 2.5,
571+
asset_width: 1.2,
572+
asset_area: 3.0,
573+
actual_value: 750,
574+
installation_date: '2022-06-15',
575+
},
576+
],
577+
};
578+
579+
const result = await service.bulkCreateAssets(dto as any);
580+
581+
expect(result[0]).toMatchObject({
582+
asset_id: 30,
583+
parent_id: 5,
584+
asset_tag: 'T-30',
585+
rec_resource_id: 'REC-3',
586+
asset_code: 5,
587+
asset_name: 'My Bench',
588+
actual_value: 750,
589+
installation_date: '2022-06-15',
590+
});
591+
});
592+
});
593+
424594
describe('RecreationAssetService - findAllAssets', () => {
425595
// eslint-disable-next-line @typescript-eslint/no-unused-vars
426596
let prismaService: PrismaService;

admin/frontend/test/pages/rec-resource-page/components/RecResourceAssetsSection/RecResourceAssetsSection.test.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ vi.mock('@/services/hooks/recreation-resource-admin', () => ({
2828
useGetAssetsByRecResourceId: vi.fn(),
2929
useGetRecreationResourceById: vi.fn(),
3030
useGetRepairCodes: vi.fn(),
31+
useCreateBulkAssets: vi.fn().mockReturnValue({
32+
mutateAsync: vi.fn(),
33+
isPending: false,
34+
}),
3135
}));
3236

3337
const buildAsset = (overrides: Partial<Asset> = {}): Asset => ({

0 commit comments

Comments
 (0)