Files
promptfoo--promptfoo/test/providers/pythonCompletion.cliState.test.ts
wehub-resource-sync 0d3cb498a3
Deploy local.promptfoo.app / Deploy to Cloudflare Pages (push) Waiting to run
Test and Publish Multi-arch Docker Image / test (push) Waiting to run
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-amd64 platform:linux/amd64 runner:ubuntu-latest]) (push) Blocked by required conditions
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-arm64 platform:linux/arm64 runner:ubuntu-24.04-arm]) (push) Blocked by required conditions
Test and Publish Multi-arch Docker Image / merge-docker-digests (push) Blocked by required conditions
Test and Publish Multi-arch Docker Image / Attest Multi-arch Image (push) Blocked by required conditions
Validate Renovate Config / Validate Renovate Configuration (push) Waiting to run
CI / Shell Format Check (push) Has been cancelled
CI / Check Ruby (3.4) (push) Has been cancelled
CI / CI Config (push) Has been cancelled
CI / Test on Node ${{ matrix.node }} and ${{ matrix.os }}${{ matrix.shard && format(' (shard {0}/3)', matrix.shard) || '' }} (push) Has been cancelled
CI / Build on Node ${{ matrix.node }} (push) Has been cancelled
CI / Style Check (push) Has been cancelled
CI / Generate Assets (push) Has been cancelled
CI / Check Python (3.14) (push) Has been cancelled
CI / Check Python (3.9) (push) Has been cancelled
CI / Build Docs (push) Has been cancelled
CI / Code Scan Action (push) Has been cancelled
CI / Site tests (push) Has been cancelled
CI / webui tests (push) Has been cancelled
CI / Run Integration Tests (push) Has been cancelled
CI / Run Smoke Tests (push) Has been cancelled
CI / Go Tests (push) Has been cancelled
CI / Share Test (push) Has been cancelled
CI / Redteam (Production API) (push) Has been cancelled
CI / Redteam (Staging API) (push) Has been cancelled
CI / GitHub Actions Lint (push) Has been cancelled
CI / Check Ruby (3.0) (push) Has been cancelled
release-please / release-please (push) Has been cancelled
release-please / build (push) Has been cancelled
release-please / publish-npm (push) Has been cancelled
release-please / publish-npm-backfill (push) Has been cancelled
release-please / docker (push) Has been cancelled
release-please / publish-code-scan-action (push) Has been cancelled
release-please / attest-code-scan-action (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:24:08 +08:00

229 lines
6.3 KiB
TypeScript

/**
* Tests for cliState.maxConcurrency propagation to Python worker pool.
* This is a focused test file to avoid the complex mocking issues in pythonCompletion.test.ts.
*/
import { beforeEach, describe, expect, it, vi } from 'vitest';
import cliState from '../../src/cliState';
// Mock all dependencies to avoid import chain issues
vi.mock('../../src/python/pythonUtils', () => ({
getEnvInt: vi.fn(),
getConfiguredPythonPath: vi.fn(),
state: { cachedPythonPath: null, validationPromise: null },
}));
vi.mock('../../src/cache', () => ({
getCache: vi.fn().mockResolvedValue({ get: vi.fn(), set: vi.fn() }),
isCacheEnabled: vi.fn().mockReturnValue(false),
}));
vi.mock('../../src/logger', () => ({
default: {
debug: vi.fn(),
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
},
}));
vi.mock('../../src/util/createHash', () => ({
sha256: vi.fn().mockReturnValue('mockhash'),
}));
vi.mock('../../src/util/fileReference', () => ({
processConfigFileReferences: vi.fn((config) => config),
}));
vi.mock('../../src/util/index', () => ({
parsePathOrGlob: vi.fn().mockReturnValue({
filePath: 'script.py',
functionName: undefined,
isPathPattern: false,
extension: '.py',
}),
}));
vi.mock('../../src/util/json', () => ({
safeJsonStringify: vi.fn(JSON.stringify),
}));
vi.mock('../../src/providers/providerRegistry', () => ({
providerRegistry: {
register: vi.fn(),
unregister: vi.fn(),
},
}));
vi.mock('fs', () => ({
default: {
readFileSync: vi.fn().mockReturnValue('mock content'),
},
}));
vi.mock('path', () => ({
default: {
resolve: vi.fn().mockReturnValue('/absolute/path/script.py'),
relative: vi.fn().mockReturnValue('script.py'),
extname: vi.fn().mockReturnValue('.py'),
join: vi.fn((...args: string[]) => args.join('/')),
},
}));
// Create hoisted mock for PythonWorkerPool
const workerPoolMocks = vi.hoisted(() => {
const mockPoolInstance = {
initialize: vi.fn().mockResolvedValue(undefined),
execute: vi.fn().mockResolvedValue({ output: 'test' }),
getWorkerCount: vi.fn().mockReturnValue(1),
shutdown: vi.fn().mockResolvedValue(undefined),
};
// Use function keyword to make it a proper constructor
const PythonWorkerPoolMock = vi.fn(function (this: any) {
return mockPoolInstance;
});
return { mockPoolInstance, PythonWorkerPoolMock };
});
vi.mock('../../src/python/workerPool', async (importOriginal) => ({
...(await importOriginal()),
PythonWorkerPool: workerPoolMocks.PythonWorkerPoolMock,
}));
import { PythonProvider } from '../../src/providers/pythonCompletion';
// Import after mocks are set up
import { getEnvInt } from '../../src/python/pythonUtils';
import { PythonWorkerPool } from '../../src/python/workerPool';
describe('PythonProvider cliState.maxConcurrency', () => {
const mockPythonWorkerPool = vi.mocked(PythonWorkerPool);
const mockGetEnvInt = vi.mocked(getEnvInt);
const mockPoolInstance = workerPoolMocks.mockPoolInstance;
beforeEach(() => {
vi.clearAllMocks();
workerPoolMocks.PythonWorkerPoolMock.mockClear();
mockPoolInstance.initialize.mockReset().mockResolvedValue(undefined);
mockPoolInstance.execute.mockReset().mockResolvedValue({ output: 'test' });
// Reset cliState
cliState.maxConcurrency = undefined;
// Reset getEnvInt
mockGetEnvInt.mockReset();
mockGetEnvInt.mockReturnValue(undefined);
});
it('should use cliState.maxConcurrency when config.workers is not set', async () => {
cliState.maxConcurrency = 8;
const provider = new PythonProvider('script.py');
await provider.initialize();
expect(mockPythonWorkerPool).toHaveBeenCalledWith(
expect.any(String),
'call_api',
8, // from cliState.maxConcurrency
undefined,
undefined,
);
});
it('should prioritize config.workers over cliState.maxConcurrency', async () => {
cliState.maxConcurrency = 10;
const provider = new PythonProvider('script.py', {
config: {
basePath: process.cwd(),
workers: 3,
},
});
await provider.initialize();
expect(mockPythonWorkerPool).toHaveBeenCalledWith(
expect.any(String),
'call_api',
3, // config.workers takes priority
undefined,
undefined,
);
});
it('should prioritize PROMPTFOO_PYTHON_WORKERS over cliState.maxConcurrency', async () => {
cliState.maxConcurrency = 12;
mockGetEnvInt.mockReturnValue(5);
const provider = new PythonProvider('script.py');
await provider.initialize();
expect(mockPythonWorkerPool).toHaveBeenCalledWith(
expect.any(String),
'call_api',
5, // env var takes priority over cliState (Python-specific setting over general -j flag)
undefined,
undefined,
);
});
it('should fall back to cliState.maxConcurrency when PROMPTFOO_PYTHON_WORKERS is undefined', async () => {
cliState.maxConcurrency = 6;
mockGetEnvInt.mockReturnValue(undefined);
const provider = new PythonProvider('script.py');
await provider.initialize();
expect(mockPythonWorkerPool).toHaveBeenCalledWith(
expect.any(String),
'call_api',
6, // from cliState.maxConcurrency when env var not set
undefined,
undefined,
);
});
it('should default to 1 worker when nothing is set', async () => {
cliState.maxConcurrency = undefined;
mockGetEnvInt.mockReturnValue(undefined);
const provider = new PythonProvider('script.py');
await provider.initialize();
expect(mockPythonWorkerPool).toHaveBeenCalledWith(
expect.any(String),
'call_api',
1, // default
undefined,
undefined,
);
});
it('should enforce minimum of 1 worker when cliState.maxConcurrency is 0', async () => {
cliState.maxConcurrency = 0;
const provider = new PythonProvider('script.py');
await provider.initialize();
expect(mockPythonWorkerPool).toHaveBeenCalledWith(
expect.any(String),
'call_api',
1, // minimum enforced
undefined,
undefined,
);
});
it('should enforce minimum of 1 worker when cliState.maxConcurrency is negative', async () => {
cliState.maxConcurrency = -5;
const provider = new PythonProvider('script.py');
await provider.initialize();
expect(mockPythonWorkerPool).toHaveBeenCalledWith(
expect.any(String),
'call_api',
1, // minimum enforced
undefined,
undefined,
);
});
});