diff --git a/scripts/lib/extension-test-plan.mts b/scripts/lib/extension-test-plan.mts index 37afa98d7f33..3352aefeeddb 100644 --- a/scripts/lib/extension-test-plan.mts +++ b/scripts/lib/extension-test-plan.mts @@ -93,6 +93,11 @@ const EXTENSION_TEST_PROCESS_FILE_LIMITS = new Map([ // The non-isolated Matrix suite intentionally shares module state within a process. // Bound its lifetime so Vite's transformed module graph cannot grow across the whole suite. ["test/vitest/vitest.extension-matrix.config.ts", 40], + [ + "test/vitest/vitest.extension-telegram.config.ts", + // A 10-file serial worker reached 231s and 2.7 GiB RSS (observed 2026-08). + 5, + ], ]); const EXTENSION_TEST_CONFIG_ROUTES: Array<[(root: string) => boolean, string]> = [ [isActiveMemoryExtensionRoot, "test/vitest/vitest.extension-active-memory.config.ts"], diff --git a/scripts/test-projects.test-support.mts b/scripts/test-projects.test-support.mts index 0f4ef6eefd39..86ec315cd17e 100644 --- a/scripts/test-projects.test-support.mts +++ b/scripts/test-projects.test-support.mts @@ -50,7 +50,10 @@ import { isProviderOpenAiExtensionRoot, } from "../test/vitest/vitest.extension-provider-paths.mjs"; import { isQaExtensionRoot } from "../test/vitest/vitest.extension-qa-paths.mjs"; -import { isTelegramExtensionRoot } from "../test/vitest/vitest.extension-telegram-paths.mjs"; +import { + isTelegramExtensionRoot, + telegramExtensionTestRoots, +} from "../test/vitest/vitest.extension-telegram-paths.mjs"; import { isVoiceCallExtensionRoot } from "../test/vitest/vitest.extension-voice-call-paths.mjs"; import { isWhatsAppExtensionRoot } from "../test/vitest/vitest.extension-whatsapp-paths.mjs"; import { isZaloExtensionRoot } from "../test/vitest/vitest.extension-zalo-paths.mjs"; @@ -255,6 +258,10 @@ const UNIT_FAST_FAKE_TIMERS_VITEST_CONFIG = "test/vitest/vitest.unit-fast-fake-t const UNIT_SECURITY_VITEST_CONFIG = "test/vitest/vitest.unit-security.config.ts"; const UNIT_SRC_VITEST_CONFIG = "test/vitest/vitest.unit-src.config.ts"; const UNIT_SUPPORT_VITEST_CONFIG = "test/vitest/vitest.unit-support.config.ts"; +const EXTENSION_TEST_PROCESS_ROOTS = new Map([ + [EXTENSION_MATRIX_VITEST_CONFIG, matrixExtensionTestRoots], + [EXTENSION_TELEGRAM_VITEST_CONFIG, telegramExtensionTestRoots], +]); const FULL_SUITE_CONFIG_WEIGHT = new Map([ [GATEWAY_VITEST_CONFIG, 180], @@ -962,16 +969,15 @@ function createBroadToolingScriptPlans(params: VitestRunPlan & { cwd: string }) : null; } -function createBoundedExtensionPlans( - params: Omit & { roots: string[] }, -) { - const { config, forwardedArgs, roots, watchMode } = params; - if (watchMode) { - return null; +function createBoundedExtensionPlans(plan: VitestRunPlan) { + const { config, forwardedArgs, watchMode } = plan; + const roots = EXTENSION_TEST_PROCESS_ROOTS.get(config); + if (watchMode || !roots) { + return [plan]; } const chunks = createExtensionTestProcessTargetChunks(config, roots, forwardedArgs); if (chunks.length <= 1) { - return null; + return [plan]; } return chunks.map((includePatterns) => ({ config, @@ -3602,12 +3608,14 @@ export function buildVitestRunPlans( "watch mode with mixed test suites is not supported; target one suite at a time or use a dedicated suite command", ); } - return explicitConfigTargets.map((config) => ({ - config, - forwardedArgs: nonTargetArgs, - includePatterns: null, - watchMode, - })); + return explicitConfigTargets.flatMap((config) => + createBoundedExtensionPlans({ + config, + forwardedArgs: nonTargetArgs, + includePatterns: null, + watchMode, + }), + ); } const groupedTargets = new Map(); @@ -3718,13 +3726,7 @@ export function buildVitestRunPlans( includePatterns: null, watchMode, }; - const boundedPlans = createBoundedExtensionPlans({ - config, - forwardedArgs: nonTargetArgs, - roots: matrixExtensionTestRoots, - watchMode, - }); - plans.push(...(boundedPlans ?? [plan])); + plans.push(...createBoundedExtensionPlans(plan)); } continue; } @@ -3761,9 +3763,12 @@ export function buildVitestRunPlans( plans.push(...broadToolingScriptPlans); continue; } + const processRoots = EXTENSION_TEST_PROCESS_ROOTS.get(config); const boundedExtensionRoots = grouped.flatMap((targetArg) => { const root = toRepoRelativeTarget(targetArg, cwd); - return isMatrixExtensionRoot(root) && isExistingDirectoryTarget(targetArg, cwd) ? [root] : []; + return processRoots?.includes(root) && isExistingDirectoryTarget(targetArg, cwd) + ? [root] + : []; }); const boundedRootsCoverGroupedTargets = grouped.every((targetArg) => { const relativeTarget = toRepoRelativeTarget(targetArg, cwd); @@ -3776,7 +3781,7 @@ export function buildVitestRunPlans( ? createBoundedExtensionPlans({ config, forwardedArgs: forwardedPlanArgs, - roots: boundedExtensionRoots, + includePatterns, watchMode, }) : null; @@ -3856,12 +3861,11 @@ export function buildFullSuiteVitestRunPlans(args: string[], cwd = process.cwd() listGatewayServerTestTargets(cwd), GATEWAY_SERVER_TEST_PROCESS_COUNT, ); - } else if (config === EXTENSION_MATRIX_VITEST_CONFIG) { - chunks = createExtensionTestProcessTargetChunks( - config, - matrixExtensionTestRoots, - forwardedArgs, - ); + } else { + const roots = EXTENSION_TEST_PROCESS_ROOTS.get(config); + if (roots) { + chunks = createExtensionTestProcessTargetChunks(config, roots, forwardedArgs); + } } if (chunks.length > 0) { return chunks.map((targets) => ({ diff --git a/test/scripts/test-projects.test.ts b/test/scripts/test-projects.test.ts index 87a461ee6a06..7a46df9dd70a 100644 --- a/test/scripts/test-projects.test.ts +++ b/test/scripts/test-projects.test.ts @@ -42,26 +42,35 @@ import { const normalizeRepoPath = toRepoPath; const MATRIX_TEST_PROCESS_FILE_LIMIT = 40; +const TELEGRAM_TEST_PROCESS_FILE_LIMIT = 5; function expectedMatrixTestProcessCount() { const testFileCount = listExtensionTestFilesForRoots(["extensions/matrix"]).length; return Math.max(1, Math.ceil(testFileCount / MATRIX_TEST_PROCESS_FILE_LIMIT)); } +function expectedTelegramTestProcessCount() { + const testFileCount = listExtensionTestFilesForRoots(["extensions/telegram"]).length; + return Math.max(1, Math.ceil(testFileCount / TELEGRAM_TEST_PROCESS_FILE_LIMIT)); +} + function listExpectedFullExtensionRunPlans() { const matrixConfig = "test/vitest/vitest.extension-matrix.config.ts"; - const matrixPlans = buildVitestRunPlans(["extensions/matrix"], process.cwd()); - return listFullExtensionVitestProjectConfigs().flatMap((config) => - config === matrixConfig - ? matrixPlans - : [ - { - config, - forwardedArgs: [], - includePatterns: null, - watchMode: false, - }, - ], + const telegramConfig = "test/vitest/vitest.extension-telegram.config.ts"; + const boundedPlansByConfig = new Map([ + [matrixConfig, buildVitestRunPlans(["extensions/matrix"], process.cwd())], + [telegramConfig, buildVitestRunPlans(["extensions/telegram"], process.cwd())], + ]); + return listFullExtensionVitestProjectConfigs().flatMap( + (config) => + boundedPlansByConfig.get(config) ?? [ + { + config, + forwardedArgs: [], + includePatterns: null, + watchMode: false, + }, + ], ); } @@ -2084,12 +2093,15 @@ describe("scripts/test-projects changed-target routing", () => { it("routes the top-level extensions target to every extension shard", () => { const matrixConfig = "test/vitest/vitest.extension-matrix.config.ts"; + const telegramConfig = "test/vitest/vitest.extension-telegram.config.ts"; const plans = buildVitestRunPlans(["extensions"], process.cwd()); const matrixPlans = plans.filter((plan) => plan.config === matrixConfig); + const telegramPlans = plans.filter((plan) => plan.config === telegramConfig); + const boundedConfigs = new Set([matrixConfig, telegramConfig]); - expect(plans.filter((plan) => plan.config !== matrixConfig)).toEqual( + expect(plans.filter((plan) => !boundedConfigs.has(plan.config))).toEqual( listFullExtensionVitestProjectConfigs() - .filter((config) => config !== matrixConfig) + .filter((config) => !boundedConfigs.has(config)) .map((config) => ({ config, forwardedArgs: [], @@ -2106,9 +2118,34 @@ describe("scripts/test-projects changed-target routing", () => { expect(matrixPlans.flatMap((plan) => plan.includePatterns ?? [])).toEqual( listExtensionTestFilesForRoots(["extensions/matrix"]), ); + expect(telegramPlans).toHaveLength(expectedTelegramTestProcessCount()); + expect( + telegramPlans.every( + (plan) => (plan.includePatterns?.length ?? 0) <= TELEGRAM_TEST_PROCESS_FILE_LIMIT, + ), + ).toBe(true); + expect(telegramPlans.flatMap((plan) => plan.includePatterns ?? [])).toEqual( + listExtensionTestFilesForRoots(["extensions/telegram"]), + ); expect(plans).toEqual(listExpectedFullExtensionRunPlans()); }); + it("bounds an explicit Telegram config target across process lifetimes", () => { + const config = "test/vitest/vitest.extension-telegram.config.ts"; + const plans = buildVitestRunPlans([config], process.cwd()); + + expect(plans).toHaveLength(expectedTelegramTestProcessCount()); + expect(plans.every((plan) => plan.config === config)).toBe(true); + expect( + plans.every( + (plan) => (plan.includePatterns?.length ?? 0) <= TELEGRAM_TEST_PROCESS_FILE_LIMIT, + ), + ).toBe(true); + expect(plans.flatMap((plan) => plan.includePatterns ?? [])).toEqual( + listExtensionTestFilesForRoots(["extensions/telegram"]), + ); + }); + it("bounds an explicit Matrix directory target across process lifetimes", () => { const plans = buildVitestRunPlans(["extensions/matrix"], process.cwd());