mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
Lints are added (deny) at crate level. In each offending file, the failing lints are explicitly allowed. Each file will be fixed in subsequent PRs. Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
360 lines
11 KiB
Rust
360 lines
11 KiB
Rust
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT License.
|
|
|
|
#![allow(clippy::unwrap_used, clippy::unused_trait_names)] // tests unwrap intentional failures and import ToString anonymously
|
|
|
|
use super::super::*;
|
|
use crate::Value;
|
|
use alloc::string::ToString;
|
|
use serde_json::json;
|
|
|
|
#[test]
|
|
fn test_target_deserialization_with_direct_schemas() {
|
|
let target_json = json!({
|
|
"name": "test_target",
|
|
"description": "A test target for validation",
|
|
"version": "1.0.0",
|
|
"resource_schema_selector": "type",
|
|
"resource_schemas": [
|
|
{
|
|
"type": "object",
|
|
"properties": {
|
|
"name": { "type": "string" },
|
|
"type": { "const": "user" }
|
|
},
|
|
"required": ["name", "type"]
|
|
},
|
|
{
|
|
"type": "object",
|
|
"properties": {
|
|
"name": { "type": "string" },
|
|
"type": { "const": "group" }
|
|
},
|
|
"required": ["name", "type"]
|
|
}
|
|
],
|
|
"effects": {
|
|
"allow": { "type": "boolean" },
|
|
"deny": { "type": "boolean" }
|
|
}
|
|
});
|
|
|
|
let target = Target::from_json_str(&target_json.to_string()).unwrap();
|
|
|
|
assert_eq!(target.name.as_ref(), "test_target");
|
|
assert_eq!(
|
|
target.description.as_ref().unwrap().as_ref(),
|
|
"A test target for validation"
|
|
);
|
|
assert_eq!(target.version.as_ref(), "1.0.0");
|
|
assert_eq!(target.resource_schema_selector.as_ref(), "type");
|
|
assert_eq!(target.resource_schemas.len(), 2);
|
|
assert_eq!(target.effects.len(), 2);
|
|
|
|
// Check that lookup table was populated
|
|
assert_eq!(target.resource_schema_lookup.len(), 2);
|
|
assert!(target
|
|
.resource_schema_lookup
|
|
.contains_key(&Value::String("user".into())));
|
|
assert!(target
|
|
.resource_schema_lookup
|
|
.contains_key(&Value::String("group".into())));
|
|
|
|
// Check that default_resource_schema is None since all schemas have the discriminator
|
|
assert!(target.default_resource_schema.is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn test_target_deserialization_with_mixed_schemas() {
|
|
let target_json = json!({
|
|
"name": "mixed_target",
|
|
"version": "1.0.0",
|
|
"resource_schema_selector": "resourceType",
|
|
"resource_schemas": [
|
|
{
|
|
"type": "object",
|
|
"properties": {
|
|
"id": { "type": "string" },
|
|
"resourceType": { "const": "storage" }
|
|
},
|
|
"required": ["id", "resourceType"]
|
|
},
|
|
{
|
|
"type": "object",
|
|
"properties": {
|
|
"id": { "type": "string" },
|
|
"name": { "type": "string" }
|
|
},
|
|
"required": ["id"]
|
|
}
|
|
],
|
|
"effects": {
|
|
"permit": { "type": "string" }
|
|
}
|
|
});
|
|
|
|
let target = Target::from_json_str(&target_json.to_string()).unwrap();
|
|
|
|
assert_eq!(target.name.as_ref(), "mixed_target");
|
|
assert!(target.description.is_none());
|
|
assert_eq!(target.resource_schema_selector.as_ref(), "resourceType");
|
|
|
|
// One schema has discriminator, one doesn't
|
|
assert_eq!(target.resource_schema_lookup.len(), 1);
|
|
assert!(target
|
|
.resource_schema_lookup
|
|
.contains_key(&Value::String("storage".into())));
|
|
|
|
assert!(target.default_resource_schema.is_some());
|
|
}
|
|
|
|
#[test]
|
|
fn test_target_deserialization_multiple_default_schemas_error() {
|
|
let target_json = json!({
|
|
"name": "multiple_default_target",
|
|
"version": "1.0.0",
|
|
"resource_schema_selector": "kind",
|
|
"resource_schemas": [
|
|
{
|
|
"type": "object",
|
|
"properties": {
|
|
"id": { "type": "string" },
|
|
"data": { "type": "object" }
|
|
},
|
|
"required": ["id"]
|
|
},
|
|
{
|
|
"type": "object",
|
|
"properties": {
|
|
"name": { "type": "string" },
|
|
"value": { "type": "number" }
|
|
},
|
|
"required": ["name"]
|
|
}
|
|
],
|
|
"effects": {
|
|
"allow": { "type": "boolean" },
|
|
"deny": { "type": "boolean" }
|
|
}
|
|
});
|
|
|
|
// No schemas have the discriminator field - this should fail with MultipleDefaultSchemas error
|
|
let result = Target::from_json_str(&target_json.to_string());
|
|
assert!(result.is_err());
|
|
|
|
let error = result.unwrap_err();
|
|
assert!(matches!(error, TargetError::MultipleDefaultSchemas(_)));
|
|
}
|
|
|
|
#[test]
|
|
fn test_target_deserialization_single_default_schema() {
|
|
let target_json = json!({
|
|
"name": "single_default_target",
|
|
"version": "1.0.0",
|
|
"resource_schema_selector": "kind",
|
|
"resource_schemas": [
|
|
{
|
|
"type": "object",
|
|
"properties": {
|
|
"id": { "type": "string" },
|
|
"data": { "type": "object" }
|
|
},
|
|
"required": ["id"]
|
|
}
|
|
],
|
|
"effects": {
|
|
"allow": { "type": "boolean" }
|
|
}
|
|
});
|
|
|
|
let target = Target::from_json_str(&target_json.to_string()).unwrap();
|
|
|
|
// Single schema without discriminator should work fine
|
|
assert_eq!(target.resource_schema_lookup.len(), 0);
|
|
assert!(target.default_resource_schema.is_some());
|
|
}
|
|
|
|
#[test]
|
|
fn test_target_deserialization_duplicate_discriminator_error() {
|
|
let target_json = json!({
|
|
"name": "duplicate_target",
|
|
"version": "1.0.0",
|
|
"resource_schema_selector": "type",
|
|
"resource_schemas": [
|
|
{
|
|
"type": "object",
|
|
"properties": {
|
|
"id": { "type": "string" },
|
|
"type": { "const": "duplicate" }
|
|
},
|
|
"required": ["id", "type"]
|
|
},
|
|
{
|
|
"type": "object",
|
|
"properties": {
|
|
"name": { "type": "string" },
|
|
"type": { "const": "duplicate" }
|
|
},
|
|
"required": ["name", "type"]
|
|
}
|
|
],
|
|
"effects": {
|
|
"allow": { "type": "boolean" }
|
|
}
|
|
});
|
|
|
|
let result = Target::from_json_str(&target_json.to_string());
|
|
|
|
assert!(result.is_err());
|
|
let error = result.unwrap_err();
|
|
assert!(matches!(error, TargetError::DuplicateConstantValue(_)));
|
|
}
|
|
|
|
#[test]
|
|
fn test_target_deserialization_missing_required_field() {
|
|
let target_json = json!({
|
|
"name": "incomplete_target",
|
|
"version": "1.0.0",
|
|
// Missing resource_schema_selector
|
|
"resource_schemas": [
|
|
{
|
|
"type": "object",
|
|
"properties": {
|
|
"id": { "type": "string" }
|
|
}
|
|
}
|
|
],
|
|
"effects": {}
|
|
});
|
|
|
|
let result = Target::from_json_str(&target_json.to_string());
|
|
assert!(result.is_err());
|
|
let error = result.unwrap_err();
|
|
assert!(matches!(
|
|
error,
|
|
TargetError::JsonParseError(_) | TargetError::DeserializationError(_)
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn test_target_deserialization_invalid_json() {
|
|
let invalid_json = "{ invalid json }";
|
|
|
|
let result = Target::from_json_str(invalid_json);
|
|
assert!(result.is_err());
|
|
|
|
let error = result.unwrap_err();
|
|
assert!(matches!(error, TargetError::JsonParseError(_)));
|
|
}
|
|
|
|
#[test]
|
|
fn test_target_deserialization_with_registry_schemas() {
|
|
// This test assumes that there are some schemas in the registries
|
|
// If the registries are empty, this test will fail with appropriate errors
|
|
|
|
let target_json = json!({
|
|
"name": "registry_target",
|
|
"version": "1.0.0",
|
|
"resource_schema_selector": "type",
|
|
"resource_schemas": [
|
|
"some_registry_schema_name" // This will be looked up from RESOURCE_SCHEMA_REGISTRY
|
|
],
|
|
"effects": {
|
|
"allow": "some_effect_schema_name" // This will be looked up from EFFECT_SCHEMA_REGISTRY
|
|
}
|
|
});
|
|
|
|
// This test will likely fail if the registries are empty, but it demonstrates
|
|
// the structure for testing registry-based schema resolution
|
|
let result = Target::from_json_str(&target_json.to_string());
|
|
|
|
// We expect this to fail with a "not found in registry" error since we haven't
|
|
// populated the registries with test data
|
|
if let Err(error) = result {
|
|
assert!(matches!(
|
|
error,
|
|
TargetError::JsonParseError(_) | TargetError::DeserializationError(_)
|
|
));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_target_deserialization_numeric_discriminator() {
|
|
let target_json = json!({
|
|
"name": "numeric_target",
|
|
"version": "1.0.0",
|
|
"resource_schema_selector": "level",
|
|
"resource_schemas": [
|
|
{
|
|
"type": "object",
|
|
"properties": {
|
|
"name": { "type": "string" },
|
|
"level": { "const": 1 }
|
|
},
|
|
"required": ["name", "level"]
|
|
},
|
|
{
|
|
"type": "object",
|
|
"properties": {
|
|
"name": { "type": "string" },
|
|
"level": { "const": 2 }
|
|
},
|
|
"required": ["name", "level"]
|
|
}
|
|
],
|
|
"effects": {
|
|
"grant": { "type": "string" }
|
|
}
|
|
});
|
|
|
|
let target = Target::from_json_str(&target_json.to_string()).unwrap();
|
|
|
|
// Check that numeric discriminator values work
|
|
assert_eq!(target.resource_schema_lookup.len(), 2);
|
|
assert!(target.resource_schema_lookup.contains_key(&Value::from(1)));
|
|
assert!(target.resource_schema_lookup.contains_key(&Value::from(2)));
|
|
assert!(target.default_resource_schema.is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn test_target_deserialization_boolean_discriminator() {
|
|
let target_json = json!({
|
|
"name": "boolean_target",
|
|
"version": "1.0.0",
|
|
"resource_schema_selector": "enabled",
|
|
"resource_schemas": [
|
|
{
|
|
"type": "object",
|
|
"properties": {
|
|
"name": { "type": "string" },
|
|
"enabled": { "const": true }
|
|
},
|
|
"required": ["name", "enabled"]
|
|
},
|
|
{
|
|
"type": "object",
|
|
"properties": {
|
|
"name": { "type": "string" },
|
|
"enabled": { "const": false }
|
|
},
|
|
"required": ["name", "enabled"]
|
|
}
|
|
],
|
|
"effects": {
|
|
"activate": { "type": "boolean" }
|
|
}
|
|
});
|
|
|
|
let target = Target::from_json_str(&target_json.to_string()).unwrap();
|
|
|
|
// Check that boolean discriminator values work
|
|
assert_eq!(target.resource_schema_lookup.len(), 2);
|
|
assert!(target
|
|
.resource_schema_lookup
|
|
.contains_key(&Value::from(true)));
|
|
assert!(target
|
|
.resource_schema_lookup
|
|
.contains_key(&Value::from(false)));
|
|
assert!(target.default_resource_schema.is_none());
|
|
}
|