package unit; import support.Moad0003Algorithm; /** * Unit tests for MOAD-0003: A Leaked Context. * * Proves from first principles: * 1. Defective: ThreadLocal set in request R1 remains accessible after R1 * completes — the next request R2 (which never calls set()) inherits R1's * identity. This is the thread-pool leak: the thread is reused, but the * ThreadLocal is not cleared between units of work. * 2. Fixed: ThreadLocal.remove() in a finally block ensures R2 sees null — * each request starts from a clean slate. * 3. Both correctly return the identity when explicitly set. * 4. Partial-set scenario: R2 sets its own identity and removes it correctly * even when R1 failed to clean up in the defective variant. * * No build tool required. Compile and run: * * cd tests * java -m jdk.compiler/com.sun.tools.javac.Main -cp . \ * support/Moad0003Algorithm.java unit/Moad0003UnitTest.java * java -cp . unit.Moad0003UnitTest */ public class Moad0003UnitTest { private static int passed = 0; private static int failed = 0; public static void main(String[] args) { System.out.println("=== Moad0003UnitTest (A Leaked Context) ===\n"); System.out.println("-- Correctness: identity returned correctly when set --"); testDefectiveReturnsSetIdentity(); testFixedReturnsSetIdentity(); System.out.println("\n-- Defect: stale identity leaks to anonymous next request --"); testDefectiveLeaksToAnonymousRequest(); testDefectiveLeaksAcrossMultipleRequests(); System.out.println("\n-- Fix: ThreadLocal.remove() cleans up before next request --"); testFixedAnonymousRequestSeesNull(); testFixedMultipleRequestsClean(); System.out.printf("\n%d passed, %d failed%n", passed, failed); if (failed > 0) System.exit(1); } // ── Correctness ─────────────────────────────────────────────────────────── static void testDefectiveReturnsSetIdentity() { Moad0003Algorithm.reset(); String result = Moad0003Algorithm.handleDefective("alice"); assertEqual("defective: returns set identity 'alice'", "alice", result); Moad0003Algorithm.reset(); // clean up after defective handler } static void testFixedReturnsSetIdentity() { Moad0003Algorithm.reset(); String result = Moad0003Algorithm.handleFixed("bob"); assertEqual("fixed: returns set identity 'bob'", "bob", result); // handleFixed removes ThreadLocal automatically } // ── Defect ──────────────────────────────────────────────────────────────── static void testDefectiveLeaksToAnonymousRequest() { Moad0003Algorithm.reset(); // Request R1: authenticated as "alice" Moad0003Algorithm.handleDefective("alice"); // R1 completes but DOES NOT call remove() — ThreadLocal still holds "alice" // Request R2: anonymous — never calls set() // Simulates the same pooled thread handling the next request. String leaked = Moad0003Algorithm.handleDefectiveAnonymous(); // DEFECT: R2 sees "alice" even though it never set any identity. // A CSRF-style confusion: R2 could perform privileged operations as "alice". assertEqual("defective: anonymous request leaks alice's identity", "alice", leaked); Moad0003Algorithm.reset(); } static void testDefectiveLeaksAcrossMultipleRequests() { Moad0003Algorithm.reset(); // R1 sets identity, completes without cleanup Moad0003Algorithm.handleDefective("carol"); // R2 anonymous — sees leaked "carol" String leak1 = Moad0003Algorithm.handleDefectiveAnonymous(); // R3 anonymous — still sees "carol" (ThreadLocal persists until explicitly removed) String leak2 = Moad0003Algorithm.handleDefectiveAnonymous(); assertEqual("defective: first anonymous request leaks 'carol'", "carol", leak1); assertEqual("defective: second anonymous request still leaks 'carol'", "carol", leak2); Moad0003Algorithm.reset(); } // ── Fix ─────────────────────────────────────────────────────────────────── static void testFixedAnonymousRequestSeesNull() { Moad0003Algorithm.reset(); // R1: authenticated — handleFixed removes ThreadLocal in finally Moad0003Algorithm.handleFixed("alice"); // ThreadLocal is now null — handleFixed called remove() before returning // R2: anonymous — sees null (clean slate) String identity = Moad0003Algorithm.handleFixedAnonymous(); assertEqual("fixed: anonymous request after fixed handler sees null", null, identity); } static void testFixedMultipleRequestsClean() { Moad0003Algorithm.reset(); // Simulate 3 requests on the same pooled thread, alternating auth/anon String r1 = Moad0003Algorithm.handleFixed("alice"); // auth String r2 = Moad0003Algorithm.handleFixedAnonymous(); // anon — must be null String r3 = Moad0003Algorithm.handleFixed("bob"); // auth String r4 = Moad0003Algorithm.handleFixedAnonymous(); // anon — must be null assertEqual("fixed: R1 returns 'alice'", "alice", r1); assertEqual("fixed: R2 anonymous sees null (not alice)", null, r2); assertEqual("fixed: R3 returns 'bob'", "bob", r3); assertEqual("fixed: R4 anonymous sees null (not bob)", null, r4); } // ── Helpers ─────────────────────────────────────────────────────────────── static void assertEqual(String label, String expected, String actual) { if (expected == null ? actual == null : expected.equals(actual)) { System.out.printf(" PASS: %s%n", label); passed++; } else { System.out.printf(" FAIL: %s — expected '%s', got '%s'%n", label, expected, actual); failed++; } } }