|
4 | 4 | from typing import Optional |
5 | 5 | from unittest.mock import MagicMock, patch |
6 | 6 |
|
| 7 | +import pytest |
7 | 8 | from pyfakefs.fake_filesystem import FakeFilesystem |
8 | 9 |
|
9 | 10 | from cycode.cli.apps.ai_guardrails.scan.policy import ( |
10 | 11 | deep_merge, |
| 12 | + get_machine_policy_path, |
11 | 13 | get_policy_value, |
12 | 14 | load_defaults, |
13 | 15 | load_policy, |
@@ -197,3 +199,52 @@ def test_load_policy_none_workspace_root(mock_load: MagicMock) -> None: |
197 | 199 |
|
198 | 200 | # Should only load defaults (no repo config) |
199 | 201 | assert 'mode' in policy |
| 202 | + |
| 203 | + |
| 204 | +def test_get_machine_policy_path_per_os(monkeypatch: pytest.MonkeyPatch) -> None: |
| 205 | + """Test the per-OS machine policy locations.""" |
| 206 | + with patch('sys.platform', 'darwin'): |
| 207 | + assert get_machine_policy_path() == Path('/Library/Application Support/Cycode') / 'ai-guardrails.yaml' |
| 208 | + |
| 209 | + with patch('sys.platform', 'linux'): |
| 210 | + assert get_machine_policy_path() == Path('/etc/cycode') / 'ai-guardrails.yaml' |
| 211 | + |
| 212 | + with patch('sys.platform', 'win32'): |
| 213 | + monkeypatch.setenv('PROGRAMDATA', 'C:\\ProgramData') |
| 214 | + assert get_machine_policy_path() == Path('C:\\ProgramData') / 'Cycode' / 'ai-guardrails.yaml' |
| 215 | + |
| 216 | + |
| 217 | +@patch('pathlib.Path.home', return_value=Path('/home/testuser')) |
| 218 | +@patch('cycode.cli.apps.ai_guardrails.scan.policy.get_machine_policy_path') |
| 219 | +def test_load_policy_with_machine_config( |
| 220 | + mock_machine_path: MagicMock, mock_home: MagicMock, fs: FakeFilesystem |
| 221 | +) -> None: |
| 222 | + """Test that the machine-wide config overrides defaults.""" |
| 223 | + machine_path = Path('/machine/ai-guardrails.yaml') |
| 224 | + mock_machine_path.return_value = machine_path |
| 225 | + fs.create_file(str(machine_path), contents='mode: warn\n') |
| 226 | + |
| 227 | + policy = load_policy() |
| 228 | + |
| 229 | + # Machine config overrides the built-in default (block); other keys inherit from defaults. |
| 230 | + assert policy['mode'] == 'warn' |
| 231 | + assert policy['fail_open'] is True |
| 232 | + |
| 233 | + |
| 234 | +@patch('pathlib.Path.home', return_value=Path('/home/testuser')) |
| 235 | +@patch('cycode.cli.apps.ai_guardrails.scan.policy.get_machine_policy_path') |
| 236 | +def test_load_policy_precedence_defaults_machine_user_repo( |
| 237 | + mock_machine_path: MagicMock, mock_home: MagicMock, fs: FakeFilesystem |
| 238 | +) -> None: |
| 239 | + """Test full precedence: defaults < machine < user < repo.""" |
| 240 | + machine_path = Path('/machine/ai-guardrails.yaml') |
| 241 | + mock_machine_path.return_value = machine_path |
| 242 | + fs.create_file(str(machine_path), contents='mode: warn\nfail_open: false\n') |
| 243 | + fs.create_file('/home/testuser/.cycode/ai-guardrails.yaml', contents='fail_open: true\n') |
| 244 | + fs.create_file('/fake/repo/.cycode/ai-guardrails.yaml', contents='mode: block\n') |
| 245 | + |
| 246 | + policy = load_policy('/fake/repo') |
| 247 | + |
| 248 | + # repo overrides machine's mode; user overrides machine's fail_open. |
| 249 | + assert policy['mode'] == 'block' |
| 250 | + assert policy['fail_open'] is True |
0 commit comments