packages/graphql: add slot tests

This commit is contained in:
vchikalkin 2025-08-04 17:59:51 +03:00
parent d8f357b3f8
commit 5ec324d207
4 changed files with 315 additions and 2 deletions

View File

@ -6,7 +6,7 @@ export default defineConfig({
plugins: [tsconfigPaths(), react()],
test: {
environment: 'jsdom',
exclude: ['**/e2e/**', '**/*.spec.ts'],
exclude: ['**/e2e/**', '**/*.spec.ts', '**/node_modules/**'],
include: ['**/*.test.{ts,tsx}'],
},
});

View File

@ -0,0 +1,303 @@
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()
.mockResolvedValueOnce(mockGetCustomerResult)
.mockResolvedValueOnce(mockGetSlotResult);
mockGetClientWithToken.mockResolvedValue({
mutate: mockMutate,
query: mockQuery,
});
const result = slotsService.updateSlot(mockVariables);
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()
.mockResolvedValueOnce({
data: { customers: [unrelatedCustomer] },
})
.mockResolvedValueOnce({
data: { slot: mockSlot }, // slot принадлежит другому пользователю
});
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()
.mockResolvedValueOnce(mockGetCustomerResult)
.mockResolvedValueOnce({
data: { slot: null }, // slot не найден
});
mockGetClientWithToken.mockResolvedValue({
mutate: vi.fn(),
query: mockQuery,
});
const result = slotsService.updateSlot(mockVariables);
await expect(result).rejects.toThrow();
});
it('should throw error when customer is not found', async () => {
const mockQuery = vi.fn().mockResolvedValue({
data: { customers: [] }, // пользователь не найден
});
mockGetClientWithToken.mockResolvedValue({
mutate: vi.fn(),
query: mockQuery,
});
const result = slotsService.updateSlot(mockVariables);
await expect(result).rejects.toThrow('Customer not found');
});
});
describe('checkPermission', () => {
const mockVariables = {
documentId: 'slot-123',
};
it('should not throw error when user has permission', async () => {
const mockQuery = vi
.fn()
.mockResolvedValueOnce(mockGetCustomerResult)
.mockResolvedValueOnce(mockGetSlotResult);
mockGetClientWithToken.mockResolvedValue({
query: mockQuery,
});
const result = slotsService.checkPermission(mockVariables);
await expect(result).resolves.toBeUndefined();
});
it('should throw error when user does not have permission', async () => {
const unrelatedCustomer = {
...mockCustomer,
documentId: 'different-customer-123',
};
const mockQuery = vi
.fn()
.mockResolvedValueOnce({
data: { customers: [unrelatedCustomer] },
})
.mockResolvedValueOnce({
data: { slot: mockSlot }, // slot принадлежит другому пользователю
});
mockGetClientWithToken.mockResolvedValue({
query: mockQuery,
});
const result = slotsService.checkPermission(mockVariables);
await expect(result).rejects.toThrow(ERRORS.NO_PERMISSION);
});
it('should throw error when slot does not exist', async () => {
const mockQuery = vi
.fn()
.mockResolvedValueOnce(mockGetCustomerResult)
.mockResolvedValueOnce({
data: { slot: null }, // slot не найден
});
mockGetClientWithToken.mockResolvedValue({
query: mockQuery,
});
const result = slotsService.checkPermission(mockVariables);
await expect(result).rejects.toThrow();
});
});
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()
.mockResolvedValueOnce(mockGetCustomerResult)
.mockResolvedValue(mockGetSlotResult);
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()
.mockResolvedValueOnce(mockGetCustomerResult)
.mockResolvedValue({
data: { slot: slotWithOrders }, // slot с заказами
});
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()
.mockResolvedValueOnce({
data: { customers: [unrelatedCustomer] },
})
.mockResolvedValueOnce({
data: { slot: mockSlot }, // slot принадлежит другому пользователю
});
mockGetClientWithToken.mockResolvedValue({
mutate: vi.fn(),
query: mockQuery,
});
const result = slotsService.deleteSlot(mockVariables);
await expect(result).rejects.toThrow(ERRORS.NO_PERMISSION);
});
});
});

View File

@ -6,7 +6,7 @@ import { type VariablesOf } from '@graphql-typed-document-node/core';
import { getMinutes } from '@repo/utils/datetime-format';
import dayjs from 'dayjs';
const ERRORS = {
export const ERRORS = {
HAS_ORDERS: 'Slot has orders',
MISSING_DATE: 'Missing date',
MISSING_SERVICE: 'Missing service',

View File

@ -0,0 +1,10 @@
import { defineConfig } from 'vitest/config';
import tsconfigPaths from 'vite-tsconfig-paths';
export default defineConfig({
plugins: [tsconfigPaths()],
test: {
exclude: ['**/e2e/**', '**/*.spec.ts', '**/node_modules/**'],
include: ['**/*.test.{js,ts}'],
},
});