Skip to content
Merged
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
56 changes: 43 additions & 13 deletions cedar-testing/src/cedar_test_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
*/

//! Definition of a `CedarTestImplementation` trait that describes an
//! implementation of Cedar to use during testing.
//! implementation of Cedar to use during testing. This trait is used for
//! running the integration tests and for performing randomized differential
//! testing (see <https://github.com/cedar-policy/cedar-spec>).

pub use cedar_policy::frontend::is_authorized::InterfaceResponse;
use cedar_policy_core::ast::{Expr, PolicySet, Request, Value};
Expand Down Expand Up @@ -49,6 +51,14 @@ impl<T> TestResult<T> {
Self::Failure(err) => panic!("{msg}: {err}"),
}
}

/// Apply a function to the success value.
pub fn map<F: FnOnce(T) -> T>(self, f: F) -> Self {
match self {
Self::Success(t) => Self::Success(f(t)),
Self::Failure(err) => Self::Failure(err),
}
}
}

/// Simple wrapper around u128 to remind ourselves that timing info is in microseconds.
Expand Down Expand Up @@ -116,28 +126,44 @@ pub trait CedarTestImplementation {

/// `ErrorComparisonMode` that should be used for this `CedarTestImplementation`
fn error_comparison_mode(&self) -> ErrorComparisonMode;

/// `ValidationComparisonMode` that should be used for this `CedarTestImplementation`
fn validation_comparison_mode(&self) -> ValidationComparisonMode;
}

/// Specifies how errors coming from a `CedarTestImplementation` should be
/// compared against errors coming from the Rust implementation.
/// Specifies how authorization errors coming from this [`CedarTestImplementation`]
/// should be compared against errors coming from another implementation.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ErrorComparisonMode {
/// Don't compare errors at all; the `CedarTestImplementation` is not
/// expected to produce errors matching the Rust implementation's errors in
/// any way.
/// In fact, the `CedarTestImplementation` will be expected to never report
/// errors.
/// Don't compare errors at all. The [`CedarTestImplementation`] will be
/// expected to never report errors.
Ignore,
/// The `CedarTestImplementation` is expected to produce "error messages" that
/// are actually just the id of the erroring policy. This will be compared to
/// ensure that the `CedarTestImplementation` agrees with the Rust
/// implementation on which policies produce errors.
/// The [`CedarTestImplementation`] is expected to produce "error messages"
/// that are actually just the id of the erroring policy. This will used to
/// ensure that different implementations agree on which policies produce
/// errors.
PolicyIds,
/// The `CedarTestImplementation` is expected to produce error messages that
/// The [`CedarTestImplementation`] is expected to produce error messages that
/// exactly match the Rust implementation's error messages' `Display` text.
Full,
}

/// Specifies how validation results from this [`CedarTestImplementation`] should
/// be compared against validation results from another implementation.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ValidationComparisonMode {
/// When comparing this [`CedarTestImplementation`] against another
/// implementation, `validate` should return a `validation_passed` result
/// for any input that the other implementation says is valid. This allows
/// for flexibility in cases where the other implementation (incorrectly)
/// says the input is invalid due to weaker typing precision.
AgreeOnValid,
/// When comparing this [`CedarTestImplementation`] against another
/// implementation, the valid / not valid decision should agree for all
/// inputs, although the exact validation errors may differ.
AgreeOnAll,
}

/// Basic struct to support implementing the `CedarTestImplementation` trait
#[derive(Debug, Default)]
pub struct RustEngine {}
Expand Down Expand Up @@ -231,4 +257,8 @@ impl CedarTestImplementation for RustEngine {
fn error_comparison_mode(&self) -> ErrorComparisonMode {
ErrorComparisonMode::PolicyIds
}

fn validation_comparison_mode(&self) -> ValidationComparisonMode {
ValidationComparisonMode::AgreeOnAll
}
}
13 changes: 9 additions & 4 deletions cedar-testing/src/integration_testing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -313,10 +313,15 @@ pub fn perform_integration_test_from_json_custom(
validation_result.errors
);
} else {
assert!(
!validation_result.validation_passed(),
"Expected that validation would fail in {test_name}, but it did not.",
);
match test_impl.validation_comparison_mode() {
ValidationComparisonMode::AgreeOnAll => {
assert!(
!validation_result.validation_passed(),
"Expected that validation would fail in {test_name}, but it did not.",
);
}
ValidationComparisonMode::AgreeOnValid => {} // ignore
}
}

