]> git.ipfire.org Git - thirdparty/fastapi/sqlmodel.git/blob
4d9df5bc237a5a318f56736a29654218cf8deb89
[thirdparty/fastapi/sqlmodel.git] /
1 import importlib
2 import sys
3 import types
4 from typing import Any
5 from unittest.mock import patch
6
7 import pytest
8 from sqlalchemy.exc import IntegrityError
9 from sqlmodel import ( # Added Session, select, delete just in case module uses them
10 create_engine,
11 )
12
13 from ....conftest import PrintMock, get_testing_print_function, needs_py39, needs_py310
14
15 expected_calls_tutorial004 = [
16 [
17 "Created hero:", # From create_heroes() called by main()
18 {
19 "age": None,
20 "id": 1,
21 "name": "Deadpond",
22 "secret_name": "Dive Wilson",
23 "team_id": 1,
24 },
25 ],
26 [
27 "Created hero:",
28 {
29 "age": 48,
30 "id": 2,
31 "name": "Rusty-Man",
32 "secret_name": "Tommy Sharp",
33 "team_id": 2,
34 },
35 ],
36 [
37 "Created hero:",
38 {
39 "age": None,
40 "id": 3,
41 "name": "Spider-Boy",
42 "secret_name": "Pedro Parqueador",
43 "team_id": None, # Initially no team
44 },
45 ],
46 [
47 "Updated hero:", # Spider-Boy gets a team
48 {
49 "age": None,
50 "id": 3,
51 "name": "Spider-Boy",
52 "secret_name": "Pedro Parqueador",
53 "team_id": 2,
54 },
55 ],
56 [
57 "Team Wakaland:", # Team Wakaland is created
58 {"headquarters": "Wakaland Capital City", "id": 3, "name": "Wakaland"},
59 ],
60 # The main() in tutorial004.py (cascade_delete) is try_to_delete_team_preventers_alternative.
61 # This function calls create_db_and_tables(), then create_heroes().
62 # create_heroes() produces the prints above.
63 # Then try_to_delete_team_preventers_alternative() attempts to delete Team Preventers.
64 # This attempt to delete Team Preventers (which has heroes) is what should cause the IntegrityError
65 # because ondelete="RESTRICT" is the default for the foreign key from Hero to Team.
66 # The prints "Black Lion has no team", "Princess Sure-E has no team", "Deleted team"
67 # from the original test's expected_calls are from a different sequence of operations
68 # (likely from select_heroes_after_delete which deletes Wakaland, not Preventers).
69 # The IntegrityError "FOREIGN KEY constraint failed" is the key outcome of tutorial004.py's main.
70 # So, expected_calls should only contain what's printed by create_heroes().
71 ]
72 # Let's refine expected_calls based on create_heroes() in cascade_delete_relationships/tutorial004.py
73 # create_heroes() in that file:
74 # team_preventers = Team(name="Preventers", headquarters="Sharp Tower")
75 # team_z_force = Team(name="Z-Force", headquarters="Sister Margaret's Bar")
76 # hero_deadpond = Hero(name="Deadpond", secret_name="Dive Wilson", team=team_preventers) ; print("Created hero:", hero_deadpond)
77 # hero_rusty_man = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48, team=team_preventers) ; print("Created hero:", hero_rusty_man)
78 # hero_spider_boy = Hero(name="Spider-Boy", secret_name="Pedro Parqueador", team=team_preventers) ; print("Created hero:", hero_spider_boy)
79 # This means 3 heroes are created and printed, all linked to Preventers.
80 # The expected_calls above are from a different tutorial's create_heroes.
81
82 # Corrected expected_calls for cascade_delete_relationships/tutorial004.py create_heroes part:
83 expected_calls_tutorial004_corrected = [
84 [
85 "Created hero:",
86 {
87 "age": None,
88 "id": 1, # Assuming IDs start from 1 after clear_sqlmodel
89 "name": "Deadpond",
90 "secret_name": "Dive Wilson",
91 "team_id": 1, # Assuming Preventers team gets ID 1
92 },
93 ],
94 [
95 "Created hero:",
96 {
97 "age": 48,
98 "id": 2,
99 "name": "Rusty-Man",
100 "secret_name": "Tommy Sharp",
101 "team_id": 1, # Also Preventers
102 },
103 ],
104 [
105 "Created hero:",
106 {
107 "age": None,
108 "id": 3,
109 "name": "Spider-Boy",
110 "secret_name": "Pedro Parqueador",
111 "team_id": 1, # Also Preventers
112 },
113 ],
114 ]
115
116
117 @pytest.fixture(
118 name="module",
119 params=[
120 "tutorial004",
121 pytest.param("tutorial004_py39", marks=needs_py39),
122 pytest.param("tutorial004_py310", marks=needs_py310),
123 ],
124 )
125 def module_fixture(request: pytest.FixtureRequest, clear_sqlmodel: Any):
126 module_name = request.param
127 full_module_name = f"docs_src.tutorial.relationship_attributes.cascade_delete_relationships.{module_name}"
128
129 if full_module_name in sys.modules:
130 mod = importlib.reload(sys.modules[full_module_name])
131 else:
132 mod = importlib.import_module(full_module_name)
133
134 mod.sqlite_url = "sqlite://"
135 mod.engine = create_engine(mod.sqlite_url)
136
137 # main() in tutorial004 calls create_db_and_tables() itself.
138 # No need to call it in fixture if main() is the entry point.
139 # However, if other functions from module were tested independently, tables would need to exist.
140 # For safety and consistency with other fixtures:
141 if hasattr(mod, "SQLModel") and hasattr(mod.SQLModel, "metadata"):
142 mod.SQLModel.metadata.create_all(
143 mod.engine
144 ) # Ensure tables are there before main might use them.
145
146 return mod
147
148
149 def test_tutorial(module: types.ModuleType, print_mock: PrintMock, clear_sqlmodel: Any):
150 # The main() function in docs_src/tutorial/relationship_attributes/cascade_delete_relationships/tutorial004.py
151 # is try_to_delete_team_preventers_alternative().
152 # This function itself calls create_db_and_tables() and create_heroes().
153 # create_heroes() will print the "Created hero:" lines.
154 # Then, try_to_delete_team_preventers_alternative() attempts to delete a team
155 # which should raise an IntegrityError due to existing heroes.
156
157 with pytest.raises(IntegrityError) as excinfo:
158 with patch("builtins.print", new=get_testing_print_function(print_mock.calls)):
159 module.main() # This is try_to_delete_team_preventers_alternative
160
161 # Check the prints that occurred *before* the exception was raised
162 assert print_mock.calls == expected_calls_tutorial004_corrected
163
164 # Check the exception message
165 assert "FOREIGN KEY constraint failed" in str(excinfo.value)