97 lines
4.5 KiB
Python
97 lines
4.5 KiB
Python
"""Configuration boundaries; credentials and model calls use synthetic fixtures."""
|
|
|
|
import os
|
|
from pathlib import Path
|
|
import tempfile
|
|
import unittest
|
|
from unittest.mock import patch, sentinel
|
|
|
|
from logistics_chat import config, create_app
|
|
|
|
|
|
class ConfigurationTests(unittest.TestCase):
|
|
def setUp(self):
|
|
directory = tempfile.TemporaryDirectory()
|
|
self.addCleanup(directory.cleanup)
|
|
self.root = Path(directory.name)
|
|
root_patch = patch.object(config, 'ROOT_DIR', self.root)
|
|
root_patch.start()
|
|
self.addCleanup(root_patch.stop)
|
|
environment = patch.dict(os.environ)
|
|
environment.start()
|
|
self.addCleanup(environment.stop)
|
|
os.environ.pop('DEEPSEEK_API_KEY', None)
|
|
|
|
def write_env(self, text):
|
|
(self.root / '.env').write_text(text, encoding='utf-8')
|
|
|
|
def application(self, **overrides):
|
|
return create_app({'TESTING': True, 'INITIALIZE_RUNTIME': False, **overrides})
|
|
|
|
def test_missing_file_leaves_ai_disabled_without_creating_files(self):
|
|
self.assertEqual(config.default_config()['DEEPSEEK_API_KEY'], '')
|
|
self.assertEqual(list(self.root.iterdir()), [])
|
|
|
|
def test_file_accepts_bom_quotes_comments_and_whitespace(self):
|
|
self.write_env('\ufeff# Local configuration\nexport DEEPSEEK_API_KEY=" file-key " # comment\n')
|
|
self.assertEqual(self.application().config['DEEPSEEK_API_KEY'], 'file-key')
|
|
self.assertNotIn('DEEPSEEK_API_KEY', os.environ)
|
|
|
|
def test_empty_or_unassigned_file_value_leaves_ai_disabled(self):
|
|
for line in ('DEEPSEEK_API_KEY=', 'DEEPSEEK_API_KEY', 'OTHER_SETTING=unused'):
|
|
with self.subTest(line=line):
|
|
self.write_env(line + '\n')
|
|
self.assertEqual(config.default_config()['DEEPSEEK_API_KEY'], '')
|
|
|
|
def test_environment_overrides_file_including_an_explicit_empty_value(self):
|
|
self.write_env('DEEPSEEK_API_KEY=file-key\n')
|
|
for value, expected in ((' environment-key ', 'environment-key'), ('', '')):
|
|
with self.subTest(value=value):
|
|
os.environ['DEEPSEEK_API_KEY'] = value
|
|
self.assertEqual(self.application().config['DEEPSEEK_API_KEY'], expected)
|
|
|
|
def test_file_values_are_literal_and_do_not_expand_environment_variables(self):
|
|
os.environ['SYNTHETIC_CONFIG_VALUE'] = 'expanded'
|
|
self.write_env("DEEPSEEK_API_KEY='literal-${SYNTHETIC_CONFIG_VALUE}'\n")
|
|
self.assertEqual(config.default_config()['DEEPSEEK_API_KEY'], 'literal-${SYNTHETIC_CONFIG_VALUE}')
|
|
|
|
def test_file_is_resolved_from_repository_root_instead_of_working_directory(self):
|
|
self.write_env('DEEPSEEK_API_KEY=repository-key\n')
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
(Path(directory) / '.env').write_text('DEEPSEEK_API_KEY=wrong-key\n', encoding='utf-8')
|
|
previous = Path.cwd()
|
|
try:
|
|
os.chdir(directory)
|
|
self.assertEqual(self.application().config['DEEPSEEK_API_KEY'], 'repository-key')
|
|
finally:
|
|
os.chdir(previous)
|
|
|
|
def test_explicit_factory_configuration_has_highest_priority(self):
|
|
self.write_env('DEEPSEEK_API_KEY=file-key\n')
|
|
os.environ['DEEPSEEK_API_KEY'] = 'environment-key'
|
|
for value in ('override-key', ''):
|
|
with self.subTest(value=value):
|
|
self.assertEqual(self.application(DEEPSEEK_API_KEY=value).config['DEEPSEEK_API_KEY'], value)
|
|
|
|
def test_new_factory_reads_updated_file_without_changing_an_existing_app(self):
|
|
self.write_env('DEEPSEEK_API_KEY=first-key\n')
|
|
first = self.application()
|
|
self.write_env('DEEPSEEK_API_KEY=second-key\n')
|
|
self.assertEqual(self.application().config['DEEPSEEK_API_KEY'], 'second-key')
|
|
self.assertEqual(first.config['DEEPSEEK_API_KEY'], 'first-key')
|
|
self.assertNotIn('DEEPSEEK_API_KEY', os.environ)
|
|
|
|
def test_model_client_sends_file_key_without_a_real_http_request(self):
|
|
from logistics_chat.ai_client import post_completion
|
|
|
|
self.write_env('DEEPSEEK_API_KEY=file-key\n')
|
|
app = self.application()
|
|
with app.app_context(), patch('logistics_chat.ai_client.requests.post', return_value=sentinel.response) as post:
|
|
response = post_completion({'model': 'deepseek-chat'}, timeout=(5, 90), stream=True)
|
|
self.assertIs(response, sentinel.response)
|
|
self.assertEqual(post.call_args.kwargs['headers']['Authorization'], 'Bearer file-key')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|