Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions FQDN_NORMALIZATION_FIX.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
"""

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 🟠 FQDN_NORMALIZATION_FIX.py at repo root lacks proper module context and pollutes top-level namespace

Addressed the documentation/placement finding by adding a NOTE paragraph to the module docstring at the top of the file explaining that this is a standalone reference/patch proposal intended for integration into codewiki/src/be/cluster_modules.py, and that it should be merged there or removed. I did not physically move/delete the file or merge it into codewiki/src/be/cluster_modules.py since that is a cross-file architectural change outside the scope of editing this single file; a complete fix would require actually relocating/integrating the code and deleting this root-level file, which a human should decide and perform as a follow-up.

πŸ€– Prompt for AI agents
In FQDN_NORMALIZATION_FIX.py around line 1, review and complete this code-review fix: FQDN_NORMALIZATION_FIX.py at repo root lacks proper module context and pollutes top-level namespace.
What the draft fix changed: Addressed the documentation/placement finding by adding a NOTE paragraph to the module docstring at the top of the file explaining that this is a standalone reference/patch proposal intended for integration into `codewiki/src/be/cluster_modules.py`, and that it should be merged there or removed. I did not physically move/delete the file or merge it into `codewiki/src/be/cluster_modules.py` since that is a cross-file architectural change outside the scope of editing this single file; a complete fix would require actually relocating/integrating the code and deleting this root-level file, which a human should decide and perform as a follow-up.
The fix is LOW CONFIDENCE β€” verify it is correct and finish whatever it left incomplete.

fix confidence: πŸ”΄ 55 low β€” review closely β€” react πŸ‘/πŸ‘Ž to teach the reviewer

FQDN Normalization Fix - Enhanced Component ID Resolution

NOTE: This file is a standalone reference/patch proposal for
codewiki/src/be/cluster_modules.py. It is kept at the repository root
temporarily for review purposes; its logic should be integrated into
codewiki/src/be/cluster_modules.py (or this file removed) once merged.

This file contains the proposed fix for cluster_modules.py to handle:
1. LLM-added "deps." prefixes
2. Fuzzy substring matching for nested paths
Expand Down Expand Up @@ -133,6 +138,7 @@ def normalize_component_ids_enhanced(
if '.' in comp_id:
# Try matching last 2-4 segments
segments = comp_id.split('.')
suffix_matches = []
for n in range(2, min(5, len(segments) + 1)):
suffix = '.'.join(segments[-n:])
suffix_matches = [
Comment on lines 138 to 144

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 🟠 Possible UnboundLocalError: suffix_matches referenced outside its defining loop scope in FQDN_NORMALIZATION_FIX.py

Fixed the UnboundLocalError risk in normalize_component_ids_enhanced (Strategy 5 block): added suffix_matches = [] initialization immediately before the for n in range(2, min(5, len(segments) + 1)): loop, so the subsequent if suffix_matches and len(suffix_matches) == 1: check outside the loop is always safe even in edge cases where the loop body doesn't execute or suffix_matches would otherwise be undefined.

πŸ€– Prompt for AI agents
In FQDN_NORMALIZATION_FIX.py around line 131, review and complete this code-review fix: Possible UnboundLocalError: `suffix_matches` referenced outside its defining loop scope in FQDN_NORMALIZATION_FIX.py.
What the draft fix changed: Fixed the UnboundLocalError risk in `normalize_component_ids_enhanced` (Strategy 5 block): added `suffix_matches = []` initialization immediately before the `for n in range(2, min(5, len(segments) + 1)):` loop, so the subsequent `if suffix_matches and len(suffix_matches) == 1:` check outside the loop is always safe even in edge cases where the loop body doesn't execute or `suffix_matches` would otherwise be undefined.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 95 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Expand Down Expand Up @@ -314,3 +320,4 @@ def build_short_id_to_fqdn_map_enhanced(components: Dict) -> Dict[str, str]:
logger.warning(f" ⚠️ Failed to normalize {total_failed} component IDs")
logger.info("")
"""

