1010
1111class TestAdminSite :
1212 """Test AdminSite functionality."""
13-
13+
1414 def test_admin_site_creation (self , admin_config : AdminConfig ):
1515 """Test AdminSite can be created."""
1616 site = AdminSite (admin_config )
1717 assert site .config == admin_config
1818 assert not site ._initialized
19-
19+
2020 def test_model_registration (self , admin_site : AdminSite ):
2121 """Test model registration works."""
2222 from tests .conftest import TestModel , TestUser
2323 assert admin_site .is_registered (TestModel )
2424 assert not admin_site .is_registered (TestUser )
25-
25+
2626 def test_get_registered_models (self , admin_site : AdminSite ):
2727 """Test getting registered models."""
2828 models = admin_site .get_registered_models ()
2929 assert len (models ) == 1
30- # Check that TestModel is registered (by class name since import paths may differ)
3130 model_names = [model .__name__ for model in models .keys ()]
3231 assert "TestModel" in model_names
3332
3433
3534class TestAdminConfig :
3635 """Test AdminConfig functionality."""
37-
36+
3837 def test_config_validation (self , test_db : str ):
3938 """Test config validation."""
4039 from tests .conftest import TestUser
41- # Valid config should work
4240 config = AdminConfig (
4341 database_url = test_db ,
4442 secret_key = "test-key" ,
@@ -47,7 +45,7 @@ def test_config_validation(self, test_db: str):
4745 assert config .database_url == test_db
4846 assert config .is_sqlite
4947 assert not config .is_postgresql
50-
48+
5149 def test_invalid_config (self ):
5250 """Test invalid config raises errors."""
5351 from tests .conftest import TestUser
@@ -57,7 +55,7 @@ def test_invalid_config(self):
5755 secret_key = "test-key" ,
5856 user_model = TestUser
5957 )
60-
58+
6159 with pytest .raises (ValueError , match = "secret_key is required" ):
6260 AdminConfig (
6361 database_url = "sqlite:///test.db" ,
@@ -68,47 +66,46 @@ def test_invalid_config(self):
6866
6967class TestModelAdmin :
7068 """Test ModelAdmin functionality."""
71-
69+
7270 def test_model_admin_creation (self ):
7371 """Test ModelAdmin can be created."""
7472 from tests .conftest import TestModel
7573 admin = ModelAdmin (TestModel )
7674 assert admin .model == TestModel
77-
75+
7876 def test_list_display (self ):
7977 """Test list_display configuration."""
8078 from tests .conftest import TestModel
8179 admin = ModelAdmin (TestModel )
82-
83- # Should return default columns if not configured
80+
8481 display_fields = admin .get_list_display ()
8582 assert "id" in display_fields
8683 assert "name" in display_fields
87-
84+
8885 def test_search_fields (self ):
8986 """Test search_fields configuration."""
9087 from tests .conftest import TestModel
9188 admin = ModelAdmin (TestModel )
9289 admin .search_fields = ["name" ]
93-
90+
9491 search_fields = admin .get_search_fields ()
9592 assert search_fields == ["name" ]
9693
9794
9895class TestAdminRoutes :
9996 """Test admin route functionality."""
100-
97+
10198 def test_dashboard_requires_auth (self , client : TestClient ):
10299 """Test dashboard requires authentication."""
103100 response = client .get ("/admin/" )
104101 assert response .status_code == 401
105-
102+
106103 def test_login_page (self , client : TestClient ):
107104 """Test login page is accessible."""
108105 response = client .get ("/admin/login" )
109106 assert response .status_code == 200
110107 assert "login" in response .text .lower ()
111-
108+
112109 def test_model_list_requires_auth (self , client : TestClient ):
113110 """Test model list requires authentication."""
114111 response = client .get ("/admin/testmodel/" )
@@ -117,24 +114,24 @@ def test_model_list_requires_auth(self, client: TestClient):
117114
118115class TestAuthentication :
119116 """Test authentication functionality."""
120-
117+
121118 def test_successful_login (self , client : TestClient , test_user ):
122119 """Test successful login."""
123120 response = client .post ("/admin/login" , data = {
124121 "username" : test_user .username ,
125122 "password" : "testpass123"
126123 })
127- assert response .status_code == 302 # Redirect after login
128-
124+ assert response .status_code == 302
125+
129126 def test_invalid_login (self , client : TestClient ):
130127 """Test invalid login credentials."""
131128 response = client .post ("/admin/login" , data = {
132129 "username" : "nonexistent" ,
133130 "password" : "wrongpass"
134131 })
135- assert response .status_code == 302 # Redirect to login with error
132+ assert response .status_code == 302
136133 assert "error=invalid_credentials" in response .headers ["location" ]
137-
134+
138135 def test_authenticated_dashboard_access (self , authenticated_client : TestClient ):
139136 """Test authenticated users can access dashboard."""
140137 response = authenticated_client .get ("/admin/" )
@@ -144,36 +141,36 @@ def test_authenticated_dashboard_access(self, authenticated_client: TestClient):
144141
145142class TestCRUDOperations :
146143 """Test CRUD operations via admin interface."""
147-
144+
148145 def test_model_list_view (self , authenticated_client : TestClient , test_objects ):
149146 """Test model list view."""
150147 response = authenticated_client .get ("/admin/testmodel/" )
151148 assert response .status_code == 200
152149 assert "Test 1" in response .text
153150 assert "Test 2" in response .text
154-
151+
155152 def test_create_form_view (self , authenticated_client : TestClient ):
156153 """Test create form view."""
157154 response = authenticated_client .get ("/admin/testmodel/create/" )
158155 assert response .status_code == 200
159156 assert "form" in response .text .lower ()
160157 assert "name" in response .text .lower ()
161-
158+
162159 def test_create_object (self , authenticated_client : TestClient ):
163160 """Test creating new object."""
164161 response = authenticated_client .post ("/admin/testmodel/create/" , data = {
165162 "name" : "New Test Object" ,
166163 "description" : "Created via test" ,
167164 "is_active" : True
168165 })
169- assert response .status_code == 302 # Redirect after create
170-
166+ assert response .status_code == 302
167+
171168 def test_edit_form_view (self , authenticated_client : TestClient , test_objects ):
172169 """Test edit form view."""
173170 response = authenticated_client .get (f"/admin/testmodel/{ test_objects [0 ].id } /" )
174171 assert response .status_code == 200
175172 assert test_objects [0 ].name in response .text
176-
173+
177174 def test_delete_confirmation (self , authenticated_client : TestClient , test_objects ):
178175 """Test delete confirmation page."""
179176 response = authenticated_client .get (f"/admin/testmodel/{ test_objects [0 ].id } /delete/" )
@@ -184,29 +181,64 @@ def test_delete_confirmation(self, authenticated_client: TestClient, test_object
184181
185182class TestSearchAndFiltering :
186183 """Test search and filtering functionality."""
187-
184+
188185 def test_search (self , authenticated_client : TestClient , test_objects ):
189186 """Test search functionality."""
190187 response = authenticated_client .get ("/admin/testmodel/?search=Test 1" )
191188 assert response .status_code == 200
192189 assert "Test 1" in response .text
193-
190+
194191 def test_filter (self , authenticated_client : TestClient , test_objects ):
195192 """Test filtering functionality."""
196193 response = authenticated_client .get ("/admin/testmodel/?is_active=true" )
197194 assert response .status_code == 200
198- # Should show active objects but not inactive ones
199195
200196
201197class TestPermissions :
202198 """Test permission system."""
203-
199+
204200 def test_superuser_permissions (self , authenticated_client : TestClient ):
205201 """Test superuser has all permissions."""
206- # Should be able to access model list
207202 response = authenticated_client .get ("/admin/testmodel/" )
208203 assert response .status_code == 200
209-
210- # Should be able to access create form
204+
211205 response = authenticated_client .get ("/admin/testmodel/create/" )
212- assert response .status_code == 200
206+ assert response .status_code == 200
207+
208+
209+ class TestForeignKeys :
210+ """Test foreign key functionality in forms and filters."""
211+
212+ def test_fk_field_renders_select_choices (self , authenticated_client : TestClient , fk_objects ):
213+ """Create form should render related model choices for FK fields."""
214+ response = authenticated_client .get ("/admin/testproduct/create/" )
215+ assert response .status_code == 200
216+ assert "category_id" in response .text
217+ assert "Hardware" in response .text
218+ assert "Software" in response .text
219+
220+ def test_fk_create_submission_persists_relation (self , authenticated_client : TestClient , db_session , fk_objects ):
221+ """Submitting FK value should be validated and persisted correctly."""
222+ from tests .conftest import TestProduct
223+
224+ hardware_id = fk_objects ["categories" ][0 ].id
225+ response = authenticated_client .post ("/admin/testproduct/create/" , data = {
226+ "name" : "Mouse" ,
227+ "category_id" : str (hardware_id ),
228+ "is_active" : "true" ,
229+ })
230+
231+ assert response .status_code == 302
232+
233+ created = db_session .query (TestProduct ).filter (TestProduct .name == "Mouse" ).first ()
234+ assert created is not None
235+ assert created .category_id == hardware_id
236+
237+ def test_fk_filter_applies_correctly (self , authenticated_client : TestClient , fk_objects ):
238+ """FK list filter should match rows by related ID."""
239+ software_id = fk_objects ["categories" ][1 ].id
240+ response = authenticated_client .get (f"/admin/testproduct/?category_id={ software_id } " )
241+
242+ assert response .status_code == 200
243+ assert "IDE License" in response .text
244+ assert "Keyboard" not in response .text
0 commit comments