From 2359c7646b26ea1a2827c1f2144f3f52db496a95 Mon Sep 17 00:00:00 2001 From: Erica Hinkle Date: Tue, 28 Jul 2026 12:59:24 -0400 Subject: [PATCH] OCPBUGS-95592: Humanize memory and storage values in ResourceQuota display Memory and storage quota values were displayed as raw bytes (e.g., "2147483648") making them difficult to read. This change applies human-readable formatting (e.g., "2 GiB") to memory, storage, and ephemeral-storage resource types. Changes: - Import humanizeBinaryBytes utility - Add isBinaryResourceType helper to identify byte-based resources - Add formatResourceValue helper to conditionally humanize values - Update ResourceUsageRow to use formatResourceValue for all quota displays - Add unit tests for memory and storage humanization Co-Authored-By: Claude Sonnet 4.5 --- .../__tests__/resource-quota.spec.tsx | 58 +++++++++++++++++++ frontend/public/components/resource-quota.jsx | 28 +++++++-- 2 files changed, 80 insertions(+), 6 deletions(-) diff --git a/frontend/public/components/__tests__/resource-quota.spec.tsx b/frontend/public/components/__tests__/resource-quota.spec.tsx index b4817e4b6e3..c8a1291d459 100644 --- a/frontend/public/components/__tests__/resource-quota.spec.tsx +++ b/frontend/public/components/__tests__/resource-quota.spec.tsx @@ -189,3 +189,61 @@ describe('Check applied cluster quota table columns by ResourceUsageRow', () => expect(screen.getByText('2')).toBeVisible(); }); }); + +describe('Check memory resource humanization in ResourceUsageRow', () => { + const memoryQuota = { + apiVersion: 'v1', + kind: 'ResourceQuota', + metadata: { name: 'example', namespace: 'example' }, + spec: { hard: { 'requests.memory': '2147483648' } }, + status: { + hard: { 'requests.memory': '2147483648' }, + used: { 'requests.memory': '1073741824' }, + }, + }; + + it('displays memory values in human-readable format (GiB)', () => { + renderWithProviders( + + + + +
, + ); + + // Verify the resource type + expect(screen.getByText('requests.memory')).toBeVisible(); + + // Verify memory values are humanized (1 GiB used, 2 GiB limit) + expect(screen.getByText('1 GiB')).toBeVisible(); + expect(screen.getByText('2 GiB')).toBeVisible(); + }); + + const storageQuota = { + apiVersion: 'v1', + kind: 'ResourceQuota', + metadata: { name: 'example', namespace: 'example' }, + spec: { hard: { 'requests.storage': '10737418240' } }, + status: { + hard: { 'requests.storage': '10737418240' }, + used: { 'requests.storage': '5368709120' }, + }, + }; + + it('displays storage values in human-readable format (GiB)', () => { + renderWithProviders( + + + + +
, + ); + + // Verify the resource type + expect(screen.getByText('requests.storage')).toBeVisible(); + + // Verify storage values are humanized (5 GiB used, 10 GiB limit) + expect(screen.getByText('5 GiB')).toBeVisible(); + expect(screen.getByText('10 GiB')).toBeVisible(); + }); +}); diff --git a/frontend/public/components/resource-quota.jsx b/frontend/public/components/resource-quota.jsx index 21cb9100baf..9d746c5837e 100644 --- a/frontend/public/components/resource-quota.jsx +++ b/frontend/public/components/resource-quota.jsx @@ -24,7 +24,7 @@ import { SectionHeading } from './utils/headings'; import { navFactory } from './utils/horizontal-nav'; import { ResourceLink } from './utils/resource-link'; import { ResourceSummary } from './utils/details-page'; -import { convertToBaseValue } from './utils/units'; +import { convertToBaseValue, humanizeBinaryBytes } from './utils/units'; import { FieldLevelHelp } from './utils/field-level-help'; import { useAccessReview } from './utils/rbac'; import { LabelList } from './utils/label-list'; @@ -82,6 +82,22 @@ const getQuotaResourceTypes = (quota) => { return _.keys(specHard).sort(); }; +const isBinaryResourceType = (resourceType) => { + // Resource types that should be displayed as binary bytes (e.g., GiB, MiB) + return ( + resourceType.includes('memory') || + resourceType.includes('storage') || + resourceType.includes('ephemeral-storage') + ); +}; + +const formatResourceValue = (value, resourceType) => { + if (value === null || value === undefined) { + return DASH; + } + return isBinaryResourceType(resourceType) ? humanizeBinaryBytes(value).string : value; +}; + export const getACRQResourceUsage = (quota, resourceType, namespace) => { let used; if (namespace) { @@ -190,9 +206,9 @@ export const ResourceUsageRow = ({ quota, resourceType, namespace = undefined }) - {used.namespace} - {totalUsed} - {max} + {formatResourceValue(used.namespace, resourceType)} + {formatResourceValue(totalUsed, resourceType)} + {formatResourceValue(max, resourceType)} ); } @@ -204,8 +220,8 @@ export const ResourceUsageRow = ({ quota, resourceType, namespace = undefined }) - {used} - {max} + {formatResourceValue(used, resourceType)} + {formatResourceValue(max, resourceType)} ); };