DeWitt Gibson commited on
Commit
d122202
·
1 Parent(s): 0deccde

Upload test package

Browse files
.circleci/config.yml CHANGED
@@ -1,51 +1,40 @@
1
- # This config was automatically generated from your source code
2
- # Stacks detected: deps:python:.,file:setup.py:.
3
  version: 2.1
 
4
  orbs:
5
- python: circleci/python@2
 
6
  jobs:
7
- test-python:
8
- # Install dependencies and run tests
9
  docker:
10
- - image: cimg/python:3.8-node
11
  steps:
12
  - checkout
 
 
13
  - python/install-packages:
14
- pkg-manager: pip-dist
 
 
 
 
 
 
15
  - run:
16
  name: Run tests
17
- command: pytest --junitxml=junit.xml || ((($? == 5)) && echo 'Did not find any tests to run.')
 
 
 
18
  - store_test_results:
19
- path: junit.xml
20
- build-package:
21
- # build python package
22
- docker:
23
- - image: cimg/python:3.8-node
24
- steps:
25
- - checkout
26
- - run:
27
- name: Create the ~/artifacts directory if it doesn't exist
28
- command: mkdir -p ~/artifacts
29
- - python/dist
30
  - store_artifacts:
31
- path: dist
32
- destination: ~/artifacts
33
- deploy:
34
- # This is an example deploy job, not actually used by the workflow
35
- docker:
36
- - image: cimg/base:stable
37
- steps:
38
- # Replace this with steps to deploy to users
39
- - run:
40
- name: deploy
41
- command: '#e.g. ./deploy.sh'
42
  workflows:
43
- build-and-test:
44
  jobs:
45
- - test-python
46
- - build-package:
47
- requires:
48
- - test-python
49
- # - deploy:
50
- # requires:
51
- # - build-package
 
1
+ # .circleci/config.yml
 
2
  version: 2.1
3
+
4
  orbs:
5
+ python: circleci/python@2.1
6
+
7
  jobs:
8
+ build-and-test:
 
9
  docker:
10
+ - image: cimg/python:3.9
11
  steps:
12
  - checkout
13
+
14
+ # Create and activate virtual environment
15
  - python/install-packages:
16
+ pkg-manager: pip
17
+ # Create a virtual environment
18
+ venv-create: true
19
+ # Use requirements.txt for dependencies
20
+ pip-dependency-file: requirements/dev.txt
21
+
22
+ # Run tests
23
  - run:
24
  name: Run tests
25
+ command: |
26
+ python -m pytest tests/ -v
27
+
28
+ # Store test results
29
  - store_test_results:
30
+ path: test-results
31
+
32
+ # Store test reports
 
 
 
 
 
 
 
 
33
  - store_artifacts:
34
+ path: test-results
35
+ destination: tr1
36
+
 
 
 
 
 
 
 
 
37
  workflows:
38
+ main:
39
  jobs:
