add orders.test.js
This commit is contained in:
parent
429f5dcab2
commit
f7c21d5c01
796
packages/graphql/api/orders.test.js
Normal file
796
packages/graphql/api/orders.test.js
Normal file
@ -0,0 +1,796 @@
|
||||
import { getClientWithToken } from '../apollo/client';
|
||||
import * as GQL from '../types';
|
||||
import { CustomersService } from './customers';
|
||||
import { ERRORS, OrdersService } from './orders';
|
||||
import { ServicesService } from './services';
|
||||
import { SlotsService } from './slots';
|
||||
import dayjs from 'dayjs';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
vi.mock('../apollo/client');
|
||||
vi.mock('./customers');
|
||||
vi.mock('./services');
|
||||
vi.mock('./slots');
|
||||
vi.mock('../config/env', () => {
|
||||
return {
|
||||
env: {
|
||||
BOT_TOKEN: 'test',
|
||||
LOGIN_GRAPHQL: 'test',
|
||||
PASSWORD_GRAPHQL: 'test',
|
||||
URL_GRAPHQL: 'test',
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const mockGetClientWithToken = vi.mocked(getClientWithToken);
|
||||
const mockCustomersService = vi.mocked(CustomersService);
|
||||
const mockServicesService = vi.mocked(ServicesService);
|
||||
const mockSlotsService = vi.mocked(SlotsService);
|
||||
|
||||
describe('OrdersService', () => {
|
||||
/**
|
||||
* @type {OrdersService}
|
||||
*/
|
||||
let ordersService;
|
||||
const mockUser = { telegramId: 123_456_789 };
|
||||
|
||||
const mockCustomer = {
|
||||
active: true,
|
||||
documentId: 'customer-123',
|
||||
firstName: 'John',
|
||||
lastName: 'Doe',
|
||||
role: GQL.Enum_Customer_Role.Customer,
|
||||
telegramId: 123_456_789,
|
||||
};
|
||||
|
||||
const mockMaster = {
|
||||
active: true,
|
||||
documentId: 'master-123',
|
||||
firstName: 'Jane',
|
||||
lastName: 'Master',
|
||||
role: GQL.Enum_Customer_Role.Master,
|
||||
telegramId: 987_654_321,
|
||||
};
|
||||
|
||||
const now = dayjs().minute(0).second(0).millisecond(0);
|
||||
vi.setSystemTime(now.toDate());
|
||||
|
||||
const mockSlot = {
|
||||
datetime_end: now.add(6, 'hour').toISOString(),
|
||||
datetime_start: now.toISOString(),
|
||||
documentId: 'slot-123',
|
||||
master: mockMaster,
|
||||
orders: [],
|
||||
state: GQL.Enum_Slot_State.Open,
|
||||
};
|
||||
|
||||
const mockService = {
|
||||
active: true,
|
||||
documentId: 'service-123',
|
||||
duration: '01:00:00', // 1 час
|
||||
master: mockMaster,
|
||||
name: 'Test Service',
|
||||
};
|
||||
|
||||
const mockOrder = {
|
||||
client: mockCustomer,
|
||||
datetime_end: now.add(1, 'hour').toISOString(),
|
||||
datetime_start: now.toISOString(),
|
||||
documentId: 'order-123',
|
||||
services: [mockService],
|
||||
slot: mockSlot,
|
||||
state: GQL.Enum_Order_State.Created,
|
||||
};
|
||||
|
||||
const mockGetCustomerResult = {
|
||||
data: {
|
||||
customers: [mockCustomer],
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
ordersService = new OrdersService(mockUser);
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
describe('createOrder', () => {
|
||||
const mockVariables = {
|
||||
input: {
|
||||
client: 'customer-123',
|
||||
datetime_end: now.add(1, 'hour').toISOString(),
|
||||
datetime_start: now.toISOString(),
|
||||
services: ['service-123'],
|
||||
slot: 'slot-123',
|
||||
},
|
||||
};
|
||||
|
||||
const mockMutationResult = {
|
||||
data: {
|
||||
createOrder: mockOrder,
|
||||
},
|
||||
errors: undefined,
|
||||
};
|
||||
|
||||
it('should successfully create order for customer', async () => {
|
||||
const mockMutate = vi.fn().mockResolvedValue(mockMutationResult);
|
||||
const mockQuery = vi.fn().mockImplementation(({ query }) => {
|
||||
if (query === GQL.GetCustomerDocument) {
|
||||
return Promise.resolve(mockGetCustomerResult);
|
||||
}
|
||||
|
||||
if (query === GQL.GetOrdersDocument) {
|
||||
return Promise.resolve({ data: { orders: [] } }); // нет пересекающихся заказов
|
||||
}
|
||||
|
||||
return Promise.resolve({ data: {} });
|
||||
});
|
||||
|
||||
mockGetClientWithToken.mockResolvedValue({
|
||||
mutate: mockMutate,
|
||||
query: mockQuery,
|
||||
});
|
||||
|
||||
// Мокаем ServicesService.getService
|
||||
const mockGetService = vi.fn().mockResolvedValue({
|
||||
service: mockService,
|
||||
});
|
||||
mockServicesService.mockImplementation(() => ({
|
||||
getService: mockGetService,
|
||||
}));
|
||||
|
||||
// Мокаем SlotsService.getSlot
|
||||
const mockGetSlot = vi.fn().mockResolvedValue({
|
||||
slot: mockSlot,
|
||||
});
|
||||
mockSlotsService.mockImplementation(() => ({
|
||||
getSlot: mockGetSlot,
|
||||
}));
|
||||
|
||||
// Мокаем CustomersService.getCustomer
|
||||
const mockGetCustomer = vi.fn().mockResolvedValue({
|
||||
customer: mockCustomer,
|
||||
});
|
||||
mockCustomersService.mockImplementation(() => ({
|
||||
getCustomer: mockGetCustomer,
|
||||
getMasters: vi.fn().mockResolvedValue({
|
||||
masters: [mockMaster],
|
||||
}),
|
||||
}));
|
||||
|
||||
const result = ordersService.createOrder(mockVariables);
|
||||
|
||||
await expect(result).resolves.toBe(mockMutationResult.data);
|
||||
});
|
||||
|
||||
it('should successfully create approved order for master', async () => {
|
||||
const masterCustomer = {
|
||||
...mockCustomer,
|
||||
role: GQL.Enum_Customer_Role.Master,
|
||||
};
|
||||
|
||||
const masterSlot = {
|
||||
...mockSlot,
|
||||
master: masterCustomer,
|
||||
};
|
||||
|
||||
const mockMutate = vi.fn().mockResolvedValue(mockMutationResult);
|
||||
const mockQuery = vi.fn().mockImplementation(({ query }) => {
|
||||
if (query === GQL.GetCustomerDocument) {
|
||||
return Promise.resolve({
|
||||
data: { customers: [masterCustomer] },
|
||||
});
|
||||
}
|
||||
|
||||
if (query === GQL.GetOrdersDocument) {
|
||||
return Promise.resolve({ data: { orders: [] } });
|
||||
}
|
||||
|
||||
return Promise.resolve({ data: {} });
|
||||
});
|
||||
|
||||
mockGetClientWithToken.mockResolvedValue({
|
||||
mutate: mockMutate,
|
||||
query: mockQuery,
|
||||
});
|
||||
|
||||
const mockGetService = vi.fn().mockResolvedValue({
|
||||
service: mockService,
|
||||
});
|
||||
mockServicesService.mockImplementation(() => ({
|
||||
getService: mockGetService,
|
||||
}));
|
||||
|
||||
const mockGetSlot = vi.fn().mockResolvedValue({
|
||||
slot: masterSlot,
|
||||
});
|
||||
mockSlotsService.mockImplementation(() => ({
|
||||
getSlot: mockGetSlot,
|
||||
}));
|
||||
|
||||
const mockGetCustomer = vi.fn().mockResolvedValue({
|
||||
customer: masterCustomer,
|
||||
});
|
||||
mockCustomersService.mockImplementation(() => ({
|
||||
getCustomer: mockGetCustomer,
|
||||
getMasters: vi.fn().mockResolvedValue({
|
||||
masters: [masterCustomer],
|
||||
}),
|
||||
}));
|
||||
|
||||
const result = ordersService.createOrder({
|
||||
...mockVariables,
|
||||
input: {
|
||||
...mockVariables.input,
|
||||
client: masterCustomer.documentId,
|
||||
},
|
||||
});
|
||||
|
||||
await expect(result).resolves.toBe(mockMutationResult.data);
|
||||
});
|
||||
|
||||
it('should throw error when slot is missing', async () => {
|
||||
const variablesWithoutSlot = {
|
||||
input: {
|
||||
client: 'customer-123',
|
||||
datetime_start: now.toISOString(),
|
||||
services: ['service-123'],
|
||||
},
|
||||
};
|
||||
|
||||
const result = ordersService.createOrder(variablesWithoutSlot);
|
||||
|
||||
await expect(result).rejects.toThrow(ERRORS.MISSING_SLOT);
|
||||
});
|
||||
|
||||
it('should throw error when services are missing', async () => {
|
||||
const variablesWithoutServices = {
|
||||
input: {
|
||||
client: 'customer-123',
|
||||
datetime_start: now.toISOString(),
|
||||
slot: 'slot-123',
|
||||
},
|
||||
};
|
||||
|
||||
const result = ordersService.createOrder(variablesWithoutServices);
|
||||
|
||||
await expect(result).rejects.toThrow(ERRORS.MISSING_SERVICES);
|
||||
});
|
||||
|
||||
it('should throw error when datetime_start is missing', async () => {
|
||||
const variablesWithoutStart = {
|
||||
input: {
|
||||
client: 'customer-123',
|
||||
services: ['service-123'],
|
||||
slot: 'slot-123',
|
||||
},
|
||||
};
|
||||
|
||||
const result = ordersService.createOrder(variablesWithoutStart);
|
||||
|
||||
await expect(result).rejects.toThrow(ERRORS.MISSING_START_TIME);
|
||||
});
|
||||
|
||||
it('should throw error when client is missing', async () => {
|
||||
const variablesWithoutClient = {
|
||||
input: {
|
||||
datetime_start: now.toISOString(),
|
||||
services: ['service-123'],
|
||||
slot: 'slot-123',
|
||||
},
|
||||
};
|
||||
|
||||
const result = ordersService.createOrder(variablesWithoutClient);
|
||||
|
||||
await expect(result).rejects.toThrow(ERRORS.MISSING_CLIENT);
|
||||
});
|
||||
|
||||
it('should throw error when order time is in the past', async () => {
|
||||
const pastTime = now.subtract(1, 'hour');
|
||||
const variablesWithPastTime = {
|
||||
input: {
|
||||
client: 'customer-123',
|
||||
datetime_end: pastTime.add(1, 'hour').toISOString(),
|
||||
datetime_start: pastTime.toISOString(),
|
||||
services: ['service-123'],
|
||||
slot: 'slot-123',
|
||||
},
|
||||
};
|
||||
|
||||
const result = ordersService.createOrder(variablesWithPastTime);
|
||||
|
||||
await expect(result).rejects.toThrow(ERRORS.NO_ORDER_IN_PAST);
|
||||
});
|
||||
|
||||
it('should throw error when order time is invalid', async () => {
|
||||
const variablesWithInvalidTime = {
|
||||
input: {
|
||||
client: 'customer-123',
|
||||
datetime_end: now.toISOString(), // равно datetime_start
|
||||
datetime_start: now.toISOString(),
|
||||
services: ['service-123'],
|
||||
slot: 'slot-123',
|
||||
},
|
||||
};
|
||||
|
||||
const result = ordersService.createOrder(variablesWithInvalidTime);
|
||||
|
||||
await expect(result).rejects.toThrow(ERRORS.INVALID_TIME);
|
||||
});
|
||||
|
||||
it('should throw error when slot is not found', async () => {
|
||||
const mockQuery = vi.fn().mockImplementation(({ query }) => {
|
||||
if (query === GQL.GetCustomerDocument) {
|
||||
return Promise.resolve(mockGetCustomerResult);
|
||||
}
|
||||
|
||||
return Promise.resolve({ data: {} });
|
||||
});
|
||||
|
||||
mockGetClientWithToken.mockResolvedValue({
|
||||
mutate: vi.fn(),
|
||||
query: mockQuery,
|
||||
});
|
||||
|
||||
const mockGetSlot = vi.fn().mockResolvedValue({
|
||||
slot: null, // слот не найден
|
||||
});
|
||||
mockSlotsService.mockImplementation(() => ({
|
||||
getSlot: mockGetSlot,
|
||||
}));
|
||||
|
||||
const result = ordersService.createOrder(mockVariables);
|
||||
|
||||
await expect(result).rejects.toThrow(ERRORS.MISSING_SLOT);
|
||||
});
|
||||
|
||||
it('should throw error when order is out of slot time', async () => {
|
||||
const mockQuery = vi.fn().mockImplementation(({ query }) => {
|
||||
if (query === GQL.GetCustomerDocument) {
|
||||
return Promise.resolve(mockGetCustomerResult);
|
||||
}
|
||||
|
||||
return Promise.resolve({ data: {} });
|
||||
});
|
||||
|
||||
mockGetClientWithToken.mockResolvedValue({
|
||||
mutate: vi.fn(),
|
||||
query: mockQuery,
|
||||
});
|
||||
|
||||
const mockGetSlot = vi.fn().mockResolvedValue({
|
||||
slot: mockSlot,
|
||||
});
|
||||
mockSlotsService.mockImplementation(() => ({
|
||||
getSlot: mockGetSlot,
|
||||
}));
|
||||
|
||||
const mockGetCustomer = vi.fn().mockResolvedValue({
|
||||
customer: mockCustomer,
|
||||
});
|
||||
mockCustomersService.mockImplementation(() => ({
|
||||
getCustomer: mockGetCustomer,
|
||||
getMasters: vi.fn().mockResolvedValue({
|
||||
masters: [mockMaster],
|
||||
}),
|
||||
}));
|
||||
|
||||
const variablesWithOutOfSlotTime = {
|
||||
input: {
|
||||
client: 'customer-123',
|
||||
datetime_end: now.add(8, 'hour').toISOString(),
|
||||
datetime_start: now.add(7, 'hour').toISOString(), // после окончания слота
|
||||
services: ['service-123'],
|
||||
slot: 'slot-123',
|
||||
},
|
||||
};
|
||||
|
||||
const result = ordersService.createOrder(variablesWithOutOfSlotTime);
|
||||
|
||||
await expect(result).rejects.toThrow(ERRORS.NO_ORDER_OUT_OF_SLOT);
|
||||
});
|
||||
|
||||
it('should throw error when slot is closed', async () => {
|
||||
const closedSlot = {
|
||||
...mockSlot,
|
||||
state: GQL.Enum_Slot_State.Closed,
|
||||
};
|
||||
|
||||
const mockQuery = vi.fn().mockImplementation(({ query }) => {
|
||||
if (query === GQL.GetCustomerDocument) {
|
||||
return Promise.resolve(mockGetCustomerResult);
|
||||
}
|
||||
|
||||
return Promise.resolve({ data: {} });
|
||||
});
|
||||
|
||||
mockGetClientWithToken.mockResolvedValue({
|
||||
mutate: vi.fn(),
|
||||
query: mockQuery,
|
||||
});
|
||||
|
||||
const mockGetSlot = vi.fn().mockResolvedValue({
|
||||
slot: closedSlot,
|
||||
});
|
||||
mockSlotsService.mockImplementation(() => ({
|
||||
getSlot: mockGetSlot,
|
||||
}));
|
||||
|
||||
const mockGetCustomer = vi.fn().mockResolvedValue({
|
||||
customer: mockCustomer,
|
||||
});
|
||||
mockCustomersService.mockImplementation(() => ({
|
||||
getCustomer: mockGetCustomer,
|
||||
getMasters: vi.fn().mockResolvedValue({
|
||||
masters: [mockMaster],
|
||||
}),
|
||||
}));
|
||||
|
||||
const result = ordersService.createOrder(mockVariables);
|
||||
|
||||
await expect(result).rejects.toThrow(ERRORS.SLOT_CLOSED);
|
||||
});
|
||||
|
||||
it('should throw error when client is not found', async () => {
|
||||
const mockQuery = vi.fn().mockImplementation(({ query }) => {
|
||||
if (query === GQL.GetCustomerDocument) {
|
||||
return Promise.resolve(mockGetCustomerResult);
|
||||
}
|
||||
|
||||
return Promise.resolve({ data: {} });
|
||||
});
|
||||
|
||||
mockGetClientWithToken.mockResolvedValue({
|
||||
mutate: vi.fn(),
|
||||
query: mockQuery,
|
||||
});
|
||||
|
||||
const mockGetSlot = vi.fn().mockResolvedValue({
|
||||
slot: mockSlot,
|
||||
});
|
||||
mockSlotsService.mockImplementation(() => ({
|
||||
getSlot: mockGetSlot,
|
||||
}));
|
||||
|
||||
const mockGetCustomer = vi.fn().mockResolvedValue({
|
||||
customer: null, // клиент не найден
|
||||
});
|
||||
mockCustomersService.mockImplementation(() => ({
|
||||
getCustomer: mockGetCustomer,
|
||||
getMasters: vi.fn().mockResolvedValue({
|
||||
masters: [mockMaster],
|
||||
}),
|
||||
}));
|
||||
|
||||
const result = ordersService.createOrder(mockVariables);
|
||||
|
||||
await expect(result).rejects.toThrow(ERRORS.NOT_FOUND_CLIENT);
|
||||
});
|
||||
|
||||
it('should throw error when client is inactive', async () => {
|
||||
const inactiveCustomer = {
|
||||
...mockCustomer,
|
||||
active: false,
|
||||
};
|
||||
|
||||
const mockQuery = vi.fn().mockImplementation(({ query }) => {
|
||||
if (query === GQL.GetCustomerDocument) {
|
||||
return Promise.resolve(mockGetCustomerResult);
|
||||
}
|
||||
|
||||
return Promise.resolve({ data: {} });
|
||||
});
|
||||
|
||||
mockGetClientWithToken.mockResolvedValue({
|
||||
mutate: vi.fn(),
|
||||
query: mockQuery,
|
||||
});
|
||||
|
||||
const mockGetSlot = vi.fn().mockResolvedValue({
|
||||
slot: mockSlot,
|
||||
});
|
||||
mockSlotsService.mockImplementation(() => ({
|
||||
getSlot: mockGetSlot,
|
||||
}));
|
||||
|
||||
const mockGetCustomer = vi.fn().mockResolvedValue({
|
||||
customer: inactiveCustomer,
|
||||
});
|
||||
mockCustomersService.mockImplementation(() => ({
|
||||
getCustomer: mockGetCustomer,
|
||||
getMasters: vi.fn().mockResolvedValue({
|
||||
masters: [mockMaster],
|
||||
}),
|
||||
}));
|
||||
|
||||
const result = ordersService.createOrder(mockVariables);
|
||||
|
||||
await expect(result).rejects.toThrow(ERRORS.INACTIVE_CLIENT);
|
||||
});
|
||||
|
||||
it('should throw error when master is inactive', async () => {
|
||||
const inactiveMaster = {
|
||||
...mockMaster,
|
||||
active: false,
|
||||
};
|
||||
|
||||
const slotWithInactiveMaster = {
|
||||
...mockSlot,
|
||||
master: inactiveMaster,
|
||||
};
|
||||
|
||||
const mockQuery = vi.fn().mockImplementation(({ query }) => {
|
||||
if (query === GQL.GetCustomerDocument) {
|
||||
return Promise.resolve(mockGetCustomerResult);
|
||||
}
|
||||
|
||||
return Promise.resolve({ data: {} });
|
||||
});
|
||||
|
||||
mockGetClientWithToken.mockResolvedValue({
|
||||
mutate: vi.fn(),
|
||||
query: mockQuery,
|
||||
});
|
||||
|
||||
const mockGetSlot = vi.fn().mockResolvedValue({
|
||||
slot: slotWithInactiveMaster,
|
||||
});
|
||||
mockSlotsService.mockImplementation(() => ({
|
||||
getSlot: mockGetSlot,
|
||||
}));
|
||||
|
||||
const mockGetCustomer = vi.fn().mockResolvedValue({
|
||||
customer: mockCustomer,
|
||||
});
|
||||
mockCustomersService.mockImplementation(() => ({
|
||||
getCustomer: mockGetCustomer,
|
||||
getMasters: vi.fn().mockResolvedValue({
|
||||
masters: [inactiveMaster],
|
||||
}),
|
||||
}));
|
||||
|
||||
const result = ordersService.createOrder(mockVariables);
|
||||
|
||||
await expect(result).rejects.toThrow(ERRORS.INACTIVE_MASTER);
|
||||
});
|
||||
|
||||
it('should throw error when customer tries to book themselves as master', async () => {
|
||||
const activeCustomerAsMaster = {
|
||||
...mockCustomer,
|
||||
active: true,
|
||||
role: GQL.Enum_Customer_Role.Master,
|
||||
};
|
||||
|
||||
const slotWithCustomerAsMaster = {
|
||||
...mockSlot,
|
||||
master: activeCustomerAsMaster,
|
||||
};
|
||||
|
||||
const mockQuery = vi.fn().mockImplementation(({ query }) => {
|
||||
if (query === GQL.GetCustomerDocument) {
|
||||
return Promise.resolve(mockGetCustomerResult);
|
||||
}
|
||||
|
||||
return Promise.resolve({ data: {} });
|
||||
});
|
||||
|
||||
mockGetClientWithToken.mockResolvedValue({
|
||||
mutate: vi.fn(),
|
||||
query: mockQuery,
|
||||
});
|
||||
|
||||
const mockGetSlot = vi.fn().mockResolvedValue({
|
||||
slot: slotWithCustomerAsMaster,
|
||||
});
|
||||
mockSlotsService.mockImplementation(() => ({
|
||||
getSlot: mockGetSlot,
|
||||
}));
|
||||
|
||||
const mockGetCustomer = vi.fn().mockResolvedValue({
|
||||
customer: mockCustomer,
|
||||
});
|
||||
mockCustomersService.mockImplementation(() => ({
|
||||
getCustomer: mockGetCustomer,
|
||||
getMasters: vi.fn().mockResolvedValue({
|
||||
masters: [activeCustomerAsMaster],
|
||||
}),
|
||||
}));
|
||||
|
||||
const result = ordersService.createOrder(mockVariables);
|
||||
|
||||
await expect(result).rejects.toThrow(ERRORS.NO_MASTER_SELF_BOOK);
|
||||
});
|
||||
|
||||
it('should throw error when customer is not linked to master', async () => {
|
||||
const mockQuery = vi.fn().mockImplementation(({ query }) => {
|
||||
if (query === GQL.GetCustomerDocument) {
|
||||
return Promise.resolve(mockGetCustomerResult);
|
||||
}
|
||||
|
||||
return Promise.resolve({ data: {} });
|
||||
});
|
||||
|
||||
mockGetClientWithToken.mockResolvedValue({
|
||||
mutate: vi.fn(),
|
||||
query: mockQuery,
|
||||
});
|
||||
|
||||
const mockGetSlot = vi.fn().mockResolvedValue({
|
||||
slot: mockSlot,
|
||||
});
|
||||
mockSlotsService.mockImplementation(() => ({
|
||||
getSlot: mockGetSlot,
|
||||
}));
|
||||
|
||||
const mockGetCustomer = vi.fn().mockResolvedValue({
|
||||
customer: mockCustomer,
|
||||
});
|
||||
mockCustomersService.mockImplementation(() => ({
|
||||
getCustomer: mockGetCustomer,
|
||||
getMasters: vi.fn().mockResolvedValue({
|
||||
masters: [], // клиент не связан с мастером
|
||||
}),
|
||||
}));
|
||||
|
||||
const result = ordersService.createOrder(mockVariables);
|
||||
|
||||
await expect(result).rejects.toThrow(ERRORS.INVALID_MASTER);
|
||||
});
|
||||
|
||||
it('should throw error when time overlaps with other orders', async () => {
|
||||
const overlappingOrder = {
|
||||
...mockOrder,
|
||||
documentId: 'order-456',
|
||||
};
|
||||
|
||||
const mockQuery = vi.fn().mockImplementation(({ query }) => {
|
||||
if (query === GQL.GetCustomerDocument) {
|
||||
return Promise.resolve(mockGetCustomerResult);
|
||||
}
|
||||
|
||||
if (query === GQL.GetOrdersDocument) {
|
||||
return Promise.resolve({
|
||||
data: { orders: [overlappingOrder] },
|
||||
}); // есть пересекающиеся заказы
|
||||
}
|
||||
|
||||
return Promise.resolve({ data: {} });
|
||||
});
|
||||
|
||||
mockGetClientWithToken.mockResolvedValue({
|
||||
mutate: vi.fn(),
|
||||
query: mockQuery,
|
||||
});
|
||||
|
||||
const mockGetSlot = vi.fn().mockResolvedValue({
|
||||
slot: mockSlot,
|
||||
});
|
||||
mockSlotsService.mockImplementation(() => ({
|
||||
getSlot: mockGetSlot,
|
||||
}));
|
||||
|
||||
const mockGetCustomer = vi.fn().mockResolvedValue({
|
||||
customer: mockCustomer,
|
||||
});
|
||||
mockCustomersService.mockImplementation(() => ({
|
||||
getCustomer: mockGetCustomer,
|
||||
getMasters: vi.fn().mockResolvedValue({
|
||||
masters: [mockMaster],
|
||||
}),
|
||||
}));
|
||||
|
||||
const result = ordersService.createOrder(mockVariables);
|
||||
|
||||
await expect(result).rejects.toThrow(ERRORS.OVERLAPPING_TIME);
|
||||
});
|
||||
|
||||
it('should throw error when service duration is invalid', async () => {
|
||||
const serviceWithoutDuration = {
|
||||
...mockService,
|
||||
duration: null,
|
||||
};
|
||||
|
||||
const mockQuery = vi.fn().mockImplementation(({ query }) => {
|
||||
if (query === GQL.GetCustomerDocument) {
|
||||
return Promise.resolve(mockGetCustomerResult);
|
||||
}
|
||||
|
||||
return Promise.resolve({ data: {} });
|
||||
});
|
||||
|
||||
mockGetClientWithToken.mockResolvedValue({
|
||||
mutate: vi.fn(),
|
||||
query: mockQuery,
|
||||
});
|
||||
|
||||
const mockGetSlot = vi.fn().mockResolvedValue({
|
||||
slot: mockSlot,
|
||||
});
|
||||
mockSlotsService.mockImplementation(() => ({
|
||||
getSlot: mockGetSlot,
|
||||
}));
|
||||
|
||||
const mockGetCustomer = vi.fn().mockResolvedValue({
|
||||
customer: mockCustomer,
|
||||
});
|
||||
mockCustomersService.mockImplementation(() => ({
|
||||
getCustomer: mockGetCustomer,
|
||||
getMasters: vi.fn().mockResolvedValue({
|
||||
masters: [mockMaster],
|
||||
}),
|
||||
}));
|
||||
|
||||
const mockGetService = vi.fn().mockResolvedValue({
|
||||
service: serviceWithoutDuration,
|
||||
});
|
||||
mockServicesService.mockImplementation(() => ({
|
||||
getService: mockGetService,
|
||||
}));
|
||||
|
||||
const result = ordersService.createOrder(mockVariables);
|
||||
|
||||
await expect(result).rejects.toThrow(ERRORS.INVALID_SERVICE_DURATION);
|
||||
});
|
||||
|
||||
it('should calculate datetime_end based on service duration', async () => {
|
||||
const mockMutate = vi.fn().mockResolvedValue(mockMutationResult);
|
||||
const mockQuery = vi.fn().mockImplementation(({ query }) => {
|
||||
if (query === GQL.GetCustomerDocument) {
|
||||
return Promise.resolve(mockGetCustomerResult);
|
||||
}
|
||||
|
||||
if (query === GQL.GetOrdersDocument) {
|
||||
return Promise.resolve({ data: { orders: [] } });
|
||||
}
|
||||
|
||||
return Promise.resolve({ data: {} });
|
||||
});
|
||||
|
||||
mockGetClientWithToken.mockResolvedValue({
|
||||
mutate: mockMutate,
|
||||
query: mockQuery,
|
||||
});
|
||||
|
||||
const mockGetService = vi.fn().mockResolvedValue({
|
||||
service: mockService,
|
||||
});
|
||||
mockServicesService.mockImplementation(() => ({
|
||||
getService: mockGetService,
|
||||
}));
|
||||
|
||||
const mockGetSlot = vi.fn().mockResolvedValue({
|
||||
slot: mockSlot,
|
||||
});
|
||||
mockSlotsService.mockImplementation(() => ({
|
||||
getSlot: mockGetSlot,
|
||||
}));
|
||||
|
||||
const mockGetCustomer = vi.fn().mockResolvedValue({
|
||||
customer: mockCustomer,
|
||||
});
|
||||
mockCustomersService.mockImplementation(() => ({
|
||||
getCustomer: mockGetCustomer,
|
||||
getMasters: vi.fn().mockResolvedValue({
|
||||
masters: [mockMaster],
|
||||
}),
|
||||
}));
|
||||
|
||||
await ordersService.createOrder(mockVariables);
|
||||
|
||||
expect(mockMutate).toHaveBeenCalledWith({
|
||||
mutation: GQL.CreateOrderDocument,
|
||||
variables: {
|
||||
...mockVariables,
|
||||
input: {
|
||||
...mockVariables.input,
|
||||
datetime_end: now.add(1, 'hour').toISOString(), // 1 час от начала
|
||||
state: GQL.Enum_Order_State.Created,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -11,13 +11,14 @@ import { isCustomerMaster } from '@repo/utils/customer';
|
||||
import { getMinutes, isBeforeNow } from '@repo/utils/datetime-format';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
const ERRORS = {
|
||||
export const ERRORS = {
|
||||
INACTIVE_CLIENT: 'Клиент не активен',
|
||||
INACTIVE_MASTER: 'Мастер не активен',
|
||||
INVALID_MASTER: 'Некорректный мастер',
|
||||
INVALID_SERVICE_DURATION: 'Неверная длительность услуги',
|
||||
INVALID_TIME: 'Некорректное время',
|
||||
MISSING_CLIENT: 'Не указан клиент',
|
||||
MISSING_END_TIME: 'Не указано время окончания',
|
||||
MISSING_ORDER: 'Заказ не найден',
|
||||
MISSING_SERVICE_ID: 'Не указан идентификатор услуги',
|
||||
MISSING_SERVICES: 'Отсутствуют услуги',
|
||||
@ -25,11 +26,11 @@ const ERRORS = {
|
||||
MISSING_START_TIME: 'Не указано время начала',
|
||||
MISSING_TIME: 'Не указано время',
|
||||
NO_MASTER_SELF_BOOK: 'Нельзя записать к самому себе',
|
||||
NO_ORDER_IN_PAST: 'Нельзя создать запись на время в прошлом',
|
||||
NO_ORDER_OUT_OF_SLOT: 'Время заказа выходит за пределы слота',
|
||||
NO_PERMISSION: 'Нет доступа',
|
||||
NOT_FOUND_CLIENT: 'Клиент не найден',
|
||||
NOT_FOUND_MASTER: 'Мастер не найден',
|
||||
ORDER_IN_PAST: 'Нельзя создать запись на время в прошлом',
|
||||
ORDER_OUT_OF_SLOT: 'Время заказа выходит за пределы слота',
|
||||
OVERLAPPING_TIME: 'Время пересекается с другими заказами',
|
||||
SLOT_CLOSED: 'Слот закрыт',
|
||||
};
|
||||
@ -44,7 +45,6 @@ export class OrdersService extends BaseService {
|
||||
if (!variables.input.slot) throw new Error(ERRORS.MISSING_SLOT);
|
||||
if (!variables.input.services?.length) throw new Error(ERRORS.MISSING_SERVICES);
|
||||
if (!variables.input.services[0]) throw new Error(ERRORS.MISSING_SERVICE_ID);
|
||||
if (!variables.input.datetime_start) throw new Error(ERRORS.MISSING_START_TIME);
|
||||
if (!variables.input.client) throw new Error(ERRORS.MISSING_CLIENT);
|
||||
|
||||
const servicesService = new ServicesService(this._user);
|
||||
@ -163,8 +163,12 @@ export class OrdersService extends BaseService {
|
||||
if (!services?.length) throw new Error(ERRORS.MISSING_SERVICES);
|
||||
|
||||
// Проверка корректности времени заказа.
|
||||
if (!datetime_start || !datetime_end) {
|
||||
throw new Error(ERRORS.MISSING_TIME);
|
||||
if (!datetime_start) {
|
||||
throw new Error(ERRORS.MISSING_START_TIME);
|
||||
}
|
||||
|
||||
if (!datetime_end) {
|
||||
throw new Error(ERRORS.MISSING_END_TIME);
|
||||
}
|
||||
|
||||
if (new Date(datetime_end) <= new Date(datetime_start)) {
|
||||
@ -173,7 +177,7 @@ export class OrdersService extends BaseService {
|
||||
|
||||
// Проверка, что заказ не создается на время в прошлом
|
||||
if (isBeforeNow(datetime_start, 'minute')) {
|
||||
throw new Error(ERRORS.ORDER_IN_PAST);
|
||||
throw new Error(ERRORS.NO_ORDER_IN_PAST);
|
||||
}
|
||||
|
||||
const slotService = new SlotsService(this._user);
|
||||
@ -187,7 +191,7 @@ export class OrdersService extends BaseService {
|
||||
new Date(datetime_start) < new Date(slot.datetime_start) ||
|
||||
new Date(datetime_end) > new Date(slot.datetime_end)
|
||||
) {
|
||||
throw new Error(ERRORS.ORDER_OUT_OF_SLOT);
|
||||
throw new Error(ERRORS.NO_ORDER_OUT_OF_SLOT);
|
||||
}
|
||||
|
||||
// 1. Слот не должен быть закрыт
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user