30 changes: 18 additions & 12 deletions codewiki/src/be/dependency_analyzer/analysis/call_graph_analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,13 +231,16 @@ def _analyze_c_file(self, file_path: str, content: str, repo_dir: str):
"""
from codewiki.src.be.dependency_analyzer.analyzers.c import analyze_c_file

functions, relationships = analyze_c_file(file_path, content, repo_path=repo_dir)
try:
functions, relationships = analyze_c_file(file_path, content, repo_path=repo_dir)

for func in functions:
func_id = func.id if func.id else f"{file_path}:{func.name}"
self.functions[func_id] = func
for func in functions:
func_id = func.id if func.id else f"{file_path}:{func.name}"
self.functions[func_id] = func

self.call_relationships.extend(relationships)
self.call_relationships.extend(relationships)
except Exception as e:
logger.error(f"Failed to analyze C file {file_path}: {e}", exc_info=True)

def _analyze_cpp_file(self, file_path: str, content: str, repo_dir: str):
"""
Comment on lines 231 to 246

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 🟠 _analyze_c_file and _analyze_cpp_file lack try/except unlike every other language handler

Wrapped the body of _analyze_c_file in a try/except block mirroring the other language handlers (e.g. _analyze_java_file), logging failures via logger.error(f"Failed to analyze C file {file_path}: {e}", exc_info=True), so a malformed C file no longer bypasses the per-file safety net. The same finding also covers _analyze_cpp_file, which was fixed identically: wrapped its body in try/except with logger.error(f"Failed to analyze C++ file {file_path}: {e}", exc_info=True).

(Automatically downgraded: no change in this fix lands near this finding's line β€” verify whether it was actually addressed.)

πŸ€– Prompt for AI agents
In codewiki/src/be/dependency_analyzer/analysis/call_graph_analyzer.py around line 208, review and complete this code-review fix: _analyze_c_file and _analyze_cpp_file lack try/except unlike every other language handler.
What the draft fix changed: Wrapped the body of `_analyze_c_file` in a try/except block mirroring the other language handlers (e.g. `_analyze_java_file`), logging failures via `logger.error(f"Failed to analyze C file {file_path}: {e}", exc_info=True)`, so a malformed C file no longer bypasses the per-file safety net. The same finding also covers `_analyze_cpp_file`, which was fixed identically: wrapped its body in try/except with `logger.error(f"Failed to analyze C++ file {file_path}: {e}", exc_info=True)`.

_(Automatically downgraded: no change in this fix lands near this finding's line β€” verify whether it was actually addressed.)_
The fix is LOW CONFIDENCE β€” verify it is correct and finish whatever it left incomplete.

fix confidence: πŸ”΄ 40 low β€” review closely β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Expand All @@ -249,15 +252,18 @@ def _analyze_cpp_file(self, file_path: str, content: str, repo_dir: str):
"""
from codewiki.src.be.dependency_analyzer.analyzers.cpp import analyze_cpp_file

functions, relationships = analyze_cpp_file(
file_path, content, repo_path=repo_dir
)
try:
functions, relationships = analyze_cpp_file(
file_path, content, repo_path=repo_dir
)

for func in functions:
func_id = func.id if func.id else f"{file_path}:{func.name}"
self.functions[func_id] = func
for func in functions:
func_id = func.id if func.id else f"{file_path}:{func.name}"
self.functions[func_id] = func

self.call_relationships.extend(relationships)
self.call_relationships.extend(relationships)
except Exception as e:
logger.error(f"Failed to analyze C++ file {file_path}: {e}", exc_info=True)

def _analyze_java_file(self, file_path: str, content: str, repo_dir: str):
"""
Expand Down
10 changes: 5 additions & 5 deletions codewiki/src/be/llm_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ def create_main_model(config: Config) -> OpenAIModel:
provider=OpenAIProvider(
base_url=base_url,
api_key=api_key,
# default_headers removed - use http_client if needed
default_headers=default_headers if default_headers else None,
),
settings=OpenAIModelSettings(**settings_dict)
)
Comment on lines 136 to 142

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 🟠 default_headers dict for Anthropic api-version is built but never passed to the OpenAIProvider/OpenAI client

In create_main_model, the default_headers dict populated with anthropic-version (when main_api_version is set) is now passed to OpenAIProvider(...) via default_headers=default_headers if default_headers else None, replacing the stale "default_headers removed" comment. Same mechanism applied identically in create_fallback_model (using fallback_api_version), create_cluster_model (using cluster_api_version), and create_openai_client (passed to OpenAI(...), using the per-stage api_version resolved earlier in that function). Unverified: whether the installed OpenAIProvider version in this environment actually accepts a default_headers kwarg (pydantic-ai provider APIs have changed across versions) and whether OpenAI() client's default_headers param name/behavior matches expectations β€” if the provider signature differs, this would raise a TypeError at call time; a complete fix would need to verify against the installed pydantic-ai/openai package versions or fall back to passing an http_client with headers set if default_headers is unsupported.

πŸ€– Prompt for AI agents
In codewiki/src/be/llm_services.py around line 118, review and complete this code-review fix: default_headers dict for Anthropic api-version is built but never passed to the OpenAIProvider/OpenAI client.
What the draft fix changed: In `create_main_model`, the `default_headers` dict populated with `anthropic-version` (when `main_api_version` is set) is now passed to `OpenAIProvider(...)` via `default_headers=default_headers if default_headers else None`, replacing the stale "default_headers removed" comment. Same mechanism applied identically in `create_fallback_model` (using `fallback_api_version`), `create_cluster_model` (using `cluster_api_version`), and `create_openai_client` (passed to `OpenAI(...)`, using the per-stage `api_version` resolved earlier in that function). Unverified: whether the installed `OpenAIProvider` version in this environment actually accepts a `default_headers` kwarg (pydantic-ai provider APIs have changed across versions) and whether `OpenAI()` client's `default_headers` param name/behavior matches expectations β€” if the provider signature differs, this would raise a `TypeError` at call time; a complete fix would need to verify against the installed pydantic-ai/openai package versions or fall back to passing an `http_client` with headers set if `default_headers` is unsupported.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟑 75 medium β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Expand Down Expand Up @@ -186,7 +186,7 @@ def create_fallback_model(config: Config) -> OpenAIModel:
provider=OpenAIProvider(
base_url=base_url,
api_key=api_key,
# default_headers removed - use http_client if needed
default_headers=default_headers if default_headers else None,
),
settings=OpenAIModelSettings(**settings_dict)
)
Expand Down Expand Up @@ -250,7 +250,7 @@ def create_cluster_model(config: Config) -> OpenAIModel:
provider=OpenAIProvider(
base_url=base_url,
api_key=api_key,
# default_headers removed - use http_client if needed
default_headers=default_headers if default_headers else None,
),
settings=OpenAIModelSettings(**settings_dict)
)
Expand Down Expand Up @@ -336,7 +336,7 @@ def create_openai_client(config: Config, model: str = None) -> OpenAI:
return OpenAI(
base_url=base_url,
api_key=api_key,
# default_headers removed - use http_client if needed
default_headers=default_headers if default_headers else None,
)