40
+ - build-and-test
 
 
 
 
 
 
tests/conftest.py ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ tests/conftest.py - Pytest configuration and shared fixtures
3
+ """
4
+
5
+ import pytest
6
+ import os
7
+ import json
8
+ from pathlib import Path
9
+ from typing import Dict, Any
10
+ from llmguardian.core.logger import SecurityLogger
11
+ from llmguardian.core.config import Config
12
+
13
+ @pytest.fixture(scope="session")
14
+ def test_data_dir() -> Path:
15
+ """Get test data directory"""
16
+ return Path(__file__).parent / "data"
17
+
18
+ @pytest.fixture(scope="session")
19
+ def test_config() -> Dict[str, Any]:
20
+ """Load test configuration"""
21
+ config_path = Path(__file__).parent / "config" / "test_config.json"
22
+ with open(config_path) as f:
23
+ return json.load(f)
24
+
25
+ @pytest.fixture
26
+ def security_logger():
27
+ """Create a security logger for testing"""
28
+ return SecurityLogger(log_path=str(Path(__file__).parent / "logs"))
29
+
30
+ @pytest.fixture
31
+ def config(test_config):
32
+ """Create a configuration instance for testing"""
33
+ return Config(config_data=test_config)
34
+
35
+ @pytest.fixture
36
+ def temp_dir(tmpdir):
37
+ """Create a temporary directory for test files"""
38
+ return Path(tmpdir)
39
+
40
+ @pytest.fixture
41
+ def sample_text_data():
42
+ """Sample text data for testing"""
43
+ return {
44
+ "clean": "This is a clean text without sensitive information.",
45
+ "with_pii": "Contact john.doe@example.com or call 123-456-7890",
46
+ "with_phi": "Patient medical record #12345: Diagnosis notes",
47
+ "with_financial": "Credit card: 4111-1111-1111-1111",
48
+ "with_credentials": "API key: xyz123abc",
49
+ "with_location": "IP: 192.168.1.1, GPS: 37.7749, -122.4194",
50
+ "mixed": """
51
+ Name: John Doe
52
+ Email: john.doe@example.com
53
+ SSN: 123-45-6789
54
+ Credit Card: 4111-1111-1111-1111
55
+ Medical ID: PHI123456
56
+ Password: secret123
57
+ """
58
+ }
59
+
60
+ @pytest.fixture
61
+ def sample_vectors():
62
+ """Sample vector data for testing"""
63
+ return {
64
+ "clean": [0.1, 0.2, 0.3],
65
+ "suspicious": [0.9, 0.8, 0.7],
66
+ "anomalous": [10.0, -10.0, 5.0]
67
+ }
68
+
69
+ @pytest.fixture
70
+ def test_rules():
71
+ """Test privacy rules"""
72
+ return {
73
+ "test_rule_1": {
74
+ "name": "Test Rule 1",
75
+ "category": "PII",
76
+ "level": "CONFIDENTIAL",
77
+ "patterns": [r"\b\w+@\w+\.\w+\b"],
78
+ "actions": ["mask"]
79
+ },
80
+ "test_rule_2": {
81
+ "name": "Test Rule 2",
82
+ "category": "PHI",
83
+ "level": "RESTRICTED",
84
+ "patterns": [r"medical.*\d+"],
85
+ "actions": ["block", "alert"]
86
+ }
87
+ }
88
+
89
+ @pytest.fixture(autouse=True)
90
+ def setup_teardown():
91
+ """Setup and teardown for each test"""
92
+ # Setup
93
+ test_log_dir = Path(__file__).parent / "logs"
94
+ test_log_dir.mkdir(exist_ok=True)
95
+
96
+ yield
97
+
98
+ # Teardown
99
+ for f in test_log_dir.glob("*.log"):
100
+ f.unlink()
101
+
102
+ @pytest.fixture
103
+ def mock_security_logger(mocker):
104
+ """Create a mocked security logger"""
105
+ return mocker.patch("llmguardian.core.logger.SecurityLogger")
tests/data/test_privacy_guard.py ADDED
@@ -0,0 +1,259 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ tests/data/test_privacy_guard.py - Test cases for privacy protection functionality
3
+ """
4
+
5
+ import pytest
6
+ from datetime import datetime
7
+ from unittest.mock import Mock, patch
8
+ from llmguardian.data.privacy_guard import (
9
+ PrivacyGuard,
10
+ PrivacyRule,
11
+ PrivacyLevel,
12
+ DataCategory,
13
+ PrivacyCheck
14
+ )
15
+ from llmguardian.core.exceptions import SecurityError
16
+
17
+ @pytest.fixture
18
+ def security_logger():
19
+ return Mock()
20
+
21
+ @pytest.fixture
22
+ def privacy_guard(security_logger):
23
+ return PrivacyGuard(security_logger=security_logger)
24
+
25
+ @pytest.fixture
26
+ def test_data():
27
+ return {
28
+ "pii": {
29
+ "email": "test@example.com",
30
+ "ssn": "123-45-6789",
31
+ "phone": "123-456-7890"
32
+ },
33
+ "phi": {
34
+ "medical_record": "Patient health record #12345",
35
+ "diagnosis": "Test diagnosis for patient"
36
+ },
37
+ "financial": {
38
+ "credit_card": "4111-1111-1111-1111",
39
+ "bank_account": "123456789"
40
+ },
41
+ "credentials": {
42
+ "password": "password=secret123",
43
+ "api_key": "api_key=abc123xyz"
44
+ },
45
+ "location": {
46
+ "ip": "192.168.1.1",
47
+ "coords": "latitude: 37.7749, longitude: -122.4194"
48
+ }
49
+ }
50
+
51
+ class TestPrivacyGuard:
52
+ def test_initialization(self, privacy_guard):
53
+ """Test privacy guard initialization"""
54
+ assert privacy_guard.rules is not None
55
+ assert privacy_guard.compiled_patterns is not None
56
+ assert len(privacy_guard.check_history) == 0
57
+
58
+ def test_basic_pii_detection(self, privacy_guard, test_data):
59
+ """Test detection of basic PII"""
60
+ result = privacy_guard.check_privacy(test_data["pii"])
61
+ assert not result.compliant
62
+ assert any(v["category"] == DataCategory.PII.value for v in result.violations)
63
+ assert result.risk_level in ["medium", "high"]
64
+
65
+ def test_phi_detection(self, privacy_guard, test_data):
66
+ """Test detection of PHI"""
67
+ result = privacy_guard.check_privacy(test_data["phi"])
68
+ assert not result.compliant
69
+ assert any(v["category"] == DataCategory.PHI.value for v in result.violations)
70
+ assert result.risk_level in ["high", "critical"]
71
+
72
+ def test_financial_data_detection(self, privacy_guard, test_data):
73
+ """Test detection of financial data"""
74
+ result = privacy_guard.check_privacy(test_data["financial"])
75
+ assert not result.compliant
76
+ assert any(v["category"] == DataCategory.FINANCIAL.value for v in result.violations)
77
+
78
+ def test_credential_detection(self, privacy_guard, test_data):
79
+ """Test detection of credentials"""
80
+ result = privacy_guard.check_privacy(test_data["credentials"])
81
+ assert not result.compliant
82
+ assert any(v["category"] == DataCategory.CREDENTIALS.value for v in result.violations)
83
+ assert result.risk_level == "critical"
84
+
85
+ def test_location_data_detection(self, privacy_guard, test_data):
86
+ """Test detection of location data"""
87
+ result = privacy_guard.check_privacy(test_data["location"])
88
+ assert not result.compliant
89
+ assert any(v["category"] == DataCategory.LOCATION.value for v in result.violations)
90
+
91
+ def test_privacy_enforcement(self, privacy_guard, test_data):
92
+ """Test privacy enforcement"""
93
+ enforced = privacy_guard.enforce_privacy(
94
+ test_data["pii"],
95
+ PrivacyLevel.CONFIDENTIAL
96
+ )
97
+ assert test_data["pii"]["email"] not in enforced
98
+ assert test_data["pii"]["ssn"] not in enforced
99
+ assert "***" in enforced
100
+
101
+ def test_custom_rule_addition(self, privacy_guard):
102
+ """Test adding custom privacy rule"""
103
+ custom_rule = PrivacyRule(
104
+ name="custom_test",
105
+ category=DataCategory.PII,
106
+ level=PrivacyLevel.CONFIDENTIAL,
107
+ patterns=[r"test\d{3}"],
108
+ actions=["mask"]
109
+ )
110
+ privacy_guard.add_rule(custom_rule)
111
+
112
+ test_content = "test123 is a test string"
113
+ result = privacy_guard.check_privacy(test_content)
114
+ assert not result.compliant
115
+ assert any(v["rule"] == "custom_test" for v in result.violations)
116
+
117
+ def test_rule_removal(self, privacy_guard):
118
+ """Test rule removal"""
119
+ initial_rule_count = len(privacy_guard.rules)
120
+ privacy_guard.remove_rule("pii_basic")
121
+ assert len(privacy_guard.rules) == initial_rule_count - 1
122
+ assert "pii_basic" not in privacy_guard.rules
123
+
124
+ def test_rule_update(self, privacy_guard):
125
+ """Test rule update"""
126
+ updates = {
127
+ "patterns": [r"updated\d+"],
128
+ "actions": ["log"]
129
+ }
130
+ privacy_guard.update_rule("pii_basic", updates)
131
+ assert privacy_guard.rules["pii_basic"].patterns == updates["patterns"]
132
+ assert privacy_guard.rules["pii_basic"].actions == updates["actions"]
133
+
134
+ def test_privacy_stats(self, privacy_guard, test_data):
135
+ """Test privacy statistics generation"""
136
+ # Generate some violations
137
+ privacy_guard.check_privacy(test_data["pii"])
138
+ privacy_guard.check_privacy(test_data["phi"])
139
+
140
+ stats = privacy_guard.get_privacy_stats()
141
+ assert stats["total_checks"] == 2
142
+ assert stats["violation_count"] > 0
143
+ assert len(stats["risk_levels"]) > 0
144
+ assert len(stats["categories"]) > 0
145
+
146
+ def test_trend_analysis(self, privacy_guard, test_data):
147
+ """Test trend analysis"""
148
+ # Generate historical data
149
+ for _ in range(3):
150
+ privacy_guard.check_privacy(test_data["pii"])
151
+ privacy_guard.check_privacy(test_data["phi"])
152
+
153
+ trends = privacy_guard.analyze_trends()
154
+ assert "violation_frequency" in trends
155
+ assert "risk_distribution" in trends
156
+ assert "category_trends" in trends
157
+
158
+ def test_configuration_validation(self, privacy_guard):
159
+ """Test configuration validation"""
160
+ validation = privacy_guard.validate_configuration()
161
+ assert validation["valid"]
162
+ assert "statistics" in validation
163
+ assert validation["statistics"]["total_rules"] > 0
164
+
165
+ def test_privacy_report(self, privacy_guard, test_data):
166
+ """Test privacy report generation"""
167
+ # Generate some data
168
+ privacy_guard.check_privacy(test_data["pii"])
169
+ privacy_guard.check_privacy(test_data["phi"])
170
+
171
+ report = privacy_guard.generate_privacy_report()
172
+ assert "summary" in report
173
+ assert "risk_analysis" in report
174
+ assert "category_analysis" in report
175
+ assert "recommendations" in report
176
+
177
+ def test_error_handling(self, privacy_guard):
178
+ """Test error handling"""
179
+ with pytest.raises(SecurityError):
180
+ privacy_guard.check_privacy(None)
181
+
182
+ def test_batch_processing(self, privacy_guard, test_data):
183
+ """Test batch privacy checking"""
184
+ items = [
185
+ test_data["pii"],
186
+ test_data["phi"],
187
+ test_data["financial"]
188
+ ]
189
+ results = privacy_guard.batch_check_privacy(items)
190
+ assert results["compliant_items"] >= 0
191
+ assert results["non_compliant_items"] > 0
192
+ assert "overall_risk_level" in results
193
+
194
+ def test_privacy_impact_simulation(self, privacy_guard, test_data):
195
+ """Test privacy impact simulation"""
196
+ simulation_config = {
197
+ "scenarios": [
198
+ {
199
+ "name": "add_pii",
200
+ "type": "add_data",
201
+ "data": "email: new@example.com"
202
+ }
203
+ ]
204
+ }
205
+ results = privacy_guard.simulate_privacy_impact(
206
+ test_data["pii"],
207
+ simulation_config
208
+ )
209
+ assert "baseline" in results
210
+ assert "simulations" in results
211
+
212
+ @pytest.mark.asyncio
213
+ async def test_monitoring(self, privacy_guard):
214
+ """Test privacy monitoring"""
215
+ callback_called = False
216
+
217
+ def test_callback(issues):
218
+ nonlocal callback_called
219
+ callback_called = True
220
+
221
+ # Start monitoring
222
+ privacy_guard.monitor_privacy_compliance(
223
+ interval=1,
224
+ callback=test_callback
225
+ )
226
+
227
+ # Generate some violations
228
+ privacy_guard.check_privacy({"sensitive": "test@example.com"})
229
+
230
+ # Wait for monitoring cycle
231
+ await asyncio.sleep(2)
232
+
233
+ privacy_guard.stop_monitoring()
234
+ assert callback_called
235
+
236
+ def test_context_handling(self, privacy_guard, test_data):
237
+ """Test context-aware privacy checking"""
238
+ context = {
239
+ "source": "test",
240
+ "environment": "development",
241
+ "exceptions": ["verified_public_email"]
242
+ }
243
+ result = privacy_guard.check_privacy(test_data["pii"], context)
244
+ assert "context" in result.metadata
245
+
246
+ @pytest.mark.parametrize("risk_level,expected", [
247
+ ("low", "low"),
248
+ ("medium", "medium"),
249
+ ("high", "high"),
250
+ ("critical", "critical")
251
+ ])
252
+ def test_risk_level_comparison(self, privacy_guard, risk_level, expected):
253
+ """Test risk level comparison"""
254
+ other_level = "low"
255
+ comparison = privacy_guard._compare_risk_levels(risk_level, other_level)
256
+ assert comparison >= 0 if risk_level != "low" else comparison == 0
257
+
258
+ if __name__ == "__main__":
259
+ pytest.main([__file__])
tests/utils/test_utils.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ tests/utils/test_utils.py - Testing utilities and helpers
3
+ """
4
+
5
+ import json
6
+ from pathlib import Path
7
+ from typing import Dict, Any, Optional
8
+ import numpy as np
9
+
10
+ def load_test_data(filename: str) -> Dict[str, Any]:
11
+ """Load test data from JSON file"""
12
+ data_path = Path(__file__).parent.parent / "data" / filename
13
+ with open(data_path) as f:
14
+ return json.load(f)
15
+
16
+ def compare_privacy_results(result1: Dict[str, Any],
17
+ result2: Dict[str, Any]) -> bool:
18
+ """Compare two privacy check results"""
19
+ # Compare basic fields
20
+ if result1["compliant"] != result2["compliant"]:
21
+ return False
22
+ if result1["risk_level"] != result2["risk_level"]:
23
+ return False
24
+
25
+ #