diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cc4854f4..24ed8effd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,6 @@ Full release notes with details on each version: [GitHub Releases](https://githu ## 0.9.61 (2026-09-12) -- Fix: a dedup collision between a defining page and a page that merely mentions the same entity now correctly keeps the defining page's node for non-Latin filenames (Korean, CJK, Cyrillic, ...) — `_id_prefixes` now slugifies with the same Unicode-aware `normalize_id` every extractor mints an id with, instead of an ASCII-only regex that silently dropped the whole path (#3352, thanks @ayushcodes10). - Fix: `graphify.serve` now imports cleanly on Python 3.12 and 3.13. The `chinese` extra pins `jieba-py` from 3.12 onward (0.9.60 mistakenly kept the old `jieba` until 3.14, and its invalid regex escapes are a hard error on 3.12+), and the jieba import now suppresses the tokenizer's `SyntaxWarning` regardless of message or line so it never escalates under `-W error`. - Fix: the git hook's rebuild-root guard now rejects a symlink-loop or dangling `.graphify_root` on Python 3.13, whose `Path.resolve()` no longer raises on a loop — the saved root must resolve to a real directory inside the repo before it is adopted. diff --git a/tests/test_cluster.py b/tests/test_cluster.py index 8c8b7cd4d..4f0d83763 100644 --- a/tests/test_cluster.py +++ b/tests/test_cluster.py @@ -46,6 +46,34 @@ def test_cohesion_score_range(): score = cohesion_score(G, nodes) assert 0.0 <= score <= 1.0 +def test_cohesion_score_ignores_self_loops(): + # #3558: a recursive calls self-loop must not inflate the ratio. Two nodes + # with one real edge plus a self-loop is still fully cohesive (1.0), not 2.0. + G = nx.Graph() + G.add_edge("a", "b") + G.add_edge("a", "a") # recursion self-loop + score = cohesion_score(G, ["a", "b"]) + assert score == 1.0 + +def test_cohesion_score_self_loops_only_is_zero(): + # Self-loops alone are not inter-node connectivity. + G = nx.Graph() + G.add_nodes_from(["a", "b", "c"]) + for n in ("a", "b", "c"): + G.add_edge(n, n) + score = cohesion_score(G, ["a", "b", "c"]) + assert score == 0.0 + +def test_cohesion_score_range_with_self_loops(): + # The 0..1 bound holds even when every node carries a self-loop. + G = nx.complete_graph(4) + G = nx.relabel_nodes(G, {i: str(i) for i in G.nodes}) + for n in list(G.nodes): + G.add_edge(n, n) + score = cohesion_score(G, list(G.nodes)) + assert 0.0 <= score <= 1.0 + assert score == 1.0 + def test_score_all_keys_match_communities(): G = make_graph() communities = cluster(G)