use serde_json::json;
use crate::common::{ValidationErrorCode, builders::UserBuilder, fixtures::rfc_examples};
use scim_server::error::ValidationError;
use scim_server::schema::{OperationContext, SchemaRegistry};
#[test]
fn test_missing_id_attribute() {
let registry = SchemaRegistry::new().expect("Failed to create registry");
let invalid_user = UserBuilder::new().without_id().build();
assert!(!invalid_user.as_object().unwrap().contains_key("id"));
let result = registry.validate_json_resource_with_context(
"User",
&invalid_user,
OperationContext::Update,
);
assert!(result.is_err());
match result {
Err(ValidationError::MissingId) => {
}
Err(other) => panic!("Expected MissingId error, got {:?}", other),
Ok(_) => panic!("Expected validation to fail, but it passed"),
}
}
#[test]
fn test_empty_id_value() {
let registry = SchemaRegistry::new().expect("Failed to create registry");
let invalid_user = UserBuilder::new().with_empty_id().build();
assert_eq!(invalid_user["id"], "");
let result = registry.validate_json_resource_with_context(
"User",
&invalid_user,
OperationContext::Update,
);
assert!(result.is_err());
match result {
Err(ValidationError::EmptyId) => {
}
Err(other) => panic!("Expected EmptyId error, got {:?}", other),
Ok(_) => panic!("Expected validation to fail, but it passed"),
}
}
#[test]
fn test_null_id_value() {
let registry = SchemaRegistry::new().expect("Failed to create registry");
let null_id_user = json!({
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"id": null,
"userName": "test@example.com",
"meta": {
"resourceType": "User"
}
});
assert!(null_id_user["id"].is_null());
let result = registry.validate_json_resource_with_context(
"User",
&null_id_user,
OperationContext::Update,
);
assert!(result.is_err());
match result {
Err(ValidationError::InvalidIdFormat { id }) => {
assert_eq!(id, "null");
}
Err(other) => panic!("Expected InvalidIdFormat error, got {:?}", other),
Ok(_) => panic!("Expected validation to fail, but it passed"),
}
}
#[test]
fn test_invalid_id_format() {
let registry = SchemaRegistry::new().expect("Failed to create registry");
let invalid_user = UserBuilder::new().with_numeric_id().build();
assert!(invalid_user["id"].is_number());
let result = registry.validate_json_resource_with_context(
"User",
&invalid_user,
OperationContext::Update,
);
assert!(result.is_err());
match result {
Err(ValidationError::InvalidIdFormat { id }) => {
assert_eq!(id, "123");
}
Err(other) => panic!("Expected InvalidIdFormat error, got {:?}", other),
Ok(_) => panic!("Expected validation to fail, but it passed"),
}
}
#[test]
fn test_invalid_id_format_array() {
let registry = SchemaRegistry::new().expect("Failed to create registry");
let invalid_user = UserBuilder::new().with_array_id().build();
assert!(invalid_user["id"].is_array());
let result = registry.validate_json_resource_with_context(
"User",
&invalid_user,
OperationContext::Update,
);
assert!(result.is_err());
match result {
Err(ValidationError::InvalidIdFormat { id }) => {
assert_eq!(id, r#"["123","456"]"#);
}
Err(other) => panic!("Expected InvalidIdFormat error, got {:?}", other),
Ok(_) => panic!("Expected validation to fail, but it passed"),
}
}
#[test]
fn test_invalid_id_format_object() {
let registry = SchemaRegistry::new().expect("Failed to create registry");
let invalid_user = UserBuilder::new().with_object_id().build();
assert!(invalid_user["id"].is_object());
let result = registry.validate_json_resource_with_context(
"User",
&invalid_user,
OperationContext::Update,
);
assert!(result.is_err());
match result {
Err(ValidationError::InvalidIdFormat { id }) => {
assert_eq!(id, r#"{"nested":"value"}"#);
}
Err(other) => panic!("Expected InvalidIdFormat error, got {:?}", other),
Ok(_) => panic!("Expected validation to fail, but it passed"),
}
}
#[test]
fn test_client_provided_id_in_create() {
let registry = SchemaRegistry::new().expect("Failed to create registry");
let user_with_id = UserBuilder::new().build();
assert!(user_with_id.as_object().unwrap().contains_key("id"));
let result = registry.validate_json_resource_with_context(
"User",
&user_with_id,
OperationContext::Create,
);
assert!(result.is_err());
match result {
Err(ValidationError::ClientProvidedId) => {
}
Err(other) => panic!("Expected ClientProvidedId error, got {:?}", other),
Ok(_) => panic!("Expected validation to fail, but it passed"),
}
}
#[test]
fn test_invalid_external_id_format() {
let registry = SchemaRegistry::new().expect("Failed to create registry");
let invalid_user = UserBuilder::new().with_numeric_external_id().build();
assert!(invalid_user["externalId"].is_number());
let result = registry.validate_json_resource_with_context(
"User",
&invalid_user,
OperationContext::Update,
);
assert!(result.is_err());
match result {
Err(ValidationError::InvalidExternalId) => {
}
Err(other) => panic!("Expected InvalidExternalId error, got {:?}", other),
Ok(_) => panic!("Expected validation to fail, but it passed"),
}
}
#[test]
fn test_invalid_external_id_format_array() {
let registry = SchemaRegistry::new().expect("Failed to create registry");
let invalid_user = UserBuilder::new().with_array_external_id().build();
assert!(invalid_user["externalId"].is_array());
let result = registry.validate_json_resource_with_context(
"User",
&invalid_user,
OperationContext::Update,
);
assert!(result.is_err());
match result {
Err(ValidationError::InvalidExternalId) => {
}
Err(other) => panic!("Expected InvalidExternalId error, got {:?}", other),
Ok(_) => panic!("Expected validation to fail, but it passed"),
}
}
#[test]
fn test_invalid_meta_structure() {
let registry = SchemaRegistry::new().expect("Failed to create registry");
let invalid_user = UserBuilder::new().with_string_meta().build();
assert!(invalid_user["meta"].is_string());
let result = registry.validate_json_resource_with_context(
"User",
&invalid_user,
OperationContext::Update,
);
assert!(result.is_err());
match result {
Err(ValidationError::InvalidMetaStructure) => {
}
Err(other) => panic!("Expected InvalidMetaStructure error, got {:?}", other),
Ok(_) => panic!("Expected validation to fail, but it passed"),
}
}
#[test]
fn test_invalid_meta_structure_array() {
let registry = SchemaRegistry::new().expect("Failed to create registry");
let invalid_user = UserBuilder::new().with_array_meta().build();
assert!(invalid_user["meta"].is_array());
let result = registry.validate_json_resource_with_context(
"User",
&invalid_user,
OperationContext::Update,
);
assert!(result.is_err());
match result {
Err(ValidationError::InvalidMetaStructure) => {
}
Err(other) => panic!("Expected InvalidMetaStructure error, got {:?}", other),
Ok(_) => panic!("Expected validation to fail, but it passed"),
}
}
#[test]
fn test_missing_meta_resource_type() {
let registry = SchemaRegistry::new().expect("Failed to create registry");
let invalid_user = UserBuilder::new().without_meta_resource_type().build();
assert!(
!invalid_user["meta"]
.as_object()
.unwrap()
.contains_key("resourceType")
);
let result = registry.validate_json_resource_with_context(
"User",
&invalid_user,
OperationContext::Update,
);
assert!(result.is_err());
match result {
Err(ValidationError::MissingResourceType) => {
}
Err(other) => panic!("Expected MissingResourceType error, got {:?}", other),
Ok(_) => panic!("Expected validation to fail, but it passed"),
}
}
#[test]
fn test_invalid_meta_resource_type() {
let registry = SchemaRegistry::new().expect("Failed to create registry");
let invalid_user = UserBuilder::new().with_invalid_meta_resource_type().build();
assert_eq!(invalid_user["meta"]["resourceType"], "InvalidType");
let result = registry.validate_json_resource_with_context(
"User",
&invalid_user,
OperationContext::Update,
);
assert!(result.is_err());
match result {
Err(ValidationError::InvalidResourceType { resource_type }) => {
assert_eq!(resource_type, "InvalidType");
}
Err(other) => panic!("Expected InvalidResourceType error, got {:?}", other),
Ok(_) => panic!("Expected validation to fail, but it passed"),
}
}
#[test]
fn test_invalid_meta_resource_type_non_string() {
let registry = SchemaRegistry::new().expect("Failed to create registry");
let invalid_user = UserBuilder::new().with_numeric_meta_resource_type().build();
assert!(invalid_user["meta"]["resourceType"].is_number());
let result = registry.validate_json_resource_with_context(
"User",
&invalid_user,
OperationContext::Update,
);
assert!(result.is_err());
match result {
Err(ValidationError::InvalidMetaStructure) => {
}
Err(other) => panic!("Expected InvalidMetaStructure error, got {:?}", other),
Ok(_) => panic!("Expected validation to fail, but it passed"),
}
}
#[test]
fn test_client_provided_meta_readonly_attributes() {
let registry = SchemaRegistry::new().expect("Failed to create registry");
let user_with_readonly_meta = UserBuilder::new()
.without_id() .with_readonly_meta_attributes()
.build();
let meta = user_with_readonly_meta["meta"].as_object().unwrap();
assert!(meta.contains_key("created") || meta.contains_key("lastModified"));
let result = registry.validate_json_resource_with_context(
"User",
&user_with_readonly_meta,
OperationContext::Create,
);
assert!(result.is_err());
match result {
Err(ValidationError::ClientProvidedMeta) => {
}
Err(other) => panic!("Expected ClientProvidedMeta error, got {:?}", other),
Ok(_) => panic!("Expected validation to fail, but it passed"),
}
}
#[test]
fn test_invalid_created_datetime() {
let registry = SchemaRegistry::new().expect("Failed to create registry");
let invalid_user = UserBuilder::new().with_numeric_created_datetime().build();
assert!(invalid_user["meta"]["created"].is_number());
let result = registry.validate_json_resource_with_context(
"User",
&invalid_user,
OperationContext::Update,
);
assert!(result.is_err());
match result {
Err(ValidationError::InvalidCreatedDateTime) => {
}
Err(other) => panic!("Expected InvalidCreatedDateTime error, got {:?}", other),
Ok(_) => panic!("Expected validation to fail, but it passed"),
}
}
#[test]
fn test_invalid_created_datetime_non_string() {
let registry = SchemaRegistry::new().expect("Failed to create registry");
let invalid_user = UserBuilder::new().with_numeric_created_datetime().build();
assert!(invalid_user["meta"]["created"].is_number());
let result = registry.validate_json_resource_with_context(
"User",
&invalid_user,
OperationContext::Update,
);
assert!(result.is_err());
match result {
Err(ValidationError::InvalidCreatedDateTime) => {
}
Err(other) => panic!("Expected InvalidCreatedDateTime error, got {:?}", other),
Ok(_) => panic!("Expected validation to fail, but it passed"),
}
}
#[test]
fn test_invalid_last_modified_datetime() {
let registry = SchemaRegistry::new().expect("Failed to create registry");
let invalid_user = json!({
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"id": "123",
"userName": "test@example.com",
"meta": {
"resourceType": "User",
"lastModified": 123456789
}
});
assert!(invalid_user["meta"]["lastModified"].is_number());
let result = registry.validate_json_resource_with_context(
"User",
&invalid_user,
OperationContext::Update,
);
assert!(result.is_err());
match result {
Err(ValidationError::InvalidModifiedDateTime) => {
}
Err(other) => panic!("Expected InvalidModifiedDateTime error, got {:?}", other),
Ok(_) => panic!("Expected validation to fail, but it passed"),
}
}
#[test]
fn test_invalid_location_uri() {
let registry = SchemaRegistry::new().expect("Failed to create registry");
let invalid_user = json!({
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"id": "123",
"userName": "test@example.com",
"meta": {
"resourceType": "User",
"location": 123
}
});
assert!(invalid_user["meta"]["location"].is_number());
let result = registry.validate_json_resource_with_context(
"User",
&invalid_user,
OperationContext::Update,
);
assert!(result.is_err());
match result {
Err(ValidationError::InvalidLocationUri) => {
}
Err(other) => panic!("Expected InvalidLocationUri error, got {:?}", other),
Ok(_) => panic!("Expected validation to fail, but it passed"),
}
}
#[test]
fn test_invalid_version_format() {
let registry = SchemaRegistry::new().expect("Failed to create registry");
let invalid_user = UserBuilder::new().with_invalid_version_format().build();
assert!(invalid_user["meta"]["version"].is_array());
let result = registry.validate_json_resource_with_context(
"User",
&invalid_user,
OperationContext::Update,
);
assert!(result.is_err());
match result {
Err(ValidationError::InvalidVersionFormat) => {
}
Err(other) => panic!("Expected InvalidVersionFormat error, got {:?}", other),
Ok(_) => panic!("Expected validation to fail, but it passed"),
}
}
#[test]
fn test_valid_common_attributes() {
let registry = SchemaRegistry::new().expect("Failed to create registry");
let valid_user = rfc_examples::user_minimal();
let result =
registry.validate_json_resource_with_context("User", &valid_user, OperationContext::Update);
assert!(
result.is_ok(),
"Valid user should pass validation: {:?}",
result
);
}
#[test]
fn test_valid_common_attributes_with_optional() {
let registry = SchemaRegistry::new().expect("Failed to create registry");
let valid_user = UserBuilder::new()
.with_id("123")
.with_external_id("ext-123")
.build();
let result =
registry.validate_json_resource_with_context("User", &valid_user, OperationContext::Update);
assert!(
result.is_ok(),
"Valid user with external ID should pass validation: {:?}",
result
);
}
#[test]
fn test_multiple_common_attribute_errors() {
let registry = SchemaRegistry::new().expect("Failed to create registry");
let invalid_user = UserBuilder::new().without_id().with_string_meta().build();
assert!(!invalid_user.as_object().unwrap().contains_key("id"));
assert!(invalid_user["meta"].is_string());
let result = registry.validate_json_resource_with_context(
"User",
&invalid_user,
OperationContext::Update,
);
assert!(result.is_err());
match result {
Err(ValidationError::MissingId) => {
}
Err(ValidationError::InvalidMetaStructure) => {
}
Err(other) => panic!(
"Expected MissingId or InvalidMetaStructure error, got {:?}",
other
),
Ok(_) => panic!("Expected validation to fail, but it passed"),
}
}
#[test]
fn test_valid_external_id() {
let registry = SchemaRegistry::new().expect("Failed to create registry");
let valid_user = UserBuilder::new()
.with_external_id("valid-external-id")
.build();
assert_eq!(valid_user["externalId"], "valid-external-id");
let result =
registry.validate_json_resource_with_context("User", &valid_user, OperationContext::Update);
assert!(
result.is_ok(),
"User with valid external ID should pass validation: {:?}",
result
);
}
mod coverage_tests {
use super::*;
#[test]
fn test_common_attributes_error_coverage() {
let expected_errors = vec![
ValidationErrorCode::MissingId, ValidationErrorCode::EmptyId, ValidationErrorCode::InvalidIdFormat, ValidationErrorCode::ClientProvidedId, ValidationErrorCode::InvalidExternalId, ValidationErrorCode::InvalidMetaStructure, ValidationErrorCode::MissingResourceType, ValidationErrorCode::InvalidResourceType, ValidationErrorCode::ClientProvidedMeta, ValidationErrorCode::InvalidCreatedDateTime, ValidationErrorCode::InvalidModifiedDateTime, ValidationErrorCode::InvalidLocationUri, ValidationErrorCode::InvalidVersionFormat, ];
println!("Common attributes error coverage:");
for error in &expected_errors {
match error {
ValidationErrorCode::ClientProvidedId | ValidationErrorCode::ClientProvidedMeta => {
println!(" {:?}: 🔲 Deferred (needs operation context)", error);
}
_ => {
println!(" {:?}: ✅ Tested", error);
}
}
}
assert_eq!(
expected_errors.len(),
13,
"Should test 13 common attribute error types"
);
let testable_errors = expected_errors
.iter()
.filter(|e| {
!matches!(
e,
ValidationErrorCode::ClientProvidedId
| ValidationErrorCode::ClientProvidedMeta
| ValidationErrorCode::MissingResourceType
)
})
.count();
assert_eq!(
testable_errors, 10,
"Should have 10 testable common attribute errors in current implementation"
);
}
}