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.
167 lines
4.7 KiB
Python
167 lines
4.7 KiB
Python
"""Tests for SQLAlchemy model definitions.
|
|
|
|
These tests verify model structure without needing a database connection.
|
|
"""
|
|
|
|
from sqlalchemy import inspect
|
|
|
|
from src.models import (
|
|
Base,
|
|
ConsentRecord,
|
|
Cookie,
|
|
CookieAllowListEntry,
|
|
CookieCategory,
|
|
KnownCookie,
|
|
Organisation,
|
|
ScanJob,
|
|
ScanResult,
|
|
Site,
|
|
SiteConfig,
|
|
Translation,
|
|
User,
|
|
)
|
|
|
|
|
|
def test_all_models_registered_in_metadata():
|
|
"""All expected tables should be present in Base.metadata."""
|
|
table_names = set(Base.metadata.tables.keys())
|
|
expected = {
|
|
"organisations",
|
|
"users",
|
|
"sites",
|
|
"site_configs",
|
|
"cookie_categories",
|
|
"cookies",
|
|
"cookie_allow_list",
|
|
"known_cookies",
|
|
"consent_records",
|
|
"scan_jobs",
|
|
"scan_results",
|
|
"translations",
|
|
}
|
|
assert expected.issubset(table_names), f"Missing tables: {expected - table_names}"
|
|
|
|
|
|
def test_organisation_columns():
|
|
mapper = inspect(Organisation)
|
|
column_names = {c.key for c in mapper.columns}
|
|
assert "id" in column_names
|
|
assert "name" in column_names
|
|
assert "slug" in column_names
|
|
assert "contact_email" in column_names
|
|
assert "billing_plan" in column_names
|
|
assert "created_at" in column_names
|
|
assert "updated_at" in column_names
|
|
assert "deleted_at" in column_names
|
|
|
|
|
|
def test_user_columns_and_fk():
|
|
mapper = inspect(User)
|
|
column_names = {c.key for c in mapper.columns}
|
|
assert "organisation_id" in column_names
|
|
assert "email" in column_names
|
|
assert "password_hash" in column_names
|
|
assert "role" in column_names
|
|
|
|
|
|
def test_site_unique_constraint():
|
|
table = Site.__table__
|
|
constraint_names = {c.name for c in table.constraints if hasattr(c, "name") and c.name}
|
|
assert "uq_sites_org_domain" in constraint_names
|
|
|
|
|
|
def test_site_config_jsonb_fields():
|
|
mapper = inspect(SiteConfig)
|
|
column_names = {c.key for c in mapper.columns}
|
|
for field in ["regional_modes", "gcm_default", "banner_config"]:
|
|
assert field in column_names, f"Missing JSONB field: {field}"
|
|
|
|
|
|
def test_cookie_category_columns():
|
|
mapper = inspect(CookieCategory)
|
|
column_names = {c.key for c in mapper.columns}
|
|
assert "tcf_purpose_ids" in column_names
|
|
assert "gcm_consent_types" in column_names
|
|
assert "is_essential" in column_names
|
|
|
|
|
|
def test_cookie_unique_constraint():
|
|
table = Cookie.__table__
|
|
constraint_names = {c.name for c in table.constraints if hasattr(c, "name") and c.name}
|
|
assert "uq_cookies_site_name_domain_type" in constraint_names
|
|
|
|
|
|
def test_cookie_allow_list_unique_constraint():
|
|
table = CookieAllowListEntry.__table__
|
|
constraint_names = {c.name for c in table.constraints if hasattr(c, "name") and c.name}
|
|
assert "uq_allow_list_site_name_domain" in constraint_names
|
|
|
|
|
|
def test_known_cookie_unique_constraint():
|
|
table = KnownCookie.__table__
|
|
constraint_names = {c.name for c in table.constraints if hasattr(c, "name") and c.name}
|
|
assert "uq_known_cookies_name_domain" in constraint_names
|
|
|
|
|
|
def test_consent_record_columns():
|
|
mapper = inspect(ConsentRecord)
|
|
column_names = {c.key for c in mapper.columns}
|
|
for field in [
|
|
"visitor_id",
|
|
"action",
|
|
"categories_accepted",
|
|
"tc_string",
|
|
"gcm_state",
|
|
"country_code",
|
|
"consented_at",
|
|
]:
|
|
assert field in column_names, f"Missing field: {field}"
|
|
|
|
|
|
def test_scan_job_columns():
|
|
mapper = inspect(ScanJob)
|
|
column_names = {c.key for c in mapper.columns}
|
|
assert "status" in column_names
|
|
assert "pages_scanned" in column_names
|
|
assert "cookies_found" in column_names
|
|
|
|
|
|
def test_scan_result_columns():
|
|
mapper = inspect(ScanResult)
|
|
column_names = {c.key for c in mapper.columns}
|
|
assert "page_url" in column_names
|
|
assert "cookie_name" in column_names
|
|
assert "script_source" in column_names
|
|
assert "auto_category" in column_names
|
|
|
|
|
|
def test_translation_unique_constraint():
|
|
table = Translation.__table__
|
|
constraint_names = {c.name for c in table.constraints if hasattr(c, "name") and c.name}
|
|
assert "uq_translations_site_locale" in constraint_names
|
|
|
|
|
|
def test_uuid_primary_keys():
|
|
"""All models should use UUID primary keys."""
|
|
models = [
|
|
Organisation,
|
|
User,
|
|
Site,
|
|
SiteConfig,
|
|
CookieCategory,
|
|
Cookie,
|
|
CookieAllowListEntry,
|
|
KnownCookie,
|
|
ConsentRecord,
|
|
ScanJob,
|
|
ScanResult,
|
|
Translation,
|
|
]
|
|
for model in models:
|
|
mapper = inspect(model)
|
|
pk_cols = mapper.primary_key
|
|
assert len(pk_cols) == 1, f"{model.__name__} should have exactly one PK column"
|
|
assert str(pk_cols[0].type) == "UUID", (
|
|
f"{model.__name__} PK should be UUID, got {pk_cols[0].type}"
|
|
)
|