feat: initial public release
ConsentOS — a privacy-first cookie consent management platform. Self-hosted, source-available alternative to OneTrust, Cookiebot, and CookieYes. Full standards coverage (IAB TCF v2.2, GPP v1, Google Consent Mode v2, GPC, Shopify Customer Privacy API), multi-tenant architecture with role-based access, configuration cascade (system → org → group → site → region), dark-pattern detection in the scanner, and a tamper-evident consent record audit trail. This is the initial public release. Prior development history is retained internally. See README.md for the feature list, architecture overview, and quick-start instructions. Licensed under the Elastic Licence 2.0 — self-host freely; do not resell as a managed service.
This commit is contained in:
130
apps/api/tests/test_consent.py
Normal file
130
apps/api/tests/test_consent.py
Normal file
@@ -0,0 +1,130 @@
|
||||
"""Tests for consent recording API schemas and routes."""
|
||||
|
||||
import uuid
|
||||
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from src.schemas.consent import (
|
||||
ConsentAction,
|
||||
ConsentRecordCreate,
|
||||
ConsentRecordResponse,
|
||||
ConsentVerifyResponse,
|
||||
)
|
||||
|
||||
|
||||
class TestConsentSchemas:
|
||||
def test_create_accept_all(self):
|
||||
record = ConsentRecordCreate(
|
||||
site_id=uuid.uuid4(),
|
||||
visitor_id="visitor-abc-123",
|
||||
action=ConsentAction.ACCEPT_ALL,
|
||||
categories_accepted=["necessary", "analytics", "marketing"],
|
||||
)
|
||||
assert record.action == "accept_all"
|
||||
assert len(record.categories_accepted) == 3
|
||||
assert record.categories_rejected is None
|
||||
|
||||
def test_create_custom(self):
|
||||
record = ConsentRecordCreate(
|
||||
site_id=uuid.uuid4(),
|
||||
visitor_id="visitor-xyz",
|
||||
action=ConsentAction.CUSTOM,
|
||||
categories_accepted=["necessary", "functional"],
|
||||
categories_rejected=["analytics", "marketing"],
|
||||
tc_string="COwQHgAAAAA",
|
||||
gcm_state={"analytics_storage": "denied", "ad_storage": "denied"},
|
||||
page_url="https://example.com/page",
|
||||
country_code="GB",
|
||||
region_code="GB-ENG",
|
||||
)
|
||||
assert record.action == "custom"
|
||||
assert record.tc_string == "COwQHgAAAAA"
|
||||
assert record.gcm_state["analytics_storage"] == "denied"
|
||||
|
||||
def test_create_reject_all(self):
|
||||
record = ConsentRecordCreate(
|
||||
site_id=uuid.uuid4(),
|
||||
visitor_id="v-1",
|
||||
action=ConsentAction.REJECT_ALL,
|
||||
categories_accepted=["necessary"],
|
||||
categories_rejected=["analytics", "marketing", "functional"],
|
||||
)
|
||||
assert record.action == "reject_all"
|
||||
|
||||
def test_empty_visitor_id_rejected(self):
|
||||
with pytest.raises(ValidationError):
|
||||
ConsentRecordCreate(
|
||||
site_id=uuid.uuid4(),
|
||||
visitor_id="",
|
||||
action=ConsentAction.ACCEPT_ALL,
|
||||
categories_accepted=["necessary"],
|
||||
)
|
||||
|
||||
def test_invalid_action_rejected(self):
|
||||
with pytest.raises(ValidationError):
|
||||
ConsentRecordCreate(
|
||||
site_id=uuid.uuid4(),
|
||||
visitor_id="v-1",
|
||||
action="invalid_action",
|
||||
categories_accepted=[],
|
||||
)
|
||||
|
||||
def test_response_from_attributes(self):
|
||||
resp = ConsentRecordResponse(
|
||||
id=uuid.uuid4(),
|
||||
site_id=uuid.uuid4(),
|
||||
visitor_id="v-1",
|
||||
action="accept_all",
|
||||
categories_accepted=["necessary"],
|
||||
categories_rejected=None,
|
||||
tc_string=None,
|
||||
gcm_state=None,
|
||||
page_url=None,
|
||||
country_code=None,
|
||||
region_code=None,
|
||||
consented_at="2026-01-01T00:00:00Z",
|
||||
)
|
||||
assert resp.action == "accept_all"
|
||||
|
||||
def test_verify_response(self):
|
||||
resp = ConsentVerifyResponse(
|
||||
id=uuid.uuid4(),
|
||||
site_id=uuid.uuid4(),
|
||||
visitor_id="v-1",
|
||||
action="accept_all",
|
||||
categories_accepted=["necessary"],
|
||||
consented_at="2026-01-01T00:00:00Z",
|
||||
)
|
||||
assert resp.valid is True
|
||||
|
||||
|
||||
class TestConsentActions:
|
||||
def test_action_values(self):
|
||||
assert ConsentAction.ACCEPT_ALL == "accept_all"
|
||||
assert ConsentAction.REJECT_ALL == "reject_all"
|
||||
assert ConsentAction.CUSTOM == "custom"
|
||||
assert ConsentAction.WITHDRAW == "withdraw"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
class TestConsentRoutesRegistered:
|
||||
async def test_consent_routes_exist(self, client):
|
||||
response = await client.get("/openapi.json")
|
||||
paths = response.json()["paths"]
|
||||
assert "/api/v1/consent/" in paths
|
||||
assert "/api/v1/consent/{consent_id}" in paths
|
||||
assert "/api/v1/consent/verify/{consent_id}" in paths
|
||||
|
||||
async def test_consent_post_validates_body(self, client):
|
||||
"""POST /consent rejects invalid payloads."""
|
||||
response = await client.post(
|
||||
"/api/v1/consent/",
|
||||
json={"invalid": "body"},
|
||||
)
|
||||
assert response.status_code == 422
|
||||
|
||||
async def test_config_public_endpoint_exists(self, client):
|
||||
response = await client.get("/openapi.json")
|
||||
paths = response.json()["paths"]
|
||||
assert "/api/v1/config/sites/{site_id}" in paths
|
||||
Reference in New Issue
Block a user