015c622016
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.
155 lines
5.1 KiB
Python
155 lines
5.1 KiB
Python
"""Pytest fixtures for integration tests."""
|
|
import pytest
|
|
import uuid
|
|
from datetime import datetime, timezone
|
|
|
|
from gatehouse_app import create_app, db
|
|
from gatehouse_app.extensions import limiter
|
|
from gatehouse_app.models.user.user import User
|
|
from gatehouse_app.models.organization.organization import Organization
|
|
from gatehouse_app.models.organization.organization_member import OrganizationMember
|
|
from gatehouse_app.models.ssh_ca.ca import CA, CaType, KeyType
|
|
from gatehouse_app.utils.constants import OrganizationRole
|
|
from tests.integration.client.base import SecuirdClient
|
|
|
|
|
|
# Disable the global rate limiter for integration tests.
|
|
# The default app created at module level in gatehouse_app/__init__.py
|
|
# initializes the limiter with production settings; we turn it off here
|
|
# so tests don't hit rate limits.
|
|
limiter.enabled = False
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def integration_app():
|
|
"""Create a test Flask app with in-memory SQLite.
|
|
|
|
Yields the configured application; tears down the DB after the
|
|
module finishes.
|
|
"""
|
|
app = create_app(config_name="testing")
|
|
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///:memory:"
|
|
app.config["TESTING"] = True
|
|
app.config["WTF_CSRF_ENABLED"] = False
|
|
app.config["RATELIMIT_ENABLED"] = False
|
|
|
|
with app.app_context():
|
|
db.create_all()
|
|
yield app
|
|
db.session.remove()
|
|
db.drop_all()
|
|
|
|
|
|
@pytest.fixture
|
|
def integration_client(integration_app):
|
|
"""Yield a fresh SecuirdClient for every test function."""
|
|
with integration_app.test_client() as flask_client:
|
|
client = SecuirdClient(flask_client)
|
|
yield client
|
|
client.clear_token()
|
|
|
|
|
|
@pytest.fixture
|
|
def create_test_user(integration_app):
|
|
"""Return a factory that creates a user inside the app context."""
|
|
from gatehouse_app.models.auth.authentication_method import AuthenticationMethod
|
|
from gatehouse_app.utils.constants import AuthMethodType
|
|
|
|
def _factory(
|
|
*,
|
|
email: str | None = None,
|
|
password: str = "password123",
|
|
full_name: str = "Test User",
|
|
email_verified: bool = True,
|
|
) -> dict:
|
|
email = email or f"test_{uuid.uuid4().hex[:8]}@example.com"
|
|
with integration_app.app_context():
|
|
user = User(
|
|
email=email,
|
|
full_name=full_name,
|
|
email_verified=email_verified,
|
|
)
|
|
db.session.add(user)
|
|
db.session.commit()
|
|
|
|
from gatehouse_app.extensions import bcrypt
|
|
password_hash = bcrypt.generate_password_hash(password).decode("utf-8")
|
|
auth_method = AuthenticationMethod(
|
|
user_id=user.id,
|
|
method_type=AuthMethodType.PASSWORD,
|
|
password_hash=password_hash,
|
|
is_primary=True,
|
|
verified=True,
|
|
)
|
|
db.session.add(auth_method)
|
|
db.session.commit()
|
|
|
|
return {
|
|
"id": str(user.id),
|
|
"email": user.email,
|
|
"password": password,
|
|
"full_name": user.full_name,
|
|
}
|
|
|
|
return _factory
|
|
|
|
|
|
@pytest.fixture
|
|
def create_test_org(integration_app):
|
|
"""Return a factory that creates an organization inside the app context."""
|
|
def _factory(*, name: str | None = None, slug: str | None = None) -> dict:
|
|
name = name or f"Test Org {uuid.uuid4().hex[:8]}"
|
|
slug = slug or name.lower().replace(" ", "-")
|
|
with integration_app.app_context():
|
|
org = Organization(name=name, slug=slug)
|
|
db.session.add(org)
|
|
db.session.commit()
|
|
return {"id": str(org.id), "name": org.name, "slug": org.slug}
|
|
|
|
return _factory
|
|
|
|
|
|
@pytest.fixture
|
|
def create_test_membership(integration_app):
|
|
"""Return a factory that creates an org membership."""
|
|
def _factory(user_id: str, org_id: str, role: OrganizationRole = OrganizationRole.MEMBER) -> dict:
|
|
with integration_app.app_context():
|
|
membership = OrganizationMember(
|
|
user_id=user_id,
|
|
organization_id=org_id,
|
|
role=role,
|
|
)
|
|
db.session.add(membership)
|
|
db.session.commit()
|
|
return {"id": str(membership.id), "role": role.value}
|
|
|
|
return _factory
|
|
|
|
|
|
@pytest.fixture
|
|
def create_test_ca(integration_app):
|
|
"""Return a factory that creates a Certificate Authority."""
|
|
def _factory(
|
|
*,
|
|
org_id: str,
|
|
name: str = "Test CA",
|
|
ca_type: CaType = CaType.USER,
|
|
key_type: KeyType = KeyType.ED25519,
|
|
) -> dict:
|
|
with integration_app.app_context():
|
|
ca = CA(
|
|
organization_id=org_id,
|
|
name=name,
|
|
ca_type=ca_type,
|
|
key_type=key_type,
|
|
private_key="encrypted_private_key_placeholder",
|
|
public_key="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI...",
|
|
fingerprint="sha256:ABC123...",
|
|
is_active=True,
|
|
)
|
|
db.session.add(ca)
|
|
db.session.commit()
|
|
return {"id": str(ca.id), "name": ca.name, "ca_type": ca.ca_type.value}
|
|
|
|
return _factory
|