[verified] refactor: unify dashboard rate windows
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
@@ -30,7 +30,7 @@ import {
|
|||||||
import { toast, Toaster } from 'sonner';
|
import { toast, Toaster } from 'sonner';
|
||||||
import { api } from '@/lib/api';
|
import { api } from '@/lib/api';
|
||||||
import { clearToken, getToken, setToken } from '@/lib/storage';
|
import { clearToken, getToken, setToken } from '@/lib/storage';
|
||||||
import { flattenNumericMetrics, formatDate, titleizeMetric } from '@/lib/utils';
|
import { summarizeUsageWindows, formatDate, formatDurationSeconds } from '@/lib/utils';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
@@ -568,10 +568,6 @@ function Dashboard() {
|
|||||||
onError: (error: Error) => toast.error(error.message),
|
onError: (error: Error) => toast.error(error.message),
|
||||||
});
|
});
|
||||||
|
|
||||||
const metricCards = useMemo(() => {
|
|
||||||
return flattenNumericMetrics(summaryQuery.data?.aggregatedUsage).slice(0, 6);
|
|
||||||
}, [summaryQuery.data?.aggregatedUsage]);
|
|
||||||
|
|
||||||
if (summaryQuery.isLoading || userQuery.isLoading) {
|
if (summaryQuery.isLoading || userQuery.isLoading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen items-center justify-center text-slate-300">
|
<div className="flex min-h-screen items-center justify-center text-slate-300">
|
||||||
@@ -601,12 +597,27 @@ function Dashboard() {
|
|||||||
|
|
||||||
const summary = summaryQuery.data!;
|
const summary = summaryQuery.data!;
|
||||||
const user = userQuery.data!;
|
const user = userQuery.data!;
|
||||||
const firstMetric = metricCards[0]?.value ?? 0;
|
const usageWindows = summarizeUsageWindows(
|
||||||
const secondMetric = metricCards[1]?.value ?? 0;
|
summary.accounts.map((account) => account.usage),
|
||||||
const progressValue =
|
);
|
||||||
firstMetric + secondMetric > 0
|
const windowCards = [
|
||||||
? (firstMetric / (firstMetric + secondMetric)) * 100
|
{
|
||||||
: 0;
|
title: 'Primary window',
|
||||||
|
tone: 'text-sky-300',
|
||||||
|
window: usageWindows.primary,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Secondary window',
|
||||||
|
tone: 'text-violet-300',
|
||||||
|
window: usageWindows.secondary,
|
||||||
|
},
|
||||||
|
].filter(
|
||||||
|
(item): item is {
|
||||||
|
title: string;
|
||||||
|
tone: string;
|
||||||
|
window: NonNullable<typeof usageWindows.primary>;
|
||||||
|
} => item.window !== null,
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto min-h-screen max-w-7xl px-4 py-5 sm:px-6 lg:px-8">
|
<div className="mx-auto min-h-screen max-w-7xl px-4 py-5 sm:px-6 lg:px-8">
|
||||||
@@ -647,25 +658,53 @@ function Dashboard() {
|
|||||||
<CardTitle>Unified capacity</CardTitle>
|
<CardTitle>Unified capacity</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-4">
|
<CardContent className="space-y-4">
|
||||||
<div className="flex items-end justify-between gap-4">
|
{windowCards.length > 0 ? (
|
||||||
|
<div className="grid gap-4 md:grid-cols-2">
|
||||||
|
{windowCards.map((item) => {
|
||||||
|
const usedPercent = item.window.usedPercent;
|
||||||
|
const progressValue =
|
||||||
|
usedPercent !== null && usedPercent !== undefined
|
||||||
|
? Math.max(0, Math.min(100, usedPercent))
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={item.title}
|
||||||
|
className="rounded-2xl border border-white/10 bg-white/4 p-4"
|
||||||
|
>
|
||||||
|
<div className="flex items-start justify-between gap-4">
|
||||||
<div>
|
<div>
|
||||||
<div className="text-4xl font-semibold text-white">
|
<div className="text-sm text-slate-400">{item.title}</div>
|
||||||
{firstMetric.toLocaleString()}
|
<div className={`mt-2 text-3xl font-semibold ${item.tone}`}>
|
||||||
|
{usedPercent !== null && usedPercent !== undefined
|
||||||
|
? `${usedPercent.toFixed(0)}%`
|
||||||
|
: '—'}
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-1 text-sm text-slate-400">
|
<div className="mt-1 text-xs uppercase tracking-[0.2em] text-slate-500">
|
||||||
{titleizeMetric(metricCards[0]?.label ?? 'Primary metric')}
|
{item.window.accountCount
|
||||||
|
? `Window data from ${item.window.accountCount} account${item.window.accountCount === 1 ? '' : 's'}`
|
||||||
|
: 'Used'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-right">
|
<div className="text-right text-sm text-slate-400">
|
||||||
<div className="text-2xl font-semibold text-slate-100">
|
{item.window.limitWindowSeconds !== null ? (
|
||||||
{secondMetric.toLocaleString()}
|
<div>{formatDurationSeconds(item.window.limitWindowSeconds)}</div>
|
||||||
</div>
|
) : null}
|
||||||
<div className="mt-1 text-sm text-slate-500">
|
<div className={item.window.limitWindowSeconds !== null ? 'mt-1' : ''}>
|
||||||
{titleizeMetric(metricCards[1]?.label ?? 'Secondary metric')}
|
Resets {formatDate(item.window.resetAt)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Progress value={progressValue} />
|
<Progress value={progressValue} className="mt-4" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="rounded-2xl border border-dashed border-white/10 bg-white/3 p-6 text-sm text-slate-400">
|
||||||
|
No rate-limit window data yet. Connect an OpenAI account and refresh to load Codex usage windows.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div className="flex flex-wrap gap-3 text-sm text-slate-400">
|
<div className="flex flex-wrap gap-3 text-sm text-slate-400">
|
||||||
<span>Accounts: {summary.totals.totalAccounts}</span>
|
<span>Accounts: {summary.totals.totalAccounts}</span>
|
||||||
<span>Healthy: {summary.totals.activeAccounts}</span>
|
<span>Healthy: {summary.totals.activeAccounts}</span>
|
||||||
@@ -705,35 +744,6 @@ function Dashboard() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Card className="mt-6 min-w-0">
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle>Usage metrics</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="min-w-0">
|
|
||||||
{metricCards.length === 0 ? (
|
|
||||||
<div className="rounded-2xl border border-dashed border-white/10 bg-white/3 p-6 text-sm text-slate-400">
|
|
||||||
No usage data yet. Connect an OpenAI account and complete the sign-in flow to start refreshing.
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="grid gap-3 sm:grid-cols-2">
|
|
||||||
{metricCards.map((metric) => (
|
|
||||||
<div
|
|
||||||
key={metric.label}
|
|
||||||
className="min-w-0 rounded-2xl border border-white/10 bg-white/4 p-4"
|
|
||||||
>
|
|
||||||
<div className="text-sm text-slate-400 break-words">
|
|
||||||
{titleizeMetric(metric.label)}
|
|
||||||
</div>
|
|
||||||
<div className="mt-3 text-2xl font-semibold text-white">
|
|
||||||
{metric.value.toLocaleString()}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card className="mt-6">
|
<Card className="mt-6">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>Connected OpenAI accounts</CardTitle>
|
<CardTitle>Connected OpenAI accounts</CardTitle>
|
||||||
|
|||||||
@@ -13,6 +13,181 @@ export function formatDate(value: string | null) {
|
|||||||
}).format(new Date(value));
|
}).format(new Date(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UsageWindowSummary = {
|
||||||
|
usedPercent: number | null;
|
||||||
|
limitWindowSeconds: number | null;
|
||||||
|
resetAt: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
type AggregatedUsageWindowSummary = UsageWindowSummary & {
|
||||||
|
accountCount: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||||
|
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toFiniteNumber(value: unknown) {
|
||||||
|
return typeof value === 'number' && Number.isFinite(value) ? value : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPath(value: unknown, path: string[]) {
|
||||||
|
let current: unknown = value;
|
||||||
|
for (const segment of path) {
|
||||||
|
if (!isRecord(current) || !(segment in current)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
current = current[segment];
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeResetAt(value: unknown) {
|
||||||
|
if (typeof value === 'string' && value.trim()) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof value === 'number' && Number.isFinite(value)) {
|
||||||
|
return new Date(value * 1000).toISOString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractWindow(value: unknown, paths: string[][]): UsageWindowSummary | null {
|
||||||
|
for (const path of paths) {
|
||||||
|
const windowValue = getPath(value, path);
|
||||||
|
|
||||||
|
if (!isRecord(windowValue)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const summary = {
|
||||||
|
usedPercent: toFiniteNumber(windowValue.used_percent),
|
||||||
|
limitWindowSeconds:
|
||||||
|
toFiniteNumber(windowValue.limit_window_seconds) ??
|
||||||
|
(toFiniteNumber(windowValue.window_minutes) !== null
|
||||||
|
? toFiniteNumber(windowValue.window_minutes)! * 60
|
||||||
|
: null),
|
||||||
|
resetAt: normalizeResetAt(windowValue.reset_at ?? windowValue.resets_at),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (
|
||||||
|
summary.usedPercent === null &&
|
||||||
|
summary.limitWindowSeconds === null &&
|
||||||
|
summary.resetAt === null
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return summary;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function extractUsageWindows(value: unknown) {
|
||||||
|
return {
|
||||||
|
primary: extractWindow(value, [
|
||||||
|
['rate_limit', 'primary_window'],
|
||||||
|
['rate_limits', 'primary'],
|
||||||
|
['primary_window'],
|
||||||
|
]),
|
||||||
|
secondary: extractWindow(value, [
|
||||||
|
['rate_limit', 'secondary_window'],
|
||||||
|
['rate_limits', 'secondary'],
|
||||||
|
['secondary_window'],
|
||||||
|
]),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function average(values: number[]) {
|
||||||
|
if (values.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return values.reduce((sum, value) => sum + value, 0) / values.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
function earliestDate(values: Array<string | null>) {
|
||||||
|
const validDates = values
|
||||||
|
.filter((value): value is string => Boolean(value))
|
||||||
|
.map((value) => ({ value, timestamp: Date.parse(value) }))
|
||||||
|
.filter((value) => Number.isFinite(value.timestamp))
|
||||||
|
.sort((left, right) => left.timestamp - right.timestamp);
|
||||||
|
|
||||||
|
return validDates[0]?.value ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function summarizeWindowCollection(
|
||||||
|
windows: Array<UsageWindowSummary | null>,
|
||||||
|
): AggregatedUsageWindowSummary | null {
|
||||||
|
const presentWindows = windows.filter(
|
||||||
|
(window): window is UsageWindowSummary => window !== null,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (presentWindows.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const usedPercentValues = presentWindows
|
||||||
|
.map((window) => window.usedPercent)
|
||||||
|
.filter((value): value is number => value !== null);
|
||||||
|
|
||||||
|
const limitWindowValues = presentWindows
|
||||||
|
.map((window) => window.limitWindowSeconds)
|
||||||
|
.filter((value): value is number => value !== null);
|
||||||
|
|
||||||
|
const uniqueLimitWindowValues = [...new Set(limitWindowValues)];
|
||||||
|
|
||||||
|
return {
|
||||||
|
accountCount: presentWindows.length,
|
||||||
|
usedPercent: average(usedPercentValues),
|
||||||
|
limitWindowSeconds:
|
||||||
|
limitWindowValues.length === presentWindows.length &&
|
||||||
|
uniqueLimitWindowValues.length === 1
|
||||||
|
? uniqueLimitWindowValues[0]
|
||||||
|
: null,
|
||||||
|
resetAt: earliestDate(presentWindows.map((window) => window.resetAt)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function summarizeUsageWindows(values: Array<unknown>) {
|
||||||
|
const extractedWindows = values.map((value) => extractUsageWindows(value));
|
||||||
|
|
||||||
|
return {
|
||||||
|
primary: summarizeWindowCollection(
|
||||||
|
extractedWindows.map((windows) => windows.primary),
|
||||||
|
),
|
||||||
|
secondary: summarizeWindowCollection(
|
||||||
|
extractedWindows.map((windows) => windows.secondary),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatDurationSeconds(value: number | null) {
|
||||||
|
if (!value || value <= 0) {
|
||||||
|
return 'Unknown window';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value % 86_400 === 0) {
|
||||||
|
const days = value / 86_400;
|
||||||
|
return `${days}d window`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value % 3_600 === 0) {
|
||||||
|
const hours = value / 3_600;
|
||||||
|
return `${hours}h window`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value % 60 === 0) {
|
||||||
|
const minutes = value / 60;
|
||||||
|
return `${minutes}m window`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${value}s window`;
|
||||||
|
}
|
||||||
|
|
||||||
export function flattenNumericMetrics(
|
export function flattenNumericMetrics(
|
||||||
value: unknown,
|
value: unknown,
|
||||||
path: string[] = [],
|
path: string[] = [],
|
||||||
|
|||||||
20
apps/web/test/dashboard-capacity-windows.test.js
Normal file
20
apps/web/test/dashboard-capacity-windows.test.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { describe, expect, test } from 'bun:test';
|
||||||
|
import { readFileSync } from 'node:fs';
|
||||||
|
import { join } from 'node:path';
|
||||||
|
|
||||||
|
const appSource = readFileSync(join(import.meta.dir, '../src/App.tsx'), 'utf8');
|
||||||
|
|
||||||
|
describe('dashboard unified capacity windows', () => {
|
||||||
|
test('shows primary and secondary windows inside unified capacity and removes usage metrics card', () => {
|
||||||
|
expect(appSource).toContain('<CardTitle>Unified capacity</CardTitle>');
|
||||||
|
expect(appSource).toContain('Primary window');
|
||||||
|
expect(appSource).toContain('Secondary window');
|
||||||
|
expect(appSource).toContain('const windowCards = [');
|
||||||
|
expect(appSource).toContain("} => item.window !== null,");
|
||||||
|
expect(appSource).toContain('item.window.limitWindowSeconds !== null ? (');
|
||||||
|
expect(appSource).not.toContain('<CardTitle>Usage metrics</CardTitle>');
|
||||||
|
expect(appSource).not.toContain('flattenNumericMetrics(summaryQuery.data?.aggregatedUsage).slice(0, 6)');
|
||||||
|
expect(appSource).not.toContain('const firstMetric = metricCards[0]?.value ?? 0;');
|
||||||
|
expect(appSource).not.toContain('const secondMetric = metricCards[1]?.value ?? 0;');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -7,8 +7,10 @@ const appSource = readFileSync(join(import.meta.dir, '../src/App.tsx'), 'utf8');
|
|||||||
describe('dashboard card copy', () => {
|
describe('dashboard card copy', () => {
|
||||||
test('removes verbose dashboard card descriptions and keeps concise labels', () => {
|
test('removes verbose dashboard card descriptions and keeps concise labels', () => {
|
||||||
expect(appSource).toContain('<CardTitle>Unified capacity</CardTitle>');
|
expect(appSource).toContain('<CardTitle>Unified capacity</CardTitle>');
|
||||||
expect(appSource).toContain('<CardTitle>Usage metrics</CardTitle>');
|
|
||||||
expect(appSource).toContain('<CardTitle>Connected OpenAI accounts</CardTitle>');
|
expect(appSource).toContain('<CardTitle>Connected OpenAI accounts</CardTitle>');
|
||||||
|
expect(appSource).toContain('Primary window');
|
||||||
|
expect(appSource).toContain('Secondary window');
|
||||||
|
expect(appSource).not.toContain('<CardTitle>Usage metrics</CardTitle>');
|
||||||
expect(appSource).toContain(">Merged by default. Inspect each account below.<");
|
expect(appSource).toContain(">Merged by default. Inspect each account below.<");
|
||||||
expect(appSource).not.toContain(
|
expect(appSource).not.toContain(
|
||||||
'Fast glance card for the first two numeric metrics extracted from the merged usage payload.',
|
'Fast glance card for the first two numeric metrics extracted from the merged usage payload.',
|
||||||
|
|||||||
@@ -5,11 +5,10 @@ import { join } from 'node:path';
|
|||||||
const appSource = readFileSync(join(import.meta.dir, '../src/App.tsx'), 'utf8');
|
const appSource = readFileSync(join(import.meta.dir, '../src/App.tsx'), 'utf8');
|
||||||
|
|
||||||
describe('mobile overflow guards', () => {
|
describe('mobile overflow guards', () => {
|
||||||
test('usage metrics cards allow long metric labels to wrap on mobile', () => {
|
test('unified capacity window cards stay in a responsive two-column grid', () => {
|
||||||
expect(appSource).toContain('className="mt-6 min-w-0"');
|
expect(appSource).toContain('className="grid gap-4 md:grid-cols-2"');
|
||||||
expect(appSource).toContain('className="grid gap-3 sm:grid-cols-2"');
|
expect(appSource).toContain('className="rounded-2xl border border-white/10 bg-white/4 p-4"');
|
||||||
expect(appSource).toContain('className="min-w-0 rounded-2xl border border-white/10 bg-white/4 p-4"');
|
expect(appSource).toContain('className="text-sm text-slate-400">{item.title}</div>');
|
||||||
expect(appSource).toContain('className="text-sm text-slate-400 break-words"');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('connected account tabs no longer render a side-by-side payload column', () => {
|
test('connected account tabs no longer render a side-by-side payload column', () => {
|
||||||
|
|||||||
175
apps/web/test/usage-window-helpers.test.js
Normal file
175
apps/web/test/usage-window-helpers.test.js
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
import { describe, expect, test } from 'bun:test';
|
||||||
|
import {
|
||||||
|
extractUsageWindows,
|
||||||
|
formatDurationSeconds,
|
||||||
|
summarizeUsageWindows,
|
||||||
|
} from '../src/lib/utils';
|
||||||
|
|
||||||
|
describe('usage window helpers', () => {
|
||||||
|
test('extracts primary and secondary windows from codex rate_limit payloads', () => {
|
||||||
|
const windows = extractUsageWindows({
|
||||||
|
rate_limit: {
|
||||||
|
primary_window: {
|
||||||
|
used_percent: 14,
|
||||||
|
limit_window_seconds: 18_000,
|
||||||
|
reset_at: '2026-05-09T12:00:00.000Z',
|
||||||
|
},
|
||||||
|
secondary_window: {
|
||||||
|
used_percent: 19,
|
||||||
|
limit_window_seconds: 604_800,
|
||||||
|
reset_at: '2026-05-16T12:00:00.000Z',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(windows).toEqual({
|
||||||
|
primary: {
|
||||||
|
usedPercent: 14,
|
||||||
|
limitWindowSeconds: 18_000,
|
||||||
|
resetAt: '2026-05-09T12:00:00.000Z',
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
usedPercent: 19,
|
||||||
|
limitWindowSeconds: 604_800,
|
||||||
|
resetAt: '2026-05-16T12:00:00.000Z',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('formats canonical codex limit windows into readable labels', () => {
|
||||||
|
expect(formatDurationSeconds(18_000)).toBe('5h window');
|
||||||
|
expect(formatDurationSeconds(604_800)).toBe('7d window');
|
||||||
|
expect(formatDurationSeconds(null)).toBe('Unknown window');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('summarizes windows across multiple account payloads without summing percentages', () => {
|
||||||
|
const windows = summarizeUsageWindows([
|
||||||
|
{
|
||||||
|
rate_limit: {
|
||||||
|
primary_window: {
|
||||||
|
used_percent: 20,
|
||||||
|
limit_window_seconds: 18_000,
|
||||||
|
reset_at: '2026-05-09T12:00:00.000Z',
|
||||||
|
},
|
||||||
|
secondary_window: {
|
||||||
|
used_percent: 40,
|
||||||
|
limit_window_seconds: 604_800,
|
||||||
|
reset_at: '2026-05-16T12:00:00.000Z',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rate_limit: {
|
||||||
|
primary_window: {
|
||||||
|
used_percent: 60,
|
||||||
|
limit_window_seconds: 18_000,
|
||||||
|
reset_at: '2026-05-09T10:00:00.000Z',
|
||||||
|
},
|
||||||
|
secondary_window: {
|
||||||
|
used_percent: 80,
|
||||||
|
limit_window_seconds: 604_800,
|
||||||
|
reset_at: '2026-05-15T10:00:00.000Z',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(windows).toEqual({
|
||||||
|
primary: {
|
||||||
|
accountCount: 2,
|
||||||
|
usedPercent: 40,
|
||||||
|
limitWindowSeconds: 18_000,
|
||||||
|
resetAt: '2026-05-09T10:00:00.000Z',
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
accountCount: 2,
|
||||||
|
usedPercent: 60,
|
||||||
|
limitWindowSeconds: 604_800,
|
||||||
|
resetAt: '2026-05-15T10:00:00.000Z',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('keeps window summaries null-safe for partial or inconsistent account data', () => {
|
||||||
|
const windows = summarizeUsageWindows([
|
||||||
|
{
|
||||||
|
rate_limit: {
|
||||||
|
primary_window: {
|
||||||
|
limit_window_seconds: 18_000,
|
||||||
|
reset_at: '2026-05-09T12:00:00.000Z',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rate_limit: {
|
||||||
|
primary_window: {
|
||||||
|
used_percent: 50,
|
||||||
|
reset_at: '2026-05-09T11:00:00.000Z',
|
||||||
|
},
|
||||||
|
secondary_window: {
|
||||||
|
used_percent: 22,
|
||||||
|
limit_window_seconds: 604_800,
|
||||||
|
reset_at: '2026-05-16T12:00:00.000Z',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rate_limit: {
|
||||||
|
secondary_window: {
|
||||||
|
used_percent: 44,
|
||||||
|
limit_window_seconds: 86_400,
|
||||||
|
reset_at: '2026-05-15T12:00:00.000Z',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(windows).toEqual({
|
||||||
|
primary: {
|
||||||
|
accountCount: 2,
|
||||||
|
usedPercent: 50,
|
||||||
|
limitWindowSeconds: null,
|
||||||
|
resetAt: '2026-05-09T11:00:00.000Z',
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
accountCount: 2,
|
||||||
|
usedPercent: 33,
|
||||||
|
limitWindowSeconds: null,
|
||||||
|
resetAt: '2026-05-15T12:00:00.000Z',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('ignores empty window shells with no meaningful values', () => {
|
||||||
|
expect(
|
||||||
|
extractUsageWindows({
|
||||||
|
rate_limit: {
|
||||||
|
primary_window: {},
|
||||||
|
secondary_window: null,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).toEqual({ primary: null, secondary: null });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('falls back to later candidate paths when earlier window shells are empty', () => {
|
||||||
|
expect(
|
||||||
|
extractUsageWindows({
|
||||||
|
rate_limit: {
|
||||||
|
primary_window: {},
|
||||||
|
},
|
||||||
|
primary_window: {
|
||||||
|
used_percent: 42,
|
||||||
|
limit_window_seconds: 18_000,
|
||||||
|
reset_at: '2026-05-09T12:00:00.000Z',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).toEqual({
|
||||||
|
primary: {
|
||||||
|
usedPercent: 42,
|
||||||
|
limitWindowSeconds: 18_000,
|
||||||
|
resetAt: '2026-05-09T12:00:00.000Z',
|
||||||
|
},
|
||||||
|
secondary: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user