Skip to content

Commit

Permalink
add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
cpunion committed Dec 29, 2024
1 parent f2eebe3 commit 636d5b9
Show file tree
Hide file tree
Showing 3 changed files with 286 additions and 19 deletions.
1 change: 0 additions & 1 deletion src/params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,5 @@ fn parse_key_parts(key: &str) -> Vec<String> {
parts.push(current);
}

debug!("Final parsed parts: {:?}", parts);
parts
}
170 changes: 152 additions & 18 deletions src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ use std::collections::HashMap;

use super::*;
use ::serde::{Deserialize, Serialize};
use axum::http::HeaderValue;
use axum::Json;
use axum::{
body::Body,
extract::{FromRequest, Request},
http::StatusCode,
http::{self, HeaderValue},
response::IntoResponse,
routing::{get, post},
Router,
Json, Router,
};
use axum_test::{
multipart::{MultipartForm, Part},
Expand All @@ -30,6 +31,13 @@ struct TestParams {
extra: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
struct UploadFileResponse {
name: String,
content_type: String,
content: String,
}

#[axum::debug_handler]
async fn test_params_handler(Params(test, _): Params<TestParams>) -> impl IntoResponse {
(StatusCode::OK, serde_json::to_string(&test).unwrap())
Expand Down Expand Up @@ -94,13 +102,6 @@ struct CreatePostResponse {
attachments: Vec<AttachmentResponse>,
}

#[derive(Debug, Serialize, Deserialize)]
struct UploadFileResponse {
name: String,
content_type: String,
content: String,
}

#[axum::debug_handler]
async fn test_nested_params_handler(
Params(post, _): Params<CreatePostParams>,
Expand Down Expand Up @@ -512,19 +513,19 @@ async fn test_nested_params_with_file_upload() {
// Verify attachments
assert_eq!(body.attachments.len(), 2);

// First attachment
assert_eq!(body.attachments[0].name, "First attachment");
assert_eq!(body.attachments[0].content_type, "text/plain");
let attachment1 = &body.attachments[0];
assert_eq!(attachment1.name, "First attachment");
assert_eq!(attachment1.content_type, "text/plain");
assert_eq!(
body.attachments[0].content,
attachment1.content,
String::from_utf8_lossy(attachment1_content)
);

// Second attachment
assert_eq!(body.attachments[1].name, "Second attachment");
assert_eq!(body.attachments[1].content_type, "text/plain");
let attachment2 = &body.attachments[1];
assert_eq!(attachment2.name, "Second attachment");
assert_eq!(attachment2.content_type, "text/plain");
assert_eq!(
body.attachments[1].content,
attachment2.content,
String::from_utf8_lossy(attachment2_content)
);
}
Expand Down Expand Up @@ -898,3 +899,136 @@ async fn test_json_part() {
assert_eq!(body.profile, Some("Test profile".to_string()));
assert_eq!(body.avatar_size, 4);
}

#[derive(Debug, Deserialize)]
struct TestNumbers {
pos_int: u64,
neg_int: i64,
float: f64,
zero: i64,
big_num: u64,
small_float: f64,
exp_num: f64,
}

#[derive(Debug, Deserialize)]
struct TestMixed {
number: i64,
text: String,
boolean: bool,
opt_val: Option<String>,
numbers: Vec<f64>,
nested: TestNested,
}

#[derive(Debug, Deserialize)]
struct TestNested {
id: u64,
name: String,
}

#[tokio::test]
async fn test_json_numbers() {
setup();
let json = r#"{
"pos_int": 42,
"neg_int": -42,
"float": 42.5,
"zero": 0,
"big_num": 9007199254740991,
"small_float": 0.0000123,
"exp_num": 1.23e5
}"#;

let req = Request::builder()
.method(http::Method::POST)
.header(http::header::CONTENT_TYPE, "application/json")
.body(Body::from(json))
.unwrap();

let params = Params::<TestNumbers>::from_request(req, &()).await.unwrap();
assert_eq!(params.0.pos_int, 42);
assert_eq!(params.0.neg_int, -42);
assert!((params.0.float - 42.5).abs() < f64::EPSILON);
assert_eq!(params.0.zero, 0);
assert_eq!(params.0.big_num, 9007199254740991);
assert!((params.0.small_float - 0.0000123).abs() < f64::EPSILON);
assert!((params.0.exp_num - 123000.0).abs() < f64::EPSILON);
}

#[tokio::test]
async fn test_json_mixed_types() {
setup();
let json = r#"{
"number": 42,
"text": "hello world",
"boolean": true,
"opt_val": null,
"numbers": [1.1, 2.2, 3.3],
"nested": {
"id": 1,
"name": "test"
}
}"#;

let req = Request::builder()
.method(http::Method::POST)
.header(http::header::CONTENT_TYPE, "application/json")
.body(Body::from(json))
.unwrap();

let params = Params::<TestMixed>::from_request(req, &()).await.unwrap();
assert_eq!(params.0.number, 42);
assert_eq!(params.0.text, "hello world");
assert!(params.0.boolean);
assert!(params.0.opt_val.is_none());
assert_eq!(params.0.numbers.len(), 3);
assert!((params.0.numbers[0] - 1.1).abs() < f64::EPSILON);
assert!((params.0.numbers[1] - 2.2).abs() < f64::EPSILON);
assert!((params.0.numbers[2] - 3.3).abs() < f64::EPSILON);
assert_eq!(params.0.nested.id, 1);
assert_eq!(params.0.nested.name, "test");
}

#[tokio::test]
async fn test_form_urlencoded_numbers() {
setup();
let form_data = "pos_int=42&neg_int=-42&float=42.5&zero=0&big_num=9007199254740991&small_float=0.0000123&exp_num=123000";

let req = Request::builder()
.method(http::Method::POST)
.header(
http::header::CONTENT_TYPE,
"application/x-www-form-urlencoded",
)
.body(Body::from(form_data))
.unwrap();

let params = Params::<TestNumbers>::from_request(req, &()).await.unwrap();
assert_eq!(params.0.pos_int, 42);
assert_eq!(params.0.neg_int, -42);
assert!((params.0.float - 42.5).abs() < f64::EPSILON);
assert_eq!(params.0.zero, 0);
assert_eq!(params.0.big_num, 9007199254740991);
assert!((params.0.small_float - 0.0000123).abs() < f64::EPSILON);
assert!((params.0.exp_num - 123000.0).abs() < f64::EPSILON);
}

#[tokio::test]
async fn test_query_params_numbers() {
setup();
let req = Request::builder()
.method(http::Method::GET)
.uri("/test?pos_int=42&neg_int=-42&float=42.5&zero=0&big_num=9007199254740991&small_float=0.0000123&exp_num=123000")
.body(Body::empty())
.unwrap();

let params = Params::<TestNumbers>::from_request(req, &()).await.unwrap();
assert_eq!(params.0.pos_int, 42);
assert_eq!(params.0.neg_int, -42);
assert!((params.0.float - 42.5).abs() < f64::EPSILON);
assert_eq!(params.0.zero, 0);
assert_eq!(params.0.big_num, 9007199254740991);
assert!((params.0.small_float - 0.0000123).abs() < f64::EPSILON);
assert!((params.0.exp_num - 123000.0).abs() < f64::EPSILON);
}
134 changes: 134 additions & 0 deletions src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ pub fn parse_json(feeder: SliceJsonFeeder) -> Result<ParamsValue, JsonError> {
result.ok_or(JsonError::NoMoreInput)
}

#[derive(Debug)]
pub enum JsonError {
SyntaxError,
NoMoreInput,
Expand Down Expand Up @@ -265,4 +266,137 @@ mod tests {
assert_ne!(Number::from(-42i64), Number::from(-43i64));
assert_ne!(Number::from(42.0), Number::from(42.5));
}

#[test]
fn test_parse_json_numbers() {
// Test positive integers
let json = r#"{"pos": 42, "zero": 0, "big": 9007199254740991}"#;
let result = parse_json(SliceJsonFeeder::new(json.as_bytes())).unwrap();
if let ParamsValue::Object(map) = result {
assert!(matches!(
map["pos"],
ParamsValue::Number(Number(N::PosInt(42)))
));
assert!(matches!(
map["zero"],
ParamsValue::Number(Number(N::PosInt(0)))
));
assert!(matches!(
map["big"],
ParamsValue::Number(Number(N::PosInt(9007199254740991)))
));
} else {
panic!("Expected object");
}

// Test negative integers
let json = r#"{"neg": -42, "min": -9007199254740991}"#;
let result = parse_json(SliceJsonFeeder::new(json.as_bytes())).unwrap();
if let ParamsValue::Object(map) = result {
assert!(matches!(
map["neg"],
ParamsValue::Number(Number(N::NegInt(-42)))
));
assert!(matches!(
map["min"],
ParamsValue::Number(Number(N::NegInt(-9007199254740991)))
));
} else {
panic!("Expected object");
}

// Test floating point numbers
let json = r#"{
"float": 42.5,
"neg_float": -42.5,
"zero_float": 0.0,
"exp": 1.23e5,
"neg_exp": -1.23e-5
}"#;
let result = parse_json(SliceJsonFeeder::new(json.as_bytes())).unwrap();
if let ParamsValue::Object(map) = result {
assert!(
matches!(map["float"], ParamsValue::Number(Number(N::Float(v))) if (v - 42.5).abs() < f64::EPSILON)
);
assert!(
matches!(map["neg_float"], ParamsValue::Number(Number(N::Float(v))) if (v - (-42.5)).abs() < f64::EPSILON)
);
assert!(
matches!(map["zero_float"], ParamsValue::Number(Number(N::Float(v))) if v.abs() < f64::EPSILON)
);
assert!(
matches!(map["exp"], ParamsValue::Number(Number(N::Float(v))) if (v - 123000.0).abs() < f64::EPSILON)
);
assert!(
matches!(map["neg_exp"], ParamsValue::Number(Number(N::Float(v))) if (v - (-0.0000123)).abs() < f64::EPSILON)
);
} else {
panic!("Expected object");
}

// Test array of numbers
let json = r#"[42, -42, 42.5, 0, -0.0]"#;
let result = parse_json(SliceJsonFeeder::new(json.as_bytes())).unwrap();
if let ParamsValue::Array(arr) = result {
assert!(matches!(arr[0], ParamsValue::Number(Number(N::PosInt(42)))));
assert!(matches!(
arr[1],
ParamsValue::Number(Number(N::NegInt(-42)))
));
assert!(
matches!(arr[2], ParamsValue::Number(Number(N::Float(v))) if (v - 42.5).abs() < f64::EPSILON)
);
assert!(matches!(arr[3], ParamsValue::Number(Number(N::PosInt(0)))));
assert!(
matches!(arr[4], ParamsValue::Number(Number(N::Float(v))) if v.abs() < f64::EPSILON)
);
} else {
panic!("Expected array");
}
}

#[test]
fn test_parse_json_mixed_types() {
let json = r#"{
"number": 42,
"string": "hello",
"bool": true,
"null": null,
"array": [1, "two", false],
"nested": {"a": 1, "b": 2}
}"#;
let result = parse_json(SliceJsonFeeder::new(json.as_bytes())).unwrap();
if let ParamsValue::Object(map) = result {
assert!(matches!(
map["number"],
ParamsValue::Number(Number(N::PosInt(42)))
));
assert!(matches!(map["string"], ParamsValue::Convertible(ref s) if s == "hello"));
assert!(matches!(map["bool"], ParamsValue::Convertible(ref s) if s == "true"));
assert!(matches!(map["null"], ParamsValue::Null));

if let ParamsValue::Array(arr) = &map["array"] {
assert!(matches!(arr[0], ParamsValue::Number(Number(N::PosInt(1)))));
assert!(matches!(arr[1], ParamsValue::Convertible(ref s) if s == "two"));
assert!(matches!(arr[2], ParamsValue::Convertible(ref s) if s == "false"));
} else {
panic!("Expected array");
}

if let ParamsValue::Object(nested) = &map["nested"] {
assert!(matches!(
nested["a"],
ParamsValue::Number(Number(N::PosInt(1)))
));
assert!(matches!(
nested["b"],
ParamsValue::Number(Number(N::PosInt(2)))
));
} else {
panic!("Expected nested object");
}
} else {
panic!("Expected object");
}
}
}

0 comments on commit 636d5b9

Please sign in to comment.