Files

229 lines
8.0 KiB
Markdown
Raw Permalink Normal View History

# 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.