test: add comprehensive integration test suite for IAM platform
Add 162 integration tests covering authentication flows, TOTP MFA, SSH key/certificate management, organization workflows, multi-org access, self-service features, admin operations, authorization, security edge cases, department/principal management, CA management, policy compliance, WebAuthn passkeys, and ZeroTier network access. Includes: - Reusable API client library with session management - Test fixtures for users, organizations, memberships, and CAs - Helper functions for SSH key generation and verification - Documentation for running and writing tests Also update test configuration to disable conflicting maas plugins and configure WebAuthn/session settings for localhost testing.
This commit is contained in:
+228
@@ -0,0 +1,228 @@
|
||||
# Secuird Integration Test Suite
|
||||
|
||||
This directory contains the integration test suite for the Secuird IAM platform.
|
||||
|
||||
## Quick Start
|
||||
|
||||
Run all integration tests:
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
pytest tests/integration/
|
||||
```
|
||||
|
||||
Run a specific test file:
|
||||
|
||||
```bash
|
||||
pytest tests/integration/test_ssh_workflows.py -v
|
||||
```
|
||||
|
||||
Run without coverage (faster):
|
||||
|
||||
```bash
|
||||
pytest tests/integration/ --no-cov
|
||||
```
|
||||
|
||||
Fail fast (stop on first failure):
|
||||
|
||||
```bash
|
||||
pytest tests/integration/ -x
|
||||
```
|
||||
|
||||
Run previously failed tests first:
|
||||
|
||||
```bash
|
||||
pytest tests/integration/ --ff
|
||||
```
|
||||
|
||||
## Test Structure
|
||||
|
||||
```
|
||||
tests/
|
||||
├── conftest.py # Base pytest fixtures (app, client, test_user)
|
||||
├── integration/ # Integration tests
|
||||
│ ├── conftest.py # Integration-specific fixtures and factories
|
||||
│ ├── client/ # Reusable API client library
|
||||
│ │ ├── base.py # SecuirdClient with session management
|
||||
│ │ ├── auth.py # Authentication operations
|
||||
│ │ ├── users.py # User self-service operations
|
||||
│ │ ├── orgs.py # Organization operations
|
||||
│ │ ├── ssh.py # SSH key/cert operations
|
||||
│ │ ├── mfa.py # TOTP/WebAuthn operations
|
||||
│ │ ├── zerotier.py # ZeroTier network operations
|
||||
│ │ └── admin.py # Admin operations
|
||||
│ ├── fixtures/ # Test data and helpers
|
||||
│ │ ├── ssh_keys.py # Test SSH key pairs and helpers
|
||||
│ │ └── test_data.py # Common test data generators
|
||||
│ ├── test_auth_flows.py # Authentication flows (24 tests)
|
||||
│ ├── test_totp_workflows.py # TOTP MFA flows (15 tests)
|
||||
│ ├── test_ssh_workflows.py # SSH key/cert flows (34 tests)
|
||||
│ ├── test_org_workflows.py # Organization & invite flows (27 tests)
|
||||
│ ├── test_multi_org.py # Multi-organization access (4 tests)
|
||||
│ ├── test_self_service.py # User self-service features (9 tests)
|
||||
│ ├── test_admin_ops.py # Admin user management (9 tests)
|
||||
│ ├── test_authorization.py # RBAC & access control (8 tests)
|
||||
│ ├── test_security.py # Security & edge cases (5 tests)
|
||||
│ ├── test_dept_principal.py # Department & principal management (5 tests)
|
||||
│ ├── test_ca_management.py # Certificate authority management (4 tests)
|
||||
│ ├── test_policy_compliance.py # Security policy & compliance (4 tests)
|
||||
│ ├── test_webauthn_workflows.py# WebAuthn passkey flows (5 tests)
|
||||
│ └── test_zerotier.py # ZeroTier network access (8 tests)
|
||||
└── unit/ # Unit tests (existing)
|
||||
```
|
||||
|
||||
## Environment
|
||||
|
||||
- **Python**: 3.10+
|
||||
- **Database**: SQLite in-memory (`sqlite:///:memory:`)
|
||||
- **Rate Limiting**: Disabled in tests (`RATELIMIT_ENABLED = False`)
|
||||
- **CSRF**: Disabled (`WTF_CSRF_ENABLED = False`)
|
||||
- **Email**: Suppressed (`MAIL_SUPPRESS_SEND = True`)
|
||||
|
||||
## Configuration
|
||||
|
||||
The `pytest.ini` file configures:
|
||||
|
||||
- Verbose output (`-v`)
|
||||
- Coverage reporting (`--cov=gatehouse_app`)
|
||||
- Disabled maas plugins that cause import errors (see Known Issues below)
|
||||
- Custom markers for `unit`, `integration`, `slow`, etc.
|
||||
|
||||
## Coverage
|
||||
|
||||
Coverage reports are generated automatically:
|
||||
|
||||
- **Terminal**: printed after each run
|
||||
- **HTML**: `backend/htmlcov/index.html`
|
||||
|
||||
Target coverage: **85% minimum**.
|
||||
|
||||
```bash
|
||||
pytest tests/integration/ --cov=gatehouse_app --cov-fail-under=85
|
||||
```
|
||||
|
||||
## Known Issues
|
||||
|
||||
### maastesting Plugin Import Error
|
||||
|
||||
The `maas` system package installs pytest entry points that fail to load in this environment. The `pytest.ini` file disables them automatically with:
|
||||
|
||||
```ini
|
||||
-p no:maas-django
|
||||
-p no:maas-perftest
|
||||
-p no:maas-seeds
|
||||
```
|
||||
|
||||
If you see `ModuleNotFoundError: No module named 'maastesting'`, these flags are not being applied. Ensure you run pytest from the `backend/` directory.
|
||||
|
||||
### ssh-keygen Not Available
|
||||
|
||||
One test (`test_verify_key_positive` in `test_ssh_workflows.py`) requires `ssh-keygen` to generate real Ed25519 key pairs for signature verification. It is automatically skipped when `ssh-keygen` is not available:
|
||||
|
||||
```bash
|
||||
sudo apt-get install openssh-client # Debian/Ubuntu
|
||||
```
|
||||
|
||||
Other certificate signing tests use a DB helper (`_mark_key_verified`) to bypass the signature requirement in CI environments.
|
||||
|
||||
## Writing New Tests
|
||||
|
||||
### Pattern
|
||||
|
||||
Every test must include a verbose docstring with `WHAT`, `WHY`, and `EXPECTED`:
|
||||
|
||||
```python
|
||||
def test_add_key_positive(self, integration_client, create_test_user):
|
||||
"""TEST: SSH-KEY-01 — Add a new SSH public key.
|
||||
|
||||
WHAT: Authenticated user POSTs a valid public key with a description.
|
||||
WHY: Users must be able to register their SSH keys for later
|
||||
certificate signing and server access.
|
||||
EXPECTED: 201 Created, response contains key id and metadata.
|
||||
"""
|
||||
```
|
||||
|
||||
### Fixtures
|
||||
|
||||
| Fixture | Purpose |
|
||||
|---------|---------|
|
||||
| `integration_client` | Fresh `SecuirdClient` instance per test |
|
||||
| `create_test_user` | Factory returning `{"id", "email", "password", "full_name"}` |
|
||||
| `create_test_org` | Factory returning `{"id", "name", "slug"}` |
|
||||
| `create_test_membership` | Links user to org with a role |
|
||||
| `create_test_ca` | Creates a Certificate Authority for an org |
|
||||
|
||||
### Client Usage
|
||||
|
||||
```python
|
||||
# Authentication
|
||||
integration_client.auth.register(email, password, full_name)
|
||||
integration_client.auth.login(email, password)
|
||||
integration_client.auth.logout()
|
||||
|
||||
# SSH
|
||||
integration_client.ssh.add_key(public_key, description)
|
||||
integration_client.ssh.sign_certificate(key_id=key_id, principals=["deploy"])
|
||||
integration_client.ssh.revoke_certificate(cert_id)
|
||||
|
||||
# Organizations
|
||||
integration_client.orgs.create(name, slug)
|
||||
integration_client.orgs.create_principal(org_id, name)
|
||||
integration_client.orgs.create_ca(org_id, name, ca_type="user")
|
||||
```
|
||||
|
||||
### Assertions
|
||||
|
||||
Use the standard helpers:
|
||||
|
||||
```python
|
||||
def assert_success(response: dict, message_contains: str = "") -> dict:
|
||||
data = response.get("data", {})
|
||||
assert response.get("success") is not False
|
||||
if message_contains:
|
||||
assert message_contains.lower() in response.get("message", "").lower()
|
||||
return data
|
||||
|
||||
# Negative tests
|
||||
with pytest.raises(ApiError) as exc_info:
|
||||
integration_client.ssh.get_key(str(uuid.uuid4()))
|
||||
assert exc_info.value.status_code == 404
|
||||
assert exc_info.value.error_type == "NOT_FOUND"
|
||||
```
|
||||
|
||||
## Test Counts
|
||||
|
||||
| Module | Tests | Focus |
|
||||
|--------|-------|-------|
|
||||
| test_auth_flows.py | 24 | Registration, login, logout, sessions, password reset, email verification |
|
||||
| test_totp_workflows.py | 15 | TOTP enrollment, verification, backup codes, disable, regenerate |
|
||||
| test_ssh_workflows.py | 34 | Key CRUD, verification, certificate signing & management |
|
||||
| test_org_workflows.py | 27 | Org CRUD, members, roles, invites, ownership transfer |
|
||||
| test_multi_org.py | 4 | Cross-org isolation, role-based access |
|
||||
| test_self_service.py | 9 | Profile, password change, account deletion |
|
||||
| test_admin_ops.py | 9 | Suspend, unsuspend, verify email, set password, remove MFA, hard delete |
|
||||
| test_authorization.py | 8 | RBAC, cross-user isolation, soft-delete behavior |
|
||||
| test_security.py | 5 | SQL injection, XSS, oversized payload, malformed JSON, empty body |
|
||||
| test_dept_principal.py | 5 | Department/principal CRUD, membership, linking |
|
||||
| test_ca_management.py | 4 | CA creation, listing, rotation |
|
||||
| test_policy_compliance.py | 4 | Security policy, MFA compliance |
|
||||
| test_webauthn_workflows.py | 5 | WebAuthn registration/login (mocked) |
|
||||
| test_zerotier.py | 8 | Network CRUD, devices, approvals, memberships (mocked) |
|
||||
| **Total** | **162** | |
|
||||
|
||||
## Pre-Commit Checklist
|
||||
|
||||
Before committing backend changes:
|
||||
|
||||
1. Run the integration suite: `pytest tests/integration/ -x`
|
||||
2. Verify coverage hasn't decreased: `pytest tests/integration/ --cov=gatehouse_app --cov-fail-under=85`
|
||||
3. If tests fail, fix before committing
|
||||
|
||||
## CI/CD
|
||||
|
||||
Integration tests run automatically on:
|
||||
- Every pull request
|
||||
- Every push to main
|
||||
- Nightly builds
|
||||
|
||||
**Failure policy**: Integration test failures block merging.
|
||||
Reference in New Issue
Block a user