for json_request in test.requests {
Expand Down
3 changes: 3 additions & 0 deletions cedar-testing/tests/cedar-policy-cli/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,12 @@
#![allow(clippy::expect_used)]
// PANIC SAFETY tests
#![allow(clippy::panic)]

mod corpus_tests;
#[cfg(feature = "decimal")]
mod decimal;
mod example_use_cases;
#[cfg(feature = "ipaddr")]
mod ip;
mod multi;

Expand Down
4 changes: 2 additions & 2 deletions cedar-testing/tests/cedar-policy/corpus_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@

//! Integration tests auto-generated using the differential tester.

use crate::integration_testing::perform_integration_test_from_json;
use crate::integration_testing::resolve_integration_test_path;
use cedar_testing::integration_testing::perform_integration_test_from_json;
use cedar_testing::integration_testing::resolve_integration_test_path;
use std::path::Path;

/// Path of the folder containing the corpus tests
Expand Down
4 changes: 1 addition & 3 deletions cedar-testing/tests/cedar-policy/decimal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

//! Integration tests targeting the decimal extension

use crate::integration_testing::perform_integration_test_from_json;
use cedar_testing::integration_testing::perform_integration_test_from_json;
use std::path::Path;

/// Path of the folder containing the JSON tests
Expand All @@ -25,13 +25,11 @@ fn folder() -> &'static Path {
}

#[test]
#[cfg(feature = "decimal")]
fn decimal_1() {
perform_integration_test_from_json(folder().join("1.json"));
}

#[test]
#[cfg(feature = "decimal")]
fn decimal_2() {
perform_integration_test_from_json(folder().join("2.json"));
}
2 changes: 1 addition & 1 deletion cedar-testing/tests/cedar-policy/example_use_cases.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

use crate::integration_testing::perform_integration_test_from_json;
use cedar_testing::integration_testing::perform_integration_test_from_json;
use std::path::Path;

/// Path of the folder containing the JSON tests
Expand Down
5 changes: 1 addition & 4 deletions cedar-testing/tests/cedar-policy/ip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

//! Integration tests targeting the ipaddr extension

use crate::integration_testing::perform_integration_test_from_json;
use cedar_testing::integration_testing::perform_integration_test_from_json;
use std::path::Path;

/// Path of the folder containing the JSON tests
Expand All @@ -25,19 +25,16 @@ fn folder() -> &'static Path {
}

#[test]
#[cfg(feature = "ipaddr")]
fn ip_1() {
perform_integration_test_from_json(folder().join("1.json"));
}

#[test]
#[cfg(feature = "ipaddr")]
fn ip_2() {
perform_integration_test_from_json(folder().join("2.json"));
}

#[test]
#[cfg(feature = "ipaddr")]
fn ip_3() {
perform_integration_test_from_json(folder().join("3.json"));
}
28 changes: 28 additions & 0 deletions cedar-testing/tests/cedar-policy/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any reason to have main.rs instead of lib.rs? this is a library crate, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No special reason, I was just copying the structure of the CLI tests. The files just contain #[test]s. Is lib or main more appropriate? Or something else?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lib.rs is used for crates that provide libraries, main.rs is used for crates compiling an executable. (main.rs usually contains a main() -- in fact, I'm surprised Cargo isn't complaining that this one doesn't.) See https://doc.rust-lang.org/cargo/guide/project-layout.html

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on that link, I think that using a main.rs file in the tests subfolder is the correct structure.

If a binary, example, bench, or integration test consists of multiple source files, place a main.rs file along with the extra modules within a subdirectory of the src/bin, examples, benches, or tests directory. The name of the executable will be the directory name.

Also learned: the reason that the cedar-policy integration tests weren't running after #707 was the nested directory structure I introduced under tests/.

* Copyright 2022-2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* 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.
*/

// PANIC SAFETY tests
#![allow(clippy::expect_used)]
// PANIC SAFETY tests
#![allow(clippy::panic)]

mod corpus_tests;
#[cfg(feature = "decimal")]
mod decimal;
mod example_use_cases;
#[cfg(feature = "ipaddr")]
mod ip;
mod multi;
2 changes: 1 addition & 1 deletion cedar-testing/tests/cedar-policy/multi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

//! Integration tests that involve interactions between multiple policies

use crate::integration_testing::perform_integration_test_from_json;
use cedar_testing::integration_testing::perform_integration_test_from_json;
use std::path::Path;

/// Path of the folder containing the JSON tests
Expand Down