Fix credit-counter reset on early subscription renewal
extendUserTier called setUserTier, which unconditionally zeroed monthly_consumed and re-anchored the cycle. A user who renewed mid-cycle (or a webhook double-firing across a restart) got their full monthly allotment back for free. The monthly cycle already rolls on its own anniversary via ensureRenewalRollover, so renewal must not reset it. Add resetCycle to setUserTier (default true, preserving operator-grant behavior); extendUserTier passes false for an in-force subscription and true only for a brand-new or lapsed one. Add regression tests.
This commit is contained in:
@@ -76,4 +76,26 @@ describe("extendUserTier (prepaid periods)", () => {
|
||||
const days = (new Date(renewed.subscription_expires_at).getTime() - Date.now()) / DAY;
|
||||
assert.ok(days > 29.9 && days < 30.1, `fresh ~30 days from now, got ${days}`);
|
||||
});
|
||||
|
||||
test("early renewal PRESERVES the monthly credit counter (no free reset)", async () => {
|
||||
await extendUserTier({ userId: "u4", tier: "pro", periodDays: 30 });
|
||||
const row = await getUserCreditRow("u4");
|
||||
row.monthly_consumed = 7; // simulate credits already spent this cycle
|
||||
row.monthly_gemini_consumed = 3;
|
||||
// Pay early / renew while the subscription is still in force.
|
||||
await extendUserTier({ userId: "u4", tier: "pro", periodDays: 30 });
|
||||
const after = await getUserCreditRow("u4");
|
||||
assert.equal(after.monthly_consumed, 7, "consumed credits must survive an early renewal");
|
||||
assert.equal(after.monthly_gemini_consumed, 3);
|
||||
});
|
||||
|
||||
test("resubscribing AFTER a lapse starts a fresh cycle (counter reset)", async () => {
|
||||
await extendUserTier({ userId: "u5", tier: "pro", periodDays: 30 });
|
||||
const row = await getUserCreditRow("u5");
|
||||
row.monthly_consumed = 9;
|
||||
row.subscription_expires_at = new Date(Date.now() - 5 * DAY).toISOString(); // lapsed
|
||||
await extendUserTier({ userId: "u5", tier: "pro", periodDays: 30 });
|
||||
const after = await getUserCreditRow("u5");
|
||||
assert.equal(after.monthly_consumed, 0, "a lapsed resubscribe starts a clean cycle");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user