73 lines
3.1 KiB
TypeScript
73 lines
3.1 KiB
TypeScript
import assert from 'node:assert/strict'
|
|
import path from 'node:path'
|
|
|
|
import { test } from 'vitest'
|
|
|
|
import { createSourcePythonBackend } from './source-backend'
|
|
import { resolveSourcePython } from './source-python'
|
|
|
|
const ROOT: string = path.join(path.sep, 'checkout')
|
|
|
|
/** A checkout reporting exactly `present` (root-relative) as existing files. */
|
|
function checkout(present: string[] = [], exists: boolean = true) {
|
|
const files = new Set(present.map(entry => path.join(ROOT, entry)))
|
|
|
|
return { root: ROOT, fileExists: (candidate: string): boolean => exists && files.has(candidate) }
|
|
}
|
|
|
|
test('a checkout with no virtualenv resolves to no interpreter, never a PATH Python', (): void => {
|
|
const { root, fileExists } = checkout()
|
|
|
|
// The invariant: "this root has no runtime" must reach callers as null.
|
|
// Answering with a PATH Python runs an install's update probe and its backend
|
|
// under an interpreter nothing selected — it can import the checkout while
|
|
// lacking its selected dependencies (updater/checkout-source.ts).
|
|
assert.equal(resolveSourcePython(root, { fileExists, isWindows: false }), null)
|
|
assert.equal(resolveSourcePython(root, { fileExists, isWindows: true }), null)
|
|
})
|
|
|
|
test('a virtualenv-less checkout yields no source backend, so the ladder moves on', (): void => {
|
|
const { root, fileExists } = checkout()
|
|
const python: string | null = resolveSourcePython(root, { fileExists, isWindows: false })
|
|
|
|
assert.equal(createSourcePythonBackend(root, python, ['serve']), null)
|
|
})
|
|
|
|
test('the checkout virtualenv is preferred over the flat venv, on both layouts', (): void => {
|
|
const venv: string = path.join('venv', 'bin', 'python')
|
|
const dotVenv: string = path.join('.venv', 'bin', 'python')
|
|
const windows: string = path.join('.venv', 'Scripts', 'python.exe')
|
|
|
|
assert.equal(
|
|
resolveSourcePython(ROOT, { fileExists: checkout([venv, dotVenv]).fileExists, isWindows: false }),
|
|
path.join(ROOT, dotVenv)
|
|
)
|
|
assert.equal(
|
|
resolveSourcePython(ROOT, { fileExists: checkout([venv]).fileExists, isWindows: false }),
|
|
path.join(ROOT, venv)
|
|
)
|
|
assert.equal(
|
|
resolveSourcePython(ROOT, { fileExists: checkout([windows]).fileExists, isWindows: true }),
|
|
path.join(ROOT, windows)
|
|
)
|
|
})
|
|
|
|
test('an explicit developer interpreter wins, and is only honoured when it exists', (): void => {
|
|
const preferred: string = path.join(path.sep, 'opt', 'hermes', 'bin', 'python')
|
|
const aged: string = path.join(ROOT, 'venv', 'bin', 'python')
|
|
const { root, fileExists } = checkout(['venv/bin/python'], true)
|
|
const withPreferred = (candidate: string): boolean => candidate === preferred || fileExists(candidate)
|
|
|
|
assert.equal(
|
|
resolveSourcePython(root, { override: preferred, fileExists: withPreferred, isWindows: false }),
|
|
preferred
|
|
)
|
|
// An override naming a file that is gone does not resurrect a PATH Python;
|
|
// it falls through to the checkout, then to null.
|
|
assert.equal(resolveSourcePython(root, { override: preferred, fileExists, isWindows: false }), aged)
|
|
assert.equal(
|
|
resolveSourcePython(ROOT, { override: preferred, fileExists: checkout().fileExists, isWindows: false }),
|
|
null
|
|
)
|
|
})
|