Skip to content

Commit bbad52d

Browse files
gkorlandCopilot
andcommitted
fix(javascript): skip NullLanguageServer in second pass, add integration test
- Skip symbol resolution for files whose LSP is NullLanguageServer, avoiding unnecessary exception handling during second-pass (addresses coderabbit review on source_analyzer.py) - Add SourceAnalyzer.create_hierarchy integration test with MockGraph to verify the production code path for JS entity extraction and edge creation without requiring a database connection (addresses coderabbit review on test coverage) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 1de114e commit bbad52d

2 files changed

Lines changed: 47 additions & 0 deletions

File tree

api/analyzers/source_analyzer.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,9 @@ def second_pass(self, graph: Graph, files: list[Path], path: Path) -> None:
151151
for i, file_path in enumerate(files):
152152
file = self.files[file_path]
153153
logging.info(f'Processing file ({i + 1}/{files_len}): {file_path}')
154+
# Skip symbol resolution when no real LSP is available
155+
if isinstance(lsps.get(file_path.suffix), NullLanguageServer):
156+
continue
154157
for _, entity in file.entities.items():
155158
entity.resolved_symbol(lambda key, symbol, fp=file_path: analyzers[fp.suffix].resolve_symbol(self.files, lsps[fp.suffix], fp, path, key, symbol))
156159
for key, symbols in entity.symbols.items():

tests/test_javascript_analyzer.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,50 @@ def test_source_analyzer_supported_types_includes_js(self):
221221
sa = SourceAnalyzer()
222222
self.assertIn(".js", sa.supported_types())
223223

224+
def test_source_analyzer_create_hierarchy(self):
225+
"""SourceAnalyzer.create_hierarchy() should process JS files correctly.
226+
227+
Uses a lightweight mock Graph to verify the production code path
228+
without requiring a database connection.
229+
"""
230+
class MockGraph:
231+
def __init__(self):
232+
self._next_id = 1
233+
self.entities = {}
234+
self.edges = []
235+
236+
def add_file(self, file):
237+
file.id = self._next_id
238+
self._next_id += 1
239+
240+
def add_entity(self, label, name, doc, path, src_start, src_end, props):
241+
eid = self._next_id
242+
self._next_id += 1
243+
self.entities[eid] = {"label": label, "name": name, "doc": doc}
244+
return eid
245+
246+
def connect_entities(self, rel, src, dest, props=None):
247+
self.edges.append((rel, src, dest))
248+
249+
sa = SourceAnalyzer()
250+
source = self.sample_path.read_bytes()
251+
tree = self.analyzer.parser.parse(source)
252+
file = File(self.sample_path, tree)
253+
graph = MockGraph()
254+
graph.add_file(file)
255+
sa.create_hierarchy(file, self.analyzer, graph)
256+
257+
entity_names = [e["name"] for e in graph.entities.values()]
258+
self.assertIn("Shape", entity_names)
259+
self.assertIn("Circle", entity_names)
260+
self.assertIn("calculateTotal", entity_names)
261+
self.assertIn("area", entity_names)
262+
self.assertIn("constructor", entity_names)
263+
264+
# Verify DEFINES edges were created (file → entities)
265+
defines_edges = [e for e in graph.edges if e[0] == "DEFINES"]
266+
self.assertTrue(len(defines_edges) > 0, "Should create DEFINES edges")
267+
224268

225269
if __name__ == "__main__":
226270
unittest.main()

0 commit comments

Comments
 (0)