Coverage for tests / test_circular_deps / test_go.py: 100%

152 statements  

« prev     ^ index     » next       coverage.py v7.13.3, created at 2026-02-08 15:04 -0800

1from __future__ import annotations 

2 

3import os 

4import shutil 

5import sys 

6import tempfile 

7from pathlib import Path 

8 

9sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 

10 

11from circular_deps import find_cycles 

12from circular_deps.parsers.go import GoImportParser 

13from circular_deps.resolvers.go import GoPathResolver 

14 

15 

16def test_go_parser_extract_import(): 

17 parser = GoImportParser() 

18 source = """ 

19import ( 

20 "fmt" 

21 "os" 

22 "mymodule/sub" 

23) 

24""" 

25 imports = parser.extract_imports(source, "test.go") 

26 assert len(imports) == 3 

27 assert imports[0].raw_module == "fmt" 

28 assert imports[1].raw_module == "os" 

29 assert imports[2].raw_module == "mymodule/sub" 

30 

31 

32def test_go_stdlib_filtering(): 

33 from circular_deps.resolvers.go import GoPathResolver 

34 

35 tmpdir = Path(tempfile.mkdtemp()) 

36 try: 

37 resolver = GoPathResolver() 

38 

39 path = resolver.resolve("fmt", "a.go", str(tmpdir)) 

40 assert path is None 

41 

42 path = resolver.resolve("encoding/json", "a.go", str(tmpdir)) 

43 assert path is None 

44 

45 sub_dir = tmpdir / "subdir" 

46 go_dir = sub_dir / "pkg" 

47 go_dir.mkdir(parents=True) 

48 go_file = go_dir / "pkg.go" 

49 go_file.write_text("") 

50 

51 path = resolver.resolve("subdir/pkg", str(tmpdir), str(tmpdir)) 

52 assert path == str(go_file) 

53 finally: 

54 shutil.rmtree(tmpdir) 

55 

56 

57def test_go_cycle_detection_simple(): 

58 tmpdir = Path(tempfile.mkdtemp()) 

59 try: 

60 a_go = tmpdir / "a.go" 

61 b_dir = tmpdir / "module" / "b" 

62 b_go = b_dir / "b.go" 

63 

64 a_go.write_text('package main\nimport "mymodule/b"\n') 

65 b_dir.mkdir(parents=True, exist_ok=True) 

66 b_go.write_text('package b\nimport ".."\n') 

67 

68 files = [a_go, b_go] 

69 cycles = find_cycles(files) 

70 

71 assert isinstance(cycles, list) 

72 finally: 

73 shutil.rmtree(tmpdir) 

74 

75 

76def test_go_resolver_package_resolution(): 

77 from circular_deps.resolvers.go import GoPathResolver 

78 

79 tmpdir = Path(tempfile.mkdtemp()) 

80 try: 

81 resolver = GoPathResolver() 

82 

83 package_dir = tmpdir / "mypackage" / "subdir" 

84 package_dir.mkdir(parents=True) 

85 go_file = package_dir / "file.go" 

86 go_file.write_text("") 

87 

88 path = resolver.resolve("mypackage/subdir", str(tmpdir), str(tmpdir)) 

89 assert path == str(go_file) 

90 

91 go_file2 = package_dir / "other.go" 

92 go_file2.write_text("") 

93 path = resolver.resolve("mypackage/subdir", str(tmpdir), str(tmpdir)) 

94 assert path is not None and path.endswith(".go") 

95 finally: 

96 shutil.rmtree(tmpdir) 

97 

98 

99def test_go_resolver_finds_go_file(): 

100 from circular_deps.resolvers.go import GoPathResolver 

101 

102 tmpdir = Path(tempfile.mkdtemp()) 

103 try: 

104 resolver = GoPathResolver() 

105 

106 pkg_dir = tmpdir / "pkg" 

107 pkg_dir.mkdir() 

108 go_file = pkg_dir / "main.go" 

109 go_file.write_text("") 

110 

111 path = resolver.resolve("pkg", str(tmpdir), str(tmpdir)) 

112 assert path == str(go_file) 

113 

114 direct_go = tmpdir / "direct.go" 

115 direct_go.write_text("") 

116 path = resolver.resolve("direct.go", str(tmpdir), str(tmpdir)) 

117 assert path == str(direct_go) 

