Skip to content

[Tests] Cover abortSignalFromRequestBehaviour request cancellation - #8696

Merged
Suleimanlatrsh merged 1 commit into
mainfrom
tests-maintenance-36503092988
Sep 29, 2026
Merged

Suleimanlatrsh merged 1 commit into
mainfrom
tests-maintenance-36503092988

Conversation

@github-actions

@github-actions github-actions Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

WHY are these changes introduced?

The seven-day review of Main tests runs (2026-09-22T00:00Z through 2026-09-29T00:27Z UTC) found 10 failed main runs, all from the same job — [Main] Node 22.12.0 in windows-latest — and all the same signature: version.test.ts subprocess assertions timing out at 20000ms (representative job). That flake is already addressed by open PR #8669, and the one stable/4.8 failure in the window was a real npm audit timeout already fixed on main in 6b70f18. With no actionable, non-duplicate flake remaining, this PR closes a coverage gap instead.

abortSignalFromRequestBehaviour in packages/cli-kit/src/public/node/http.ts:108 decides whether an HTTP request can be cancelled at all. It is used by shopifyFetch and by the GraphQL client (packages/cli-kit/src/public/node/api/graphql.ts:145), but had no direct tests: its sibling requestMode has a full describe block, while all three of this function's branches were only exercised indirectly through shopifyFetch, where an assertion failure reports as a fetch error rather than naming the faulty branch.

WHAT is this pull request doing?

Adds a describe block to the existing http.test.ts covering the function's contract: the timeout case, the disabled case, a signal from a factory function, a signal supplied directly, and that each call returns a fresh signal — the property that keeps a retried request from starting out already aborted.

AbortSignal.timeout is backed by libuv rather than a JS timer, so the file's faked timers never fire it. The two timeout cases call vi.useRealTimers() (matching the existing pattern at http.test.ts:122) and await the abort event with a 10ms timeout instead of sleeping on a fixed delay, so they finish in ~11ms and do not depend on wall-clock slack.

No production code changed.

Validation on Linux / Node 26.1.0:

  • All 24 tests in http.test.ts pass, as do 170 tests across the dependent api and environment suites.
  • Five shuffled runs (--sequence.shuffle, varied seeds) and a --pool=forks run all pass, so the new tests are not order- or worker-dependent.
  • eslint and tsc --noEmit on packages/cli-kit are clean.
  • Each of the three branches was verified to fail without the behavior: removing the factory-function branch, removing the supplied-signal branch, and returning a pre-aborted signal each broke the corresponding new test (plus the matching shopifyFetch cases).

Windows and macOS are left to CI; the assertions are platform-independent, but only CI confirms the originally failing platform.

How to manually test your changes?

CI

Checklist

  • I've considered possible cross-platform impacts (Mac, Linux, Windows)
  • I've considered possible documentation changes
  • I've considered analytics changes to measure impact
  • The change is user-facing — I've identified the correct bump type (patch for bug fixes · minor for new features · major for breaking changes) and added a changeset with pnpm changeset add

`abortSignalFromRequestBehaviour` decides whether an HTTP request can be
cancelled, and is used by both shopifyFetch and the GraphQL client, but had
no direct tests: its sibling `requestMode` was covered while all three of
its branches were only exercised indirectly through shopifyFetch.

Cover the timeout, disabled, factory-function and supplied-signal cases, and
assert that each call returns a fresh signal so a retried request is not born
aborted. `AbortSignal.timeout` is backed by libuv rather than a JS timer, so
the timeout cases use real timers and await the abort event instead of
sleeping on a fixed delay.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Suleimanlatrsh
Suleimanlatrsh marked this pull request as ready for review September 29, 2026 16:22
@Suleimanlatrsh
Suleimanlatrsh requested a review from a team as a code owner September 29, 2026 16:22
@github-actions github-actions Bot added the no-changelog This PR doesn't include a changeset entry. Is an internal only change not relevant to end users. label Sep 29, 2026
@github-actions

Copy link
Copy Markdown
Contributor Author

Differences in type declarations

We detected differences in the type declarations generated by Typescript for this branch compared to the baseline ('main' branch). Please, review them to ensure they are backward-compatible. Here are some important things to keep in mind:

  • Some seemingly private modules might be re-exported through public modules.
  • If the branch is behind main you might see odd diffs, rebase main into this branch.

New type declarations

We found no new type declarations in this PR

Existing type declarations

