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:
166
apps/api/tests/test_models.py
Normal file
166
apps/api/tests/test_models.py
Normal file
@@ -0,0 +1,166 @@
|
||||
"""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}"
|
||||
)
|
||||
Reference in New Issue
Block a user