118 finally: 

119 shutil.rmtree(tmpdir) 

120 

121 

122def test_go_cycle_detection_in_src_subdirectory(): 

123 """Test circular dependency detection with files in ./src subdirectory.""" 

124 tmpdir = Path(tempfile.mkdtemp()) 

125 try: 

126 src_dir = tmpdir / "src" 

127 src_dir.mkdir(parents=True, exist_ok=True) 

128 

129 afet_dir = src_dir / "afet" 

130 bfet_dir = src_dir / "bfet" 

131 afet_dir.mkdir(parents=True, exist_ok=True) 

132 bfet_dir.mkdir(parents=True, exist_ok=True) 

133 

134 a_go = afet_dir / "a.go" 

135 b_go = bfet_dir / "b.go" 

136 

137 a_go.write_text('package afet\nimport "bfet"\n') 

138 b_go.write_text('package bfet\nimport "afet"\n') 

139 

140 files = [a_go, b_go] 

141 cycles = find_cycles(files) 

142 

143 assert len(cycles) == 1 

144 assert cycles[0].depth == 2 

145 assert cycles[0].severity == "warning" 

146 finally: 

147 shutil.rmtree(tmpdir) 

148 

149 

150def test_go_cycle_transitive_three_files(): 

151 """Test transitive circular dependency with 3 files.""" 

152 tmpdir = Path(tempfile.mkdtemp()) 

153 try: 

154 a_dir = tmpdir / "a" 

155 b_dir = tmpdir / "b" 

156 c_dir = tmpdir / "c" 

157 

158 a_dir.mkdir(parents=True, exist_ok=True) 

159 b_dir.mkdir(parents=True, exist_ok=True) 

160 c_dir.mkdir(parents=True, exist_ok=True) 

161 

162 a_go = a_dir / "a.go" 

163 b_go = b_dir / "b.go" 

164 c_go = c_dir / "c.go" 

165 

166 a_go.write_text('package a\nimport "b"\n') 

167 b_go.write_text('package b\nimport "c"\n') 

168 c_go.write_text('package c\nimport "a"\n') 

169 

170 files = [a_go, b_go, c_go] 

171 cycles = find_cycles(files) 

172 

173 assert len(cycles) == 1 

174 assert cycles[0].depth == 3 

175 finally: 

176 shutil.rmtree(tmpdir) 

177 

178 

179def test_go_cycle_nested_to_root(): 

180 """Test circular dependency with nested package importing root.""" 

181 tmpdir = Path(tempfile.mkdtemp()) 

182 try: 

183 analysis_dir = tmpdir / "analysis" 

184 analysis_dir.mkdir(parents=True, exist_ok=True) 

185 

186 verticality_go = analysis_dir / "verticality.go" 

187 index_go = tmpdir / "index.go" 

188 

189 verticality_go.write_text('package analysis\nimport "."\n') 

190 index_go.write_text('package main\nimport "analysis"\n') 

191 

192 files = [verticality_go, index_go] 

193 cycles = find_cycles(files) 

194 

195 assert len(cycles) == 1 

196 assert cycles[0].depth == 2 

197 finally: 

198 shutil.rmtree(tmpdir) 

199 

200 

201def test_go_no_cycles_linear_chain(): 

202 """Test linear import chain with no circular dependencies.""" 

203 tmpdir = Path(tempfile.mkdtemp()) 

204 try: 

205 a_dir = tmpdir / "a" 

206 b_dir = tmpdir / "b" 

207 c_dir = tmpdir / "c" 

208 

209 a_dir.mkdir(parents=True, exist_ok=True) 

210 b_dir.mkdir(parents=True, exist_ok=True) 

211 c_dir.mkdir(parents=True, exist_ok=True) 

212 

213 a_go = a_dir / "a.go" 

214 b_go = b_dir / "b.go" 

215 c_go = c_dir / "c.go" 

216 

217 a_go.write_text('package a\nimport "b"\n') 

218 b_go.write_text('package b\nimport (\n\t"fmt"\n)\n') 

219 c_go.write_text('package c\nimport (\n\t"os"\n)\n') 

220 

221 files = [a_go, b_go, c_go] 

222 cycles = find_cycles(files) 

223 

224 assert len(cycles) == 0 

225 finally: 

226 shutil.rmtree(tmpdir)