You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
155 lines
5.0 KiB
155 lines
5.0 KiB
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
from sqlalchemy import Table, UniqueConstraint
|
|
from sqlalchemy.dialects import mysql, sqlite
|
|
|
|
from app import create_app
|
|
from app.extensions import db
|
|
from app.models.base import ID_TYPE
|
|
|
|
|
|
EXPECTED_TABLES = {
|
|
"accounts",
|
|
"audit_logs",
|
|
"gift_records",
|
|
"household_members",
|
|
"households",
|
|
"option_items",
|
|
"share_links",
|
|
}
|
|
|
|
|
|
def get_table(table_name: str) -> Table:
|
|
return db.metadata.tables[table_name]
|
|
|
|
|
|
def test_create_app_uses_testing_config() -> None:
|
|
app = create_app("testing")
|
|
|
|
assert app.config["TESTING"] is True
|
|
assert app.config["SQLALCHEMY_DATABASE_URI"] == "sqlite:///:memory:"
|
|
|
|
|
|
def test_healthcheck_route() -> None:
|
|
app = create_app("testing")
|
|
client = app.test_client()
|
|
|
|
response = client.get("/health")
|
|
|
|
assert response.status_code == 200
|
|
assert response.get_json() == {
|
|
"project": "HappyWedding",
|
|
"short_name": "hw",
|
|
"status": "ok",
|
|
}
|
|
|
|
|
|
def test_auth_related_blueprints_are_registered() -> None:
|
|
app = create_app("testing")
|
|
|
|
assert {"auth", "admin", "main", "quick_entry", "health"}.issubset(app.blueprints.keys())
|
|
|
|
|
|
def test_metadata_contains_expected_tables() -> None:
|
|
app = create_app("testing")
|
|
|
|
with app.app_context():
|
|
db.create_all()
|
|
table_names = set(db.metadata.tables.keys())
|
|
|
|
assert EXPECTED_TABLES.issubset(table_names)
|
|
|
|
|
|
def test_household_table_contains_core_columns() -> None:
|
|
household_columns = get_table("households").columns.keys()
|
|
|
|
assert "household_code" in household_columns
|
|
assert "tag_option_ids_json" in household_columns
|
|
assert "total_gift_amount" in household_columns
|
|
assert "version" in household_columns
|
|
assert "deleted_at" in household_columns
|
|
|
|
|
|
def test_account_table_contains_tracking_columns() -> None:
|
|
account_columns = get_table("accounts").columns.keys()
|
|
|
|
assert "username" in account_columns
|
|
assert "password_hash" in account_columns
|
|
assert "role" in account_columns
|
|
assert "status" in account_columns
|
|
assert "created_by" in account_columns
|
|
assert "updated_by" in account_columns
|
|
|
|
|
|
def test_relationship_foreign_keys_exist() -> None:
|
|
household_fks = {fk.parent.name for fk in get_table("households").foreign_keys}
|
|
gift_record_fks = {fk.parent.name for fk in get_table("gift_records").foreign_keys}
|
|
member_fks = {fk.parent.name for fk in get_table("household_members").foreign_keys}
|
|
option_item_fks = {fk.parent.name for fk in get_table("option_items").foreign_keys}
|
|
audit_log_fks = {fk.parent.name for fk in get_table("audit_logs").foreign_keys}
|
|
|
|
assert {
|
|
"relation_category_option_id",
|
|
"relation_detail_option_id",
|
|
"gift_method_option_id",
|
|
"gift_scene_option_id",
|
|
"created_by",
|
|
"updated_by",
|
|
}.issubset(household_fks)
|
|
assert {"household_id", "method_option_id", "scene_option_id", "created_by", "updated_by"}.issubset(
|
|
gift_record_fks,
|
|
)
|
|
assert {"household_id", "created_by", "updated_by"}.issubset(member_fks)
|
|
assert {"parent_id", "created_by", "updated_by"}.issubset(option_item_fks)
|
|
assert {"actor_user_id"}.issubset(audit_log_fks)
|
|
|
|
|
|
def test_unique_constraints_exist_on_key_tables() -> None:
|
|
account_unique_columns = [
|
|
list(constraint.columns.keys())
|
|
for constraint in get_table("accounts").constraints
|
|
if isinstance(constraint, UniqueConstraint)
|
|
]
|
|
household_unique_columns = [
|
|
list(constraint.columns.keys())
|
|
for constraint in get_table("households").constraints
|
|
if isinstance(constraint, UniqueConstraint)
|
|
]
|
|
option_item_unique_columns = [
|
|
list(constraint.columns.keys())
|
|
for constraint in get_table("option_items").constraints
|
|
if isinstance(constraint, UniqueConstraint)
|
|
]
|
|
|
|
assert ["username"] in account_unique_columns
|
|
assert ["household_code"] in household_unique_columns
|
|
assert ["option_group", "option_code"] in option_item_unique_columns
|
|
|
|
|
|
def test_id_type_compiles_for_sqlite_and_mysql() -> None:
|
|
assert ID_TYPE.compile(dialect=sqlite.dialect()) == "INTEGER"
|
|
assert ID_TYPE.compile(dialect=mysql.dialect()) == "BIGINT"
|
|
|
|
|
|
def test_sqlite_connections_apply_runtime_pragmas(monkeypatch, tmp_path: Path) -> None:
|
|
db_path = tmp_path / "pragma-check.db"
|
|
monkeypatch.setenv("DATABASE_URL", f"sqlite:///{db_path}")
|
|
|
|
app = create_app("development")
|
|
|
|
with app.app_context():
|
|
db.create_all()
|
|
connection = db.session.connection()
|
|
journal_mode = connection.exec_driver_sql("PRAGMA journal_mode;").scalar()
|
|
busy_timeout = connection.exec_driver_sql("PRAGMA busy_timeout;").scalar()
|
|
temp_store = connection.exec_driver_sql("PRAGMA temp_store;").scalar()
|
|
synchronous = connection.exec_driver_sql("PRAGMA synchronous;").scalar()
|
|
foreign_keys = connection.exec_driver_sql("PRAGMA foreign_keys;").scalar()
|
|
|
|
assert str(journal_mode).lower() == "wal"
|
|
assert busy_timeout == 5000
|
|
assert temp_store == 2
|
|
assert synchronous == 1
|
|
assert foreign_keys == 1
|
|
|