Test coverage: plugin lifecycle, registry, lockout unlock, contract fleet, seed

Fills the review's highest-value coverage gaps in the framework's own core
feature.

New tests/test_plugin_lifecycle.py: manager enable/disable dependency guards
(beta depends on alpha - enable-beta-first refused, disable-alpha-while-beta-on
refused) via synthetic plugins; enable seeds a plugin's RBAC permissions
idempotently; registry disable-survives-reload, corrupt-file recovery, and the
equipment->machines rename migration; `flask seed settings` idempotency.

test_plugin_contract.py: BUNDLED_PLUGINS now covers all bundled plugins incl.
geenforce, measuringtools, warranty (was 9, contradicting the CLAUDE.md "all
bundled satisfy the contract" claim); the structural checks now run against them.

test_authz.py: account lockout auto-unlock path (expired lockeduntil -> correct
password logs in and clears the lock state), previously untested.

All new tests pass; naming green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-07-13 08:41:23 -04:00
parent d49baeb5fa
commit ea3cca8954
3 changed files with 185 additions and 1 deletions

View File

@@ -203,6 +203,25 @@ def test_account_locks_after_repeated_bad_logins(client, db, admin_user):
assert 'locked' in locked.get_json()['data']['error']['message'].lower()
def test_lockout_auto_unlocks_after_expiry(client, db, admin_user):
"""Once lockeduntil is in the past the account unlocks itself: a correct
password logs in and clears the lock state."""
from datetime import datetime, timezone, timedelta
# Simulate an expired lock (naive-UTC to match the column comparisons).
admin_user.lockeduntil = (datetime.now(timezone.utc).replace(tzinfo=None)
- timedelta(minutes=1))
admin_user.failedlogins = 5
db.session.commit()
ok = client.post('/api/auth/login',
json={'username': 'testadmin', 'password': 'testpass'})
assert ok.status_code == 200, ok.get_json()
db.session.refresh(admin_user)
assert admin_user.lockeduntil is None
assert admin_user.failedlogins == 0
def test_successful_login_resets_failed_counter(client, db, admin_user):
"""A good login before the threshold clears the failure count."""
for _ in range(3):