1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
|
# pyright: reportUnusedImport=false
from __future__ import annotations
import warnings
from typing import cast
from sqlalchemy import Table, create_engine
from sqlalchemy.dialects import mssql, oracle, postgresql
from sqlalchemy.orm import declarative_mixin
from sqlalchemy.schema import CreateTable
from tests.helpers import purge_module
def test_deprecated_classes_functionality() -> None:
"""Test that mixins classes maintain have base functionality."""
purge_module(["advanced_alchemy.base", "advanced_alchemy.mixins"], __file__)
warnings.filterwarnings("ignore", category=DeprecationWarning)
from sqlalchemy import exc as sa_exc
warnings.filterwarnings("ignore", category=sa_exc.SAWarning)
# Test instantiation and basic attributes
from advanced_alchemy.mixins import (
AuditColumns,
NanoIDPrimaryKey,
UUIDPrimaryKey,
UUIDv6PrimaryKey,
UUIDv7PrimaryKey,
)
uuidv7_pk = UUIDv7PrimaryKey()
uuidv6_pk = UUIDv6PrimaryKey()
uuid_pk = UUIDPrimaryKey()
nanoid_pk = NanoIDPrimaryKey()
audit = AuditColumns()
# Verify the classes have the expected attributes
assert hasattr(uuidv7_pk, "id")
assert hasattr(uuidv7_pk, "_sentinel")
assert hasattr(uuidv6_pk, "id")
assert hasattr(uuidv6_pk, "_sentinel")
assert hasattr(uuid_pk, "id")
assert hasattr(uuid_pk, "_sentinel")
assert hasattr(nanoid_pk, "id")
assert hasattr(nanoid_pk, "_sentinel")
assert hasattr(audit, "created_at")
assert hasattr(audit, "updated_at")
def test_identity_primary_key_generates_identity_ddl() -> None:
"""Test that IdentityPrimaryKey generates proper IDENTITY DDL for PostgreSQL."""
from advanced_alchemy.base import BigIntBase
from advanced_alchemy.mixins.bigint import IdentityPrimaryKey
@declarative_mixin
class TestMixin(IdentityPrimaryKey):
__tablename__ = "test_identity"
class TestModel(TestMixin, BigIntBase):
pass
# Get the CREATE TABLE statement
create_stmt = CreateTable(cast(Table, TestModel.__table__))
# Test with PostgreSQL dialect
pg_ddl = str(create_stmt.compile(dialect=postgresql.dialect())) # type: ignore[no-untyped-call,unused-ignore]
# Should contain GENERATED BY DEFAULT AS IDENTITY
assert "GENERATED BY DEFAULT AS IDENTITY" in pg_ddl
assert "BIGSERIAL" not in pg_ddl.upper()
assert "START WITH 1" in pg_ddl
assert "INCREMENT BY 1" in pg_ddl
def test_identity_audit_base_generates_identity_ddl() -> None:
"""Test that IdentityAuditBase generates proper IDENTITY DDL for PostgreSQL."""
from advanced_alchemy.base import IdentityAuditBase
class TestModel(IdentityAuditBase):
__tablename__ = "test_identity_audit"
# Get the CREATE TABLE statement
create_stmt = CreateTable(cast(Table, TestModel.__table__))
# Test with PostgreSQL dialect
pg_ddl = str(create_stmt.compile(dialect=postgresql.dialect())) # type: ignore[no-untyped-call,unused-ignore]
# Should contain GENERATED BY DEFAULT AS IDENTITY
assert "GENERATED BY DEFAULT AS IDENTITY" in pg_ddl
assert "BIGSERIAL" not in pg_ddl.upper()
def test_bigint_primary_key_still_uses_sequence() -> None:
"""Test that BigIntPrimaryKey still uses sequences as before."""
from advanced_alchemy.base import BigIntBase
from advanced_alchemy.mixins.bigint import BigIntPrimaryKey
@declarative_mixin
class TestMixin(BigIntPrimaryKey):
__tablename__ = "test_bigint"
class TestModel(TestMixin, BigIntBase):
pass
# Get the CREATE TABLE statement
create_stmt = CreateTable(cast(Table, TestModel.__table__))
# Test with PostgreSQL dialect
pg_ddl = str(create_stmt.compile(dialect=postgresql.dialect())) # type: ignore[no-untyped-call,unused-ignore]
# BigIntPrimaryKey should use a Sequence (not IDENTITY)
assert "GENERATED" not in pg_ddl
assert "IDENTITY" not in pg_ddl.upper()
# The sequence is defined on the column but rendered separately
assert TestModel.__table__.c.id.default is not None
assert TestModel.__table__.c.id.default.name == "test_bigint_id_seq"
def test_identity_ddl_for_oracle() -> None:
"""Test Identity DDL generation for Oracle."""
from advanced_alchemy.base import IdentityAuditBase
class TestModel(IdentityAuditBase):
__tablename__ = "test_oracle"
create_stmt = CreateTable(cast(Table, TestModel.__table__))
oracle_ddl = str(create_stmt.compile(dialect=oracle.dialect())) # type: ignore[no-untyped-call,unused-ignore]
# Oracle should generate IDENTITY
assert "GENERATED BY DEFAULT AS IDENTITY" in oracle_ddl
def test_identity_ddl_for_mssql() -> None:
"""Test Identity DDL generation for SQL Server."""
from advanced_alchemy.base import IdentityAuditBase
class TestModel(IdentityAuditBase):
__tablename__ = "test_mssql"
create_stmt = CreateTable(cast(Table, TestModel.__table__))
mssql_ddl = str(create_stmt.compile(dialect=mssql.dialect())) # type: ignore[no-untyped-call,unused-ignore]
# SQL Server should generate IDENTITY
assert "IDENTITY(1,1)" in mssql_ddl
def test_identity_works_with_sqlite() -> None:
"""Test that Identity columns work with SQLite (fallback to autoincrement)."""
from advanced_alchemy.base import IdentityAuditBase
class TestModel(IdentityAuditBase):
__tablename__ = "test_sqlite"
# Create an in-memory SQLite engine
engine = create_engine("sqlite:///:memory:")
cast(Table, TestModel.__table__).create(engine)
# Should not raise any errors
assert True # If we get here, it worked
|