mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-04 06:12:10 +03:00
fix(quota-share): guard usage.dimensions to stop "reading 'length'" ISE
The pool usage snapshot can come back without a dimensions array (e.g. when the
plan resolves to empty for catalog-only providers). PoolCard.computeStatus and
hasDimensions read usage.dimensions.length directly, crashing the whole page
("Cannot read properties of undefined (reading 'length')"). Normalize to [] in
PoolCard and in usePoolsUsageAggregate (dimensions/perKey).
This commit is contained in:
@@ -24,8 +24,9 @@ export interface PoolCardProps {
|
||||
}
|
||||
|
||||
function computeStatus(usage: PoolUsageSnapshot | null): "green" | "amber" | "red" {
|
||||
if (!usage || usage.dimensions.length === 0) return "green";
|
||||
const utilizations = usage.dimensions.map((d) =>
|
||||
const dims = usage?.dimensions ?? [];
|
||||
if (dims.length === 0) return "green";
|
||||
const utilizations = dims.map((d) =>
|
||||
d.limit > 0 ? (d.consumedTotal / d.limit) * 100 : 0
|
||||
);
|
||||
const avg = utilizations.reduce((s, u) => s + u, 0) / utilizations.length;
|
||||
@@ -54,7 +55,7 @@ export default function PoolCard({
|
||||
const { icon: statusIcon, cls: statusCls } = STATUS_ICONS[status];
|
||||
|
||||
// Check for plan dimensions from usage
|
||||
const hasDimensions = usage && usage.dimensions.length > 0;
|
||||
const hasDimensions = !!usage?.dimensions?.length;
|
||||
|
||||
return (
|
||||
<Card padding="md">
|
||||
@@ -103,10 +104,10 @@ export default function PoolCard({
|
||||
<div
|
||||
className="grid gap-3 mb-3"
|
||||
style={{
|
||||
gridTemplateColumns: `repeat(${Math.min(usage.dimensions.length, 3)}, 1fr)`,
|
||||
gridTemplateColumns: `repeat(${Math.min(usage?.dimensions?.length ?? 0, 3)}, 1fr)`,
|
||||
}}
|
||||
>
|
||||
{usage.dimensions.map((dim, i) => (
|
||||
{(usage?.dimensions ?? []).map((dim, i) => (
|
||||
<DimensionBar
|
||||
key={`${dim.unit}-${dim.window}-${i}`}
|
||||
dimension={{ unit: dim.unit, window: dim.window, limit: dim.limit }}
|
||||
|
||||
@@ -40,12 +40,12 @@ export function usePoolsUsageAggregate(pools: QuotaPool[]): PoolsUsageAggregate
|
||||
let utilCount = 0;
|
||||
let borrowing = 0;
|
||||
for (const { usage } of valid) {
|
||||
for (const dim of usage.dimensions) {
|
||||
for (const dim of usage.dimensions ?? []) {
|
||||
if (dim.limit > 0) {
|
||||
totalUtil += (dim.consumedTotal / dim.limit) * 100;
|
||||
utilCount += 1;
|
||||
}
|
||||
for (const key of dim.perKey) {
|
||||
for (const key of dim.perKey ?? []) {
|
||||
if (key.borrowing) borrowing += 1;
|
||||
}
|
||||
}
|
||||
|
||||
23
tests/unit/v388-quota-share-usage-guard.test.ts
Normal file
23
tests/unit/v388-quota-share-usage-guard.test.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
|
||||
// Regression guard: quota-share crashed with "Cannot read properties of undefined
|
||||
// (reading 'length')" because PoolCard/aggregate read usage.dimensions without a
|
||||
// guard when the usage snapshot came back without a dimensions array.
|
||||
const root = join(import.meta.dirname, "../..");
|
||||
const read = (p: string) => readFileSync(join(root, p), "utf8");
|
||||
|
||||
test("quota-share PoolCard guards usage.dimensions", () => {
|
||||
const pc = read("src/app/(dashboard)/dashboard/costs/quota-share/components/PoolCard.tsx");
|
||||
assert.ok(pc.includes("usage?.dimensions ?? []"), "computeStatus normalizes dimensions to []");
|
||||
assert.ok(pc.includes("!!usage?.dimensions?.length"), "hasDimensions guards dimensions");
|
||||
assert.equal(/[^?.]usage\.dimensions\.length/.test(pc), false, "no unguarded usage.dimensions.length");
|
||||
});
|
||||
|
||||
test("quota-share aggregate hook guards dimensions/perKey", () => {
|
||||
const agg = read("src/app/(dashboard)/dashboard/costs/quota-share/hooks/usePoolsUsageAggregate.ts");
|
||||
assert.ok(agg.includes("usage.dimensions ?? []"), "iterates dimensions with ?? []");
|
||||
assert.ok(agg.includes("dim.perKey ?? []"), "iterates perKey with ?? []");
|
||||
});
|
||||
Reference in New Issue
Block a user