Files
headlamp-tns-csi-plugin/docs/development/testing.md
T
Chris Farhood e2512ec500 docs: clean up repo cruft and update all documentation for v0.2.4
- Remove PROMPT.md (AI scaffolding artifact)
- Add .claude/settings* to .gitignore
- Commit .mcp.json (MCP server config)
- Fix ArtifactHub URLs (headlamp/tns-csi path)
- Fix tarball name (tns-csi-VERSION.tar.gz) in all install docs
- Update version URLs from v0.1.0/v0.2.0 to v0.2.4
- Update test count from 67 to 159 across 12 files
- Update Node.js version from 20 to 22
- Add CHANGELOG entry for v0.2.4
- Update testing.md with full test file inventory and CI description

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 18:04:43 +00:00

5.1 KiB

Testing Guide

Test Suite Overview

The plugin has 159 unit tests across 12 test files:

File Tests Coverage
src/api/k8s.test.ts Type guards, filter helpers, format utilities k8s.ts
src/api/metrics.test.ts Prometheus text format parser metrics.ts
src/api/kbench.test.ts FIO log parser, manifest builders, format helpers kbench.ts
src/api/TnsCsiDataContext.test.tsx Context provider integration TnsCsiDataContext.tsx
src/components/OverviewPage.test.tsx Overview dashboard rendering OverviewPage.tsx
src/components/StorageClassesPage.test.tsx StorageClass list and detail panel StorageClassesPage.tsx
src/components/VolumesPage.test.tsx PV list and detail panel VolumesPage.tsx
src/components/SnapshotsPage.test.tsx VolumeSnapshot list SnapshotsPage.tsx
src/components/MetricsPage.test.tsx Prometheus metrics display MetricsPage.tsx
src/components/BenchmarkPage.test.tsx kbench runner UI BenchmarkPage.tsx
src/components/DriverStatusCard.test.tsx Driver health card DriverStatusCard.tsx
src/components/PVCDetailSection.test.tsx PVC detail injection PVCDetailSection.tsx

Running Tests

# Run all tests once
npm test

# Watch mode (re-runs on file changes)
npm run test:watch

# TypeScript type-check (no emit)
npm run tsc

All tests must pass before committing. The CI workflow enforces this.

Test Framework

  • Vitest — test runner
  • @testing-library/react — React component testing utilities
  • jsdom — DOM environment (configured in vitest.config.mts)

Mocking Headlamp APIs

Headlamp APIs must be mocked in tests. Use this pattern:

import { vi, describe, it, expect, beforeEach } from 'vitest';
import { render, screen } from '@testing-library/react';

vi.mock('@kinvolk/headlamp-plugin/lib', () => ({
  ApiProxy: {
    request: vi.fn().mockResolvedValue({ items: [] }),
  },
  K8s: {
    ResourceClasses: {
      StorageClass: {
        useList: vi.fn(() => [[], null]),
      },
      PersistentVolume: {
        useList: vi.fn(() => [[], null]),
      },
      PersistentVolumeClaim: {
        useList: vi.fn(() => [[], null]),
      },
    },
  },
}));

vi.mock('@kinvolk/headlamp-plugin/lib/CommonComponents', () => ({
  SectionBox: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
  SectionHeader: ({ title }: { title: string }) => <h1>{title}</h1>,
  SimpleTable: ({ data, emptyMessage }: { data: unknown[]; emptyMessage: string }) =>
    data.length === 0 ? <p>{emptyMessage}</p> : <table />,
  Loader: ({ title }: { title: string }) => <div>{title}</div>,
  // add other CommonComponents as needed
}));

Testing the Prometheus Parser

import { parsePrometheusText, extractTnsCsiMetrics } from '../metrics';

it('parses gauge metrics correctly', () => {
  const text = `
# HELP kubelet_volume_stats_capacity_bytes Capacity in bytes of the volume
# TYPE kubelet_volume_stats_capacity_bytes gauge
kubelet_volume_stats_capacity_bytes{namespace="default",persistentvolumeclaim="my-pvc"} 10737418240
`;
  const metrics = parsePrometheusText(text);
  expect(metrics.get('kubelet_volume_stats_capacity_bytes{namespace="default",persistentvolumeclaim="my-pvc"}')).toBe(10737418240);
});

Testing the FIO Log Parser

import { parseKbenchLog } from '../kbench';

it('parses kbench FIO output into result cards', () => {
  const log = `
READ: bw=512MiB/s (537MB/s), 512MiB/s-512MiB/s (537MB/s-537MB/s), io=32.0GiB (34.4GB), run=63999-63999msec
  iops        : min=128000, max=135000, avg=131072.00, stdev=1024.00, samples=64
  lat (usec)  : min=10, max=500, avg=50.00, stdev=20.00
`;
  const result = parseKbenchLog(log);
  expect(result).not.toBeNull();
  expect(result!.readBandwidthMBs).toBeGreaterThan(0);
});

Testing Type Guards

import { isTnsCsiStorageClass } from '../k8s';

it('identifies tns.csi.io provisioner', () => {
  expect(isTnsCsiStorageClass({ provisioner: 'tns.csi.io', metadata: { name: 'test' } })).toBe(true);
  expect(isTnsCsiStorageClass({ provisioner: 'other.csi.io', metadata: { name: 'test' } })).toBe(false);
  expect(isTnsCsiStorageClass(null)).toBe(false);
  expect(isTnsCsiStorageClass(undefined)).toBe(false);
});

vitest.setup.ts

The setup file shims localStorage for Node 22+ (jsdom doesn't provide it in some versions):

// vitest.setup.ts
if (typeof localStorage === 'undefined') {
  const store: Record<string, string> = {};
  Object.defineProperty(global, 'localStorage', {
    value: {
      getItem: (k: string) => store[k] ?? null,
      setItem: (k: string, v: string) => { store[k] = v; },
      removeItem: (k: string) => { delete store[k]; },
      clear: () => { Object.keys(store).forEach(k => delete store[k]); },
    },
  });
}

CI Test Enforcement

The GitHub Actions CI workflow runs lint, typecheck, and test as three parallel jobs on every push and PR. A fourth build job gates on all three passing. The test job uses a JUnit reporter that posts test summaries directly on PRs.

All three checks must pass for the PR to merge.