719 lines
20 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 { ERRORS, SlotsService } from './slots';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
vi.mock('../apollo/client');
vi.mock('./services');
vi.mock('../config/env', () => {
return {
env: {
BOT_TOKEN: 'test',
LOGIN_GRAPHQL: 'test',
PASSWORD_GRAPHQL: 'test',
URL_GRAPHQL: 'test',
},
};
});
const mockGetClientWithToken = vi.mocked(getClientWithToken);
describe('SlotsService', () => {
let slotsService;
const mockUser = { telegramId: 123_456_789 };
const mockCustomer = {
documentId: 'customer-123',
firstName: 'John',
lastName: 'Doe',
telegramId: 123_456_789,
};
const mockSlot = {
datetime_end: '2024-01-01T11:00:00Z',
datetime_start: '2024-01-01T10:00:00Z',
documentId: 'slot-123',
master: mockCustomer,
orders: [],
state: GQL.Enum_Slot_State.Open,
};
const mockGetCustomerResult = {
data: {
customers: [mockCustomer],
},
};
const mockGetSlotResult = {
data: {
slot: mockSlot,
},
};
beforeEach(() => {
slotsService = new SlotsService(mockUser);
vi.clearAllMocks();
});
afterEach(() => {
vi.restoreAllMocks();
});
describe('updateSlot', () => {
const mockVariables = {
data: {
datetime_end: '2024-01-01T11:00:00Z',
datetime_start: '2024-01-01T10:00:00Z',
state: GQL.Enum_Slot_State.Open,
},
documentId: 'slot-123',
};
const mockMutationResult = {
data: {
updateSlot: mockSlot,
},
errors: undefined,
};
it('should successfully update slot when user has permission', async () => {
const mockMutate = vi.fn().mockResolvedValue(mockMutationResult);
const mockQuery = vi.fn().mockImplementation(({ query }) => {
if (query === GQL.GetCustomerDocument) {
return Promise.resolve(mockGetCustomerResult);
}
if (query === GQL.GetSlotDocument) {
return Promise.resolve(mockGetSlotResult);
}
if (query === GQL.GetSlotsDocument) {
return Promise.resolve({ data: { slots: [] } });
}
return Promise.resolve({ data: {} });
});
mockGetClientWithToken.mockResolvedValue({
mutate: mockMutate,
query: mockQuery,
});
const result = slotsService.updateSlot(mockVariables);
await expect(result).resolves.toBe(mockMutationResult.data);
});
it('should successfully update slot when time is not changing', async () => {
const variablesWithoutTime = {
data: {
state: GQL.Enum_Slot_State.Closed,
},
documentId: 'slot-123',
};
const mockMutate = vi.fn().mockResolvedValue(mockMutationResult);
const mockQuery = vi.fn().mockImplementation(({ query }) => {
if (query === GQL.GetCustomerDocument) {
return Promise.resolve(mockGetCustomerResult);
}
if (query === GQL.GetSlotDocument) {
return Promise.resolve(mockGetSlotResult);
}
return Promise.resolve({ data: {} });
});
mockGetClientWithToken.mockResolvedValue({
mutate: mockMutate,
query: mockQuery,
});
const result = slotsService.updateSlot(variablesWithoutTime);
await expect(result).resolves.toBe(mockMutationResult.data);
});
it('should throw error when user does not have permission', async () => {
const unrelatedCustomer = {
...mockCustomer,
documentId: 'different-customer-123',
};
const mockQuery = vi.fn().mockImplementation(({ query }) => {
if (query === GQL.GetCustomerDocument) {
return Promise.resolve({
data: { customers: [unrelatedCustomer] },
});
}
if (query === GQL.GetSlotDocument) {
return Promise.resolve({
data: { slot: mockSlot }, // slot принадлежит другому пользователю
});
}
return Promise.resolve({ data: {} });
});
mockGetClientWithToken.mockResolvedValue({
mutate: vi.fn(),
query: mockQuery,
});
const result = slotsService.updateSlot(mockVariables);
await expect(result).rejects.toThrow(ERRORS.NO_PERMISSION);
});
it('should throw error when slot does not exist', async () => {
const mockQuery = vi.fn().mockImplementation(({ query }) => {
if (query === GQL.GetCustomerDocument) {
return Promise.resolve(mockGetCustomerResult);
}
if (query === GQL.GetSlotDocument) {
return Promise.resolve({
data: { slot: null }, // slot не найден
});
}
return Promise.resolve({ data: {} });
});
mockGetClientWithToken.mockResolvedValue({
mutate: vi.fn(),
query: mockQuery,
});
const result = slotsService.updateSlot(mockVariables);
await expect(result).rejects.toThrow(ERRORS.SLOT_NOT_FOUND);
});
it('should throw error when customer is not found', async () => {
const mockQuery = vi.fn().mockImplementation(({ query }) => {
if (query === GQL.GetCustomerDocument) {
return Promise.resolve({
data: { customers: [] }, // пользователь не найден
});
}
return Promise.resolve({ data: {} });
});
mockGetClientWithToken.mockResolvedValue({
mutate: vi.fn(),
query: mockQuery,
});
const result = slotsService.updateSlot(mockVariables);
await expect(result).rejects.toThrow('Customer not found');
});
it('should throw error when datetime_start is missing', async () => {
const variablesWithMissingStart = {
data: {
datetime_end: '2024-01-01T11:00:00Z',
},
documentId: 'slot-123',
};
const slotWithoutStart = {
...mockSlot,
datetime_start: null,
};
const mockQuery = vi.fn().mockImplementation(({ query }) => {
if (query === GQL.GetCustomerDocument) {
return Promise.resolve(mockGetCustomerResult);
}
if (query === GQL.GetSlotDocument) {
return Promise.resolve({
data: { slot: slotWithoutStart },
});
}
return Promise.resolve({ data: {} });
});
mockGetClientWithToken.mockResolvedValue({
mutate: vi.fn(),
query: mockQuery,
});
const result = slotsService.updateSlot(variablesWithMissingStart);
await expect(result).rejects.toThrow(ERRORS.MISSING_DATETIME_START);
});
it('should throw error when datetime_end is missing', async () => {
const variablesWithMissingEnd = {
data: {
datetime_start: '2024-01-01T10:00:00Z',
},
documentId: 'slot-123',
};
const slotWithoutEnd = {
...mockSlot,
datetime_end: null,
};
const mockQuery = vi.fn().mockImplementation(({ query }) => {
if (query === GQL.GetCustomerDocument) {
return Promise.resolve(mockGetCustomerResult);
}
if (query === GQL.GetSlotDocument) {
return Promise.resolve({
data: { slot: slotWithoutEnd },
});
}
return Promise.resolve({ data: {} });
});
mockGetClientWithToken.mockResolvedValue({
mutate: vi.fn(),
query: mockQuery,
});
const result = slotsService.updateSlot(variablesWithMissingEnd);
await expect(result).rejects.toThrow(ERRORS.MISSING_DATETIME_END);
});
it('should throw error when datetime_end is before datetime_start', async () => {
const variablesWithInvalidTime = {
data: {
datetime_end: '2024-01-01T10:00:00Z',
datetime_start: '2024-01-01T11:00:00Z',
},
documentId: 'slot-123',
};
const mockQuery = vi.fn().mockImplementation(({ query }) => {
if (query === GQL.GetCustomerDocument) {
return Promise.resolve(mockGetCustomerResult);
}
if (query === GQL.GetSlotDocument) {
return Promise.resolve(mockGetSlotResult);
}
return Promise.resolve({ data: {} });
});
mockGetClientWithToken.mockResolvedValue({
mutate: vi.fn(),
query: mockQuery,
});
const result = slotsService.updateSlot(variablesWithInvalidTime);
await expect(result).rejects.toThrow(ERRORS.INVALID_TIME);
});
it('should throw error when datetime_end equals datetime_start', async () => {
const variablesWithEqualTime = {
data: {
datetime_end: '2024-01-01T10:00:00Z',
datetime_start: '2024-01-01T10:00:00Z',
},
documentId: 'slot-123',
};
const mockQuery = vi.fn().mockImplementation(({ query }) => {
if (query === GQL.GetCustomerDocument) {
return Promise.resolve(mockGetCustomerResult);
}
if (query === GQL.GetSlotDocument) {
return Promise.resolve(mockGetSlotResult);
}
return Promise.resolve({ data: {} });
});
mockGetClientWithToken.mockResolvedValue({
mutate: vi.fn(),
query: mockQuery,
});
const result = slotsService.updateSlot(variablesWithEqualTime);
await expect(result).rejects.toThrow(ERRORS.INVALID_TIME);
});
it('should throw error when slot has scheduled orders', async () => {
const slotWithScheduledOrders = {
...mockSlot,
orders: [
{
datetime_end: '2024-01-01T10:30:00Z',
datetime_start: '2024-01-01T10:00:00Z',
documentId: 'order-123',
state: GQL.Enum_Order_State.Scheduled,
},
],
};
const mockQuery = vi.fn().mockImplementation(({ query }) => {
if (query === GQL.GetCustomerDocument) {
return Promise.resolve(mockGetCustomerResult);
}
if (query === GQL.GetSlotDocument) {
return Promise.resolve({
data: { slot: slotWithScheduledOrders },
});
}
return Promise.resolve({ data: {} });
});
mockGetClientWithToken.mockResolvedValue({
mutate: vi.fn(),
query: mockQuery,
});
const result = slotsService.updateSlot(mockVariables);
await expect(result).rejects.toThrow(ERRORS.HAS_ORDERS);
});
it('should throw error when slot has approved orders', async () => {
const slotWithApprovedOrders = {
...mockSlot,
orders: [
{
datetime_end: '2024-01-01T10:30:00Z',
datetime_start: '2024-01-01T10:00:00Z',
documentId: 'order-123',
state: GQL.Enum_Order_State.Approved,
},
],
};
const mockQuery = vi.fn().mockImplementation(({ query }) => {
if (query === GQL.GetCustomerDocument) {
return Promise.resolve(mockGetCustomerResult);
}
if (query === GQL.GetSlotDocument) {
return Promise.resolve({
data: { slot: slotWithApprovedOrders },
});
}
return Promise.resolve({ data: {} });
});
mockGetClientWithToken.mockResolvedValue({
mutate: vi.fn(),
query: mockQuery,
});
const result = slotsService.updateSlot(mockVariables);
await expect(result).rejects.toThrow(ERRORS.HAS_ORDERS);
});
it('should throw error when slot has completed orders', async () => {
const slotWithCompletedOrders = {
...mockSlot,
orders: [
{
datetime_end: '2024-01-01T10:30:00Z',
datetime_start: '2024-01-01T10:00:00Z',
documentId: 'order-123',
state: GQL.Enum_Order_State.Completed,
},
],
};
const mockQuery = vi.fn().mockImplementation(({ query }) => {
if (query === GQL.GetCustomerDocument) {
return Promise.resolve(mockGetCustomerResult);
}
if (query === GQL.GetSlotDocument) {
return Promise.resolve({
data: { slot: slotWithCompletedOrders },
});
}
return Promise.resolve({ data: {} });
});
mockGetClientWithToken.mockResolvedValue({
mutate: vi.fn(),
query: mockQuery,
});
const result = slotsService.updateSlot(mockVariables);
await expect(result).rejects.toThrow(ERRORS.HAS_ORDERS);
});
it('should throw error when slot has cancelled orders', async () => {
const slotWithCancelledOrders = {
...mockSlot,
orders: [
{
datetime_end: '2024-01-01T10:30:00Z',
datetime_start: '2024-01-01T10:00:00Z',
documentId: 'order-123',
state: GQL.Enum_Order_State.Cancelled,
},
],
};
const mockQuery = vi.fn().mockImplementation(({ query }) => {
if (query === GQL.GetCustomerDocument) {
return Promise.resolve(mockGetCustomerResult);
}
if (query === GQL.GetSlotDocument) {
return Promise.resolve({
data: { slot: slotWithCancelledOrders },
});
}
return Promise.resolve({ data: {} });
});
mockGetClientWithToken.mockResolvedValue({
mutate: vi.fn(),
query: mockQuery,
});
const result = slotsService.updateSlot(mockVariables);
await expect(result).rejects.toThrow(ERRORS.HAS_ORDERS);
});
it('should allow update when slot has non-forbidden order states', async () => {
const slotWithNonForbiddenOrders = {
...mockSlot,
orders: [
{
datetime_end: '2024-01-01T10:30:00Z',
datetime_start: '2024-01-01T10:00:00Z',
documentId: 'order-123',
state: GQL.Enum_Order_State.Draft, // не запрещенное состояние
},
],
};
const mockMutate = vi.fn().mockResolvedValue(mockMutationResult);
const mockQuery = vi.fn().mockImplementation(({ query }) => {
if (query === GQL.GetCustomerDocument) {
return Promise.resolve(mockGetCustomerResult);
}
if (query === GQL.GetSlotDocument) {
return Promise.resolve({
data: { slot: slotWithNonForbiddenOrders },
});
}
if (query === GQL.GetSlotsDocument) {
return Promise.resolve({ data: { slots: [] } }); // нет пересекающихся слотов
}
return Promise.resolve({ data: {} });
});
mockGetClientWithToken.mockResolvedValue({
mutate: mockMutate,
query: mockQuery,
});
const result = slotsService.updateSlot(mockVariables);
await expect(result).resolves.toBe(mockMutationResult.data);
});
it('should throw error when time overlaps with other slots', async () => {
const overlappingSlot = {
datetime_end: '2024-01-01T11:30:00Z',
datetime_start: '2024-01-01T10:30:00Z',
documentId: 'slot-456',
master: mockCustomer,
orders: [],
state: GQL.Enum_Slot_State.Open,
};
const mockQuery = vi.fn().mockImplementation(({ query }) => {
if (query === GQL.GetCustomerDocument) {
return Promise.resolve(mockGetCustomerResult);
}
if (query === GQL.GetSlotDocument) {
return Promise.resolve(mockGetSlotResult);
}
if (query === GQL.GetSlotsDocument) {
return Promise.resolve({
data: { slots: [overlappingSlot] },
}); // есть пересекающиеся слоты
}
return Promise.resolve({ data: {} });
});
mockGetClientWithToken.mockResolvedValue({
mutate: vi.fn(),
query: mockQuery,
});
const result = slotsService.updateSlot(mockVariables);
await expect(result).rejects.toThrow(ERRORS.OVERLAPPING_TIME);
});
it('should allow update when time does not overlap with other slots', async () => {
const mockMutate = vi.fn().mockResolvedValue(mockMutationResult);
const mockQuery = vi.fn().mockImplementation(({ query }) => {
if (query === GQL.GetCustomerDocument) {
return Promise.resolve(mockGetCustomerResult);
}
if (query === GQL.GetSlotDocument) {
return Promise.resolve(mockGetSlotResult);
}
if (query === GQL.GetSlotsDocument) {
return Promise.resolve({
data: { slots: [] },
}); // нет пересекающихся слотов
}
return Promise.resolve({ data: {} });
});
mockGetClientWithToken.mockResolvedValue({
mutate: mockMutate,
query: mockQuery,
});
const result = slotsService.updateSlot(mockVariables);
await expect(result).resolves.toBe(mockMutationResult.data);
});
});
describe('deleteSlot', () => {
const mockVariables = {
documentId: 'slot-123',
};
const mockMutationResult = {
data: {
deleteSlot: {
documentId: 'slot-123',
},
},
errors: undefined,
};
it('should successfully delete slot when no orders', async () => {
const mockMutate = vi.fn().mockResolvedValue(mockMutationResult);
const mockQuery = vi.fn().mockImplementation(({ query }) => {
if (query === GQL.GetCustomerDocument) {
return Promise.resolve(mockGetCustomerResult);
}
if (query === GQL.GetSlotDocument) {
return Promise.resolve(mockGetSlotResult);
}
return Promise.resolve({ data: {} });
});
mockGetClientWithToken.mockResolvedValue({
mutate: mockMutate,
query: mockQuery,
});
const result = slotsService.deleteSlot(mockVariables);
await expect(result).resolves.toBe(mockMutationResult.data);
});
it('should throw error when slot has orders', async () => {
const slotWithOrders = {
...mockSlot,
orders: [
{
datetime_end: '2024-01-01T11:00:00Z',
datetime_start: '2024-01-01T10:00:00Z',
documentId: 'order-123',
state: GQL.Enum_Order_State.Scheduled,
},
],
};
const mockQuery = vi.fn().mockImplementation(({ query }) => {
if (query === GQL.GetCustomerDocument) {
return Promise.resolve(mockGetCustomerResult);
}
if (query === GQL.GetSlotDocument) {
return Promise.resolve({
data: { slot: slotWithOrders }, // slot с заказами
});
}
return Promise.resolve({ data: {} });
});
mockGetClientWithToken.mockResolvedValue({
mutate: vi.fn(),
query: mockQuery,
});
const result = slotsService.deleteSlot(mockVariables);
await expect(result).rejects.toThrow(ERRORS.HAS_ORDERS);
});
it('should throw error when user does not have permission', async () => {
const unrelatedCustomer = {
...mockCustomer,
documentId: 'different-customer-123',
};
const mockQuery = vi.fn().mockImplementation(({ query }) => {
if (query === GQL.GetCustomerDocument) {
return Promise.resolve({
data: { customers: [unrelatedCustomer] },
});
}
if (query === GQL.GetSlotDocument) {
return Promise.resolve({
data: { slot: mockSlot }, // slot принадлежит другому пользователю
});
}
return Promise.resolve({ data: {} });
});
mockGetClientWithToken.mockResolvedValue({
mutate: vi.fn(),
query: mockQuery,
});
const result = slotsService.deleteSlot(mockVariables);
await expect(result).rejects.toThrow(ERRORS.NO_PERMISSION);
});
});
});