mirror of
https://github.com/HeyPuter/puter.git
synced 2026-08-26 07:57:10 +00:00
fix: double counting purchased credit usage (#3359)
This commit is contained in:
@@ -787,6 +787,45 @@ describe('MeteringService', () => {
|
||||
expect(await target.hasAnyUsage(actor)).toBe(false);
|
||||
});
|
||||
|
||||
it('does not double-charge same-month overage against remaining (usage total + consumed credits)', async () => {
|
||||
const sub = await target.getActorSubscription(actor);
|
||||
await target.updateAddonCredit(actor.user.uuid, 5_000_000);
|
||||
|
||||
// Exhaust the allowance, then overspend by 1_000_000 — the overage
|
||||
// is consumed from purchased credits.
|
||||
await target.incrementUsage(
|
||||
actor,
|
||||
'kv:read',
|
||||
1,
|
||||
sub.monthUsageAllowance,
|
||||
);
|
||||
await target.incrementUsage(actor, 'kv:read', 1, 1_000_000);
|
||||
await waitFor(async () => {
|
||||
const addons = await target.getActorAddons(actor);
|
||||
expect(addons.consumedPurchaseCredits).toBe(1_000_000);
|
||||
});
|
||||
|
||||
// The overage already lives in both this month's usage total and
|
||||
// consumedPurchaseCredits; remaining must only be reduced once.
|
||||
const allowed = await target.getAllowedUsage(actor);
|
||||
expect(allowed.remaining).toBe(4_000_000);
|
||||
});
|
||||
|
||||
it('counts consumed credits from prior months against the credit pool only', async () => {
|
||||
// Simulate a prior-month overage: consumed credits exist but the
|
||||
// current month has no usage (monthly usage keys roll over).
|
||||
await target.updateAddonCredit(actor.user.uuid, 5_000_000);
|
||||
await server.stores.kv.incr({
|
||||
key: `${POLICY_PREFIX}:actor:${actor.user.uuid}:addons`,
|
||||
pathAndAmountMap: { consumedPurchaseCredits: 2_000_000 },
|
||||
});
|
||||
|
||||
const allowed = await target.getAllowedUsage(actor);
|
||||
expect(allowed.remaining).toBe(
|
||||
allowed.monthUsageAllowance + 3_000_000,
|
||||
);
|
||||
});
|
||||
|
||||
it('hasEnoughCredits compares remaining against the requested amount', async () => {
|
||||
await target.updateAddonCredit(actor.user.uuid, 1_000);
|
||||
expect(await target.hasEnoughCredits(actor, 100)).toBe(true);
|
||||
|
||||
@@ -627,13 +627,21 @@ export class MeteringService extends PuterService {
|
||||
],
|
||||
);
|
||||
|
||||
const remaining = Math.max(
|
||||
// Overage past the allowance is already charged to purchased credits
|
||||
// via consumedPurchaseCredits, so the allowance and the credit pool
|
||||
// must be netted separately — subtracting month usage AND consumed
|
||||
// credits from one combined pool would charge the overage twice.
|
||||
const remainingAllowance = Math.max(
|
||||
0,
|
||||
(userSubscription.monthUsageAllowance || 0) +
|
||||
(addons?.purchasedCredits || 0) -
|
||||
(currentMonthUsage.usage.total || 0) -
|
||||
(userSubscription.monthUsageAllowance || 0) -
|
||||
(currentMonthUsage.usage.total || 0),
|
||||
);
|
||||
const remainingPurchasedCredits = Math.max(
|
||||
0,
|
||||
(addons?.purchasedCredits || 0) -
|
||||
(addons?.consumedPurchaseCredits || 0),
|
||||
);
|
||||
const remaining = remainingAllowance + remainingPurchasedCredits;
|
||||
|
||||
return {
|
||||
remaining,
|
||||
|
||||
Reference in New Issue
Block a user