diff --git a/CedarJava/src/test/java/com/cedarpolicy/SharedIntegrationTests.java b/CedarJava/src/test/java/com/cedarpolicy/SharedIntegrationTests.java index 261fd435..aa2e857b 100644 --- a/CedarJava/src/test/java/com/cedarpolicy/SharedIntegrationTests.java +++ b/CedarJava/src/test/java/com/cedarpolicy/SharedIntegrationTests.java @@ -38,6 +38,7 @@ import com.cedarpolicy.value.EntityUID; import com.cedarpolicy.serializer.JsonEUID; import com.cedarpolicy.value.Value; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.ObjectMapper; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; @@ -87,6 +88,16 @@ private Path resolveIntegrationTestPath(String path) { } } + /** The format a policy set or schema file is written in. */ + private enum JsonOrCedarFormat { + /** The Cedar (human-readable) format. */ + @JsonProperty("cedar") + Cedar, + /** The JSON format. */ + @JsonProperty("json") + Json, + } + /** * Directly corresponds to the structure of the JSON formatted tests files. The fields are * populated by Jackson when the test files are deserialized. @@ -99,6 +110,12 @@ private static class JsonTest { */ public String policies; + /** + * Format of the policy set file. Defaults to Cedar, matching the integration test format, + * for files that don't specify it. + */ + public JsonOrCedarFormat policyFormat = JsonOrCedarFormat.Cedar; + /** * File name of the file containing entities. Path is relative to the integration tests * root. @@ -112,6 +129,12 @@ private static class JsonTest { */ public String schema; + /** + * Format of the schema file. Defaults to Cedar, matching the integration test format, for + * files that don't specify it. + */ + public JsonOrCedarFormat schemaFormat = JsonOrCedarFormat.Cedar; + /** * Whether the given policies are expected to pass the validator with this schema, or not */ @@ -193,6 +216,7 @@ private static class JsonEntity { "tests/decimal/2.json", "tests/example_use_cases/1a.json", "tests/example_use_cases/2a.json", + "tests/example_use_cases/2a_json_schema.json", "tests/example_use_cases/2b.json", "tests/example_use_cases/2c.json", "tests/example_use_cases/3a.json", @@ -263,8 +287,8 @@ private DynamicContainer loadJsonTests(String jsonFile) throws InternalException test = OBJECT_MAPPER.reader().readValue(jsonIn, JsonTest.class); } Set entities = loadEntities(test.entities); - PolicySet policySet = PolicySet.parsePolicies(resolveIntegrationTestPath(test.policies)); - Schema schema = loadSchema(test.schema); + PolicySet policySet = loadPolicySet(test.policies, test.policyFormat); + Schema schema = loadSchema(test.schema, test.schemaFormat); return DynamicContainer.dynamicContainer( jsonFile, @@ -284,12 +308,27 @@ private DynamicContainer loadJsonTests(String jsonFile) throws InternalException schema))))); } - /** Load the schema file. */ - private Schema loadSchema(String schemaFile) throws IOException { + /** + * Load the policy set file. Only the Cedar policy format is supported; there is not yet a Java + * interface for parsing a policy set from its JSON (EST) representation. + */ + private PolicySet loadPolicySet(String policiesFile, JsonOrCedarFormat format) + throws InternalException, IOException { + if (format == JsonOrCedarFormat.Json) { + throw new UnsupportedOperationException( + "The JSON policy format is not supported by these tests yet: " + policiesFile); + } + return PolicySet.parsePolicies(resolveIntegrationTestPath(policiesFile)); + } + + /** Load the schema file, in either the Cedar or JSON schema format. */ + private Schema loadSchema(String schemaFile, JsonOrCedarFormat format) throws IOException { try (InputStream schemaStream = new FileInputStream(resolveIntegrationTestPath(schemaFile).toFile())) { String schemaText = new String(schemaStream.readAllBytes(), StandardCharsets.UTF_8); - return new Schema(schemaText); + return format == JsonOrCedarFormat.Json + ? new Schema(OBJECT_MAPPER.readTree(schemaText)) + : new Schema(schemaText); } } diff --git a/CedarJavaFFI/src/interface.rs b/CedarJavaFFI/src/interface.rs index 2bdbaa18..e072d2bf 100644 --- a/CedarJavaFFI/src/interface.rs +++ b/CedarJavaFFI/src/interface.rs @@ -506,12 +506,16 @@ pub fn validate_entities(input: &str) -> serde_json::Result { match CedarEntities::from_json_value(validate_entity_call.entities, Some(&schema)) { Err(error) => { + // Unwrap only the variants whose own `Display` impl summarizes instead of + // delegating, so the caller keeps the specific inner diagnostic. The rest + // are `#[error(transparent)]` or already interpolate their source, so the + // catch-all preserves their detail and keeps this compiling when upstream + // adds a variant. let err_message = match error { - EntitiesError::Serialization(err) => err.to_string(), EntitiesError::Deserialization(err) => err.to_string(), - EntitiesError::Duplicate(err) => err.to_string(), EntitiesError::TransitiveClosureError(err) => err.to_string(), EntitiesError::InvalidEntity(err) => err.to_string(), + err => err.to_string(), }; Ok(Answer::fail_bad_request(vec![err_message])) }