use openidauthzen::*;
#[test]
fn evaluation_request_should_serialize_all_required_fields() {
let req = EvaluationRequest {
subject: Subject {
subject_type: "user".to_owned(),
id: Some("alice@example.com".to_owned()),
properties: None,
},
action: Action { name: "can_read".to_owned(), properties: None },
resource: Resource {
resource_type: "account".to_owned(),
id: Some("123".to_owned()),
properties: None,
},
context: None,
};
let json = serde_json::to_value(&req).unwrap();
assert_eq!(json["subject"]["type"], "user");
assert_eq!(json["action"]["name"], "can_read");
assert_eq!(json["resource"]["type"], "account");
}
#[test]
fn evaluation_request_should_omit_context_when_none() {
let req = EvaluationRequest {
subject: Subject { subject_type: "user".to_owned(), id: None, properties: None },
action: Action { name: "read".to_owned(), properties: None },
resource: Resource { resource_type: "doc".to_owned(), id: None, properties: None },
context: None,
};
let json = serde_json::to_value(&req).unwrap();
assert!(json.get("context").is_none());
}
#[test]
fn evaluation_request_should_include_context_when_present() {
let mut ctx: Context = serde_json::Map::new();
ctx.insert("time".to_owned(), serde_json::json!("1985-10-26T01:22-07:00"));
let req = EvaluationRequest {
subject: Subject { subject_type: "user".to_owned(), id: Some("alice@example.com".to_owned()), properties: None },
action: Action { name: "can_read".to_owned(), properties: None },
resource: Resource { resource_type: "account".to_owned(), id: Some("123".to_owned()), properties: None },
context: Some(ctx),
};
let json = serde_json::to_value(&req).unwrap();
assert_eq!(json["context"]["time"], "1985-10-26T01:22-07:00");
}
#[test]
fn evaluation_request_should_roundtrip_spec_example() {
let json = r#"{
"subject": {"type": "user", "id": "alice@example.com"},
"resource": {"type": "account", "id": "123"},
"action": {"name": "can_read", "properties": {"method": "GET"}},
"context": {"time": "1985-10-26T01:22-07:00"}
}"#;
let req: EvaluationRequest = serde_json::from_str(json).unwrap();
assert_eq!(req.subject.subject_type, "user");
assert_eq!(req.action.properties.as_ref().unwrap()["method"], "GET");
assert_eq!(req.context.as_ref().unwrap()["time"], "1985-10-26T01:22-07:00");
let roundtripped: EvaluationRequest = serde_json::from_value(serde_json::to_value(&req).unwrap()).unwrap();
assert_eq!(roundtripped, req);
}
#[test]
fn evaluation_response_should_serialize_decision_true() {
let resp = EvaluationResponse { decision: true, context: None };
let json = serde_json::to_value(&resp).unwrap();
assert_eq!(json["decision"], true);
}
#[test]
fn evaluation_response_should_serialize_decision_false() {
let resp = EvaluationResponse { decision: false, context: None };
let json = serde_json::to_value(&resp).unwrap();
assert_eq!(json["decision"], false);
}
#[test]
fn evaluation_response_should_omit_context_when_none() {
let resp = EvaluationResponse { decision: true, context: None };
let json = serde_json::to_value(&resp).unwrap();
assert!(json.get("context").is_none());
}
#[test]
fn evaluation_response_should_roundtrip_spec_example() {
let json = r#"{"decision": true}"#;
let resp: EvaluationResponse = serde_json::from_str(json).unwrap();
assert!(resp.decision);
assert!(resp.context.is_none());
let roundtripped: EvaluationResponse = serde_json::from_value(serde_json::to_value(&resp).unwrap()).unwrap();
assert_eq!(roundtripped, resp);
}
#[test]
fn evaluations_request_should_serialize_with_defaults_and_items() {
let req = EvaluationsRequest {
subject: Some(Subject { subject_type: "user".to_owned(), id: Some("alice@example.com".to_owned()), properties: None }),
action: Some(Action { name: "can_read".to_owned(), properties: None }),
resource: None,
context: None,
evaluations: vec![
EvaluationItem {
subject: None,
action: None,
resource: Some(Resource { resource_type: "document".to_owned(), id: Some("boxcarring.md".to_owned()), properties: None }),
context: None,
},
],
options: None,
};
let json = serde_json::to_value(&req).unwrap();
assert_eq!(json["subject"]["type"], "user");
assert_eq!(json["action"]["name"], "can_read");
assert_eq!(json["evaluations"][0]["resource"]["id"], "boxcarring.md");
}
#[test]
fn evaluations_request_should_omit_all_optional_fields_when_none() {
let req = EvaluationsRequest {
subject: None,
action: None,
resource: None,
context: None,
evaluations: vec![],
options: None,
};
let json = serde_json::to_value(&req).unwrap();
assert!(json.get("subject").is_none());
assert!(json.get("action").is_none());
assert!(json.get("resource").is_none());
assert!(json.get("context").is_none());
assert!(json.get("options").is_none());
assert!(json["evaluations"].is_array());
}
#[test]
fn evaluations_request_should_include_options_when_present() {
let req = EvaluationsRequest {
subject: None,
action: None,
resource: None,
context: None,
evaluations: vec![],
options: Some(EvaluationsOptions {
evaluations_semantic: Some(EvaluationSemantic::DenyOnFirstDeny),
extra: None,
}),
};
let json = serde_json::to_value(&req).unwrap();
assert_eq!(json["options"]["evaluations_semantic"], "deny_on_first_deny");
}
#[test]
fn evaluations_request_should_roundtrip_batch_spec_example() {
let json = r#"{
"subject": {"type": "user", "id": "alice@example.com"},
"context": {"time": "2024-05-31T15:22-07:00"},
"evaluations": [
{"action": {"name": "can_read"}, "resource": {"type": "document", "id": "boxcarring.md"}},
{"action": {"name": "can_read"}, "resource": {"type": "document", "id": "subject-search.md"}}
]
}"#;
let req: EvaluationsRequest = serde_json::from_str(json).unwrap();
assert_eq!(req.evaluations.len(), 2);
assert_eq!(req.subject.as_ref().unwrap().id.as_deref(), Some("alice@example.com"));
let roundtripped: EvaluationsRequest = serde_json::from_value(serde_json::to_value(&req).unwrap()).unwrap();
assert_eq!(roundtripped, req);
}
#[test]
fn evaluation_item_should_omit_all_fields_when_none() {
let item = EvaluationItem {
subject: None,
action: None,
resource: None,
context: None,
};
let json = serde_json::to_value(&item).unwrap();
assert!(json.as_object().unwrap().is_empty());
}
#[test]
fn evaluation_item_should_override_subject_only() {
let item = EvaluationItem {
subject: Some(Subject { subject_type: "service".to_owned(), id: Some("svc-1".to_owned()), properties: None }),
action: None,
resource: None,
context: None,
};
let json = serde_json::to_value(&item).unwrap();
assert_eq!(json["subject"]["type"], "service");
assert!(json.get("action").is_none());
}
#[test]
fn evaluation_item_should_override_all_fields() {
let item = EvaluationItem {
subject: Some(Subject { subject_type: "user".to_owned(), id: Some("bob".to_owned()), properties: None }),
action: Some(Action { name: "can_edit".to_owned(), properties: None }),
resource: Some(Resource { resource_type: "doc".to_owned(), id: Some("1".to_owned()), properties: None }),
context: Some({
let mut m = serde_json::Map::new();
m.insert("key".to_owned(), serde_json::json!("val"));
m
}),
};
let json = serde_json::to_value(&item).unwrap();
assert_eq!(json["subject"]["id"], "bob");
assert_eq!(json["action"]["name"], "can_edit");
assert_eq!(json["resource"]["id"], "1");
assert_eq!(json["context"]["key"], "val");
}
#[test]
fn evaluations_options_should_omit_semantic_when_none() {
let opts = EvaluationsOptions {
evaluations_semantic: None,
extra: None,
};
let json = serde_json::to_value(&opts).unwrap();
assert!(json.as_object().unwrap().is_empty());
}
#[test]
fn evaluations_options_should_serialize_semantic_when_present() {
let opts = EvaluationsOptions {
evaluations_semantic: Some(EvaluationSemantic::ExecuteAll),
extra: None,
};
let json = serde_json::to_value(&opts).unwrap();
assert_eq!(json["evaluations_semantic"], "execute_all");
}
#[test]
fn evaluations_options_should_flatten_extra_fields() {
let mut extra = serde_json::Map::new();
extra.insert("another_option".to_owned(), serde_json::json!("value"));
let opts = EvaluationsOptions {
evaluations_semantic: Some(EvaluationSemantic::ExecuteAll),
extra: Some(extra),
};
let json = serde_json::to_value(&opts).unwrap();
assert_eq!(json["evaluations_semantic"], "execute_all");
assert_eq!(json["another_option"], "value");
}
#[test]
fn evaluations_options_should_deserialize_unknown_fields_into_extra() {
let json = r#"{"evaluations_semantic": "execute_all", "another_option": "value"}"#;
let opts: EvaluationsOptions = serde_json::from_str(json).unwrap();
assert_eq!(opts.evaluations_semantic, Some(EvaluationSemantic::ExecuteAll));
assert_eq!(opts.extra.as_ref().unwrap()["another_option"], "value");
}
#[test]
fn evaluation_semantic_should_serialize_execute_all() {
let json = serde_json::to_value(EvaluationSemantic::ExecuteAll).unwrap();
assert_eq!(json, "execute_all");
}
#[test]
fn evaluation_semantic_should_serialize_deny_on_first_deny() {
let json = serde_json::to_value(EvaluationSemantic::DenyOnFirstDeny).unwrap();
assert_eq!(json, "deny_on_first_deny");
}
#[test]
fn evaluation_semantic_should_serialize_permit_on_first_permit() {
let json = serde_json::to_value(EvaluationSemantic::PermitOnFirstPermit).unwrap();
assert_eq!(json, "permit_on_first_permit");
}
#[test]
fn evaluation_semantic_should_deserialize_execute_all() {
let s: EvaluationSemantic = serde_json::from_str(r#""execute_all""#).unwrap();
assert_eq!(s, EvaluationSemantic::ExecuteAll);
}
#[test]
fn evaluation_semantic_should_deserialize_deny_on_first_deny() {
let s: EvaluationSemantic = serde_json::from_str(r#""deny_on_first_deny""#).unwrap();
assert_eq!(s, EvaluationSemantic::DenyOnFirstDeny);
}
#[test]
fn evaluation_semantic_should_deserialize_permit_on_first_permit() {
let s: EvaluationSemantic = serde_json::from_str(r#""permit_on_first_permit""#).unwrap();
assert_eq!(s, EvaluationSemantic::PermitOnFirstPermit);
}
#[test]
fn evaluations_response_should_omit_decision_when_none() {
let resp = EvaluationsResponse {
decision: None,
evaluations: vec![
EvaluationResponse { decision: true, context: None },
EvaluationResponse { decision: false, context: None },
],
};
let json = serde_json::to_value(&resp).unwrap();
assert!(json.get("decision").is_none());
assert_eq!(json["evaluations"].as_array().unwrap().len(), 2);
}