@@ -349,6 +349,90 @@ def test_init_interactive_cleans_up_on_failure(
349349 ),
350350 ]
351351
352+ @patch ("fastapi_fastkit.cli.subprocess.run" )
353+ @patch ("fastapi_fastkit.backend.package_managers.uv_manager.UvManager.is_available" )
354+ def test_domain_starter_dockerfile_targets_src_app_main (
355+ self ,
356+ mock_uv_available : MagicMock ,
357+ mock_subprocess : MagicMock ,
358+ temp_dir : str ,
359+ ) -> None :
360+ """Regression for Codex P1 on PR #55.
361+
362+ domain-starter ships ``src/app/main.py``; the generated Dockerfile's
363+ ``CMD`` must target ``src.app.main:app``, not the legacy default
364+ ``src.main:app`` — otherwise the container fails to boot.
365+ """
366+ # given
367+ os .chdir (temp_dir )
368+ project_name = "docker-domain-starter"
369+ mock_uv_available .return_value = True
370+
371+ def _mock_subprocess (* args : Any , ** kwargs : Any ) -> MagicMock :
372+ if args and "venv" in str (args [0 ]):
373+ venv_path = Path (temp_dir ) / project_name / ".venv"
374+ venv_path .mkdir (parents = True , exist_ok = True )
375+ bin_dir = "Scripts" if os .name == "nt" else "bin"
376+ (venv_path / bin_dir ).mkdir (exist_ok = True )
377+ mock_result = MagicMock ()
378+ mock_result .returncode = 0
379+ return mock_result
380+
381+ mock_subprocess .side_effect = _mock_subprocess
382+
383+ # when — pick the domain-starter preset and Docker-only deployment.
384+ result = self .runner .invoke (
385+ fastkit_cli ,
386+ ["init" , "--interactive" ],
387+ input = "\n " .join (
388+ [
389+ project_name ,
390+ "Docker Tester" ,
391+ "docker@test.com" ,
392+ "Domain-starter Docker regression" ,
393+ "4" , # Architecture preset: domain-starter
394+ "6" , # Database: None (last option)
395+ "5" , # Authentication: None (last option)
396+ "3" , # Background Tasks: None (last option)
397+ "2" , # Caching: None (last option)
398+ "4" , # Monitoring: None (last option)
399+ "1" , # Testing: Basic
400+ "" , # Utilities: skip
401+ "1" , # Deployment: Docker
402+ "2" , # Package manager: uv
403+ "" , # Custom packages: skip
404+ "Y" , # Proceed
405+ "Y" , # Create project folder
406+ ]
407+ ),
408+ )
409+
410+ # then
411+ project_path = Path (temp_dir ) / project_name
412+ assert project_path .exists (), (
413+ f"project not created. exit_code={ result .exit_code } \n "
414+ f"output:\n { result .output } "
415+ )
416+
417+ dockerfile = project_path / "Dockerfile"
418+ assert dockerfile .exists (), (
419+ f"Dockerfile missing despite Docker deployment selection.\n "
420+ f"output:\n { result .output } "
421+ )
422+
423+ content = dockerfile .read_text ()
424+ assert "src.app.main:app" in content , (
425+ "Domain-starter Dockerfile must target src.app.main:app; "
426+ "the CMD currently reads:\n "
427+ + "\n " .join (
428+ line for line in content .splitlines () if line .startswith ("CMD " )
429+ )
430+ )
431+ assert '"src.main:app"' not in content , (
432+ "Bogus legacy default src.main:app leaked into the generated "
433+ "Dockerfile despite the preset override."
434+ )
435+
352436 @pytest .mark .parametrize (
353437 "preset_choice,suffix,base_template_name,main_relpath,db_relpath,regenerates_main" ,
354438 _PRESET_LAYOUT_CASES ,
0 commit comments