Skip to content

Commit be70ae7

Browse files
fix(billing): use status allowlist for plan resolution in usage endpoint
- Replace past_due-only check with active/trialing allowlist - All other statuses (canceled, unpaid, incomplete, etc.) now map to free - Add test.each for all non-active statuses + trialing test
1 parent 03cd794 commit be70ae7

2 files changed

Lines changed: 27 additions & 3 deletions

File tree

modules/billing/controllers/billing.controller.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ const getUsage = async (req, res) => {
6767
try {
6868
// Determine current plan via service layer
6969
const subscription = await BillingService.getSubscription(req.organization._id);
70-
const plan = (!subscription || subscription.status === 'past_due') ? 'free' : (subscription.plan || 'free');
70+
const activeStatuses = ['active', 'trialing'];
71+
const plan = (!subscription || !activeStatuses.includes(subscription.status)) ? 'free' : (subscription.plan || 'free');
7172

7273
// Get usage counters (includes month field)
7374
const usage = await BillingUsageService.get(req.organization._id.toString());

modules/billing/tests/billing.usage.endpoint.unit.tests.js

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,15 @@ describe('Billing usage endpoint unit tests:', () => {
9797
}));
9898
});
9999

100-
test('should return free plan when subscription is past_due', async () => {
101-
mockBillingService.getSubscription.mockResolvedValue({ plan: 'starter', status: 'past_due' });
100+
test.each([
101+
'past_due',
102+
'canceled',
103+
'unpaid',
104+
'incomplete',
105+
'incomplete_expired',
106+
'paused',
107+
])('should return free plan when subscription status is %s', async (status) => {
108+
mockBillingService.getSubscription.mockResolvedValue({ plan: 'starter', status });
102109
mockBillingUsageService.get.mockResolvedValue({ month: '2026-03', counters: { 'documents.create': 2 } });
103110

104111
const req = { organization: { _id: orgId } };
@@ -113,6 +120,22 @@ describe('Billing usage endpoint unit tests:', () => {
113120
}));
114121
});
115122

123+
test('should return paid plan when subscription status is trialing', async () => {
124+
mockBillingService.getSubscription.mockResolvedValue({ plan: 'starter', status: 'trialing' });
125+
mockBillingUsageService.get.mockResolvedValue({ month: '2026-03', counters: {} });
126+
127+
const req = { organization: { _id: orgId } };
128+
await billingController.getUsage(req, res);
129+
130+
expect(res.status).toHaveBeenCalledWith(200);
131+
expect(res.json).toHaveBeenCalledWith(expect.objectContaining({
132+
data: expect.objectContaining({
133+
plan: 'starter',
134+
limits: { 'documents.create': 20, 'requests.execute': 2000 },
135+
}),
136+
}));
137+
});
138+
116139
test('should return empty limits when plan has no quota config', async () => {
117140
mockBillingService.getSubscription.mockResolvedValue({ plan: 'enterprise', status: 'active' });
118141
mockBillingUsageService.get.mockResolvedValue({ month: '2026-03', counters: {} });

0 commit comments

Comments
 (0)