///|
pub(all) struct MultipartFixture {
name : String
boundary : String
body : String
expected_parts : Int
expected_fields : Int
expected_files : Int
} derive(Debug, Eq)
///|
pub fn fixture_simple_fields() -> MultipartFixture {
{
name: "simple fields",
boundary: "simple",
body: "--simple\r\n" +
"Content-Disposition: form-data; name=\"title\"\r\n" +
"\r\n" +
"MoonBit\r\n" +
"--simple\r\n" +
"Content-Disposition: form-data; name=\"tag\"\r\n" +
"\r\n" +
"parser\r\n" +
"--simple--\r\n",
expected_parts: 2,
expected_fields: 2,
expected_files: 0,
}
}
///|
pub fn fixture_file_upload() -> MultipartFixture {
{
name: "file upload",
boundary: "upload",
body: "--upload\r\n" +
"Content-Disposition: form-data; name=\"file\"; filename=\"report.txt\"\r\n" +
"Content-Type: text/plain\r\n" +
"\r\n" +
"hello\r\n" +
"--upload--\r\n",
expected_parts: 1,
expected_fields: 0,
expected_files: 1,
}
}
///|
pub fn fixture_mixed_upload() -> MultipartFixture {
{
name: "mixed upload",
boundary: "mixed",
body: "--mixed\r\n" +
"Content-Disposition: form-data; name=\"title\"\r\n" +
"\r\n" +
"demo\r\n" +
"--mixed\r\n" +
"Content-Disposition: form-data; name=\"file\"; filename=\"a.txt\"\r\n" +
"Content-Type: text/plain\r\n" +
"\r\n" +
"A\r\n" +
"--mixed\r\n" +
"Content-Disposition: form-data; name=\"file\"; filename=\"b.txt\"\r\n" +
"Content-Type: text/plain\r\n" +
"\r\n" +
"B\r\n" +
"--mixed--\r\n",
expected_parts: 3,
expected_fields: 1,
expected_files: 2,
}
}
///|
pub fn fixture_empty_file() -> MultipartFixture {
{
name: "empty file",
boundary: "empty",
body: "--empty\r\n" +
"Content-Disposition: form-data; name=\"file\"; filename=\"empty.txt\"\r\n" +
"Content-Type: text/plain\r\n" +
"\r\n" +
"\r\n" +
"--empty--\r\n",
expected_parts: 1,
expected_fields: 0,
expected_files: 1,
}
}
///|
pub fn fixture_custom_headers() -> MultipartFixture {
{
name: "custom headers",
boundary: "headers",
body: "--headers\r\n" +
"Content-Disposition: form-data; name=\"token\"\r\n" +
"X-Trace-Id: abc123\r\n" +
"\r\n" +
"value\r\n" +
"--headers--\r\n",
expected_parts: 1,
expected_fields: 1,
expected_files: 0,
}
}
///|
pub fn fixture_quoted_boundary() -> (String, String) {
(
"multipart/form-data; charset=utf-8; boundary=\"quoted-boundary\"", "quoted-boundary",
)
}
///|
pub fn standard_fixtures() -> Array[MultipartFixture] {
[
fixture_simple_fields(),
fixture_file_upload(),
fixture_mixed_upload(),
fixture_empty_file(),
fixture_custom_headers(),
]
}
///|
pub fn parse_fixture(
fixture : MultipartFixture,
) -> Result[MultipartForm, MultipartError] {
parse_multipart(fixture.body, fixture.boundary)
}
///|
pub fn fixture_status_line(fixture : MultipartFixture) -> String {
fixture.name +
": parts=" +
fixture.expected_parts.to_string() +
", fields=" +
fixture.expected_fields.to_string() +
", files=" +
fixture.expected_files.to_string()
}
///|
pub fn assert_fixture_shape(
fixture : MultipartFixture,
) -> Result[FormSummary, MultipartError] {
match parse_fixture(fixture) {
Ok(form) => {
let summary = form.summary()
if summary.part_count != fixture.expected_parts {
return Err(
MalformedBody("fixture part count mismatch: " + fixture.name),
)
}
if summary.field_count != fixture.expected_fields {
return Err(
MalformedBody("fixture field count mismatch: " + fixture.name),
)
}
if summary.file_count != fixture.expected_files {
return Err(
MalformedBody("fixture file count mismatch: " + fixture.name),
)
}
Ok(summary)
}
Err(err) => Err(err)
}
}