File: test_base.py

package info (click to toggle)
python-advanced-alchemy 1.8.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 4,904 kB
  • sloc: python: 36,227; makefile: 153; sh: 4
file content (161 lines) | stat: -rw-r--r-- 5,654 bytes parent folder | download
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