MCPcopy Create free account
hub / github.com/TestSprite/testsprite-cli / triggerOne

Function triggerOne

src/commands/test.ts:2426–2754  ·  view source on GitHub ↗

* Trigger (and optionally poll) a single testId. * * When `opts.wait` is set, a per-spec wall-clock deadline is set * immediately before the first trigger attempt. Every throttle/retry sleep * and the final `pollRunUntilTerminal` call receive only the remaining * seconds so the `--wa

(testId: string)

Source from the content-addressed store, hash-verified

2424 * into the result's `error` field so one failure doesn't abort siblings.
2425 */
2426 async function triggerOne(testId: string): Promise<CliBatchRunResult> {
2427 // Mint a fresh idempotency key per run — MUST NOT reuse the create key.
2428 const runIdempotencyKey = `cli-batch-run-${randomUUID()}`;
2429 if (opts.debug) {
2430 stderrFn(`[batch-run] ${testId} idempotency-key: ${runIdempotencyKey}`);
2431 }
2432
2433 // MAJOR 2: record the wall-clock deadline before the first trigger attempt
2434 // when --wait is set so that throttle/retry sleeps + the subsequent poll all
2435 // draw from the SAME budget. Triggering at t=0, waiting 60 s for throttle,
2436 // then starting a fresh full-timeout poll would allow the spec to consume
2437 // 2× the intended budget.
2438 const specDeadlineMs: number | undefined = opts.wait
2439 ? Date.now() + timeoutSeconds * 1000
2440 : undefined;
2441
2442 /** Returns remaining milliseconds until the per-spec deadline, or Infinity when no deadline. */
2443 function remainingMs(): number {
2444 if (specDeadlineMs === undefined) return Infinity;
2445 return Math.max(0, specDeadlineMs - Date.now());
2446 }
2447
2448 let triggerResponse: TriggerRunResponse;
2449
2450 // Outer RATE_LIMITED retry loop.
2451 // The batch call site passes `retryOnRateLimit: false` to `triggerRunWithMeta`
2452 // so the HTTP layer throws on the first 429 — this loop is the SOLE owner of
2453 // rate-limit handling. Single `test run` / `test create --run` still use
2454 // retryOnRateLimit: true (the default) and are unaffected by this loop.
2455 let outerRateLimitAttempt = 0;
2456 while (true) {
2457 // Fix 2: deadline check BEFORE acquiring a throttle slot or firing a trigger.
2458 // Without this guard, an outer retry could acquire a slot and send a new
2459 // POST even after the --wait deadline has already expired.
2460 if (opts.wait && remainingMs() <= 0) {
2461 return {
2462 testId,
2463 runId: '',
2464 status: 'timeout',
2465 codeVersion: '',
2466 error: {
2467 code: 'UNSUPPORTED',
2468 message: `Timed out after ${timeoutSeconds}s before trigger attempt for ${testId}.`,
2469 exitCode: 7,
2470 },
2471 };
2472 }
2473
2474 // Acquire a slot in the client-side rate window before firing the trigger.
2475 // If the window is full, sleep until the oldest slot ages out — clamped to
2476 // the remaining deadline so we don't overshoot the --wait budget.
2477 let throttleWait: number;
2478 while ((throttleWait = rateThrottle.acquire()) > 0) {
2479 const clampedWait = Math.min(throttleWait, remainingMs());
2480 if (clampedWait <= 0) {
2481 // Deadline already passed while waiting for a throttle slot.
2482 return {
2483 testId,

Callers 1

drainOneFunction · 0.85

Calls 6

remainingMsFunction · 0.85
isTransientRateLimitFunction · 0.85
pollRunUntilTerminalFunction · 0.85
acquireMethod · 0.80
triggerRunWithMetaMethod · 0.80
getDetailMethod · 0.80

Tested by

no test coverage detected