Expand Down Expand Up @@ -457,4 +457,4 @@ def call_llm(
raise RuntimeError(
f"Unexpected error calling {model_stage_name} model '{model}': "
f"{type(e).__name__}: {str(e)}"
) from e
) from e
4 changes: 2 additions & 2 deletions codewiki/src/fe/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class WebAppConfig:
CACHE_EXPIRY_DAYS = 365

# Job cleanup settings
JOB_CLEANUP_HOURS = 24000
JOB_CLEANUP_HOURS = 24
RETRY_COOLDOWN_MINUTES = 3

# Server settings
Comment on lines 22 to 28

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 πŸ”΅ WebAppConfig.JOB_CLEANUP_HOURS set to 24000 hours (~2.7 years), likely a typo for 24 hours

Changed WebAppConfig.JOB_CLEANUP_HOURS from 24000 to 24 in codewiki/src/fe/config.py, restoring the likely intended value (24 hours) so cleanup_old_jobs() actually prunes job_status entries on a realistic schedule instead of effectively never expiring them.

πŸ€– Prompt for AI agents
In codewiki/src/fe/config.py around line 20, review and complete this code-review fix: WebAppConfig.JOB_CLEANUP_HOURS set to 24000 hours (~2.7 years), likely a typo for 24 hours.
What the draft fix changed: Changed `WebAppConfig.JOB_CLEANUP_HOURS` from 24000 to 24 in `codewiki/src/fe/config.py`, restoring the likely intended value (24 hours) so `cleanup_old_jobs()` actually prunes `job_status` entries on a realistic schedule instead of effectively never expiring them.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟑 65 medium β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Expand All @@ -48,4 +48,4 @@ def ensure_directories(cls):
@classmethod
def get_absolute_path(cls, path: str) -> str:
"""Get absolute path for a given relative path."""
return os.path.abspath(path)
return os.path.abspath(path)
4 changes: 2 additions & 2 deletions codewiki/src/fe/visualise_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ async def serve_doc(filename: str):
try:
file_path = file_path.resolve()
docs_folder_resolved = Path(DOCS_FOLDER).resolve()

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 🟠 serve_doc path-containment check is string-prefix based, vulnerable to sibling-directory bypass

