@@ -493,8 +493,8 @@ public void testNoIpV4SearchTreeStream(int chunkSizes) throws IOException {
493493
494494 private void testNoIpV4SearchTree (Reader reader ) throws IOException {
495495
496- assertEquals ("::0 /64" , reader .get (InetAddress .getByName ("1.1.1.1" ), String .class ));
497- assertEquals ("::0 /64" , reader .get (InetAddress .getByName ("192.1.1.1" ), String .class ));
496+ assertEquals ("::/64" , reader .get (InetAddress .getByName ("1.1.1.1" ), String .class ));
497+ assertEquals ("::/64" , reader .get (InetAddress .getByName ("192.1.1.1" ), String .class ));
498498 }
499499
500500 @ ParameterizedTest
@@ -2042,6 +2042,148 @@ private void testIpV6(Reader reader, File file) throws IOException {
20422042 }
20432043 }
20442044
2045+ // =========================================================================
2046+ // Tests for enum with @MaxMindDbCreator when data is stored via pointer
2047+ // See: https://github.com/maxmind/GeoIP2-java/issues/644
2048+ // =========================================================================
2049+
2050+ /**
2051+ * Enum with @MaxMindDbCreator for converting string values.
2052+ * This simulates how ConnectionType enum works in geoip2.
2053+ */
2054+ enum ConnectionTypeEnum {
2055+ DIALUP ("Dialup" ),
2056+ CABLE_DSL ("Cable/DSL" ),
2057+ CORPORATE ("Corporate" ),
2058+ CELLULAR ("Cellular" ),
2059+ SATELLITE ("Satellite" ),
2060+ UNKNOWN ("Unknown" );
2061+
2062+ private final String name ;
2063+
2064+ ConnectionTypeEnum (String name ) {
2065+ this .name = name ;
2066+ }
2067+
2068+ @ MaxMindDbCreator
2069+ public static ConnectionTypeEnum fromString (String s ) {
2070+ if (s == null ) {
2071+ return UNKNOWN ;
2072+ }
2073+ return switch (s ) {
2074+ case "Dialup" -> DIALUP ;
2075+ case "Cable/DSL" -> CABLE_DSL ;
2076+ case "Corporate" -> CORPORATE ;
2077+ case "Cellular" -> CELLULAR ;
2078+ case "Satellite" -> SATELLITE ;
2079+ default -> UNKNOWN ;
2080+ };
2081+ }
2082+ }
2083+
2084+ /**
2085+ * Model class that uses the ConnectionTypeEnum for the connection_type field.
2086+ */
2087+ static class TraitsModel {
2088+ ConnectionTypeEnum connectionType ;
2089+
2090+ @ MaxMindDbConstructor
2091+ public TraitsModel (
2092+ @ MaxMindDbParameter (name = "connection_type" )
2093+ ConnectionTypeEnum connectionType
2094+ ) {
2095+ this .connectionType = connectionType ;
2096+ }
2097+ }
2098+
2099+ /**
2100+ * Top-level model for Enterprise database records.
2101+ */
2102+ static class EnterpriseModel {
2103+ TraitsModel traits ;
2104+
2105+ @ MaxMindDbConstructor
2106+ public EnterpriseModel (
2107+ @ MaxMindDbParameter (name = "traits" )
2108+ TraitsModel traits
2109+ ) {
2110+ this .traits = traits ;
2111+ }
2112+ }
2113+
2114+ /**
2115+ * This test passes because IP 74.209.24.0 has connection_type stored inline.
2116+ */
2117+ @ ParameterizedTest
2118+ @ MethodSource ("chunkSizes" )
2119+ public void testEnumCreatorWithInlineData (int chunkSize ) throws IOException {
2120+ try (var reader = new Reader (getFile ("GeoIP2-Enterprise-Test.mmdb" ), chunkSize )) {
2121+ var ip = InetAddress .getByName ("74.209.24.0" );
2122+ var result = reader .get (ip , EnterpriseModel .class );
2123+ assertNotNull (result );
2124+ assertNotNull (result .traits );
2125+ assertEquals (ConnectionTypeEnum .CABLE_DSL , result .traits .connectionType );
2126+ }
2127+ }
2128+
2129+ /**
2130+ * This test verifies that enums with @MaxMindDbCreator work correctly when
2131+ * the data is stored via a pointer (common for deduplication in databases).
2132+ *
2133+ * <p>Previously, this would throw ConstructorNotFoundException because
2134+ * requiresLookupContext() called loadConstructorMetadata() before checking
2135+ * for creator methods.
2136+ */
2137+ @ ParameterizedTest
2138+ @ MethodSource ("chunkSizes" )
2139+ public void testEnumCreatorWithPointerData (int chunkSize ) throws IOException {
2140+ try (var reader = new Reader (getFile ("GeoIP2-Enterprise-Test.mmdb" ), chunkSize )) {
2141+ var ip = InetAddress .getByName ("89.160.20.112" );
2142+ var result = reader .get (ip , EnterpriseModel .class );
2143+ assertNotNull (result );
2144+ assertNotNull (result .traits );
2145+ assertEquals (ConnectionTypeEnum .CORPORATE , result .traits .connectionType );
2146+ }
2147+ }
2148+
2149+ // Model class with primitive double field for testing null-to-primitive error messages
2150+ static class IpRiskModelWithPrimitive {
2151+ public final double ipRisk ;
2152+
2153+ @ MaxMindDbConstructor
2154+ public IpRiskModelWithPrimitive (
2155+ @ MaxMindDbParameter (name = "ip_risk" ) double ipRisk
2156+ ) {
2157+ this .ipRisk = ipRisk ;
2158+ }
2159+ }
2160+
2161+ /**
2162+ * Tests that error messages correctly report null-to-primitive issues instead of
2163+ * misleading boxed/primitive type mismatch messages.
2164+ *
2165+ * <p>IP 11.1.2.3 in the IP Risk test database doesn't have an ip_risk field.
2166+ * When deserializing to a model with a primitive double, the error message should
2167+ * correctly identify "null value for primitive double" rather than reporting
2168+ * misleading Boolean/boolean mismatches.
2169+ */
2170+ @ ParameterizedTest
2171+ @ MethodSource ("chunkSizes" )
2172+ public void testNullToPrimitiveErrorMessage (int chunkSize ) throws IOException {
2173+ try (var reader = new Reader (getFile ("GeoIP2-IP-Risk-Test.mmdb" ), chunkSize )) {
2174+ var ip = InetAddress .getByName ("11.1.2.3" );
2175+
2176+ var exception = assertThrows (DeserializationException .class ,
2177+ () -> reader .get (ip , IpRiskModelWithPrimitive .class ));
2178+
2179+ // Error message should mention null value for primitive, not type mismatch
2180+ assertTrue (exception .getMessage ().contains ("null value for primitive double" ),
2181+ "Error message should identify null-to-primitive issue: " + exception .getMessage ());
2182+ assertTrue (exception .getMessage ().contains ("ip_risk" ),
2183+ "Error message should name the problematic parameter: " + exception .getMessage ());
2184+ }
2185+ }
2186+
20452187 static File getFile (String name ) {
20462188 return new File (ReaderTest .class .getResource ("/maxmind-db/test-data/" + name ).getFile ());
20472189 }
0 commit comments