vchikalkin 269dabe067 feat(orders): implement updateOrder functionality with comprehensive validation tests
- Added updateOrder method in OrdersService with checks for permissions, order state, and datetime validation.
- Implemented tests for various scenarios including successful updates, permission errors, and validation failures.
- Enhanced error handling for overlapping time and invalid state changes.
- Updated GraphQL operations to support sorting in GetOrders query.
2025-08-11 14:38:33 +03:00

1465 lines
40 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 datetime_end is missing', async () => {
const variablesWithoutEnd = {
input: {
client: 'customer-123',
datetime_start: now.toISOString(),
services: ['service-123'],
slot: 'slot-123',
},
};
const result = ordersService.createOrder(variablesWithoutEnd);
await expect(result).rejects.toThrow(ERRORS.MISSING_END_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,
},
},
});
});
});
describe('updateOrder', () => {
const mockVariables = {
data: {
datetime_end: now.add(2, 'hour').toISOString(),
datetime_start: now.add(1, 'hour').toISOString(),
state: GQL.Enum_Order_State.Approved,
},
documentId: 'order-123',
};
const mockMutationResult = {
data: {
updateOrder: {
...mockOrder,
...mockVariables.data,
},
},
errors: undefined,
};
it('should successfully update order by master', async () => {
const mockMutate = vi.fn().mockResolvedValue(mockMutationResult);
const mockQuery = vi.fn().mockImplementation(({ query }) => {
if (query === GQL.GetOrderDocument) {
return Promise.resolve({
data: {
order: {
...mockOrder,
slot: { ...mockSlot, master: mockMaster },
},
},
});
}
if (query === GQL.GetOrdersDocument) {
return Promise.resolve({ data: { orders: [] } });
}
return Promise.resolve({ data: {} });
});
mockGetClientWithToken.mockResolvedValue({
mutate: mockMutate,
query: mockQuery,
});
// Мокаем _getUser для мастера
vi.spyOn(ordersService, '_getUser').mockResolvedValue({
customer: mockMaster,
});
const result = await ordersService.updateOrder(mockVariables);
expect(result).toBe(mockMutationResult.data);
expect(mockMutate).toHaveBeenCalledWith({
mutation: GQL.UpdateOrderDocument,
variables: {
...mockVariables,
data: {
...mockVariables.data,
order_number: undefined,
},
},
});
});
it('should successfully update order by client (cancelling)', async () => {
const mockMutate = vi.fn().mockResolvedValue(mockMutationResult);
const mockQuery = vi.fn().mockImplementation(({ query }) => {
if (query === GQL.GetOrderDocument) {
return Promise.resolve({
data: {
order: {
...mockOrder,
client: mockCustomer,
slot: { ...mockSlot, master: mockMaster },
},
},
});
}
return Promise.resolve({ data: {} });
});
mockGetClientWithToken.mockResolvedValue({
mutate: mockMutate,
query: mockQuery,
});
// Мокаем _getUser для клиента
vi.spyOn(ordersService, '_getUser').mockResolvedValue({
customer: mockCustomer,
});
const clientVariables = {
...mockVariables,
data: {
state: GQL.Enum_Order_State.Cancelling,
},
};
const result = await ordersService.updateOrder(clientVariables);
expect(result).toBe(mockMutationResult.data);
});
it('should throw error when order is not found', async () => {
const mockQuery = vi.fn().mockResolvedValue({
data: { order: null },
});
mockGetClientWithToken.mockResolvedValue({
mutate: vi.fn(),
query: mockQuery,
});
// Мокаем _getUser для мастера
vi.spyOn(ordersService, '_getUser').mockResolvedValue({
customer: mockMaster,
});
const result = ordersService.updateOrder(mockVariables);
await expect(result).rejects.toThrow(ERRORS.MISSING_ORDER);
});
it('should throw error when user has no permission', async () => {
const unauthorizedUser = {
...mockCustomer,
documentId: 'unauthorized-user',
};
const mockQuery = vi.fn().mockResolvedValue({
data: {
order: {
...mockOrder,
client: mockCustomer,
slot: { ...mockSlot, master: mockMaster },
},
},
});
mockGetClientWithToken.mockResolvedValue({
mutate: vi.fn(),
query: mockQuery,
});
// Мокаем _getUser для неавторизованного пользователя
vi.spyOn(ordersService, '_getUser').mockResolvedValue({
customer: unauthorizedUser,
});
const result = ordersService.updateOrder(mockVariables);
await expect(result).rejects.toThrow(ERRORS.NO_PERMISSION);
});
it('should throw error when client tries to change more than one field', async () => {
const mockQuery = vi.fn().mockResolvedValue({
data: {
order: {
...mockOrder,
client: mockCustomer,
slot: { ...mockSlot, master: mockMaster },
},
},
});
mockGetClientWithToken.mockResolvedValue({
mutate: vi.fn(),
query: mockQuery,
});
// Мокаем _getUser для клиента
vi.spyOn(ordersService, '_getUser').mockResolvedValue({
customer: mockCustomer,
});
const clientVariables = {
...mockVariables,
data: {
datetime_start: now.add(1, 'hour').toISOString(),
state: GQL.Enum_Order_State.Cancelling,
},
};
const result = ordersService.updateOrder(clientVariables);
await expect(result).rejects.toThrow(ERRORS.NO_PERMISSION);
});
it('should throw error when client tries to change state to non-cancelling', async () => {
const mockQuery = vi.fn().mockResolvedValue({
data: {
order: {
...mockOrder,
client: mockCustomer,
slot: { ...mockSlot, master: mockMaster },
},
},
});
mockGetClientWithToken.mockResolvedValue({
mutate: vi.fn(),
query: mockQuery,
});
// Мокаем _getUser для клиента
vi.spyOn(ordersService, '_getUser').mockResolvedValue({
customer: mockCustomer,
});
const clientVariables = {
...mockVariables,
data: {
state: GQL.Enum_Order_State.Approved,
},
};
const result = ordersService.updateOrder(clientVariables);
await expect(result).rejects.toThrow(ERRORS.NO_PERMISSION);
});
it('should throw error when trying to change client', async () => {
const mockQuery = vi.fn().mockResolvedValue({
data: {
order: {
...mockOrder,
slot: { ...mockSlot, master: mockMaster },
},
},
});
mockGetClientWithToken.mockResolvedValue({
mutate: vi.fn(),
query: mockQuery,
});
// Мокаем _getUser для мастера
vi.spyOn(ordersService, '_getUser').mockResolvedValue({
customer: mockMaster,
});
const variablesWithClient = {
...mockVariables,
data: {
...mockVariables.data,
client: 'new-client-id',
},
};
const result = ordersService.updateOrder(variablesWithClient);
await expect(result).rejects.toThrow(ERRORS.NO_PERMISSION);
});
it('should throw error when trying to change services', async () => {
const mockQuery = vi.fn().mockResolvedValue({
data: {
order: {
...mockOrder,
slot: { ...mockSlot, master: mockMaster },
},
},
});
mockGetClientWithToken.mockResolvedValue({
mutate: vi.fn(),
query: mockQuery,
});
// Мокаем _getUser для мастера
vi.spyOn(ordersService, '_getUser').mockResolvedValue({
customer: mockMaster,
});
const variablesWithServices = {
...mockVariables,
data: {
...mockVariables.data,
services: ['new-service-id'],
},
};
const result = ordersService.updateOrder(variablesWithServices);
await expect(result).rejects.toThrow(ERRORS.NO_PERMISSION);
});
it('should throw error when trying to change slot', async () => {
const mockQuery = vi.fn().mockResolvedValue({
data: {
order: {
...mockOrder,
slot: { ...mockSlot, master: mockMaster },
},
},
});
mockGetClientWithToken.mockResolvedValue({
mutate: vi.fn(),
query: mockQuery,
});
// Мокаем _getUser для мастера
vi.spyOn(ordersService, '_getUser').mockResolvedValue({
customer: mockMaster,
});
const variablesWithSlot = {
...mockVariables,
data: {
...mockVariables.data,
slot: 'new-slot-id',
},
};
const result = ordersService.updateOrder(variablesWithSlot);
await expect(result).rejects.toThrow(ERRORS.NO_PERMISSION);
});
it('should throw error when order slot is not found', async () => {
const mockQuery = vi.fn().mockResolvedValue({
data: {
order: {
...mockOrder,
client: mockCustomer,
slot: null,
},
},
});
mockGetClientWithToken.mockResolvedValue({
mutate: vi.fn(),
query: mockQuery,
});
// Мокаем _getUser для клиента заказа
vi.spyOn(ordersService, '_getUser').mockResolvedValue({
customer: mockCustomer,
});
const variablesWithSingleField = {
...mockVariables,
data: {
state: GQL.Enum_Order_State.Cancelling,
},
};
const result = ordersService.updateOrder(variablesWithSingleField);
await expect(result).rejects.toThrow(ERRORS.NOT_FOUND_ORDER_SLOT);
});
it('should throw error when trying to change completed order state', async () => {
const completedOrder = {
...mockOrder,
state: GQL.Enum_Order_State.Completed,
};
const mockQuery = vi.fn().mockResolvedValue({
data: {
order: completedOrder,
},
});
mockGetClientWithToken.mockResolvedValue({
mutate: vi.fn(),
query: mockQuery,
});
// Мокаем _getUser для мастера
vi.spyOn(ordersService, '_getUser').mockResolvedValue({
customer: mockMaster,
});
const result = ordersService.updateOrder(mockVariables);
await expect(result).rejects.toThrow(ERRORS.NO_CHANGE_STATE_COMPLETED);
});
it('should throw error when time is invalid', async () => {
const mockQuery = vi.fn().mockImplementation(({ query }) => {
if (query === GQL.GetOrderDocument) {
return Promise.resolve({
data: {
order: {
...mockOrder,
slot: { ...mockSlot, master: mockMaster },
},
},
});
}
return Promise.resolve({ data: {} });
});
mockGetClientWithToken.mockResolvedValue({
mutate: vi.fn(),
query: mockQuery,
});
// Мокаем _getUser для мастера
vi.spyOn(ordersService, '_getUser').mockResolvedValue({
customer: mockMaster,
});
const variablesWithInvalidTime = {
...mockVariables,
data: {
datetime_end: now.add(1, 'hour').toISOString(),
datetime_start: now.add(2, 'hour').toISOString(), // конец раньше начала
},
};
const result = ordersService.updateOrder(variablesWithInvalidTime);
await expect(result).rejects.toThrow(ERRORS.INVALID_TIME);
});
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.GetOrderDocument) {
return Promise.resolve({
data: {
order: {
...mockOrder,
slot: { ...mockSlot, master: mockMaster },
},
},
});
}
if (query === GQL.GetOrdersDocument) {
return Promise.resolve({
data: { orders: [overlappingOrder] },
});
}
return Promise.resolve({ data: {} });
});
mockGetClientWithToken.mockResolvedValue({
mutate: vi.fn(),
query: mockQuery,
});
// Мокаем _getUser для мастера
vi.spyOn(ordersService, '_getUser').mockResolvedValue({
customer: mockMaster,
});
const result = ordersService.updateOrder(mockVariables);
await expect(result).rejects.toThrow(ERRORS.OVERLAPPING_TIME);
});
it('should successfully complete order and set order number', async () => {
const mockMutate = vi.fn().mockResolvedValue(mockMutationResult);
const mockQuery = vi.fn().mockImplementation(({ query }) => {
if (query === GQL.GetOrderDocument) {
return Promise.resolve({
data: {
order: {
...mockOrder,
client: mockCustomer,
slot: { ...mockSlot, master: mockMaster },
},
},
});
}
if (query === GQL.GetOrdersDocument) {
return Promise.resolve({
data: {
orders: [
{
...mockOrder,
order_number: 5,
},
],
},
});
}
return Promise.resolve({ data: {} });
});
mockGetClientWithToken.mockResolvedValue({
mutate: mockMutate,
query: mockQuery,
});
// Мокаем _getUser для мастера
vi.spyOn(ordersService, '_getUser').mockResolvedValue({
customer: mockMaster,
});
const completeVariables = {
...mockVariables,
data: {
state: GQL.Enum_Order_State.Completed,
},
};
const result = await ordersService.updateOrder(completeVariables);
expect(result).toBe(mockMutationResult.data);
expect(mockMutate).toHaveBeenCalledWith({
mutation: GQL.UpdateOrderDocument,
variables: {
...completeVariables,
data: {
...completeVariables.data,
order_number: 6, // 5 + 1
},
},
});
});
it('should successfully complete order and set order number to 1 when no previous completed orders', async () => {
const mockMutate = vi.fn().mockResolvedValue(mockMutationResult);
const mockQuery = vi.fn().mockImplementation(({ query }) => {
if (query === GQL.GetOrderDocument) {
return Promise.resolve({
data: {
order: {
...mockOrder,
client: mockCustomer,
slot: { ...mockSlot, master: mockMaster },
},
},
});
}
if (query === GQL.GetOrdersDocument) {
return Promise.resolve({
data: {
orders: [], // нет других завершенных заказов
},
});
}
return Promise.resolve({ data: {} });
});
mockGetClientWithToken.mockResolvedValue({
mutate: mockMutate,
query: mockQuery,
});
// Мокаем _getUser для мастера
vi.spyOn(ordersService, '_getUser').mockResolvedValue({
customer: mockMaster,
});
const completeVariables = {
...mockVariables,
data: {
state: GQL.Enum_Order_State.Completed,
},
};
const result = await ordersService.updateOrder(completeVariables);
expect(result).toBe(mockMutationResult.data);
expect(mockMutate).toHaveBeenCalledWith({
mutation: GQL.UpdateOrderDocument,
variables: {
...completeVariables,
data: {
...completeVariables.data,
order_number: undefined,
},
},
});
});
it('should throw error when trying to complete order before start time', async () => {
const pastOrder = {
...mockOrder,
datetime_start: now.subtract(1, 'hour').toISOString(),
};
const mockQuery = vi.fn().mockResolvedValue({
data: {
order: pastOrder,
},
});
mockGetClientWithToken.mockResolvedValue({
mutate: vi.fn(),
query: mockQuery,
});
// Мокаем _getUser для мастера
vi.spyOn(ordersService, '_getUser').mockResolvedValue({
customer: mockMaster,
});
const completeVariables = {
...mockVariables,
data: {
state: GQL.Enum_Order_State.Completed,
},
};
const result = ordersService.updateOrder(completeVariables);
await expect(result).rejects.toThrow(ERRORS.CANNOT_COMPLETE_BEFORE_START);
});
it('should return early when no datetime changes are provided', async () => {
const mockMutate = vi.fn().mockResolvedValue(mockMutationResult);
const mockQuery = vi.fn().mockResolvedValue({
data: {
order: {
...mockOrder,
slot: { ...mockSlot, master: mockMaster },
},
},
});
mockGetClientWithToken.mockResolvedValue({
mutate: mockMutate,
query: mockQuery,
});
// Мокаем _getUser для мастера
vi.spyOn(ordersService, '_getUser').mockResolvedValue({
customer: mockMaster,
});
const variablesWithoutTime = {
...mockVariables,
data: {
state: GQL.Enum_Order_State.Approved,
},
};
const result = await ordersService.updateOrder(variablesWithoutTime);
expect(result).toBe(mockMutationResult.data);
});
});
});