From 66aff75446289e5d5f1db562252fc26923553ba4 Mon Sep 17 00:00:00 2001 From: TristonianJones Date: Tue, 13 Jan 2026 15:24:31 -0800 Subject: [PATCH 1/5] Networking extension conformance tests --- tests/simple/testdata/network_ext.textproto | 315 ++++++++++++++++++++ 1 file changed, 315 insertions(+) create mode 100644 tests/simple/testdata/network_ext.textproto diff --git a/tests/simple/testdata/network_ext.textproto b/tests/simple/testdata/network_ext.textproto new file mode 100644 index 0000000..ef6bd65 --- /dev/null +++ b/tests/simple/testdata/network_ext.textproto @@ -0,0 +1,315 @@ +# proto-file: ../../../proto/cel/expr/conformance/test/simple.proto +# proto-message: cel.expr.conformance.test.SimpleTestFile + +name: "network_ext" +description: "Conformance tests for IP address extensions in CEL" +section { + name: "ip_type" + description: "Tests for parsing and validating IP addresses" + test { + name: "parse_ipv4" + description: "Successfully parse a standard IPv4 address" + expr: "string(ip('192.168.0.1'))" + value: { string_value: "192.168.0.1" } + } + test { + name: "parse_invalid_ipv4" + description: "Fail to parse an invalid IPv4 address" + expr: "ip('192.168.0.1.0')" + eval_error: { + errors: { + message: "IP Address '192.168.0.1.0' parse error during conversion from string" + } + } + } + test { + name: "is_ip_valid_ipv4" + description: "isIP returns true for valid IPv4" + expr: "isIP('192.168.0.1')" + value: { bool_value: true } + } + test { + name: "is_ip_invalid_ipv4" + description: "isIP returns false for invalid IPv4" + expr: "isIP('192.168.0.1.0')" + value: { bool_value: false } + } + test { + name: "ip_is_canonical_valid_ipv4" + description: "ip.isCanonical returns true for valid IPv4" + expr: "ip.isCanonical('127.0.0.1')" + value: { bool_value: true } + } + test { + name: "ip_is_canonical_invalid_ipv4" + description: "ip.isCanonical errors on invalid address string" + expr: "ip.isCanonical('127.0.0.1.0')" + eval_error: { + errors: { + message: "IP Address '127.0.0.1.0' parse error during conversion from string" + } + } + } + test { + name: "ip_is_canonical_non_canonical_ipv6" + description: "IPv6 with uppercase letters is not canonical" + expr: "ip.isCanonical('2001:DB8::68')" + value: { bool_value: false } + } + test { + name: "parse_ipv6" + expr: "string(ip('2001:db8::68'))" + value: { string_value: "2001:db8::68" } + } + test { + name: "parse_invalid_ipv6" + expr: "ip('2001:db8:::68')" + eval_error: { + errors: { + message: "IP Address '2001:db8:::68' parse error" + } + } + } + test { + name: "ip_to_string" + description: "Converting an IP address back to a string" + expr: "string(ip('192.168.0.1'))" + value: { string_value: "192.168.0.1" } + } + test { + name: "ip_type" + description: "Check the type of the IP object" + expr: "type(ip('192.168.0.1')) == net.IP" + value: { bool_value: true } + } + test { + name: "is_ip_cidr_compile_error" + description: "isIP should not accept a CIDR type" + expr: "isIP(cidr('192.168.0.0/24'))" + disable_check: false + eval_error: { + errors: { + message: "found no matching overload for 'isIP' applied to '(net.CIDR)'" + } + } + } +} +section { + name: "ipv4" + description: "Tests for properties and methods of IPv4 addresses" + test { + name: "ipv4_family" + expr: "ip('192.168.0.1').family()" + value: { int64_value: 4 } + } + test { + name: "ipv4_is_unspecified_true" + expr: "ip('0.0.0.0').isUnspecified()" + value: { bool_value: true } + } + test { + name: "ipv4_is_unspecified_false" + expr: "ip('127.0.0.1').isUnspecified()" + value: { bool_value: false } + } + test { + name: "ipv4_is_loopback_true" + expr: "ip('127.0.0.1').isLoopback()" + value: { bool_value: true } + } + test { + name: "ipv4_is_loopback_false" + expr: "ip('1.2.3.4').isLoopback()" + value: { bool_value: false } + } + test { + name: "ipv4_is_global_unicast_true" + expr: "ip('192.168.0.1').isGlobalUnicast()" + value: { bool_value: true } + } + test { + name: "ipv4_is_global_unicast_false" + expr: "ip('255.255.255.255').isGlobalUnicast()" + value: { bool_value: false } + } + test { + name: "is_link_local_multicast_true" + expr: "ip('224.0.0.1').isLinkLocalMulticast()" + value: { bool_value: true } + } + test { + name: "is_link_local_multicast_false" + expr: "ip('224.0.1.1').isLinkLocalMulticast()" + value: { bool_value: false } + } + test { + name: "is_link_local_unicast_true" + expr: "ip('169.254.169.254').isLinkLocalUnicast()" + value: { bool_value: true } + } + test { + name: "is_link_local_unicast_false" + expr: "ip('192.168.0.1').isLinkLocalUnicast()" + value: { bool_value: false } + } +} +section { + name: "ipv6" + description: "Tests for properties and methods of IPv6 addresses" + test { + name: "family" + expr: "ip('2001:db8::68').family()" + value: { int64_value: 6 } + } + test { + name: "is_unspecified_true" + expr: "ip('::').isUnspecified()" + value: { bool_value: true } + } + test { + name: "is_loopback_true" + expr: "ip('::1').isLoopback()" + value: { bool_value: true } + } + test { + name: "is_global_unicast_true" + expr: "ip('2001:db8::abcd').isGlobalUnicast()" + value: { bool_value: true } + } + test { + name: "is_global_unicast_false" + expr: "ip('ff00::1').isGlobalUnicast()" + value: { bool_value: false } + } + test { + name: "is_link_local_multicast_true" + expr: "ip('ff02::1').isLinkLocalMulticast()" + value: { bool_value: true } + } + test { + name: "is_link_local_multicast_false" + expr: "ip('fd00::1').isLinkLocalMulticast()" + value: { bool_value: false } + } + test { + name: "is_link_local_unicast_true" + expr: "ip('fe80::1').isLinkLocalUnicast()" + value: { bool_value: true } + } + test { + name: "is_link_local_unicast_false" + expr: "ip('fd80::1').isLinkLocalUnicast()" + value: { bool_value: false } + } +} + +section { + name: "cidr" + description: "Tests for CIDR parsing and range checking" + test { + name: "parse_cidr_ipv4" + expr: "type(cidr('192.168.0.0/24')) == net.CIDR" + value: { bool_value: true } + } + test { + name: "parse_invalid_cidr_ipv4" + expr: "cidr('192.168.0.0/')" + eval_error: { + errors: { + message: "network address parse error during conversion from string" + } + } + } + test { + name: "cidr_contains_ip_ipv4_object" + expr: "cidr('192.168.0.0/24').containsIP(ip('192.168.0.1'))" + value: { bool_value: true } + } + test { + name: "cidr_does_not_contain_ip_ipv4_object" + expr: "cidr('192.168.0.0/24').containsIP(ip('192.168.1.1'))" + value: { bool_value: false } + } + test { + name: "cidr_contains_ip_ipv4_string" + expr: "cidr('192.168.0.0/24').containsIP('192.168.0.1')" + value: { bool_value: true } + } + test { + name: "cidr_does_not_contain_ip_ipv4_string" + expr: "cidr('192.168.0.0/24').containsIP('192.168.1.1')" + value: { bool_value: false } + } + test { + name: "cidr_contains_cidr_ipv4_object" + expr: "cidr('192.168.0.0/24').containsCIDR(cidr('192.168.0.0/25'))" + value: { bool_value: true } + } + test { + name: "cidr_contains_cidr_ipv4_object_32" + expr: "cidr('192.168.0.0/24').containsCIDR(cidr('192.168.0.1/32'))" + value: { bool_value: true } + } + test { + name: "cidr_does_not_contain_cidr_ipv4_object" + expr: "cidr('192.168.0.0/24').containsCIDR(cidr('192.168.0.0/23'))" + value: { bool_value: false } + } + test { + name: "cidr_contains_cidr_ipv4_string" + expr: "cidr('192.168.0.0/24').containsCIDR('192.168.0.0/25')" + value: { bool_value: true } + } + test { + name: "cidr_get_ip_ipv4" + expr: "cidr('192.168.0.0/24').ip() == ip('192.168.0.0')" + value: { bool_value: true } + } + test { + name: "cidr_masked_ipv4" + expr: "cidr('192.168.0.1/24').masked()" + value: { string_value: "192.168.0.0/24" } + } + test { + name: "cidr_prefix_length_ipv4" + expr: "cidr('192.168.0.0/24').prefixLength()" + value: { int64_value: 24 } + } + test { + name: "parse_cidr_ipv6" + expr: "string(cidr('2001:db8::/32'))" + value: { string_value: "2001:db8::/32" } + } + test { + name: "cidr_contains_ip_ipv6_object" + expr: "cidr('2001:db8::/32').containsIP(ip('2001:db8::1'))" + value: { bool_value: true } + } + test { + name: "cidr_contains_cidr_ipv6_object" + expr: "cidr('2001:db8::/32').containsCIDR(cidr('2001:db8::/33'))" + value: { bool_value: true } + } + test { + name: "cidr_get_ip_ipv6" + expr: "cidr('2001:db8::/32').ip() == ip('2001:db8::')" + value: { bool_value: "true" } + } + test { + name: "cidr_prefix_length_ipv6" + expr: "cidr('2001:db8::/32').prefixLength()" + value: { int64_value: 32 } + } + test { + name: "cidr_to_string" + description: "Converting a CIDR back to a string" + expr: "string(cidr('192.168.0.0/24'))" + value: { string_value: "192.168.0.0/24" } + } + test { + name: "cidr_type" + description: "Check the type of the CIDR object" + expr: "type(cidr('192.168.0.0/24')) == net.CIDR" + value: { bool_value: true } + } +} From 440e3b66d6c6364538c53ddd81f63639a376afca Mon Sep 17 00:00:00 2001 From: TristonianJones Date: Fri, 30 Jan 2026 14:28:51 -0800 Subject: [PATCH 2/5] Additional test cases to cover parse and equivalence edge cases --- tests/simple/testdata/network_ext.textproto | 101 +++++++++++++++++++- 1 file changed, 96 insertions(+), 5 deletions(-) diff --git a/tests/simple/testdata/network_ext.textproto b/tests/simple/testdata/network_ext.textproto index ef6bd65..f651e1c 100644 --- a/tests/simple/testdata/network_ext.textproto +++ b/tests/simple/testdata/network_ext.textproto @@ -70,6 +70,24 @@ section { } } } + test { + name: "parse_invalid_ipv6_with_zone" + expr: "ip('fe80::1%en0')" + eval_error: { + errors: { + message: "IP Address with zone value is not allowed" + } + } + } + test { + name: "parse_invalid_ipv4_in_ipv6" + expr: "ip('::ffff:192.168.0.1')" + eval_error: { + errors: { + message: "IPv4-mapped IPv6 address is not allowed" + } + } + } test { name: "ip_to_string" description: "Converting an IP address back to a string" @@ -152,6 +170,26 @@ section { expr: "ip('192.168.0.1').isLinkLocalUnicast()" value: { bool_value: false } } + test { + name: "ipv4_equals" + expr: "ip('127.0.0.1') == ip('127.0.0.1')" + value: { bool_value: true } + } + test { + name: "ipv4_not_equals" + expr: "ip('127.0.0.1') == ip('10.0.0.1') + value: { bool_value: false } + } + test { + name: "ipv4_equals_ipv6" + expr: "ip('::ffff:c0a8:1') == ip('192.168.0.1')" + value: { bool_value: true } + } + test { + name: "ipv4_not_equals_ipv6" + expr: "ip('::ffff:c0a8:1') == ip('192.168.10.1')" + value: { bool_value: false } + } } section { name: "ipv6" @@ -201,6 +239,16 @@ section { expr: "ip('fd80::1').isLinkLocalUnicast()" value: { bool_value: false } } + test { + name: "ipv6_equals" + expr: "ip('2001:db8::1') == ip('2001:DB8::1')" + value: { bool_value: true } + } + test { + name: "ipv6_not_equals" + expr: "ip('::') == ip('::ffff') + value: { bool_value: false } + } } section { @@ -220,6 +268,24 @@ section { } } } + test { + name: "parse_invalid_cidr_with_zone" + expr: "ip('fe80::1%en0/24')" + eval_error: { + errors: { + message: "CIDR with zone value is not allowed" + } + } + } + test { + name: "parse_invalid_cidr_ipv4_in_ipv6" + expr: "ip('::ffff:192.168.0.1/24')" + eval_error: { + errors: { + message: "IPv4-mapped IPv6 address is not allowed" + } + } + } test { name: "cidr_contains_ip_ipv4_object" expr: "cidr('192.168.0.0/24').containsIP(ip('192.168.0.1'))" @@ -239,7 +305,7 @@ section { name: "cidr_does_not_contain_ip_ipv4_string" expr: "cidr('192.168.0.0/24').containsIP('192.168.1.1')" value: { bool_value: false } - } + } test { name: "cidr_contains_cidr_ipv4_object" expr: "cidr('192.168.0.0/24').containsCIDR(cidr('192.168.0.0/25'))" @@ -251,7 +317,7 @@ section { value: { bool_value: true } } test { - name: "cidr_does_not_contain_cidr_ipv4_object" + name: "cidr_not_contains_cidr_ipv4_object" expr: "cidr('192.168.0.0/24').containsCIDR(cidr('192.168.0.0/23'))" value: { bool_value: false } } @@ -260,6 +326,26 @@ section { expr: "cidr('192.168.0.0/24').containsCIDR('192.168.0.0/25')" value: { bool_value: true } } + test { + name: "cidr_contains_cidr" + expr: "cidr('10.0.0.0/8').containsCIDR(cidr('10.0.0.0/8')) + value: { bool_value: true } + } + test { + name: "cidr_contains_cidr_ipv4_exact" + expr: "cidr('10.0.0.0/8').containsCIDR('10.0.0.0/8') + value: { bool_value: true } + } + test { + name: "cidr_ipv6_not_contains_ip_ipv4_object" + expr: "cidr('2001:db8::/32').containsIP(ip('192.168.1.1'))" + value: { bool_value: false } + } + test { + name: "cidr_ipv4_not_contains_ip_ipv6_object" + expr: "cidr('192.168.1.1/32').containsIP(ip('2001:db8::1'))" + value: { bool_value: false } + } test { name: "cidr_get_ip_ipv4" expr: "cidr('192.168.0.0/24').ip() == ip('192.168.0.0')" @@ -267,8 +353,8 @@ section { } test { name: "cidr_masked_ipv4" - expr: "cidr('192.168.0.1/24').masked()" - value: { string_value: "192.168.0.0/24" } + expr: "cidr('192.168.0.1/24').masked() == cidr('192.168.0.0/24')" + value: { bool_value: true } } test { name: "cidr_prefix_length_ipv4" @@ -290,10 +376,15 @@ section { expr: "cidr('2001:db8::/32').containsCIDR(cidr('2001:db8::/33'))" value: { bool_value: true } } + test { + name: "cidr_ipv4_contains_ip_ipv6_object" + expr: "cidr('192.168.0.0/24').containsIP(ip('2001:db8::1'))" + value: { bool_value: true } + } test { name: "cidr_get_ip_ipv6" expr: "cidr('2001:db8::/32').ip() == ip('2001:db8::')" - value: { bool_value: "true" } + value: { bool_value: true } } test { name: "cidr_prefix_length_ipv6" From fc8449d7c48e51423aa7c82c80551b90b3f34569 Mon Sep 17 00:00:00 2001 From: TristonianJones Date: Fri, 30 Jan 2026 14:34:04 -0800 Subject: [PATCH 3/5] Additional test case for cross IPv4, IPv6 comparison within CIDR mask --- tests/simple/testdata/network_ext.textproto | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/tests/simple/testdata/network_ext.textproto b/tests/simple/testdata/network_ext.textproto index f651e1c..2bfbe0c 100644 --- a/tests/simple/testdata/network_ext.textproto +++ b/tests/simple/testdata/network_ext.textproto @@ -177,7 +177,7 @@ section { } test { name: "ipv4_not_equals" - expr: "ip('127.0.0.1') == ip('10.0.0.1') + expr: "ip('127.0.0.1') == ip('10.0.0.1')" value: { bool_value: false } } test { @@ -246,7 +246,7 @@ section { } test { name: "ipv6_not_equals" - expr: "ip('::') == ip('::ffff') + expr: "ip('::') == ip('::ffff')" value: { bool_value: false } } } @@ -286,6 +286,21 @@ section { } } } + test { + name: "cidr_equals" + expr: "cidr('127.0.0.1/24') == cidr('127.0.0.1/24')" + value: { bool_value: true } + } + test { + name: "cidr_not_equals" + expr: "cidr('192.0.0.1/32') == cidr('10.0.0.1/8')" + value: { bool_value: false } + } + test { + name: "cidr_not_equals_ipv4_ipv6" + expr: "cidr('2001:db8::/32') == cidr('10.0.0.1/32')" + value: { bool_value: false } + } test { name: "cidr_contains_ip_ipv4_object" expr: "cidr('192.168.0.0/24').containsIP(ip('192.168.0.1'))" From 3f7ebfd2cb747aafcb9e5d12819aefc5e423470d Mon Sep 17 00:00:00 2001 From: TristonianJones Date: Fri, 30 Jan 2026 16:59:23 -0800 Subject: [PATCH 4/5] Fixes to test cases --- tests/simple/testdata/network_ext.textproto | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/simple/testdata/network_ext.textproto b/tests/simple/testdata/network_ext.textproto index 2bfbe0c..e06166e 100644 --- a/tests/simple/testdata/network_ext.textproto +++ b/tests/simple/testdata/network_ext.textproto @@ -270,7 +270,7 @@ section { } test { name: "parse_invalid_cidr_with_zone" - expr: "ip('fe80::1%en0/24')" + expr: "cidr('fe80::1%en0/24')" eval_error: { errors: { message: "CIDR with zone value is not allowed" @@ -279,7 +279,7 @@ section { } test { name: "parse_invalid_cidr_ipv4_in_ipv6" - expr: "ip('::ffff:192.168.0.1/24')" + expr: "cidr('::ffff:192.168.0.1/24')" eval_error: { errors: { message: "IPv4-mapped IPv6 address is not allowed" @@ -343,12 +343,12 @@ section { } test { name: "cidr_contains_cidr" - expr: "cidr('10.0.0.0/8').containsCIDR(cidr('10.0.0.0/8')) + expr: "cidr('10.0.0.0/8').containsCIDR(cidr('10.0.0.0/8'))" value: { bool_value: true } } test { name: "cidr_contains_cidr_ipv4_exact" - expr: "cidr('10.0.0.0/8').containsCIDR('10.0.0.0/8') + expr: "cidr('10.0.0.0/8').containsCIDR('10.0.0.0/8')" value: { bool_value: true } } test { @@ -394,7 +394,7 @@ section { test { name: "cidr_ipv4_contains_ip_ipv6_object" expr: "cidr('192.168.0.0/24').containsIP(ip('2001:db8::1'))" - value: { bool_value: true } + value: { bool_value: false } } test { name: "cidr_get_ip_ipv6" From e10c52ecb238754e506cd6777dd23f285089ab58 Mon Sep 17 00:00:00 2001 From: TristonianJones Date: Tue, 3 Feb 2026 08:48:35 -0800 Subject: [PATCH 5/5] Remove redundant test --- tests/simple/testdata/network_ext.textproto | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/simple/testdata/network_ext.textproto b/tests/simple/testdata/network_ext.textproto index e06166e..3bd7f66 100644 --- a/tests/simple/testdata/network_ext.textproto +++ b/tests/simple/testdata/network_ext.textproto @@ -391,11 +391,6 @@ section { expr: "cidr('2001:db8::/32').containsCIDR(cidr('2001:db8::/33'))" value: { bool_value: true } } - test { - name: "cidr_ipv4_contains_ip_ipv6_object" - expr: "cidr('192.168.0.0/24').containsIP(ip('2001:db8::1'))" - value: { bool_value: false } - } test { name: "cidr_get_ip_ipv6" expr: "cidr('2001:db8::/32').ip() == ip('2001:db8::')"