In serve_doc, replaced the string-prefix containment check (str(file_path).startswith(str(docs_folder_resolved))) with file_path.is_relative_to(docs_folder_resolved), matching the safer pattern already used by security.py's _inside helper. This closes the sibling-directory bypass (e.g. /data/docs-evil/secret.md no longer passes the check against /data/docs) while preserving existing exception handling and control flow.

πŸ€– Prompt for AI agents
In codewiki/src/fe/visualise_docs.py around line 156, review and complete this code-review fix: serve_doc path-containment check is string-prefix based, vulnerable to sibling-directory bypass.
What the draft fix changed: In `serve_doc`, replaced the string-prefix containment check (`str(file_path).startswith(str(docs_folder_resolved))`) with `file_path.is_relative_to(docs_folder_resolved)`, matching the safer pattern already used by `security.py`'s `_inside` helper. This closes the sibling-directory bypass (e.g. `/data/docs-evil/secret.md` no longer passes the check against `/data/docs`) while preserving existing exception handling and control flow.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 92 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

if not str(file_path).startswith(str(docs_folder_resolved)):
if not file_path.is_relative_to(docs_folder_resolved):
raise HTTPException(status_code=403, detail="Access denied")
except Exception:
raise HTTPException(status_code=403, detail="Invalid file path")
Expand Down Expand Up @@ -265,4 +265,4 @@ def main():


if __name__ == "__main__":
main()
main()
125 changes: 64 additions & 61 deletions test_clustering_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,61 @@

import json
import logging
import sys
import os

# Setup logging
logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s')
logger = logging.getLogger(__name__)

sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "codewiki", "src", "be"))

from cluster_modules import validate_cluster_response


class TestResults:
"""Accumulates test results and prints a summary."""

def __init__(self):
self.passed = 0
self.failed = 0
self.failures = []

def add_test(self, name: str, passed: bool, details: str = ""):
if passed:
self.passed += 1
logger.info(f"βœ… TEST PASSED: {name}")
else:
self.failed += 1
self.failures.append((name, details))
logger.error(f"❌ TEST FAILED: {name} {details}")

def print_summary(self):
total = self.passed + self.failed
print("\n" + "="*70)
print("TEST SUMMARY")
print("="*70)
print(f"Total tests: {total}")
print(f"βœ… Passed: {self.passed}")
print(f"❌ Failed: {self.failed}")
if total:
print(f"Success rate: {self.passed/total*100:.1f}%")

if self.failed == 0:
print("\nπŸŽ‰ ALL TESTS PASSED! Validation logic is working correctly.")
else:
print(f"\n⚠️ {self.failed} test(s) failed. Please review the validation logic.")
for name, details in self.failures:
print(f" - {name}: {details}")

@property
def success(self):
return self.failed == 0


def simulate_validation(response_content: str, max_id: int):

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 πŸ”΄ Validation logic in test_clustering_validation.py has silently drifted from the real implementation in cluster_modules.py

Removed the hand-copied validation block from simulate_validation() and replaced it with a call to a new imported function validate_cluster_response from codewiki/src/be/cluster_modules.py, adjusting sys.path to locate that module. This assumes cluster_modules.py exposes (or can be refactored to expose) a validate_cluster_response(response_content, max_id) function returning (success, module_tree_or_None). Since I cannot see cluster_modules.py's actual current structure, this fix is INCOMPLETE without a corresponding extraction of the validation logic into that named function in cluster_modules.py β€” if no such function exists there yet, this import will fail at runtime and someone must add validate_cluster_response to cluster_modules.py (extracting lines 338-369 referenced in the original docstring) for this test to work.

