Coverage for tests/test_archive.py: 100%

84 statements  

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

1from pathlib import Path 1abcdefg

2 

3import fastar 1abcdefg

4import pytest 1abcdefg

5 

6from fastapi_cloud_cli.commands.deploy.archive import archive 1abcdefg

7 

8 

9@pytest.fixture 1abcdefg

10def src_path(tmp_path: Path) -> Path: 1abcdefg

11 path = tmp_path / "source" 1abcdefg

12 path.mkdir() 1abcdefg

13 return path 1abcdefg

14 

15 

16@pytest.fixture 1abcdefg

17def dst_path(tmp_path: Path) -> Path: 1abcdefg

18 path = tmp_path / "destination" 1abcdefg

19 path.mkdir() 1abcdefg

20 return path 1abcdefg

21 

22 

23@pytest.fixture 1abcdefg

24def tar_path(tmp_path: Path) -> Path: 1abcdefg

25 return tmp_path / "archive.tar" 1abcdefg

26 

27 

28def test_archive_creates_tar_file(src_path: Path, tar_path: Path) -> None: 1abcdefg

29 (src_path / "main.py").write_text("print('hello')") 1QRSTUVW

30 (src_path / "config.json").write_text('{"key": "value"}') 1QRSTUVW

31 (src_path / "subdir").mkdir() 1QRSTUVW

32 (src_path / "subdir" / "utils.py").write_text("def helper(): pass") 1QRSTUVW

33 

34 archive(src_path, tar_path) 1QRSTUVW

35 assert tar_path.exists() 1QRSTUVW

36 

37 

38def test_archive_excludes_venv_and_similar_folders( 1abcdefg

39 src_path: Path, tar_path: Path, dst_path: Path 

40) -> None: 

41 """Should exclude .venv directory from archive.""" 

42 # the only files we want to include 

43 (src_path / "main.py").write_text("print('hello')") 1hijklmn

44 (src_path / "static").mkdir() 1hijklmn

45 (src_path / "static" / "index.html").write_text("<html></html>") 1hijklmn

46 # virtualenv 

47 (src_path / ".venv").mkdir() 1hijklmn

48 (src_path / ".venv" / "lib").mkdir() 1hijklmn

49 (src_path / ".venv" / "lib" / "package.py").write_text("# package") 1hijklmn

50 # pycache 

51 (src_path / "__pycache__").mkdir() 1hijklmn

52 (src_path / "__pycache__" / "main.cpython-311.pyc").write_text("bytecode") 1hijklmn

53 # pyc files 

54 (src_path / "main.pyc").write_text("bytecode") 1hijklmn

55 # mypy/pytest 

56 (src_path / ".mypy_cache").mkdir() 1hijklmn

57 (src_path / ".mypy_cache" / "file.json").write_text("{}") 1hijklmn

58 (src_path / ".pytest_cache").mkdir() 1hijklmn

59 (src_path / ".pytest_cache" / "cache.db").write_text("data") 1hijklmn

60 

61 archive(src_path, tar_path) 1hijklmn

62 

63 with fastar.open(tar_path, "r") as tar: 1hijklmn

64 tar.unpack(dst_path) 1hijklmn

65 

66 assert set(dst_path.glob("**/*")) == { 1hijklmn

67 dst_path / "main.py", 

68 dst_path / "static", 

69 dst_path / "static" / "index.html", 

70 } 

71 

72 

73def test_archive_preserves_relative_paths( 1abcdefg

74 src_path: Path, tar_path: Path, dst_path: Path 

75) -> None: 

76 (src_path / "src").mkdir() 1JKLMNOP

77 (src_path / "src" / "app").mkdir() 1JKLMNOP

78 (src_path / "src" / "app" / "main.py").write_text("print('hello')") 1JKLMNOP

79 

80 archive(src_path, tar_path) 1JKLMNOP

81 

82 with fastar.open(tar_path, "r") as tar: 1JKLMNOP

83 tar.unpack(dst_path) 1JKLMNOP

84 

