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
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-08 15:04 -0800
1from __future__ import annotations
3import os
4import shutil
5import sys
6import tempfile
7from pathlib import Path
9sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
11from circular_deps import find_cycles
12from circular_deps.parsers.go import GoImportParser
13from circular_deps.resolvers.go import GoPathResolver
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"
32def test_go_stdlib_filtering():
33 from circular_deps.resolvers.go import GoPathResolver
35 tmpdir = Path(tempfile.mkdtemp())
36 try:
37 resolver = GoPathResolver()
39 path = resolver.resolve("fmt", "a.go", str(tmpdir))
40 assert path is None
42 path = resolver.resolve("encoding/json", "a.go", str(tmpdir))
43 assert path is None
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("")
51 path = resolver.resolve("subdir/pkg", str(tmpdir), str(tmpdir))
52 assert path == str(go_file)
53 finally:
54 shutil.rmtree(tmpdir)
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"
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')
68 files = [a_go, b_go]
69 cycles = find_cycles(files)
71 assert isinstance(cycles, list)
72 finally:
73 shutil.rmtree(tmpdir)
76def test_go_resolver_package_resolution():
77 from circular_deps.resolvers.go import GoPathResolver
79 tmpdir = Path(tempfile.mkdtemp())
80 try:
81 resolver = GoPathResolver()
83 package_dir = tmpdir / "mypackage" / "subdir"
84 package_dir.mkdir(parents=True)
85 go_file = package_dir / "file.go"
86 go_file.write_text("")
88 path = resolver.resolve("mypackage/subdir", str(tmpdir), str(tmpdir))
89 assert path == str(go_file)
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)
99def test_go_resolver_finds_go_file():
100 from circular_deps.resolvers.go import GoPathResolver
102 tmpdir = Path(tempfile.mkdtemp())
103 try:
104 resolver = GoPathResolver()
106 pkg_dir = tmpdir / "pkg"
107 pkg_dir.mkdir()
108 go_file = pkg_dir / "main.go"
109 go_file.write_text("")
111 path = resolver.resolve("pkg", str(tmpdir), str(tmpdir))
112 assert path == str(go_file)
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)
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)
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)
134 a_go = afet_dir / "a.go"
135 b_go = bfet_dir / "b.go"
137 a_go.write_text('package afet\nimport "bfet"\n')
138 b_go.write_text('package bfet\nimport "afet"\n')
140 files = [a_go, b_go]
141 cycles = find_cycles(files)
143 assert len(cycles) == 1
144 assert cycles[0].depth == 2
145 assert cycles[0].severity == "warning"
146 finally:
147 shutil.rmtree(tmpdir)
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"
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)
162 a_go = a_dir / "a.go"
163 b_go = b_dir / "b.go"
164 c_go = c_dir / "c.go"
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')
170 files = [a_go, b_go, c_go]
171 cycles = find_cycles(files)
173 assert len(cycles) == 1
174 assert cycles[0].depth == 3
175 finally:
176 shutil.rmtree(tmpdir)
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)
186 verticality_go = analysis_dir / "verticality.go"
187 index_go = tmpdir / "index.go"
189 verticality_go.write_text('package analysis\nimport "."\n')
190 index_go.write_text('package main\nimport "analysis"\n')
192 files = [verticality_go, index_go]
193 cycles = find_cycles(files)
195 assert len(cycles) == 1
196 assert cycles[0].depth == 2
197 finally:
198 shutil.rmtree(tmpdir)
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"
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)
213 a_go = a_dir / "a.go"
214 b_go = b_dir / "b.go"
215 c_go = c_dir / "c.go"
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')
221 files = [a_go, b_go, c_go]
222 cycles = find_cycles(files)
224 assert len(cycles) == 0
225 finally:
226 shutil.rmtree(tmpdir)