From 753a7ad46cc0c1397f86e89b12438283284d8374 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 24 Oct 2025 18:38:52 +0000 Subject: [PATCH 1/3] Initial plan From 1f246e66f67d74bf9d93e784931c65ff40f9142c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 24 Oct 2025 18:48:39 +0000 Subject: [PATCH 2/3] Add comprehensive tests for resolveAndUpdatePythonPath prioritization logic Co-authored-by: eleanorjboyd <26030610+eleanorjboyd@users.noreply.github.com> --- .../configuration/resolvers/base.unit.test.ts | 355 ++++++++++++++++++ 1 file changed, 355 insertions(+) diff --git a/src/test/unittest/configuration/resolvers/base.unit.test.ts b/src/test/unittest/configuration/resolvers/base.unit.test.ts index ae63c4b9..34c80bf4 100644 --- a/src/test/unittest/configuration/resolvers/base.unit.test.ts +++ b/src/test/unittest/configuration/resolvers/base.unit.test.ts @@ -15,6 +15,7 @@ import * as helper from '../../../../extension/debugger/configuration/resolvers/ import * as vscodeapi from '../../../../extension/common/vscodeapi'; import { AttachRequestArguments, DebugOptions, LaunchRequestArguments } from '../../../../extension/types'; import { PythonEnvironment } from '../../../../extension/debugger/adapter/types'; +import { PythonPathSource } from '../../../../extension/debugger/types'; import * as pythonApi from '../../../../extension/common/python'; suite('Debugging - Config Resolver', () => { @@ -61,6 +62,10 @@ suite('Debugging - Config Resolver', () => { public isDebuggingFlask(debugConfiguration: Partial) { return BaseConfigurationResolver.isDebuggingFlask(debugConfiguration); } + + public getPythonPathSource() { + return this.pythonPathSource; + } } let resolver: BaseResolver; let getWorkspaceFoldersStub: sinon.SinonStub; @@ -265,6 +270,356 @@ suite('Debugging - Config Resolver', () => { expect(config).to.have.property('debugLauncherPython', pythonPath); }); + // Tests for prioritization of python path configuration + suite('resolveAndUpdatePythonPath prioritization tests', () => { + test('When pythonPath is a concrete path and python is undefined, python should be set to pythonPath value', async () => { + const expectedPath = path.join('path', 'to', 'custom', 'python'); + const config = { + pythonPath: expectedPath, + python: undefined, + }; + + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + + expect(config).to.not.have.property('pythonPath'); + expect(config).to.have.property('python', expectedPath); + }); + + test('When pythonPath is a concrete path and python is a different concrete path, python should take precedence', async () => { + const pythonPathValue = path.join('path', 'to', 'pythonPath', 'python'); + const pythonValue = path.join('path', 'to', 'python', 'python'); + const config = { + pythonPath: pythonPathValue, + python: pythonValue, + }; + + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + + expect(config).to.not.have.property('pythonPath'); + expect(config).to.have.property('python', pythonValue); + }); + + test('When pythonPath is ${command:python.interpreterPath} and python is a concrete path, python should take precedence', async () => { + const pythonValue = path.join('path', 'to', 'python', 'python'); + const interpreterPath = path.join('path', 'from', 'interpreter'); + const config = { + pythonPath: '${command:python.interpreterPath}', + python: pythonValue, + }; + + getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment); + + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + + expect(config).to.not.have.property('pythonPath'); + expect(config).to.have.property('python', pythonValue); + }); + + test('When pythonPath is a concrete path and python is ${command:python.interpreterPath}, python should resolve from interpreter', async () => { + const pythonPathValue = path.join('path', 'to', 'pythonPath', 'python'); + const interpreterPath = path.join('path', 'from', 'interpreter'); + const config = { + pythonPath: pythonPathValue, + python: '${command:python.interpreterPath}', + }; + + getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment); + + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + + expect(config).to.not.have.property('pythonPath'); + expect(config).to.have.property('python', interpreterPath); + }); + + test('When both pythonPath and python are ${command:python.interpreterPath}, both should resolve to interpreter path', async () => { + const interpreterPath = path.join('path', 'from', 'interpreter'); + const config = { + pythonPath: '${command:python.interpreterPath}', + python: '${command:python.interpreterPath}', + }; + + getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment); + + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + + expect(config).to.not.have.property('pythonPath'); + expect(config).to.have.property('python', interpreterPath); + }); + + test('When pythonPath is not set and python is not set, both should resolve from interpreter', async () => { + const interpreterPath = path.join('path', 'from', 'interpreter'); + const config = {}; + + getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment); + + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + + expect(config).to.not.have.property('pythonPath'); + expect(config).to.have.property('python', interpreterPath); + }); + + test('debugAdapterPython should use pythonPath when neither debugAdapterPython nor python are set', async () => { + const pythonPathValue = path.join('path', 'to', 'custom', 'python'); + const config = { + pythonPath: pythonPathValue, + }; + + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + + expect(config).to.not.have.property('pythonPath'); + expect(config).to.have.property('python', pythonPathValue); + expect(config).to.have.property('debugAdapterPython', pythonPathValue); + }); + + test('debugAdapterPython should use python when pythonPath is set but python has different value', async () => { + const pythonPathValue = path.join('path', 'to', 'pythonPath', 'python'); + const pythonValue = path.join('path', 'to', 'python', 'python'); + const config = { + pythonPath: pythonPathValue, + python: pythonValue, + }; + + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + + expect(config).to.not.have.property('pythonPath'); + expect(config).to.have.property('python', pythonValue); + expect(config).to.have.property('debugAdapterPython', pythonValue); + }); + + test('debugAdapterPython should prefer explicitly set debugAdapterPython over pythonPath', async () => { + const pythonPathValue = path.join('path', 'to', 'pythonPath', 'python'); + const debugAdapterValue = path.join('path', 'to', 'debugAdapter', 'python'); + const config = { + pythonPath: pythonPathValue, + debugAdapterPython: debugAdapterValue, + }; + + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + + expect(config).to.not.have.property('pythonPath'); + expect(config).to.have.property('python', pythonPathValue); + expect(config).to.have.property('debugAdapterPython', debugAdapterValue); + }); + + test('debugLauncherPython should use pythonPath when neither debugLauncherPython nor python are set', async () => { + const pythonPathValue = path.join('path', 'to', 'custom', 'python'); + const config = { + pythonPath: pythonPathValue, + }; + + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + + expect(config).to.not.have.property('pythonPath'); + expect(config).to.have.property('python', pythonPathValue); + expect(config).to.have.property('debugLauncherPython', pythonPathValue); + }); + + test('debugLauncherPython should use python when pythonPath is set but python has different value', async () => { + const pythonPathValue = path.join('path', 'to', 'pythonPath', 'python'); + const pythonValue = path.join('path', 'to', 'python', 'python'); + const config = { + pythonPath: pythonPathValue, + python: pythonValue, + }; + + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + + expect(config).to.not.have.property('pythonPath'); + expect(config).to.have.property('python', pythonValue); + expect(config).to.have.property('debugLauncherPython', pythonValue); + }); + + test('debugLauncherPython should prefer explicitly set debugLauncherPython over pythonPath', async () => { + const pythonPathValue = path.join('path', 'to', 'pythonPath', 'python'); + const debugLauncherValue = path.join('path', 'to', 'debugLauncher', 'python'); + const config = { + pythonPath: pythonPathValue, + debugLauncherPython: debugLauncherValue, + }; + + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + + expect(config).to.not.have.property('pythonPath'); + expect(config).to.have.property('python', pythonPathValue); + expect(config).to.have.property('debugLauncherPython', debugLauncherValue); + }); + + test('All three debug python fields can have different values when explicitly set', async () => { + const pythonValue = path.join('path', 'to', 'python', 'python'); + const debugAdapterValue = path.join('path', 'to', 'debugAdapter', 'python'); + const debugLauncherValue = path.join('path', 'to', 'debugLauncher', 'python'); + const config = { + python: pythonValue, + debugAdapterPython: debugAdapterValue, + debugLauncherPython: debugLauncherValue, + }; + + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + + expect(config).to.not.have.property('pythonPath'); + expect(config).to.have.property('python', pythonValue); + expect(config).to.have.property('debugAdapterPython', debugAdapterValue); + expect(config).to.have.property('debugLauncherPython', debugLauncherValue); + }); + + test('When debugAdapterPython is ${command:python.interpreterPath}, it should fallback to resolved pythonPath', async () => { + const interpreterPath = path.join('path', 'from', 'interpreter'); + const config = { + pythonPath: '${command:python.interpreterPath}', + debugAdapterPython: '${command:python.interpreterPath}', + }; + + getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment); + + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + + expect(config).to.not.have.property('pythonPath'); + expect(config).to.have.property('python', interpreterPath); + expect(config).to.have.property('debugAdapterPython', interpreterPath); + }); + + test('When debugLauncherPython is ${command:python.interpreterPath}, it should fallback to resolved pythonPath', async () => { + const interpreterPath = path.join('path', 'from', 'interpreter'); + const config = { + pythonPath: '${command:python.interpreterPath}', + debugLauncherPython: '${command:python.interpreterPath}', + }; + + getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment); + + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + + expect(config).to.not.have.property('pythonPath'); + expect(config).to.have.property('python', interpreterPath); + expect(config).to.have.property('debugLauncherPython', interpreterPath); + }); + + test('Complex scenario: pythonPath set, python differs, debugAdapterPython and debugLauncherPython both set differently', async () => { + const pythonPathValue = path.join('path', 'to', 'pythonPath', 'python'); + const pythonValue = path.join('path', 'to', 'python', 'python'); + const debugAdapterValue = path.join('path', 'to', 'debugAdapter', 'python'); + const debugLauncherValue = path.join('path', 'to', 'debugLauncher', 'python'); + const config = { + pythonPath: pythonPathValue, + python: pythonValue, + debugAdapterPython: debugAdapterValue, + debugLauncherPython: debugLauncherValue, + }; + + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + + expect(config).to.not.have.property('pythonPath'); + expect(config).to.have.property('python', pythonValue); + expect(config).to.have.property('debugAdapterPython', debugAdapterValue); + expect(config).to.have.property('debugLauncherPython', debugLauncherValue); + }); + + test('When pythonPath is undefined and python is concrete path, debugAdapter and debugLauncher should use python', async () => { + const pythonValue = path.join('path', 'to', 'python', 'python'); + const config = { + python: pythonValue, + }; + + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + + expect(config).to.not.have.property('pythonPath'); + expect(config).to.have.property('python', pythonValue); + expect(config).to.have.property('debugAdapterPython', pythonValue); + expect(config).to.have.property('debugLauncherPython', pythonValue); + }); + + test('When pythonPath is empty string, it should be treated as not set and resolve from interpreter', async () => { + const interpreterPath = path.join('path', 'from', 'interpreter'); + const config = { + pythonPath: '', + }; + + getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment); + + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + + expect(config).to.not.have.property('pythonPath'); + expect(config).to.have.property('python', interpreterPath); + }); + + // Tests for pythonPathSource field + test('pythonPathSource should be settingsJson when python is ${command:python.interpreterPath}', async () => { + const interpreterPath = path.join('path', 'from', 'interpreter'); + const config = { + python: '${command:python.interpreterPath}', + }; + + getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment); + + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + + expect(resolver.getPythonPathSource()).to.equal(PythonPathSource.settingsJson); + }); + + test('pythonPathSource should be settingsJson when python is undefined', async () => { + const interpreterPath = path.join('path', 'from', 'interpreter'); + const config = { + pythonPath: interpreterPath, + python: undefined, + }; + + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + + expect(resolver.getPythonPathSource()).to.equal(PythonPathSource.settingsJson); + }); + + test('pythonPathSource should be launchJson when python is explicitly set to a concrete path', async () => { + const pythonValue = path.join('path', 'to', 'python', 'python'); + const config = { + python: pythonValue, + }; + + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + + expect(resolver.getPythonPathSource()).to.equal(PythonPathSource.launchJson); + }); + + test('pythonPathSource should be launchJson when python is a concrete path even if pythonPath is ${command:python.interpreterPath}', async () => { + const pythonValue = path.join('path', 'to', 'python', 'python'); + const interpreterPath = path.join('path', 'from', 'interpreter'); + const config = { + pythonPath: '${command:python.interpreterPath}', + python: pythonValue, + }; + + getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment); + + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + + expect(resolver.getPythonPathSource()).to.equal(PythonPathSource.launchJson); + }); + + test('pythonPathSource should be settingsJson when both pythonPath and python are ${command:python.interpreterPath}', async () => { + const interpreterPath = path.join('path', 'from', 'interpreter'); + const config = { + pythonPath: '${command:python.interpreterPath}', + python: '${command:python.interpreterPath}', + }; + + getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment); + + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + + expect(resolver.getPythonPathSource()).to.equal(PythonPathSource.settingsJson); + }); + + test('pythonPathSource should be settingsJson when neither pythonPath nor python are set', async () => { + const interpreterPath = path.join('path', 'from', 'interpreter'); + const config = {}; + + getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment); + + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + + expect(resolver.getPythonPathSource()).to.equal(PythonPathSource.settingsJson); + }); + }); + const localHostTestMatrix: Record = { localhost: true, '127.0.0.1': true, From 3b64f0c369c7930e4ceac879e3d2e41ce5443d71 Mon Sep 17 00:00:00 2001 From: eleanorjboyd <26030610+eleanorjboyd@users.noreply.github.com> Date: Fri, 24 Oct 2025 14:45:37 -0700 Subject: [PATCH 3/3] add tests for resolveAndUpdatePythonPath --- .vscode/launch.json | 6 +- .../configuration/resolvers/base.unit.test.ts | 664 ++++++++++-------- 2 files changed, 357 insertions(+), 313 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index da993863..0b406f92 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -27,9 +27,9 @@ "--extensionDevelopmentPath=${workspaceFolder}", "--extensionTestsPath=${workspaceFolder}/out/test/unittest/index" ], - "env": { - "TEST_GREP": "Python API Tests" - }, + // "env": { + // "TEST_GREP": "Python API Tests" + // }, "outFiles": ["${workspaceFolder}/out/**/*.js"], "preLaunchTask": "tasks: watch-tests" } diff --git a/src/test/unittest/configuration/resolvers/base.unit.test.ts b/src/test/unittest/configuration/resolvers/base.unit.test.ts index 34c80bf4..14765f6d 100644 --- a/src/test/unittest/configuration/resolvers/base.unit.test.ts +++ b/src/test/unittest/configuration/resolvers/base.unit.test.ts @@ -270,399 +270,443 @@ suite('Debugging - Config Resolver', () => { expect(config).to.have.property('debugLauncherPython', pythonPath); }); - // Tests for prioritization of python path configuration - suite('resolveAndUpdatePythonPath prioritization tests', () => { - test('When pythonPath is a concrete path and python is undefined, python should be set to pythonPath value', async () => { - const expectedPath = path.join('path', 'to', 'custom', 'python'); - const config = { - pythonPath: expectedPath, - python: undefined, - }; - - await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); - - expect(config).to.not.have.property('pythonPath'); - expect(config).to.have.property('python', expectedPath); + const localHostTestMatrix: Record = { + localhost: true, + '127.0.0.1': true, + '::1': true, + '127.0.0.2': false, + '156.1.2.3': false, + '::2': false, + }; + Object.keys(localHostTestMatrix).forEach((key) => { + test(`Local host = ${localHostTestMatrix[key]} for ${key}`, () => { + const isLocalHost = resolver.isLocalHost(key); + + expect(isLocalHost).to.equal(localHostTestMatrix[key]); }); + }); + test('Is debugging fastapi=true', () => { + const config = { module: 'fastapi' }; + const isFastAPI = resolver.isDebuggingFastAPI(config as LaunchRequestArguments); + expect(isFastAPI).to.equal(true, 'not fastapi'); + }); + test('Is debugging fastapi=false', () => { + const config = { module: 'fastapi2' }; + const isFastAPI = resolver.isDebuggingFastAPI(config as LaunchRequestArguments); + expect(isFastAPI).to.equal(false, 'fastapi'); + }); + test('Is debugging fastapi=false when not defined', () => { + const config = {}; + const isFastAPI = resolver.isDebuggingFastAPI(config as LaunchRequestArguments); + expect(isFastAPI).to.equal(false, 'fastapi'); + }); + test('Is debugging flask=true', () => { + const config = { module: 'flask' }; + const isFlask = resolver.isDebuggingFlask(config as LaunchRequestArguments); + expect(isFlask).to.equal(true, 'not flask'); + }); + test('Is debugging flask=false', () => { + const config = { module: 'flask2' }; + const isFlask = resolver.isDebuggingFlask(config as LaunchRequestArguments); + expect(isFlask).to.equal(false, 'flask'); + }); + test('Is debugging flask=false when not defined', () => { + const config = {}; + const isFlask = resolver.isDebuggingFlask(config as LaunchRequestArguments); + expect(isFlask).to.equal(false, 'flask'); + }); +}); - test('When pythonPath is a concrete path and python is a different concrete path, python should take precedence', async () => { - const pythonPathValue = path.join('path', 'to', 'pythonPath', 'python'); - const pythonValue = path.join('path', 'to', 'python', 'python'); - const config = { - pythonPath: pythonPathValue, - python: pythonValue, - }; +// Tests for prioritization of python path configuration +suite('resolveAndUpdatePythonPath prioritization tests', () => { + class BaseResolver2 extends BaseConfigurationResolver { + public resolveDebugConfiguration( + _folder: WorkspaceFolder | undefined, + _debugConfiguration: DebugConfiguration, + _token?: CancellationToken, + ): Promise { + throw new Error('Not Implemented'); + } - await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + public resolveDebugConfigurationWithSubstitutedVariables( + _folder: WorkspaceFolder | undefined, + _debugConfiguration: DebugConfiguration, + _token?: CancellationToken, + ): Promise { + throw new Error('Not Implemented'); + } - expect(config).to.not.have.property('pythonPath'); - expect(config).to.have.property('python', pythonValue); - }); + public resolveAndUpdatePythonPath( + workspaceFolderUri: Uri | undefined, + debugConfiguration: LaunchRequestArguments, + ) { + return super.resolveAndUpdatePythonPath(workspaceFolderUri, debugConfiguration); + } - test('When pythonPath is ${command:python.interpreterPath} and python is a concrete path, python should take precedence', async () => { - const pythonValue = path.join('path', 'to', 'python', 'python'); - const interpreterPath = path.join('path', 'from', 'interpreter'); - const config = { - pythonPath: '${command:python.interpreterPath}', - python: pythonValue, - }; + public getPythonPathSource() { + return this.pythonPathSource; + } + } - getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment); + let resolver: BaseResolver2; + let getInterpreterDetailsStub: sinon.SinonStub; - await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + setup(() => { + resolver = new BaseResolver2(); + getInterpreterDetailsStub = sinon.stub(pythonApi, 'getInterpreterDetails'); + }); - expect(config).to.not.have.property('pythonPath'); - expect(config).to.have.property('python', pythonValue); - }); + teardown(() => { + sinon.restore(); + }); - test('When pythonPath is a concrete path and python is ${command:python.interpreterPath}, python should resolve from interpreter', async () => { - const pythonPathValue = path.join('path', 'to', 'pythonPath', 'python'); - const interpreterPath = path.join('path', 'from', 'interpreter'); - const config = { - pythonPath: pythonPathValue, - python: '${command:python.interpreterPath}', - }; + test('When pythonPath is a concrete path and python is undefined, python should be set to pythonPath value', async () => { + const expectedPath = path.join('path', 'to', 'custom', 'python'); + const config = { + pythonPath: expectedPath, + python: undefined, + }; - getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment); + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); - await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + expect(config).to.not.have.property('pythonPath'); + expect(config).to.have.property('python', expectedPath); + }); - expect(config).to.not.have.property('pythonPath'); - expect(config).to.have.property('python', interpreterPath); - }); + test('When pythonPath is a concrete path and python is a different concrete path, python should take precedence', async () => { + const pythonPathValue = path.join('path', 'to', 'pythonPath', 'python'); + const pythonValue = path.join('path', 'to', 'python', 'python'); + const config = { + pythonPath: pythonPathValue, + python: pythonValue, + }; - test('When both pythonPath and python are ${command:python.interpreterPath}, both should resolve to interpreter path', async () => { - const interpreterPath = path.join('path', 'from', 'interpreter'); - const config = { - pythonPath: '${command:python.interpreterPath}', - python: '${command:python.interpreterPath}', - }; + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); - getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment); + expect(config).to.not.have.property('pythonPath'); + expect(config).to.have.property('python', pythonValue); + }); - await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + test('When pythonPath is ${command:python.interpreterPath} and python is a concrete path, python should take precedence', async () => { + const pythonValue = path.join('path', 'to', 'python', 'python'); + const interpreterPath = path.join('path', 'from', 'interpreter'); + const config = { + pythonPath: '${command:python.interpreterPath}', + python: pythonValue, + }; - expect(config).to.not.have.property('pythonPath'); - expect(config).to.have.property('python', interpreterPath); - }); + getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment); - test('When pythonPath is not set and python is not set, both should resolve from interpreter', async () => { - const interpreterPath = path.join('path', 'from', 'interpreter'); - const config = {}; + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); - getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment); + expect(config).to.not.have.property('pythonPath'); + expect(config).to.have.property('python', pythonValue); + }); - await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + test('When pythonPath is a concrete path and python is ${command:python.interpreterPath}, python should resolve from interpreter', async () => { + const pythonPathValue = path.join('path', 'to', 'pythonPath', 'python'); + const interpreterPath = path.join('path', 'from', 'interpreter'); + const config = { + pythonPath: pythonPathValue, + python: '${command:python.interpreterPath}', + }; - expect(config).to.not.have.property('pythonPath'); - expect(config).to.have.property('python', interpreterPath); - }); + getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment); - test('debugAdapterPython should use pythonPath when neither debugAdapterPython nor python are set', async () => { - const pythonPathValue = path.join('path', 'to', 'custom', 'python'); - const config = { - pythonPath: pythonPathValue, - }; + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); - await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + expect(config).to.not.have.property('pythonPath'); + expect(config).to.have.property('python', interpreterPath); + }); - expect(config).to.not.have.property('pythonPath'); - expect(config).to.have.property('python', pythonPathValue); - expect(config).to.have.property('debugAdapterPython', pythonPathValue); - }); + test('When both pythonPath and python are ${command:python.interpreterPath}, both should resolve to interpreter path', async () => { + const interpreterPath = path.join('path', 'from', 'interpreter'); + const config = { + pythonPath: '${command:python.interpreterPath}', + python: '${command:python.interpreterPath}', + }; - test('debugAdapterPython should use python when pythonPath is set but python has different value', async () => { - const pythonPathValue = path.join('path', 'to', 'pythonPath', 'python'); - const pythonValue = path.join('path', 'to', 'python', 'python'); - const config = { - pythonPath: pythonPathValue, - python: pythonValue, - }; + getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment); - await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); - expect(config).to.not.have.property('pythonPath'); - expect(config).to.have.property('python', pythonValue); - expect(config).to.have.property('debugAdapterPython', pythonValue); - }); + expect(config).to.not.have.property('pythonPath'); + expect(config).to.have.property('python', interpreterPath); + }); - test('debugAdapterPython should prefer explicitly set debugAdapterPython over pythonPath', async () => { - const pythonPathValue = path.join('path', 'to', 'pythonPath', 'python'); - const debugAdapterValue = path.join('path', 'to', 'debugAdapter', 'python'); - const config = { - pythonPath: pythonPathValue, - debugAdapterPython: debugAdapterValue, - }; + test('When pythonPath is not set and python is not set, both should resolve from interpreter', async () => { + const interpreterPath = path.join('path', 'from', 'interpreter'); + const config = {}; - await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment); - expect(config).to.not.have.property('pythonPath'); - expect(config).to.have.property('python', pythonPathValue); - expect(config).to.have.property('debugAdapterPython', debugAdapterValue); - }); + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); - test('debugLauncherPython should use pythonPath when neither debugLauncherPython nor python are set', async () => { - const pythonPathValue = path.join('path', 'to', 'custom', 'python'); - const config = { - pythonPath: pythonPathValue, - }; + expect(config).to.not.have.property('pythonPath'); + expect(config).to.have.property('python', interpreterPath); + }); - await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + test('debugAdapterPython should use pythonPath when neither debugAdapterPython nor python are set', async () => { + const pythonPathValue = path.join('path', 'to', 'custom', 'python'); + const config = { + pythonPath: pythonPathValue, + }; - expect(config).to.not.have.property('pythonPath'); - expect(config).to.have.property('python', pythonPathValue); - expect(config).to.have.property('debugLauncherPython', pythonPathValue); - }); + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); - test('debugLauncherPython should use python when pythonPath is set but python has different value', async () => { - const pythonPathValue = path.join('path', 'to', 'pythonPath', 'python'); - const pythonValue = path.join('path', 'to', 'python', 'python'); - const config = { - pythonPath: pythonPathValue, - python: pythonValue, - }; + expect(config).to.not.have.property('pythonPath'); + expect(config).to.have.property('python', pythonPathValue); + expect(config).to.have.property('debugAdapterPython', pythonPathValue); + }); - await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + test('debugAdapterPython should use pythonPath when pythonPath is set but python has different value', async () => { + const pythonPathValue = path.join('path', 'to', 'pythonPath', 'python'); + const pythonValue = path.join('path', 'to', 'python', 'python'); + const config = { + pythonPath: pythonPathValue, + python: pythonValue, + }; - expect(config).to.not.have.property('pythonPath'); - expect(config).to.have.property('python', pythonValue); - expect(config).to.have.property('debugLauncherPython', pythonValue); - }); + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); - test('debugLauncherPython should prefer explicitly set debugLauncherPython over pythonPath', async () => { - const pythonPathValue = path.join('path', 'to', 'pythonPath', 'python'); - const debugLauncherValue = path.join('path', 'to', 'debugLauncher', 'python'); - const config = { - pythonPath: pythonPathValue, - debugLauncherPython: debugLauncherValue, - }; + expect(config).to.not.have.property('pythonPath'); + expect(config).to.have.property('python', pythonValue); + expect(config).to.have.property('debugAdapterPython', pythonPathValue); + }); - await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + test('debugAdapterPython should prefer explicitly set debugAdapterPython over pythonPath', async () => { + const pythonPathValue = path.join('path', 'to', 'pythonPath', 'python'); + const debugAdapterValue = path.join('path', 'to', 'debugAdapter', 'python'); + const config = { + pythonPath: pythonPathValue, + debugAdapterPython: debugAdapterValue, + }; - expect(config).to.not.have.property('pythonPath'); - expect(config).to.have.property('python', pythonPathValue); - expect(config).to.have.property('debugLauncherPython', debugLauncherValue); - }); + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); - test('All three debug python fields can have different values when explicitly set', async () => { - const pythonValue = path.join('path', 'to', 'python', 'python'); - const debugAdapterValue = path.join('path', 'to', 'debugAdapter', 'python'); - const debugLauncherValue = path.join('path', 'to', 'debugLauncher', 'python'); - const config = { - python: pythonValue, - debugAdapterPython: debugAdapterValue, - debugLauncherPython: debugLauncherValue, - }; - - await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); - - expect(config).to.not.have.property('pythonPath'); - expect(config).to.have.property('python', pythonValue); - expect(config).to.have.property('debugAdapterPython', debugAdapterValue); - expect(config).to.have.property('debugLauncherPython', debugLauncherValue); - }); + expect(config).to.not.have.property('pythonPath'); + expect(config).to.have.property('python', pythonPathValue); + expect(config).to.have.property('debugAdapterPython', debugAdapterValue); + }); - test('When debugAdapterPython is ${command:python.interpreterPath}, it should fallback to resolved pythonPath', async () => { - const interpreterPath = path.join('path', 'from', 'interpreter'); - const config = { - pythonPath: '${command:python.interpreterPath}', - debugAdapterPython: '${command:python.interpreterPath}', - }; + test('debugLauncherPython should use pythonPath when neither debugLauncherPython nor python are set', async () => { + const pythonPathValue = path.join('path', 'to', 'custom', 'python'); + const config = { + pythonPath: pythonPathValue, + }; - getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment); + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); - await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + expect(config).to.not.have.property('pythonPath'); + expect(config).to.have.property('python', pythonPathValue); + expect(config).to.have.property('debugLauncherPython', pythonPathValue); + }); - expect(config).to.not.have.property('pythonPath'); - expect(config).to.have.property('python', interpreterPath); - expect(config).to.have.property('debugAdapterPython', interpreterPath); - }); + test('debugLauncherPython should use pythonPath when pythonPath is set but python has different value', async () => { + const pythonPathValue = path.join('path', 'to', 'pythonPath', 'python'); + const pythonValue = path.join('path', 'to', 'python', 'python'); + const config = { + pythonPath: pythonPathValue, + python: pythonValue, + }; - test('When debugLauncherPython is ${command:python.interpreterPath}, it should fallback to resolved pythonPath', async () => { - const interpreterPath = path.join('path', 'from', 'interpreter'); - const config = { - pythonPath: '${command:python.interpreterPath}', - debugLauncherPython: '${command:python.interpreterPath}', - }; + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); - getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment); + expect(config).to.not.have.property('pythonPath'); + expect(config).to.have.property('python', pythonValue); + expect(config).to.have.property('debugLauncherPython', pythonPathValue); + }); - await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + test('debugLauncherPython should prefer explicitly set debugLauncherPython over pythonPath', async () => { + const pythonPathValue = path.join('path', 'to', 'pythonPath', 'python'); + const debugLauncherValue = path.join('path', 'to', 'debugLauncher', 'python'); + const config = { + pythonPath: pythonPathValue, + debugLauncherPython: debugLauncherValue, + }; - expect(config).to.not.have.property('pythonPath'); - expect(config).to.have.property('python', interpreterPath); - expect(config).to.have.property('debugLauncherPython', interpreterPath); - }); + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); - test('Complex scenario: pythonPath set, python differs, debugAdapterPython and debugLauncherPython both set differently', async () => { - const pythonPathValue = path.join('path', 'to', 'pythonPath', 'python'); - const pythonValue = path.join('path', 'to', 'python', 'python'); - const debugAdapterValue = path.join('path', 'to', 'debugAdapter', 'python'); - const debugLauncherValue = path.join('path', 'to', 'debugLauncher', 'python'); - const config = { - pythonPath: pythonPathValue, - python: pythonValue, - debugAdapterPython: debugAdapterValue, - debugLauncherPython: debugLauncherValue, - }; - - await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); - - expect(config).to.not.have.property('pythonPath'); - expect(config).to.have.property('python', pythonValue); - expect(config).to.have.property('debugAdapterPython', debugAdapterValue); - expect(config).to.have.property('debugLauncherPython', debugLauncherValue); - }); + expect(config).to.not.have.property('pythonPath'); + expect(config).to.have.property('python', pythonPathValue); + expect(config).to.have.property('debugLauncherPython', debugLauncherValue); + }); - test('When pythonPath is undefined and python is concrete path, debugAdapter and debugLauncher should use python', async () => { - const pythonValue = path.join('path', 'to', 'python', 'python'); - const config = { - python: pythonValue, - }; + test('All three debug python fields can have different values when explicitly set', async () => { + const pythonValue = path.join('path', 'to', 'python', 'python'); + const debugAdapterValue = path.join('path', 'to', 'debugAdapter', 'python'); + const debugLauncherValue = path.join('path', 'to', 'debugLauncher', 'python'); + const config = { + python: pythonValue, + debugAdapterPython: debugAdapterValue, + debugLauncherPython: debugLauncherValue, + }; - await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); - expect(config).to.not.have.property('pythonPath'); - expect(config).to.have.property('python', pythonValue); - expect(config).to.have.property('debugAdapterPython', pythonValue); - expect(config).to.have.property('debugLauncherPython', pythonValue); - }); + expect(config).to.not.have.property('pythonPath'); + expect(config).to.have.property('python', pythonValue); + expect(config).to.have.property('debugAdapterPython', debugAdapterValue); + expect(config).to.have.property('debugLauncherPython', debugLauncherValue); + }); - test('When pythonPath is empty string, it should be treated as not set and resolve from interpreter', async () => { - const interpreterPath = path.join('path', 'from', 'interpreter'); - const config = { - pythonPath: '', - }; + test('When debugAdapterPython is ${command:python.interpreterPath}, it should fallback to resolved pythonPath', async () => { + const interpreterPath = path.join('path', 'from', 'interpreter'); + const config = { + pythonPath: '${command:python.interpreterPath}', + debugAdapterPython: '${command:python.interpreterPath}', + }; - getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment); + getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment); - await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); - expect(config).to.not.have.property('pythonPath'); - expect(config).to.have.property('python', interpreterPath); - }); + expect(config).to.not.have.property('pythonPath'); + expect(config).to.have.property('python', interpreterPath); + expect(config).to.have.property('debugAdapterPython', interpreterPath); + }); - // Tests for pythonPathSource field - test('pythonPathSource should be settingsJson when python is ${command:python.interpreterPath}', async () => { - const interpreterPath = path.join('path', 'from', 'interpreter'); - const config = { - python: '${command:python.interpreterPath}', - }; + test('When debugLauncherPython is ${command:python.interpreterPath}, it should fallback to resolved pythonPath', async () => { + const interpreterPath = path.join('path', 'from', 'interpreter'); + const config = { + pythonPath: '${command:python.interpreterPath}', + debugLauncherPython: '${command:python.interpreterPath}', + }; - getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment); + getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment); - await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); - expect(resolver.getPythonPathSource()).to.equal(PythonPathSource.settingsJson); - }); + expect(config).to.not.have.property('pythonPath'); + expect(config).to.have.property('python', interpreterPath); + expect(config).to.have.property('debugLauncherPython', interpreterPath); + }); - test('pythonPathSource should be settingsJson when python is undefined', async () => { - const interpreterPath = path.join('path', 'from', 'interpreter'); - const config = { - pythonPath: interpreterPath, - python: undefined, - }; + test('Complex scenario: pythonPath set, python differs, debugAdapterPython and debugLauncherPython both set differently', async () => { + const pythonPathValue = path.join('path', 'to', 'pythonPath', 'python'); + const pythonValue = path.join('path', 'to', 'python', 'python'); + const debugAdapterValue = path.join('path', 'to', 'debugAdapter', 'python'); + const debugLauncherValue = path.join('path', 'to', 'debugLauncher', 'python'); + const config = { + pythonPath: pythonPathValue, + python: pythonValue, + debugAdapterPython: debugAdapterValue, + debugLauncherPython: debugLauncherValue, + }; - await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); - expect(resolver.getPythonPathSource()).to.equal(PythonPathSource.settingsJson); - }); + expect(config).to.not.have.property('pythonPath'); + expect(config).to.have.property('python', pythonValue); + expect(config).to.have.property('debugAdapterPython', debugAdapterValue); + expect(config).to.have.property('debugLauncherPython', debugLauncherValue); + }); - test('pythonPathSource should be launchJson when python is explicitly set to a concrete path', async () => { - const pythonValue = path.join('path', 'to', 'python', 'python'); - const config = { - python: pythonValue, - }; + test('When pythonPath is undefined and python is concrete path, debugAdapter and debugLauncher should use resolved pythonPath from interpreter', async () => { + const pythonValue = path.join('path', 'to', 'python', 'python'); + const interpreterPath = path.join('path', 'from', 'interpreter'); + const config = { + python: pythonValue, + }; - await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment); - expect(resolver.getPythonPathSource()).to.equal(PythonPathSource.launchJson); - }); + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); - test('pythonPathSource should be launchJson when python is a concrete path even if pythonPath is ${command:python.interpreterPath}', async () => { - const pythonValue = path.join('path', 'to', 'python', 'python'); - const interpreterPath = path.join('path', 'from', 'interpreter'); - const config = { - pythonPath: '${command:python.interpreterPath}', - python: pythonValue, - }; + expect(config).to.not.have.property('pythonPath'); + expect(config).to.have.property('python', pythonValue); + expect(config).to.have.property('debugAdapterPython', interpreterPath); + expect(config).to.have.property('debugLauncherPython', interpreterPath); + }); - getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment); + test('When pythonPath is empty string, it should be treated as not set and resolve from interpreter', async () => { + const interpreterPath = path.join('path', 'from', 'interpreter'); + const config = { + pythonPath: '', + }; - await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment); - expect(resolver.getPythonPathSource()).to.equal(PythonPathSource.launchJson); - }); + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); - test('pythonPathSource should be settingsJson when both pythonPath and python are ${command:python.interpreterPath}', async () => { - const interpreterPath = path.join('path', 'from', 'interpreter'); - const config = { - pythonPath: '${command:python.interpreterPath}', - python: '${command:python.interpreterPath}', - }; + expect(config).to.not.have.property('pythonPath'); + expect(config).to.have.property('python', interpreterPath); + }); - getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment); + // Tests for pythonPathSource field + test('pythonPathSource should be settingsJson when python is ${command:python.interpreterPath}', async () => { + const interpreterPath = path.join('path', 'from', 'interpreter'); + const config = { + python: '${command:python.interpreterPath}', + }; - await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment); - expect(resolver.getPythonPathSource()).to.equal(PythonPathSource.settingsJson); - }); + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); - test('pythonPathSource should be settingsJson when neither pythonPath nor python are set', async () => { - const interpreterPath = path.join('path', 'from', 'interpreter'); - const config = {}; + expect(resolver.getPythonPathSource()).to.equal(PythonPathSource.settingsJson); + }); - getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment); + test('pythonPathSource should be settingsJson when python is undefined', async () => { + const interpreterPath = path.join('path', 'from', 'interpreter'); + const config = { + pythonPath: interpreterPath, + python: undefined, + }; - await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); - expect(resolver.getPythonPathSource()).to.equal(PythonPathSource.settingsJson); - }); + expect(resolver.getPythonPathSource()).to.equal(PythonPathSource.settingsJson); }); - const localHostTestMatrix: Record = { - localhost: true, - '127.0.0.1': true, - '::1': true, - '127.0.0.2': false, - '156.1.2.3': false, - '::2': false, - }; - Object.keys(localHostTestMatrix).forEach((key) => { - test(`Local host = ${localHostTestMatrix[key]} for ${key}`, () => { - const isLocalHost = resolver.isLocalHost(key); + test('pythonPathSource should be launchJson when python is explicitly set to a concrete path', async () => { + const pythonValue = path.join('path', 'to', 'python', 'python'); + const config = { + python: pythonValue, + }; - expect(isLocalHost).to.equal(localHostTestMatrix[key]); - }); - }); - test('Is debugging fastapi=true', () => { - const config = { module: 'fastapi' }; - const isFastAPI = resolver.isDebuggingFastAPI(config as LaunchRequestArguments); - expect(isFastAPI).to.equal(true, 'not fastapi'); - }); - test('Is debugging fastapi=false', () => { - const config = { module: 'fastapi2' }; - const isFastAPI = resolver.isDebuggingFastAPI(config as LaunchRequestArguments); - expect(isFastAPI).to.equal(false, 'fastapi'); - }); - test('Is debugging fastapi=false when not defined', () => { - const config = {}; - const isFastAPI = resolver.isDebuggingFastAPI(config as LaunchRequestArguments); - expect(isFastAPI).to.equal(false, 'fastapi'); + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + + expect(resolver.getPythonPathSource()).to.equal(PythonPathSource.launchJson); }); - test('Is debugging flask=true', () => { - const config = { module: 'flask' }; - const isFlask = resolver.isDebuggingFlask(config as LaunchRequestArguments); - expect(isFlask).to.equal(true, 'not flask'); + + test('pythonPathSource should be launchJson when python is a concrete path even if pythonPath is ${command:python.interpreterPath}', async () => { + const pythonValue = path.join('path', 'to', 'python', 'python'); + const interpreterPath = path.join('path', 'from', 'interpreter'); + const config = { + pythonPath: '${command:python.interpreterPath}', + python: pythonValue, + }; + + getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment); + + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + + expect(resolver.getPythonPathSource()).to.equal(PythonPathSource.launchJson); }); - test('Is debugging flask=false', () => { - const config = { module: 'flask2' }; - const isFlask = resolver.isDebuggingFlask(config as LaunchRequestArguments); - expect(isFlask).to.equal(false, 'flask'); + + test('pythonPathSource should be settingsJson when both pythonPath and python are ${command:python.interpreterPath}', async () => { + const interpreterPath = path.join('path', 'from', 'interpreter'); + const config = { + pythonPath: '${command:python.interpreterPath}', + python: '${command:python.interpreterPath}', + }; + + getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment); + + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + + expect(resolver.getPythonPathSource()).to.equal(PythonPathSource.settingsJson); }); - test('Is debugging flask=false when not defined', () => { + + test('pythonPathSource should be settingsJson when neither pythonPath nor python are set', async () => { + const interpreterPath = path.join('path', 'from', 'interpreter'); const config = {}; - const isFlask = resolver.isDebuggingFlask(config as LaunchRequestArguments); - expect(isFlask).to.equal(false, 'flask'); + + getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment); + + await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments); + + expect(resolver.getPythonPathSource()).to.equal(PythonPathSource.settingsJson); }); });