#![cfg(kani)]
use crate::http::{into_response_parts, public_error_code_for, PublicErrorCode};
use crate::kind::AppError;
fn symbolic_app_error() -> AppError {
let case: u8 = kani::any();
kani::assume(case < 9);
match case {
0 => AppError::Validation {
code: "internal_validation_code",
},
1 => AppError::Forbidden {
policy: "admin_only_policy",
},
2 => AppError::NotFound,
3 => AppError::Conflict,
4 => AppError::Dependency {
dep: "primary_database",
},
5 => AppError::Crypto,
6 => AppError::Internal,
7 => AppError::RateLimit {
retry_after_seconds: None,
},
_ => AppError::RateLimit {
retry_after_seconds: Some(30),
},
}
}
#[kani::proof]
fn public_status_code_is_in_4xx_5xx_range() {
let err = symbolic_app_error();
let (status, _public) = into_response_parts(&err);
assert!(status >= 400);
assert!(status < 600);
}
#[kani::proof]
fn public_error_code_is_non_empty_static_literal() {
let err = symbolic_app_error();
let (_status, public) = into_response_parts(&err);
assert!(!public.code.is_empty());
}
#[kani::proof]
fn public_error_code_is_in_whitelist() {
let err = symbolic_app_error();
let code = public_error_code_for(&err);
let allowed = matches!(
code,
PublicErrorCode::InvalidRequest
| PublicErrorCode::Forbidden
| PublicErrorCode::NotFound
| PublicErrorCode::Conflict
| PublicErrorCode::TemporarilyUnavailable
| PublicErrorCode::InternalError
| PublicErrorCode::RateLimited
);
assert!(allowed);
}