feat: add workspace onboarding templates
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
from fastapi.testclient import TestClient
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.models.domain import Domain
|
||||
from app.models.mail_source import MailSource
|
||||
from app.models.setting import Setting
|
||||
from app.models.workspace import Workspace
|
||||
from app.models.workspace_access import WorkspaceAuditLog
|
||||
|
||||
|
||||
def _standard_payload(**overrides):
|
||||
payload = {
|
||||
"template_id": "standard_monitoring",
|
||||
"workspace": {
|
||||
"slug": "Client One",
|
||||
"name": "Client One",
|
||||
"description": "Managed client workspace",
|
||||
},
|
||||
"variables": {
|
||||
"domain": "Example.COM",
|
||||
"report_mailbox": "dmarc@example.com",
|
||||
"imap_server": "imap.example.com",
|
||||
"imap_password": "super-secret-password",
|
||||
},
|
||||
}
|
||||
payload.update(overrides)
|
||||
return payload
|
||||
|
||||
|
||||
def test_onboarding_templates_are_available(authed_client: TestClient):
|
||||
"""Operators can discover the versioned onboarding template bundle."""
|
||||
response = authed_client.get("/api/v1/onboarding/templates")
|
||||
|
||||
assert response.status_code == 200
|
||||
templates = response.json()["templates"]
|
||||
assert {template["id"] for template in templates} >= {
|
||||
"standard_monitoring",
|
||||
"dns_only_assessment",
|
||||
}
|
||||
standard = next(template for template in templates if template["id"] == "standard_monitoring")
|
||||
assert standard["domains"]
|
||||
assert standard["mail_sources"]
|
||||
assert standard["notification_defaults"]
|
||||
assert standard["checklist"]
|
||||
|
||||
|
||||
def test_onboarding_preview_renders_without_persisting(
|
||||
authed_client: TestClient,
|
||||
db_session: Session,
|
||||
):
|
||||
"""Preview renders a safe plan and leaves the database unchanged."""
|
||||
response = authed_client.post("/api/v1/onboarding/preview", json=_standard_payload())
|
||||
|
||||
assert response.status_code == 200
|
||||
plan = response.json()["plan"]
|
||||
assert plan["workspace"]["slug"] == "client-one"
|
||||
assert plan["domains"][0]["name"] == "example.com"
|
||||
assert plan["mail_sources"][0]["password"] == "[redacted]"
|
||||
assert "super-secret-password" not in str(plan)
|
||||
assert db_session.query(Workspace).count() == 0
|
||||
assert db_session.query(Domain).count() == 0
|
||||
|
||||
|
||||
def test_onboarding_rejects_invalid_domain(authed_client: TestClient):
|
||||
"""Invalid rendered domains fail before any apply attempt."""
|
||||
payload = _standard_payload(variables={"domain": "bad domain"})
|
||||
|
||||
response = authed_client.post("/api/v1/onboarding/preview", json=payload)
|
||||
|
||||
assert response.status_code == 422
|
||||
assert "invalid domain" in str(response.json()["detail"])
|
||||
|
||||
|
||||
def test_apply_onboarding_creates_workspace_assets_and_audit(
|
||||
authed_client: TestClient,
|
||||
db_session: Session,
|
||||
):
|
||||
"""Applying a template creates the workspace, assets, defaults, and audit event."""
|
||||
response = authed_client.post(
|
||||
"/api/v1/onboarding/apply",
|
||||
json=_standard_payload(),
|
||||
headers={"x-real-ip": "198.51.100.25"},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
result = response.json()["result"]
|
||||
assert result["applied"] is True
|
||||
assert result["workspace"]["slug"] == "client-one"
|
||||
assert result["results"]["domains"][0]["status"] == "created"
|
||||
assert result["results"]["mail_sources"][0]["status"] == "created"
|
||||
assert "super-secret-password" not in str(result)
|
||||
|
||||
workspace = db_session.query(Workspace).filter(Workspace.slug == "client-one").one()
|
||||
domain = db_session.query(Domain).filter(Domain.name == "example.com").one()
|
||||
source = db_session.query(MailSource).filter(MailSource.name == "Client One DMARC inbox").one()
|
||||
setting = (
|
||||
db_session.query(Setting)
|
||||
.filter(Setting.key == "notifications.alert_missing_reports_enabled")
|
||||
.one()
|
||||
)
|
||||
audit = (
|
||||
db_session.query(WorkspaceAuditLog)
|
||||
.filter(WorkspaceAuditLog.action == "workspace.onboarding_applied")
|
||||
.one()
|
||||
)
|
||||
|
||||
assert domain.workspace_id == workspace.id
|
||||
assert domain.dkim_selectors == "google,selector1"
|
||||
assert source.workspace_id == workspace.id
|
||||
assert source.server == "imap.example.com"
|
||||
assert source.enabled is False
|
||||
assert source.password == "super-secret-password"
|
||||
assert setting.value == "true"
|
||||
assert audit.workspace_id == workspace.id
|
||||
assert audit.ip_address == "198.51.100.25"
|
||||
assert "super-secret-password" not in (audit.details or "")
|
||||
|
||||
|
||||
def test_apply_onboarding_is_idempotent_for_existing_assets(
|
||||
authed_client: TestClient,
|
||||
db_session: Session,
|
||||
):
|
||||
"""Applying the same template again reports existing assets instead of duplicating them."""
|
||||
first = authed_client.post("/api/v1/onboarding/apply", json=_standard_payload())
|
||||
second = authed_client.post("/api/v1/onboarding/apply", json=_standard_payload())
|
||||
|
||||
assert first.status_code == 200
|
||||
assert second.status_code == 200
|
||||
result = second.json()["result"]
|
||||
assert result["workspace"]["status"] == "existing"
|
||||
assert result["results"]["domains"][0]["status"] == "existing"
|
||||
assert result["results"]["mail_sources"][0]["status"] == "existing"
|
||||
assert db_session.query(Workspace).count() == 1
|
||||
assert db_session.query(Domain).count() == 1
|
||||
assert db_session.query(MailSource).count() == 1
|
||||
|
||||
|
||||
def test_onboarding_notification_overwrite_is_explicit(
|
||||
authed_client: TestClient,
|
||||
db_session: Session,
|
||||
):
|
||||
"""Existing notification settings are preserved unless overwrite_existing is true."""
|
||||
db_session.add(
|
||||
Setting(
|
||||
key="notifications.summary_weekly_enabled",
|
||||
value="false",
|
||||
description="Existing setting",
|
||||
value_type="boolean",
|
||||
category="notifications",
|
||||
)
|
||||
)
|
||||
db_session.commit()
|
||||
|
||||
response = authed_client.post("/api/v1/onboarding/apply", json=_standard_payload())
|
||||
assert response.status_code == 200
|
||||
row = (
|
||||
db_session.query(Setting)
|
||||
.filter(Setting.key == "notifications.summary_weekly_enabled")
|
||||
.one()
|
||||
)
|
||||
assert row.value == "false"
|
||||
|
||||
overwrite_payload = _standard_payload(overwrite_existing=True)
|
||||
response = authed_client.post("/api/v1/onboarding/apply", json=overwrite_payload)
|
||||
assert response.status_code == 200
|
||||
db_session.refresh(row)
|
||||
assert row.value == "true"
|
||||
Reference in New Issue
Block a user