-
-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathbilling.cron.weeklyReset.unit.tests.js
More file actions
155 lines (124 loc) · 5.82 KB
/
Copy pathbilling.cron.weeklyReset.unit.tests.js
File metadata and controls
155 lines (124 loc) · 5.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/**
* Module dependencies.
*/
import { jest, describe, test, beforeEach, afterEach, expect } from '@jest/globals';
/**
* Unit tests for billing.weeklyReset cron script logic.
*
* The script itself is a top-level-await CLI entry point that connects to MongoDB and exits.
* We test the underlying BillingResetService.resetAllDue integration path rather than the
* script file directly (which would require a live DB connection).
*/
describe('billing.weeklyReset cron — applyJitter contract:', () => {
test('applyJitter honours getCronJitterMaxMs bound: returns delay in [0, maxMs)', async () => {
// Contract test: weeklyReset calls applyJitter(getCronJitterMaxMs()).
// Verify the two helpers interoperate correctly (applyJitter respects the configured bound).
jest.resetModules();
const mockConfig = { billing: { meterMode: true, crons: { jitterMaxMs: 100 } } };
jest.unstable_mockModule('../../../config/index.js', () => ({ default: mockConfig }));
const { getCronJitterMaxMs } = await import('../lib/billing.constants.js');
const { applyJitter } = await import('../lib/billing.cron-utils.js');
const maxMs = getCronJitterMaxMs();
expect(maxMs).toBe(100);
const delay = await applyJitter(maxMs);
expect(delay).toBeGreaterThanOrEqual(0);
expect(delay).toBeLessThan(maxMs);
});
test('applyJitter returns 0 for invalid (non-positive) jitter max — safe no-op', async () => {
const { applyJitter } = await import('../lib/billing.cron-utils.js');
expect(await applyJitter(0)).toBe(0);
expect(await applyJitter(-1)).toBe(0);
expect(await applyJitter(NaN)).toBe(0);
});
});
describe('billing.weeklyReset cron — BillingResetService.resetAllDue:', () => {
let BillingResetService;
let mockConfig;
let mockUsageRepository;
let mockSubscriptionRepository;
let mockPlanService;
beforeEach(async () => {
jest.resetModules();
mockConfig = {
billing: {
meterMode: true,
plans: ['pro'],
},
};
mockUsageRepository = {
findByWeek: jest.fn().mockResolvedValue(null),
archiveOtherWeeks: jest.fn().mockResolvedValue({ modifiedCount: 0 }),
upsertWeekSnapshot: jest.fn(),
};
mockPlanService = {
getActivePlan: jest.fn().mockResolvedValue({ planId: 'pro', version: 'v1', meterQuota: 500000 }),
};
mockSubscriptionRepository = {
findAllDueForResetByLastReset: jest.fn(),
findByOrganization: jest.fn().mockResolvedValue({ plan: 'pro' }),
findPlan: jest.fn().mockResolvedValue({ plan: 'pro' }),
updateLastResetAt: jest.fn().mockResolvedValue(undefined),
};
jest.unstable_mockModule('../../../config/index.js', () => ({ default: mockConfig }));
jest.unstable_mockModule('../repositories/billing.usage.repository.js', () => ({
default: mockUsageRepository,
}));
jest.unstable_mockModule('../repositories/billing.subscription.repository.js', () => ({
default: mockSubscriptionRepository,
}));
jest.unstable_mockModule('../services/billing.plan.service.js', () => ({
default: mockPlanService,
}));
const mod = await import('../services/billing.reset.service.js');
BillingResetService = mod.default;
});
afterEach(() => {
jest.restoreAllMocks();
});
test('resetAllDue returns { processed: 0, errors: 0 } when meterMode is false', async () => {
mockConfig.billing.meterMode = false;
const result = await BillingResetService.resetAllDue();
expect(result).toEqual({ processed: 0, errors: 0 });
expect(mockSubscriptionRepository.findAllDueForResetByLastReset).not.toHaveBeenCalled();
});
test('resetAllDue returns { processed: 0, errors: 0 } when no subscriptions are due', async () => {
mockSubscriptionRepository.findAllDueForResetByLastReset.mockResolvedValue([]);
const result = await BillingResetService.resetAllDue();
expect(result).toEqual({ processed: 0, errors: 0 });
});
test('resetAllDue processes each due subscription and returns correct count', async () => {
const subs = [
{ organization: '507f1f77bcf86cd799439011', currentPeriodStart: new Date() },
{ organization: '507f1f77bcf86cd799439022', currentPeriodStart: new Date() },
];
mockSubscriptionRepository.findAllDueForResetByLastReset.mockResolvedValue(subs);
mockUsageRepository.upsertWeekSnapshot.mockResolvedValue({ weekKey: '2026-W18' });
const result = await BillingResetService.resetAllDue();
expect(result.processed).toBe(2);
expect(result.errors).toBe(0);
});
test('resetAllDue counts errors and continues on individual failure', async () => {
const subs = [
{ organization: '507f1f77bcf86cd799439011', currentPeriodStart: new Date() },
{ organization: '507f1f77bcf86cd799439022', currentPeriodStart: new Date() },
];
mockSubscriptionRepository.findAllDueForResetByLastReset.mockResolvedValue(subs);
// First call throws, second succeeds
mockUsageRepository.upsertWeekSnapshot
.mockRejectedValueOnce(new Error('DB error'))
.mockResolvedValueOnce({ weekKey: '2026-W18' });
const result = await BillingResetService.resetAllDue();
expect(result.processed).toBe(1);
expect(result.errors).toBe(1);
});
test('resetAllDue is idempotent — no double-upsert when week doc already exists', async () => {
const subs = [{ organization: '507f1f77bcf86cd799439011', currentPeriodStart: new Date() }];
mockSubscriptionRepository.findAllDueForResetByLastReset.mockResolvedValue(subs);
// findByWeek returns existing doc → resetWeek returns early without upsert
mockUsageRepository.findByWeek.mockResolvedValue({ weekKey: '2026-W18', meterUsed: 100 });
const result = await BillingResetService.resetAllDue();
expect(result.processed).toBe(1);
expect(result.errors).toBe(0);
expect(mockUsageRepository.upsertWeekSnapshot).not.toHaveBeenCalled();
});
});