test: add 149 unit tests across core, auth, config, message crates

Test coverage increased from ~34 to 183 tests (zero failures):

- erp-core (21): version check, pagination, API response, error mapping
- erp-auth (39): org tree building, DTO validation, error conversion,
  password hashing, user model mapping
- erp-config (57): DTO validation, numbering reset logic, menu tree
  building, error conversion. Fixed BatchSaveMenusReq nested validation
- erp-message (50): DTO validation, template rendering, query defaults,
  error conversion
- erp-workflow (16): unchanged (parser + expression tests)

All tests are pure unit tests requiring no database.
This commit is contained in:
iven
2026-04-15 01:06:34 +08:00
parent 9568dd7875
commit ee65b6e3c9
13 changed files with 1995 additions and 4 deletions

View File

@@ -1,6 +1,6 @@
use axum::Json;
use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use axum::Json;
use serde::Serialize;
/// 统一错误响应格式
@@ -95,3 +95,91 @@ pub fn check_version(expected: i32, actual: i32) -> AppResult<i32> {
Err(AppError::VersionMismatch)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn check_version_ok() {
assert_eq!(check_version(1, 1).unwrap(), 2);
assert_eq!(check_version(5, 5).unwrap(), 6);
}
#[test]
fn check_version_mismatch() {
let result = check_version(1, 2);
assert!(result.is_err());
match result.unwrap_err() {
AppError::VersionMismatch => {}
other => panic!("Expected VersionMismatch, got {:?}", other),
}
}
#[test]
fn db_err_record_not_found_maps_to_not_found() {
let err = sea_orm::DbErr::RecordNotFound("test".to_string());
let app_err: AppError = err.into();
match app_err {
AppError::NotFound(msg) => assert_eq!(msg, "test"),
other => panic!("Expected NotFound, got {:?}", other),
}
}
#[test]
fn db_err_generic_maps_to_internal() {
let db_err = sea_orm::DbErr::Custom("some error".to_string());
let app_err: AppError = db_err.into();
match app_err {
AppError::Internal(msg) => assert!(msg.contains("some error")),
other => panic!("Expected Internal, got {:?}", other),
}
}
#[test]
fn app_error_into_response_status_codes() {
// NotFound -> 404
let resp = AppError::NotFound("test".to_string()).into_response();
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
// Validation -> 400
let resp = AppError::Validation("bad input".to_string()).into_response();
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
// Unauthorized -> 401
let resp = AppError::Unauthorized.into_response();
assert_eq!(resp.status(), StatusCode::UNAUTHORIZED);
// Forbidden -> 403
let resp = AppError::Forbidden("no access".to_string()).into_response();
assert_eq!(resp.status(), StatusCode::FORBIDDEN);
// VersionMismatch -> 409
let resp = AppError::VersionMismatch.into_response();
assert_eq!(resp.status(), StatusCode::CONFLICT);
// TooManyRequests -> 429
let resp = AppError::TooManyRequests.into_response();
assert_eq!(resp.status(), StatusCode::TOO_MANY_REQUESTS);
// Internal -> 500
let resp = AppError::Internal("oops".to_string()).into_response();
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
}
#[test]
fn app_error_internal_hides_details_from_response() {
// Internal errors should map to 500 with a generic message
let resp = AppError::Internal("sensitive db error detail".to_string()).into_response();
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
}
#[test]
fn anyhow_error_maps_to_internal() {
let err: AppError = anyhow::anyhow!("something went wrong").into();
match err {
AppError::Internal(msg) => assert_eq!(msg, "something went wrong"),
other => panic!("Expected Internal, got {:?}", other),
}
}
}