Coverage for tests/test_cli_pyproject.py: 100%

62 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-11-11 09:18 +0000

1from pathlib import Path 1abcdefghijklmn

2from unittest.mock import patch 1abcdefghijklmn

3 

4import uvicorn 1abcdefghijklmn

5from typer.testing import CliRunner 1abcdefghijklmn

6 

7from fastapi_cli.cli import app 1abcdefghijklmn

8from tests.utils import changing_dir 1abcdefghijklmn

9 

10runner = CliRunner() 1abcdefghijklmn

11 

12assets_path = Path(__file__).parent / "assets" 1abcdefghijklmn

13 

14 

15def test_dev_with_pyproject_app_config_uses() -> None: 1abcdefghijklmn

16 with ( 1QR

17 changing_dir(assets_path / "pyproject_config"), 

18 patch.object(uvicorn, "run") as mock_run, 

19 ): 

20 result = runner.invoke(app, ["dev"]) 1QUVWXRYZ012345

21 assert result.exit_code == 0, result.output 1QUVWXRYZ012345

22 

23 assert mock_run.call_args.kwargs["app"] == "my_module:app" 1QUVWXRYZ012345

24 assert mock_run.call_args.kwargs["host"] == "127.0.0.1" 1QUVWXRYZ012345

25 assert mock_run.call_args.kwargs["port"] == 8000 1QUVWXRYZ012345

26 assert mock_run.call_args.kwargs["reload"] is True 1QUVWXRYZ012345

27 

28 assert "Using import string: my_module:app" in result.output 1QUVWXRYZ012345

29 # a configured entrypoint is not auto-discovery 

30 assert "(auto-discovered" not in result.output 1QUVWXRYZ012345

31 assert "Configuration sources:" not in result.output 1QUVWXRYZ012345

32 

33 

34def test_run_with_pyproject_app_config() -> None: 1abcdefghijklmn

35 with ( 1ST

36 changing_dir(assets_path / "pyproject_config"), 

37 patch.object(uvicorn, "run") as mock_run, 

38 ): 

39 result = runner.invoke(app, ["run"]) 1S6789T!#$%'()*

40 assert result.exit_code == 0, result.output 1S6789T!#$%'()*

41 

42 assert mock_run.call_args.kwargs["app"] == "my_module:app" 1S6789T!#$%'()*

43 assert mock_run.call_args.kwargs["host"] == "0.0.0.0" 1S6789T!#$%'()*

44 assert mock_run.call_args.kwargs["port"] == 8000 1S6789T!#$%'()*

45 assert mock_run.call_args.kwargs["reload"] is False 1S6789T!#$%'()*

46 

47 assert "Using import string: my_module:app" in result.output 1S6789T!#$%'()*

48 assert "(auto-discovered" not in result.output 1S6789T!#$%'()*

49 assert "Configuration sources:" not in result.output 1S6789T!#$%'()*

50 

51 

52def test_cli_arg_overrides_pyproject_config() -> None: 1abcdefghijklmn

53 with ( 1+,

54 changing_dir(assets_path / "pyproject_config"), 

55 patch.object(uvicorn, "run") as mock_run, 

56 ): 

57 result = runner.invoke(app, ["dev", "another_module.py"]) 1+-./:,;=?@[]^_

58 

59 assert result.exit_code == 0, result.output 1+-./:,;=?@[]^_

60 

61 assert mock_run.call_args.kwargs["app"] == "another_module:app" 1+-./:,;=?@[]^_

62 assert "Using import string: another_module:app" in result.output 1+-./:,;=?@[]^_

63 assert "Configuration sources:" not in result.output 1+-./:,;=?@[]^_

64 

65 

66def test_pyproject_app_config_invalid_format() -> None: 1abcdefghijklmn

67 test_dir = assets_path / "pyproject_invalid_config" 1CDEFGHIJKLMNOP

68 test_dir.mkdir(exist_ok=True) 1CDEFGHIJKLMNOP

69 

70 pyproject_file = test_dir / "pyproject.toml" 1CDEFGHIJKLMNOP

71 pyproject_file.write_text(""" 1CDEFGHIJKLMNOP

72[tool.fastapi] 

73entrypoint = "invalid_format_without_colon" 

74""") 

75 

76 try: 1CDEFGHIJKLMNOP

77 with changing_dir(test_dir): 1CDEFGHIJKLMNOP

78 result = runner.invoke(app, ["dev"]) 1CDEFGHIJKLMNOP

79 assert result.exit_code == 1 1CDEFGHIJKLMNOP

80 assert ( 1CDEFGHIJKLMNOP

81 "Import string must be in the format module.submodule:app_name" 

82 in result.output 

83 ) 

84 finally: 

85 pyproject_file.unlink() 1CDEFGHIJKLMNOP

86 test_dir.rmdir() 1CDEFGHIJKLMNOP

87 

88 

89def test_pyproject_validation_error() -> None: 1abcdefghijklmn

90 test_dir = assets_path / "pyproject_validation_error" 1opqrstuvwxyzAB

91 test_dir.mkdir(exist_ok=True) 1opqrstuvwxyzAB

92 

93 pyproject_file = test_dir / "pyproject.toml" 1opqrstuvwxyzAB

94 pyproject_file.write_text(""" 1opqrstuvwxyzAB

95[tool.fastapi] 

96entrypoint = 123 

97""") 

98 

99 try: 1opqrstuvwxyzAB

100 with changing_dir(test_dir): 1opqrstuvwxyzAB

101 result = runner.invoke(app, ["dev"]) 1opqrstuvwxyzAB

102 assert result.exit_code == 1 1opqrstuvwxyzAB

103 assert "Invalid configuration in pyproject.toml:" in result.output 1opqrstuvwxyzAB

104 assert "entrypoint" in result.output.lower() 1opqrstuvwxyzAB

105 finally: 

106 pyproject_file.unlink() 1opqrstuvwxyzAB

107 test_dir.rmdir() 1opqrstuvwxyzAB