| 20 | skip_message = "Skipping LLM tests as LLM is not configured. This is normal if you haven't set up a local LLM server." |
| 21 | |
| 22 | class TestEnvironmentLoading(unittest.TestCase): |
| 23 | def setUp(self): |
| 24 | # Save original environment |
| 25 | self.original_env = dict(os.environ) |
| 26 | # Clear environment variables we're testing |
| 27 | for key in ['TEST_VAR']: |
| 28 | if key in os.environ: |
| 29 | del os.environ[key] |
| 30 | |
| 31 | def tearDown(self): |
| 32 | # Restore original environment |
| 33 | os.environ.clear() |
| 34 | os.environ.update(self.original_env) |
| 35 | |
| 36 | @patch('pathlib.Path.exists') |
| 37 | @patch('tools.llm_api.load_dotenv') |
| 38 | @patch('builtins.open') |
| 39 | def test_environment_loading_precedence(self, mock_open, mock_load_dotenv, mock_exists): |
| 40 | # Mock all env files exist |
| 41 | mock_exists.return_value = True |
| 42 | |
| 43 | # Mock file contents |
| 44 | mock_file = MagicMock() |
| 45 | mock_file.__enter__.return_value = io.StringIO('TEST_VAR=value\n') |
| 46 | mock_open.return_value = mock_file |
| 47 | |
| 48 | # Mock different values for TEST_VAR in different files |
| 49 | def load_dotenv_side_effect(dotenv_path, **kwargs): |
| 50 | if '.env.local' in str(dotenv_path): |
| 51 | os.environ['TEST_VAR'] = 'local' |
| 52 | elif '.env' in str(dotenv_path): |
| 53 | if 'TEST_VAR' not in os.environ: # Only set if not already set |
| 54 | os.environ['TEST_VAR'] = 'default' |
| 55 | elif '.env.example' in str(dotenv_path): |
| 56 | if 'TEST_VAR' not in os.environ: # Only set if not already set |
| 57 | os.environ['TEST_VAR'] = 'example' |
| 58 | mock_load_dotenv.side_effect = load_dotenv_side_effect |
| 59 | |
| 60 | # Load environment |
| 61 | load_environment() |
| 62 | |
| 63 | # Verify precedence (.env.local should win) |
| 64 | self.assertEqual(os.environ.get('TEST_VAR'), 'local') |
| 65 | |
| 66 | # Verify order of loading |
| 67 | calls = mock_load_dotenv.call_args_list |
| 68 | self.assertEqual(len(calls), 3) |
| 69 | self.assertTrue(str(calls[0][1]['dotenv_path']).endswith('.env.local')) |
| 70 | self.assertTrue(str(calls[1][1]['dotenv_path']).endswith('.env')) |
| 71 | self.assertTrue(str(calls[2][1]['dotenv_path']).endswith('.env.example')) |
| 72 | |
| 73 | @patch('pathlib.Path.exists') |
| 74 | @patch('tools.llm_api.load_dotenv') |
| 75 | def test_environment_loading_no_files(self, mock_load_dotenv, mock_exists): |
| 76 | # Mock no env files exist |
| 77 | mock_exists.return_value = False |
| 78 | |
| 79 | # Load environment |
nothing calls this directly
no outgoing calls
no test coverage detected