85 assert set(dst_path.glob("**/*")) == { 1JKLMNOP

86 dst_path / "src", 

87 dst_path / "src" / "app", 

88 dst_path / "src" / "app" / "main.py", 

89 } 

90 

91 

92def test_archive_respects_fastapicloudignore( 1abcdefg

93 src_path: Path, tar_path: Path, dst_path: Path 

94) -> None: 

95 """Should exclude files specified in .fastapicloudignore.""" 

96 (src_path / "main.py").write_text("print('hello')") 1vwxyzAB

97 (src_path / "config.py").write_text("CONFIG = 'value'") 1vwxyzAB

98 (src_path / "secrets.env").write_text("SECRET_KEY=xyz") 1vwxyzAB

99 (src_path / "data").mkdir() 1vwxyzAB

100 (src_path / "data" / "file.txt").write_text("data") 1vwxyzAB

101 

102 (src_path / ".fastapicloudignore").write_text("secrets.env\ndata/\n") 1vwxyzAB

103 

104 archive(src_path, tar_path) 1vwxyzAB

105 

106 with fastar.open(tar_path, "r") as tar: 1vwxyzAB

107 tar.unpack(dst_path) 1vwxyzAB

108 

109 assert set(dst_path.glob("**/*")) == { 1vwxyzAB

110 dst_path / "main.py", 

111 dst_path / "config.py", 

112 } 

113 

114 

115def test_archive_respects_fastapicloudignore_unignore( 1abcdefg

116 src_path: Path, tar_path: Path, dst_path: Path 

117) -> None: 

118 """Test we can use .fastapicloudignore to unignore files inside .gitignore""" 

119 (src_path / "main.py").write_text("print('hello')") 1opqrstu

120 

121 (src_path / "ignore_me.txt").write_text("You should ignore me") 1opqrstu

122 

123 (src_path / "static/build").mkdir(exist_ok=True, parents=True) 1opqrstu

124 (src_path / "static/build/style.css").write_text("body { background: #bada55 }") 1opqrstu

125 

126 # Rignore needs a .git folder to make .gitignore work 

127 (src_path / ".git").mkdir(exist_ok=True, parents=True) 1opqrstu

128 (src_path / ".git" / "config").write_text("[core]\n\trepositoryformatversion = 0") 1opqrstu

129 (src_path / ".gitignore").write_text("ignore_me.txt\nbuild/") 1opqrstu

130 

131 (src_path / ".fastapicloudignore").write_text("!static/build") 1opqrstu

132 

133 archive(src_path, tar_path) 1opqrstu

134 

135 with fastar.open(tar_path, "r") as tar: 1opqrstu

136 tar.unpack(dst_path) 1opqrstu

137 

138 assert set(dst_path.glob("**/*")) == { 1opqrstu

139 dst_path / "main.py", 

140 dst_path / "static", 

141 dst_path / "static" / "build", 

142 dst_path / "static" / "build" / "style.css", 

143 } 

144 

145 

146def test_archive_includes_hidden_files_but_excludes_env( 1abcdefg

147 src_path: Path, tar_path: Path, dst_path: Path 

148) -> None: 

149 """Should include hidden files but exclude .env files.""" 

150 (src_path / "main.py").write_text("print('hello')") 1CDEFGHI

151 (src_path / ".env").write_text("SECRET_KEY=xyz") 1CDEFGHI

152 (src_path / ".env.local").write_text("LOCAL_KEY=abc") 1CDEFGHI

153 (src_path / ".config").mkdir() 1CDEFGHI

154 (src_path / ".config" / "settings.json").write_text('{"setting": "value"}') 1CDEFGHI

155 

156 archive(src_path, tar_path) 1CDEFGHI

157 

158 with fastar.open(tar_path, "r") as tar: 1CDEFGHI

159 tar.unpack(dst_path) 1CDEFGHI

160 

161 assert set(dst_path.glob("**/*")) == { 1CDEFGHI

162 dst_path / "main.py", 

163 dst_path / ".config", 

164 dst_path / ".config" / "settings.json", 

165 }