blob: 6b684d900ac71f48bc117fb7d56ca0c811737c23 [file]
#!/usr/bin/env vpython3
# Copyright 2025 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Tests for promptfoo_installation."""
import pathlib
import subprocess
import signal
import unittest
from unittest import mock
from pyfakefs import fake_filesystem_unittest
import promptfoo_installation
# pylint: disable=protected-access
class FromCipdPromptfooInstallationUnittest(fake_filesystem_unittest.TestCase):
"""Unit tests for FromCipdPromptfooInstallation."""
def setUp(self):
self.setUpPyfakefs()
run_patcher = mock.patch('promptfoo_installation._run')
self.mock_run = run_patcher.start()
self.addCleanup(run_patcher.stop)
check_call_patcher = mock.patch('subprocess.check_call')
self.mock_check_call = check_call_patcher.start()
self.addCleanup(check_call_patcher.stop)
def test_setup(self):
"""Tests that setup runs the correct cipd commands."""
promptfoo_installation.FromCipdPromptfooInstallation()
self.mock_check_call.assert_has_calls([
mock.call([
'cipd',
'init',
'-force',
str(promptfoo_installation.CIPD_ROOT),
]),
mock.call([
'cipd',
'install',
'infra/3pp/tools/nodejs/linux-${arch}',
mock.ANY,
'-root',
promptfoo_installation.CIPD_ROOT,
'-log-level',
'warning',
],
stdout=subprocess.DEVNULL),
mock.call([
'cipd',
'install',
'infra/3pp/npm/promptfoo/linux-${arch}',
mock.ANY,
'-root',
promptfoo_installation.CIPD_ROOT,
'-log-level',
'warning',
],
stdout=subprocess.DEVNULL),
])
def test_setup_already_installed(self):
"""Tests that setup does not init cipd if already installed."""
executable_path = (promptfoo_installation.CIPD_ROOT / 'node_modules' /
'.bin' / 'promptfoo')
self.fs.create_file(executable_path)
promptfoo_installation.FromCipdPromptfooInstallation()
self.mock_check_call.assert_has_calls([
mock.call([
'cipd',
'install',
'infra/3pp/tools/nodejs/linux-${arch}',
mock.ANY,
'-root',
promptfoo_installation.CIPD_ROOT,
'-log-level',
'warning',
],
stdout=subprocess.DEVNULL),
mock.call([
'cipd',
'install',
'infra/3pp/npm/promptfoo/linux-${arch}',
mock.ANY,
'-root',
promptfoo_installation.CIPD_ROOT,
'-log-level',
'warning',
],
stdout=subprocess.DEVNULL),
])
# Check that 'cipd init' was NOT called.
for call in self.mock_check_call.call_args_list:
self.assertNotIn('init', call.args[0])
def test_installed_true(self):
"""Tests that installed is true when the executable exists."""
executable_path = (promptfoo_installation.CIPD_ROOT / 'node_modules' /
'.bin' / 'promptfoo')
self.fs.create_file(executable_path)
installation = promptfoo_installation.FromCipdPromptfooInstallation()
self.assertTrue(installation.installed)
def test_installed_false(self):
"""Tests that installed is false when the executable does not exist."""
installation = promptfoo_installation.FromCipdPromptfooInstallation()
self.assertFalse(installation.installed)
def test_run(self):
"""Tests that run calls the promptfoo executable."""
executable_path = (promptfoo_installation.CIPD_ROOT / 'node_modules' /
'.bin' / 'promptfoo')
self.fs.create_file(executable_path)
node_path = promptfoo_installation.CIPD_ROOT / 'bin' / 'node'
self.fs.create_file(node_path)
installation = promptfoo_installation.FromCipdPromptfooInstallation()
installation.run(['eval', '-c', 'config.yaml'], cwd='/tmp/test')
self.mock_run.assert_called_once_with([
str(node_path),
str(executable_path), 'eval', '-c', 'config.yaml'
],
cwd='/tmp/test')
class PreinstalledPromptfooInstallationUnittest(
fake_filesystem_unittest.TestCase):
"""Unit tests for PreinstalledPromptfooInstallation."""
def setUp(self):
self.setUpPyfakefs()
run_patcher = mock.patch('promptfoo_installation._run')
self.mock_run = run_patcher.start()
self.addCleanup(run_patcher.stop)
def test_installed_true(self):
"""Tests that installed is true when the executable exists."""
self.fs.create_file('/tmp/promptfoo')
installation = (
promptfoo_installation.PreinstalledPromptfooInstallation(
pathlib.Path('/tmp/promptfoo')))
self.assertTrue(installation.installed)
def test_installed_false(self):
"""Tests that installed is false when the executable does not exist."""
installation = (
promptfoo_installation.PreinstalledPromptfooInstallation(
pathlib.Path('/tmp/promptfoo')))
self.assertFalse(installation.installed)
def test_run(self):
"""Tests that run calls the promptfoo executable."""
self.fs.create_file('/tmp/promptfoo')
installation = (
promptfoo_installation.PreinstalledPromptfooInstallation(
pathlib.Path('/tmp/promptfoo')))
installation.run(['eval', '-c', 'config.yaml'], cwd='/tmp/test')
self.mock_run.assert_called_once_with(
[str(pathlib.Path('/tmp/promptfoo')), 'eval', '-c', 'config.yaml'],
cwd='/tmp/test')
class RunUnittest(unittest.TestCase):
"""Unit tests for _run."""
@mock.patch('sys.platform', 'win32')
@mock.patch('subprocess.run')
def test_win32(self, mock_run):
mock_run.return_value = subprocess.CompletedProcess(['cmd'], 0, 'out',
'')
result = promptfoo_installation._run(['cmd'], cwd='/tmp')
mock_run.assert_called_once_with(['cmd'],
cwd='/tmp',
check=False,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
self.assertEqual(result.returncode, 0)
self.assertEqual(result.stdout, 'out')
@mock.patch('signal.SIGKILL', 9, create=True)
@mock.patch('sys.platform', 'linux')
@mock.patch('os.killpg', create=True)
@mock.patch('subprocess.Popen')
def test_unix(self, mock_popen, mock_killpg):
mock_proc = mock.MagicMock()
mock_proc.pid = 12345
mock_proc.args = ['cmd']
mock_proc.returncode = 0
mock_proc.communicate.return_value = ('out', '')
mock_proc.__enter__.return_value = mock_proc
mock_popen.return_value = mock_proc
result = promptfoo_installation._run(['cmd'], cwd='/tmp')
mock_popen.assert_called_once_with(['cmd'],
cwd='/tmp',
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
start_new_session=True)
mock_proc.communicate.assert_called_once()
mock_killpg.assert_called_once_with(12345, signal.SIGKILL)
self.assertEqual(result.returncode, 0)
self.assertEqual(result.stdout, 'out')
@mock.patch('signal.SIGKILL', 9, create=True)
@mock.patch('sys.platform', 'linux')
@mock.patch('os.killpg', create=True)
@mock.patch('subprocess.Popen')
def test_unix_killpg_oserror(self, mock_popen, mock_killpg):
mock_proc = mock.MagicMock()
mock_proc.pid = 12345
mock_proc.args = ['cmd']
mock_proc.returncode = 0
mock_proc.communicate.return_value = ('out', '')
mock_proc.__enter__.return_value = mock_proc
mock_popen.return_value = mock_proc
mock_killpg.side_effect = OSError('Process not found')
result = promptfoo_installation._run(['cmd'], cwd='/tmp')
mock_killpg.assert_called_once_with(12345, signal.SIGKILL)
self.assertEqual(result.returncode, 0)
self.assertEqual(result.stdout, 'out')
if __name__ == '__main__':
unittest.main()