Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .bazelversion
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
8.5.0
12 changes: 8 additions & 4 deletions policy/src/main/java/dev/cel/policy/CelPolicy.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
Expand Down Expand Up @@ -77,8 +78,7 @@ public abstract static class Builder {

public abstract Builder setPolicySource(CelPolicySource policySource);

// This should stay package-private to encourage add/set methods to be used instead.
abstract ImmutableMap.Builder<String, Object> metadataBuilder();
private final HashMap<String, Object> metadata = new HashMap<>();

public abstract Builder setMetadata(ImmutableMap<String, Object> value);

Expand All @@ -90,6 +90,10 @@ public List<Import> imports() {
return Collections.unmodifiableList(importList);
}

public Map<String, Object> metadata() {
return Collections.unmodifiableMap(metadata);
}

@CanIgnoreReturnValue
public Builder addImport(Import value) {
importList.add(value);
Expand All @@ -104,13 +108,13 @@ public Builder addImports(Collection<Import> values) {

@CanIgnoreReturnValue
public Builder putMetadata(String key, Object value) {
metadataBuilder().put(key, value);
metadata.put(key, value);
return this;
}

@CanIgnoreReturnValue
public Builder putMetadata(Map<String, Object> map) {
metadataBuilder().putAll(map);
metadata.putAll(map);
return this;
}

Expand Down
29 changes: 29 additions & 0 deletions policy/src/main/java/dev/cel/policy/testing/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
load("@rules_java//java:defs.bzl", "java_library")

package(
default_applicable_licenses = ["//:license"],
default_visibility = [
"//policy/testing:__pkg__",
],
)

java_library(
name = "policy_test_suite_helper",
testonly = True,
srcs = [
"PolicyTestSuiteHelper.java",
],
deps = [
"//bundle:cel",
"//common:cel_ast",
"//common:compiler_common",
"//common/formats:value_string",
"//policy",
"//policy:parser",
"//policy:parser_builder",
"//policy:policy_parser_context",
"//runtime:evaluation_exception",
"@maven//:com_google_guava_guava",
"@maven//:org_yaml_snakeyaml",
],
)
192 changes: 192 additions & 0 deletions policy/src/main/java/dev/cel/policy/testing/PolicyTestSuiteHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package dev.cel.policy.testing;

import static com.google.common.base.Strings.isNullOrEmpty;
import static java.nio.charset.StandardCharsets.UTF_8;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Ascii;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.Resources;
import dev.cel.bundle.Cel;
import dev.cel.common.CelAbstractSyntaxTree;
import dev.cel.common.CelValidationException;
import dev.cel.runtime.CelEvaluationException;
import java.io.IOException;
import java.net.URL;
import java.util.List;
import java.util.Map;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;

/**
* Helper to assist with policy testing.
*
**/
public final class PolicyTestSuiteHelper {

/**
* TODO
*/
public static PolicyTestSuite readTestSuite(String path) throws IOException {
Yaml yaml = new Yaml(new Constructor(PolicyTestSuite.class, new LoaderOptions()));
String testContent = readFile(path);

return yaml.load(testContent);
}

/**
* TODO
* @param yamlPath
* @return
* @throws IOException
*/
public static String readFromYaml(String yamlPath) throws IOException {
return readFile(yamlPath);
}

/**
* TestSuite describes a set of tests divided by section.
*
* <p>Visibility must be public for YAML deserialization to work. This is effectively
* package-private since the outer class is.
*/
@VisibleForTesting
public static final class PolicyTestSuite {
private String description;
private List<PolicyTestSection> section;

public void setDescription(String description) {
this.description = description;
}

public void setSection(List<PolicyTestSection> section) {
this.section = section;
}

public String getDescription() {
return description;
}

public List<PolicyTestSection> getSection() {
return section;
}

@VisibleForTesting
public static final class PolicyTestSection {
private String name;
private List<PolicyTestCase> tests;

public void setName(String name) {
this.name = name;
}

public void setTests(List<PolicyTestCase> tests) {
this.tests = tests;
}

public String getName() {
return name;
}

public List<PolicyTestCase> getTests() {
return tests;
}

@VisibleForTesting
public static final class PolicyTestCase {
private String name;
private Map<String, PolicyTestInput> input;
private String output;

public void setName(String name) {
this.name = name;
}

public void setInput(Map<String, PolicyTestInput> input) {
this.input = input;
}

public void setOutput(String output) {
this.output = output;
}

public String getName() {
return name;
}

public Map<String, PolicyTestInput> getInput() {
return input;
}

public String getOutput() {
return output;
}

@VisibleForTesting
public static final class PolicyTestInput {
private Object value;
private String expr;

public Object getValue() {
return value;
}

public void setValue(Object value) {
this.value = value;
}

public String getExpr() {
return expr;
}

public void setExpr(String expr) {
this.expr = expr;
}
}

public ImmutableMap<String, Object> toInputMap(Cel cel)
throws CelValidationException, CelEvaluationException {
ImmutableMap.Builder<String, Object> inputBuilder = ImmutableMap.builderWithExpectedSize(
input.size());
for (Map.Entry<String, PolicyTestInput> entry : input.entrySet()) {
String exprInput = entry.getValue().getExpr();
if (isNullOrEmpty(exprInput)) {
inputBuilder.put(entry.getKey(), entry.getValue().getValue());
} else {
CelAbstractSyntaxTree exprInputAst = cel.compile(exprInput).getAst();
inputBuilder.put(entry.getKey(), cel.createProgram(exprInputAst).eval());
}
}

return inputBuilder.buildOrThrow();
}
}
}
}


private static URL getResource(String path) {
return Resources.getResource(Ascii.toLowerCase(path));
}

private static String readFile(String path) throws IOException {
return Resources.toString(getResource(path), UTF_8);
}

private PolicyTestSuiteHelper() {}
}
1 change: 1 addition & 0 deletions policy/src/test/java/dev/cel/policy/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ java_library(
"//policy:policy_parser_context",
"//policy:source",
"//policy:validation_exception",
"//policy/testing:policy_test_suite_helper",
"//runtime",
"//runtime:function_binding",
"//runtime:late_function_binding",
Expand Down
24 changes: 6 additions & 18 deletions policy/src/test/java/dev/cel/policy/CelPolicyCompilerImplTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@

package dev.cel.policy;

import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.truth.Truth.assertThat;
import static dev.cel.policy.PolicyTestHelper.readFromYaml;
import static dev.cel.policy.testing.PolicyTestSuiteHelper.readFromYaml;
import static org.junit.Assert.assertThrows;

import com.google.common.collect.ImmutableList;
Expand All @@ -38,17 +37,15 @@
import dev.cel.parser.CelStandardMacro;
import dev.cel.parser.CelUnparserFactory;
import dev.cel.policy.PolicyTestHelper.K8sTagHandler;
import dev.cel.policy.PolicyTestHelper.PolicyTestSuite;
import dev.cel.policy.PolicyTestHelper.PolicyTestSuite.PolicyTestSection;
import dev.cel.policy.PolicyTestHelper.PolicyTestSuite.PolicyTestSection.PolicyTestCase;
import dev.cel.policy.PolicyTestHelper.PolicyTestSuite.PolicyTestSection.PolicyTestCase.PolicyTestInput;
import dev.cel.policy.PolicyTestHelper.TestYamlPolicy;
import dev.cel.policy.testing.PolicyTestSuiteHelper.PolicyTestSuite;
import dev.cel.policy.testing.PolicyTestSuiteHelper.PolicyTestSuite.PolicyTestSection;
import dev.cel.policy.testing.PolicyTestSuiteHelper.PolicyTestSuite.PolicyTestSection.PolicyTestCase;
import dev.cel.runtime.CelFunctionBinding;
import dev.cel.runtime.CelLateFunctionBindings;
import dev.cel.testing.testdata.SingleFileProto.SingleFile;
import dev.cel.testing.testdata.proto3.StandaloneGlobalEnum;
import java.io.IOException;
import java.util.Map;
import java.util.Optional;
import org.junit.Test;
import org.junit.runner.RunWith;
Expand Down Expand Up @@ -215,17 +212,8 @@ public void evaluateYamlPolicy_withCanonicalTestData(
// Compile then evaluate the policy
CelAbstractSyntaxTree compiledPolicyAst =
CelPolicyCompilerFactory.newPolicyCompiler(cel).build().compile(policy);
ImmutableMap.Builder<String, Object> inputBuilder = ImmutableMap.builder();
for (Map.Entry<String, PolicyTestInput> entry : testData.testCase.getInput().entrySet()) {
String exprInput = entry.getValue().getExpr();
if (isNullOrEmpty(exprInput)) {
inputBuilder.put(entry.getKey(), entry.getValue().getValue());
} else {
CelAbstractSyntaxTree exprInputAst = cel.compile(exprInput).getAst();
inputBuilder.put(entry.getKey(), cel.createProgram(exprInputAst).eval());
}
}
Object evalResult = cel.createProgram(compiledPolicyAst).eval(inputBuilder.buildOrThrow());
ImmutableMap<String, Object> inputMap = testData.testCase.toInputMap(cel);
Object evalResult = cel.createProgram(compiledPolicyAst).eval(inputMap);

// Assert
// Note that policies may either produce an optional or a non-optional result,
Expand Down
Loading
Loading