feat(p9): per-tenant outbound quota + cellId assignment
Task T3.4: OutboundService gains a per-tenant egress cap (tenantLimit/tenantWindowMs, IIOS_TENANT_OUTBOUND_*) counting a scope's commands in the window — a noisy tenant is RATE_LIMITED while others keep sending (noisy-neighbor protection). Persists scopeId on every command (fixes the previously-unscoped table). resolveScope assigns IiosScope.cellId (IIOS_CELL_ID, default cell-default) — the cell-partition hook. Tests: per-tenant cap isolates tenants; new scopes carry a cellId. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -133,4 +133,15 @@ describe('Adapter outbound (P5)', () => {
|
||||
expect(cmd.status).toBe('BLOCKED');
|
||||
expect(await prisma.iiosDeliveryAttempt.count({ where: { commandId: cmd.id, status: 'SENT' } })).toBe(0);
|
||||
});
|
||||
|
||||
it('per-tenant quota: a noisy tenant is capped while another still sends (P9 slice 3)', async () => {
|
||||
const svc = outbound();
|
||||
svc.tenantLimit = 1;
|
||||
const a1 = await svc.send('WEBHOOK', 't1', {}, 'ta-1', 'scope-A');
|
||||
const a2 = await svc.send('WEBHOOK', 't2', {}, 'ta-2', 'scope-A'); // A over its cap
|
||||
const b1 = await svc.send('WEBHOOK', 't3', {}, 'tb-1', 'scope-B'); // B unaffected
|
||||
expect(a1.status).toBe('SENT');
|
||||
expect(a2.status).toBe('RATE_LIMITED');
|
||||
expect(b1.status).toBe('SENT');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user