Skip to content
This repository was archived by the owner on Jun 18, 2026. It is now read-only.

Commit 0f18b85

Browse files
author
Repo Gardener
committed
test: add unit tests for GraphVoronoiPartitioner (13) and CentralityRadarExporter (9)
GraphVoronoiPartitionerTest covers: - Constructor validation (null graph, null/empty seeds, unknown seed) - Single-seed BFS absorbs reachable component and reports correct distances - Two-seed midpoint split with alphabetical tie-break (A wins over E at equidistant C) - Disconnected components are reported via VoronoiResult.unreachable - CellStats correctly count size, internal edges, and density on triangle+pendant - Dual-graph adjacency matches boundary edges on a 3-seed line - VoronoiResult collections (assignment/cells/unreachable/boundaryEdges) are unmodifiable - toText() reports seed headers, density, boundary edges, and unreachable count - exportHtml() produces non-trivial document with Legend, Cell Details, and Adjacency sections CentralityRadarExporterTest covers: - Null-graph constructor rejection - Empty-graph path produces well-formed HTML (DATA=[], 'All (0)') - Required DOM scaffolding present (canvas, table, search, topN, sortBy, themeBtn, stats) - All node ids appear in the embedded DATA JSON literal - Default and custom titles render in <title> - XSS escaping: <script> title is escaped, not injected raw - File export writes byte-equal copy of exportToString() - Null File argument fails fast Total: 22 new tests, all green via mvn surefire under Java 17.
1 parent 82b2102 commit 0f18b85

2 files changed

Lines changed: 406 additions & 0 deletions

