@@ -39,39 +39,42 @@ def _cap(
3939 )
4040
4141
42- engine = DefaultPolicyEngine ()
42+ @pytest .fixture ()
43+ def engine () -> DefaultPolicyEngine :
44+ """Fresh engine per test to avoid shared rate-limit state."""
45+ return DefaultPolicyEngine ()
4346
4447
4548# ── READ ───────────────────────────────────────────────────────────────────────
4649
4750
48- def test_read_allowed_no_roles () -> None :
51+ def test_read_allowed_no_roles (engine : DefaultPolicyEngine ) -> None :
4952 p = Principal (principal_id = "u1" )
5053 dec = engine .evaluate (_req ("cap.r" ), _cap ("cap.r" , SafetyClass .READ ), p , justification = "" )
5154 assert dec .allowed is True
5255
5356
54- def test_read_sets_max_rows_user () -> None :
57+ def test_read_sets_max_rows_user (engine : DefaultPolicyEngine ) -> None :
5558 p = Principal (principal_id = "u1" , roles = ["reader" ])
5659 dec = engine .evaluate (_req ("cap.r" ), _cap ("cap.r" , SafetyClass .READ ), p , justification = "" )
5760 assert dec .constraints ["max_rows" ] == 50
5861
5962
60- def test_read_sets_max_rows_service () -> None :
63+ def test_read_sets_max_rows_service (engine : DefaultPolicyEngine ) -> None :
6164 p = Principal (principal_id = "svc1" , roles = ["service" ])
6265 dec = engine .evaluate (_req ("cap.r" ), _cap ("cap.r" , SafetyClass .READ ), p , justification = "" )
6366 assert dec .constraints ["max_rows" ] == 500
6467
6568
66- def test_read_respects_tighter_constraint () -> None :
69+ def test_read_respects_tighter_constraint (engine : DefaultPolicyEngine ) -> None :
6770 p = Principal (principal_id = "u1" )
6871 dec = engine .evaluate (
6972 _req ("cap.r" , max_rows = 5 ), _cap ("cap.r" , SafetyClass .READ ), p , justification = ""
7073 )
7174 assert dec .constraints ["max_rows" ] == 5
7275
7376
74- def test_read_tighter_constraint_cannot_exceed_cap () -> None :
77+ def test_read_tighter_constraint_cannot_exceed_cap (engine : DefaultPolicyEngine ) -> None :
7578 p = Principal (principal_id = "u1" )
7679 dec = engine .evaluate (
7780 _req ("cap.r" , max_rows = 9999 ), _cap ("cap.r" , SafetyClass .READ ), p , justification = ""
@@ -82,7 +85,7 @@ def test_read_tighter_constraint_cannot_exceed_cap() -> None:
8285# ── WRITE ──────────────────────────────────────────────────────────────────────
8386
8487
85- def test_write_denied_no_role () -> None :
88+ def test_write_denied_no_role (engine : DefaultPolicyEngine ) -> None :
8689 p = Principal (principal_id = "u1" , roles = ["reader" ])
8790 with pytest .raises (PolicyDenied , match = "writer.*admin" ):
8891 engine .evaluate (
@@ -93,15 +96,15 @@ def test_write_denied_no_role() -> None:
9396 )
9497
9598
96- def test_write_denied_short_justification () -> None :
99+ def test_write_denied_short_justification (engine : DefaultPolicyEngine ) -> None :
97100 p = Principal (principal_id = "u1" , roles = ["writer" ])
98101 with pytest .raises (PolicyDenied , match = "justification" ):
99102 engine .evaluate (
100103 _req ("cap.w" ), _cap ("cap.w" , SafetyClass .WRITE ), p , justification = "too short"
101104 )
102105
103106
104- def test_write_denied_whitespace_justification () -> None :
107+ def test_write_denied_whitespace_justification (engine : DefaultPolicyEngine ) -> None :
105108 """Whitespace-only justification must not bypass the length requirement."""
106109 p = Principal (principal_id = "u1" , roles = ["writer" ])
107110 with pytest .raises (PolicyDenied , match = "justification" ):
@@ -110,7 +113,7 @@ def test_write_denied_whitespace_justification() -> None:
110113 )
111114
112115
113- def test_write_allowed_writer_role () -> None :
116+ def test_write_allowed_writer_role (engine : DefaultPolicyEngine ) -> None :
114117 p = Principal (principal_id = "u1" , roles = ["writer" ])
115118 dec = engine .evaluate (
116119 _req ("cap.w" ),
@@ -121,7 +124,7 @@ def test_write_allowed_writer_role() -> None:
121124 assert dec .allowed is True
122125
123126
124- def test_write_allowed_admin_role () -> None :
127+ def test_write_allowed_admin_role (engine : DefaultPolicyEngine ) -> None :
125128 p = Principal (principal_id = "u1" , roles = ["admin" ])
126129 dec = engine .evaluate (
127130 _req ("cap.w" ),
@@ -135,7 +138,7 @@ def test_write_allowed_admin_role() -> None:
135138# ── DESTRUCTIVE ────────────────────────────────────────────────────────────────
136139
137140
138- def test_destructive_denied_short_justification () -> None :
141+ def test_destructive_denied_short_justification (engine : DefaultPolicyEngine ) -> None :
139142 p = Principal (principal_id = "u1" , roles = ["admin" ])
140143 with pytest .raises (PolicyDenied , match = "DESTRUCTIVE capabilities require a justification" ):
141144 engine .evaluate (
@@ -146,7 +149,7 @@ def test_destructive_denied_short_justification() -> None:
146149 )
147150
148151
149- def test_destructive_denied_whitespace_justification () -> None :
152+ def test_destructive_denied_whitespace_justification (engine : DefaultPolicyEngine ) -> None :
150153 """Whitespace-only justification must not bypass the length requirement."""
151154 p = Principal (principal_id = "u1" , roles = ["admin" ])
152155 with pytest .raises (PolicyDenied , match = "justification" ):
@@ -158,7 +161,7 @@ def test_destructive_denied_whitespace_justification() -> None:
158161 )
159162
160163
161- def test_destructive_denied_no_admin () -> None :
164+ def test_destructive_denied_no_admin (engine : DefaultPolicyEngine ) -> None :
162165 p = Principal (principal_id = "u1" , roles = ["writer" ])
163166 with pytest .raises (PolicyDenied , match = "admin" ):
164167 engine .evaluate (
@@ -169,7 +172,7 @@ def test_destructive_denied_no_admin() -> None:
169172 )
170173
171174
172- def test_destructive_allowed_admin () -> None :
175+ def test_destructive_allowed_admin (engine : DefaultPolicyEngine ) -> None :
173176 p = Principal (principal_id = "u1" , roles = ["admin" ])
174177 dec = engine .evaluate (
175178 _req ("cap.d" ),
@@ -183,35 +186,35 @@ def test_destructive_allowed_admin() -> None:
183186# ── PII / PCI ──────────────────────────────────────────────────────────────────
184187
185188
186- def test_pii_requires_tenant () -> None :
189+ def test_pii_requires_tenant (engine : DefaultPolicyEngine ) -> None :
187190 p = Principal (principal_id = "u1" , roles = ["reader" ])
188191 cap = _cap ("cap.pii" , SafetyClass .READ , SensitivityTag .PII )
189192 with pytest .raises (PolicyDenied , match = "tenant" ):
190193 engine .evaluate (_req ("cap.pii" ), cap , p , justification = "" )
191194
192195
193- def test_pii_allowed_with_tenant () -> None :
196+ def test_pii_allowed_with_tenant (engine : DefaultPolicyEngine ) -> None :
194197 p = Principal (principal_id = "u1" , roles = ["reader" ], attributes = {"tenant" : "acme" })
195198 cap = _cap ("cap.pii" , SafetyClass .READ , SensitivityTag .PII )
196199 dec = engine .evaluate (_req ("cap.pii" ), cap , p , justification = "" )
197200 assert dec .allowed is True
198201
199202
200- def test_pii_enforces_allowed_fields () -> None :
203+ def test_pii_enforces_allowed_fields (engine : DefaultPolicyEngine ) -> None :
201204 p = Principal (principal_id = "u1" , roles = ["reader" ], attributes = {"tenant" : "acme" })
202205 cap = _cap ("cap.pii" , SafetyClass .READ , SensitivityTag .PII , allowed_fields = ["id" , "name" ])
203206 dec = engine .evaluate (_req ("cap.pii" ), cap , p , justification = "" )
204207 assert dec .constraints .get ("allowed_fields" ) == ["id" , "name" ]
205208
206209
207- def test_pii_reader_skips_allowed_fields () -> None :
210+ def test_pii_reader_skips_allowed_fields (engine : DefaultPolicyEngine ) -> None :
208211 p = Principal (principal_id = "u1" , roles = ["reader" , "pii_reader" ], attributes = {"tenant" : "acme" })
209212 cap = _cap ("cap.pii" , SafetyClass .READ , SensitivityTag .PII , allowed_fields = ["id" , "name" ])
210213 dec = engine .evaluate (_req ("cap.pii" ), cap , p , justification = "" )
211214 assert "allowed_fields" not in dec .constraints
212215
213216
214- def test_pci_requires_tenant () -> None :
217+ def test_pci_requires_tenant (engine : DefaultPolicyEngine ) -> None :
215218 p = Principal (principal_id = "u1" , roles = ["reader" ])
216219 cap = _cap ("cap.pci" , SafetyClass .READ , SensitivityTag .PCI )
217220 with pytest .raises (PolicyDenied , match = "tenant" ):
@@ -221,43 +224,43 @@ def test_pci_requires_tenant() -> None:
221224# ── SECRETS ────────────────────────────────────────────────────────────────────
222225
223226
224- def test_secrets_denied_no_role () -> None :
227+ def test_secrets_denied_no_role (engine : DefaultPolicyEngine ) -> None :
225228 p = Principal (principal_id = "u1" , roles = ["reader" ])
226229 cap = _cap ("cap.sec" , SafetyClass .READ , SensitivityTag .SECRETS )
227230 with pytest .raises (PolicyDenied , match = "secrets_reader" ):
228231 engine .evaluate (_req ("cap.sec" ), cap , p , justification = "long enough justification here" )
229232
230233
231- def test_secrets_denied_short_justification () -> None :
234+ def test_secrets_denied_short_justification (engine : DefaultPolicyEngine ) -> None :
232235 p = Principal (principal_id = "u1" , roles = ["secrets_reader" ])
233236 cap = _cap ("cap.sec" , SafetyClass .READ , SensitivityTag .SECRETS )
234237 with pytest .raises (PolicyDenied , match = "justification" ):
235238 engine .evaluate (_req ("cap.sec" ), cap , p , justification = "too short" )
236239
237240
238- def test_secrets_denied_whitespace_justification () -> None :
241+ def test_secrets_denied_whitespace_justification (engine : DefaultPolicyEngine ) -> None :
239242 """Whitespace-only justification must not bypass the length requirement."""
240243 p = Principal (principal_id = "u1" , roles = ["secrets_reader" ])
241244 cap = _cap ("cap.sec" , SafetyClass .READ , SensitivityTag .SECRETS )
242245 with pytest .raises (PolicyDenied , match = "justification" ):
243246 engine .evaluate (_req ("cap.sec" ), cap , p , justification = " " )
244247
245248
246- def test_secrets_allowed_secrets_reader_role () -> None :
249+ def test_secrets_allowed_secrets_reader_role (engine : DefaultPolicyEngine ) -> None :
247250 p = Principal (principal_id = "u1" , roles = ["secrets_reader" ])
248251 cap = _cap ("cap.sec" , SafetyClass .READ , SensitivityTag .SECRETS )
249252 dec = engine .evaluate (_req ("cap.sec" ), cap , p , justification = "long enough justification here" )
250253 assert dec .allowed is True
251254
252255
253- def test_secrets_allowed_admin_role () -> None :
256+ def test_secrets_allowed_admin_role (engine : DefaultPolicyEngine ) -> None :
254257 p = Principal (principal_id = "u1" , roles = ["admin" ])
255258 cap = _cap ("cap.sec" , SafetyClass .READ , SensitivityTag .SECRETS )
256259 dec = engine .evaluate (_req ("cap.sec" ), cap , p , justification = "long enough justification here" )
257260 assert dec .allowed is True
258261
259262
260- def test_secrets_denied_writer_role () -> None :
263+ def test_secrets_denied_writer_role (engine : DefaultPolicyEngine ) -> None :
261264 """Writer role is insufficient for SECRETS capabilities."""
262265 p = Principal (principal_id = "u1" , roles = ["writer" ])
263266 cap = _cap ("cap.sec" , SafetyClass .READ , SensitivityTag .SECRETS )
@@ -268,7 +271,7 @@ def test_secrets_denied_writer_role() -> None:
268271# ── Confused-deputy binding (via token) ────────────────────────────────────────
269272
270273
271- def test_max_rows_enforcement () -> None :
274+ def test_max_rows_enforcement (engine : DefaultPolicyEngine ) -> None :
272275 """max_rows in constraints is capped by the policy ceiling."""
273276 p = Principal (principal_id = "u1" )
274277 dec = engine .evaluate (
@@ -277,7 +280,7 @@ def test_max_rows_enforcement() -> None:
277280 assert dec .constraints ["max_rows" ] == 50
278281
279282
280- def test_max_rows_invalid_raises_policy_denied () -> None :
283+ def test_max_rows_invalid_raises_policy_denied (engine : DefaultPolicyEngine ) -> None :
281284 """Non-numeric max_rows raises PolicyDenied, not bare ValueError."""
282285 p = Principal (principal_id = "u1" )
283286 with pytest .raises (PolicyDenied , match = "Invalid 'max_rows'" ):
@@ -289,7 +292,7 @@ def test_max_rows_invalid_raises_policy_denied() -> None:
289292 )
290293
291294
292- def test_max_rows_negative_clamped_to_zero () -> None :
295+ def test_max_rows_negative_clamped_to_zero (engine : DefaultPolicyEngine ) -> None :
293296 """Negative max_rows is clamped to 0."""
294297 p = Principal (principal_id = "u1" )
295298 dec = engine .evaluate (
0 commit comments