test: fix Windows CI execution and resolve exposed platform failures (#24476)

This commit is contained in:
Emily Hedlund
2026-04-03 08:50:29 -07:00
committed by GitHub
parent 7a70ab9a5d
commit ca0e6f9bd9
21 changed files with 308 additions and 175 deletions

View File

@@ -6,10 +6,20 @@
import { describe, it, expect, afterEach, vi } from 'vitest';
import { ApiKeyAuthProvider } from './api-key-provider.js';
import * as resolver from './value-resolver.js';
vi.mock('./value-resolver.js', async (importOriginal) => {
const actual = await importOriginal<typeof import('./value-resolver.js')>();
return {
...actual,
resolveAuthValue: vi.fn(),
};
});
describe('ApiKeyAuthProvider', () => {
afterEach(() => {
vi.unstubAllEnvs();
vi.restoreAllMocks();
});
describe('initialization', () => {
@@ -26,6 +36,7 @@ describe('ApiKeyAuthProvider', () => {
it('should resolve API key from environment variable', async () => {
vi.stubEnv('TEST_API_KEY', 'env-api-key');
vi.mocked(resolver.resolveAuthValue).mockResolvedValue('env-api-key');
const provider = new ApiKeyAuthProvider({
type: 'apiKey',
@@ -38,6 +49,10 @@ describe('ApiKeyAuthProvider', () => {
});
it('should throw if environment variable is not set', async () => {
vi.mocked(resolver.resolveAuthValue).mockRejectedValue(
new Error("Environment variable 'MISSING_KEY_12345' is not set"),
);
const provider = new ApiKeyAuthProvider({
type: 'apiKey',
key: '$MISSING_KEY_12345',
@@ -114,6 +129,8 @@ describe('ApiKeyAuthProvider', () => {
it('should return undefined for env-var keys on 403', async () => {
vi.stubEnv('RETRY_TEST_KEY', 'some-key');
vi.mocked(resolver.resolveAuthValue).mockResolvedValue('some-key');
const provider = new ApiKeyAuthProvider({
type: 'apiKey',
key: '$RETRY_TEST_KEY',
@@ -128,9 +145,13 @@ describe('ApiKeyAuthProvider', () => {
});
it('should re-resolve and return headers for command keys on 401', async () => {
vi.mocked(resolver.resolveAuthValue)
.mockResolvedValueOnce('initial-key')
.mockResolvedValueOnce('refreshed-key');
const provider = new ApiKeyAuthProvider({
type: 'apiKey',
key: '!echo refreshed-key',
key: '!some command',
});
await provider.initialize();
@@ -142,9 +163,11 @@ describe('ApiKeyAuthProvider', () => {
});
it('should stop retrying after MAX_AUTH_RETRIES', async () => {
vi.mocked(resolver.resolveAuthValue).mockResolvedValue('rotating-key');
const provider = new ApiKeyAuthProvider({
type: 'apiKey',
key: '!echo rotating-key',
key: '!some command',
});
await provider.initialize();

View File

@@ -10,6 +10,16 @@ import {
needsResolution,
maskSensitiveValue,
} from './value-resolver.js';
import * as shellUtils from '../../utils/shell-utils.js';
vi.mock('../../utils/shell-utils.js', async (importOriginal) => {
const actual =
await importOriginal<typeof import('../../utils/shell-utils.js')>();
return {
...actual,
spawnAsync: vi.fn(),
};
});
describe('value-resolver', () => {
describe('resolveAuthValue', () => {
@@ -39,12 +49,24 @@ describe('value-resolver', () => {
});
describe('shell commands', () => {
afterEach(() => {
vi.restoreAllMocks();
});
it('should execute shell command with ! prefix', async () => {
vi.mocked(shellUtils.spawnAsync).mockResolvedValue({
stdout: 'hello\n',
stderr: '',
});
const result = await resolveAuthValue('!echo hello');
expect(result).toBe('hello');
});
it('should trim whitespace from command output', async () => {
vi.mocked(shellUtils.spawnAsync).mockResolvedValue({
stdout: ' hello \n',
stderr: '',
});
const result = await resolveAuthValue('!echo " hello "');
expect(result).toBe('hello');
});
@@ -56,16 +78,32 @@ describe('value-resolver', () => {
});
it('should throw error for command that returns empty output', async () => {
vi.mocked(shellUtils.spawnAsync).mockResolvedValue({
stdout: '',
stderr: '',
});
await expect(resolveAuthValue('!echo -n ""')).rejects.toThrow(
'returned empty output',
);
});
it('should throw error for failed command', async () => {
vi.mocked(shellUtils.spawnAsync).mockRejectedValue(
new Error('Command failed'),
);
await expect(
resolveAuthValue('!nonexistent-command-12345'),
).rejects.toThrow(/Command.*failed/);
});
it('should throw error for timeout', async () => {
const timeoutError = new Error('AbortError');
timeoutError.name = 'AbortError';
vi.mocked(shellUtils.spawnAsync).mockRejectedValue(timeoutError);
await expect(resolveAuthValue('!sleep 100')).rejects.toThrow(
/timed out after/,
);
});
});
describe('literal values', () => {

View File

@@ -159,7 +159,9 @@ describe('BrowserManager', () => {
expect.objectContaining({
command: 'node',
args: expect.arrayContaining([
expect.stringMatching(/bundled\/chrome-devtools-mcp\.mjs$/),
expect.stringMatching(
/(dist[\\/])?bundled[\\/]chrome-devtools-mcp\.mjs$/,
),
]),
}),
);
@@ -175,7 +177,7 @@ describe('BrowserManager', () => {
command: 'node',
args: expect.arrayContaining([
expect.stringMatching(
/(dist\/)?bundled\/chrome-devtools-mcp\.mjs$/,
/(dist[\\/])?bundled[\\/]chrome-devtools-mcp\.mjs$/,
),
]),
}),