Genace

generation API fake timer tests: prove deadlines without real waiting

Polling and retry tests should not wait several real minutes to prove that a deadline expires. A controlled clock makes boundary behavior deterministic and keeps failures easy to reproduce. Separate pure scheduling calculations from asynchronous timers, and remember that a local observation deadline does not change the remote generation state.

Test pure time calculations directly

Accept the current time as an argument in application scheduling helpers. Then compare exact output timestamps without installing fake timers at all. Genace’s own backoff helper follows this useful pattern by accepting an optional Date and calculating a bounded delay.

The repository currently starts its internal scheduling delay at five seconds, doubles it, and caps it at sixty seconds. That is a next-action timestamp calculation, not a promise that the configured cron executes every minute. The deployment cron currently runs daily, while authorized GET observation can also advance a job.

Use fake timers for actual waiting code

When testing setTimeout-based application behavior, install fake timers and restore them after each test. This minimal example verifies a delay resolves at the intended boundary without making an external request.

import { afterEach, expect, it, vi } from 'vitest';

afterEach(() => vi.useRealTimers());

it('does not finish an observation delay early', async () => {
  vi.useFakeTimers();
  let finished = false;
  const waiting = new Promise((resolve) => setTimeout(resolve, 5000))
    .then(() => { finished = true; });
  await vi.advanceTimersByTimeAsync(4999);
  expect(finished).toBe(false);
  await vi.advanceTimersByTimeAsync(1);
  await waiting;
  expect(finished).toBe(true);
});

For your collector, replace the trivial promise with its real scheduling boundary and mock its HTTP dependency. Advance asynchronous timers with an API that allows pending promise work to settle. Avoid using a blanket run-all-timers operation on an intentionally repeating poll loop with no terminal fixture.

Assert the state at budget exhaustion

Feed queued or running snapshots until the observation deadline. The result should retain the saved job ID and report a local pending or deferred outcome. It should not fabricate failed, issue cancellation, or call the generation POST.

Test a terminal response arriving just before the deadline and one whose transport completes after the budget. Define your application policy clearly and preserve the actual remote snapshot when available. Separate wall-clock timestamp tests from elapsed-time tests so clock changes do not make deadlines unpredictable.

Verify cleanup and isolation

Cancel an observer and assert no retry timer remains scheduled. Restore mocked clocks even when assertions fail. Test multiple collectors with independent budgets, and ensure a shared test clock does not accidentally hide leaked intervals between cases.

Use fixtures for HTTP failure, malformed JSON, and successful terminal state so retry behavior is exercised deliberately. No real API key or paid model belongs in these tests. Keep one small real-timer integration check only when necessary for a transport behavior that fake timers cannot represent reliably.

Acceptance criteria

The suite should complete quickly, produce identical decisions on repeated runs, and explain every boundary timestamp. Confirm a deadline expiry preserves recovery identity and that backoff caps do not imply an unsupported deployment schedule.

See deadline propagation, retry policy, and contract fixtures.

Continue with production operations

Where these facts come from