From 80481653f6bfc3448b6ce3e814649f153bade0d0 Mon Sep 17 00:00:00 2001 From: Jacob Foshee Date: Wed, 3 Sep 2025 09:38:37 -0500 Subject: [PATCH] extension/settings: add support for multiple test env files New Setting: go.testEnvFiles Added go.testEnvFiles setting that accepts an array of environment file paths Allows loading multiple .env files in a specific order for test execution Later files in the array override variables from earlier files Backward Compatibility Existing go.testEnvFile setting continues to work as before Marked go.testEnvFile as deprecated with appropriate VS Code deprecation messaging When both settings are used together, go.testEnvFile is processed after all files in go.testEnvFiles Fixes #2473 --- extension/package-lock.json | 2 +- extension/package.json | 12 ++++- extension/src/testUtils.ts | 26 ++++++++-- extension/test/unit/testEnvFiles.test.ts | 61 ++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 7 deletions(-) create mode 100644 extension/test/unit/testEnvFiles.test.ts diff --git a/extension/package-lock.json b/extension/package-lock.json index d15fff953b..648162322b 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -49,7 +49,7 @@ }, "engines": { "node": ">=16.14.2", - "vscode": "^1.75.0" + "vscode": "^1.90.0" } }, "node_modules/@babel/code-frame": { diff --git a/extension/package.json b/extension/package.json index 2d48b1fff8..f3e06d109a 100644 --- a/extension/package.json +++ b/extension/package.json @@ -1438,7 +1438,17 @@ "go.testEnvFile": { "type": "string", "default": null, - "description": "Absolute path to a file containing environment variables definitions. File contents should be of the form key=value.", + "description": "Absolute path to a file containing environment variables definitions. File contents should be of the form key=value. (Deprecated: Use go.testEnvFiles instead)", + "scope": "resource", + "deprecationMessage": "Use go.testEnvFiles instead" + }, + "go.testEnvFiles": { + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "description": "Array of absolute paths to files containing environment variables definitions. File contents should be of the form key=value.", "scope": "resource" }, "go.testFlags": { diff --git a/extension/src/testUtils.ts b/extension/src/testUtils.ts index 4070f6e6d1..8a379be5c9 100644 --- a/extension/src/testUtils.ts +++ b/extension/src/testUtils.ts @@ -19,7 +19,7 @@ import { getCurrentPackage } from './goModules'; import { GoDocumentSymbolProvider } from './goDocumentSymbols'; import { getNonVendorPackages } from './goPackages'; import { getBinPath, getCurrentGoPath, getTempFilePath, LineBuffer, resolvePath } from './util'; -import { parseEnvFile } from './utils/envUtils'; +import { parseEnvFile, parseEnvFiles } from './utils/envUtils'; import { getEnvPath, expandFilePathInOutput, @@ -111,12 +111,28 @@ export function getTestEnvVars(config: vscode.WorkspaceConfiguration): any { const envVars = toolExecutionEnvironment(); const testEnvConfig = config['testEnvVars'] || {}; - let fileEnv: { [key: string]: any } = {}; - let testEnvFile = config['testEnvFile']; + // Collect environment files from both settings + const envFiles: string[] = []; + + // Add files from the new testEnvFiles setting (array) + const testEnvFiles = config['testEnvFiles'] || []; + if (Array.isArray(testEnvFiles)) { + envFiles.push(...testEnvFiles); + } + + // Add the deprecated testEnvFile setting (single file) for backward compatibility + const testEnvFile = config['testEnvFile']; if (testEnvFile) { - testEnvFile = resolvePath(testEnvFile); + envFiles.push(testEnvFile); + } + + // Parse all environment files + let fileEnv: { [key: string]: any } = {}; + if (envFiles.length > 0) { try { - fileEnv = parseEnvFile(testEnvFile, envVars); + // Resolve paths for all files + const resolvedFiles = envFiles.map((file) => resolvePath(file)); + fileEnv = parseEnvFiles(resolvedFiles, envVars); } catch (e) { console.log(e); } diff --git a/extension/test/unit/testEnvFiles.test.ts b/extension/test/unit/testEnvFiles.test.ts new file mode 100644 index 0000000000..c9cdcc7359 --- /dev/null +++ b/extension/test/unit/testEnvFiles.test.ts @@ -0,0 +1,61 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See LICENSE in the project root for license information. + *--------------------------------------------------------*/ + +import assert from 'assert'; +import path from 'path'; +import fs from 'fs'; +import os from 'os'; +import { parseEnvFiles } from '../../src/utils/envUtils'; + +suite('parseEnvFiles Tests', () => { + let tmpDir: string; + + setup(() => { + tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'go-test-env-')); + }); + + teardown(() => { + if (tmpDir && fs.existsSync(tmpDir)) { + fs.rmdirSync(tmpDir, { recursive: true }); + } + }); + + test('should handle empty array', () => { + const result = parseEnvFiles([]); + assert.deepStrictEqual(result, {}); + }); + + test('should handle undefined input', () => { + const result = parseEnvFiles(undefined); + assert.deepStrictEqual(result, {}); + }); + + test('should handle array of files', () => { + const envFile1 = path.join(tmpDir, 'first.env'); + const envFile2 = path.join(tmpDir, 'second.env'); + + fs.writeFileSync(envFile1, 'VAR1=value1\nSHARED=from_first'); + fs.writeFileSync(envFile2, 'VAR2=value2\nSHARED=from_second'); + + const result = parseEnvFiles([envFile1, envFile2]); + + assert.strictEqual(result.VAR1, 'value1'); + assert.strictEqual(result.VAR2, 'value2'); + // Later files should override earlier ones + assert.strictEqual(result.SHARED, 'from_second'); + }); + + test('should handle mixed valid and invalid files', () => { + const validFile = path.join(tmpDir, 'valid.env'); + const invalidFile = path.join(tmpDir, 'nonexistent.env'); + + fs.writeFileSync(validFile, 'VALID_VAR=valid_value'); + + // This should throw when trying to parse invalid file + assert.throws(() => { + parseEnvFiles([validFile, invalidFile]); + }); + }); +});