@misterhomer1992/miit-bot-payment 1.1.6 → 2.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/ConfigurationManager.d.ts +64 -0
- package/dist/config/ConfigurationManager.d.ts.map +1 -0
- package/dist/config/ConfigurationManager.js +144 -0
- package/dist/config/ConfigurationManager.js.map +1 -0
- package/dist/config/defaults.d.ts +18 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/defaults.js +26 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/environment.d.ts +38 -0
- package/dist/config/environment.d.ts.map +1 -0
- package/dist/config/environment.js +91 -0
- package/dist/config/environment.js.map +1 -0
- package/dist/config/index.d.ts +5 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +18 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/types.d.ts +53 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +3 -0
- package/dist/config/types.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -1
- package/dist/modules/cache/InMemoryCache.d.ts +17 -0
- package/dist/modules/cache/InMemoryCache.d.ts.map +1 -0
- package/dist/modules/cache/InMemoryCache.js +77 -0
- package/dist/modules/cache/InMemoryCache.js.map +1 -0
- package/dist/modules/cache/index.d.ts +3 -0
- package/dist/modules/cache/index.d.ts.map +1 -0
- package/dist/modules/cache/index.js +19 -0
- package/dist/modules/cache/index.js.map +1 -0
- package/dist/modules/cache/types.d.ts +52 -0
- package/dist/modules/cache/types.d.ts.map +1 -0
- package/dist/modules/cache/types.js +3 -0
- package/dist/modules/cache/types.js.map +1 -0
- package/dist/modules/errors/index.d.ts +2 -0
- package/dist/modules/errors/index.d.ts.map +1 -0
- package/dist/modules/errors/index.js +19 -0
- package/dist/modules/errors/index.js.map +1 -0
- package/dist/modules/errors/types.d.ts +112 -0
- package/dist/modules/errors/types.d.ts.map +1 -0
- package/dist/modules/errors/types.js +174 -0
- package/dist/modules/errors/types.js.map +1 -0
- package/dist/modules/payments/api.d.ts +63 -1
- package/dist/modules/payments/api.d.ts.map +1 -1
- package/dist/modules/payments/api.js +103 -1
- package/dist/modules/payments/api.js.map +1 -1
- package/dist/modules/payments/const.d.ts.map +1 -1
- package/dist/modules/payments/const.js +1 -0
- package/dist/modules/payments/const.js.map +1 -1
- package/dist/modules/payments/index.d.ts +8 -0
- package/dist/modules/payments/index.d.ts.map +1 -1
- package/dist/modules/payments/index.js +8 -0
- package/dist/modules/payments/index.js.map +1 -1
- package/dist/modules/payments/service.d.ts +42 -2
- package/dist/modules/payments/service.d.ts.map +1 -1
- package/dist/modules/payments/service.js +132 -3
- package/dist/modules/payments/service.js.map +1 -1
- package/dist/modules/payments/subscription-check-webhook.handler.d.ts +85 -0
- package/dist/modules/payments/subscription-check-webhook.handler.d.ts.map +1 -0
- package/dist/modules/payments/subscription-check-webhook.handler.js +155 -0
- package/dist/modules/payments/subscription-check-webhook.handler.js.map +1 -0
- package/dist/modules/payments/subscription-check-webhook.service.d.ts +59 -0
- package/dist/modules/payments/subscription-check-webhook.service.d.ts.map +1 -0
- package/dist/modules/payments/subscription-check-webhook.service.js +330 -0
- package/dist/modules/payments/subscription-check-webhook.service.js.map +1 -0
- package/dist/modules/payments/subscription-check-webhook.types.d.ts +25 -0
- package/dist/modules/payments/subscription-check-webhook.types.d.ts.map +1 -0
- package/dist/modules/payments/subscription-check-webhook.types.js +3 -0
- package/dist/modules/payments/subscription-check-webhook.types.js.map +1 -0
- package/dist/modules/payments/types.d.ts +69 -2
- package/dist/modules/payments/types.d.ts.map +1 -1
- package/dist/modules/payments/utils.d.ts +151 -5
- package/dist/modules/payments/utils.d.ts.map +1 -1
- package/dist/modules/payments/utils.js +253 -9
- package/dist/modules/payments/utils.js.map +1 -1
- package/dist/modules/payments/wayforpay.service.d.ts +39 -0
- package/dist/modules/payments/wayforpay.service.d.ts.map +1 -0
- package/dist/modules/payments/wayforpay.service.js +217 -0
- package/dist/modules/payments/wayforpay.service.js.map +1 -0
- package/dist/modules/payments/wayforpay.types.d.ts +115 -0
- package/dist/modules/payments/wayforpay.types.d.ts.map +1 -0
- package/dist/modules/payments/wayforpay.types.js +3 -0
- package/dist/modules/payments/wayforpay.types.js.map +1 -0
- package/dist/modules/payments/webhook.handler.d.ts +98 -0
- package/dist/modules/payments/webhook.handler.d.ts.map +1 -0
- package/dist/modules/payments/webhook.handler.js +153 -0
- package/dist/modules/payments/webhook.handler.js.map +1 -0
- package/dist/modules/payments/webhook.service.d.ts +99 -0
- package/dist/modules/payments/webhook.service.d.ts.map +1 -0
- package/dist/modules/payments/webhook.service.js +672 -0
- package/dist/modules/payments/webhook.service.js.map +1 -0
- package/dist/modules/payments/webhook.types.d.ts +35 -0
- package/dist/modules/payments/webhook.types.d.ts.map +1 -0
- package/dist/modules/payments/webhook.types.js +3 -0
- package/dist/modules/payments/webhook.types.js.map +1 -0
- package/dist/modules/subscription/change.service.d.ts +80 -0
- package/dist/modules/subscription/change.service.d.ts.map +1 -0
- package/dist/modules/subscription/change.service.js +226 -0
- package/dist/modules/subscription/change.service.js.map +1 -0
- package/dist/modules/subscription/index.d.ts +2 -0
- package/dist/modules/subscription/index.d.ts.map +1 -1
- package/dist/modules/subscription/index.js +2 -0
- package/dist/modules/subscription/index.js.map +1 -1
- package/dist/modules/subscription/service.d.ts +8 -1
- package/dist/modules/subscription/service.d.ts.map +1 -1
- package/dist/modules/subscription/service.js +59 -2
- package/dist/modules/subscription/service.js.map +1 -1
- package/dist/modules/subscription/status-check.handler.d.ts +117 -0
- package/dist/modules/subscription/status-check.handler.d.ts.map +1 -0
- package/dist/modules/subscription/status-check.handler.js +164 -0
- package/dist/modules/subscription/status-check.handler.js.map +1 -0
- package/dist/modules/subscription/types.d.ts +37 -1
- package/dist/modules/subscription/types.d.ts.map +1 -1
- package/dist/modules/subscriptionPlan/const.d.ts +5 -0
- package/dist/modules/subscriptionPlan/const.d.ts.map +1 -0
- package/dist/modules/subscriptionPlan/const.js +106 -0
- package/dist/modules/subscriptionPlan/const.js.map +1 -0
- package/dist/modules/subscriptionPlan/index.d.ts +5 -0
- package/dist/modules/subscriptionPlan/index.d.ts.map +1 -0
- package/dist/modules/subscriptionPlan/index.js +21 -0
- package/dist/modules/subscriptionPlan/index.js.map +1 -0
- package/dist/modules/subscriptionPlan/repository.d.ts +22 -0
- package/dist/modules/subscriptionPlan/repository.d.ts.map +1 -0
- package/dist/modules/subscriptionPlan/repository.js +95 -0
- package/dist/modules/subscriptionPlan/repository.js.map +1 -0
- package/dist/modules/subscriptionPlan/service.d.ts +21 -0
- package/dist/modules/subscriptionPlan/service.d.ts.map +1 -0
- package/dist/modules/subscriptionPlan/service.js +128 -0
- package/dist/modules/subscriptionPlan/service.js.map +1 -0
- package/dist/modules/subscriptionPlan/types.d.ts +40 -0
- package/dist/modules/subscriptionPlan/types.d.ts.map +1 -0
- package/dist/modules/subscriptionPlan/types.js +3 -0
- package/dist/modules/subscriptionPlan/types.js.map +1 -0
- package/dist/modules/token/const.d.ts +7 -0
- package/dist/modules/token/const.d.ts.map +1 -0
- package/dist/modules/token/const.js +66 -0
- package/dist/modules/token/const.js.map +1 -0
- package/dist/modules/token/index.d.ts +4 -0
- package/dist/modules/token/index.d.ts.map +1 -0
- package/dist/modules/token/index.js +20 -0
- package/dist/modules/token/index.js.map +1 -0
- package/dist/modules/token/service.d.ts +46 -0
- package/dist/modules/token/service.d.ts.map +1 -0
- package/dist/modules/token/service.js +249 -0
- package/dist/modules/token/service.js.map +1 -0
- package/dist/modules/token/types.d.ts +109 -0
- package/dist/modules/token/types.d.ts.map +1 -0
- package/dist/modules/token/types.js +3 -0
- package/dist/modules/token/types.js.map +1 -0
- package/dist/modules/tokenPack/const.d.ts +4 -0
- package/dist/modules/tokenPack/const.d.ts.map +1 -0
- package/dist/modules/tokenPack/const.js +10 -0
- package/dist/modules/tokenPack/const.js.map +1 -0
- package/dist/modules/tokenPack/index.d.ts +5 -0
- package/dist/modules/tokenPack/index.d.ts.map +1 -0
- package/dist/modules/tokenPack/index.js +21 -0
- package/dist/modules/tokenPack/index.js.map +1 -0
- package/dist/modules/tokenPack/repository.d.ts +32 -0
- package/dist/modules/tokenPack/repository.d.ts.map +1 -0
- package/dist/modules/tokenPack/repository.js +103 -0
- package/dist/modules/tokenPack/repository.js.map +1 -0
- package/dist/modules/tokenPack/service.d.ts +28 -0
- package/dist/modules/tokenPack/service.d.ts.map +1 -0
- package/dist/modules/tokenPack/service.js +106 -0
- package/dist/modules/tokenPack/service.js.map +1 -0
- package/dist/modules/tokenPack/types.d.ts +124 -0
- package/dist/modules/tokenPack/types.d.ts.map +1 -0
- package/dist/modules/tokenPack/types.js +3 -0
- package/dist/modules/tokenPack/types.js.map +1 -0
- package/package.json +9 -5
- package/src/config/ConfigurationManager.ts +159 -0
- package/src/config/defaults.ts +27 -0
- package/src/config/environment.ts +94 -0
- package/src/config/index.ts +22 -0
- package/src/config/types.ts +56 -0
- package/src/index.ts +29 -0
- package/src/modules/cache/InMemoryCache.ts +98 -0
- package/src/modules/cache/index.ts +2 -0
- package/src/modules/cache/types.ts +60 -0
- package/src/modules/cancellableAPI/utils.ts +60 -0
- package/src/modules/errors/index.ts +16 -0
- package/src/modules/errors/types.ts +201 -0
- package/src/modules/invoice/const.ts +7 -0
- package/src/modules/invoice/index.ts +4 -0
- package/src/modules/invoice/repository.ts +52 -0
- package/src/modules/invoice/service.ts +44 -0
- package/src/modules/invoice/types.ts +47 -0
- package/src/modules/logger/types.ts +8 -0
- package/src/modules/network/utils.ts +24 -0
- package/src/modules/payments/api.ts +289 -0
- package/src/modules/payments/const.ts +11 -0
- package/src/modules/payments/index.ts +14 -0
- package/src/modules/payments/repository.ts +125 -0
- package/src/modules/payments/service.test.ts +400 -0
- package/src/modules/payments/service.ts +365 -0
- package/src/modules/payments/subscription-check-webhook.handler.integration.test.ts +935 -0
- package/src/modules/payments/subscription-check-webhook.handler.ts +211 -0
- package/src/modules/payments/subscription-check-webhook.service.ts +398 -0
- package/src/modules/payments/subscription-check-webhook.types.ts +29 -0
- package/src/modules/payments/types.ts +193 -0
- package/src/modules/payments/utils.ts +428 -0
- package/src/modules/payments/wayforpay.service.test.ts +375 -0
- package/src/modules/payments/wayforpay.service.ts +284 -0
- package/src/modules/payments/wayforpay.types.ts +138 -0
- package/src/modules/payments/webhook.handler.integration.test.ts +975 -0
- package/src/modules/payments/webhook.handler.ts +219 -0
- package/src/modules/payments/webhook.service.ts +812 -0
- package/src/modules/payments/webhook.types.ts +38 -0
- package/src/modules/subscription/change.service.ts +317 -0
- package/src/modules/subscription/const.ts +9 -0
- package/src/modules/subscription/index.ts +5 -0
- package/src/modules/subscription/repository.ts +277 -0
- package/src/modules/subscription/service.test.ts +665 -0
- package/src/modules/subscription/service.ts +328 -0
- package/src/modules/subscription/status-check.handler.ts +254 -0
- package/src/modules/subscription/types.ts +267 -0
- package/src/modules/subscription/utils.ts +5 -0
- package/src/modules/subscriptionPlan/const.ts +106 -0
- package/src/modules/subscriptionPlan/index.ts +4 -0
- package/src/modules/subscriptionPlan/repository.ts +129 -0
- package/src/modules/subscriptionPlan/service.test.ts +401 -0
- package/src/modules/subscriptionPlan/service.ts +148 -0
- package/src/modules/subscriptionPlan/types.ts +67 -0
- package/src/modules/token/const.ts +64 -0
- package/src/modules/token/index.ts +3 -0
- package/src/modules/token/service.test.ts +499 -0
- package/src/modules/token/service.ts +297 -0
- package/src/modules/token/types.ts +124 -0
- package/src/modules/tokenPack/const.ts +9 -0
- package/src/modules/tokenPack/index.ts +4 -0
- package/src/modules/tokenPack/repository.ts +144 -0
- package/src/modules/tokenPack/service.ts +119 -0
- package/src/modules/tokenPack/types.ts +131 -0
- package/src/modules/user/index.ts +3 -0
- package/src/modules/user/types.ts +143 -0
- package/src/modules/user/userRepository.ts +64 -0
- package/src/modules/user/userService.ts +68 -0
- package/src/types/extend-express.d.ts +16 -0
- package/src/types/function.ts +5 -0
- package/src/types/utilities.ts +22 -0
- package/src/utils.ts +53 -0
- package/tsconfig.json +29 -0
- package/dist/modules/subscription/subscriptionPlan.d.ts +0 -4
- package/dist/modules/subscription/subscriptionPlan.d.ts.map +0 -1
- package/dist/modules/subscription/subscriptionPlan.js +0 -67
- package/dist/modules/subscription/subscriptionPlan.js.map +0 -1
|
@@ -0,0 +1,665 @@
|
|
|
1
|
+
import { describe, it, beforeEach, mock } from 'node:test';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
import { SubscriptionService } from './service';
|
|
4
|
+
import type { SubscriptionEntity, ISubscriptionRepository, SubscriptionPlan } from './types';
|
|
5
|
+
import type { IPaymentService, PaymentEntity } from '../payments/types';
|
|
6
|
+
import type { Logger } from '../logger/types';
|
|
7
|
+
import type { GracePeriodConfig } from '../../config';
|
|
8
|
+
|
|
9
|
+
// Mock logger
|
|
10
|
+
const createMockLogger = (): Logger => ({
|
|
11
|
+
info: mock.fn(),
|
|
12
|
+
warning: mock.fn(),
|
|
13
|
+
error: mock.fn(),
|
|
14
|
+
debug: mock.fn(),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// Mock subscription entity
|
|
18
|
+
const createMockSubscription = (overrides: Partial<SubscriptionEntity> = {}): SubscriptionEntity => ({
|
|
19
|
+
id: 'sub-123',
|
|
20
|
+
userId: 'user-123',
|
|
21
|
+
platform: 'telegram',
|
|
22
|
+
planId: 'plan-monthly',
|
|
23
|
+
status: 'active',
|
|
24
|
+
expiresAt: '2025-02-01T00:00:00.000Z',
|
|
25
|
+
startedAt: '2025-01-01T00:00:00.000Z',
|
|
26
|
+
provider: 'wayforpay',
|
|
27
|
+
...overrides,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Mock payment entity
|
|
31
|
+
const createMockPayment = (overrides: Partial<PaymentEntity> = {}): PaymentEntity => ({
|
|
32
|
+
id: 'pay-123',
|
|
33
|
+
orderReference: 'order-123',
|
|
34
|
+
userId: 'user-123',
|
|
35
|
+
platform: 'telegram',
|
|
36
|
+
status: 'pending',
|
|
37
|
+
paymentLink: 'https://pay.example.com/link',
|
|
38
|
+
planId: 'plan-monthly',
|
|
39
|
+
paymentType: 'subscription',
|
|
40
|
+
amount: 100,
|
|
41
|
+
currency: 'UAH',
|
|
42
|
+
createdAt: new Date().toISOString(),
|
|
43
|
+
provider: 'wayforpay',
|
|
44
|
+
...overrides,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Mock subscription plan
|
|
48
|
+
const createMockSubscriptionPlan = (): SubscriptionPlan => ({
|
|
49
|
+
id: 'plan-monthly',
|
|
50
|
+
titleCode: 'subscription.monthly.title',
|
|
51
|
+
descriptionCode: 'subscription.monthly.description',
|
|
52
|
+
amount: 100,
|
|
53
|
+
currency: 'UAH',
|
|
54
|
+
features: {
|
|
55
|
+
messages: 1000,
|
|
56
|
+
images: 50,
|
|
57
|
+
voice: 100,
|
|
58
|
+
},
|
|
59
|
+
regularMode: 'monthly',
|
|
60
|
+
count: 1,
|
|
61
|
+
credits: 5000,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Mock repository
|
|
65
|
+
const createMockRepository = (overrides: Partial<ISubscriptionRepository> = {}): ISubscriptionRepository => ({
|
|
66
|
+
getByUser: mock.fn(async () => null),
|
|
67
|
+
create: mock.fn(async (params) => ({
|
|
68
|
+
id: 'new-sub-id',
|
|
69
|
+
...params,
|
|
70
|
+
status: 'active',
|
|
71
|
+
provider: 'wayforpay',
|
|
72
|
+
})),
|
|
73
|
+
updateFieldsByUserId: mock.fn(async () => {}),
|
|
74
|
+
getExpiredActiveSubscriptions: mock.fn(async () => []),
|
|
75
|
+
updateFieldsById: mock.fn(async () => {}),
|
|
76
|
+
activateSubscription: mock.fn(async () => {}),
|
|
77
|
+
renewSubscription: mock.fn(async () => {}),
|
|
78
|
+
getByStatus: mock.fn(async () => []),
|
|
79
|
+
deactivateSubscription: mock.fn(async () => {}),
|
|
80
|
+
cancelSubscription: mock.fn(async () => {}),
|
|
81
|
+
...overrides,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Mock payment service
|
|
85
|
+
const createMockPaymentService = (overrides: Partial<IPaymentService> = {}): IPaymentService => ({
|
|
86
|
+
getByOrderReference: mock.fn(async () => null),
|
|
87
|
+
getByUser: mock.fn(async () => []),
|
|
88
|
+
create: mock.fn(async (data) => ({ id: 'pay-id', ...data })),
|
|
89
|
+
updateStatus: mock.fn(async () => {}),
|
|
90
|
+
changeStatus: mock.fn(async () => {}),
|
|
91
|
+
getExpiredPendingPayments: mock.fn(async () => []),
|
|
92
|
+
createPaymentIntent: mock.fn(async () => createMockPayment()),
|
|
93
|
+
createTokenPackPaymentIntent: mock.fn(async () => createMockPayment()),
|
|
94
|
+
createUpgradePaymentIntent: mock.fn(async () => createMockPayment()),
|
|
95
|
+
validateSignature: mock.fn(() => ({ isValid: true })),
|
|
96
|
+
...overrides,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
describe('SubscriptionService', () => {
|
|
100
|
+
let logger: Logger;
|
|
101
|
+
let repository: ISubscriptionRepository;
|
|
102
|
+
let paymentService: IPaymentService;
|
|
103
|
+
let gracePeriodConfig: GracePeriodConfig;
|
|
104
|
+
let service: SubscriptionService;
|
|
105
|
+
|
|
106
|
+
beforeEach(() => {
|
|
107
|
+
logger = createMockLogger();
|
|
108
|
+
repository = createMockRepository();
|
|
109
|
+
paymentService = createMockPaymentService();
|
|
110
|
+
gracePeriodConfig = {
|
|
111
|
+
enabled: true,
|
|
112
|
+
durationMs: 3 * 24 * 60 * 60 * 1000, // 3 days
|
|
113
|
+
};
|
|
114
|
+
service = new SubscriptionService({
|
|
115
|
+
logger,
|
|
116
|
+
repository,
|
|
117
|
+
paymentService,
|
|
118
|
+
gracePeriodConfig,
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
describe('getByUser', () => {
|
|
123
|
+
it('should return subscription when found', async () => {
|
|
124
|
+
const mockSubscription = createMockSubscription();
|
|
125
|
+
repository = createMockRepository({
|
|
126
|
+
getByUser: mock.fn(async () => mockSubscription),
|
|
127
|
+
});
|
|
128
|
+
service = new SubscriptionService({ logger, repository, paymentService, gracePeriodConfig });
|
|
129
|
+
|
|
130
|
+
const result = await service.getByUser({
|
|
131
|
+
userId: 'user-123',
|
|
132
|
+
platform: 'telegram',
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
assert.deepStrictEqual(result, mockSubscription);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should return null when subscription not found', async () => {
|
|
139
|
+
const result = await service.getByUser({
|
|
140
|
+
userId: 'user-123',
|
|
141
|
+
platform: 'telegram',
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
assert.strictEqual(result, null);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('should return null and log error on repository error', async () => {
|
|
148
|
+
repository = createMockRepository({
|
|
149
|
+
getByUser: mock.fn(async () => {
|
|
150
|
+
throw new Error('DB error');
|
|
151
|
+
}),
|
|
152
|
+
});
|
|
153
|
+
service = new SubscriptionService({ logger, repository, paymentService, gracePeriodConfig });
|
|
154
|
+
|
|
155
|
+
const result = await service.getByUser({
|
|
156
|
+
userId: 'user-123',
|
|
157
|
+
platform: 'telegram',
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
assert.strictEqual(result, null);
|
|
161
|
+
assert.strictEqual((logger.error as any).mock.calls.length, 1);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('should filter by status when provided', async () => {
|
|
165
|
+
const getByUserMock = mock.fn(async () => createMockSubscription());
|
|
166
|
+
repository = createMockRepository({ getByUser: getByUserMock });
|
|
167
|
+
service = new SubscriptionService({ logger, repository, paymentService, gracePeriodConfig });
|
|
168
|
+
|
|
169
|
+
await service.getByUser({
|
|
170
|
+
userId: 'user-123',
|
|
171
|
+
platform: 'telegram',
|
|
172
|
+
status: 'active',
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
const calls = (getByUserMock as any).mock.calls;
|
|
176
|
+
const callArgs = calls[0]?.arguments[0];
|
|
177
|
+
assert.strictEqual(callArgs?.userId, 'user-123');
|
|
178
|
+
assert.strictEqual(callArgs?.platform, 'telegram');
|
|
179
|
+
assert.strictEqual(callArgs?.status, 'active');
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
describe('create', () => {
|
|
184
|
+
it('should create subscription successfully', async () => {
|
|
185
|
+
const params = {
|
|
186
|
+
userId: 'user-123',
|
|
187
|
+
platform: 'telegram',
|
|
188
|
+
planId: 'plan-monthly',
|
|
189
|
+
expiresAt: '2025-02-01T00:00:00.000Z',
|
|
190
|
+
startedAt: '2025-01-01T00:00:00.000Z',
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const result = await service.create(params);
|
|
194
|
+
|
|
195
|
+
assert.strictEqual(result.userId, params.userId);
|
|
196
|
+
assert.strictEqual(result.platform, params.platform);
|
|
197
|
+
assert.strictEqual(result.planId, params.planId);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it('should throw error and log on repository error', async () => {
|
|
201
|
+
repository = createMockRepository({
|
|
202
|
+
create: mock.fn(async () => {
|
|
203
|
+
throw new Error('DB error');
|
|
204
|
+
}),
|
|
205
|
+
});
|
|
206
|
+
service = new SubscriptionService({ logger, repository, paymentService, gracePeriodConfig });
|
|
207
|
+
|
|
208
|
+
await assert.rejects(
|
|
209
|
+
async () =>
|
|
210
|
+
service.create({
|
|
211
|
+
userId: 'user-123',
|
|
212
|
+
platform: 'telegram',
|
|
213
|
+
planId: 'plan-monthly',
|
|
214
|
+
expiresAt: '2025-02-01T00:00:00.000Z',
|
|
215
|
+
startedAt: '2025-01-01T00:00:00.000Z',
|
|
216
|
+
}),
|
|
217
|
+
{ message: 'DB error' },
|
|
218
|
+
);
|
|
219
|
+
assert.strictEqual((logger.error as any).mock.calls.length, 1);
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
describe('updateFieldsByUserId', () => {
|
|
224
|
+
it('should update fields successfully', async () => {
|
|
225
|
+
const updateMock = mock.fn(async () => {});
|
|
226
|
+
repository = createMockRepository({ updateFieldsByUserId: updateMock });
|
|
227
|
+
service = new SubscriptionService({ logger, repository, paymentService, gracePeriodConfig });
|
|
228
|
+
|
|
229
|
+
await service.updateFieldsByUserId({
|
|
230
|
+
userId: 'user-123',
|
|
231
|
+
platform: 'telegram',
|
|
232
|
+
fields: [['status', 'cancelled']],
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
assert.strictEqual(updateMock.mock.calls.length, 1);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it('should throw error on repository error', async () => {
|
|
239
|
+
repository = createMockRepository({
|
|
240
|
+
updateFieldsByUserId: mock.fn(async () => {
|
|
241
|
+
throw new Error('Update error');
|
|
242
|
+
}),
|
|
243
|
+
});
|
|
244
|
+
service = new SubscriptionService({ logger, repository, paymentService, gracePeriodConfig });
|
|
245
|
+
|
|
246
|
+
await assert.rejects(
|
|
247
|
+
async () =>
|
|
248
|
+
service.updateFieldsByUserId({
|
|
249
|
+
userId: 'user-123',
|
|
250
|
+
platform: 'telegram',
|
|
251
|
+
fields: [['status', 'cancelled']],
|
|
252
|
+
}),
|
|
253
|
+
{ message: 'Update error' },
|
|
254
|
+
);
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
describe('getExpiredActiveSubscriptions', () => {
|
|
259
|
+
it('should return expired subscriptions', async () => {
|
|
260
|
+
const expiredSubs = [createMockSubscription({ status: 'active' })];
|
|
261
|
+
repository = createMockRepository({
|
|
262
|
+
getExpiredActiveSubscriptions: mock.fn(async () => expiredSubs),
|
|
263
|
+
});
|
|
264
|
+
service = new SubscriptionService({ logger, repository, paymentService, gracePeriodConfig });
|
|
265
|
+
|
|
266
|
+
const result = await service.getExpiredActiveSubscriptions();
|
|
267
|
+
|
|
268
|
+
assert.deepStrictEqual(result, expiredSubs);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it('should return empty array on error', async () => {
|
|
272
|
+
repository = createMockRepository({
|
|
273
|
+
getExpiredActiveSubscriptions: mock.fn(async () => {
|
|
274
|
+
throw new Error('DB error');
|
|
275
|
+
}),
|
|
276
|
+
});
|
|
277
|
+
service = new SubscriptionService({ logger, repository, paymentService, gracePeriodConfig });
|
|
278
|
+
|
|
279
|
+
const result = await service.getExpiredActiveSubscriptions();
|
|
280
|
+
|
|
281
|
+
assert.deepStrictEqual(result, []);
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
describe('updateFieldsById', () => {
|
|
286
|
+
it('should update fields by ID successfully', async () => {
|
|
287
|
+
const updateMock = mock.fn(async () => {});
|
|
288
|
+
repository = createMockRepository({ updateFieldsById: updateMock });
|
|
289
|
+
service = new SubscriptionService({ logger, repository, paymentService, gracePeriodConfig });
|
|
290
|
+
|
|
291
|
+
await service.updateFieldsById({
|
|
292
|
+
subscriptionId: 'sub-123',
|
|
293
|
+
fields: [['status', 'expired']],
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
assert.strictEqual(updateMock.mock.calls.length, 1);
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it('should throw error on repository error', async () => {
|
|
300
|
+
repository = createMockRepository({
|
|
301
|
+
updateFieldsById: mock.fn(async () => {
|
|
302
|
+
throw new Error('Update error');
|
|
303
|
+
}),
|
|
304
|
+
});
|
|
305
|
+
service = new SubscriptionService({ logger, repository, paymentService, gracePeriodConfig });
|
|
306
|
+
|
|
307
|
+
await assert.rejects(
|
|
308
|
+
async () =>
|
|
309
|
+
service.updateFieldsById({
|
|
310
|
+
subscriptionId: 'sub-123',
|
|
311
|
+
fields: [['status', 'expired']],
|
|
312
|
+
}),
|
|
313
|
+
{ message: 'Update error' },
|
|
314
|
+
);
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
describe('getOrCreateSubscriptionPaymentUrl', () => {
|
|
319
|
+
it('should return existing payment URL if recent pending payment exists', async () => {
|
|
320
|
+
const recentPayment = createMockPayment({
|
|
321
|
+
planId: 'plan-monthly',
|
|
322
|
+
createdAt: new Date().toISOString(),
|
|
323
|
+
paymentLink: 'https://existing-payment-link.com',
|
|
324
|
+
});
|
|
325
|
+
paymentService = createMockPaymentService({
|
|
326
|
+
getByUser: mock.fn(async () => [recentPayment]),
|
|
327
|
+
});
|
|
328
|
+
service = new SubscriptionService({ logger, repository, paymentService, gracePeriodConfig });
|
|
329
|
+
|
|
330
|
+
const translate = () => 'Monthly Subscription';
|
|
331
|
+
const result = await service.getOrCreateSubscriptionPaymentUrl({
|
|
332
|
+
userId: 'user-123',
|
|
333
|
+
platform: 'telegram',
|
|
334
|
+
languageCode: 'en',
|
|
335
|
+
translate,
|
|
336
|
+
subscriptionPlan: createMockSubscriptionPlan(),
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
assert.strictEqual(result, 'https://existing-payment-link.com');
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it('should create new payment if no recent payment exists', async () => {
|
|
343
|
+
const newPayment = createMockPayment({
|
|
344
|
+
paymentLink: 'https://new-payment-link.com',
|
|
345
|
+
});
|
|
346
|
+
paymentService = createMockPaymentService({
|
|
347
|
+
getByUser: mock.fn(async () => []),
|
|
348
|
+
createPaymentIntent: mock.fn(async () => newPayment),
|
|
349
|
+
});
|
|
350
|
+
service = new SubscriptionService({ logger, repository, paymentService, gracePeriodConfig });
|
|
351
|
+
|
|
352
|
+
const translate = () => 'Monthly Subscription';
|
|
353
|
+
const result = await service.getOrCreateSubscriptionPaymentUrl({
|
|
354
|
+
userId: 'user-123',
|
|
355
|
+
platform: 'telegram',
|
|
356
|
+
languageCode: 'en',
|
|
357
|
+
translate,
|
|
358
|
+
subscriptionPlan: createMockSubscriptionPlan(),
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
assert.strictEqual(result, 'https://new-payment-link.com');
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
it('should throw error and log on payment service error', async () => {
|
|
365
|
+
paymentService = createMockPaymentService({
|
|
366
|
+
getByUser: mock.fn(async () => {
|
|
367
|
+
throw new Error('Payment error');
|
|
368
|
+
}),
|
|
369
|
+
});
|
|
370
|
+
service = new SubscriptionService({ logger, repository, paymentService, gracePeriodConfig });
|
|
371
|
+
|
|
372
|
+
const translate = () => 'Monthly Subscription';
|
|
373
|
+
await assert.rejects(
|
|
374
|
+
async () =>
|
|
375
|
+
service.getOrCreateSubscriptionPaymentUrl({
|
|
376
|
+
userId: 'user-123',
|
|
377
|
+
platform: 'telegram',
|
|
378
|
+
languageCode: 'en',
|
|
379
|
+
translate,
|
|
380
|
+
subscriptionPlan: createMockSubscriptionPlan(),
|
|
381
|
+
}),
|
|
382
|
+
{ message: 'Payment error' },
|
|
383
|
+
);
|
|
384
|
+
});
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
describe('activateSubscription', () => {
|
|
388
|
+
it('should activate subscription successfully', async () => {
|
|
389
|
+
const activateMock = mock.fn(async () => {});
|
|
390
|
+
repository = createMockRepository({ activateSubscription: activateMock });
|
|
391
|
+
service = new SubscriptionService({ logger, repository, paymentService, gracePeriodConfig });
|
|
392
|
+
|
|
393
|
+
await service.activateSubscription({
|
|
394
|
+
userId: 'user-123',
|
|
395
|
+
platform: 'telegram',
|
|
396
|
+
planId: 'plan-monthly',
|
|
397
|
+
expiresAt: '2025-02-01T00:00:00.000Z',
|
|
398
|
+
startedAt: '2025-01-01T00:00:00.000Z',
|
|
399
|
+
provider: 'wayforpay',
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
assert.strictEqual(activateMock.mock.calls.length, 1);
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
it('should throw error on repository error', async () => {
|
|
406
|
+
repository = createMockRepository({
|
|
407
|
+
activateSubscription: mock.fn(async () => {
|
|
408
|
+
throw new Error('Activation error');
|
|
409
|
+
}),
|
|
410
|
+
});
|
|
411
|
+
service = new SubscriptionService({ logger, repository, paymentService, gracePeriodConfig });
|
|
412
|
+
|
|
413
|
+
await assert.rejects(
|
|
414
|
+
async () =>
|
|
415
|
+
service.activateSubscription({
|
|
416
|
+
userId: 'user-123',
|
|
417
|
+
platform: 'telegram',
|
|
418
|
+
planId: 'plan-monthly',
|
|
419
|
+
expiresAt: '2025-02-01T00:00:00.000Z',
|
|
420
|
+
startedAt: '2025-01-01T00:00:00.000Z',
|
|
421
|
+
provider: 'wayforpay',
|
|
422
|
+
}),
|
|
423
|
+
{ message: 'Activation error' },
|
|
424
|
+
);
|
|
425
|
+
});
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
describe('renewSubscription', () => {
|
|
429
|
+
it('should renew subscription successfully', async () => {
|
|
430
|
+
const renewMock = mock.fn(async () => {});
|
|
431
|
+
repository = createMockRepository({ renewSubscription: renewMock });
|
|
432
|
+
service = new SubscriptionService({ logger, repository, paymentService, gracePeriodConfig });
|
|
433
|
+
|
|
434
|
+
await service.renewSubscription({
|
|
435
|
+
userId: 'user-123',
|
|
436
|
+
platform: 'telegram',
|
|
437
|
+
expiresAt: '2025-03-01T00:00:00.000Z',
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
assert.strictEqual(renewMock.mock.calls.length, 1);
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
it('should throw error on repository error', async () => {
|
|
444
|
+
repository = createMockRepository({
|
|
445
|
+
renewSubscription: mock.fn(async () => {
|
|
446
|
+
throw new Error('Renew error');
|
|
447
|
+
}),
|
|
448
|
+
});
|
|
449
|
+
service = new SubscriptionService({ logger, repository, paymentService, gracePeriodConfig });
|
|
450
|
+
|
|
451
|
+
await assert.rejects(
|
|
452
|
+
async () =>
|
|
453
|
+
service.renewSubscription({
|
|
454
|
+
userId: 'user-123',
|
|
455
|
+
platform: 'telegram',
|
|
456
|
+
expiresAt: '2025-03-01T00:00:00.000Z',
|
|
457
|
+
}),
|
|
458
|
+
{ message: 'Renew error' },
|
|
459
|
+
);
|
|
460
|
+
});
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
describe('getSubscriptionsForDeactivationCheck', () => {
|
|
464
|
+
it('should return active and cancelled subscriptions excluding unlimited', async () => {
|
|
465
|
+
const subs = [
|
|
466
|
+
createMockSubscription({ id: 'sub-1', status: 'active' }),
|
|
467
|
+
createMockSubscription({ id: 'sub-2', status: 'cancelled' }),
|
|
468
|
+
createMockSubscription({ id: 'unlimited', status: 'active' }),
|
|
469
|
+
];
|
|
470
|
+
repository = createMockRepository({
|
|
471
|
+
getByStatus: mock.fn(async () => subs),
|
|
472
|
+
});
|
|
473
|
+
service = new SubscriptionService({ logger, repository, paymentService, gracePeriodConfig });
|
|
474
|
+
|
|
475
|
+
const result = await service.getSubscriptionsForDeactivationCheck();
|
|
476
|
+
|
|
477
|
+
assert.strictEqual(result.length, 2);
|
|
478
|
+
assert.ok(!result.find((s) => s.id === 'unlimited'));
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
it('should throw error on repository error', async () => {
|
|
482
|
+
repository = createMockRepository({
|
|
483
|
+
getByStatus: mock.fn(async () => {
|
|
484
|
+
throw new Error('DB error');
|
|
485
|
+
}),
|
|
486
|
+
});
|
|
487
|
+
service = new SubscriptionService({ logger, repository, paymentService, gracePeriodConfig });
|
|
488
|
+
|
|
489
|
+
await assert.rejects(async () => service.getSubscriptionsForDeactivationCheck(), { message: 'DB error' });
|
|
490
|
+
});
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
describe('deactivateSubscription', () => {
|
|
494
|
+
it('should deactivate subscription successfully', async () => {
|
|
495
|
+
const deactivateMock = mock.fn(async () => {});
|
|
496
|
+
repository = createMockRepository({ deactivateSubscription: deactivateMock });
|
|
497
|
+
service = new SubscriptionService({ logger, repository, paymentService, gracePeriodConfig });
|
|
498
|
+
|
|
499
|
+
await service.deactivateSubscription({ id: 'sub-123' });
|
|
500
|
+
|
|
501
|
+
assert.strictEqual(deactivateMock.mock.calls.length, 1);
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
it('should throw error on repository error', async () => {
|
|
505
|
+
repository = createMockRepository({
|
|
506
|
+
deactivateSubscription: mock.fn(async () => {
|
|
507
|
+
throw new Error('Deactivation error');
|
|
508
|
+
}),
|
|
509
|
+
});
|
|
510
|
+
service = new SubscriptionService({ logger, repository, paymentService, gracePeriodConfig });
|
|
511
|
+
|
|
512
|
+
await assert.rejects(async () => service.deactivateSubscription({ id: 'sub-123' }), {
|
|
513
|
+
message: 'Deactivation error',
|
|
514
|
+
});
|
|
515
|
+
});
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
describe('cancelSubscription', () => {
|
|
519
|
+
it('should cancel subscription successfully', async () => {
|
|
520
|
+
const cancelMock = mock.fn(async () => {});
|
|
521
|
+
repository = createMockRepository({ cancelSubscription: cancelMock });
|
|
522
|
+
service = new SubscriptionService({ logger, repository, paymentService, gracePeriodConfig });
|
|
523
|
+
|
|
524
|
+
await service.cancelSubscription({ id: 'sub-123' });
|
|
525
|
+
|
|
526
|
+
assert.strictEqual(cancelMock.mock.calls.length, 1);
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
it('should not throw on repository error (silent failure)', async () => {
|
|
530
|
+
repository = createMockRepository({
|
|
531
|
+
cancelSubscription: mock.fn(async () => {
|
|
532
|
+
throw new Error('Cancel error');
|
|
533
|
+
}),
|
|
534
|
+
});
|
|
535
|
+
service = new SubscriptionService({ logger, repository, paymentService, gracePeriodConfig });
|
|
536
|
+
|
|
537
|
+
// Should not throw, just logs error
|
|
538
|
+
await service.cancelSubscription({ id: 'sub-123' });
|
|
539
|
+
assert.strictEqual((logger.error as any).mock.calls.length, 1);
|
|
540
|
+
});
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
describe('getExpiredSubscriptions', () => {
|
|
544
|
+
it('should return expired subscriptions after grace period', async () => {
|
|
545
|
+
// Subscription expired 5 days ago (past 3-day grace period)
|
|
546
|
+
const expiredSub = createMockSubscription({
|
|
547
|
+
expiresAt: new Date(Date.now() - 5 * 24 * 60 * 60 * 1000).toISOString(),
|
|
548
|
+
});
|
|
549
|
+
repository = createMockRepository({
|
|
550
|
+
getExpiredActiveSubscriptions: mock.fn(async () => [expiredSub]),
|
|
551
|
+
});
|
|
552
|
+
service = new SubscriptionService({ logger, repository, paymentService, gracePeriodConfig });
|
|
553
|
+
|
|
554
|
+
const result = await service.getExpiredSubscriptions();
|
|
555
|
+
|
|
556
|
+
assert.strictEqual(result.length, 1);
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
it('should not return subscriptions still within grace period', async () => {
|
|
560
|
+
// Subscription expired 1 day ago (within 3-day grace period)
|
|
561
|
+
const recentlyExpiredSub = createMockSubscription({
|
|
562
|
+
expiresAt: new Date(Date.now() - 1 * 24 * 60 * 60 * 1000).toISOString(),
|
|
563
|
+
});
|
|
564
|
+
repository = createMockRepository({
|
|
565
|
+
getExpiredActiveSubscriptions: mock.fn(async () => [recentlyExpiredSub]),
|
|
566
|
+
});
|
|
567
|
+
service = new SubscriptionService({ logger, repository, paymentService, gracePeriodConfig });
|
|
568
|
+
|
|
569
|
+
const result = await service.getExpiredSubscriptions();
|
|
570
|
+
|
|
571
|
+
assert.strictEqual(result.length, 0);
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
it('should return all expired subscriptions if grace period is disabled', async () => {
|
|
575
|
+
const expiredSub = createMockSubscription({
|
|
576
|
+
expiresAt: new Date(Date.now() - 1 * 60 * 1000).toISOString(), // 1 minute ago
|
|
577
|
+
});
|
|
578
|
+
repository = createMockRepository({
|
|
579
|
+
getExpiredActiveSubscriptions: mock.fn(async () => [expiredSub]),
|
|
580
|
+
});
|
|
581
|
+
service = new SubscriptionService({
|
|
582
|
+
logger,
|
|
583
|
+
repository,
|
|
584
|
+
paymentService,
|
|
585
|
+
gracePeriodConfig: { enabled: false, durationMs: 0 },
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
const result = await service.getExpiredSubscriptions();
|
|
589
|
+
|
|
590
|
+
assert.strictEqual(result.length, 1);
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
it('should return empty array on error', async () => {
|
|
594
|
+
repository = createMockRepository({
|
|
595
|
+
getExpiredActiveSubscriptions: mock.fn(async () => {
|
|
596
|
+
throw new Error('DB error');
|
|
597
|
+
}),
|
|
598
|
+
});
|
|
599
|
+
service = new SubscriptionService({ logger, repository, paymentService, gracePeriodConfig });
|
|
600
|
+
|
|
601
|
+
const result = await service.getExpiredSubscriptions();
|
|
602
|
+
|
|
603
|
+
assert.deepStrictEqual(result, []);
|
|
604
|
+
});
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
describe('getFailedRenewals', () => {
|
|
608
|
+
it('should return cancelled subscriptions past grace period', async () => {
|
|
609
|
+
const cancelledSub = createMockSubscription({
|
|
610
|
+
id: 'sub-cancelled',
|
|
611
|
+
status: 'cancelled',
|
|
612
|
+
expiresAt: new Date(Date.now() - 5 * 24 * 60 * 60 * 1000).toISOString(),
|
|
613
|
+
});
|
|
614
|
+
repository = createMockRepository({
|
|
615
|
+
getByStatus: mock.fn(async () => [cancelledSub]),
|
|
616
|
+
});
|
|
617
|
+
service = new SubscriptionService({ logger, repository, paymentService, gracePeriodConfig });
|
|
618
|
+
|
|
619
|
+
const result = await service.getFailedRenewals();
|
|
620
|
+
|
|
621
|
+
assert.strictEqual(result.length, 1);
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
it('should exclude unlimited subscriptions', async () => {
|
|
625
|
+
const unlimitedSub = createMockSubscription({
|
|
626
|
+
id: 'unlimited',
|
|
627
|
+
status: 'cancelled',
|
|
628
|
+
expiresAt: new Date(Date.now() - 5 * 24 * 60 * 60 * 1000).toISOString(),
|
|
629
|
+
});
|
|
630
|
+
repository = createMockRepository({
|
|
631
|
+
getByStatus: mock.fn(async () => [unlimitedSub]),
|
|
632
|
+
});
|
|
633
|
+
service = new SubscriptionService({ logger, repository, paymentService, gracePeriodConfig });
|
|
634
|
+
|
|
635
|
+
const result = await service.getFailedRenewals();
|
|
636
|
+
|
|
637
|
+
assert.strictEqual(result.length, 0);
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
it('should return empty array on error', async () => {
|
|
641
|
+
repository = createMockRepository({
|
|
642
|
+
getByStatus: mock.fn(async () => {
|
|
643
|
+
throw new Error('DB error');
|
|
644
|
+
}),
|
|
645
|
+
});
|
|
646
|
+
service = new SubscriptionService({ logger, repository, paymentService, gracePeriodConfig });
|
|
647
|
+
|
|
648
|
+
const result = await service.getFailedRenewals();
|
|
649
|
+
|
|
650
|
+
assert.deepStrictEqual(result, []);
|
|
651
|
+
});
|
|
652
|
+
});
|
|
653
|
+
|
|
654
|
+
describe('deactivate', () => {
|
|
655
|
+
it('should call deactivateSubscription', async () => {
|
|
656
|
+
const deactivateMock = mock.fn(async () => {});
|
|
657
|
+
repository = createMockRepository({ deactivateSubscription: deactivateMock });
|
|
658
|
+
service = new SubscriptionService({ logger, repository, paymentService, gracePeriodConfig });
|
|
659
|
+
|
|
660
|
+
await service.deactivate({ id: 'sub-123' });
|
|
661
|
+
|
|
662
|
+
assert.strictEqual(deactivateMock.mock.calls.length, 1);
|
|
663
|
+
});
|
|
664
|
+
});
|
|
665
|
+
});
|