Files
gatehouse-api/tests
nexgen_mirrors de6f39e7e3 feat(ssh): change SSH key uniqueness to per-user scope
Previously, SSH key fingerprints were globally unique across all users,
preventing the same key from being registered by different users. This
change makes fingerprint uniqueness scoped to individual users.

- Remove global unique constraints on payload and fingerprint columns
- Add composite unique constraint on (user_id, fingerprint)
- Make add_ssh_key operation idempotent for same user
- Return tuple (SSHKey, is_new) from service to indicate creation status
- Update API to return 200 for existing keys, 201 for new keys

BREAKING CHANGE: API behavior changed - duplicate key addition now
returns 200 OK instead of 409 Conflict. Service method signature changed
from returning SSHKey to tuple[SSHKey, bool].
2026-04-25 06:22:08 +09:30
..

Secuird Integration Test Suite

This directory contains the integration test suite for the Secuird IAM platform.

Quick Start

Run all integration tests:

cd backend
pytest tests/integration/

Run a specific test file:

pytest tests/integration/test_ssh_workflows.py -v

Run without coverage (faster):

pytest tests/integration/ --no-cov

Fail fast (stop on first failure):

pytest tests/integration/ -x

Run previously failed tests first:

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.

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:

-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:

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:

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

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

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.