feat(tests): move SDK integration tests to integration-tests to share globalSetup

This commit is contained in:
mingholy.lmh
2025-12-01 14:56:11 +08:00
parent ae7d6af717
commit 3056f8a63d
17 changed files with 95 additions and 86 deletions

View File

@@ -238,7 +238,7 @@ export default tseslint.config(
prettierConfig, prettierConfig,
// extra settings for scripts that we run directly with node // extra settings for scripts that we run directly with node
{ {
files: ['./integration-tests/**/*.js'], files: ['./integration-tests/**/*.{js,ts,tsx}'],
languageOptions: { languageOptions: {
globals: { globals: {
...globals.node, ...globals.node,

View File

@@ -1,6 +1,6 @@
/** /**
* @license * @license
* Copyright 2025 Google LLC * Copyright 2025 Qwen Team
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -30,6 +30,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
const rootDir = join(__dirname, '..'); const rootDir = join(__dirname, '..');
const integrationTestsDir = join(rootDir, '.integration-tests'); const integrationTestsDir = join(rootDir, '.integration-tests');
let runDir = ''; // Make runDir accessible in teardown let runDir = ''; // Make runDir accessible in teardown
let sdkE2eRunDir = ''; // SDK E2E test run directory
const memoryFilePath = join( const memoryFilePath = join(
os.homedir(), os.homedir(),
@@ -48,14 +49,36 @@ export async function setup() {
// File doesn't exist, which is fine. // File doesn't exist, which is fine.
} }
// Setup for CLI integration tests
runDir = join(integrationTestsDir, `${Date.now()}`); runDir = join(integrationTestsDir, `${Date.now()}`);
await mkdir(runDir, { recursive: true }); await mkdir(runDir, { recursive: true });
// Setup for SDK E2E tests (separate directory with prefix)
sdkE2eRunDir = join(integrationTestsDir, `sdk-e2e-${Date.now()}`);
await mkdir(sdkE2eRunDir, { recursive: true });
// Clean up old test runs, but keep the latest few for debugging // Clean up old test runs, but keep the latest few for debugging
try { try {
const testRuns = await readdir(integrationTestsDir); const testRuns = await readdir(integrationTestsDir);
if (testRuns.length > 5) {
const oldRuns = testRuns.sort().slice(0, testRuns.length - 5); // Clean up old CLI integration test runs (without sdk-e2e- prefix)
const cliTestRuns = testRuns.filter((run) => !run.startsWith('sdk-e2e-'));
if (cliTestRuns.length > 5) {
const oldRuns = cliTestRuns.sort().slice(0, cliTestRuns.length - 5);
await Promise.all(
oldRuns.map((oldRun) =>
rm(join(integrationTestsDir, oldRun), {
recursive: true,
force: true,
}),
),
);
}
// Clean up old SDK E2E test runs (with sdk-e2e- prefix)
const sdkTestRuns = testRuns.filter((run) => run.startsWith('sdk-e2e-'));
if (sdkTestRuns.length > 5) {
const oldRuns = sdkTestRuns.sort().slice(0, sdkTestRuns.length - 5);
await Promise.all( await Promise.all(
oldRuns.map((oldRun) => oldRuns.map((oldRun) =>
rm(join(integrationTestsDir, oldRun), { rm(join(integrationTestsDir, oldRun), {
@@ -69,24 +92,37 @@ export async function setup() {
console.error('Error cleaning up old test runs:', e); console.error('Error cleaning up old test runs:', e);
} }
// Environment variables for CLI integration tests
process.env['INTEGRATION_TEST_FILE_DIR'] = runDir; process.env['INTEGRATION_TEST_FILE_DIR'] = runDir;
process.env['GEMINI_CLI_INTEGRATION_TEST'] = 'true'; process.env['GEMINI_CLI_INTEGRATION_TEST'] = 'true';
process.env['TELEMETRY_LOG_FILE'] = join(runDir, 'telemetry.log'); process.env['TELEMETRY_LOG_FILE'] = join(runDir, 'telemetry.log');
// Environment variables for SDK E2E tests
process.env['E2E_TEST_FILE_DIR'] = sdkE2eRunDir;
process.env['TEST_CLI_PATH'] = join(rootDir, 'dist/cli.js');
if (process.env['KEEP_OUTPUT']) { if (process.env['KEEP_OUTPUT']) {
console.log(`Keeping output for test run in: ${runDir}`); console.log(`Keeping output for test run in: ${runDir}`);
console.log(`Keeping output for SDK E2E test run in: ${sdkE2eRunDir}`);
} }
process.env['VERBOSE'] = process.env['VERBOSE'] ?? 'false'; process.env['VERBOSE'] = process.env['VERBOSE'] ?? 'false';
console.log(`\nIntegration test output directory: ${runDir}`); console.log(`\nIntegration test output directory: ${runDir}`);
console.log(`SDK E2E test output directory: ${sdkE2eRunDir}`);
console.log(`CLI path: ${process.env['TEST_CLI_PATH']}`);
} }
export async function teardown() { export async function teardown() {
// Cleanup the test run directory unless KEEP_OUTPUT is set // Cleanup the CLI test run directory unless KEEP_OUTPUT is set
if (process.env['KEEP_OUTPUT'] !== 'true' && runDir) { if (process.env['KEEP_OUTPUT'] !== 'true' && runDir) {
await rm(runDir, { recursive: true, force: true }); await rm(runDir, { recursive: true, force: true });
} }
// Cleanup the SDK E2E test run directory unless KEEP_OUTPUT is set
if (process.env['KEEP_OUTPUT'] !== 'true' && sdkE2eRunDir) {
await rm(sdkE2eRunDir, { recursive: true, force: true });
}
if (originalMemoryContent !== null) { if (originalMemoryContent !== null) {
await mkdir(dirname(memoryFilePath), { recursive: true }); await mkdir(dirname(memoryFilePath), { recursive: true });
await writeFile(memoryFilePath, originalMemoryContent, 'utf-8'); await writeFile(memoryFilePath, originalMemoryContent, 'utf-8');

View File

@@ -13,7 +13,7 @@ import {
isSDKAssistantMessage, isSDKAssistantMessage,
type TextBlock, type TextBlock,
type ContentBlock, type ContentBlock,
} from '../../src/index.js'; } from '@qwen-code/sdk-typescript';
import { SDKTestHelper, createSharedTestOptions } from './test-helper.js'; import { SDKTestHelper, createSharedTestOptions } from './test-helper.js';
const SHARED_TEST_OPTIONS = createSharedTestOptions(); const SHARED_TEST_OPTIONS = createSharedTestOptions();

View File

@@ -12,12 +12,12 @@
*/ */
import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { query } from '../../src/index.js';
import { import {
query,
isSDKAssistantMessage, isSDKAssistantMessage,
isSDKSystemMessage, isSDKSystemMessage,
type SDKMessage, type SDKMessage,
} from '../../src/types/protocol.js'; } from '@qwen-code/sdk-typescript';
import { import {
SDKTestHelper, SDKTestHelper,
extractText, extractText,

View File

@@ -10,8 +10,8 @@
*/ */
import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { query } from '../../src/index.js';
import { import {
query,
isSDKAssistantMessage, isSDKAssistantMessage,
isSDKResultMessage, isSDKResultMessage,
isSDKSystemMessage, isSDKSystemMessage,
@@ -19,7 +19,7 @@ import {
type SDKMessage, type SDKMessage,
type ToolUseBlock, type ToolUseBlock,
type SDKSystemMessage, type SDKSystemMessage,
} from '../../src/types/protocol.js'; } from '@qwen-code/sdk-typescript';
import { import {
SDKTestHelper, SDKTestHelper,
createMCPServer, createMCPServer,

View File

@@ -4,8 +4,8 @@
*/ */
import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { query } from '../../src/index.js';
import { import {
query,
isSDKUserMessage, isSDKUserMessage,
isSDKAssistantMessage, isSDKAssistantMessage,
isSDKSystemMessage, isSDKSystemMessage,
@@ -21,7 +21,7 @@ import {
type SDKMessage, type SDKMessage,
type ControlMessage, type ControlMessage,
type ToolUseBlock, type ToolUseBlock,
} from '../../src/types/protocol.js'; } from '@qwen-code/sdk-typescript';
import { SDKTestHelper, createSharedTestOptions } from './test-helper.js'; import { SDKTestHelper, createSharedTestOptions } from './test-helper.js';
const SHARED_TEST_OPTIONS = createSharedTestOptions(); const SHARED_TEST_OPTIONS = createSharedTestOptions();

View File

@@ -13,8 +13,8 @@ import {
beforeEach, beforeEach,
afterEach, afterEach,
} from 'vitest'; } from 'vitest';
import { query } from '../../src/index.js';
import { import {
query,
isSDKAssistantMessage, isSDKAssistantMessage,
isSDKResultMessage, isSDKResultMessage,
isSDKUserMessage, isSDKUserMessage,
@@ -22,7 +22,7 @@ import {
type SDKUserMessage, type SDKUserMessage,
type ToolUseBlock, type ToolUseBlock,
type ContentBlock, type ContentBlock,
} from '../../src/types/protocol.js'; } from '@qwen-code/sdk-typescript';
import { import {
SDKTestHelper, SDKTestHelper,
createSharedTestOptions, createSharedTestOptions,

View File

@@ -4,8 +4,8 @@
*/ */
import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { query } from '../../src/index.js';
import { import {
query,
isSDKAssistantMessage, isSDKAssistantMessage,
isSDKSystemMessage, isSDKSystemMessage,
isSDKResultMessage, isSDKResultMessage,
@@ -13,7 +13,7 @@ import {
type SDKMessage, type SDKMessage,
type SDKSystemMessage, type SDKSystemMessage,
type SDKAssistantMessage, type SDKAssistantMessage,
} from '../../src/types/protocol.js'; } from '@qwen-code/sdk-typescript';
import { import {
SDKTestHelper, SDKTestHelper,
extractText, extractText,

View File

@@ -10,14 +10,14 @@
*/ */
import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { query } from '../../src/index.js';
import { import {
query,
isSDKAssistantMessage, isSDKAssistantMessage,
type SDKMessage, type SDKMessage,
type SubagentConfig, type SubagentConfig,
type ContentBlock, type ContentBlock,
type ToolUseBlock, type ToolUseBlock,
} from '../../src/types/protocol.js'; } from '@qwen-code/sdk-typescript';
import { import {
SDKTestHelper, SDKTestHelper,
extractText, extractText,

View File

@@ -4,12 +4,12 @@
*/ */
import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { query } from '../../src/index.js';
import { import {
query,
isSDKAssistantMessage, isSDKAssistantMessage,
isSDKSystemMessage, isSDKSystemMessage,
type SDKUserMessage, type SDKUserMessage,
} from '../../src/types/protocol.js'; } from '@qwen-code/sdk-typescript';
import { SDKTestHelper, createSharedTestOptions } from './test-helper.js'; import { SDKTestHelper, createSharedTestOptions } from './test-helper.js';
const SHARED_TEST_OPTIONS = createSharedTestOptions(); const SHARED_TEST_OPTIONS = createSharedTestOptions();
@@ -265,7 +265,7 @@ describe('System Control (E2E)', () => {
// First model change // First model change
await q.setModel('qwen3-turbo'); await q.setModel('qwen3-turbo');
resumeResolve1?.(); resumeResolve1!();
// Wait for second response // Wait for second response
await Promise.race([ await Promise.race([
@@ -277,7 +277,7 @@ describe('System Control (E2E)', () => {
// Second model change // Second model change
await q.setModel('qwen3-vl-plus'); await q.setModel('qwen3-vl-plus');
resumeResolve2?.(); resumeResolve2!();
// Wait for third response // Wait for third response
await Promise.race([ await Promise.race([

View File

@@ -21,12 +21,12 @@ import type {
ContentBlock, ContentBlock,
TextBlock, TextBlock,
ToolUseBlock, ToolUseBlock,
} from '../../src/types/protocol.js'; } from '@qwen-code/sdk-typescript';
import { import {
isSDKAssistantMessage, isSDKAssistantMessage,
isSDKSystemMessage, isSDKSystemMessage,
isSDKResultMessage, isSDKResultMessage,
} from '../../src/types/protocol.js'; } from '@qwen-code/sdk-typescript';
// ============================================================================ // ============================================================================
// Core Test Helper Class // Core Test Helper Class

View File

@@ -12,11 +12,11 @@
*/ */
import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { query } from '../../src/index.js';
import { import {
query,
isSDKAssistantMessage, isSDKAssistantMessage,
type SDKMessage, type SDKMessage,
} from '../../src/types/protocol.js'; } from '@qwen-code/sdk-typescript';
import { import {
SDKTestHelper, SDKTestHelper,
extractText, extractText,

View File

@@ -2,7 +2,13 @@
"extends": "../tsconfig.json", "extends": "../tsconfig.json",
"compilerOptions": { "compilerOptions": {
"noEmit": true, "noEmit": true,
"allowJs": true "allowJs": true,
"baseUrl": ".",
"paths": {
"@qwen-code/sdk-typescript": [
"../packages/sdk-typescript/dist/index.d.ts"
]
}
}, },
"include": ["**/*.ts"], "include": ["**/*.ts"],
"references": [{ "path": "../packages/core" }] "references": [{ "path": "../packages/core" }]

View File

@@ -1,12 +1,15 @@
/** /**
* @license * @license
* Copyright 2025 Google LLC * Copyright 2025 Qwen Team
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
import { defineConfig } from 'vitest/config'; import { defineConfig } from 'vitest/config';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
const timeoutMinutes = Number(process.env.TB_TIMEOUT_MINUTES || '5'); const __dirname = dirname(fileURLToPath(import.meta.url));
const timeoutMinutes = Number(process.env['TB_TIMEOUT_MINUTES'] || '5');
const testTimeoutMs = timeoutMinutes * 60 * 1000; const testTimeoutMs = timeoutMinutes * 60 * 1000;
export default defineConfig({ export default defineConfig({
@@ -25,4 +28,13 @@ export default defineConfig({
}, },
}, },
}, },
resolve: {
alias: {
// Use built SDK bundle for e2e tests
'@qwen-code/sdk-typescript': resolve(
__dirname,
'../packages/sdk-typescript/dist/index.mjs',
),
},
},
}); });

View File

@@ -22,6 +22,7 @@
"scripts": { "scripts": {
"build": "node scripts/build.js", "build": "node scripts/build.js",
"test": "vitest run", "test": "vitest run",
"test:ci": "vitest run",
"test:watch": "vitest", "test:watch": "vitest",
"test:coverage": "vitest run --coverage", "test:coverage": "vitest run --coverage",
"lint": "eslint src test", "lint": "eslint src test",

View File

@@ -18,6 +18,14 @@ export type {
SDKResultMessage, SDKResultMessage,
SDKPartialAssistantMessage, SDKPartialAssistantMessage,
SDKMessage, SDKMessage,
ControlMessage,
CLIControlRequest,
CLIControlResponse,
ControlCancelRequest,
SubagentConfig,
SubagentLevel,
ModelConfig,
RunConfig,
} from './types/protocol.js'; } from './types/protocol.js';
export { export {
@@ -26,6 +34,9 @@ export {
isSDKSystemMessage, isSDKSystemMessage,
isSDKResultMessage, isSDKResultMessage,
isSDKPartialAssistantMessage, isSDKPartialAssistantMessage,
isControlRequest,
isControlResponse,
isControlCancel,
} from './types/protocol.js'; } from './types/protocol.js';
export type { export type {

View File

@@ -1,57 +0,0 @@
/**
* @license
* Copyright 2025 Qwen Team
* SPDX-License-Identifier: Apache-2.0
*/
import { mkdir, readdir, rm } from 'node:fs/promises';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const rootDir = join(__dirname, '../..');
const e2eTestsDir = join(rootDir, '.integration-tests');
let runDir = '';
export async function setup() {
runDir = join(e2eTestsDir, `sdk-e2e-${Date.now()}`);
await mkdir(runDir, { recursive: true });
// Clean up old test runs, but keep the latest few for debugging
try {
const testRuns = await readdir(e2eTestsDir);
const sdkTestRuns = testRuns.filter((run) => run.startsWith('sdk-e2e-'));
if (sdkTestRuns.length > 5) {
const oldRuns = sdkTestRuns.sort().slice(0, sdkTestRuns.length - 5);
await Promise.all(
oldRuns.map((oldRun) =>
rm(join(e2eTestsDir, oldRun), {
recursive: true,
force: true,
}),
),
);
}
} catch (e) {
console.error('Error cleaning up old test runs:', e);
}
process.env['E2E_TEST_FILE_DIR'] = runDir;
process.env['QWEN_CLI_E2E_TEST'] = 'true';
process.env['TEST_CLI_PATH'] = join(rootDir, '../../dist/cli.js');
if (process.env['KEEP_OUTPUT']) {
console.log(`Keeping output for test run in: ${runDir}`);
}
process.env['VERBOSE'] = process.env['VERBOSE'] ?? 'false';
console.log(`\nSDK E2E test output directory: ${runDir}`);
console.log(`CLI path: ${process.env['TEST_CLI_PATH']}`);
}
export async function teardown() {
// Cleanup the test run directory unless KEEP_OUTPUT is set
if (process.env['KEEP_OUTPUT'] !== 'true' && runDir) {
await rm(runDir, { recursive: true, force: true });
}
}