diff --git a/tests/unit/test_multipart.c b/tests/unit/test_multipart.c new file mode 100644 index 0000000..4797268 --- /dev/null +++ b/tests/unit/test_multipart.c @@ -0,0 +1,183 @@ +#include "unity.h" +#include "multipart.h" +#include +#include + +/* ===== multipart_extract_boundary ===== */ + +void test_extract_boundary_simple(void) { + char buf[256]; + TEST_ASSERT_TRUE(multipart_extract_boundary( + "multipart/form-data; boundary=----WebKitFormBoundary", buf, sizeof(buf))); + TEST_ASSERT_EQUAL_STRING("----WebKitFormBoundary", buf); +} + +void test_extract_boundary_quoted(void) { + char buf[256]; + TEST_ASSERT_TRUE(multipart_extract_boundary( + "multipart/form-data; boundary=\"abc123\"", buf, sizeof(buf))); + TEST_ASSERT_EQUAL_STRING("abc123", buf); +} + +void test_extract_boundary_with_extra_params(void) { + char buf[256]; + TEST_ASSERT_TRUE(multipart_extract_boundary( + "multipart/form-data; boundary=xyz; charset=utf-8", buf, sizeof(buf))); + TEST_ASSERT_EQUAL_STRING("xyz", buf); +} + +void test_extract_boundary_null(void) { + char buf[256]; + TEST_ASSERT_FALSE(multipart_extract_boundary(NULL, buf, sizeof(buf))); + TEST_ASSERT_FALSE(multipart_extract_boundary("text/plain", buf, sizeof(buf))); +} + +void test_extract_boundary_too_small_buffer(void) { + char buf[4]; + TEST_ASSERT_FALSE(multipart_extract_boundary( + "multipart/form-data; boundary=abcdef", buf, sizeof(buf))); +} + +/* ===== multipart_parse ===== */ + +static const char *SAMPLE_MULTIPART = + "------WebKitFormBoundary7MA4YWxkTrZu0gW\r\n" + "Content-Disposition: form-data; name=\"file\"; filename=\"test.txt\"\r\n" + "Content-Type: text/plain\r\n" + "\r\n" + "Hello, World!\r\n" + "------WebKitFormBoundary7MA4YWxkTrZu0gW\r\n" + "Content-Disposition: form-data; name=\"description\"\r\n" + "\r\n" + "Test file upload\r\n" + "------WebKitFormBoundary7MA4YWxkTrZu0gW--\r\n"; + +void test_parse_simple_multipart(void) { + multipart_part_t *parts = NULL; + int num_parts = 0; + TEST_ASSERT_EQUAL(0, multipart_parse(SAMPLE_MULTIPART, strlen(SAMPLE_MULTIPART), + "----WebKitFormBoundary7MA4YWxkTrZu0gW", + &parts, &num_parts)); + TEST_ASSERT_EQUAL(2, num_parts); + + TEST_ASSERT_EQUAL_STRING("file", parts[0].name); + TEST_ASSERT_EQUAL_STRING("test.txt", parts[0].filename); + TEST_ASSERT_EQUAL_STRING("text/plain", parts[0].content_type); + TEST_ASSERT_EQUAL(13, parts[0].data_len); + TEST_ASSERT_EQUAL_STRING("Hello, World!", parts[0].data); + + TEST_ASSERT_EQUAL_STRING("description", parts[1].name); + TEST_ASSERT_NULL(parts[1].filename); + TEST_ASSERT_EQUAL(16, parts[1].data_len); + TEST_ASSERT_EQUAL_STRING("Test file upload", parts[1].data); + + multipart_parts_free(parts, num_parts); +} + +void test_parse_no_filename(void) { + const char *body = + "--boundary\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + "\r\n" + "value1\r\n" + "--boundary--\r\n"; + multipart_part_t *parts = NULL; + int num_parts = 0; + TEST_ASSERT_EQUAL(0, multipart_parse(body, strlen(body), "boundary", &parts, &num_parts)); + TEST_ASSERT_EQUAL(1, num_parts); + TEST_ASSERT_EQUAL_STRING("field1", parts[0].name); + TEST_ASSERT_NULL(parts[0].filename); + TEST_ASSERT_EQUAL_STRING("value1", parts[0].data); + multipart_parts_free(parts, num_parts); +} + +void test_parse_empty_body(void) { + multipart_part_t *parts = NULL; + int num_parts = 0; + TEST_ASSERT_EQUAL(-1, multipart_parse("", 0, "boundary", &parts, &num_parts)); +} + +void test_parse_null_args(void) { + multipart_part_t *parts = NULL; + int num_parts = 0; + TEST_ASSERT_EQUAL(-1, multipart_parse(NULL, 0, "boundary", &parts, &num_parts)); + TEST_ASSERT_EQUAL(-1, multipart_parse("x", 1, NULL, &parts, &num_parts)); +} + +void test_parse_invalid_boundary(void) { + const char *body = "not a multipart body"; + multipart_part_t *parts = NULL; + int num_parts = 0; + TEST_ASSERT_EQUAL(-1, multipart_parse(body, strlen(body), "boundary", &parts, &num_parts)); +} + +void test_parse_binary_data(void) { + /* 包含 \0 的二进制数据 */ + char body[256]; + int n = snprintf(body, sizeof(body), + "--bin\r\n" + "Content-Disposition: form-data; name=\"bin\"; filename=\"data.bin\"\r\n" + "Content-Type: application/octet-stream\r\n" + "\r\n"); + /* 追加二进制数据 */ + body[n++] = 0x00; + body[n++] = 0x01; + body[n++] = 0x02; + body[n++] = 0x03; + int end = snprintf(body + n, sizeof(body) - n, "\r\n--bin--\r\n"); + n += end; + + multipart_part_t *parts = NULL; + int num_parts = 0; + TEST_ASSERT_EQUAL(0, multipart_parse(body, (size_t)n, "bin", &parts, &num_parts)); + TEST_ASSERT_EQUAL(1, num_parts); + TEST_ASSERT_EQUAL(4, parts[0].data_len); + TEST_ASSERT_EQUAL(0x00, (unsigned char)parts[0].data[0]); + TEST_ASSERT_EQUAL(0x01, (unsigned char)parts[0].data[1]); + TEST_ASSERT_EQUAL(0x02, (unsigned char)parts[0].data[2]); + TEST_ASSERT_EQUAL(0x03, (unsigned char)parts[0].data[3]); + multipart_parts_free(parts, num_parts); +} + +void test_parse_multiple_files(void) { + const char *body = + "--multi\r\n" + "Content-Disposition: form-data; name=\"files\"; filename=\"a.txt\"\r\n" + "\r\n" + "A\r\n" + "--multi\r\n" + "Content-Disposition: form-data; name=\"files\"; filename=\"b.txt\"\r\n" + "\r\n" + "B\r\n" + "--multi--\r\n"; + multipart_part_t *parts = NULL; + int num_parts = 0; + TEST_ASSERT_EQUAL(0, multipart_parse(body, strlen(body), "multi", &parts, &num_parts)); + TEST_ASSERT_EQUAL(2, num_parts); + TEST_ASSERT_EQUAL_STRING("a.txt", parts[0].filename); + TEST_ASSERT_EQUAL_STRING("b.txt", parts[1].filename); + multipart_parts_free(parts, num_parts); +} + +void setUp(void) {} +void tearDown(void) {} + +int main(void) { + UNITY_BEGIN(); + + RUN_TEST(test_extract_boundary_simple); + RUN_TEST(test_extract_boundary_quoted); + RUN_TEST(test_extract_boundary_with_extra_params); + RUN_TEST(test_extract_boundary_null); + RUN_TEST(test_extract_boundary_too_small_buffer); + + RUN_TEST(test_parse_simple_multipart); + RUN_TEST(test_parse_no_filename); + RUN_TEST(test_parse_empty_body); + RUN_TEST(test_parse_null_args); + RUN_TEST(test_parse_invalid_boundary); + RUN_TEST(test_parse_binary_data); + RUN_TEST(test_parse_multiple_files); + + return UNITY_END(); +}