import java.util.*; /** * CWE-407 unit test — Consul consul-0001 * * agent/ui_endpoint.go summarizeServices() * * For each service instance in the catalog dump the function deduplicates: * (a) Node names → sum.Nodes []string — linear scan O(N) per instance * (b) Service tags → sum.Tags []string — linear scan O(T) per tag * * When iterating over I instances of S services (outer dump loop), the tag * dedup becomes O(S×I×T²): for each instance, for each tag, scan the already- * accumulated sum.Tags slice. In large Consul deployments (S=500 services, * I=20 instances each, T=30 tags) this is millions of comparisons per API call. * * Fix: add nodesSet map[string]struct{} and tagsSet map[string]struct{} fields * to ServiceSummary. O(1) set-membership replaces O(N)/O(T) slice scan. * Total complexity drops from O(S×I×T²) → O(S×I×T). */ public class ConsulTest { // ---- defect simulation -------------------------------------------------- /** Simulate the defect: sum.Tags is a List with linear-scan dedup. */ static List summarizeTags_quadratic(List> instanceTagLists) { List tags = new ArrayList<>(); for (List instanceTags : instanceTagLists) { for (String tag : instanceTags) { boolean found = false; for (String existing : tags) { // O(T) linear scan — the defect if (existing.equals(tag)) { found = true; break; } } if (!found) tags.add(tag); } } return tags; } /** Simulate the fix: use a HashSet for O(1) membership. */ static List summarizeTags_linear(List> instanceTagLists) { Set tagsSet = new LinkedHashSet<>(); // preserves insertion order for (List instanceTags : instanceTagLists) { tagsSet.addAll(instanceTags); } return new ArrayList<>(tagsSet); } // Node dedup — same pattern static List deduplicateNodes_quadratic(List nodeNames) { List nodes = new ArrayList<>(); for (String node : nodeNames) { boolean found = false; for (String existing : nodes) { if (existing.equals(node)) { found = true; break; } } if (!found) nodes.add(node); } return nodes; } static List deduplicateNodes_linear(List nodeNames) { Set seen = new LinkedHashSet<>(nodeNames); return new ArrayList<>(seen); } // ---- helpers ------------------------------------------------------------ /** Build instanceTagLists: I instances each sharing the same T tags. */ static List> makeInstanceTags(int instances, int tagsPerInstance) { List tags = new ArrayList<>(); for (int t = 0; t < tagsPerInstance; t++) { tags.add("tag-" + t); } List> result = new ArrayList<>(); for (int i = 0; i < instances; i++) { result.add(new ArrayList<>(tags)); // each instance has all tags } return result; } // ---- tests -------------------------------------------------------------- static void testTagCorrectnessSmall() { List> instanceTags = Arrays.asList( Arrays.asList("web", "v1", "prod"), Arrays.asList("v1", "prod", "dc1"), Arrays.asList("web", "dc1", "v2") ); List q = summarizeTags_quadratic(instanceTags); List l = summarizeTags_linear(instanceTags); Collections.sort(q); Collections.sort(l); assert q.equals(l) : "tag mismatch: " + q + " vs " + l; System.out.println("PASS testTagCorrectnessSmall"); } static void testNodeDeduplicateCorrectness() { List nodeNames = Arrays.asList( "node-1", "node-2", "node-1", "node-3", "node-2", "node-4" ); List q = deduplicateNodes_quadratic(nodeNames); List l = deduplicateNodes_linear(nodeNames); Collections.sort(q); Collections.sort(l); assert q.equals(l) : "node dedup mismatch: " + q + " vs " + l; assert q.size() == 4 : "expected 4 unique nodes, got " + q.size(); System.out.println("PASS testNodeDeduplicateCorrectness"); } static void testEmptyInstanceList() { List> instanceTags = Collections.emptyList(); List q = summarizeTags_quadratic(instanceTags); List l = summarizeTags_linear(instanceTags); assert q.equals(l) && q.isEmpty() : "empty instance list should yield empty tags"; System.out.println("PASS testEmptyInstanceList"); } static void testSingleInstanceSingleTag() { List> instanceTags = Collections.singletonList( Collections.singletonList("only-tag") ); List q = summarizeTags_quadratic(instanceTags); List l = summarizeTags_linear(instanceTags); assert q.equals(l) && q.equals(Collections.singletonList("only-tag")) : "single tag mismatch"; System.out.println("PASS testSingleInstanceSingleTag"); } static void testBenchmarkTagDedup() { // Simulate: 20 instances each carrying 40 tags (all the same → max dedup pressure) int INSTANCES = 20; int TAGS = 40; int ITERS = 300; List> instanceTags = makeInstanceTags(INSTANCES, TAGS); long t0 = System.nanoTime(); for (int i = 0; i < ITERS; i++) { summarizeTags_quadratic(instanceTags); } long quadraticNs = System.nanoTime() - t0; long t1 = System.nanoTime(); for (int i = 0; i < ITERS; i++) { summarizeTags_linear(instanceTags); } long linearNs = System.nanoTime() - t1; double ratio = (double) quadraticNs / linearNs; System.out.printf( "BENCH instances=%d tags=%d iters=%d quadratic=%.2fms linear=%.2fms ratio=%.1fx%n", INSTANCES, TAGS, ITERS, quadraticNs / 1e6, linearNs / 1e6, ratio); assert ratio >= 2.0 : "Expected >=2x speedup for I=" + INSTANCES + " T=" + TAGS + ", got " + ratio; System.out.println("PASS testBenchmarkTagDedup"); } static void testBenchmarkLargeDeployment() { // Simulate a large Consul deployment: 50 instances × 30 tags int INSTANCES = 50; int TAGS = 30; int ITERS = 500; List> instanceTags = makeInstanceTags(INSTANCES, TAGS); // JIT warmup for (int i = 0; i < 200; i++) { summarizeTags_quadratic(instanceTags); summarizeTags_linear(instanceTags); } long t0 = System.nanoTime(); for (int i = 0; i < ITERS; i++) { summarizeTags_quadratic(instanceTags); } long quadraticNs = System.nanoTime() - t0; long t1 = System.nanoTime(); for (int i = 0; i < ITERS; i++) { summarizeTags_linear(instanceTags); } long linearNs = System.nanoTime() - t1; double ratio = (double) quadraticNs / linearNs; System.out.printf( "BENCH large instances=%d tags=%d iters=%d quadratic=%.2fms linear=%.2fms ratio=%.1fx%n", INSTANCES, TAGS, ITERS, quadraticNs / 1e6, linearNs / 1e6, ratio); assert ratio >= 2.0 : "Expected >=2x speedup for large deployment, got " + ratio; System.out.println("PASS testBenchmarkLargeDeployment"); } public static void main(String[] args) { testTagCorrectnessSmall(); testNodeDeduplicateCorrectness(); testEmptyInstanceList(); testSingleInstanceSingleTag(); testBenchmarkTagDedup(); testBenchmarkLargeDeployment(); System.out.println("ALL PASS"); } }