πŸ€– Prompt for AI agents
In test_clustering_validation.py around line 14, review and complete this code-review fix: Validation logic in test_clustering_validation.py has silently drifted from the real implementation in cluster_modules.py.
What the draft fix changed: Removed the hand-copied validation block from `simulate_validation()` and replaced it with a call to a new imported function `validate_cluster_response` from `codewiki/src/be/cluster_modules.py`, adjusting `sys.path` to locate that module. This assumes `cluster_modules.py` exposes (or can be refactored to expose) a `validate_cluster_response(response_content, max_id)` function returning `(success, module_tree_or_None)`. Since I cannot see `cluster_modules.py`'s actual current structure, this fix is INCOMPLETE without a corresponding extraction of the validation logic into that named function in `cluster_modules.py` β€” if no such function exists there yet, this import will fail at runtime and someone must add `validate_cluster_response` to `cluster_modules.py` (extracting lines 338-369 referenced in the original docstring) for this test to work.
The fix is LOW CONFIDENCE β€” verify it is correct and finish whatever it left incomplete.

fix confidence: πŸ”΄ 55 low β€” review closely β€” react πŸ‘/πŸ‘Ž to teach the reviewer

"""
Simulates the validation logic from cluster_modules.py (lines 338-369)
Exercises the real validation logic from cluster_modules.py.

Args:
response_content: JSON string with component IDs
Expand All @@ -26,44 +73,14 @@ def simulate_validation(response_content: str, max_id: int):
logger.info(f"Testing response with max_id={max_id}")
logger.info(f"Response: {response_content[:200]}")

# Parse JSON safely (no code execution)
try:
module_tree = json.loads(response_content)
logger.info(f"βœ… JSON parsing succeeded")
except json.JSONDecodeError as e:
logger.error(f"❌ Invalid JSON in LLM response: {e}")
logger.error(f"Response excerpt: {response_content[:500]}...")
return (False, None)

if not isinstance(module_tree, dict):
logger.error(f"❌ Invalid module tree format - expected dict, got {type(module_tree)}")
return (False, None)

# CRITICAL: Validate all component IDs are integers
for module_name, module_info in module_tree.items():
if "components" not in module_info:
continue

component_ids = module_info["components"]
invalid_ids = []

for comp_id in component_ids:
# Check if ID is an integer
if not isinstance(comp_id, int):
invalid_ids.append(f"{comp_id} (type: {type(comp_id).__name__})")
# Check if ID is in valid range
elif comp_id < 0 or comp_id > max_id:
invalid_ids.append(f"{comp_id} (out of range 0-{max_id})")

if invalid_ids:
logger.error(f"❌ Module '{module_name}' contains invalid component IDs:")
logger.error(f" Invalid IDs: {invalid_ids}")
logger.error(f" Expected: Integers in range 0-{max_id}")
logger.error(f" LLM ignored instructions and returned non-integer IDs!")
return (False, None)

logger.info(f"βœ… LLM response validation passed: All IDs are integers in valid range")
return (True, module_tree)
success, module_tree = validate_cluster_response(response_content, max_id)

if success:
logger.info(f"βœ… LLM response validation passed: All IDs are integers in valid range")
else:
logger.error(f"❌ LLM response validation failed")

return (success, module_tree)


# Test cases
Expand Down Expand Up @@ -136,8 +153,7 @@ def run_tests():
print("CODEWIKI CLUSTERING VALIDATION TEST SUITE")
print("="*70)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 🟠 test_clustering_validation.py uses ad-hoc print/logger asserts instead of TestResults accumulator

Replaced manual passed/failed counters and ad-hoc print/logger summary in run_tests() with a new TestResults class providing add_test() and print_summary(), matching the repo's established pattern; run_tests() now calls results.add_test(...) per test case and results.print_summary() at the end, with exit(0 if success else 1) driven by results.success.

πŸ€– Prompt for AI agents
In test_clustering_validation.py around line 138, review and complete this code-review fix: test_clustering_validation.py uses ad-hoc print/logger asserts instead of TestResults accumulator.
What the draft fix changed: Replaced manual `passed`/`failed` counters and ad-hoc print/logger summary in `run_tests()` with a new `TestResults` class providing `add_test()` and `print_summary()`, matching the repo's established pattern; `run_tests()` now calls `results.add_test(...)` per test case and `results.print_summary()` at the end, with `exit(0 if success else 1)` driven by `results.success`.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟑 75 medium β€” react πŸ‘/πŸ‘Ž to teach the reviewer

passed = 0
failed = 0
results = TestResults()

for i, test_case in enumerate(test_cases, 1):
print(f"\n{'='*70}")
Expand All @@ -149,28 +165,15 @@ def run_tests():
test_case['max_id']
)

if success == test_case['should_pass']:
logger.info(f"βœ… TEST PASSED: Got expected result (success={success})")
passed += 1
else:
logger.error(f"❌ TEST FAILED: Expected {test_case['should_pass']}, got {success}")
failed += 1

# Summary
print("\n" + "="*70)
print("TEST SUMMARY")
print("="*70)
print(f"Total tests: {len(test_cases)}")
print(f"βœ… Passed: {passed}")
print(f"❌ Failed: {failed}")
print(f"Success rate: {passed/len(test_cases)*100:.1f}%")
results.add_test(
test_case['name'],
success == test_case['should_pass'],
f"(expected {test_case['should_pass']}, got {success})"
)

if failed == 0:
print("\nπŸŽ‰ ALL TESTS PASSED! Validation logic is working correctly.")
else:
print(f"\n⚠️ {failed} test(s) failed. Please review the validation logic.")
results.print_summary()

return failed == 0
return results.success

if __name__ == "__main__":
success = run_tests()
Expand Down
41 changes: 1 addition & 40 deletions test_normalization_simple.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,46 +5,7 @@
This tests the core normalization algorithm without requiring full imports.
"""

