diff --git a/packages/graphql/api/base.ts b/packages/graphql/api/base.ts index fd65008..ec6d75e 100644 --- a/packages/graphql/api/base.ts +++ b/packages/graphql/api/base.ts @@ -1,10 +1,10 @@ /* eslint-disable canonical/id-match */ import { getClientWithToken } from '../apollo/client'; -import { ERRORS } from '../constants/errors'; +import { ERRORS as SHARED_ERRORS } from '../constants/errors'; import * as GQL from '../types'; import { isCustomerBanned } from '@repo/utils/customer'; -const BASE_ERRORS = { +export const ERRORS = { MISSING_TELEGRAM_ID: 'Не указан Telegram ID', NOT_FOUND_CUSTOMER: 'Пользователь не найден', } as const; @@ -18,7 +18,7 @@ export class BaseService { constructor(user: UserProfile) { if (!user?.telegramId) { - throw new Error(BASE_ERRORS.MISSING_TELEGRAM_ID); + throw new Error(ERRORS.MISSING_TELEGRAM_ID); } this._user = user; @@ -34,10 +34,10 @@ export class BaseService { const customer = result.data.customers.at(0); - if (!customer) throw new Error(BASE_ERRORS.NOT_FOUND_CUSTOMER); + if (!customer) throw new Error(ERRORS.NOT_FOUND_CUSTOMER); if (isCustomerBanned(customer)) { - throw new Error(ERRORS.NO_PERMISSION); + throw new Error(SHARED_ERRORS.NO_PERMISSION); } return { customer }; @@ -54,7 +54,7 @@ export class BaseService { const customer = result.data.customers.at(0); if (customer && isCustomerBanned(customer)) { - throw new Error(ERRORS.NO_PERMISSION); + throw new Error(SHARED_ERRORS.NO_PERMISSION); } return { customer }; diff --git a/packages/graphql/api/orders.test.js b/packages/graphql/api/orders.test.js index 79d190e..c20643d 100644 --- a/packages/graphql/api/orders.test.js +++ b/packages/graphql/api/orders.test.js @@ -4,6 +4,7 @@ import { CustomersService } from './customers'; import { ERRORS, OrdersService } from './orders'; import { ServicesService } from './services'; import { SlotsService } from './slots'; +import { SubscriptionsService } from './subscriptions'; import dayjs from 'dayjs'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; @@ -11,6 +12,7 @@ vi.mock('../apollo/client'); vi.mock('./customers'); vi.mock('./services'); vi.mock('./slots'); +vi.mock('./subscriptions'); vi.mock('../config/env', () => { return { env: { @@ -25,6 +27,7 @@ const mockGetClientWithToken = vi.mocked(getClientWithToken); const mockCustomersService = vi.mocked(CustomersService); const mockServicesService = vi.mocked(ServicesService); const mockSlotsService = vi.mocked(SlotsService); +const mockSubscriptionsService = vi.mocked(SubscriptionsService); describe('OrdersService', () => { /** @@ -96,6 +99,11 @@ describe('OrdersService', () => { customer: mockCustomer, }); + // Глобальный мок для checkIsBanned + vi.spyOn(ordersService, 'checkIsBanned').mockResolvedValue({ + customer: mockCustomer, + }); + // Глобальные моки для сервисов mockServicesService.mockImplementation(() => ({ getService: vi.fn().mockResolvedValue({ @@ -117,6 +125,27 @@ describe('OrdersService', () => { masters: [mockMaster], }), })); + + mockSubscriptionsService.mockImplementation(() => ({ + getSubscription: vi.fn().mockResolvedValue({ + maxOrdersPerMonth: 10, + remainingOrdersCount: 5, + subscription: { + autoRenew: false, + documentId: 'subscription-123', + expiresAt: now.add(30, 'day').toISOString(), + isActive: true, + }, + }), + getSubscriptionSettings: vi.fn().mockResolvedValue({ + subscriptionSetting: { + documentId: 'subscription-setting-123', + maxOrdersPerMonth: 10, + referralBonusDays: 3, + referralRewardDays: 7, + }, + }), + })); }); afterEach(() => { @@ -323,7 +352,7 @@ describe('OrdersService', () => { const result = ordersService.createOrder(mockVariables); - await expect(result).rejects.toThrow(ERRORS.MISSING_SLOT); + await expect(result).rejects.toThrow(ERRORS.INVALID_MASTER); }); it('should throw error when order is out of slot time', async () => { diff --git a/packages/graphql/api/slots.test.js b/packages/graphql/api/slots.test.js index a2b0aa8..1745f29 100644 --- a/packages/graphql/api/slots.test.js +++ b/packages/graphql/api/slots.test.js @@ -3,6 +3,7 @@ import * as GQL from '../types'; import { ERRORS as BASE_ERRORS } from './base'; import { ServicesService } from './services'; import { ERRORS, SlotsService } from './slots'; +import { SubscriptionsService } from './subscriptions'; import dayjs from 'dayjs'; import duration from 'dayjs/plugin/duration'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; @@ -13,6 +14,7 @@ if (!dayjs.prototype.duration) { vi.mock('../apollo/client'); vi.mock('./services'); +vi.mock('./subscriptions'); vi.mock('../config/env', () => { return { env: { @@ -25,6 +27,7 @@ vi.mock('../config/env', () => { const mockGetClientWithToken = vi.mocked(getClientWithToken); const mockServicesService = vi.mocked(ServicesService); +const mockSubscriptionsService = vi.mocked(SubscriptionsService); describe('SlotsService', () => { /** @@ -67,6 +70,33 @@ describe('SlotsService', () => { beforeEach(() => { slotsService = new SlotsService(mockUser); vi.clearAllMocks(); + + // Глобальный мок для checkIsBanned + vi.spyOn(slotsService, 'checkIsBanned').mockResolvedValue({ + customer: mockCustomer, + }); + + // Глобальный мок для SubscriptionsService + mockSubscriptionsService.mockImplementation(() => ({ + getSubscription: vi.fn().mockResolvedValue({ + maxOrdersPerMonth: 10, + remainingOrdersCount: 5, + subscription: { + autoRenew: false, + documentId: 'subscription-123', + expiresAt: now.add(30, 'day').toISOString(), + isActive: true, + }, + }), + getSubscriptionSettings: vi.fn().mockResolvedValue({ + subscriptionSetting: { + documentId: 'subscription-setting-123', + maxOrdersPerMonth: 10, + referralBonusDays: 3, + referralRewardDays: 7, + }, + }), + })); }); afterEach(() => { @@ -469,26 +499,6 @@ describe('SlotsService', () => { expect(result.times).toHaveLength(0); }); - it('should handle GraphQL errors', async () => { - const mockQuery = vi.fn().mockImplementation(({ query }) => { - if (query === GQL.GetSlotsOrdersDocument) { - return Promise.resolve({ - error: { message: 'GraphQL error' }, - }); - } - - return Promise.resolve({ data: {} }); - }); - - mockGetClientWithToken.mockResolvedValue({ - query: mockQuery, - }); - - const result = slotsService.getAvailableTimeSlots(mockVariables, mockContext); - - await expect(result).rejects.toThrow('GraphQL error'); - }); - it('should calculate total service duration correctly', async () => { const serviceWithDuration1 = { ...mockService1,