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:
James Cottrill
2026-04-13 14:20:15 +00:00
commit fbf26453f2
341 changed files with 62807 additions and 0 deletions

View File

@@ -0,0 +1,218 @@
"""Tests for cookie category, cookie, and allow-list schemas and routes."""
import uuid
import pytest
from pydantic import ValidationError
from src.schemas.cookie import (
AllowListEntryCreate,
AllowListEntryUpdate,
CookieCategoryResponse,
CookieCreate,
CookieResponse,
CookieUpdate,
ReviewStatus,
StorageType,
)
# ─── Schema tests ────────────────────────────────────────────────────
class TestStorageType:
def test_values(self):
assert StorageType.cookie == "cookie"
assert StorageType.local_storage == "local_storage"
assert StorageType.session_storage == "session_storage"
assert StorageType.indexed_db == "indexed_db"
class TestReviewStatus:
def test_values(self):
assert ReviewStatus.pending == "pending"
assert ReviewStatus.approved == "approved"
assert ReviewStatus.rejected == "rejected"
class TestCookieCreate:
def test_valid_minimal(self):
schema = CookieCreate(name="_ga", domain=".example.com")
assert schema.name == "_ga"
assert schema.domain == ".example.com"
assert schema.storage_type == StorageType.cookie
assert schema.category_id is None
def test_valid_full(self):
cat_id = uuid.uuid4()
schema = CookieCreate(
name="_ga",
domain=".google.com",
storage_type=StorageType.cookie,
category_id=cat_id,
description="Google Analytics cookie",
vendor="Google",
path="/",
max_age_seconds=63072000,
is_http_only=False,
is_secure=True,
same_site="Lax",
)
assert schema.category_id == cat_id
assert schema.max_age_seconds == 63072000
def test_rejects_empty_name(self):
with pytest.raises(ValidationError):
CookieCreate(name="", domain=".example.com")
def test_rejects_empty_domain(self):
with pytest.raises(ValidationError):
CookieCreate(name="_ga", domain="")
class TestCookieUpdate:
def test_partial_update(self):
schema = CookieUpdate(review_status=ReviewStatus.approved)
dump = schema.model_dump(exclude_unset=True)
assert dump == {"review_status": ReviewStatus.approved}
def test_update_category(self):
cat_id = uuid.uuid4()
schema = CookieUpdate(category_id=cat_id)
assert schema.category_id == cat_id
class TestAllowListEntryCreate:
def test_valid(self):
cat_id = uuid.uuid4()
schema = AllowListEntryCreate(
name_pattern="_ga*",
domain_pattern=".google.com",
category_id=cat_id,
description="Google Analytics cookies",
)
assert schema.name_pattern == "_ga*"
assert schema.category_id == cat_id
def test_rejects_empty_name_pattern(self):
with pytest.raises(ValidationError):
AllowListEntryCreate(
name_pattern="",
domain_pattern=".example.com",
category_id=uuid.uuid4(),
)
class TestAllowListEntryUpdate:
def test_partial_update(self):
schema = AllowListEntryUpdate(description="Updated description")
dump = schema.model_dump(exclude_unset=True)
assert dump == {"description": "Updated description"}
class TestCookieCategoryResponse:
def test_from_dict(self):
now = "2024-01-01T00:00:00"
resp = CookieCategoryResponse(
id=uuid.uuid4(),
name="Analytics",
slug="analytics",
description="Analytics cookies",
is_essential=False,
display_order=2,
tcf_purpose_ids=[1, 3],
gcm_consent_types=["analytics_storage"],
created_at=now,
updated_at=now,
)
assert resp.slug == "analytics"
assert resp.is_essential is False
class TestCookieResponse:
def test_from_dict(self):
now = "2024-01-01T00:00:00"
resp = CookieResponse(
id=uuid.uuid4(),
site_id=uuid.uuid4(),
name="_ga",
domain=".google.com",
storage_type="cookie",
review_status="pending",
created_at=now,
updated_at=now,
)
assert resp.name == "_ga"
assert resp.review_status == "pending"
# ─── Route tests ─────────────────────────────────────────────────────
class TestCookieCategoryRoutes:
def test_categories_route_registered(self, app):
"""Verify the categories routes are registered in the app."""
routes = [r.path for r in app.routes]
assert "/api/v1/cookies/categories" in routes
assert "/api/v1/cookies/categories/{category_id}" in routes
class TestCookieRoutes:
@pytest.mark.asyncio
async def test_list_cookies_requires_auth(self, client):
site_id = uuid.uuid4()
resp = await client.get(f"/api/v1/cookies/sites/{site_id}")
assert resp.status_code == 401
@pytest.mark.asyncio
async def test_create_cookie_requires_auth(self, client):
site_id = uuid.uuid4()
resp = await client.post(
f"/api/v1/cookies/sites/{site_id}",
json={"name": "_ga", "domain": ".google.com"},
)
assert resp.status_code == 401
@pytest.mark.asyncio
async def test_create_cookie_rejects_invalid_body(self, client):
site_id = uuid.uuid4()
resp = await client.post(
f"/api/v1/cookies/sites/{site_id}",
json={"name": "", "domain": ""},
headers={"Authorization": "Bearer fake-token"},
)
# Should return 401 (bad token) or 422 (validation)
assert resp.status_code in (401, 422)
@pytest.mark.asyncio
async def test_summary_route_requires_auth(self, client):
site_id = uuid.uuid4()
resp = await client.get(f"/api/v1/cookies/sites/{site_id}/summary")
assert resp.status_code == 401
class TestAllowListRoutes:
@pytest.mark.asyncio
async def test_list_allow_list_requires_auth(self, client):
site_id = uuid.uuid4()
resp = await client.get(f"/api/v1/cookies/sites/{site_id}/allow-list")
assert resp.status_code == 401
@pytest.mark.asyncio
async def test_create_allow_list_requires_auth(self, client):
site_id = uuid.uuid4()
resp = await client.post(
f"/api/v1/cookies/sites/{site_id}/allow-list",
json={
"name_pattern": "_ga*",
"domain_pattern": ".google.com",
"category_id": str(uuid.uuid4()),
},
)
assert resp.status_code == 401
@pytest.mark.asyncio
async def test_delete_allow_list_requires_auth(self, client):
site_id = uuid.uuid4()
entry_id = uuid.uuid4()
resp = await client.delete(f"/api/v1/cookies/sites/{site_id}/allow-list/{entry_id}")
assert resp.status_code == 401