tigrbl-tests 0.3.2.dev1__py3-none-any.whl → 0.3.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. tests/i9n/test_tigrbl_api_app_usage_uvicorn.py +98 -0
  2. tests/i9n/test_tigrbl_api_usage_uvicorn.py +105 -0
  3. tests/i9n/test_tigrbl_api_uvicorn.py +67 -0
  4. tests/i9n/test_tigrbl_app_include_api_uvicorn.py +149 -0
  5. tests/i9n/test_tigrbl_app_multi_api_uvicorn.py +80 -0
  6. tests/i9n/test_tigrbl_app_usage_uvicorn.py +103 -0
  7. tests/i9n/test_tigrbl_app_uvicorn.py +58 -0
  8. tests/unit/test_initialize_async_task.py +27 -0
  9. tests/unit/test_initialize_mixed_engines.py +41 -0
  10. tests/unit/test_initialize_task_schedule.py +27 -0
  11. tests/unit/test_spec_app.py +1 -1
  12. tests/unit/test_table_collect_spec.py +1 -1
  13. tests/unit/test_table_namespace_init.py +27 -0
  14. tests/unit/test_table_namespace_isolation.py +43 -0
  15. tests/unit/test_tigrbl_api_app_configuration.py +83 -0
  16. tests/unit/test_tigrbl_api_app_instantiation.py +42 -0
  17. tests/unit/test_tigrbl_api_app_subclass_definition.py +38 -0
  18. tests/unit/test_tigrbl_api_configuration.py +47 -0
  19. tests/unit/test_tigrbl_api_instantiation.py +37 -0
  20. tests/unit/test_tigrbl_api_subclass_definition.py +37 -0
  21. tests/unit/test_tigrbl_app_configuration.py +47 -0
  22. tests/unit/test_tigrbl_app_instantiation.py +37 -0
  23. tests/unit/test_tigrbl_app_subclass_definition.py +37 -0
  24. {tigrbl_tests-0.3.2.dev1.dist-info → tigrbl_tests-0.3.3.dist-info}/METADATA +16 -1
  25. {tigrbl_tests-0.3.2.dev1.dist-info → tigrbl_tests-0.3.3.dist-info}/RECORD +27 -6
  26. {tigrbl_tests-0.3.2.dev1.dist-info → tigrbl_tests-0.3.3.dist-info}/WHEEL +0 -0
  27. {tigrbl_tests-0.3.2.dev1.dist-info → tigrbl_tests-0.3.3.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,41 @@
1
+ import asyncio
2
+
3
+ import pytest
4
+ from sqlalchemy import Column, Integer
5
+
6
+ from tigrbl import Base, TigrblApi, TigrblApp
7
+ from tigrbl.engine.shortcuts import mem
8
+
9
+
10
+ class AsyncWidget(Base):
11
+ __tablename__ = "widgets_async_api"
12
+
13
+ id = Column(Integer, primary_key=True)
14
+
15
+
16
+ class SyncWidget(Base):
17
+ __tablename__ = "widgets_sync_api"
18
+
19
+ id = Column(Integer, primary_key=True)
20
+
21
+
22
+ @pytest.mark.asyncio
23
+ async def test_initialize_handles_mixed_sync_async_apis():
24
+ async_api = TigrblApi(engine=mem())
25
+ async_api.include_model(AsyncWidget, prefix="")
26
+
27
+ sync_api = TigrblApi(engine=mem(async_=False))
28
+ sync_api.include_model(SyncWidget, prefix="")
29
+
30
+ app = TigrblApp(engine=mem(async_=False))
31
+ app.include_apis([async_api, sync_api])
32
+
33
+ result = app.initialize()
34
+
35
+ assert isinstance(result, asyncio.Task)
36
+
37
+ await result
38
+
39
+ assert getattr(app, "_ddl_executed", False) is True
40
+ assert getattr(async_api, "_ddl_executed", False) is True
41
+ assert getattr(sync_api, "_ddl_executed", False) is True
@@ -0,0 +1,27 @@
1
+ import asyncio
2
+
3
+ import pytest
4
+ from sqlalchemy import Column, Integer
5
+
6
+ from tigrbl import Base, TigrblApp
7
+ from tigrbl.engine.shortcuts import mem
8
+
9
+
10
+ class Widget(Base):
11
+ __tablename__ = "widgets"
12
+
13
+ id = Column(Integer, primary_key=True)
14
+
15
+
16
+ @pytest.mark.asyncio
17
+ async def test_initialize_schedules_task_for_sync_engine():
18
+ api = TigrblApp(engine=mem(async_=False))
19
+ api.models["Widget"] = Widget
20
+
21
+ result = api.initialize()
22
+
23
+ assert isinstance(result, asyncio.Task)
24
+
25
+ await result
26
+
27
+ assert getattr(api, "_ddl_executed", False) is True
@@ -15,7 +15,7 @@ def test_app_spec_defaults_and_merge():
15
15
  spec = mro_collect_app_spec(ChildApp)
16
16
  assert spec.title == "Base"
17
17
  assert spec.version == "1.0"
18
- assert spec.apis == ("base", "base", "child")
18
+ assert spec.apis == ("child", "base")
19
19
  assert spec.ops == ("read",)
20
20
  assert spec.models == ()
21
21
  assert spec.schemas == ()
@@ -32,7 +32,7 @@ class Model(SpecA, SpecB, Base, GUIDPk):
32
32
  def test_collect_table_spec_merges_mro():
33
33
  spec = mro_collect_table_spec(Model)
34
34
  assert spec.model is Model
35
- assert spec.engine == "db_b"
35
+ assert spec.engine == "db_a"
36
36
  assert spec.ops == ("a", "b")
37
37
  assert spec.columns == ("col_a", "col_b")
38
38
  assert spec.schemas == ("SchemaA", "SchemaB")
@@ -0,0 +1,27 @@
1
+ from types import SimpleNamespace
2
+
3
+ from tigrbl.specs import F, S, acol
4
+ from tigrbl.table import Table
5
+ from tigrbl.types import Integer, Mapped
6
+
7
+
8
+ class Widget(Table):
9
+ __tablename__ = "widgets_namespace_init"
10
+
11
+ id: Mapped[int] = acol(
12
+ storage=S(type_=Integer, primary_key=True), field=F(py_type=int)
13
+ )
14
+
15
+
16
+ def test_table_initializes_model_namespaces() -> None:
17
+ assert isinstance(Widget.ops, SimpleNamespace)
18
+ assert Widget.ops is Widget.opspecs
19
+ assert isinstance(Widget.schemas, SimpleNamespace)
20
+ assert isinstance(Widget.hooks, SimpleNamespace)
21
+ assert isinstance(Widget.handlers, SimpleNamespace)
22
+ assert isinstance(Widget.rpc, SimpleNamespace)
23
+ assert isinstance(Widget.rest, SimpleNamespace)
24
+ assert hasattr(Widget.rest, "router")
25
+ assert isinstance(Widget.__tigrbl_hooks__, dict)
26
+ assert isinstance(Widget.columns, SimpleNamespace)
27
+ assert Widget.columns.id is Widget.__tigrbl_cols__["id"]
@@ -0,0 +1,43 @@
1
+ from types import SimpleNamespace
2
+
3
+ from tigrbl.specs import F, S, acol
4
+ from tigrbl.table import Table
5
+ from tigrbl.types import Integer, Mapped
6
+
7
+
8
+ class BaseWidget(Table):
9
+ __abstract__ = True
10
+
11
+ id: Mapped[int] = acol(
12
+ storage=S(type_=Integer, primary_key=True), field=F(py_type=int)
13
+ )
14
+
15
+
16
+ class WidgetAlpha(BaseWidget):
17
+ __tablename__ = "widgets_namespace_alpha"
18
+
19
+
20
+ class WidgetBeta(BaseWidget):
21
+ __tablename__ = "widgets_namespace_beta"
22
+
23
+
24
+ def test_model_namespaces_are_per_class() -> None:
25
+ assert isinstance(WidgetAlpha.ops, SimpleNamespace)
26
+ assert isinstance(WidgetBeta.ops, SimpleNamespace)
27
+ assert WidgetAlpha.ops is WidgetAlpha.opspecs
28
+ assert WidgetBeta.ops is WidgetBeta.opspecs
29
+ assert WidgetAlpha.ops is not WidgetBeta.ops
30
+ assert WidgetAlpha.schemas is not WidgetBeta.schemas
31
+ assert WidgetAlpha.hooks is not WidgetBeta.hooks
32
+ assert WidgetAlpha.handlers is not WidgetBeta.handlers
33
+ assert WidgetAlpha.rpc is not WidgetBeta.rpc
34
+ assert WidgetAlpha.rest is not WidgetBeta.rest
35
+ assert WidgetAlpha.__tigrbl_hooks__ is not WidgetBeta.__tigrbl_hooks__
36
+
37
+
38
+ def test_model_namespace_mutations_do_not_bleed() -> None:
39
+ WidgetAlpha.ops.by_alias["alpha_only"] = ["alpha"]
40
+ assert "alpha_only" not in WidgetBeta.ops.by_alias
41
+
42
+ WidgetAlpha.schemas.alpha_only = "alpha_schema"
43
+ assert not hasattr(WidgetBeta.schemas, "alpha_only")
@@ -0,0 +1,83 @@
1
+ import pytest
2
+ from fastapi import Security
3
+ from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
4
+
5
+ from tigrbl import Base, TigrblApi, TigrblApp
6
+ from tigrbl.engine.shortcuts import mem
7
+ from tigrbl.orm.mixins import GUIDPk
8
+ from tigrbl.specs import F, IO, S, acol
9
+ from tigrbl.types import Mapped, String
10
+
11
+
12
+ def _auth_dependency(
13
+ credentials: HTTPAuthorizationCredentials = Security(HTTPBearer()),
14
+ ) -> HTTPAuthorizationCredentials:
15
+ return credentials
16
+
17
+
18
+ class Iota(Base, GUIDPk):
19
+ __tablename__ = "iota_api_app_cfg"
20
+ __allow_unmapped__ = True
21
+
22
+ name: Mapped[str] = acol(
23
+ storage=S(type_=String, nullable=False),
24
+ field=F(py_type=str),
25
+ io=IO(in_verbs=("create",), out_verbs=("read", "list")),
26
+ )
27
+
28
+ __tigrbl_cols__ = {"id": GUIDPk.id, "name": name}
29
+
30
+
31
+ class IotaApi(TigrblApi):
32
+ MODELS = (Iota,)
33
+
34
+
35
+ @pytest.mark.unit
36
+ def test_tigrbl_api_app_constructor_configuration_applies_metadata() -> None:
37
+ api = IotaApi(
38
+ engine=mem(async_=False),
39
+ jsonrpc_prefix="/rpcx",
40
+ system_prefix="/systemx",
41
+ )
42
+
43
+ class IotaApp(TigrblApp):
44
+ APIS = (api,)
45
+
46
+ app = IotaApp(engine=mem(async_=False), title="Iota App", version="9.9.9")
47
+
48
+ api_dir = dir(api)
49
+ app_dir = dir(app)
50
+
51
+ assert "jsonrpc_prefix" in api_dir
52
+ assert "system_prefix" in api_dir
53
+ assert "TITLE" in app_dir
54
+ assert "VERSION" in app_dir
55
+ assert api.jsonrpc_prefix == "/rpcx"
56
+ assert api.system_prefix == "/systemx"
57
+ assert app.TITLE == "Iota App"
58
+ assert app.VERSION == "9.9.9"
59
+ assert app.apis == [api]
60
+
61
+
62
+ @pytest.mark.unit
63
+ def test_tigrbl_api_app_post_instantiation_updates_auth_state() -> None:
64
+ api = IotaApi(engine=mem(async_=False))
65
+
66
+ class IotaApp(TigrblApp):
67
+ APIS = (api,)
68
+
69
+ app = IotaApp(engine=mem(async_=False))
70
+ api.set_auth(authn=_auth_dependency, allow_anon=False)
71
+ app.set_auth(authn=_auth_dependency, allow_anon=False)
72
+
73
+ api_dir = dir(api)
74
+ app_dir = dir(app)
75
+
76
+ assert "_authn" in api_dir
77
+ assert "_allow_anon" in api_dir
78
+ assert api._authn is _auth_dependency
79
+ assert api._allow_anon is False
80
+ assert "_authn" in app_dir
81
+ assert "_allow_anon" in app_dir
82
+ assert app._authn is _auth_dependency
83
+ assert app._allow_anon is False
@@ -0,0 +1,42 @@
1
+ import pytest
2
+
3
+ from tigrbl import Base, TigrblApi, TigrblApp
4
+ from tigrbl.engine.shortcuts import mem
5
+ from tigrbl.orm.mixins import GUIDPk
6
+ from tigrbl.specs import F, IO, S, acol
7
+ from tigrbl.types import Mapped, String
8
+
9
+
10
+ class Theta(Base, GUIDPk):
11
+ __tablename__ = "theta_api_app_inst"
12
+ __allow_unmapped__ = True
13
+
14
+ name: Mapped[str] = acol(
15
+ storage=S(type_=String, nullable=False),
16
+ field=F(py_type=str),
17
+ io=IO(in_verbs=("create",), out_verbs=("read", "list")),
18
+ )
19
+
20
+ __tigrbl_cols__ = {"id": GUIDPk.id, "name": name}
21
+
22
+
23
+ class ThetaApi(TigrblApi):
24
+ MODELS = (Theta,)
25
+
26
+
27
+ @pytest.mark.unit
28
+ def test_tigrbl_api_app_instantiation_sets_composed_state() -> None:
29
+ api = ThetaApi(engine=mem(async_=False))
30
+
31
+ class ThetaApp(TigrblApp):
32
+ APIS = (api,)
33
+
34
+ app = ThetaApp(engine=mem(async_=False))
35
+
36
+ api_dir = dir(api)
37
+ app_dir = dir(app)
38
+
39
+ assert "models" in api_dir
40
+ assert api.models["Theta"] is Theta
41
+ assert "apis" in app_dir
42
+ assert app.apis == [api]
@@ -0,0 +1,38 @@
1
+ import pytest
2
+
3
+ from tigrbl import Base, TigrblApi, TigrblApp
4
+ from tigrbl.orm.mixins import GUIDPk
5
+ from tigrbl.specs import F, IO, S, acol
6
+ from tigrbl.types import Mapped, String
7
+
8
+
9
+ class Zeta(Base, GUIDPk):
10
+ __tablename__ = "zeta_api_app_decl"
11
+ __allow_unmapped__ = True
12
+
13
+ name: Mapped[str] = acol(
14
+ storage=S(type_=String, nullable=False),
15
+ field=F(py_type=str),
16
+ io=IO(in_verbs=("create",), out_verbs=("read", "list")),
17
+ )
18
+
19
+ __tigrbl_cols__ = {"id": GUIDPk.id, "name": name}
20
+
21
+
22
+ class ZetaApi(TigrblApi):
23
+ MODELS = (Zeta,)
24
+
25
+
26
+ class ZetaApp(TigrblApp):
27
+ APIS = (ZetaApi,)
28
+
29
+
30
+ @pytest.mark.unit
31
+ def test_tigrbl_api_app_subclass_declares_composition() -> None:
32
+ api_dir = dir(ZetaApi)
33
+ app_dir = dir(ZetaApp)
34
+
35
+ assert "MODELS" in api_dir
36
+ assert "APIS" in app_dir
37
+ assert ZetaApi.MODELS == (Zeta,)
38
+ assert ZetaApp.APIS == (ZetaApi,)
@@ -0,0 +1,47 @@
1
+ import pytest
2
+ from fastapi import Security
3
+ from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
4
+
5
+ from tigrbl import TigrblApi
6
+ from tigrbl.engine.shortcuts import mem
7
+
8
+
9
+ def _auth_dependency(
10
+ credentials: HTTPAuthorizationCredentials = Security(HTTPBearer()),
11
+ ) -> HTTPAuthorizationCredentials:
12
+ return credentials
13
+
14
+
15
+ @pytest.mark.unit
16
+ def test_tigrbl_api_constructor_configuration_sets_prefixes() -> None:
17
+ def sample_hook() -> None:
18
+ return None
19
+
20
+ api = TigrblApi(
21
+ engine=mem(async_=False),
22
+ jsonrpc_prefix="/rpcx",
23
+ system_prefix="/systemx",
24
+ api_hooks={"*": {"pre": [sample_hook]}},
25
+ )
26
+
27
+ api_dir = dir(api)
28
+
29
+ assert "jsonrpc_prefix" in api_dir
30
+ assert "system_prefix" in api_dir
31
+ assert "_api_hooks_map" in api_dir
32
+ assert api.jsonrpc_prefix == "/rpcx"
33
+ assert api.system_prefix == "/systemx"
34
+ assert api._api_hooks_map == {"*": {"pre": [sample_hook]}}
35
+
36
+
37
+ @pytest.mark.unit
38
+ def test_tigrbl_api_post_instantiation_set_auth_updates_state() -> None:
39
+ api = TigrblApi(engine=mem(async_=False))
40
+ api.set_auth(authn=_auth_dependency, allow_anon=False)
41
+
42
+ api_dir = dir(api)
43
+
44
+ assert "_authn" in api_dir
45
+ assert "_allow_anon" in api_dir
46
+ assert api._authn is _auth_dependency
47
+ assert api._allow_anon is False
@@ -0,0 +1,37 @@
1
+ import pytest
2
+
3
+ from tigrbl import Base, TigrblApi
4
+ from tigrbl.engine.shortcuts import mem
5
+ from tigrbl.orm.mixins import GUIDPk
6
+ from tigrbl.specs import F, IO, S, acol
7
+ from tigrbl.types import Mapped, String
8
+
9
+
10
+ class Widget(Base, GUIDPk):
11
+ __tablename__ = "widgets_api_inst"
12
+ __allow_unmapped__ = True
13
+
14
+ name: Mapped[str] = acol(
15
+ storage=S(type_=String, nullable=False),
16
+ field=F(py_type=str),
17
+ io=IO(in_verbs=("create",), out_verbs=("read", "list")),
18
+ )
19
+
20
+ __tigrbl_cols__ = {"id": GUIDPk.id, "name": name}
21
+
22
+
23
+ class WidgetApi(TigrblApi):
24
+ MODELS = (Widget,)
25
+
26
+
27
+ @pytest.mark.unit
28
+ def test_tigrbl_api_instantiation_sets_containers() -> None:
29
+ api = WidgetApi(engine=mem(async_=False))
30
+ api_dir = dir(api)
31
+
32
+ assert "models" in api_dir
33
+ assert "routers" in api_dir
34
+ assert "schemas" in api_dir
35
+ assert "jsonrpc_prefix" in api_dir
36
+ assert "system_prefix" in api_dir
37
+ assert api.models["Widget"] is Widget
@@ -0,0 +1,37 @@
1
+ import pytest
2
+
3
+ from tigrbl import Base, TigrblApi
4
+ from tigrbl.orm.mixins import GUIDPk
5
+ from tigrbl.specs import F, IO, S, acol
6
+ from tigrbl.types import Mapped, String
7
+
8
+
9
+ class Widget(Base, GUIDPk):
10
+ __tablename__ = "widgets_api_decl"
11
+ __allow_unmapped__ = True
12
+
13
+ name: Mapped[str] = acol(
14
+ storage=S(type_=String, nullable=False),
15
+ field=F(py_type=str),
16
+ io=IO(in_verbs=("create",), out_verbs=("read", "list")),
17
+ )
18
+
19
+ __tigrbl_cols__ = {"id": GUIDPk.id, "name": name}
20
+
21
+
22
+ class WidgetApi(TigrblApi):
23
+ PREFIX = "/widgets"
24
+ TAGS = ("widgets",)
25
+ MODELS = (Widget,)
26
+
27
+
28
+ @pytest.mark.unit
29
+ def test_tigrbl_api_subclass_declares_metadata() -> None:
30
+ class_dir = dir(WidgetApi)
31
+
32
+ assert "MODELS" in class_dir
33
+ assert "TAGS" in class_dir
34
+ assert "PREFIX" in class_dir
35
+ assert WidgetApi.MODELS == (Widget,)
36
+ assert WidgetApi.TAGS == ("widgets",)
37
+ assert WidgetApi.PREFIX == "/widgets"
@@ -0,0 +1,47 @@
1
+ import pytest
2
+ from fastapi import Security
3
+ from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
4
+
5
+ from tigrbl import TigrblApp
6
+ from tigrbl.engine.shortcuts import mem
7
+
8
+
9
+ def _auth_dependency(
10
+ credentials: HTTPAuthorizationCredentials = Security(HTTPBearer()),
11
+ ) -> HTTPAuthorizationCredentials:
12
+ return credentials
13
+
14
+
15
+ @pytest.mark.unit
16
+ def test_tigrbl_app_constructor_configuration_sets_metadata() -> None:
17
+ app = TigrblApp(
18
+ engine=mem(async_=False),
19
+ title="Configured App",
20
+ version="2.3.4",
21
+ jsonrpc_prefix="/rpcx",
22
+ system_prefix="/systemx",
23
+ )
24
+
25
+ app_dir = dir(app)
26
+
27
+ assert "TITLE" in app_dir
28
+ assert "VERSION" in app_dir
29
+ assert "jsonrpc_prefix" in app_dir
30
+ assert "system_prefix" in app_dir
31
+ assert app.TITLE == "Configured App"
32
+ assert app.VERSION == "2.3.4"
33
+ assert app.jsonrpc_prefix == "/rpcx"
34
+ assert app.system_prefix == "/systemx"
35
+
36
+
37
+ @pytest.mark.unit
38
+ def test_tigrbl_app_post_instantiation_set_auth_updates_state() -> None:
39
+ app = TigrblApp(engine=mem(async_=False))
40
+ app.set_auth(authn=_auth_dependency, allow_anon=False)
41
+
42
+ app_dir = dir(app)
43
+
44
+ assert "_authn" in app_dir
45
+ assert "_allow_anon" in app_dir
46
+ assert app._authn is _auth_dependency
47
+ assert app._allow_anon is False
@@ -0,0 +1,37 @@
1
+ import pytest
2
+
3
+ from tigrbl import Base, TigrblApp
4
+ from tigrbl.engine.shortcuts import mem
5
+ from tigrbl.orm.mixins import GUIDPk
6
+ from tigrbl.specs import F, IO, S, acol
7
+ from tigrbl.types import Mapped, String
8
+
9
+
10
+ class Widget(Base, GUIDPk):
11
+ __tablename__ = "widgets_app_inst"
12
+ __allow_unmapped__ = True
13
+
14
+ name: Mapped[str] = acol(
15
+ storage=S(type_=String, nullable=False),
16
+ field=F(py_type=str),
17
+ io=IO(in_verbs=("create",), out_verbs=("read", "list")),
18
+ )
19
+
20
+ __tigrbl_cols__ = {"id": GUIDPk.id, "name": name}
21
+
22
+
23
+ class WidgetApp(TigrblApp):
24
+ MODELS = (Widget,)
25
+
26
+
27
+ @pytest.mark.unit
28
+ def test_tigrbl_app_instantiation_sets_containers() -> None:
29
+ app = WidgetApp(engine=mem(async_=False))
30
+ app_dir = dir(app)
31
+
32
+ assert "models" in app_dir
33
+ assert "routers" in app_dir
34
+ assert "schemas" in app_dir
35
+ assert "jsonrpc_prefix" in app_dir
36
+ assert "system_prefix" in app_dir
37
+ assert app.models["Widget"] is Widget
@@ -0,0 +1,37 @@
1
+ import pytest
2
+
3
+ from tigrbl import Base, TigrblApp
4
+ from tigrbl.orm.mixins import GUIDPk
5
+ from tigrbl.specs import F, IO, S, acol
6
+ from tigrbl.types import Mapped, String
7
+
8
+
9
+ class Widget(Base, GUIDPk):
10
+ __tablename__ = "widgets_app_decl"
11
+ __allow_unmapped__ = True
12
+
13
+ name: Mapped[str] = acol(
14
+ storage=S(type_=String, nullable=False),
15
+ field=F(py_type=str),
16
+ io=IO(in_verbs=("create",), out_verbs=("read", "list")),
17
+ )
18
+
19
+ __tigrbl_cols__ = {"id": GUIDPk.id, "name": name}
20
+
21
+
22
+ class WidgetApp(TigrblApp):
23
+ TITLE = "Widget App"
24
+ VERSION = "1.0.0"
25
+ MODELS = (Widget,)
26
+
27
+
28
+ @pytest.mark.unit
29
+ def test_tigrbl_app_subclass_declares_metadata() -> None:
30
+ class_dir = dir(WidgetApp)
31
+
32
+ assert "TITLE" in class_dir
33
+ assert "VERSION" in class_dir
34
+ assert "MODELS" in class_dir
35
+ assert WidgetApp.TITLE == "Widget App"
36
+ assert WidgetApp.VERSION == "1.0.0"
37
+ assert WidgetApp.MODELS == (Widget,)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tigrbl-tests
3
- Version: 0.3.2.dev1
3
+ Version: 0.3.3
4
4
  Summary: Test suite and fixtures for the Tigrbl framework.
5
5
  License-Expression: Apache-2.0
6
6
  License-File: LICENSE
@@ -30,6 +30,7 @@ Requires-Dist: python-dotenv
30
30
  Requires-Dist: requests (>=2.32.3)
31
31
  Requires-Dist: ruff (>=0.9.9)
32
32
  Requires-Dist: tigrbl
33
+ Requires-Dist: tigrbl_client
33
34
  Description-Content-Type: text/markdown
34
35
 
35
36
  ![Tigrbl Logo](https://github.com/swarmauri/swarmauri-sdk/blob/a170683ecda8ca1c4f912c966d4499649ffb8224/assets/tigrbl.brand.theme.svg)
@@ -100,4 +101,18 @@ Use pytest selectors to focus on specific suites:
100
101
  pytest standards/tigrbl_tests/tests/unit
101
102
  ```
102
103
 
104
+ ## Examples Curriculum 📚
105
+
106
+ The `examples/` directory contains downstream-facing pytest lessons that
107
+ demonstrate how to implement Tigrbl in real applications. These lessons are
108
+ organized as a multi-module curriculum with uvicorn-backed usage examples and
109
+ system diagnostics validation. See the full curriculum plan for the learning
110
+ sequence and module descriptions.[^tigrbl-examples]
111
+ Run the examples from the `pkgs` directory:
112
+
113
+ ```bash
114
+ uv run --package tigrbl-tests --directory standards/tigrbl_tests pytest examples
115
+ ```
116
+
117
+ [^tigrbl-examples]: examples/README.md
103
118
 
@@ -43,6 +43,13 @@ tests/i9n/test_schema_ctx_spec_integration.py,sha256=gQB0YdqjGfkoflcbzdkrVLouefh
43
43
  tests/i9n/test_sqlite_attachments.py,sha256=Dvv2Exhbs7P47kOLQjg5TyruXYJqZIQdnqZvoitVy5Y,1216
44
44
  tests/i9n/test_storage_spec_integration.py,sha256=hHxn0odPOLSIXY9Rx5ZwgOZS5DPFtutIhw5JYlEFmPg,3918
45
45
  tests/i9n/test_symmetry_parity.py,sha256=7b2iVc0e7I84bOLgyWvFA3Zg-maGOGTLtTcX5gCX1Zg,919
46
+ tests/i9n/test_tigrbl_api_app_usage_uvicorn.py,sha256=FaE81foEFgZ8w9son4m4XSxYLV9-Ss034WkJjgpPlDU,2692
47
+ tests/i9n/test_tigrbl_api_usage_uvicorn.py,sha256=9ThMl7YsFgRZUU7Lk4R-1c5NNqn2YcDeOL6L9w37WZI,2933
48
+ tests/i9n/test_tigrbl_api_uvicorn.py,sha256=_3_z_OnKhL7JdSPzxCv7Bq0bFmm9GOyiAmp3TroRdm4,1764
49
+ tests/i9n/test_tigrbl_app_include_api_uvicorn.py,sha256=LzjwmchWqgHexYt7ebA7aPj1jM29J8q-lPCKoXp7dhg,4396
50
+ tests/i9n/test_tigrbl_app_multi_api_uvicorn.py,sha256=sFDkspBHhYSLfmMsXgzHKBIHFFpYuo7Znj0URr3bowE,2270
51
+ tests/i9n/test_tigrbl_app_usage_uvicorn.py,sha256=qUfSOk5ye509JwEJl9i6ViXtKA-LI0JAV8IdWCU_Mgw,2889
52
+ tests/i9n/test_tigrbl_app_uvicorn.py,sha256=a06Zypy1kA5glvyHb3iaBmBJso7WGoT0ZmwHu0Bv1iY,1572
46
53
  tests/i9n/test_v3_bulk_rest_endpoints.py,sha256=3nNXJ6l3Ady4wi5UnEVmrw76crw9Ur7Ys53wq6RwBAI,3928
47
54
  tests/i9n/test_v3_default_rest_ops.py,sha256=X_wJB7bmCaK6c9U6oPZlgQmRXdF2PeuYCqQYboJFxDo,4752
48
55
  tests/i9n/test_v3_default_rpc_ops.py,sha256=mnDi5DNP6gKIi31Uin7rLlxvyY47u0U_2HvK61XqpB8,7552
@@ -112,7 +119,10 @@ tests/unit/test_hybrid_session_run_sync.py,sha256=6zidxhpUNfE81xtS5kzQYPn36SinMP
112
119
  tests/unit/test_in_tx.py,sha256=Bzn5iTkJGUpXG8J0Uduj9b8OThgPGXl-jEKfmWyWkPY,549
113
120
  tests/unit/test_include_model_columns_namespace.py,sha256=IQhuCZ168NanMHiTlhs_B1seQwYIkaq3WaBqiWkC67c,591
114
121
  tests/unit/test_include_models_base_prefix.py,sha256=z8Rvl3bMjf8kMLpqLn7Ia08dR1KQORrHzBIrubyEq8s,813
122
+ tests/unit/test_initialize_async_task.py,sha256=Q2VNj6WR9DAN_niM3g1r-K8bDkRcFB4whrwG1gaviBg,560
115
123
  tests/unit/test_initialize_cross_ddl.py,sha256=kaku7ZVX68rDAxNBbZRaVyXSiw4Op6Jb5ex-eNHKmjU,689
124
+ tests/unit/test_initialize_mixed_engines.py,sha256=kQPUAsfOUwcbz3FPMaMuDQpyBIG4q_DvuC9Vye7mIvo,1017
125
+ tests/unit/test_initialize_task_schedule.py,sha256=OLMjubgyOxglRv2vtB6gicEK3_6WuYasZRftIPJ5sMw,555
116
126
  tests/unit/test_io_spec_attributes.py,sha256=Da9u7yjw-KVbHF1tpQhUvl7HSfooe9L1k5y-ceRVsaM,5662
117
127
  tests/unit/test_iospec_attributes.py,sha256=Xl5jOHBVJH4gmZqznaVrO3trqnwWYtztwRIryAnKsJo,2254
118
128
  tests/unit/test_iospec_effects.py,sha256=frPnZNX3_dVqmG_b4rUCrrJRzGQ6JzHKCZm0GsM-csA,5687
@@ -163,7 +173,7 @@ tests/unit/test_schemas_binding.py,sha256=TifSTTDJwN18ewb3dYTczjD7h5oWfLEh1MwPV_
163
173
  tests/unit/test_security_per_route.py,sha256=b_6KKWDaTVWAO5XTfcNIG2pxMoNhZOcPBVKlz3Bz5KQ,1512
164
174
  tests/unit/test_should_wire_canonical.py,sha256=ANlw1UFm77UI5LDaVWZZmBHSh1kUiJzY4hm1blLW158,1662
165
175
  tests/unit/test_spec_api.py,sha256=LUiDDLmW9oBZQXvT-29FjX_ZHXy9FPN13-f4gQ6i5tI,1130
166
- tests/unit/test_spec_app.py,sha256=6bppvI1orwKaoC7MNjByKNhjTgxnc4liOzeHGBHae5s,761
176
+ tests/unit/test_spec_app.py,sha256=fdEMeAlepypgmqCxsQawfEOMSE5d3fia1LOwsxC95t8,753
167
177
  tests/unit/test_spec_column.py,sha256=ja3IDQi5U2WtTUHRd4deniyZ6EZkwz3mylKH8CLgf7I,867
168
178
  tests/unit/test_spec_engine.py,sha256=D2pU-NCGMh0gsX-PhL5ZkJyTeO09NkATSK8WY-ynHaw,1938
169
179
  tests/unit/test_spec_field.py,sha256=68X7qtA3H2P9LQKEJTW14YVPQJ-cENBrXyVmMBwj430,491
@@ -181,8 +191,19 @@ tests/unit/test_sys_tx_async_begin.py,sha256=q0apjmNnxZcGqCkpD8I0uPFsLPxJN_1ZBjF
181
191
  tests/unit/test_sys_tx_begin.py,sha256=ElKZIxeK98O60Eo3uMGx8tuoP9nFlFYpYja5eLCears,1429
182
192
  tests/unit/test_sys_tx_commit.py,sha256=HOTCtKusz7iGr-PkW_gcId1NzpGMRXBUxUydpd5PbHc,1813
183
193
  tests/unit/test_table_base_exports.py,sha256=8GcLU0vY_j0_d5Y7PqQKMgz0L4rD3iPXMBn1u6dPz6E,666
184
- tests/unit/test_table_collect_spec.py,sha256=SFUcdZpVbUFxQkms8v1_BZs4sgYEYiTZPSkD4UctJDo,1050
194
+ tests/unit/test_table_collect_spec.py,sha256=Gs_QnfKb9rYILHMv1q7w43gUvvS6UnpXy8vsI74IQTE,1050
185
195
  tests/unit/test_table_columns_namespace.py,sha256=IVtpdg5dRHqyqc6WW0hndLJ7uNhnjivJ0GOJLebl_1w,581
196
+ tests/unit/test_table_namespace_init.py,sha256=53CnG1hMRUBD_KAU50FpKvUilk4QhBT0qbY2ECkP9Fc,945
197
+ tests/unit/test_table_namespace_isolation.py,sha256=6pWz1yLdrPNoOGzOjwe9_GBRHosrOaNSshRscq9ytIs,1394
198
+ tests/unit/test_tigrbl_api_app_configuration.py,sha256=aa0lXbJJnycUIZN3DGEgYmPIENRZi5-K-xHVEDiA6jQ,2269
199
+ tests/unit/test_tigrbl_api_app_instantiation.py,sha256=SlmotlemN4G4NNsY3UCXYF0shIClgSadbBmqTT2jbWM,1016
200
+ tests/unit/test_tigrbl_api_app_subclass_definition.py,sha256=yMoQZAmQqzK6V0fMUeYXGPx1nv7zFJ3wYc1E7-awY_I,886
201
+ tests/unit/test_tigrbl_api_configuration.py,sha256=O1J2FCbrr9acujY976gsGgpfRLoabfO_esYxwtcyRkU,1312
202
+ tests/unit/test_tigrbl_api_instantiation.py,sha256=w4edFjjIezW-cmBw_N4VHpLR0Z38zwRGvAzCMm5i4fw,960
203
+ tests/unit/test_tigrbl_api_subclass_definition.py,sha256=VL5aK7dgasH2TVlcKqoyEdmlNxuUbguJy1T8dqirR3Q,935
204
+ tests/unit/test_tigrbl_app_configuration.py,sha256=78ygfvj86Yv3YBOSnqBAm1fnPxNmTqg728owWnw_U2g,1303
205
+ tests/unit/test_tigrbl_app_instantiation.py,sha256=QyNzcwD7uuunrpjIcFMFuisPJo73D06yaDnVtEf2Hh0,960
206
+ tests/unit/test_tigrbl_app_subclass_definition.py,sha256=loeowe-FikGpGpiNIkBM8KgQgLSZv1mFCbut_stHFg4,935
186
207
  tests/unit/test_v3_favicon_endpoint.py,sha256=mlquQ6ZF83JeJDdtxS2p7kTnBeeNdMNDWhfYlhFssjc,485
187
208
  tests/unit/test_v3_healthz_endpoint.py,sha256=6WCjROm_3bQg5H__t7Z0BkVghHrpopgpvXTQ_iJKJpw,988
188
209
  tests/unit/test_v3_op_alias.py,sha256=lSb8xpA9Asmaz7VefFDJLwbNBHP_mN4hSVJr1NVvOI4,2353
@@ -190,7 +211,7 @@ tests/unit/test_v3_op_ctx_attributes.py,sha256=ygRtYSYYtDFCTIRVHX2DFYTliP15NcmVi
190
211
  tests/unit/test_v3_schemas_and_decorators.py,sha256=1wmg7pmzQNjjbQPRjO8gkCM6BQpMRbeO2jmtDMSYXLU,3818
191
212
  tests/unit/test_v3_storage_spec_attributes.py,sha256=iXpjD4GlLddhJ_jC2veND2s6usXdOm-S9sKKfQl9XK0,6540
192
213
  tests/unit/test_verbosity.py,sha256=92dIDc-LBIzjR1d90aL077R29rYJztibKYQGTjI6G68,1945
193
- tigrbl_tests-0.3.2.dev1.dist-info/METADATA,sha256=13f3TinTYP6fvcN7lqFmDg5dEkbs8C6ktVZiJyoz8FM,3300
194
- tigrbl_tests-0.3.2.dev1.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
195
- tigrbl_tests-0.3.2.dev1.dist-info/licenses/LICENSE,sha256=djUXOlCxLVszShEpZXshZ7v33G-2qIC_j9KXpWKZSzQ,11359
196
- tigrbl_tests-0.3.2.dev1.dist-info/RECORD,,
214
+ tigrbl_tests-0.3.3.dist-info/METADATA,sha256=8KuBCcu2jXkwVup247jACRJHzp_qO00-6LrQP6kB_4g,3887
215
+ tigrbl_tests-0.3.3.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
216
+ tigrbl_tests-0.3.3.dist-info/licenses/LICENSE,sha256=djUXOlCxLVszShEpZXshZ7v33G-2qIC_j9KXpWKZSzQ,11359
217
+ tigrbl_tests-0.3.3.dist-info/RECORD,,