@@ -105,10 +105,12 @@ public void testCleanCopy() {
105105 // Copy should be a deep copy.
106106 assertThat (copy ).isNotSameAs (trie );
107107 // No operations performed on the original trie, so all nodes should still exist in the trie.
108+ // Probed with an explicit lookup time of 0 to ignore expiry: the point here is that cleanCopy
109+ // did not mutate the original, not whether these nodes have expired (two of them have).
108110 assertThat (trie ).satisfies (t -> {
109- assertThat (t .longestPrefixRangeLookup ("192.168.1.100" )).isEqualTo ("IPv4 Range 1" );
110- assertThat (t .longestPrefixRangeLookup ("10.0.5.1" )).isEqualTo ("IPv4 Range 2" );
111- assertThat (t .longestPrefixRangeLookup ("35.139.253.123" )).isEqualTo ("IPv4 Range 3" );
111+ assertThat (t .longestPrefixRangeLookupWithTtl ("192.168.1.100" , 0L )).isEqualTo ("IPv4 Range 1" );
112+ assertThat (t .longestPrefixRangeLookupWithTtl ("10.0.5.1" , 0L )).isEqualTo ("IPv4 Range 2" );
113+ assertThat (t .longestPrefixRangeLookupWithTtl ("35.139.253.123" , 0L )).isEqualTo ("IPv4 Range 3" );
112114 });
113115 // Confirm two nodes with TTLs have been cleaned out of the copy and the one without a TTL has not.
114116 assertThat (copy ).satisfies (t -> {
@@ -146,6 +148,52 @@ public void testCleanCopyExpiredNodes() {
146148 }
147149 }
148150
151+ @ Test
152+ public void testLookupSkipsExpiredRange () {
153+ try {
154+ final CIDRPatriciaTrie trie = new CIDRPatriciaTrie ();
155+ final long expireAt = DateTime .now (DateTimeZone .UTC ).getMillis () + 500L ;
156+ trie .insertCIDR ("192.168.1.0/24" , "Expiring Range" , expireAt );
157+ trie .insertCIDR ("10.0.0.0/8" , "Permanent Range" );
158+
159+ // Before expiry both resolve.
160+ assertThat (trie .longestPrefixRangeLookup ("192.168.1.100" )).isEqualTo ("Expiring Range" );
161+ assertThat (trie .longestPrefixRangeLookup ("10.0.5.1" )).isEqualTo ("Permanent Range" );
162+
163+ DateTimeUtils .setCurrentMillisOffset (501 );
164+
165+ // The expired range no longer matches, even though cleanCopy has not run and the node is
166+ // still present in the trie.
167+ assertThat (trie .longestPrefixRangeLookup ("192.168.1.100" )).isNull ();
168+ assertThat (trie .longestPrefixRangeLookupWithTtl ("192.168.1.100" , 0L )).isEqualTo ("Expiring Range" );
169+
170+ // A range without a TTL is unaffected.
171+ assertThat (trie .longestPrefixRangeLookup ("10.0.5.1" )).isEqualTo ("Permanent Range" );
172+ } finally {
173+ DateTimeUtils .setCurrentMillisSystem ();
174+ }
175+ }
176+
177+ @ Test
178+ public void testLookupFallsBackToLiveEnclosingRangeWhenLongestPrefixExpired () {
179+ try {
180+ final CIDRPatriciaTrie trie = new CIDRPatriciaTrie ();
181+ final long expireAt = DateTime .now (DateTimeZone .UTC ).getMillis () + 500L ;
182+ trie .insertCIDR ("10.1.2.0/24" , "Specific Expiring" , expireAt );
183+ trie .insertCIDR ("10.0.0.0/8" , "Broad Permanent" );
184+
185+ // The more specific range wins while it is live.
186+ assertThat (trie .longestPrefixRangeLookup ("10.1.2.42" )).isEqualTo ("Specific Expiring" );
187+
188+ DateTimeUtils .setCurrentMillisOffset (501 );
189+
190+ // Once it expires the search continues to the shorter enclosing range rather than giving up.
191+ assertThat (trie .longestPrefixRangeLookup ("10.1.2.42" )).isEqualTo ("Broad Permanent" );
192+ } finally {
193+ DateTimeUtils .setCurrentMillisSystem ();
194+ }
195+ }
196+
149197 @ Test
150198 public void testToBinaryIP () {
151199 String cidrBinary = CIDRPatriciaTrie .toBinaryString ("2002:0000:0000:1234:0000:0000:0000:0000" , 64 );
0 commit comments