Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions src/cache-ttl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Convert a cache TTL configuration into bounded milliseconds.
*
* Noema accepts positive fractional configuration for compatibility with the
* existing numeric environment contract, but cache expiry must never normalize
* to zero. Values that are non-finite, non-positive, or smaller than one whole
* second therefore fall back to the reviewed default. Larger values are floored
* and capped before conversion to milliseconds.
*
* @param raw optional environment value expressed in seconds
* @param defaultSeconds safe fallback TTL in seconds
* @param maxSeconds maximum accepted TTL in seconds
* @returns a positive bounded TTL in milliseconds
*/
export function configuredTtlMs(
raw: string | undefined,
defaultSeconds: number,
maxSeconds: number,
): number {
const seconds = Number(raw ?? String(defaultSeconds));
if (!Number.isFinite(seconds) || seconds <= 0) return defaultSeconds * 1000;
const normalizedSeconds = Math.floor(seconds);
if (normalizedSeconds <= 0) return defaultSeconds * 1000;
return Math.min(normalizedSeconds, maxSeconds) * 1000;
}
11 changes: 4 additions & 7 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { configuredTtlMs } from "./cache-ttl";
import {
claimOidcTokenUsage,
OidcReplayDetected,
Expand Down Expand Up @@ -170,16 +171,12 @@ function safeHash(input: string): string {
function configuredRateLimit(env: Env): number {
const limit = Number(env.NOEMA_RATE_LIMIT_PER_MINUTE ?? "60");
if (!Number.isFinite(limit) || limit <= 0) return 60;
return Math.floor(limit);
const normalizedLimit = Math.floor(limit);
if (normalizedLimit <= 0) return 60;
return normalizedLimit;
}

/* v8 ignore start */
function configuredTtlMs(raw: string | undefined, defaultSeconds: number, maxSeconds: number): number {
const seconds = Number(raw ?? String(defaultSeconds));
if (!Number.isFinite(seconds) || seconds <= 0) return defaultSeconds * 1000;
return Math.min(Math.floor(seconds), maxSeconds) * 1000;
}

function valueType(value: unknown): string {
if (value === null) return "null";
if (Array.isArray(value)) return "array";
Expand Down
15 changes: 15 additions & 0 deletions test/cache-ttl.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { describe, expect, it } from "vitest";
import { configuredTtlMs } from "../src/cache-ttl";

describe("configured cache TTL normalization", () => {
it.each([
{ raw: undefined, expected: 300_000, reason: "uses the reviewed default when unset" },
{ raw: "NaN", expected: 300_000, reason: "rejects non-finite configuration" },
{ raw: "0", expected: 300_000, reason: "rejects non-positive configuration" },
{ raw: "0.5", expected: 300_000, reason: "does not normalize a positive fraction to zero" },
{ raw: "1.9", expected: 1_000, reason: "preserves existing whole-second floor semantics" },
{ raw: "7200", expected: 3_600_000, reason: "caps excessive configuration" },
])("$reason", ({ raw, expected }) => {
expect(configuredTtlMs(raw, 300, 3600)).toBe(expected);
});
});
1 change: 1 addition & 0 deletions test/configured-rate-limit-coverage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ describe("configured local rate-limit coverage", () => {
it.each([
["NaN", "198.51.100.202"],
["0", "198.51.100.203"],
["0.5", "198.51.100.205"],
])("fails safe to the default limit for invalid configured value %s", async (configuredLimit, client) => {
const [first, second] = await responsesFor(configuredLimit, client);
expect([first.status, second.status]).toEqual([401, 401]);
Expand Down
1 change: 1 addition & 0 deletions test/coverage-ignore-operational-helpers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ describe("operational helper coverage exclusions", () => {
"traceIdFromRequest",
"safeHash",
"configuredRateLimit",
"configuredTtlMs",
])("keeps %s inside measured production coverage", (functionName) => {
expect(
ignoredRegions.some((region) => region.includes(`function ${functionName}`)),
Expand Down
Loading