uup
This commit is contained in:
443
app/lib/__tests__/vehicle-management.test.ts
Normal file
443
app/lib/__tests__/vehicle-management.test.ts
Normal file
@@ -0,0 +1,443 @@
|
||||
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
||||
import {
|
||||
createVehicle,
|
||||
updateVehicle,
|
||||
deleteVehicle,
|
||||
getVehicles,
|
||||
getVehicleById,
|
||||
getVehiclesForSelect,
|
||||
searchVehicles,
|
||||
getVehicleStats
|
||||
} from '../vehicle-management.server';
|
||||
import { prisma } from '../db.server';
|
||||
|
||||
// Mock Prisma
|
||||
vi.mock('../db.server', () => ({
|
||||
prisma: {
|
||||
vehicle: {
|
||||
create: vi.fn(),
|
||||
findMany: vi.fn(),
|
||||
findUnique: vi.fn(),
|
||||
findFirst: vi.fn(),
|
||||
update: vi.fn(),
|
||||
delete: vi.fn(),
|
||||
count: vi.fn(),
|
||||
},
|
||||
customer: {
|
||||
findUnique: vi.fn(),
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
describe('Vehicle Management', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
describe('createVehicle', () => {
|
||||
const validVehicleData = {
|
||||
plateNumber: 'ABC-123',
|
||||
bodyType: 'سيدان',
|
||||
manufacturer: 'تويوتا',
|
||||
model: 'كامري',
|
||||
year: 2020,
|
||||
transmission: 'Automatic',
|
||||
fuel: 'Gasoline',
|
||||
useType: 'personal',
|
||||
ownerId: 1,
|
||||
};
|
||||
|
||||
it('should create a vehicle successfully', async () => {
|
||||
const mockVehicle = { id: 1, ...validVehicleData };
|
||||
const mockCustomer = { id: 1, name: 'أحمد محمد' };
|
||||
|
||||
(prisma.vehicle.findUnique as any).mockResolvedValue(null);
|
||||
(prisma.customer.findUnique as any).mockResolvedValue(mockCustomer);
|
||||
(prisma.vehicle.create as any).mockResolvedValue(mockVehicle);
|
||||
|
||||
const result = await createVehicle(validVehicleData);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.vehicle).toEqual(mockVehicle);
|
||||
expect(prisma.vehicle.create).toHaveBeenCalledWith({
|
||||
data: expect.objectContaining({
|
||||
plateNumber: 'ABC-123',
|
||||
manufacturer: 'تويوتا',
|
||||
model: 'كامري',
|
||||
ownerId: 1,
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail if plate number already exists', async () => {
|
||||
const existingVehicle = { id: 2, plateNumber: 'ABC-123' };
|
||||
(prisma.vehicle.findUnique as any).mockResolvedValue(existingVehicle);
|
||||
|
||||
const result = await createVehicle(validVehicleData);
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toBe('رقم اللوحة موجود بالفعل');
|
||||
expect(prisma.vehicle.create).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should fail if owner does not exist', async () => {
|
||||
(prisma.vehicle.findUnique as any).mockResolvedValue(null);
|
||||
(prisma.customer.findUnique as any).mockResolvedValue(null);
|
||||
|
||||
const result = await createVehicle(validVehicleData);
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toBe('المالك غير موجود');
|
||||
expect(prisma.vehicle.create).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle database errors', async () => {
|
||||
(prisma.vehicle.findUnique as any).mockResolvedValue(null);
|
||||
(prisma.customer.findUnique as any).mockResolvedValue({ id: 1 });
|
||||
(prisma.vehicle.create as any).mockRejectedValue(new Error('Database error'));
|
||||
|
||||
const result = await createVehicle(validVehicleData);
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toBe('حدث خطأ أثناء إنشاء المركبة');
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateVehicle', () => {
|
||||
const updateData = {
|
||||
plateNumber: 'XYZ-789',
|
||||
manufacturer: 'هوندا',
|
||||
model: 'أكورد',
|
||||
};
|
||||
|
||||
it('should update a vehicle successfully', async () => {
|
||||
const existingVehicle = { id: 1, plateNumber: 'ABC-123', ownerId: 1 };
|
||||
const updatedVehicle = { ...existingVehicle, ...updateData };
|
||||
|
||||
(prisma.vehicle.findUnique as any).mockResolvedValue(existingVehicle);
|
||||
(prisma.vehicle.findFirst as any).mockResolvedValue(null);
|
||||
(prisma.vehicle.update as any).mockResolvedValue(updatedVehicle);
|
||||
|
||||
const result = await updateVehicle(1, updateData);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.vehicle).toEqual(updatedVehicle);
|
||||
expect(prisma.vehicle.update).toHaveBeenCalledWith({
|
||||
where: { id: 1 },
|
||||
data: expect.objectContaining({
|
||||
plateNumber: 'XYZ-789',
|
||||
manufacturer: 'هوندا',
|
||||
model: 'أكورد',
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail if vehicle does not exist', async () => {
|
||||
(prisma.vehicle.findUnique as any).mockResolvedValue(null);
|
||||
|
||||
const result = await updateVehicle(999, updateData);
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toBe('المركبة غير موجودة');
|
||||
expect(prisma.vehicle.update).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should fail if new plate number conflicts', async () => {
|
||||
const existingVehicle = { id: 1, plateNumber: 'ABC-123' };
|
||||
const conflictVehicle = { id: 2, plateNumber: 'XYZ-789' };
|
||||
|
||||
(prisma.vehicle.findUnique as any).mockResolvedValue(existingVehicle);
|
||||
(prisma.vehicle.findFirst as any).mockResolvedValue(conflictVehicle);
|
||||
|
||||
const result = await updateVehicle(1, updateData);
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toBe('رقم اللوحة موجود بالفعل');
|
||||
expect(prisma.vehicle.update).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('deleteVehicle', () => {
|
||||
it('should delete a vehicle successfully', async () => {
|
||||
const existingVehicle = {
|
||||
id: 1,
|
||||
plateNumber: 'ABC-123',
|
||||
maintenanceVisits: []
|
||||
};
|
||||
|
||||
(prisma.vehicle.findUnique as any).mockResolvedValue(existingVehicle);
|
||||
(prisma.vehicle.delete as any).mockResolvedValue(existingVehicle);
|
||||
|
||||
const result = await deleteVehicle(1);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(prisma.vehicle.delete).toHaveBeenCalledWith({
|
||||
where: { id: 1 },
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail if vehicle does not exist', async () => {
|
||||
(prisma.vehicle.findUnique as any).mockResolvedValue(null);
|
||||
|
||||
const result = await deleteVehicle(999);
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toBe('المركبة غير موجودة');
|
||||
expect(prisma.vehicle.delete).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should fail if vehicle has maintenance visits', async () => {
|
||||
const existingVehicle = {
|
||||
id: 1,
|
||||
plateNumber: 'ABC-123',
|
||||
maintenanceVisits: [{ id: 1 }, { id: 2 }]
|
||||
};
|
||||
|
||||
(prisma.vehicle.findUnique as any).mockResolvedValue(existingVehicle);
|
||||
|
||||
const result = await deleteVehicle(1);
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toContain('لا يمكن حذف المركبة لأنها تحتوي على 2 زيارة صيانة');
|
||||
expect(prisma.vehicle.delete).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getVehicles', () => {
|
||||
it('should return vehicles with pagination', async () => {
|
||||
const mockVehicles = [
|
||||
{
|
||||
id: 1,
|
||||
plateNumber: 'ABC-123',
|
||||
manufacturer: 'تويوتا',
|
||||
owner: { id: 1, name: 'أحمد محمد' }
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
plateNumber: 'XYZ-789',
|
||||
manufacturer: 'هوندا',
|
||||
owner: { id: 2, name: 'محمد علي' }
|
||||
}
|
||||
];
|
||||
|
||||
(prisma.vehicle.findMany as any).mockResolvedValue(mockVehicles);
|
||||
(prisma.vehicle.count as any).mockResolvedValue(2);
|
||||
|
||||
const result = await getVehicles('', 1, 10);
|
||||
|
||||
expect(result.vehicles).toEqual(mockVehicles);
|
||||
expect(result.total).toBe(2);
|
||||
expect(result.totalPages).toBe(1);
|
||||
expect(prisma.vehicle.findMany).toHaveBeenCalledWith({
|
||||
where: {},
|
||||
include: {
|
||||
owner: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
phone: true,
|
||||
email: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
orderBy: { createdDate: 'desc' },
|
||||
skip: 0,
|
||||
take: 10,
|
||||
});
|
||||
});
|
||||
|
||||
it('should filter by search query', async () => {
|
||||
(prisma.vehicle.findMany as any).mockResolvedValue([]);
|
||||
(prisma.vehicle.count as any).mockResolvedValue(0);
|
||||
|
||||
await getVehicles('تويوتا', 1, 10);
|
||||
|
||||
expect(prisma.vehicle.findMany).toHaveBeenCalledWith({
|
||||
where: {
|
||||
OR: [
|
||||
{ plateNumber: { contains: 'تويوتا' } },
|
||||
{ manufacturer: { contains: 'تويوتا' } },
|
||||
{ model: { contains: 'تويوتا' } },
|
||||
{ bodyType: { contains: 'تويوتا' } },
|
||||
{ owner: { name: { contains: 'تويوتا' } } },
|
||||
],
|
||||
},
|
||||
include: expect.any(Object),
|
||||
orderBy: { createdDate: 'desc' },
|
||||
skip: 0,
|
||||
take: 10,
|
||||
});
|
||||
});
|
||||
|
||||
it('should filter by owner ID', async () => {
|
||||
(prisma.vehicle.findMany as any).mockResolvedValue([]);
|
||||
(prisma.vehicle.count as any).mockResolvedValue(0);
|
||||
|
||||
await getVehicles('', 1, 10, 5);
|
||||
|
||||
expect(prisma.vehicle.findMany).toHaveBeenCalledWith({
|
||||
where: { ownerId: 5 },
|
||||
include: expect.any(Object),
|
||||
orderBy: { createdDate: 'desc' },
|
||||
skip: 0,
|
||||
take: 10,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getVehicleById', () => {
|
||||
it('should return vehicle with full relationships', async () => {
|
||||
const mockVehicle = {
|
||||
id: 1,
|
||||
plateNumber: 'ABC-123',
|
||||
owner: { id: 1, name: 'أحمد محمد' },
|
||||
maintenanceVisits: [
|
||||
{ id: 1, visitDate: new Date(), cost: 100 }
|
||||
]
|
||||
};
|
||||
|
||||
(prisma.vehicle.findUnique as any).mockResolvedValue(mockVehicle);
|
||||
|
||||
const result = await getVehicleById(1);
|
||||
|
||||
expect(result).toEqual(mockVehicle);
|
||||
expect(prisma.vehicle.findUnique).toHaveBeenCalledWith({
|
||||
where: { id: 1 },
|
||||
include: {
|
||||
owner: true,
|
||||
maintenanceVisits: {
|
||||
include: {
|
||||
customer: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
phone: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
orderBy: { visitDate: 'desc' },
|
||||
take: 10,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should return null if vehicle not found', async () => {
|
||||
(prisma.vehicle.findUnique as any).mockResolvedValue(null);
|
||||
|
||||
const result = await getVehicleById(999);
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getVehicleStats', () => {
|
||||
it('should calculate vehicle statistics', async () => {
|
||||
const mockVehicle = {
|
||||
id: 1,
|
||||
suggestedNextVisitDate: new Date('2024-06-01'),
|
||||
maintenanceVisits: [
|
||||
{ cost: 150, visitDate: new Date('2024-03-01') }, // Most recent first (desc order)
|
||||
{ cost: 200, visitDate: new Date('2024-02-01') },
|
||||
{ cost: 100, visitDate: new Date('2024-01-01') },
|
||||
]
|
||||
};
|
||||
|
||||
(prisma.vehicle.findUnique as any).mockResolvedValue(mockVehicle);
|
||||
|
||||
const result = await getVehicleStats(1);
|
||||
|
||||
expect(result).toEqual({
|
||||
totalVisits: 3,
|
||||
totalSpent: 450,
|
||||
lastVisitDate: new Date('2024-03-01'),
|
||||
nextSuggestedVisitDate: new Date('2024-06-01'),
|
||||
averageVisitCost: 150,
|
||||
});
|
||||
});
|
||||
|
||||
it('should return null if vehicle not found', async () => {
|
||||
(prisma.vehicle.findUnique as any).mockResolvedValue(null);
|
||||
|
||||
const result = await getVehicleStats(999);
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it('should handle vehicle with no visits', async () => {
|
||||
const mockVehicle = {
|
||||
id: 1,
|
||||
suggestedNextVisitDate: null,
|
||||
maintenanceVisits: []
|
||||
};
|
||||
|
||||
(prisma.vehicle.findUnique as any).mockResolvedValue(mockVehicle);
|
||||
|
||||
const result = await getVehicleStats(1);
|
||||
|
||||
expect(result).toEqual({
|
||||
totalVisits: 0,
|
||||
totalSpent: 0,
|
||||
lastVisitDate: undefined,
|
||||
nextSuggestedVisitDate: undefined,
|
||||
averageVisitCost: 0,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('searchVehicles', () => {
|
||||
it('should search vehicles by query', async () => {
|
||||
const mockVehicles = [
|
||||
{
|
||||
id: 1,
|
||||
plateNumber: 'ABC-123',
|
||||
manufacturer: 'تويوتا',
|
||||
model: 'كامري',
|
||||
year: 2020,
|
||||
owner: { id: 1, name: 'أحمد محمد' }
|
||||
}
|
||||
];
|
||||
|
||||
(prisma.vehicle.findMany as any).mockResolvedValue(mockVehicles);
|
||||
|
||||
const result = await searchVehicles('تويوتا');
|
||||
|
||||
expect(result).toEqual(mockVehicles);
|
||||
expect(prisma.vehicle.findMany).toHaveBeenCalledWith({
|
||||
where: {
|
||||
OR: [
|
||||
{ plateNumber: { contains: 'تويوتا' } },
|
||||
{ manufacturer: { contains: 'تويوتا' } },
|
||||
{ model: { contains: 'تويوتا' } },
|
||||
],
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
plateNumber: true,
|
||||
manufacturer: true,
|
||||
model: true,
|
||||
year: true,
|
||||
owner: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
orderBy: { plateNumber: 'asc' },
|
||||
take: 10,
|
||||
});
|
||||
});
|
||||
|
||||
it('should return empty array for short queries', async () => {
|
||||
const result = await searchVehicles('a');
|
||||
|
||||
expect(result).toEqual([]);
|
||||
expect(prisma.vehicle.findMany).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user