From f25121b78eb630e18351f74e4d993ca6ebe74e83 Mon Sep 17 00:00:00 2001 From: Aayush Tiwari Date: Mon, 13 Jul 2026 17:20:44 +0530 Subject: [PATCH 1/2] Implement UUID builtins Signed-off-by: Aayush Tiwari --- .../opa/ast/builtin/BuiltinRegistry.java | 1 + .../opa/ast/builtin/impls/UUIDBuiltins.java | 218 ++++++++++++++++++ .../ast/builtin/impls/UUIDBuiltinsTest.java | 96 ++++++++ 3 files changed, 315 insertions(+) create mode 100644 opa-evaluator/src/main/java/io/github/open_policy_agent/opa/ast/builtin/impls/UUIDBuiltins.java create mode 100644 opa-evaluator/src/test/java/io/github/open_policy_agent/opa/ast/builtin/impls/UUIDBuiltinsTest.java diff --git a/opa-evaluator/src/main/java/io/github/open_policy_agent/opa/ast/builtin/BuiltinRegistry.java b/opa-evaluator/src/main/java/io/github/open_policy_agent/opa/ast/builtin/BuiltinRegistry.java index 17a055b2..58b526fa 100644 --- a/opa-evaluator/src/main/java/io/github/open_policy_agent/opa/ast/builtin/BuiltinRegistry.java +++ b/opa-evaluator/src/main/java/io/github/open_policy_agent/opa/ast/builtin/BuiltinRegistry.java @@ -29,6 +29,7 @@ public class BuiltinRegistry { HexBuiltins.class, StringBuiltins.class, PrintBuiltins.class, + UUIDBuiltins.class, }; public static final Map> diff --git a/opa-evaluator/src/main/java/io/github/open_policy_agent/opa/ast/builtin/impls/UUIDBuiltins.java b/opa-evaluator/src/main/java/io/github/open_policy_agent/opa/ast/builtin/impls/UUIDBuiltins.java new file mode 100644 index 00000000..b78a30d6 --- /dev/null +++ b/opa-evaluator/src/main/java/io/github/open_policy_agent/opa/ast/builtin/impls/UUIDBuiltins.java @@ -0,0 +1,218 @@ +package io.github.open_policy_agent.opa.ast.builtin.impls; + +import static io.github.open_policy_agent.opa.ast.builtin.impls.utils.ArgHelper.getArg; + +import io.github.open_policy_agent.opa.ast.builtin.OpaBuiltin; +import io.github.open_policy_agent.opa.ast.builtin.OpaType; +import io.github.open_policy_agent.opa.ast.types.RegoBigInt; +import io.github.open_policy_agent.opa.ast.types.RegoInt32; +import io.github.open_policy_agent.opa.ast.types.RegoObject; +import io.github.open_policy_agent.opa.ast.types.RegoString; +import io.github.open_policy_agent.opa.ast.types.RegoUndefined; +import io.github.open_policy_agent.opa.ast.types.RegoValue; +import io.github.open_policy_agent.opa.rego.EvaluationContext; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Map; +import java.util.UUID; +import java.util.function.BiFunction; + +public class UUIDBuiltins { + + private static final long UUID_EPOCH_OFFSET_100NS = 122192928000000000L; + + public static Map> builtins() { + UUIDBuiltins instance = new UUIDBuiltins(); + return Map.of( + "uuid.parse", instance::parse, + "uuid.rfc4122", instance::rfc4122); + } + + @OpaBuiltin( + name = "uuid.parse", + description = "Parses a UUID string into its RFC 4122 metadata.", + categories = {"uuid"}, + args = {@OpaType(type = "string", name = "uuid", description = "UUID string to parse")}, + result = @OpaType(type = "object", name = "result", description = "parsed UUID metadata")) + public RegoValue parse(EvaluationContext ctx, RegoValue[] args) { + String input = getArg(args, 0, RegoString.class).getValue(); + UUID uuid = parseUuid(input); + if (uuid == null) { + return RegoUndefined.INSTANCE; + } + + RegoObject result = new RegoObject(); + int version = uuid.version(); + result.setProperty("variant", new RegoString(variantName(uuid.variant()))); + result.setProperty("version", RegoInt32.of(version)); + + if (version == 1 || version == 2) { + byte[] bytes = toBytes(uuid); + result.setProperty("clocksequence", RegoInt32.of(clockSequence(uuid))); + result.setProperty("macvariables", new RegoString(macVariables(bytes[10]))); + result.setProperty("nodeid", new RegoString(nodeId(bytes))); + result.setProperty("time", new RegoBigInt((timestamp(uuid) - UUID_EPOCH_OFFSET_100NS) * 100L)); + + if (version == 2) { + result.setProperty("domain", new RegoString(domain(bytes[9]))); + result.setProperty("id", RegoInt32.of(ByteBuffer.wrap(bytes, 0, 4).getInt())); + } + } + + return result; + } + + @OpaBuiltin( + name = "uuid.rfc4122", + description = "Returns a version 4 RFC 4122 UUID.", + categories = {"uuid"}, + args = { + @OpaType( + type = "string", + name = "key", + description = "cache key for deterministic UUID generation during evaluation") + }, + result = @OpaType(type = "string", name = "uuid", description = "RFC 4122 UUID"), + nondeterministic = true) + public RegoString rfc4122(EvaluationContext ctx, RegoValue[] args) { + if (ctx != null && ctx.getNdBuiltinCache() != null) { + RegoValue cachedValue = ctx.getNdBuiltinCache().get("uuid.rfc4122", args); + if (cachedValue != null) { + return (RegoString) cachedValue; + } + } + + String key = getArg(args, 0, RegoString.class).getValue(); + RegoString result = new RegoString(deterministicVersion4Uuid(key)); + + if (ctx != null) { + if (ctx.getNdBuiltinCache() != null) { + ctx.getNdBuiltinCache().put("uuid.rfc4122", args, result); + } + ctx.recordNdCacheValue("uuid.rfc4122", args, result); + } + + return result; + } + + private UUID parseUuid(String input) { + String normalized = normalize(input); + if (normalized == null) { + return null; + } + + try { + return UUID.fromString(normalized); + } catch (IllegalArgumentException e) { + return null; + } + } + + private String normalize(String input) { + String value = input; + if (value.startsWith("urn:uuid:")) { + value = value.substring("urn:uuid:".length()); + } + if (value.startsWith("{") && value.endsWith("}")) { + value = value.substring(1, value.length() - 1); + } + if (value.length() == 32) { + value = + value.substring(0, 8) + + "-" + + value.substring(8, 12) + + "-" + + value.substring(12, 16) + + "-" + + value.substring(16, 20) + + "-" + + value.substring(20); + } + return value; + } + + private long timestamp(UUID uuid) { + long most = uuid.getMostSignificantBits(); + long timeLow = (most >>> 32) & 0xffffffffL; + long timeMid = (most >>> 16) & 0xffffL; + long timeHigh = most & 0x0fffL; + return (timeHigh << 48) | (timeMid << 32) | timeLow; + } + + private int clockSequence(UUID uuid) { + return (int) ((uuid.getLeastSignificantBits() >>> 48) & 0x3fffL); + } + + private String variantName(int variant) { + switch (variant) { + case 0: + return "Reserved"; + case 2: + return "RFC4122"; + case 6: + return "Microsoft"; + default: + return "Future"; + } + } + + private String domain(byte value) { + switch (Byte.toUnsignedInt(value)) { + case 0: + return "Person"; + case 1: + return "Group"; + case 2: + return "Org"; + default: + return "Invalid"; + } + } + + private String macVariables(byte value) { + int bits = value & 0b11; + switch (bits) { + case 0b11: + return "local:multicast"; + case 0b01: + return "global:multicast"; + case 0b10: + return "local:unicast"; + default: + return "global:unicast"; + } + } + + private String nodeId(byte[] bytes) { + StringBuilder result = new StringBuilder(17); + for (int i = 10; i < bytes.length; i++) { + if (i > 10) { + result.append("-"); + } + result.append(String.format("%02x", Byte.toUnsignedInt(bytes[i]))); + } + return result.toString(); + } + + private byte[] toBytes(UUID uuid) { + ByteBuffer buffer = ByteBuffer.wrap(new byte[16]); + buffer.putLong(uuid.getMostSignificantBits()); + buffer.putLong(uuid.getLeastSignificantBits()); + return buffer.array(); + } + + private String deterministicVersion4Uuid(String key) { + try { + byte[] bytes = + MessageDigest.getInstance("SHA-256").digest(key.getBytes(StandardCharsets.UTF_8)); + bytes[6] = (byte) ((bytes[6] & 0x0f) | 0x40); + bytes[8] = (byte) ((bytes[8] & 0x3f) | 0x80); + ByteBuffer buffer = ByteBuffer.wrap(bytes); + return new UUID(buffer.getLong(), buffer.getLong()).toString(); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException("SHA-256 digest is not available", e); + } + } +} diff --git a/opa-evaluator/src/test/java/io/github/open_policy_agent/opa/ast/builtin/impls/UUIDBuiltinsTest.java b/opa-evaluator/src/test/java/io/github/open_policy_agent/opa/ast/builtin/impls/UUIDBuiltinsTest.java new file mode 100644 index 00000000..6d24912f --- /dev/null +++ b/opa-evaluator/src/test/java/io/github/open_policy_agent/opa/ast/builtin/impls/UUIDBuiltinsTest.java @@ -0,0 +1,96 @@ +package io.github.open_policy_agent.opa.ast.builtin.impls; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import io.github.open_policy_agent.opa.ast.builtin.BuiltinRegistry; +import io.github.open_policy_agent.opa.ast.types.RegoBigInt; +import io.github.open_policy_agent.opa.ast.types.RegoInt32; +import io.github.open_policy_agent.opa.ast.types.RegoObject; +import io.github.open_policy_agent.opa.ast.types.RegoString; +import io.github.open_policy_agent.opa.ast.types.RegoUndefined; +import io.github.open_policy_agent.opa.ast.types.RegoValue; +import java.util.UUID; +import org.junit.jupiter.api.Test; + +class UUIDBuiltinsTest { + + private final UUIDBuiltins builtins = new UUIDBuiltins(); + + @Test + void registersUuidBuiltins() { + assertTrue(BuiltinRegistry.AllBuiltIns.containsKey("uuid.parse")); + assertTrue(BuiltinRegistry.AllBuiltIns.containsKey("uuid.rfc4122")); + } + + @Test + void parsesVersion4Uuid() { + RegoObject result = + (RegoObject) + builtins.parse( + null, new RegoValue[] {new RegoString("00000000-0000-4000-8000-000000000000")}); + + assertEquals(new RegoString("RFC4122"), result.getProperty("variant")); + assertEquals(RegoInt32.of(4), result.getProperty("version")); + } + + @Test + void parsesVersion2UuidMetadata() { + RegoObject result = + (RegoObject) + builtins.parse( + null, new RegoValue[] {new RegoString("000003e8-48b9-21ee-b200-325096b39f47")}); + + assertEquals(RegoInt32.of(12800), result.getProperty("clocksequence")); + assertEquals(new RegoString("Person"), result.getProperty("domain")); + assertEquals(RegoInt32.of(1000), result.getProperty("id")); + assertEquals(new RegoString("local:unicast"), result.getProperty("macvariables")); + assertEquals(new RegoString("32-50-96-b3-9f-47"), result.getProperty("nodeid")); + assertEquals(new RegoBigInt(1693566990121469600L), result.getProperty("time")); + assertEquals(new RegoString("RFC4122"), result.getProperty("variant")); + assertEquals(RegoInt32.of(2), result.getProperty("version")); + } + + @Test + void parsesAcceptedInputFormats() { + assertEquals( + RegoInt32.of(4), + ((RegoObject) + builtins.parse( + null, + new RegoValue[] {new RegoString("{00000000-0000-4000-8000-000000000000}")})) + .getProperty("version")); + assertEquals( + RegoInt32.of(2), + ((RegoObject) + builtins.parse( + null, + new RegoValue[] { + new RegoString("urn:uuid:000003e8-48b9-21ee-b200-325096b39f47") + })) + .getProperty("version")); + assertEquals( + RegoInt32.of(3), + ((RegoObject) + builtins.parse( + null, new RegoValue[] {new RegoString("38074da40b00388d9c3c362de965547a")})) + .getProperty("version")); + } + + @Test + void parseReturnsUndefinedForInvalidUuid() { + assertSame(RegoUndefined.INSTANCE, builtins.parse(null, new RegoValue[] {new RegoString("123")})); + } + + @Test + void rfc4122ReturnsConsistentVersion4UuidForSameKey() { + RegoString first = builtins.rfc4122(null, new RegoValue[] {new RegoString("key")}); + RegoString second = builtins.rfc4122(null, new RegoValue[] {new RegoString("key")}); + UUID parsed = UUID.fromString(first.getValue()); + + assertEquals(first, second); + assertEquals(4, parsed.version()); + assertEquals(2, parsed.variant()); + } +} From a48c1b880b1a8bb8adc5aa5f7ff4ac97e39fa8ca Mon Sep 17 00:00:00 2001 From: Aayush Tiwari Date: Sat, 1 Aug 2026 17:08:35 +0530 Subject: [PATCH 2/2] Address UUID builtin review feedback Signed-off-by: Aayush Tiwari --- .../opa/ast/builtin/impls/UUIDBuiltins.java | 126 +++++++++++------- .../ast/builtin/impls/UUIDBuiltinsTest.java | 53 +++++++- 2 files changed, 124 insertions(+), 55 deletions(-) diff --git a/opa-evaluator/src/main/java/io/github/open_policy_agent/opa/ast/builtin/impls/UUIDBuiltins.java b/opa-evaluator/src/main/java/io/github/open_policy_agent/opa/ast/builtin/impls/UUIDBuiltins.java index b78a30d6..7e44e4d8 100644 --- a/opa-evaluator/src/main/java/io/github/open_policy_agent/opa/ast/builtin/impls/UUIDBuiltins.java +++ b/opa-evaluator/src/main/java/io/github/open_policy_agent/opa/ast/builtin/impls/UUIDBuiltins.java @@ -3,25 +3,31 @@ import static io.github.open_policy_agent.opa.ast.builtin.impls.utils.ArgHelper.getArg; import io.github.open_policy_agent.opa.ast.builtin.OpaBuiltin; +import io.github.open_policy_agent.opa.ast.builtin.OpaDynamic; import io.github.open_policy_agent.opa.ast.builtin.OpaType; import io.github.open_policy_agent.opa.ast.types.RegoBigInt; import io.github.open_policy_agent.opa.ast.types.RegoInt32; import io.github.open_policy_agent.opa.ast.types.RegoObject; import io.github.open_policy_agent.opa.ast.types.RegoString; -import io.github.open_policy_agent.opa.ast.types.RegoUndefined; import io.github.open_policy_agent.opa.ast.types.RegoValue; import io.github.open_policy_agent.opa.rego.EvaluationContext; import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Arrays; import java.util.Map; import java.util.UUID; import java.util.function.BiFunction; +import java.util.regex.Pattern; public class UUIDBuiltins { private static final long UUID_EPOCH_OFFSET_100NS = 122192928000000000L; + private static final String RFC4122 = "uuid.rfc4122"; + private static final SecureRandom RANDOM = new SecureRandom(); + private static final Pattern CANONICAL_UUID_PATTERN = + Pattern.compile( + "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"); + private static final Pattern COMPACT_UUID_PATTERN = Pattern.compile("^[0-9a-fA-F]{32}$"); public static Map> builtins() { UUIDBuiltins instance = new UUIDBuiltins(); @@ -35,12 +41,17 @@ public static Map> description = "Parses a UUID string into its RFC 4122 metadata.", categories = {"uuid"}, args = {@OpaType(type = "string", name = "uuid", description = "UUID string to parse")}, - result = @OpaType(type = "object", name = "result", description = "parsed UUID metadata")) + result = + @OpaType( + type = "object", + name = "result", + description = "parsed UUID metadata", + dynamic = @OpaDynamic(keyType = "string", valueType = "any"))) public RegoValue parse(EvaluationContext ctx, RegoValue[] args) { String input = getArg(args, 0, RegoString.class).getValue(); UUID uuid = parseUuid(input); if (uuid == null) { - return RegoUndefined.INSTANCE; + return null; } RegoObject result = new RegoObject(); @@ -57,7 +68,8 @@ public RegoValue parse(EvaluationContext ctx, RegoValue[] args) { if (version == 2) { result.setProperty("domain", new RegoString(domain(bytes[9]))); - result.setProperty("id", RegoInt32.of(ByteBuffer.wrap(bytes, 0, 4).getInt())); + result.setProperty( + "id", new RegoBigInt(Integer.toUnsignedLong(ByteBuffer.wrap(bytes, 0, 4).getInt()))); } } @@ -77,21 +89,16 @@ public RegoValue parse(EvaluationContext ctx, RegoValue[] args) { result = @OpaType(type = "string", name = "uuid", description = "RFC 4122 UUID"), nondeterministic = true) public RegoString rfc4122(EvaluationContext ctx, RegoValue[] args) { - if (ctx != null && ctx.getNdBuiltinCache() != null) { - RegoValue cachedValue = ctx.getNdBuiltinCache().get("uuid.rfc4122", args); - if (cachedValue != null) { - return (RegoString) cachedValue; - } + RegoString cachedValue = getEvaluationCacheValue(ctx, args); + if (cachedValue != null) { + return cachedValue; } - String key = getArg(args, 0, RegoString.class).getValue(); - RegoString result = new RegoString(deterministicVersion4Uuid(key)); + getArg(args, 0, RegoString.class); + RegoString result = new RegoString(randomVersion4Uuid()); if (ctx != null) { - if (ctx.getNdBuiltinCache() != null) { - ctx.getNdBuiltinCache().put("uuid.rfc4122", args, result); - } - ctx.recordNdCacheValue("uuid.rfc4122", args, result); + ctx.recordNdCacheValue(RFC4122, args, result); } return result; @@ -111,26 +118,40 @@ private UUID parseUuid(String input) { } private String normalize(String input) { - String value = input; - if (value.startsWith("urn:uuid:")) { - value = value.substring("urn:uuid:".length()); - } - if (value.startsWith("{") && value.endsWith("}")) { - value = value.substring(1, value.length() - 1); - } - if (value.length() == 32) { - value = - value.substring(0, 8) - + "-" - + value.substring(8, 12) - + "-" - + value.substring(12, 16) - + "-" - + value.substring(16, 20) - + "-" - + value.substring(20); + String value; + switch (input.length()) { + case 32: + if (!COMPACT_UUID_PATTERN.matcher(input).matches()) { + return null; + } + return input.substring(0, 8) + + "-" + + input.substring(8, 12) + + "-" + + input.substring(12, 16) + + "-" + + input.substring(16, 20) + + "-" + + input.substring(20); + case 36: + value = input; + break; + case 38: + if (!input.startsWith("{") || !input.endsWith("}")) { + return null; + } + value = input.substring(1, input.length() - 1); + break; + case 45: + if (!input.startsWith("urn:uuid:")) { + return null; + } + value = input.substring("urn:uuid:".length()); + break; + default: + return null; } - return value; + return CANONICAL_UUID_PATTERN.matcher(value).matches() ? value : null; } private long timestamp(UUID uuid) { @@ -167,7 +188,7 @@ private String domain(byte value) { case 2: return "Org"; default: - return "Invalid"; + return "Domain" + Byte.toUnsignedInt(value); } } @@ -203,16 +224,25 @@ private byte[] toBytes(UUID uuid) { return buffer.array(); } - private String deterministicVersion4Uuid(String key) { - try { - byte[] bytes = - MessageDigest.getInstance("SHA-256").digest(key.getBytes(StandardCharsets.UTF_8)); - bytes[6] = (byte) ((bytes[6] & 0x0f) | 0x40); - bytes[8] = (byte) ((bytes[8] & 0x3f) | 0x80); - ByteBuffer buffer = ByteBuffer.wrap(bytes); - return new UUID(buffer.getLong(), buffer.getLong()).toString(); - } catch (NoSuchAlgorithmException e) { - throw new IllegalStateException("SHA-256 digest is not available", e); + private RegoString getEvaluationCacheValue(EvaluationContext ctx, RegoValue[] args) { + if (ctx == null) { + return null; + } + for (EvaluationContext.CacheCall call : + ctx.getNdCacheValues().getOrDefault(RFC4122, java.util.List.of())) { + if (Arrays.equals(call.getArgs(), args) && call.getResult() instanceof RegoString value) { + return value; + } } + return null; + } + + private String randomVersion4Uuid() { + byte[] bytes = new byte[16]; + RANDOM.nextBytes(bytes); + bytes[6] = (byte) ((bytes[6] & 0x0f) | 0x40); + bytes[8] = (byte) ((bytes[8] & 0x3f) | 0x80); + ByteBuffer buffer = ByteBuffer.wrap(bytes); + return new UUID(buffer.getLong(), buffer.getLong()).toString(); } } diff --git a/opa-evaluator/src/test/java/io/github/open_policy_agent/opa/ast/builtin/impls/UUIDBuiltinsTest.java b/opa-evaluator/src/test/java/io/github/open_policy_agent/opa/ast/builtin/impls/UUIDBuiltinsTest.java index 6d24912f..9106e974 100644 --- a/opa-evaluator/src/test/java/io/github/open_policy_agent/opa/ast/builtin/impls/UUIDBuiltinsTest.java +++ b/opa-evaluator/src/test/java/io/github/open_policy_agent/opa/ast/builtin/impls/UUIDBuiltinsTest.java @@ -1,7 +1,8 @@ package io.github.open_policy_agent.opa.ast.builtin.impls; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import io.github.open_policy_agent.opa.ast.builtin.BuiltinRegistry; @@ -9,8 +10,8 @@ import io.github.open_policy_agent.opa.ast.types.RegoInt32; import io.github.open_policy_agent.opa.ast.types.RegoObject; import io.github.open_policy_agent.opa.ast.types.RegoString; -import io.github.open_policy_agent.opa.ast.types.RegoUndefined; import io.github.open_policy_agent.opa.ast.types.RegoValue; +import io.github.open_policy_agent.opa.rego.EvaluationContext; import java.util.UUID; import org.junit.jupiter.api.Test; @@ -44,7 +45,7 @@ void parsesVersion2UuidMetadata() { assertEquals(RegoInt32.of(12800), result.getProperty("clocksequence")); assertEquals(new RegoString("Person"), result.getProperty("domain")); - assertEquals(RegoInt32.of(1000), result.getProperty("id")); + assertEquals(new RegoBigInt(1000L), result.getProperty("id")); assertEquals(new RegoString("local:unicast"), result.getProperty("macvariables")); assertEquals(new RegoString("32-50-96-b3-9f-47"), result.getProperty("nodeid")); assertEquals(new RegoBigInt(1693566990121469600L), result.getProperty("time")); @@ -80,17 +81,55 @@ void parsesAcceptedInputFormats() { @Test void parseReturnsUndefinedForInvalidUuid() { - assertSame(RegoUndefined.INSTANCE, builtins.parse(null, new RegoValue[] {new RegoString("123")})); + assertNull(builtins.parse(null, new RegoValue[] {new RegoString("123")})); } @Test - void rfc4122ReturnsConsistentVersion4UuidForSameKey() { - RegoString first = builtins.rfc4122(null, new RegoValue[] {new RegoString("key")}); - RegoString second = builtins.rfc4122(null, new RegoValue[] {new RegoString("key")}); + void parseRejectsLenientJavaUuidFieldWidths() { + assertNull(builtins.parse(null, new RegoValue[] {new RegoString("1-1-1-1-1")})); + } + + @Test + void parsesUnsignedVersion2Id() { + RegoObject result = + (RegoObject) + builtins.parse( + null, new RegoValue[] {new RegoString("ffffffff-48b9-21ee-b200-325096b39f47")}); + + assertEquals(new RegoBigInt(4294967295L), result.getProperty("id")); + } + + @Test + void parsesUnknownVersion2Domain() { + RegoObject result = + (RegoObject) + builtins.parse( + null, new RegoValue[] {new RegoString("000003e8-48b9-21ee-b203-325096b39f47")}); + + assertEquals(new RegoString("Domain3"), result.getProperty("domain")); + } + + @Test + void rfc4122ReturnsConsistentVersion4UuidForSameKeyDuringEvaluation() { + EvaluationContext ctx = new EvaluationContext.Builder().build(); + RegoString first = builtins.rfc4122(ctx, new RegoValue[] {new RegoString("key")}); + RegoString second = builtins.rfc4122(ctx, new RegoValue[] {new RegoString("key")}); UUID parsed = UUID.fromString(first.getValue()); assertEquals(first, second); assertEquals(4, parsed.version()); assertEquals(2, parsed.variant()); } + + @Test + void rfc4122ReturnsFreshValueForSameKeyInDifferentEvaluation() { + RegoString first = + builtins.rfc4122( + new EvaluationContext.Builder().build(), new RegoValue[] {new RegoString("key")}); + RegoString second = + builtins.rfc4122( + new EvaluationContext.Builder().build(), new RegoValue[] {new RegoString("key")}); + + assertNotEquals(first, second); + } }