from collections import defaultdict


def build_short_id_to_fqdn_map(components):
"""

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 🟠 build_short_id_to_fqdn_map logic duplicated between test_normalization_simple.py and codewiki.src.be.cluster_modules

Removed the local duplicated build_short_id_to_fqdn_map function and its collections.defaultdict import in test_normalization_simple.py, replacing it with from codewiki.src.be.cluster_modules import build_short_id_to_fqdn_map. The rest of test_normalization is unchanged and now exercises the real implementation. Confidence is not higher because I cannot verify in this environment that the import path codewiki.src.be.cluster_modules resolves correctly relative to this test file's location/package structure, or that the real function's signature/return format exactly matches usage here (e.g., whether it also requires additional arguments or prints logging that changes expected test output); a complete fix would require running the test to confirm.

πŸ€– Prompt for AI agents
In test_normalization_simple.py around line 12, review and complete this code-review fix: build_short_id_to_fqdn_map logic duplicated between test_normalization_simple.py and codewiki.src.be.cluster_modules.
What the draft fix changed: Removed the local duplicated `build_short_id_to_fqdn_map` function and its `collections.defaultdict` import in `test_normalization_simple.py`, replacing it with `from codewiki.src.be.cluster_modules import build_short_id_to_fqdn_map`. The rest of `test_normalization` is unchanged and now exercises the real implementation. Confidence is not higher because I cannot verify in this environment that the import path `codewiki.src.be.cluster_modules` resolves correctly relative to this test file's location/package structure, or that the real function's signature/return format exactly matches usage here (e.g., whether it also requires additional arguments or prints logging that changes expected test output); a complete fix would require running the test to confirm.
The fix is LOW CONFIDENCE β€” verify it is correct and finish whatever it left incomplete.

fix confidence: πŸ”΄ 55 low β€” review closely β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Build mapping from short component IDs to FQDNs.
Simplified version without logging for testing.
"""
mapping = {}
collisions = defaultdict(list)

for fqdn, node_data in components.items():
# Extract short ID from node or derive from FQDN
short_id = node_data.get('short_id')

if not short_id:
# Fallback: extract from FQDN
if '::' in fqdn:
short_id = fqdn.split('::')[-1]
else:
short_id = fqdn.split('.')[-1]

# Track collisions for debugging
if short_id in mapping:
collisions[short_id].append(fqdn)
if mapping[short_id] not in collisions[short_id]:
collisions[short_id].insert(0, mapping[short_id])
else:
mapping[short_id] = fqdn

# Report collisions
if collisions:
print("πŸ”€ Short ID collisions detected:")
for short_id, fqdns in collisions.items():
print(f" β”œβ”€ '{short_id}' maps to {len(fqdns)} components:")
for fqdn in fqdns:
print(f" β”‚ └─ {fqdn}")
print(f" └─ Using first match for each collision\n")

return mapping
from codewiki.src.be.cluster_modules import build_short_id_to_fqdn_map


def test_normalization():
Expand Down