File tree

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
package gvisual;
2+
3+
import edu.uci.ics.jung.graph.Graph;
4+
import edu.uci.ics.jung.graph.UndirectedSparseGraph;
5+
import org.junit.Before;
6+
import org.junit.Test;
7+
8+
import java.io.File;
9+
import java.nio.file.Files;
10+
11+
import static org.junit.Assert.*;
12+
13+
/**
14+
* Tests for {@link CentralityRadarExporter}.
15+
*
16+
* <p>Verifies construction validation, the generated HTML structure, custom
17+
* titles, JSON node payload integrity, XSS escaping in titles, and that
18+
* the file export path writes a byte-equal copy of {@link #exportToString()}.</p>
19+
*
20+
* @author sauravbhattacharya001
21+
*/
22+
public class CentralityRadarExporterTest {
23+
24+
private Graph<String, Edge> graph;
25+
26+
@Before
27+
public void setUp() {
28+
graph = new UndirectedSparseGraph<>();
29+
// Small graph: A-B-C-D with an A-C chord. 4 vertices, 4 edges.
30+
addEdge("A", "B");
31+
addEdge("B", "C");
32+
addEdge("C", "D");
33+
addEdge("A", "C");
34+
}
35+
36+
private void addEdge(String v1, String v2) {
37+
if (!graph.containsVertex(v1)) graph.addVertex(v1);
38+
if (!graph.containsVertex(v2)) graph.addVertex(v2);
39+
Edge e = new Edge("f", v1, v2);
40+
e.setWeight(1.0f);
41+
graph.addEdge(e, v1, v2);
42+
}
43+
44+
// ---------- constructor ----------
45+
46+
@Test(expected = IllegalArgumentException.class)
47+
public void constructorRejectsNullGraph() {
48+
new CentralityRadarExporter(null);
49+
}
50+
51+
@Test
52+
public void constructorAcceptsEmptyGraph() {
53+
Graph<String, Edge> empty = new UndirectedSparseGraph<>();
54+
CentralityRadarExporter exporter = new CentralityRadarExporter(empty);
55+
String html = exporter.exportToString();
56+
assertNotNull(html);
57+
// Empty graph -> "All (0)" in the topN select
58+
assertTrue(html.contains("All (0)"));
59+
// Empty DATA array
60+
assertTrue(html.contains("const DATA=[]"));
61+
}
62+
63+
// ---------- HTML structure ----------
64+
65+
@Test
66+
public void htmlContainsRequiredStructure() {
67+
CentralityRadarExporter exporter = new CentralityRadarExporter(graph);
68+
String html = exporter.exportToString();
69+
70+
assertTrue("must start with DOCTYPE", html.startsWith("<!DOCTYPE html>"));
71+
assertTrue(html.contains("<html lang=\"en\">"));
72+
assertTrue(html.contains("<meta charset=\"UTF-8\">"));
73+
assertTrue(html.contains("<meta name=\"viewport\""));
74+
assertTrue(html.contains("<canvas id=\"radar\""));
75+
assertTrue(html.contains("id=\"rankTable\""));
76+
assertTrue(html.contains("id=\"search\""));
77+
assertTrue(html.contains("id=\"topN\""));
78+
assertTrue(html.contains("id=\"sortBy\""));
79+
assertTrue(html.contains("id=\"themeBtn\""));
80+
assertTrue(html.contains("id=\"stats\""));
81+
assertTrue("must end with closing html", html.trim().endsWith("</html>"));
82+
}
83+
84+
@Test
85+
public void htmlContainsAllNodeIdsInData() {
86+
CentralityRadarExporter exporter = new CentralityRadarExporter(graph);
87+
String html = exporter.exportToString();
88+
// Each node id should appear inside the DATA JSON literal.
89+
// The exporter emits {"id":"A",...}
90+
for (String id : new String[]{"A", "B", "C", "D"}) {
91+
assertTrue("DATA payload missing node " + id,
92+
html.contains("\"id\":\"" + id + "\""));
93+
}
94+
// Total node count is rendered in the "All (N)" option
95+
assertTrue(html.contains("All (4)"));
96+
}
97+
98+
// ---------- title customization ----------
99+
100+
@Test
101+
public void defaultTitleIsUsedWhenNotSet() {
102+
CentralityRadarExporter exporter = new CentralityRadarExporter(graph);
103+
String html = exporter.exportToString();
104+
assertTrue(html.contains("<title>Centrality Radar Chart</title>"));
105+
}
106+
107+
@Test
108+
public void customTitleIsHonoured() {
109+
CentralityRadarExporter exporter = new CentralityRadarExporter(graph);
110+
exporter.setTitle("My Network");
111+
String html = exporter.exportToString();
112+
assertTrue(html.contains("<title>My Network</title>"));
113+
}
114+
115+
@Test
116+
public void titleIsXmlEscapedToPreventInjection() {
117+
CentralityRadarExporter exporter = new CentralityRadarExporter(graph);
118+
exporter.setTitle("<script>alert(1)</script>");
119+
String html = exporter.exportToString();
120+
// The literal <script> tag must not appear unescaped in the title slot.
121+
assertFalse("raw <script> must not be injected via title",
122+
html.contains("<title><script>alert(1)</script></title>"));
123+
// It should appear in escaped form (&lt; / &gt; or similar).
124+
assertTrue(html.contains("&lt;script&gt;") || html.contains("&lt;script"));
125+
}
126+
127+
// ---------- file export ----------
128+
129+
@Test
130+
public void exportWritesFileMatchingExportToString() throws Exception {
131+
CentralityRadarExporter exporter = new CentralityRadarExporter(graph);
132+
exporter.setTitle("File Export Test");
133+
String expected = exporter.exportToString();
134+
135+
File tmp = File.createTempFile("radar-", ".html");
136+
try {
137+
exporter.export(tmp);
138+
String onDisk = Files.readString(tmp.toPath());
139+
assertEquals("file contents must match exportToString output", expected, onDisk);
140+
assertTrue("file should be non-trivial in size", tmp.length() > 1000);
141+
} finally {
142+
//noinspection ResultOfMethodCallIgnored
143+
tmp.delete();
144+
}
145+
}
146+
147+
@Test(expected = NullPointerException.class)
148+
public void exportRejectsNullFile() throws Exception {
149+
new CentralityRadarExporter(graph).export(null);
150+
}
151+
}

0 commit comments

Comments
 (0)