feat: support --fake for aerich upgrade (#398)
* feat: support `--fake` for aerich upgrade * Add `--fake` to downgrade * tests: check --fake result for aerich upgrade and downgrade * Update readme * Fix unittest failed because of `db_field_types` changed * refactor: improve type hints and document
This commit is contained in:
72
tests/assets/fake/_tests.py
Normal file
72
tests/assets/fake/_tests.py
Normal file
@@ -0,0 +1,72 @@
|
||||
import pytest
|
||||
from models import NewModel
|
||||
from models_second import Config
|
||||
from settings import TORTOISE_ORM
|
||||
from tortoise import Tortoise
|
||||
from tortoise.exceptions import OperationalError
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def anyio_backend() -> str:
|
||||
return "asyncio"
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
async def init_connections():
|
||||
await Tortoise.init(TORTOISE_ORM)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
await Tortoise.close_connections()
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_init_db():
|
||||
m1 = await NewModel.filter(name="")
|
||||
assert isinstance(m1, list)
|
||||
m2 = await Config.filter(key="")
|
||||
assert isinstance(m2, list)
|
||||
await NewModel.create(name="")
|
||||
await Config.create(key="", label="", value={})
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_fake_field_1():
|
||||
assert "field_1" in NewModel._meta.fields_map
|
||||
assert "field_1" in Config._meta.fields_map
|
||||
with pytest.raises(OperationalError):
|
||||
await NewModel.create(name="", field_1=1)
|
||||
with pytest.raises(OperationalError):
|
||||
await Config.create(key="", label="", value={}, field_1=1)
|
||||
|
||||
obj1 = NewModel(name="", field_1=1)
|
||||
with pytest.raises(OperationalError):
|
||||
await obj1.save()
|
||||
obj1 = NewModel(name="")
|
||||
with pytest.raises(OperationalError):
|
||||
await obj1.save()
|
||||
with pytest.raises(OperationalError):
|
||||
obj1 = await NewModel.first()
|
||||
obj1 = await NewModel.all().first().values("id", "name")
|
||||
assert obj1 and obj1["id"]
|
||||
|
||||
obj2 = Config(key="", label="", value={}, field_1=1)
|
||||
with pytest.raises(OperationalError):
|
||||
await obj2.save()
|
||||
obj2 = Config(key="", label="", value={})
|
||||
with pytest.raises(OperationalError):
|
||||
await obj2.save()
|
||||
with pytest.raises(OperationalError):
|
||||
obj2 = await Config.first()
|
||||
obj2 = await Config.all().first().values("id", "key")
|
||||
assert obj2 and obj2["id"]
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_fake_field_2():
|
||||
assert "field_2" in NewModel._meta.fields_map
|
||||
assert "field_2" in Config._meta.fields_map
|
||||
with pytest.raises(OperationalError):
|
||||
await NewModel.create(name="")
|
||||
with pytest.raises(OperationalError):
|
||||
await Config.create(key="", label="", value={})
|
||||
28
tests/assets/fake/db.py
Normal file
28
tests/assets/fake/db.py
Normal file
@@ -0,0 +1,28 @@
|
||||
import asyncclick as click
|
||||
from settings import TORTOISE_ORM
|
||||
|
||||
from tests._utils import drop_db, init_db
|
||||
|
||||
|
||||
@click.group()
|
||||
def cli(): ...
|
||||
|
||||
|
||||
@cli.command()
|
||||
async def create():
|
||||
await init_db(TORTOISE_ORM, False)
|
||||
click.echo(f"Success to create databases for {TORTOISE_ORM['connections']}")
|
||||
|
||||
|
||||
@cli.command()
|
||||
async def drop():
|
||||
await drop_db(TORTOISE_ORM)
|
||||
click.echo(f"Dropped databases for {TORTOISE_ORM['connections']}")
|
||||
|
||||
|
||||
def main():
|
||||
cli()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
22
tests/assets/fake/settings.py
Normal file
22
tests/assets/fake/settings.py
Normal file
@@ -0,0 +1,22 @@
|
||||
import os
|
||||
from datetime import date
|
||||
|
||||
from tortoise.contrib.test import MEMORY_SQLITE
|
||||
|
||||
DB_URL = (
|
||||
_u.replace("\\{\\}", f"aerich_fake_{date.today():%Y%m%d}")
|
||||
if (_u := os.getenv("TEST_DB"))
|
||||
else MEMORY_SQLITE
|
||||
)
|
||||
DB_URL_SECOND = (DB_URL + "_second") if DB_URL != MEMORY_SQLITE else MEMORY_SQLITE
|
||||
|
||||
TORTOISE_ORM = {
|
||||
"connections": {
|
||||
"default": DB_URL.replace(MEMORY_SQLITE, "sqlite://db.sqlite3"),
|
||||
"second": DB_URL_SECOND.replace(MEMORY_SQLITE, "sqlite://db_second.sqlite3"),
|
||||
},
|
||||
"apps": {
|
||||
"models": {"models": ["models", "aerich.models"], "default_connection": "default"},
|
||||
"models_second": {"models": ["models_second"], "default_connection": "second"},
|
||||
},
|
||||
}
|
||||
75
tests/assets/sqlite_migrate/_tests.py
Normal file
75
tests/assets/sqlite_migrate/_tests.py
Normal file
@@ -0,0 +1,75 @@
|
||||
import uuid
|
||||
|
||||
import pytest
|
||||
from models import Foo
|
||||
from tortoise.exceptions import IntegrityError
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_allow_duplicate() -> None:
|
||||
await Foo.all().delete()
|
||||
await Foo.create(name="foo")
|
||||
obj = await Foo.create(name="foo")
|
||||
assert (await Foo.all().count()) == 2
|
||||
await obj.delete()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_unique_is_true() -> None:
|
||||
with pytest.raises(IntegrityError):
|
||||
await Foo.create(name="foo")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_add_unique_field() -> None:
|
||||
if not await Foo.filter(age=0).exists():
|
||||
await Foo.create(name="0_" + uuid.uuid4().hex, age=0)
|
||||
with pytest.raises(IntegrityError):
|
||||
await Foo.create(name=uuid.uuid4().hex, age=0)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_drop_unique_field() -> None:
|
||||
name = "1_" + uuid.uuid4().hex
|
||||
await Foo.create(name=name, age=0)
|
||||
assert await Foo.filter(name=name).exists()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_with_age_field() -> None:
|
||||
name = "2_" + uuid.uuid4().hex
|
||||
await Foo.create(name=name, age=0)
|
||||
obj = await Foo.get(name=name)
|
||||
assert obj.age == 0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_without_age_field() -> None:
|
||||
name = "3_" + uuid.uuid4().hex
|
||||
await Foo.create(name=name, age=0)
|
||||
obj = await Foo.get(name=name)
|
||||
assert getattr(obj, "age", None) is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_m2m_with_custom_through() -> None:
|
||||
from models import FooGroup, Group
|
||||
|
||||
name = "4_" + uuid.uuid4().hex
|
||||
foo = await Foo.create(name=name)
|
||||
group = await Group.create(name=name + "1")
|
||||
await FooGroup.all().delete()
|
||||
await foo.groups.add(group)
|
||||
foo_group = await FooGroup.get(foo=foo, group=group)
|
||||
assert not foo_group.is_active
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_add_m2m_field_after_init_db() -> None:
|
||||
from models import Group
|
||||
|
||||
name = "5_" + uuid.uuid4().hex
|
||||
foo = await Foo.create(name=name)
|
||||
group = await Group.create(name=name + "1")
|
||||
await foo.groups.add(group)
|
||||
assert (await group.users.all().first()) == foo
|
||||
26
tests/assets/sqlite_migrate/conftest_.py
Normal file
26
tests/assets/sqlite_migrate/conftest_.py
Normal file
@@ -0,0 +1,26 @@
|
||||
import asyncio
|
||||
from typing import Generator
|
||||
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
import settings
|
||||
from tortoise import Tortoise, connections
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def event_loop() -> Generator:
|
||||
policy = asyncio.get_event_loop_policy()
|
||||
res = policy.new_event_loop()
|
||||
asyncio.set_event_loop(res)
|
||||
res._close = res.close # type:ignore[attr-defined]
|
||||
res.close = lambda: None # type:ignore[method-assign]
|
||||
|
||||
yield res
|
||||
|
||||
res._close() # type:ignore[attr-defined]
|
||||
|
||||
|
||||
@pytest_asyncio.fixture(scope="session", autouse=True)
|
||||
async def api(event_loop, request):
|
||||
await Tortoise.init(config=settings.TORTOISE_ORM)
|
||||
request.addfinalizer(lambda: event_loop.run_until_complete(connections.close_all(discard=True)))
|
||||
5
tests/assets/sqlite_migrate/models.py
Normal file
5
tests/assets/sqlite_migrate/models.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from tortoise import Model, fields
|
||||
|
||||
|
||||
class Foo(Model):
|
||||
name = fields.CharField(max_length=60, db_index=False)
|
||||
4
tests/assets/sqlite_migrate/settings.py
Normal file
4
tests/assets/sqlite_migrate/settings.py
Normal file
@@ -0,0 +1,4 @@
|
||||
TORTOISE_ORM = {
|
||||
"connections": {"default": "sqlite://db.sqlite3"},
|
||||
"apps": {"models": {"models": ["models", "aerich.models"]}},
|
||||
}
|
||||
Reference in New Issue
Block a user