packages/cli-kit/dist/public/common/command-events.d.ts
@@ -23,14 +23,14 @@ export declare const commandDiagnosticEventSchema: z.ZodObject<{
 export declare const commandProgressEventSchema: z.ZodObject<{
     type: z.ZodLiteral<"progress">;
     timestamp: z.ZodString;
-    status: z.ZodEnum<["started", "updated", "retrying", "completed", "failed"]>;
+    status: z.ZodEnum<["started", "updated", "completed"]>;
     operation: z.ZodString;
     message: z.ZodOptional<z.ZodString>;
     current: z.ZodOptional<z.ZodNumber>;
     total: z.ZodOptional<z.ZodNumber>;
 }, "strict", z.ZodTypeAny, {
     type: "progress";
-    status: "started" | "updated" | "retrying" | "completed" | "failed";
+    status: "started" | "updated" | "completed";
     timestamp: string;
     operation: string;
     message?: string | undefined;
@@ -38,7 +38,7 @@ export declare const commandProgressEventSchema: z.ZodObject<{
     total?: number | undefined;
 }, {
     type: "progress";
-    status: "started" | "updated" | "retrying" | "completed" | "failed";
+    status: "started" | "updated" | "completed";
     timestamp: string;
     operation: string;
     message?: string | undefined;
@@ -67,14 +67,14 @@ export declare const commandEventSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
 }>, z.ZodObject<{
     type: z.ZodLiteral<"progress">;
     timestamp: z.ZodString;
-    status: z.ZodEnum<["started", "updated", "retrying", "completed", "failed"]>;
+    status: z.ZodEnum<["started", "updated", "completed"]>;
     operation: z.ZodString;
     message: z.ZodOptional<z.ZodString>;
     current: z.ZodOptional<z.ZodNumber>;
     total: z.ZodOptional<z.ZodNumber>;
 }, "strict", z.ZodTypeAny, {
     type: "progress";
-    status: "started" | "updated" | "retrying" | "completed" | "failed";
+    status: "started" | "updated" | "completed";
     timestamp: string;
     operation: string;
     message?: string | undefined;
@@ -82,7 +82,7 @@ export declare const commandEventSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
     total?: number | undefined;
 }, {
     type: "progress";
-    status: "started" | "updated" | "retrying" | "completed" | "failed";
+    status: "started" | "updated" | "completed";
     timestamp: string;
     operation: string;
     message?: string | undefined;
packages/cli-kit/dist/public/node/command-events.d.ts
@@ -29,14 +29,14 @@ export declare const commandEventOutputSchema: import("./json-output-schema.js")
 }>, import("zod").ZodObject<{
     type: import("zod").ZodLiteral<"progress">;
     timestamp: import("zod").ZodString;
-    status: import("zod").ZodEnum<["started", "updated", "retrying", "completed", "failed"]>;
+    status: import("zod").ZodEnum<["started", "updated", "completed"]>;
     operation: import("zod").ZodString;
     message: import("zod").ZodOptional<import("zod").ZodString>;
     current: import("zod").ZodOptional<import("zod").ZodNumber>;
     total: import("zod").ZodOptional<import("zod").ZodNumber>;
 }, "strict", import("zod").ZodTypeAny, {
     type: "progress";
-    status: "started" | "updated" | "retrying" | "completed" | "failed";
+    status: "started" | "updated" | "completed";
     timestamp: string;
     operation: string;
     message?: string | undefined;
@@ -44,7 +44,7 @@ export declare const commandEventOutputSchema: import("./json-output-schema.js")
     total?: number | undefined;
 }, {
     type: "progress";
-    status: "started" | "updated" | "retrying" | "completed" | "failed";
+    status: "started" | "updated" | "completed";
     timestamp: string;
     operation: string;
     message?: string | undefined;
packages/cli-kit/dist/public/node/ui.d.ts
@@ -318,8 +318,6 @@ export declare function renderTasks<TContext>(tasks: Task<TContext>[], { renderO
 export interface RenderSingleTaskOptions<T> {
     title: TokenizedString;
     task: (updateStatus: (status: TokenizedString) => void) => Promise<T>;
-    /** The number of additional attempts after a failure. Defaults to zero. */
-    retry?: number;
     onAbort?: () => void;
     renderOptions?: RenderOptions;
 }
@@ -328,13 +326,12 @@ export interface RenderSingleTaskOptions<T> {
  * @param options - Configuration object
  * @param options.title - The initial title to display with the loading bar
  * @param options.task - The async task to execute. Receives an updateStatus callback to change the displayed title.
- * @param options.retry - The number of additional attempts after a failure. Defaults to zero.
  * @param options.renderOptions - Optional render configuration
  * @returns The result of the task
  * @example
  * Loading app ...
  */
-export declare function renderSingleTask<T>({ title, task, retry, onAbort, renderOptions, }: RenderSingleTaskOptions<T>): Promise<T>;
+export declare function renderSingleTask<T>({ title, task, onAbort, renderOptions, }: RenderSingleTaskOptions<T>): Promise<T>;
 export interface RenderTextPromptOptions extends Omit<TextPromptProps, 'onSubmit'> {
     renderOptions?: RenderOptions;
 }
packages/cli-kit/dist/private/node/ui/hooks/use-async-and-unmount.d.ts
@@ -1,6 +1,6 @@
-interface Options<T> {
-    onFulfilled?: (result: T) => unknown;
+interface Options {
+    onFulfilled?: () => unknown;
     onRejected?: (error: Error) => void;
 }
-export default function useAsyncAndUnmount<T>(asyncFunction: () => Promise<T>, { onFulfilled, onRejected }?: Options<T>): void;
+export default function useAsyncAndUnmount(asyncFunction: () => Promise<unknown>, { onFulfilled, onRejected }?: Options): void;
 export {};
\ No newline at end of file
packages/cli-kit/dist/private/node/ui/components/Tasks.d.ts
@@ -1,7 +1,14 @@
 import { AbortSignal } from '../../../../public/node/abort.js';
-import { Task } from '../tasks.js';
+import { TokenizedString } from '../../../../public/node/output.js';
 import React from 'react';
-export type { Task } from '../tasks.js';
+export interface Task<TContext = unknown> {
+    title: string | TokenizedString;
+    task: (ctx: TContext, task: Task<TContext>) => Promise<void | Task<TContext>[]>;
+    retry?: number;
+    retryCount?: number;
+    errors?: Error[];
+    skip?: (ctx: TContext) => boolean;
+}
 interface TasksProps<TContext> {
     tasks: Task<TContext>[];
     silent?: boolean;

@Suleimanlatrsh
Suleimanlatrsh added this pull request to the merge queue Sep 29, 2026
Merged via the queue into main with commit 6e186a3 Sep 29, 2026
45 of 54 checks passed
@Suleimanlatrsh
Suleimanlatrsh deleted the tests-maintenance-36503092988 branch September 29, 2026 16:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-changelog This PR doesn't include a changeset entry. Is an internal only change not relevant to end users.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant