Back to Delivery 3 Reliable Quality Utilities

delivery-03-reliable-quality-utilities/src/summary.ts

import type { QualityCheckObservation, QualityCheckSummary } from "./models.js";

/**
 * Shared precondition for the legacy and refactored implementations. A slow
 * threshold is a finite, non-negative duration in milliseconds.
 */
function validateSlowThreshold(slowThresholdMs: number): void {
  if (!Number.isFinite(slowThresholdMs) || slowThresholdMs < 0) {
    throw new Error("slowThresholdMs must be a non-negative finite number.");
  }
}

/**
 * A clear reference implementation used to characterize legacy behaviour over
 * the same valid input domain as `summariseChecks`.
 */
export function legacySummariseChecks(
  observations: readonly QualityCheckObservation[],
  slowThresholdMs: number,
): QualityCheckSummary {
  validateSlowThreshold(slowThresholdMs);

  let completedCount = 0;
  let failedCount = 0;
  const slowEndpoints = new Set<string>();

  for (const observation of observations) {
    if (observation.state === "complete") {
      completedCount += 1;
    }
    if (observation.state === "failed") {
      failedCount += 1;
    }
    if (observation.responseTimeMs > slowThresholdMs) {
      slowEndpoints.add(observation.endpoint);
    }
  }

  return {
    executionCount: observations.length,
    completedCount,
    failedCount,
    slowEndpointCount: slowEndpoints.size,
  };
}

function countState(
  observations: readonly QualityCheckObservation[],
  expectedState: QualityCheckObservation["state"],
): number {
  return observations.filter((observation) => observation.state === expectedState).length;
}

/**
 * Refactored equivalent of `legacySummariseChecks` over their shared valid
 * input domain. Its small helpers name the rules without changing the
 * externally observable summary contract.
 */
export function summariseChecks(
  observations: readonly QualityCheckObservation[],
  slowThresholdMs: number,
): QualityCheckSummary {
  validateSlowThreshold(slowThresholdMs);
  const slowEndpoints = new Set(
    observations
      .filter((observation) => observation.responseTimeMs > slowThresholdMs)
      .map((observation) => observation.endpoint),
  );

  return {
    executionCount: observations.length,
    completedCount: countState(observations, "complete"),
    failedCount: countState(observations, "failed"),
    slowEndpointCount: slowEndpoints.size,
  };
}