diff options
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/CMakeLists.txt | 13 | ||||
| -rw-r--r-- | tests/init.c | 11 | ||||
| -rw-r--r-- | tests/t_http.c | 221 | ||||
| -rw-r--r-- | tests/t_http_next_header.c | 353 | ||||
| -rw-r--r-- | tests/test.h | 77 | ||||
| -rw-r--r-- | tests/tests.py | 72 |
6 files changed, 747 insertions, 0 deletions
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..8ae9d98 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,13 @@ +add_library(sqlite3 STATIC "${sqlite_SOURCE_DIR}/sqlite3.c") +target_include_directories(sqlite3 PUBLIC "${sqlite_SOURCE_DIR}") + +add_executable(t_http_next_header t_http_next_header.c ../http.c) +target_link_libraries(t_http_next_header PRIVATE sqlite3) +target_compile_definitions(t_http_next_header PRIVATE HTTP_BACKEND_DUMMY) +add_test(NAME http_next_header COMMAND t_http_next_header) + +add_executable(t_http t_http.c ../http.c) +target_link_libraries(t_http PRIVATE sqlite3) +target_compile_definitions(t_http PRIVATE HTTP_BACKEND_DUMMY SQLITE_CORE) +target_include_directories(t_http PRIVATE ../src) +add_test(NAME http COMMAND t_http) diff --git a/tests/init.c b/tests/init.c new file mode 100644 index 0000000..7fec8f1 --- /dev/null +++ b/tests/init.c @@ -0,0 +1,11 @@ +#include <sqlite3.h> +#include <stdio.h> + +typedef void (*entrypoint)(void); + +int sqlite3_http_init(sqlite3*, char**, const sqlite3_api_routines*); + +void test_init() { + sqlite3_initialize(); + sqlite3_auto_extension((entrypoint)sqlite3_http_init); +} diff --git a/tests/t_http.c b/tests/t_http.c new file mode 100644 index 0000000..5251dc7 --- /dev/null +++ b/tests/t_http.c @@ -0,0 +1,221 @@ +#include "http.h" + +#include <sqlite3.h> + +#include "test.h" + +SQLITE_EXTENSION_INIT3 + +void http_backend_dummy_set_errmsg(const char* zErrMsg); +void http_backend_dummy_set_response(http_response* response); +void http_backend_dummy_reset_request(); +const http_request* http_backend_dummy_get_last_request(); + +int sqlite3_http_init(sqlite3*, char**, const sqlite3_api_routines*); + +void new_text_response(http_response* response, + const char* zBody, + const char* zHeaders, + int iStatusCode, + const char* zStatus) { + memset(response, 0, sizeof(*response)); + assert(zStatus != NULL); + if (zBody) { + response->pBody = sqlite3_mprintf("%s", zBody); + response->szBody = strlen(zBody); + } + if (zHeaders) { + response->zHeaders = sqlite3_mprintf("%s", zHeaders); + response->szHeaders = strlen(zHeaders); + } + response->iStatusCode = iStatusCode; + response->zStatus = sqlite3_mprintf("%s", zStatus); +} + +static sqlite3* db; + +void test_http_get() { + sqlite3_stmt* stmt; + http_response response; + new_text_response(&response, "hello, world!", "Foo: Bar\r\n\r\n", 200, "HTTP/1.0 200 OK"); + http_backend_dummy_set_response(&response); + ASSERT_INT_EQ( + sqlite3_prepare_v2(db, "select * from http_get('http://example.com')", -1, &stmt, NULL), + SQLITE_OK); + ASSERT_INT_EQ(sqlite3_step(stmt), SQLITE_ROW); + ASSERT_INT_EQ(sqlite3_column_count(stmt), 4); + ASSERT_STR_EQ(sqlite3_column_text(stmt, 0), "HTTP/1.0 200 OK"); + ASSERT_INT_EQ(sqlite3_column_int(stmt, 1), 200); + ASSERT_STR_EQ(sqlite3_column_text(stmt, 2), "Foo: Bar\r\n\r\n"); + ASSERT_STR_EQ(sqlite3_column_text(stmt, 3), "hello, world!"); + ASSERT_INT_EQ(sqlite3_finalize(stmt), SQLITE_OK); +} + +void test_http_get_hidden_columns() { + sqlite3_stmt* stmt; + http_response response; + new_text_response(&response, "hello, world!", "Foo: Bar\r\n\r\n", 200, "HTTP/1.0 200 OK"); + http_backend_dummy_set_response(&response); + ASSERT_INT_EQ( + sqlite3_prepare_v2(db, + "select response_status, response_status_code, response_headers, " + "response_body, request_method, request_url, request_headers, " + "request_body from http_get('http://example.com/foo')", + -1, + &stmt, + NULL), + SQLITE_OK); + ASSERT_INT_EQ(sqlite3_step(stmt), SQLITE_ROW); + ASSERT_INT_EQ(sqlite3_column_count(stmt), 8); + ASSERT_STR_EQ(sqlite3_column_text(stmt, 0), "HTTP/1.0 200 OK"); + ASSERT_INT_EQ(sqlite3_column_int(stmt, 1), 200); + ASSERT_STR_EQ(sqlite3_column_text(stmt, 2), "Foo: Bar\r\n\r\n"); + ASSERT_STR_EQ(sqlite3_column_text(stmt, 3), "hello, world!"); + ASSERT_STR_EQ(sqlite3_column_text(stmt, 4), "GET"); + ASSERT_STR_EQ(sqlite3_column_text(stmt, 5), "http://example.com/foo"); + ASSERT_NULL(sqlite3_column_text(stmt, 6)); + ASSERT_NULL(sqlite3_column_text(stmt, 7)); + ASSERT_INT_EQ(sqlite3_finalize(stmt), SQLITE_OK); +} + +void test_http_get_request_headers() { + sqlite3_stmt* stmt; + http_response response; + new_text_response(&response, "hello, world!", "Foo: Bar\r\n\r\n", 200, "HTTP/1.0 200 OK"); + http_backend_dummy_set_response(&response); + ASSERT_INT_EQ( + sqlite3_prepare_v2(db, + "select response_status, response_status_code, response_headers, " + "response_body, request_method, request_url, request_headers, " + "request_body from http_get('http://example.com/foo', " + "http_headers('Req1', 'Val1', 'Req2', 'Val2'))", + -1, + &stmt, + NULL), + SQLITE_OK); + ASSERT_INT_EQ(sqlite3_step(stmt), SQLITE_ROW); + ASSERT_INT_EQ(sqlite3_column_count(stmt), 8); + ASSERT_STR_EQ(sqlite3_column_text(stmt, 0), "HTTP/1.0 200 OK"); + ASSERT_INT_EQ(sqlite3_column_int(stmt, 1), 200); + ASSERT_STR_EQ(sqlite3_column_text(stmt, 2), "Foo: Bar\r\n\r\n"); + ASSERT_STR_EQ(sqlite3_column_text(stmt, 3), "hello, world!"); + ASSERT_STR_EQ(sqlite3_column_text(stmt, 4), "GET"); + ASSERT_STR_EQ(sqlite3_column_text(stmt, 5), "http://example.com/foo"); + ASSERT_STR_EQ(sqlite3_column_text(stmt, 6), "Req1: Val1\r\nReq2: Val2\r\n"); + ASSERT_NULL(sqlite3_column_text(stmt, 7)); + ASSERT_INT_EQ(sqlite3_finalize(stmt), SQLITE_OK); +} + +void test_http_post() { + sqlite3_stmt* stmt; + http_response response; + new_text_response(&response, "hello, world!", "Foo: Bar\r\n\r\n", 200, "HTTP/1.0 200 OK"); + http_backend_dummy_set_response(&response); + ASSERT_INT_EQ( + sqlite3_prepare_v2(db, "select * from http_post('http://example.com')", -1, &stmt, NULL), + SQLITE_OK); + ASSERT_INT_EQ(sqlite3_step(stmt), SQLITE_ROW); + ASSERT_INT_EQ(sqlite3_column_count(stmt), 4); + ASSERT_STR_EQ(sqlite3_column_text(stmt, 0), "HTTP/1.0 200 OK"); + ASSERT_INT_EQ(sqlite3_column_int(stmt, 1), 200); + ASSERT_STR_EQ(sqlite3_column_text(stmt, 2), "Foo: Bar\r\n\r\n"); + ASSERT_STR_EQ(sqlite3_column_text(stmt, 3), "hello, world!"); + ASSERT_INT_EQ(sqlite3_finalize(stmt), SQLITE_OK); +} + +void test_http_post_hidden_columns() { + sqlite3_stmt* stmt; + http_response response; + new_text_response(&response, "hello, world!", "Foo: Bar\r\n\r\n", 200, "HTTP/1.0 200 OK"); + http_backend_dummy_set_response(&response); + ASSERT_INT_EQ( + sqlite3_prepare_v2(db, + "select response_status, response_status_code, response_headers, " + "response_body, request_method, request_url, request_headers, " + "request_body from http_post('http://example.com/foo')", + -1, + &stmt, + NULL), + SQLITE_OK); + ASSERT_INT_EQ(sqlite3_step(stmt), SQLITE_ROW); + ASSERT_INT_EQ(sqlite3_column_count(stmt), 8); + ASSERT_STR_EQ(sqlite3_column_text(stmt, 0), "HTTP/1.0 200 OK"); + ASSERT_INT_EQ(sqlite3_column_int(stmt, 1), 200); + ASSERT_STR_EQ(sqlite3_column_text(stmt, 2), "Foo: Bar\r\n\r\n"); + ASSERT_STR_EQ(sqlite3_column_text(stmt, 3), "hello, world!"); + ASSERT_STR_EQ(sqlite3_column_text(stmt, 4), "POST"); + ASSERT_STR_EQ(sqlite3_column_text(stmt, 5), "http://example.com/foo"); + ASSERT_NULL(sqlite3_column_text(stmt, 6)); + ASSERT_NULL(sqlite3_column_text(stmt, 7)); + ASSERT_INT_EQ(sqlite3_finalize(stmt), SQLITE_OK); +} + +void test_http_post_request_headers() { + sqlite3_stmt* stmt; + http_response response; + new_text_response(&response, "hello, world!", "Foo: Bar\r\n\r\n", 200, "HTTP/1.0 200 OK"); + http_backend_dummy_set_response(&response); + ASSERT_INT_EQ( + sqlite3_prepare_v2(db, + "select response_status, response_status_code, response_headers, " + "response_body, request_method, request_url, request_headers, " + "request_body from http_post('http://example.com/foo', " + "http_headers('Req1', 'Val1', 'Req2', 'Val2'))", + -1, + &stmt, + NULL), + SQLITE_OK); + ASSERT_INT_EQ(sqlite3_step(stmt), SQLITE_ROW); + ASSERT_INT_EQ(sqlite3_column_count(stmt), 8); + ASSERT_STR_EQ(sqlite3_column_text(stmt, 0), "HTTP/1.0 200 OK"); + ASSERT_INT_EQ(sqlite3_column_int(stmt, 1), 200); + ASSERT_STR_EQ(sqlite3_column_text(stmt, 2), "Foo: Bar\r\n\r\n"); + ASSERT_STR_EQ(sqlite3_column_text(stmt, 3), "hello, world!"); + ASSERT_STR_EQ(sqlite3_column_text(stmt, 4), "POST"); + ASSERT_STR_EQ(sqlite3_column_text(stmt, 5), "http://example.com/foo"); + ASSERT_STR_EQ(sqlite3_column_text(stmt, 6), "Req1: Val1\r\nReq2: Val2\r\n"); + ASSERT_NULL(sqlite3_column_text(stmt, 7)); + ASSERT_INT_EQ(sqlite3_finalize(stmt), SQLITE_OK); +} + +void test_http_post_request_body() { + sqlite3_stmt* stmt; + http_response response; + new_text_response(&response, "hello, world!", "Foo: Bar\r\n\r\n", 200, "HTTP/1.0 200 OK"); + http_backend_dummy_set_response(&response); + ASSERT_INT_EQ( + sqlite3_prepare_v2(db, + "select response_status, response_status_code, response_headers, " + "response_body, request_method, request_url, request_headers, " + "request_body from http_post('http://example.com/foo', " + "http_headers('Req1', 'Val1', 'Req2', 'Val2'), 'hello')", + -1, + &stmt, + NULL), + SQLITE_OK); + ASSERT_INT_EQ(sqlite3_step(stmt), SQLITE_ROW); + ASSERT_INT_EQ(sqlite3_column_count(stmt), 8); + ASSERT_STR_EQ(sqlite3_column_text(stmt, 0), "HTTP/1.0 200 OK"); + ASSERT_INT_EQ(sqlite3_column_int(stmt, 1), 200); + ASSERT_STR_EQ(sqlite3_column_text(stmt, 2), "Foo: Bar\r\n\r\n"); + ASSERT_STR_EQ(sqlite3_column_text(stmt, 3), "hello, world!"); + ASSERT_STR_EQ(sqlite3_column_text(stmt, 4), "POST"); + ASSERT_STR_EQ(sqlite3_column_text(stmt, 5), "http://example.com/foo"); + ASSERT_STR_EQ(sqlite3_column_text(stmt, 6), "Req1: Val1\r\nReq2: Val2\r\n"); + ASSERT_STR_EQ(sqlite3_column_text(stmt, 7), "hello"); + ASSERT_INT_EQ(sqlite3_finalize(stmt), SQLITE_OK); +} + +int main(int argc, char const* argv[]) { + sqlite3_initialize(); + sqlite3_auto_extension((void (*)(void))sqlite3_http_init); + ASSERT_INT_EQ(sqlite3_open(":memory:", &db), SQLITE_OK); + test_http_get(); + test_http_get_hidden_columns(); + test_http_get_request_headers(); + test_http_post(); + test_http_post_hidden_columns(); + test_http_post_request_headers(); + test_http_post_request_body(); + return 0; +} diff --git a/tests/t_http_next_header.c b/tests/t_http_next_header.c new file mode 100644 index 0000000..2f101af --- /dev/null +++ b/tests/t_http_next_header.c @@ -0,0 +1,353 @@ +#include <sqlite3.h> + +#include "test.h" + +int http_next_header(const char* headers, + int size, + int* pParsed, + const char** ppName, + int* pNameSize, + const char** ppValue, + int* pValueSize); + +void single_header() { + const char* headers = "Foo: Bar\r\n"; + int nParsed; + const char* name; + int nameSize; + const char* value; + int valueSize; + int rc = + http_next_header(headers, strlen(headers), &nParsed, &name, &nameSize, &value, &valueSize); + ASSERT_INT_EQ(rc, SQLITE_ROW); + ASSERT_INT_EQ(nameSize, 3); + ASSERT_MEM_EQ(name, "Foo", 3); + ASSERT_INT_EQ(valueSize, 3); + ASSERT_MEM_EQ(value, "Bar", 3); + ASSERT_INT_EQ(nParsed, 10); +} + +void empty_string() { + const char* headers = ""; + int nParsed; + const char* name; + int nameSize; + const char* value; + int valueSize; + int rc = + http_next_header(headers, strlen(headers), &nParsed, &name, &nameSize, &value, &valueSize); + ASSERT_INT_EQ(rc, SQLITE_DONE); + ASSERT_INT_EQ(nameSize, 0); + ASSERT_NULL(name); + ASSERT_INT_EQ(valueSize, 0); + ASSERT_NULL(value); + ASSERT_INT_EQ(nParsed, 0); +} + +void terminating_crnl() { + const char* headers = "\r\n"; + int nParsed; + const char* name; + int nameSize; + const char* value; + int valueSize; + int rc = + http_next_header(headers, strlen(headers), &nParsed, &name, &nameSize, &value, &valueSize); + ASSERT_INT_EQ(rc, SQLITE_DONE); + ASSERT_INT_EQ(nameSize, 0); + ASSERT_NULL(name); + ASSERT_INT_EQ(valueSize, 0); + ASSERT_NULL(value); + ASSERT_INT_EQ(nParsed, 2); +} + +void multiple_headers() { + const char* headers = "Content-Type: application/json\r\nContent-Length: " + "343\r\nAccess-Control-Allow-Origin: *\r\n\r\n"; + int nParsed; + const char* name; + int nameSize; + const char* value; + int valueSize; + int rc; + + rc = http_next_header(headers, strlen(headers), &nParsed, &name, &nameSize, &value, &valueSize); + ASSERT_INT_EQ(rc, SQLITE_ROW); + ASSERT_INT_EQ(nameSize, 12); + ASSERT_MEM_EQ(name, "Content-Type", 12); + ASSERT_INT_EQ(valueSize, 16); + ASSERT_MEM_EQ(value, "application/json", 16); + ASSERT_INT_EQ(nParsed, 32); + + headers += nParsed; + + rc = http_next_header(headers, strlen(headers), &nParsed, &name, &nameSize, &value, &valueSize); + ASSERT_INT_EQ(rc, SQLITE_ROW); + ASSERT_INT_EQ(nameSize, 14); + ASSERT_MEM_EQ(name, "Content-Length", 14); + ASSERT_INT_EQ(valueSize, 3); + ASSERT_MEM_EQ(value, "343", 3); + ASSERT_INT_EQ(nParsed, 21); + + headers += nParsed; + + rc = http_next_header(headers, strlen(headers), &nParsed, &name, &nameSize, &value, &valueSize); + ASSERT_INT_EQ(rc, SQLITE_ROW); + ASSERT_INT_EQ(nameSize, 27); + ASSERT_MEM_EQ(name, "Access-Control-Allow-Origin", 27); + ASSERT_INT_EQ(valueSize, 1); + ASSERT_MEM_EQ(value, "*", 1); + ASSERT_INT_EQ(nParsed, 32); + + headers += nParsed; + + rc = http_next_header(headers, strlen(headers), &nParsed, &name, &nameSize, &value, &valueSize); + ASSERT_INT_EQ(rc, SQLITE_DONE); + ASSERT_INT_EQ(nameSize, 0); + ASSERT_NULL(name); + ASSERT_INT_EQ(valueSize, 0); + ASSERT_NULL(value); + ASSERT_INT_EQ(nParsed, 2); +} + +void empty_value() { + const char* headers = "Foo:\r\n"; + int nParsed; + const char* name; + int nameSize; + const char* value; + int valueSize; + int rc = + http_next_header(headers, strlen(headers), &nParsed, &name, &nameSize, &value, &valueSize); + ASSERT_INT_EQ(rc, SQLITE_ROW); + ASSERT_INT_EQ(nameSize, 3); + ASSERT_MEM_EQ(name, "Foo", 3); + ASSERT_INT_EQ(valueSize, 0); + ASSERT_NULL(value); + ASSERT_INT_EQ(nParsed, 6); +} + +void empty_value_leading_ws() { + const char* headers = "Foo: \r\n"; + int nParsed; + const char* name; + int nameSize; + const char* value; + int valueSize; + int rc = + http_next_header(headers, strlen(headers), &nParsed, &name, &nameSize, &value, &valueSize); + ASSERT_INT_EQ(rc, SQLITE_ROW); + ASSERT_INT_EQ(nameSize, 3); + ASSERT_MEM_EQ(name, "Foo", 3); + ASSERT_INT_EQ(valueSize, 0); + ASSERT_NULL(value); + ASSERT_INT_EQ(nParsed, 11); +} + +void value_with_leading_ws() { + const char* headers = "Foo: \t Bar\r\n"; + int nParsed; + const char* name; + int nameSize; + const char* value; + int valueSize; + int rc = + http_next_header(headers, strlen(headers), &nParsed, &name, &nameSize, &value, &valueSize); + ASSERT_INT_EQ(rc, SQLITE_ROW); + ASSERT_INT_EQ(nameSize, 3); + ASSERT_MEM_EQ(name, "Foo", 3); + ASSERT_INT_EQ(valueSize, 3); + ASSERT_MEM_EQ(value, "Bar", 3); + ASSERT_INT_EQ(nParsed, 16); +} + +void value_with_trailing_ws() { + const char* headers = "Foo: Bar \t \r\n"; + int nParsed; + const char* name; + int nameSize; + const char* value; + int valueSize; + int rc = + http_next_header(headers, strlen(headers), &nParsed, &name, &nameSize, &value, &valueSize); + ASSERT_INT_EQ(rc, SQLITE_ROW); + ASSERT_INT_EQ(nameSize, 3); + ASSERT_MEM_EQ(name, "Foo", 3); + ASSERT_INT_EQ(valueSize, 3); + ASSERT_MEM_EQ(value, "Bar", 3); + ASSERT_INT_EQ(nParsed, strlen(headers)); +} + +void value_with_leading_and_trailing_ws() { + const char* headers = "Foo: Bar \t \r\n"; + int nParsed; + const char* name; + int nameSize; + const char* value; + int valueSize; + int rc = + http_next_header(headers, strlen(headers), &nParsed, &name, &nameSize, &value, &valueSize); + ASSERT_INT_EQ(rc, SQLITE_ROW); + ASSERT_INT_EQ(nameSize, 3); + ASSERT_MEM_EQ(name, "Foo", 3); + ASSERT_INT_EQ(valueSize, 3); + ASSERT_MEM_EQ(value, "Bar", 3); + ASSERT_INT_EQ(nParsed, strlen(headers)); +} + +void value_with_no_leading_ws() { + const char* headers = "Foo:Bar\r\n"; + int nParsed; + const char* name; + int nameSize; + const char* value; + int valueSize; + int rc = + http_next_header(headers, strlen(headers), &nParsed, &name, &nameSize, &value, &valueSize); + ASSERT_INT_EQ(rc, SQLITE_ROW); + ASSERT_INT_EQ(nameSize, 3); + ASSERT_MEM_EQ(name, "Foo", 3); + ASSERT_INT_EQ(valueSize, 3); + ASSERT_MEM_EQ(value, "Bar", 3); + ASSERT_INT_EQ(nParsed, strlen(headers)); +} + +void value_with_spaces_inside() { + const char* headers = "Foo: This is a value\r\n"; + int nParsed; + const char* name; + int nameSize; + const char* value; + int valueSize; + int rc = + http_next_header(headers, strlen(headers), &nParsed, &name, &nameSize, &value, &valueSize); + ASSERT_INT_EQ(rc, SQLITE_ROW); + ASSERT_INT_EQ(nameSize, 3); + ASSERT_MEM_EQ(name, "Foo", 3); + ASSERT_INT_EQ(valueSize, 20); + ASSERT_MEM_EQ(value, "This is a value", 20); + ASSERT_INT_EQ(nParsed, strlen(headers)); +} + +void value_with_spaces_inside_trailing_ws() { + const char* headers = "Foo: This is a value \r\n"; + int nParsed; + const char* name; + int nameSize; + const char* value; + int valueSize; + int rc = + http_next_header(headers, strlen(headers), &nParsed, &name, &nameSize, &value, &valueSize); + ASSERT_INT_EQ(rc, SQLITE_ROW); + ASSERT_INT_EQ(nameSize, 3); + ASSERT_MEM_EQ(name, "Foo", 3); + ASSERT_INT_EQ(valueSize, 20); + ASSERT_MEM_EQ(value, "This is a value", 20); + ASSERT_INT_EQ(nParsed, strlen(headers)); +} + +void value_with_spaces_inside_leading_ws() { + const char* headers = "Foo: This is a value\r\n"; + int nParsed; + const char* name; + int nameSize; + const char* value; + int valueSize; + int rc = + http_next_header(headers, strlen(headers), &nParsed, &name, &nameSize, &value, &valueSize); + ASSERT_INT_EQ(rc, SQLITE_ROW); + ASSERT_INT_EQ(nameSize, 3); + ASSERT_MEM_EQ(name, "Foo", 3); + ASSERT_INT_EQ(valueSize, 20); + ASSERT_MEM_EQ(value, "This is a value", 20); + ASSERT_INT_EQ(nParsed, strlen(headers)); +} + +void value_with_spaces_inside_leading_and_trailing_ws() { + const char* headers = "Foo: This is a value \r\n"; + int nParsed; + const char* name; + int nameSize; + const char* value; + int valueSize; + int rc = + http_next_header(headers, strlen(headers), &nParsed, &name, &nameSize, &value, &valueSize); + ASSERT_INT_EQ(rc, SQLITE_ROW); + ASSERT_INT_EQ(nameSize, 3); + ASSERT_MEM_EQ(name, "Foo", 3); + ASSERT_INT_EQ(valueSize, 20); + ASSERT_MEM_EQ(value, "This is a value", 20); + ASSERT_INT_EQ(nParsed, strlen(headers)); +} + +void value_with_spaces_inside_no_leading_ws() { + const char* headers = "Foo:This is a value\r\n"; + int nParsed; + const char* name; + int nameSize; + const char* value; + int valueSize; + int rc = + http_next_header(headers, strlen(headers), &nParsed, &name, &nameSize, &value, &valueSize); + ASSERT_INT_EQ(rc, SQLITE_ROW); + ASSERT_INT_EQ(nameSize, 3); + ASSERT_MEM_EQ(name, "Foo", 3); + ASSERT_INT_EQ(valueSize, 20); + ASSERT_MEM_EQ(value, "This is a value", 20); + ASSERT_INT_EQ(nParsed, strlen(headers)); +} + +void trailing_ws_after_name_is_ignored() { + const char* headers = "Foo\t : Bar\r\n"; + int nParsed; + const char* name; + int nameSize; + const char* value; + int valueSize; + int rc = + http_next_header(headers, strlen(headers), &nParsed, &name, &nameSize, &value, &valueSize); + ASSERT_INT_EQ(rc, SQLITE_ROW); + ASSERT_INT_EQ(nameSize, 3); + ASSERT_MEM_EQ(name, "Foo", 3); + ASSERT_INT_EQ(valueSize, 3); + ASSERT_MEM_EQ(value, "Bar", 3); + ASSERT_INT_EQ(nParsed, strlen(headers)); +} + +void folding() { + const char* headers = "Foo: Bar\r\n Baz\r\n\tAnd more\r\n"; + int nParsed; + const char* name; + int nameSize; + const char* value; + int valueSize; + int rc = + http_next_header(headers, strlen(headers), &nParsed, &name, &nameSize, &value, &valueSize); + ASSERT_INT_EQ(rc, SQLITE_ROW); + ASSERT_INT_EQ(nameSize, 3); + ASSERT_MEM_EQ(name, "Foo", 3); + ASSERT_INT_EQ(valueSize, 21); + ASSERT_MEM_EQ(value, "Bar\r\n Baz\r\n\tAnd more", 21); + ASSERT_INT_EQ(nParsed, strlen(headers)); +} + +int main(int argc, char const* argv[]) { + single_header(); + empty_string(); + terminating_crnl(); + multiple_headers(); + empty_value(); + empty_value_leading_ws(); + value_with_leading_ws(); + value_with_trailing_ws(); + value_with_leading_and_trailing_ws(); + value_with_no_leading_ws(); + value_with_spaces_inside(); + value_with_spaces_inside_trailing_ws(); + value_with_spaces_inside_leading_ws(); + value_with_spaces_inside_leading_and_trailing_ws(); + value_with_spaces_inside_no_leading_ws(); + trailing_ws_after_name_is_ignored(); + folding(); + return 0; +} diff --git a/tests/test.h b/tests/test.h new file mode 100644 index 0000000..38f5d6e --- /dev/null +++ b/tests/test.h @@ -0,0 +1,77 @@ +#ifndef TEST_H +#define TEST_H + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define ASSERT_INT_EQ(X, Y) \ + do { \ + int xxx = (X); \ + int yyy = (Y); \ + if (xxx != yyy) { \ + fprintf(stderr, \ + "*** %s ***\n%s is not equal to %s\nwhere\n%s is %d\nand\n%s " \ + "is %d\n", \ + __FUNCTION__, \ + #X, \ + #Y, \ + #X, \ + xxx, \ + #Y, \ + yyy); \ + exit(1); \ + } \ + } while (0) + +#define ASSERT_STR_EQ(X, Y) \ + do { \ + const char* xxx = (X); \ + const char* yyy = (Y); \ + if (strcmp(xxx, yyy) != 0) { \ + fprintf(stderr, \ + "*** %s ***\n%s is not equal to %s\nwhere\n%s is %s\nand\n%s " \ + "is %s\n", \ + __FUNCTION__, \ + #X, \ + #Y, \ + #X, \ + xxx, \ + #Y, \ + yyy); \ + exit(1); \ + } \ + } while (0) + +#define ASSERT_MEM_EQ(X, Y, SIZE) \ + do { \ + const char* xxx = (X); \ + const char* yyy = (Y); \ + if (memcmp(xxx, yyy, SIZE) != 0) { \ + fprintf(stderr, \ + "*** %s ***\n%s is not equal to %s\nwhere\n%s is %.*s\nand\n%s " \ + "is %.*s\n", \ + __FUNCTION__, \ + #X, \ + #Y, \ + #X, \ + SIZE, \ + xxx, \ + #Y, \ + SIZE, \ + yyy); \ + exit(1); \ + } \ + } while (0) + +#define ASSERT_NULL(X) \ + do { \ + const void* xxx = (X); \ + if (xxx != NULL) { \ + fprintf(stderr, "*** %s ***\n%s is not NULL (was %p)\n", __FUNCTION__, #X, xxx); \ + exit(1); \ + } \ + } while (0) + +#endif diff --git a/tests/tests.py b/tests/tests.py new file mode 100644 index 0000000..34d4f13 --- /dev/null +++ b/tests/tests.py @@ -0,0 +1,72 @@ +import http.server +import subprocess +import threading +import sqlite3 +import json + +tests = ( + { + "request_url": "http://localhost:8080/foobar", + "request_method": "GET", + "response_body": "foobar", + "response_headers": "Content-Size: 6\r\n", + "response_status_code": 200, + }, +) + +test_idx = 0 + +class Handler(http.server.BaseHTTPRequestHandler): + def version_string(self): + return "testclient/1.0" + + def date_time_string(self, timestamp=None): + return "Mon, 05 Sep 2022 19:26:53 GMT" + + def do_GET(self): + self._handle_test("GET") + + def do_POST(self): + self._handle_test("POST") + + def _handle_test(self, method): + global test_idx + data = tests[test_idx] + test_idx += 1 + + try: + if method != data["request_method"]: + self.send_response(400) + self.end_headers() + else: + self.send_response(data["response_status_code"]) + self.flush_headers() + self.wfile.write(data["response_headers"].encode("latin1", "strict")) + self.end_headers() + self.wfile.write(data["response_body"].encode("utf-8")) + except: + self.send_response(500) + self.end_headers() + + threading.Thread(target=server.shutdown).start() + + +server = http.server.HTTPServer(('', 8080), Handler) +server_thread = threading.Thread(target=server.serve_forever) +server_thread.start() + +for test in tests: + if test["request_method"] == "GET": + table = "http_get" + elif test["request_method"] == "POST": + table = "http_post" + request_url = test["request_url"] + sql = f".mode json\nselect request_method, request_url, request_headers, request_body, * from {table}('{request_url}')" + cp = subprocess.run(["./build/tests/Debug/sqlite3.exe", ":memory:"], input=sql, text=True, capture_output=True) + j = json.loads(cp.stdout) + print(j) + assert j[0]["response_status_code"] == test["response_status_code"] + assert j[0]["response_body"] == test["response_body"] + assert j[0]["response_headers"] == "Date: Mon, 05 Sep 2022 19:26:53 GMT\r\nServer: testclient/1.0\r\n" + test["response_headers"] + "\r\n" + +server_thread.join() |
