From ffb9f5b5bdc04fc0854fe921886f1d70ad875506 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Wed, 4 Mar 2026 10:55:03 +0000 Subject: [PATCH 1/4] Fix CI detection in tests --- nodejs/test/e2e/harness/sdkTestContext.ts | 5 ++++- nodejs/test/e2e/session.test.ts | 4 ++-- test/harness/replayingCapiProxy.ts | 4 +++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/nodejs/test/e2e/harness/sdkTestContext.ts b/nodejs/test/e2e/harness/sdkTestContext.ts index 4986d12995..a9008e6a00 100644 --- a/nodejs/test/e2e/harness/sdkTestContext.ts +++ b/nodejs/test/e2e/harness/sdkTestContext.ts @@ -13,6 +13,9 @@ import { CopilotClient } from "../../../src"; import { CapiProxy } from "./CapiProxy"; import { retry } from "./sdkTestHelper"; +// Unfortunately the VS Code Vitest extension sets CI=true in all non-debug runs, so we have to exclude that +export const isCI = process.env.CI === "true" && !process.env.VITEST_VSCODE; + const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const SNAPSHOTS_DIR = resolve(__dirname, "../../../../test/snapshots"); @@ -42,7 +45,7 @@ export async function createSdkTestContext({ logLevel: logLevel || "error", cliPath: process.env.COPILOT_CLI_PATH, // Use fake token in CI to allow cached responses without real auth - githubToken: process.env.CI === "true" ? "fake-token-for-e2e-tests" : undefined, + githubToken: isCI ? "fake-token-for-e2e-tests" : undefined, }); const harness = { homeDir, workDir, openAiEndpoint, copilotClient, env }; diff --git a/nodejs/test/e2e/session.test.ts b/nodejs/test/e2e/session.test.ts index 9d067a8ef7..93731d617b 100644 --- a/nodejs/test/e2e/session.test.ts +++ b/nodejs/test/e2e/session.test.ts @@ -2,7 +2,7 @@ import { rm } from "fs/promises"; import { describe, expect, it, onTestFinished } from "vitest"; import { ParsedHttpExchange } from "../../../test/harness/replayingCapiProxy.js"; import { CopilotClient, approveAll } from "../../src/index.js"; -import { createSdkTestContext } from "./harness/sdkTestContext.js"; +import { createSdkTestContext, isCI } from "./harness/sdkTestContext.js"; import { getFinalAssistantMessage, getNextEventOfType } from "./harness/sdkTestHelper.js"; describe("Sessions", async () => { @@ -187,7 +187,7 @@ describe("Sessions", async () => { // Resume using a new client const newClient = new CopilotClient({ env, - githubToken: process.env.CI === "true" ? "fake-token-for-e2e-tests" : undefined, + githubToken: isCI ? "fake-token-for-e2e-tests" : undefined, }); onTestFinished(() => newClient.forceStop()); diff --git a/test/harness/replayingCapiProxy.ts b/test/harness/replayingCapiProxy.ts index 6864731dff..40f63e8965 100644 --- a/test/harness/replayingCapiProxy.ts +++ b/test/harness/replayingCapiProxy.ts @@ -311,7 +311,9 @@ export class ReplayingCapiProxy extends CapturingHttpProxy { // Fallback to normal proxying if no cached response found // This implicitly captures the new exchange too - if (process.env.CI === "true") { + // The VS Code Vitest extension always sets CI=true in non-debug mode, so we have to exclude that case + const isCI = process.env.CI === "true" && !process.env.VITEST_VSCODE; + if (isCI) { await exitWithNoMatchingRequestError( options, state.testInfo, From cc3c73fffa331fff7d520a314a006d9822814769 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Wed, 4 Mar 2026 11:53:33 +0000 Subject: [PATCH 2/4] Use GITHUB_ACTIONS --- nodejs/test/e2e/harness/sdkTestContext.ts | 3 +-- test/harness/replayingCapiProxy.ts | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/nodejs/test/e2e/harness/sdkTestContext.ts b/nodejs/test/e2e/harness/sdkTestContext.ts index a9008e6a00..a5cf2ec57d 100644 --- a/nodejs/test/e2e/harness/sdkTestContext.ts +++ b/nodejs/test/e2e/harness/sdkTestContext.ts @@ -13,8 +13,7 @@ import { CopilotClient } from "../../../src"; import { CapiProxy } from "./CapiProxy"; import { retry } from "./sdkTestHelper"; -// Unfortunately the VS Code Vitest extension sets CI=true in all non-debug runs, so we have to exclude that -export const isCI = process.env.CI === "true" && !process.env.VITEST_VSCODE; +export const isCI = process.env.GITHUB_ACTIONS === "true"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); diff --git a/test/harness/replayingCapiProxy.ts b/test/harness/replayingCapiProxy.ts index 40f63e8965..1a8fbc2439 100644 --- a/test/harness/replayingCapiProxy.ts +++ b/test/harness/replayingCapiProxy.ts @@ -311,8 +311,7 @@ export class ReplayingCapiProxy extends CapturingHttpProxy { // Fallback to normal proxying if no cached response found // This implicitly captures the new exchange too - // The VS Code Vitest extension always sets CI=true in non-debug mode, so we have to exclude that case - const isCI = process.env.CI === "true" && !process.env.VITEST_VSCODE; + const isCI = process.env.GITHUB_ACTIONS === "true"; if (isCI) { await exitWithNoMatchingRequestError( options, From 818676f4d9c880d96950efde109f37a72042867f Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Wed, 4 Mar 2026 11:58:07 +0000 Subject: [PATCH 3/4] Same for other languages --- dotnet/test/Harness/E2ETestContext.cs | 4 ++-- go/internal/e2e/testharness/context.go | 2 +- python/e2e/test_session.py | 2 +- python/e2e/testharness/context.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dotnet/test/Harness/E2ETestContext.cs b/dotnet/test/Harness/E2ETestContext.cs index 00fc32075e..62acd580f8 100644 --- a/dotnet/test/Harness/E2ETestContext.cs +++ b/dotnet/test/Harness/E2ETestContext.cs @@ -94,13 +94,13 @@ public IReadOnlyDictionary GetEnvironment() Cwd = WorkDir, CliPath = GetCliPath(_repoRoot), Environment = GetEnvironment(), - GitHubToken = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("CI")) ? "fake-token-for-e2e-tests" : null, + GitHubToken = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("GITHUB_ACTIONS")) ? "fake-token-for-e2e-tests" : null, }); public async ValueTask DisposeAsync() { // Skip writing snapshots in CI to avoid corrupting them on test failures - var isCI = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("CI")); + var isCI = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("GITHUB_ACTIONS")); await _proxy.StopAsync(skipWritingCache: isCI); try { if (Directory.Exists(HomeDir)) Directory.Delete(HomeDir, true); } catch { } diff --git a/go/internal/e2e/testharness/context.go b/go/internal/e2e/testharness/context.go index cefb87b586..b9edab1e55 100644 --- a/go/internal/e2e/testharness/context.go +++ b/go/internal/e2e/testharness/context.go @@ -166,7 +166,7 @@ func (c *TestContext) NewClient() *copilot.Client { } // Use fake token in CI to allow cached responses without real auth - if os.Getenv("CI") == "true" { + if os.Getenv("GITHUB_ACTIONS") == "true" { options.GitHubToken = "fake-token-for-e2e-tests" } diff --git a/python/e2e/test_session.py b/python/e2e/test_session.py index 47cb1b5ae9..951528e0ac 100644 --- a/python/e2e/test_session.py +++ b/python/e2e/test_session.py @@ -183,7 +183,7 @@ async def test_should_resume_a_session_using_a_new_client(self, ctx: E2ETestCont assert "2" in answer.data.content # Resume using a new client - github_token = "fake-token-for-e2e-tests" if os.environ.get("CI") == "true" else None + github_token = "fake-token-for-e2e-tests" if os.environ.get("GITHUB_ACTIONS") == "true" else None new_client = CopilotClient( { "cli_path": ctx.cli_path, diff --git a/python/e2e/testharness/context.py b/python/e2e/testharness/context.py index eb0c440819..184c8ad405 100644 --- a/python/e2e/testharness/context.py +++ b/python/e2e/testharness/context.py @@ -60,7 +60,7 @@ async def setup(self): # Create the shared client (like Node.js/Go do) # Use fake token in CI to allow cached responses without real auth - github_token = "fake-token-for-e2e-tests" if os.environ.get("CI") == "true" else None + github_token = "fake-token-for-e2e-tests" if os.environ.get("GITHUB_ACTIONS") == "true" else None self._client = CopilotClient( { "cli_path": self.cli_path, From 8b7095657be4adbc82b403844f6ebea42671b2ec Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Wed, 4 Mar 2026 11:59:51 +0000 Subject: [PATCH 4/4] Formatting --- python/e2e/test_session.py | 4 +++- python/e2e/testharness/context.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/python/e2e/test_session.py b/python/e2e/test_session.py index 951528e0ac..13e749507d 100644 --- a/python/e2e/test_session.py +++ b/python/e2e/test_session.py @@ -183,7 +183,9 @@ async def test_should_resume_a_session_using_a_new_client(self, ctx: E2ETestCont assert "2" in answer.data.content # Resume using a new client - github_token = "fake-token-for-e2e-tests" if os.environ.get("GITHUB_ACTIONS") == "true" else None + github_token = ( + "fake-token-for-e2e-tests" if os.environ.get("GITHUB_ACTIONS") == "true" else None + ) new_client = CopilotClient( { "cli_path": ctx.cli_path, diff --git a/python/e2e/testharness/context.py b/python/e2e/testharness/context.py index 184c8ad405..c030889129 100644 --- a/python/e2e/testharness/context.py +++ b/python/e2e/testharness/context.py @@ -60,7 +60,9 @@ async def setup(self): # Create the shared client (like Node.js/Go do) # Use fake token in CI to allow cached responses without real auth - github_token = "fake-token-for-e2e-tests" if os.environ.get("GITHUB_ACTIONS") == "true" else None + github_token = ( + "fake-token-for-e2e-tests" if os.environ.get("GITHUB_ACTIONS") == "true" else None + ) self._client = CopilotClient( { "cli_path": self.cli_path,