tests: Add unit test for obat module. fix: fix multinode implementation on docker-compose-swarm.yaml
This commit is contained in:
parent
e6fcb80d88
commit
21f2990feb
|
|
@ -1,18 +1,295 @@
|
||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
import { ObatController } from './obat.controller';
|
import { ObatController } from './obat.controller';
|
||||||
|
import { ObatService } from './obat.service';
|
||||||
|
import { AuthGuard } from '../auth/guard/auth.guard';
|
||||||
|
import { UpdateObatDto } from './dto/update-obat-dto';
|
||||||
|
import { CreateObatDto } from './dto/create-obat-dto';
|
||||||
|
import { BadRequestException } from '@nestjs/common';
|
||||||
|
import type { ActiveUserPayload } from '../auth/decorator/current-user.decorator';
|
||||||
|
|
||||||
describe('ObatController', () => {
|
describe('ObatController', () => {
|
||||||
let controller: ObatController;
|
let controller: ObatController;
|
||||||
|
let obatService: jest.Mocked<ObatService>;
|
||||||
|
|
||||||
|
const mockUser: ActiveUserPayload = {
|
||||||
|
sub: 1,
|
||||||
|
username: 'testuser',
|
||||||
|
role: 'admin' as any,
|
||||||
|
csrf: 'test-csrf-token',
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockObat = {
|
||||||
|
id: 1,
|
||||||
|
id_visit: 'VISIT001',
|
||||||
|
obat: 'Paracetamol',
|
||||||
|
jumlah_obat: 10,
|
||||||
|
aturan_pakai: '3x1',
|
||||||
|
deleted_status: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockObatService = {
|
||||||
|
getAllObat: jest.fn(),
|
||||||
|
getObatById: jest.fn(),
|
||||||
|
createObat: jest.fn(),
|
||||||
|
updateObat: jest.fn(),
|
||||||
|
getLogObatById: jest.fn(),
|
||||||
|
deleteObat: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
controllers: [ObatController],
|
controllers: [ObatController],
|
||||||
}).compile();
|
providers: [
|
||||||
|
{
|
||||||
|
provide: ObatService,
|
||||||
|
useValue: mockObatService,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.overrideGuard(AuthGuard)
|
||||||
|
.useValue({ canActivate: () => true })
|
||||||
|
.compile();
|
||||||
|
|
||||||
controller = module.get<ObatController>(ObatController);
|
controller = module.get<ObatController>(ObatController);
|
||||||
|
obatService = module.get(ObatService);
|
||||||
|
|
||||||
|
jest.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be defined', () => {
|
it('should be defined', () => {
|
||||||
expect(controller).toBeDefined();
|
expect(controller).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getAllObat', () => {
|
||||||
|
it('should return all obat with pagination', async () => {
|
||||||
|
const expectedResult = {
|
||||||
|
0: mockObat,
|
||||||
|
totalCount: 1,
|
||||||
|
};
|
||||||
|
mockObatService.getAllObat.mockResolvedValue(expectedResult);
|
||||||
|
|
||||||
|
const result = await controller.getAllObat(
|
||||||
|
10,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
'id',
|
||||||
|
'Paracetamol',
|
||||||
|
'asc',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toEqual(expectedResult);
|
||||||
|
expect(obatService.getAllObat).toHaveBeenCalledWith({
|
||||||
|
take: 10,
|
||||||
|
skip: 0,
|
||||||
|
page: 1,
|
||||||
|
orderBy: { id: 'asc' },
|
||||||
|
obat: 'Paracetamol',
|
||||||
|
order: 'asc',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle undefined orderBy parameter', async () => {
|
||||||
|
const expectedResult = { 0: mockObat, totalCount: 1 };
|
||||||
|
mockObatService.getAllObat.mockResolvedValue(expectedResult);
|
||||||
|
|
||||||
|
await controller.getAllObat(
|
||||||
|
10,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
undefined as unknown as string,
|
||||||
|
undefined as unknown as string,
|
||||||
|
undefined as unknown as 'asc' | 'desc',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(obatService.getAllObat).toHaveBeenCalledWith({
|
||||||
|
take: 10,
|
||||||
|
skip: 0,
|
||||||
|
page: 1,
|
||||||
|
orderBy: undefined,
|
||||||
|
obat: undefined,
|
||||||
|
order: undefined,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should pass order parameter when orderBy is provided', async () => {
|
||||||
|
mockObatService.getAllObat.mockResolvedValue({ totalCount: 0 });
|
||||||
|
|
||||||
|
await controller.getAllObat(
|
||||||
|
10,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
'obat',
|
||||||
|
undefined as unknown as string,
|
||||||
|
'desc',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(obatService.getAllObat).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
orderBy: { obat: 'desc' },
|
||||||
|
order: 'desc',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getObatById', () => {
|
||||||
|
it('should return obat by id', async () => {
|
||||||
|
mockObatService.getObatById.mockResolvedValue(mockObat);
|
||||||
|
|
||||||
|
const result = await controller.getObatById(1);
|
||||||
|
|
||||||
|
expect(result).toEqual(mockObat);
|
||||||
|
expect(obatService.getObatById).toHaveBeenCalledWith(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return null when obat not found', async () => {
|
||||||
|
mockObatService.getObatById.mockResolvedValue(null);
|
||||||
|
|
||||||
|
const result = await controller.getObatById(999);
|
||||||
|
|
||||||
|
expect(result).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('createObat', () => {
|
||||||
|
it('should create obat successfully', async () => {
|
||||||
|
const createDto: CreateObatDto = {
|
||||||
|
id_visit: 'VISIT001',
|
||||||
|
obat: 'Paracetamol',
|
||||||
|
jumlah_obat: 10,
|
||||||
|
aturan_pakai: '3x1',
|
||||||
|
};
|
||||||
|
const expectedResult = { id: 1, ...createDto, status: 'PENDING' };
|
||||||
|
mockObatService.createObat.mockResolvedValue(expectedResult);
|
||||||
|
|
||||||
|
const result = await controller.createObat(createDto, mockUser);
|
||||||
|
|
||||||
|
expect(result).toEqual(expectedResult);
|
||||||
|
expect(obatService.createObat).toHaveBeenCalledWith(createDto, mockUser);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw BadRequestException when visit ID not found', async () => {
|
||||||
|
const createDto: CreateObatDto = {
|
||||||
|
id_visit: 'INVALID',
|
||||||
|
obat: 'Paracetamol',
|
||||||
|
jumlah_obat: 10,
|
||||||
|
aturan_pakai: '3x1',
|
||||||
|
};
|
||||||
|
mockObatService.createObat.mockRejectedValue(
|
||||||
|
new BadRequestException('Visit ID INVALID not found'),
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(controller.createObat(createDto, mockUser)).rejects.toThrow(
|
||||||
|
BadRequestException,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('updateObatById', () => {
|
||||||
|
it('should update obat successfully', async () => {
|
||||||
|
const updateDto: UpdateObatDto = {
|
||||||
|
obat: 'Ibuprofen',
|
||||||
|
jumlah_obat: 20,
|
||||||
|
aturan_pakai: '2x1',
|
||||||
|
};
|
||||||
|
const expectedResult = { id: 1, status: 'PENDING' };
|
||||||
|
mockObatService.updateObat.mockResolvedValue(expectedResult);
|
||||||
|
|
||||||
|
const result = await controller.updateObatById(1, updateDto, mockUser);
|
||||||
|
|
||||||
|
expect(result).toEqual(expectedResult);
|
||||||
|
expect(obatService.updateObat).toHaveBeenCalledWith(
|
||||||
|
1,
|
||||||
|
updateDto,
|
||||||
|
mockUser,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw BadRequestException when obat not found', async () => {
|
||||||
|
const updateDto: UpdateObatDto = {
|
||||||
|
obat: 'Ibuprofen',
|
||||||
|
jumlah_obat: 20,
|
||||||
|
aturan_pakai: '2x1',
|
||||||
|
};
|
||||||
|
mockObatService.updateObat.mockRejectedValue(
|
||||||
|
new BadRequestException('Medicine with ID 999 not found'),
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
controller.updateObatById(999, updateDto, mockUser),
|
||||||
|
).rejects.toThrow(BadRequestException);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw BadRequestException when no changes detected', async () => {
|
||||||
|
const updateDto: UpdateObatDto = {
|
||||||
|
obat: 'Paracetamol',
|
||||||
|
jumlah_obat: 10,
|
||||||
|
aturan_pakai: '3x1',
|
||||||
|
};
|
||||||
|
mockObatService.updateObat.mockRejectedValue(
|
||||||
|
new BadRequestException('No changes in medicine data detected'),
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
controller.updateObatById(1, updateDto, mockUser),
|
||||||
|
).rejects.toThrow('No changes in medicine data detected');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getObatLogs', () => {
|
||||||
|
it('should return obat logs', async () => {
|
||||||
|
const expectedLogs = {
|
||||||
|
logs: [
|
||||||
|
{
|
||||||
|
id: 'OBAT_1',
|
||||||
|
event: 'obat_created',
|
||||||
|
status: 'ORIGINAL',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
isTampered: false,
|
||||||
|
currentDataHash: 'abc123',
|
||||||
|
};
|
||||||
|
mockObatService.getLogObatById.mockResolvedValue(expectedLogs);
|
||||||
|
|
||||||
|
const result = await controller.getObatLogs('1');
|
||||||
|
|
||||||
|
expect(result).toEqual(expectedLogs);
|
||||||
|
expect(obatService.getLogObatById).toHaveBeenCalledWith('1');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle tampered data detection', async () => {
|
||||||
|
const expectedLogs = {
|
||||||
|
logs: [],
|
||||||
|
isTampered: true,
|
||||||
|
currentDataHash: 'abc123',
|
||||||
|
};
|
||||||
|
mockObatService.getLogObatById.mockResolvedValue(expectedLogs);
|
||||||
|
|
||||||
|
const result = await controller.getObatLogs('1');
|
||||||
|
|
||||||
|
expect(result.isTampered).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('deleteObatById', () => {
|
||||||
|
it('should delete obat successfully', async () => {
|
||||||
|
const expectedResult = { id: 1, status: 'PENDING', action: 'DELETE' };
|
||||||
|
mockObatService.deleteObat.mockResolvedValue(expectedResult);
|
||||||
|
|
||||||
|
const result = await controller.deleteObatById(1, mockUser);
|
||||||
|
|
||||||
|
expect(result).toEqual(expectedResult);
|
||||||
|
expect(obatService.deleteObat).toHaveBeenCalledWith(1, mockUser);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw BadRequestException when obat not found', async () => {
|
||||||
|
mockObatService.deleteObat.mockRejectedValue(
|
||||||
|
new BadRequestException('Obat with id 999 not found'),
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(controller.deleteObatById(999, mockUser)).rejects.toThrow(
|
||||||
|
BadRequestException,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -7,14 +7,6 @@ import { CreateObatDto } from './dto/create-obat-dto';
|
||||||
import { UpdateObatDto } from './dto/update-obat-dto';
|
import { UpdateObatDto } from './dto/update-obat-dto';
|
||||||
import { ObatService } from './obat.service';
|
import { ObatService } from './obat.service';
|
||||||
|
|
||||||
type PrismaDelegate<T> = {
|
|
||||||
findMany: jest.Mock;
|
|
||||||
findUnique: jest.Mock;
|
|
||||||
count: jest.Mock;
|
|
||||||
create: jest.Mock;
|
|
||||||
update: jest.Mock;
|
|
||||||
};
|
|
||||||
|
|
||||||
const createPrismaMock = () => ({
|
const createPrismaMock = () => ({
|
||||||
pemberian_obat: {
|
pemberian_obat: {
|
||||||
findMany: jest.fn(),
|
findMany: jest.fn(),
|
||||||
|
|
@ -22,10 +14,14 @@ const createPrismaMock = () => ({
|
||||||
count: jest.fn(),
|
count: jest.fn(),
|
||||||
create: jest.fn(),
|
create: jest.fn(),
|
||||||
update: jest.fn(),
|
update: jest.fn(),
|
||||||
} as PrismaDelegate<any>,
|
},
|
||||||
rekam_medis: {
|
rekam_medis: {
|
||||||
findUnique: jest.fn(),
|
findUnique: jest.fn(),
|
||||||
},
|
},
|
||||||
|
validation_queue: {
|
||||||
|
create: jest.fn(),
|
||||||
|
},
|
||||||
|
$transaction: jest.fn(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const createLogServiceMock = () => ({
|
const createLogServiceMock = () => ({
|
||||||
|
|
@ -60,161 +56,234 @@ describe('ObatService', () => {
|
||||||
service = module.get<ObatService>(ObatService);
|
service = module.get<ObatService>(ObatService);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
it('should be defined', () => {
|
it('should be defined', () => {
|
||||||
expect(service).toBeDefined();
|
expect(service).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('createHashingPayload', () => {
|
||||||
|
it('should create consistent hash for same data', () => {
|
||||||
|
const data = {
|
||||||
|
obat: 'Paracetamol',
|
||||||
|
jumlah_obat: 10,
|
||||||
|
aturan_pakai: '3x1',
|
||||||
|
};
|
||||||
|
const hash1 = service.createHashingPayload(data);
|
||||||
|
const hash2 = service.createHashingPayload(data);
|
||||||
|
|
||||||
|
expect(hash1).toBe(hash2);
|
||||||
|
expect(typeof hash1).toBe('string');
|
||||||
|
expect(hash1.length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create different hash for different data', () => {
|
||||||
|
const data1 = {
|
||||||
|
obat: 'Paracetamol',
|
||||||
|
jumlah_obat: 10,
|
||||||
|
aturan_pakai: '3x1',
|
||||||
|
};
|
||||||
|
const data2 = { obat: 'Ibuprofen', jumlah_obat: 10, aturan_pakai: '3x1' };
|
||||||
|
|
||||||
|
expect(service.createHashingPayload(data1)).not.toBe(
|
||||||
|
service.createHashingPayload(data2),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('determineStatus', () => {
|
||||||
|
it('should return ORIGINAL for last item with obat_created event', () => {
|
||||||
|
const rawLog = {
|
||||||
|
value: {
|
||||||
|
event: 'obat_created',
|
||||||
|
payload: 'hash123',
|
||||||
|
timestamp: '2024-01-01',
|
||||||
|
user_id: 1,
|
||||||
|
},
|
||||||
|
txId: 'tx123',
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = service.determineStatus(rawLog, 0, 1);
|
||||||
|
|
||||||
|
expect(result.status).toBe('ORIGINAL');
|
||||||
|
expect(result.txId).toBe('tx123');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return UPDATED for non-last items', () => {
|
||||||
|
const rawLog = {
|
||||||
|
value: {
|
||||||
|
event: 'obat_updated',
|
||||||
|
payload: 'hash123',
|
||||||
|
timestamp: '2024-01-01',
|
||||||
|
user_id: 1,
|
||||||
|
},
|
||||||
|
txId: 'tx123',
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = service.determineStatus(rawLog, 0, 2);
|
||||||
|
|
||||||
|
expect(result.status).toBe('UPDATED');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return UPDATED for last item with non-created event', () => {
|
||||||
|
const rawLog = {
|
||||||
|
value: {
|
||||||
|
event: 'obat_updated',
|
||||||
|
payload: 'hash123',
|
||||||
|
timestamp: '2024-01-01',
|
||||||
|
user_id: 1,
|
||||||
|
},
|
||||||
|
txId: 'tx123',
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = service.determineStatus(rawLog, 0, 1);
|
||||||
|
|
||||||
|
expect(result.status).toBe('UPDATED');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('getAllObat', () => {
|
describe('getAllObat', () => {
|
||||||
it('returns paginated data and total count', async () => {
|
const mockObatList = [
|
||||||
prisma.pemberian_obat.findMany.mockResolvedValueOnce([
|
{ id: 1, obat: 'Paracetamol', deleted_status: null },
|
||||||
{ id: 1, obat: 'Paracetamol' },
|
{ id: 2, obat: 'Ibuprofen', deleted_status: null },
|
||||||
]);
|
];
|
||||||
prisma.pemberian_obat.count.mockResolvedValueOnce(10);
|
|
||||||
|
it('should return paginated data with total count', async () => {
|
||||||
|
prisma.pemberian_obat.findMany.mockResolvedValue(mockObatList);
|
||||||
|
prisma.pemberian_obat.count.mockResolvedValue(10);
|
||||||
|
|
||||||
const result = await service.getAllObat({
|
const result = await service.getAllObat({
|
||||||
take: 10,
|
take: 10,
|
||||||
page: 1,
|
page: 1,
|
||||||
orderBy: { id: 'asc' },
|
|
||||||
order: 'asc',
|
order: 'asc',
|
||||||
obat: 'Para',
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(prisma.pemberian_obat.findMany).toHaveBeenCalledWith({
|
expect(prisma.pemberian_obat.findMany).toHaveBeenCalledWith({
|
||||||
skip: 0,
|
skip: 0,
|
||||||
take: 10,
|
take: 10,
|
||||||
where: {
|
where: {
|
||||||
obat: { contains: 'Para' },
|
obat: undefined,
|
||||||
|
OR: [
|
||||||
|
{ deleted_status: null },
|
||||||
|
{ deleted_status: 'DELETE_VALIDATION' },
|
||||||
|
{ deleted_status: { not: 'DELETED' } },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
orderBy: { id: 'asc' },
|
orderBy: { id: 'asc' },
|
||||||
});
|
});
|
||||||
expect(prisma.pemberian_obat.count).toHaveBeenCalledWith({
|
expect(result.totalCount).toBe(10);
|
||||||
where: {
|
|
||||||
obat: { contains: 'Para' },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
expect(result).toEqual({
|
|
||||||
0: { id: 1, obat: 'Paracetamol' },
|
|
||||||
totalCount: 10,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
describe('createObat', () => {
|
it('should filter by obat name', async () => {
|
||||||
const payload: CreateObatDto = {
|
prisma.pemberian_obat.findMany.mockResolvedValue([mockObatList[0]]);
|
||||||
id_visit: 'VISIT-1',
|
prisma.pemberian_obat.count.mockResolvedValue(1);
|
||||||
obat: 'Amoxicillin',
|
|
||||||
jumlah_obat: 2,
|
|
||||||
aturan_pakai: '3x1',
|
|
||||||
};
|
|
||||||
|
|
||||||
it('throws when visit not found', async () => {
|
await service.getAllObat({ obat: 'Para' });
|
||||||
prisma.rekam_medis.findUnique.mockResolvedValueOnce(null);
|
|
||||||
|
|
||||||
await expect(service.createObat(payload, mockUser)).rejects.toThrow(
|
expect(prisma.pemberian_obat.findMany).toHaveBeenCalledWith(
|
||||||
BadRequestException,
|
expect.objectContaining({
|
||||||
|
where: expect.objectContaining({
|
||||||
|
obat: { contains: 'Para' },
|
||||||
|
}),
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
expect(prisma.pemberian_obat.create).not.toHaveBeenCalled();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('creates obat and stores log', async () => {
|
it('should handle skip parameter', async () => {
|
||||||
prisma.rekam_medis.findUnique.mockResolvedValueOnce({
|
prisma.pemberian_obat.findMany.mockResolvedValue([]);
|
||||||
id_visit: 'VISIT-1',
|
prisma.pemberian_obat.count.mockResolvedValue(0);
|
||||||
});
|
|
||||||
prisma.pemberian_obat.create.mockResolvedValueOnce({
|
|
||||||
id: 42,
|
|
||||||
...payload,
|
|
||||||
});
|
|
||||||
logService.storeLog.mockResolvedValueOnce({ txId: 'abc' });
|
|
||||||
|
|
||||||
const result = await service.createObat(payload, mockUser);
|
await service.getAllObat({ skip: 5 });
|
||||||
|
|
||||||
expect(prisma.pemberian_obat.create).toHaveBeenCalledWith({
|
expect(prisma.pemberian_obat.findMany).toHaveBeenCalledWith(
|
||||||
data: {
|
expect.objectContaining({
|
||||||
id_visit: 'VISIT-1',
|
skip: 5,
|
||||||
obat: 'Amoxicillin',
|
}),
|
||||||
jumlah_obat: 2,
|
);
|
||||||
aturan_pakai: '3x1',
|
});
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(logService.storeLog).toHaveBeenCalledWith({
|
it('should calculate skip from page when skip not provided', async () => {
|
||||||
id: 'OBAT_42',
|
prisma.pemberian_obat.findMany.mockResolvedValue([]);
|
||||||
event: 'obat_created',
|
prisma.pemberian_obat.count.mockResolvedValue(0);
|
||||||
user_id: mockUser.sub,
|
|
||||||
payload: expect.any(String),
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(result).toEqual({
|
await service.getAllObat({ take: 10, page: 3 });
|
||||||
id: 42,
|
|
||||||
id_visit: 'VISIT-1',
|
expect(prisma.pemberian_obat.findMany).toHaveBeenCalledWith(
|
||||||
obat: 'Amoxicillin',
|
expect.objectContaining({
|
||||||
jumlah_obat: 2,
|
skip: 20,
|
||||||
aturan_pakai: '3x1',
|
take: 10,
|
||||||
txId: 'abc',
|
}),
|
||||||
});
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use default take of 10 when not provided', async () => {
|
||||||
|
prisma.pemberian_obat.findMany.mockResolvedValue([]);
|
||||||
|
prisma.pemberian_obat.count.mockResolvedValue(0);
|
||||||
|
|
||||||
|
await service.getAllObat({});
|
||||||
|
|
||||||
|
expect(prisma.pemberian_obat.findMany).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
take: 10,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle custom orderBy', async () => {
|
||||||
|
prisma.pemberian_obat.findMany.mockResolvedValue([]);
|
||||||
|
prisma.pemberian_obat.count.mockResolvedValue(0);
|
||||||
|
|
||||||
|
await service.getAllObat({ orderBy: { obat: 'desc' }, order: 'desc' });
|
||||||
|
|
||||||
|
expect(prisma.pemberian_obat.findMany).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
orderBy: { obat: 'desc' },
|
||||||
|
}),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('updateObatById', () => {
|
describe('getObatById', () => {
|
||||||
const updatePayload: UpdateObatDto = {
|
it('should return obat by id', async () => {
|
||||||
id_visit: 'VISIT-1',
|
const mockObat = { id: 1, obat: 'Paracetamol' };
|
||||||
obat: 'Ibuprofen',
|
prisma.pemberian_obat.findUnique.mockResolvedValue(mockObat);
|
||||||
jumlah_obat: 1,
|
|
||||||
aturan_pakai: '2x1',
|
|
||||||
};
|
|
||||||
|
|
||||||
it('updates obat and stores log', async () => {
|
const result = await service.getObatById(1);
|
||||||
prisma.pemberian_obat.update.mockResolvedValueOnce({
|
|
||||||
id: 99,
|
expect(result).toEqual(mockObat);
|
||||||
...updatePayload,
|
expect(prisma.pemberian_obat.findUnique).toHaveBeenCalledWith({
|
||||||
id_visit: 'VISIT-1',
|
where: { id: 1 },
|
||||||
});
|
});
|
||||||
logService.storeLog.mockResolvedValueOnce({ txId: 'updated' });
|
});
|
||||||
|
|
||||||
const result = await service.updateObat(99, updatePayload, mockUser);
|
it('should return null when obat not found', async () => {
|
||||||
|
prisma.pemberian_obat.findUnique.mockResolvedValue(null);
|
||||||
|
|
||||||
expect(prisma.pemberian_obat.update).toHaveBeenCalledWith({
|
const result = await service.getObatById(999);
|
||||||
where: { id: 99 },
|
|
||||||
data: {
|
|
||||||
obat: 'Ibuprofen',
|
|
||||||
jumlah_obat: 1,
|
|
||||||
aturan_pakai: '2x1',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(logService.storeLog).toHaveBeenCalledWith({
|
expect(result).toBeNull();
|
||||||
id: 'OBAT_99',
|
|
||||||
event: 'obat_updated',
|
|
||||||
user_id: mockUser.sub,
|
|
||||||
payload: expect.any(String),
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(result).toEqual({
|
|
||||||
id: 99,
|
|
||||||
id_visit: 'VISIT-1',
|
|
||||||
obat: 'Ibuprofen',
|
|
||||||
jumlah_obat: 1,
|
|
||||||
aturan_pakai: '2x1',
|
|
||||||
txId: 'updated',
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getLogObatById', () => {
|
describe('getLogObatById', () => {
|
||||||
it('returns processed logs and tamper status', async () => {
|
const mockObat = {
|
||||||
prisma.pemberian_obat.findUnique.mockResolvedValueOnce({
|
id: 5,
|
||||||
id: 5,
|
obat: 'Paracetamol',
|
||||||
obat: 'Paracetamol',
|
jumlah_obat: 1,
|
||||||
jumlah_obat: 1,
|
aturan_pakai: '3x1',
|
||||||
aturan_pakai: '3x1',
|
};
|
||||||
});
|
|
||||||
|
it('should return logs with tamper status false when hashes match', async () => {
|
||||||
|
prisma.pemberian_obat.findUnique.mockResolvedValue(mockObat);
|
||||||
const expectedHash = service.createHashingPayload({
|
const expectedHash = service.createHashingPayload({
|
||||||
obat: 'Paracetamol',
|
obat: mockObat.obat,
|
||||||
jumlah_obat: 1,
|
jumlah_obat: mockObat.jumlah_obat,
|
||||||
aturan_pakai: '3x1',
|
aturan_pakai: mockObat.aturan_pakai,
|
||||||
});
|
});
|
||||||
|
|
||||||
logService.getLogById.mockResolvedValueOnce([
|
logService.getLogById.mockResolvedValue([
|
||||||
{
|
{
|
||||||
value: {
|
value: {
|
||||||
event: 'obat_created',
|
event: 'obat_created',
|
||||||
|
|
@ -229,19 +298,435 @@ describe('ObatService', () => {
|
||||||
const result = await service.getLogObatById('5');
|
const result = await service.getLogObatById('5');
|
||||||
|
|
||||||
expect(logService.getLogById).toHaveBeenCalledWith('OBAT_5');
|
expect(logService.getLogById).toHaveBeenCalledWith('OBAT_5');
|
||||||
expect(result).toEqual({
|
expect(result.isTampered).toBe(false);
|
||||||
logs: [
|
expect(result.currentDataHash).toBe(expectedHash);
|
||||||
{
|
});
|
||||||
|
|
||||||
|
it('should detect tampered data when hashes do not match', async () => {
|
||||||
|
prisma.pemberian_obat.findUnique.mockResolvedValue(mockObat);
|
||||||
|
|
||||||
|
logService.getLogById.mockResolvedValue([
|
||||||
|
{
|
||||||
|
value: {
|
||||||
event: 'obat_created',
|
event: 'obat_created',
|
||||||
payload: expectedHash,
|
payload: 'different_hash',
|
||||||
timestamp: '2024-01-01T00:00:00Z',
|
timestamp: '2024-01-01T00:00:00Z',
|
||||||
user_id: 1,
|
user_id: 1,
|
||||||
txId: 'abc',
|
|
||||||
status: 'ORIGINAL',
|
|
||||||
},
|
},
|
||||||
],
|
txId: 'abc',
|
||||||
isTampered: false,
|
},
|
||||||
currentDataHash: expectedHash,
|
]);
|
||||||
|
|
||||||
|
const result = await service.getLogObatById('5');
|
||||||
|
|
||||||
|
expect(result.isTampered).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error when obat not found', async () => {
|
||||||
|
prisma.pemberian_obat.findUnique.mockResolvedValue(null);
|
||||||
|
logService.getLogById.mockResolvedValue([{ value: { payload: 'hash' } }]);
|
||||||
|
|
||||||
|
await expect(service.getLogObatById('999')).rejects.toThrow(
|
||||||
|
'Obat with id 999 not found',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// BUG TEST: This test exposes the bug where empty logs array causes crash
|
||||||
|
it('should handle empty logs array gracefully', async () => {
|
||||||
|
prisma.pemberian_obat.findUnique.mockResolvedValue(mockObat);
|
||||||
|
logService.getLogById.mockResolvedValue([]);
|
||||||
|
|
||||||
|
// This will fail because the code tries to access rawLogs[0] without checking
|
||||||
|
await expect(service.getLogObatById('5')).rejects.toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isIdVisitExists', () => {
|
||||||
|
it('should return true when visit exists', async () => {
|
||||||
|
prisma.rekam_medis.findUnique.mockResolvedValue({ id_visit: 'VISIT001' });
|
||||||
|
|
||||||
|
const result = await service.isIdVisitExists('VISIT001');
|
||||||
|
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false when visit does not exist', async () => {
|
||||||
|
prisma.rekam_medis.findUnique.mockResolvedValue(null);
|
||||||
|
|
||||||
|
const result = await service.isIdVisitExists('INVALID');
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('createObat', () => {
|
||||||
|
const createDto: CreateObatDto = {
|
||||||
|
id_visit: 'VISIT001',
|
||||||
|
obat: 'Paracetamol',
|
||||||
|
jumlah_obat: 10,
|
||||||
|
aturan_pakai: '3x1',
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should create validation queue entry for new obat', async () => {
|
||||||
|
prisma.rekam_medis.findUnique.mockResolvedValue({ id_visit: 'VISIT001' });
|
||||||
|
prisma.validation_queue.create.mockResolvedValue({
|
||||||
|
id: 1,
|
||||||
|
table_name: 'pemberian_obat',
|
||||||
|
action: 'CREATE',
|
||||||
|
status: 'PENDING',
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await service.createObat(createDto, mockUser);
|
||||||
|
|
||||||
|
expect(prisma.validation_queue.create).toHaveBeenCalledWith({
|
||||||
|
data: {
|
||||||
|
table_name: 'pemberian_obat',
|
||||||
|
action: 'CREATE',
|
||||||
|
dataPayload: createDto,
|
||||||
|
status: 'PENDING',
|
||||||
|
user_id_request: mockUser.sub,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(result.status).toBe('PENDING');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw BadRequestException when visit ID not found', async () => {
|
||||||
|
prisma.rekam_medis.findUnique.mockResolvedValue(null);
|
||||||
|
|
||||||
|
await expect(service.createObat(createDto, mockUser)).rejects.toThrow(
|
||||||
|
BadRequestException,
|
||||||
|
);
|
||||||
|
await expect(service.createObat(createDto, mockUser)).rejects.toThrow(
|
||||||
|
'Visit ID VISIT001 not found',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should propagate database errors', async () => {
|
||||||
|
prisma.rekam_medis.findUnique.mockResolvedValue({ id_visit: 'VISIT001' });
|
||||||
|
prisma.validation_queue.create.mockRejectedValue(
|
||||||
|
new Error('Database error'),
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(service.createObat(createDto, mockUser)).rejects.toThrow(
|
||||||
|
'Database error',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('createObatToDBAndBlockchain', () => {
|
||||||
|
const createDto: CreateObatDto = {
|
||||||
|
id_visit: 'VISIT001',
|
||||||
|
obat: 'Paracetamol',
|
||||||
|
jumlah_obat: 10,
|
||||||
|
aturan_pakai: '3x1',
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should create obat and store log in transaction', async () => {
|
||||||
|
prisma.rekam_medis.findUnique.mockResolvedValue({ id_visit: 'VISIT001' });
|
||||||
|
|
||||||
|
const mockTx = {
|
||||||
|
pemberian_obat: {
|
||||||
|
create: jest.fn().mockResolvedValue({ id: 1, ...createDto }),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
prisma.$transaction.mockImplementation(async (callback) =>
|
||||||
|
callback(mockTx),
|
||||||
|
);
|
||||||
|
logService.storeLog.mockResolvedValue({ txId: 'blockchain_tx_123' });
|
||||||
|
|
||||||
|
const result = await service.createObatToDBAndBlockchain(createDto, 1);
|
||||||
|
|
||||||
|
expect(mockTx.pemberian_obat.create).toHaveBeenCalledWith({
|
||||||
|
data: createDto,
|
||||||
|
});
|
||||||
|
expect(logService.storeLog).toHaveBeenCalledWith({
|
||||||
|
id: 'OBAT_1',
|
||||||
|
event: 'obat_created',
|
||||||
|
user_id: '1',
|
||||||
|
payload: expect.any(String),
|
||||||
|
});
|
||||||
|
expect(result.txId).toBe('blockchain_tx_123');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw when visit ID not found', async () => {
|
||||||
|
prisma.rekam_medis.findUnique.mockResolvedValue(null);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
service.createObatToDBAndBlockchain(createDto, 1),
|
||||||
|
).rejects.toThrow(BadRequestException);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('updateObat', () => {
|
||||||
|
const existingObat = {
|
||||||
|
id: 1,
|
||||||
|
obat: 'Paracetamol',
|
||||||
|
jumlah_obat: 10,
|
||||||
|
aturan_pakai: '3x1',
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateDto: UpdateObatDto = {
|
||||||
|
obat: 'Ibuprofen',
|
||||||
|
jumlah_obat: 20,
|
||||||
|
aturan_pakai: '2x1',
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should create validation queue entry for update', async () => {
|
||||||
|
prisma.pemberian_obat.findUnique.mockResolvedValue(existingObat);
|
||||||
|
prisma.validation_queue.create.mockResolvedValue({
|
||||||
|
id: 1,
|
||||||
|
action: 'UPDATE',
|
||||||
|
status: 'PENDING',
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await service.updateObat(1, updateDto, mockUser);
|
||||||
|
|
||||||
|
expect(prisma.validation_queue.create).toHaveBeenCalledWith({
|
||||||
|
data: {
|
||||||
|
table_name: 'pemberian_obat',
|
||||||
|
action: 'UPDATE',
|
||||||
|
dataPayload: updateDto,
|
||||||
|
record_id: '1',
|
||||||
|
user_id_request: mockUser.sub,
|
||||||
|
status: 'PENDING',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(result.status).toBe('PENDING');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw when ID is invalid (NaN)', async () => {
|
||||||
|
await expect(
|
||||||
|
service.updateObat(NaN, updateDto, mockUser),
|
||||||
|
).rejects.toThrow('Medicine ID not valid');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw when obat not found', async () => {
|
||||||
|
prisma.pemberian_obat.findUnique.mockResolvedValue(null);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
service.updateObat(999, updateDto, mockUser),
|
||||||
|
).rejects.toThrow('Medicine with ID 999 not found');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw when no changes detected', async () => {
|
||||||
|
prisma.pemberian_obat.findUnique.mockResolvedValue(existingObat);
|
||||||
|
|
||||||
|
const noChangeDto: UpdateObatDto = {
|
||||||
|
obat: 'Paracetamol',
|
||||||
|
jumlah_obat: 10,
|
||||||
|
aturan_pakai: '3x1',
|
||||||
|
};
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
service.updateObat(1, noChangeDto, mockUser),
|
||||||
|
).rejects.toThrow('No changes in medicine data detected');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should detect change in obat field only', async () => {
|
||||||
|
prisma.pemberian_obat.findUnique.mockResolvedValue(existingObat);
|
||||||
|
prisma.validation_queue.create.mockResolvedValue({ id: 1 });
|
||||||
|
|
||||||
|
const partialChangeDto: UpdateObatDto = {
|
||||||
|
obat: 'Different',
|
||||||
|
jumlah_obat: 10,
|
||||||
|
aturan_pakai: '3x1',
|
||||||
|
};
|
||||||
|
|
||||||
|
await service.updateObat(1, partialChangeDto, mockUser);
|
||||||
|
|
||||||
|
expect(prisma.validation_queue.create).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should detect change in jumlah_obat field only', async () => {
|
||||||
|
prisma.pemberian_obat.findUnique.mockResolvedValue(existingObat);
|
||||||
|
prisma.validation_queue.create.mockResolvedValue({ id: 1 });
|
||||||
|
|
||||||
|
const partialChangeDto: UpdateObatDto = {
|
||||||
|
obat: 'Paracetamol',
|
||||||
|
jumlah_obat: 99,
|
||||||
|
aturan_pakai: '3x1',
|
||||||
|
};
|
||||||
|
|
||||||
|
await service.updateObat(1, partialChangeDto, mockUser);
|
||||||
|
|
||||||
|
expect(prisma.validation_queue.create).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('updateObatToDBAndBlockchain', () => {
|
||||||
|
const updateDto: UpdateObatDto = {
|
||||||
|
obat: 'Ibuprofen',
|
||||||
|
jumlah_obat: 20,
|
||||||
|
aturan_pakai: '2x1',
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should update obat and store log in transaction', async () => {
|
||||||
|
prisma.pemberian_obat.findUnique.mockResolvedValue({ id: 1 });
|
||||||
|
|
||||||
|
const mockTx = {
|
||||||
|
pemberian_obat: {
|
||||||
|
update: jest.fn().mockResolvedValue({ id: 1, ...updateDto }),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
prisma.$transaction.mockImplementation(async (callback) =>
|
||||||
|
callback(mockTx),
|
||||||
|
);
|
||||||
|
logService.storeLog.mockResolvedValue({ txId: 'blockchain_tx_456' });
|
||||||
|
|
||||||
|
const result = await service.updateObatToDBAndBlockchain(1, updateDto, 1);
|
||||||
|
|
||||||
|
expect(mockTx.pemberian_obat.update).toHaveBeenCalledWith({
|
||||||
|
where: { id: 1 },
|
||||||
|
data: updateDto,
|
||||||
|
});
|
||||||
|
expect(logService.storeLog).toHaveBeenCalledWith({
|
||||||
|
id: 'OBAT_1',
|
||||||
|
event: 'obat_updated',
|
||||||
|
user_id: '1',
|
||||||
|
payload: expect.any(String),
|
||||||
|
});
|
||||||
|
expect(result.txId).toBe('blockchain_tx_456');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw when ID is invalid', async () => {
|
||||||
|
await expect(
|
||||||
|
service.updateObatToDBAndBlockchain(NaN, updateDto, 1),
|
||||||
|
).rejects.toThrow('ID medicine not valid');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw when obat not found', async () => {
|
||||||
|
prisma.pemberian_obat.findUnique.mockResolvedValue(null);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
service.updateObatToDBAndBlockchain(999, updateDto, 1),
|
||||||
|
).rejects.toThrow('Medicine with id 999 not found');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('deleteObat', () => {
|
||||||
|
const existingObat = {
|
||||||
|
id: 1,
|
||||||
|
obat: 'Paracetamol',
|
||||||
|
jumlah_obat: 10,
|
||||||
|
aturan_pakai: '3x1',
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should create validation queue and mark as DELETE_VALIDATION', async () => {
|
||||||
|
prisma.pemberian_obat.findUnique.mockResolvedValue(existingObat);
|
||||||
|
|
||||||
|
const mockTx = {
|
||||||
|
pemberian_obat: {
|
||||||
|
update: jest.fn().mockResolvedValue({
|
||||||
|
...existingObat,
|
||||||
|
deleted_status: 'DELETE_VALIDATION',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
prisma.$transaction.mockImplementation(async (callback) =>
|
||||||
|
callback(mockTx),
|
||||||
|
);
|
||||||
|
prisma.validation_queue.create.mockResolvedValue({
|
||||||
|
id: 1,
|
||||||
|
action: 'DELETE',
|
||||||
|
status: 'PENDING',
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await service.deleteObat(1, mockUser);
|
||||||
|
|
||||||
|
expect(prisma.validation_queue.create).toHaveBeenCalledWith({
|
||||||
|
data: {
|
||||||
|
table_name: 'pemberian_obat',
|
||||||
|
action: 'DELETE',
|
||||||
|
dataPayload: existingObat,
|
||||||
|
record_id: '1',
|
||||||
|
user_id_request: mockUser.sub,
|
||||||
|
status: 'PENDING',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(mockTx.pemberian_obat.update).toHaveBeenCalledWith({
|
||||||
|
where: { id: 1 },
|
||||||
|
data: { deleted_status: 'DELETE_VALIDATION' },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw when obat not found', async () => {
|
||||||
|
prisma.pemberian_obat.findUnique.mockResolvedValue(null);
|
||||||
|
|
||||||
|
await expect(service.deleteObat(999, mockUser)).rejects.toThrow(
|
||||||
|
'Obat with id 999 not found',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('deleteObatFromDBAndBlockchain', () => {
|
||||||
|
const existingObat = {
|
||||||
|
id: 1,
|
||||||
|
obat: 'Paracetamol',
|
||||||
|
jumlah_obat: 10,
|
||||||
|
aturan_pakai: '3x1',
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should mark as deleted and store log', async () => {
|
||||||
|
prisma.pemberian_obat.findUnique.mockResolvedValue(existingObat);
|
||||||
|
|
||||||
|
const mockTx = {
|
||||||
|
pemberian_obat: {
|
||||||
|
update: jest.fn().mockResolvedValue({
|
||||||
|
...existingObat,
|
||||||
|
deleted_status: 'DELETED',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
prisma.$transaction.mockImplementation(async (callback) =>
|
||||||
|
callback(mockTx),
|
||||||
|
);
|
||||||
|
logService.storeLog.mockResolvedValue({ txId: 'blockchain_delete_tx' });
|
||||||
|
|
||||||
|
const result = await service.deleteObatFromDBAndBlockchain(1, 1);
|
||||||
|
|
||||||
|
expect(mockTx.pemberian_obat.update).toHaveBeenCalledWith({
|
||||||
|
where: { id: 1 },
|
||||||
|
data: { deleted_status: 'DELETED' },
|
||||||
|
});
|
||||||
|
expect(logService.storeLog).toHaveBeenCalledWith({
|
||||||
|
id: 'OBAT_1',
|
||||||
|
event: 'obat_deleted',
|
||||||
|
user_id: '1',
|
||||||
|
payload: expect.any(String),
|
||||||
|
});
|
||||||
|
expect(result.txId).toBe('blockchain_delete_tx');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw when ID is invalid', async () => {
|
||||||
|
await expect(
|
||||||
|
service.deleteObatFromDBAndBlockchain(NaN, 1),
|
||||||
|
).rejects.toThrow('Medicine ID not valid');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw when obat not found', async () => {
|
||||||
|
prisma.pemberian_obat.findUnique.mockResolvedValue(null);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
service.deleteObatFromDBAndBlockchain(999, 1),
|
||||||
|
).rejects.toThrow('Medicine with ID 999 not found');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('countObat', () => {
|
||||||
|
it('should return count excluding deleted records', async () => {
|
||||||
|
prisma.pemberian_obat.count.mockResolvedValue(42);
|
||||||
|
|
||||||
|
const result = await service.countObat();
|
||||||
|
|
||||||
|
expect(result).toBe(42);
|
||||||
|
expect(prisma.pemberian_obat.count).toHaveBeenCalledWith({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ deleted_status: null },
|
||||||
|
{ deleted_status: 'DELETE_VALIDATION' },
|
||||||
|
{ deleted_status: { not: 'DELETED' } },
|
||||||
|
],
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,20 @@ export class ObatService {
|
||||||
throw new Error(`Obat with id ${id} not found`);
|
throw new Error(`Obat with id ${id} not found`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!rawLogs || rawLogs.length === 0) {
|
||||||
|
const currentDataHash = this.createHashingPayload({
|
||||||
|
obat: currentData.obat,
|
||||||
|
jumlah_obat: currentData.jumlah_obat,
|
||||||
|
aturan_pakai: currentData.aturan_pakai,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
logs: [],
|
||||||
|
isTampered: true,
|
||||||
|
currentDataHash: currentDataHash,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const currentDataHash = this.createHashingPayload({
|
const currentDataHash = this.createHashingPayload({
|
||||||
obat: currentData.obat,
|
obat: currentData.obat,
|
||||||
jumlah_obat: currentData.jumlah_obat,
|
jumlah_obat: currentData.jumlah_obat,
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,9 @@ services:
|
||||||
- /home/labai1/josafat/hospital-log/backend/blockchain/chaincode:/opt/gopath/src/github.com/hyperledger/fabric/peer/chaincode
|
- /home/labai1/josafat/hospital-log/backend/blockchain/chaincode:/opt/gopath/src/github.com/hyperledger/fabric/peer/chaincode
|
||||||
- /home/labai1/josafat/hospital-log/backend/blockchain/network/organizations:/opt/gopath/src/github.com/hyperledger/fabric/peer/organizations
|
- /home/labai1/josafat/hospital-log/backend/blockchain/network/organizations:/opt/gopath/src/github.com/hyperledger/fabric/peer/organizations
|
||||||
- /home/labai1/josafat/hospital-log/backend/blockchain/network/channel-artifacts:/opt/gopath/src/github.com/hyperledger/fabric/peer/channel-artifacts
|
- /home/labai1/josafat/hospital-log/backend/blockchain/network/channel-artifacts:/opt/gopath/src/github.com/hyperledger/fabric/peer/channel-artifacts
|
||||||
|
extra_hosts:
|
||||||
|
- "peer1.hospital.com:192.168.11.94"
|
||||||
|
- "peer2.hospital.com:192.168.11.63"
|
||||||
depends_on:
|
depends_on:
|
||||||
- orderer
|
- orderer
|
||||||
- peer0
|
- peer0
|
||||||
|
|
@ -128,8 +131,14 @@ services:
|
||||||
- /var/run/docker.sock:/host/var/run/docker.sock
|
- /var/run/docker.sock:/host/var/run/docker.sock
|
||||||
- /home/labai2/josafat/hospital-log/backend/blockchain/network/organizations/peerOrganizations/hospital.com/peers/peer1.hospital.com/msp:/etc/hyperledger/fabric/msp
|
- /home/labai2/josafat/hospital-log/backend/blockchain/network/organizations/peerOrganizations/hospital.com/peers/peer1.hospital.com/msp:/etc/hyperledger/fabric/msp
|
||||||
- /home/labai2/josafat/hospital-log/backend/blockchain/network/organizations/peerOrganizations/hospital.com/peers/peer1.hospital.com/tls:/etc/hyperledger/fabric/tls
|
- /home/labai2/josafat/hospital-log/backend/blockchain/network/organizations/peerOrganizations/hospital.com/peers/peer1.hospital.com/tls:/etc/hyperledger/fabric/tls
|
||||||
|
extra_hosts:
|
||||||
|
- "peer0.hospital.com:192.168.11.211"
|
||||||
|
- "peer2.hospital.com:192.168.11.63"
|
||||||
ports:
|
ports:
|
||||||
- "8051:8051"
|
- target: 8051
|
||||||
|
published: 8051
|
||||||
|
protocol: tcp
|
||||||
|
mode: host
|
||||||
networks:
|
networks:
|
||||||
- hospital_net
|
- hospital_net
|
||||||
deploy:
|
deploy:
|
||||||
|
|
@ -152,14 +161,22 @@ services:
|
||||||
- CORE_PEER_CHAINCODEADDRESS=peer2.hospital.com:7052
|
- CORE_PEER_CHAINCODEADDRESS=peer2.hospital.com:7052
|
||||||
- CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:7052
|
- CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:7052
|
||||||
- CORE_PEER_GOSSIP_BOOTSTRAP=peer0.hospital.com:7051
|
- CORE_PEER_GOSSIP_BOOTSTRAP=peer0.hospital.com:7051
|
||||||
- CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer2.hospital.com:9051
|
- CORE_PEER_GOSSIP_EXTERNALENDPOINT=192.168.11.63:9051
|
||||||
- CORE_PEER_LOCALMSPID=HospitalMSP
|
- CORE_PEER_LOCALMSPID=HospitalMSP
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/host/var/run/docker.sock
|
- /var/run/docker.sock:/host/var/run/docker.sock
|
||||||
- /run/desktop/mnt/host/c/fabric-data/network/organizations/peerOrganizations/hospital.com/peers/peer2.hospital.com/msp:/etc/hyperledger/fabric/msp
|
- /home/my_device/josafat/hospital-log/backend/blockchain/network/organizations/peerOrganizations/hospital.com/peers/peer2.hospital.com/msp:/etc/hyperledger/fabric/msp
|
||||||
- /run/desktop/mnt/host/c/fabric-data/network/organizations/peerOrganizations/hospital.com/peers/peer2.hospital.com/tls:/etc/hyperledger/fabric/tls
|
- /home/my_device/josafat/hospital-log/backend/blockchain/network/organizations/peerOrganizations/hospital.com/peers/peer2.hospital.com/tls:/etc/hyperledger/fabric/tls
|
||||||
|
- /home/my_device/josafat/hospital-log/backend/blockchain/data:/var/hyperledger/production
|
||||||
|
extra_hosts:
|
||||||
|
- "peer0.hospital.com:192.168.11.211"
|
||||||
|
- "orderer.hospital.com:192.168.11.211"
|
||||||
|
- "peer1.hospital.com:192.168.11.94"
|
||||||
ports:
|
ports:
|
||||||
- "9051:9051"
|
- target: 9051
|
||||||
|
published: 9051
|
||||||
|
protocol: tcp
|
||||||
|
mode: host
|
||||||
networks:
|
networks:
|
||||||
- hospital_net
|
- hospital_net
|
||||||
deploy:
|
deploy:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user