Skip to content

Commit 32ee772

Browse files
author
Diogo Andre Passagem Santos
committed
fix: replace shared engine with pytest fixture for test isolation
1 parent fa0a040 commit 32ee772

1 file changed

Lines changed: 32 additions & 29 deletions

File tree

tests/test_policy.py

Lines changed: 32 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)