fix: renforcer la robustesse du module et étendre les tests/CI

Co-authored-by: aider (openrouter/openai/gpt-5.3-codex) <aider@aider.chat>
This commit is contained in:
Jacquin Antoine
2026-02-28 20:28:40 +01:00
parent a935ed1641
commit 46291898e1
10 changed files with 735 additions and 447 deletions

View File

@ -9,12 +9,15 @@
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
/* Default configuration values */
#define DEFAULT_MAX_HEADERS 10
#define DEFAULT_MAX_HEADER_VALUE_LEN 256
#define DEFAULT_RECONNECT_INTERVAL 10
#define DEFAULT_ERROR_REPORT_INTERVAL 10
#define MAX_SOCKET_PATH_LEN 108
/* Mock configuration structure */
typedef struct {
@ -40,34 +43,59 @@ static const char *parse_socket_path(const char *value)
if (value == NULL || strlen(value) == 0) {
return NULL;
}
if (strlen(value) >= MAX_SOCKET_PATH_LEN) {
return NULL;
}
return value;
}
static int parse_int_strict(const char *value, int *result)
{
char *endptr = NULL;
long val;
if (value == NULL || *value == '\0' || result == NULL) {
return -1;
}
errno = 0;
val = strtol(value, &endptr, 10);
if (errno != 0 || endptr == value || *endptr != '\0' || val < INT_MIN || val > INT_MAX) {
return -1;
}
*result = (int)val;
return 0;
}
static int parse_max_headers(const char *value, int *result)
{
char *endptr;
long val = strtol(value, &endptr, 10);
if (*endptr != '\0' || val < 0) {
if (parse_int_strict(value, result) != 0 || *result < 0) {
return -1;
}
*result = (int)val;
return 0;
}
static int parse_interval(const char *value, int *result)
{
char *endptr;
long val = strtol(value, &endptr, 10);
if (*endptr != '\0' || val < 0) {
if (parse_int_strict(value, result) != 0 || *result < 0) {
return -1;
}
return 0;
}
static int parse_max_header_value_len(const char *value, int *result)
{
if (parse_int_strict(value, result) != 0 || *result < 1) {
return -1;
}
*result = (int)val;
return 0;
}
/* Test: Parse enabled On */
static void test_parse_enabled_on(void **state)
{
(void)state;
assert_int_equal(parse_enabled("On"), 1);
assert_int_equal(parse_enabled("on"), 1);
assert_int_equal(parse_enabled("ON"), 1);
@ -77,6 +105,7 @@ static void test_parse_enabled_on(void **state)
/* Test: Parse enabled Off */
static void test_parse_enabled_off(void **state)
{
(void)state;
assert_int_equal(parse_enabled("Off"), 0);
assert_int_equal(parse_enabled("off"), 0);
assert_int_equal(parse_enabled("OFF"), 0);
@ -86,6 +115,7 @@ static void test_parse_enabled_off(void **state)
/* Test: Parse socket path valid */
static void test_parse_socket_path_valid(void **state)
{
(void)state;
const char *result = parse_socket_path("/var/run/mod_reqin_log.sock");
assert_string_equal(result, "/var/run/mod_reqin_log.sock");
}
@ -93,6 +123,7 @@ static void test_parse_socket_path_valid(void **state)
/* Test: Parse socket path empty */
static void test_parse_socket_path_empty(void **state)
{
(void)state;
const char *result = parse_socket_path("");
assert_null(result);
}
@ -100,20 +131,45 @@ static void test_parse_socket_path_empty(void **state)
/* Test: Parse socket path NULL */
static void test_parse_socket_path_null(void **state)
{
(void)state;
const char *result = parse_socket_path(NULL);
assert_null(result);
}
/* Test: Parse socket path max length valid */
static void test_parse_socket_path_max_len_valid(void **state)
{
(void)state;
char path[MAX_SOCKET_PATH_LEN];
memset(path, 'a', MAX_SOCKET_PATH_LEN - 1);
path[MAX_SOCKET_PATH_LEN - 1] = '\0';
assert_non_null(parse_socket_path(path));
}
/* Test: Parse socket path max length invalid */
static void test_parse_socket_path_max_len_invalid(void **state)
{
(void)state;
char path[MAX_SOCKET_PATH_LEN + 1];
memset(path, 'b', MAX_SOCKET_PATH_LEN);
path[MAX_SOCKET_PATH_LEN] = '\0';
assert_null(parse_socket_path(path));
}
/* Test: Parse max headers valid */
static void test_parse_max_headers_valid(void **state)
{
int result;
(void)state;
assert_int_equal(parse_max_headers("10", &result), 0);
assert_int_equal(result, 10);
assert_int_equal(parse_max_headers("0", &result), 0);
assert_int_equal(result, 0);
assert_int_equal(parse_max_headers("100", &result), 0);
assert_int_equal(result, 100);
}
@ -122,6 +178,8 @@ static void test_parse_max_headers_valid(void **state)
static void test_parse_max_headers_invalid(void **state)
{
int result;
(void)state;
assert_int_equal(parse_max_headers("-1", &result), -1);
assert_int_equal(parse_max_headers("abc", &result), -1);
assert_int_equal(parse_max_headers("10abc", &result), -1);
@ -131,12 +189,14 @@ static void test_parse_max_headers_invalid(void **state)
static void test_parse_reconnect_interval_valid(void **state)
{
int result;
(void)state;
assert_int_equal(parse_interval("10", &result), 0);
assert_int_equal(result, 10);
assert_int_equal(parse_interval("0", &result), 0);
assert_int_equal(result, 0);
assert_int_equal(parse_interval("60", &result), 0);
assert_int_equal(result, 60);
}
@ -145,13 +205,52 @@ static void test_parse_reconnect_interval_valid(void **state)
static void test_parse_reconnect_interval_invalid(void **state)
{
int result;
(void)state;
assert_int_equal(parse_interval("-5", &result), -1);
assert_int_equal(parse_interval("abc", &result), -1);
assert_int_equal(parse_interval("10abc", &result), -1);
}
/* Test: Parse max header value length valid */
static void test_parse_max_header_value_len_valid(void **state)
{
int result;
(void)state;
assert_int_equal(parse_max_header_value_len("1", &result), 0);
assert_int_equal(result, 1);
assert_int_equal(parse_max_header_value_len("256", &result), 0);
assert_int_equal(result, 256);
}
/* Test: Parse max header value length invalid */
static void test_parse_max_header_value_len_invalid(void **state)
{
int result;
(void)state;
assert_int_equal(parse_max_header_value_len("0", &result), -1);
assert_int_equal(parse_max_header_value_len("-1", &result), -1);
assert_int_equal(parse_max_header_value_len("10abc", &result), -1);
}
/* Test: strict numeric parsing invalid suffix for all int directives */
static void test_strict_numeric_invalid_suffix_all(void **state)
{
int result;
(void)state;
assert_int_equal(parse_max_headers("10abc", &result), -1);
assert_int_equal(parse_interval("10abc", &result), -1);
assert_int_equal(parse_max_header_value_len("10abc", &result), -1);
}
/* Test: Default configuration values */
static void test_default_config_values(void **state)
{
(void)state;
assert_int_equal(DEFAULT_MAX_HEADERS, 10);
assert_int_equal(DEFAULT_MAX_HEADER_VALUE_LEN, 256);
assert_int_equal(DEFAULT_RECONNECT_INTERVAL, 10);
@ -161,34 +260,47 @@ static void test_default_config_values(void **state)
/* Test: Configuration validation - enabled requires socket */
static void test_config_validation_enabled_requires_socket(void **state)
{
/* Valid: enabled with socket */
int enabled = 1;
const char *socket = "/var/run/socket";
(void)state;
assert_true(enabled == 0 || socket != NULL);
/* Invalid: enabled without socket */
socket = NULL;
assert_false(enabled == 0 || socket != NULL);
}
/* Test: Configuration validation - enabled with empty socket is invalid */
static void test_config_validation_enabled_with_empty_socket(void **state)
{
int enabled = 1;
const char *socket = parse_socket_path("");
(void)state;
assert_false(enabled == 0 || socket != NULL);
}
/* Test: Header value length validation */
static void test_header_value_len_validation(void **state)
{
int result;
assert_int_equal(parse_interval("1", &result), 0);
(void)state;
assert_int_equal(parse_max_header_value_len("1", &result), 0);
assert_true(result >= 1);
assert_int_equal(parse_interval("0", &result), 0);
assert_false(result >= 1);
assert_int_equal(parse_max_header_value_len("0", &result), -1);
}
/* Test: Large but valid values */
static void test_large_valid_values(void **state)
{
int result;
(void)state;
assert_int_equal(parse_max_headers("1000000", &result), 0);
assert_int_equal(result, 1000000);
assert_int_equal(parse_interval("86400", &result), 0);
assert_int_equal(result, 86400);
}
@ -201,15 +313,21 @@ int main(void)
cmocka_unit_test(test_parse_socket_path_valid),
cmocka_unit_test(test_parse_socket_path_empty),
cmocka_unit_test(test_parse_socket_path_null),
cmocka_unit_test(test_parse_socket_path_max_len_valid),
cmocka_unit_test(test_parse_socket_path_max_len_invalid),
cmocka_unit_test(test_parse_max_headers_valid),
cmocka_unit_test(test_parse_max_headers_invalid),
cmocka_unit_test(test_parse_reconnect_interval_valid),
cmocka_unit_test(test_parse_reconnect_interval_invalid),
cmocka_unit_test(test_parse_max_header_value_len_valid),
cmocka_unit_test(test_parse_max_header_value_len_invalid),
cmocka_unit_test(test_strict_numeric_invalid_suffix_all),
cmocka_unit_test(test_default_config_values),
cmocka_unit_test(test_config_validation_enabled_requires_socket),
cmocka_unit_test(test_config_validation_enabled_with_empty_socket),
cmocka_unit_test(test_header_value_len_validation),
cmocka_unit_test(test_large_valid_values),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}

View File

@ -8,34 +8,88 @@
#include <cmocka.h>
#include <string.h>
#include <stdio.h>
#include <apr_pools.h>
#include <apr_strings.h>
#include <apr_time.h>
#include <apr_lib.h>
/* Mock JSON string escaping function for testing */
static void append_json_string(apr_pool_t *pool, apr_strbuf_t *buf, const char *str)
typedef struct {
char *data;
size_t len;
size_t cap;
apr_pool_t *pool;
} testbuf_t;
static void testbuf_init(testbuf_t *buf, apr_pool_t *pool, size_t initial_capacity)
{
buf->pool = pool;
buf->cap = initial_capacity;
buf->len = 0;
buf->data = apr_palloc(pool, initial_capacity);
buf->data[0] = '\0';
}
static void testbuf_append(testbuf_t *buf, const char *str, size_t len)
{
if (str == NULL) {
return;
}
if (len == (size_t)-1) {
len = strlen(str);
}
if (buf->len + len + 1 > buf->cap) {
size_t new_cap = (buf->len + len + 1) * 2;
char *new_data = apr_palloc(buf->pool, new_cap);
memcpy(new_data, buf->data, buf->len + 1);
buf->data = new_data;
buf->cap = new_cap;
}
memcpy(buf->data + buf->len, str, len);
buf->len += len;
buf->data[buf->len] = '\0';
}
static void testbuf_append_char(testbuf_t *buf, char c)
{
if (buf->len + 2 > buf->cap) {
size_t new_cap = (buf->cap * 2);
char *new_data = apr_palloc(buf->pool, new_cap);
memcpy(new_data, buf->data, buf->len + 1);
buf->data = new_data;
buf->cap = new_cap;
}
buf->data[buf->len++] = c;
buf->data[buf->len] = '\0';
}
/* Mock JSON string escaping function for testing */
static void append_json_string(testbuf_t *buf, const char *str)
{
if (str == NULL) {
return;
}
for (const char *p = str; *p; p++) {
char c = *p;
switch (c) {
case '"': apr_strbuf_append(buf, "\\\"", 2); break;
case '\\': apr_strbuf_append(buf, "\\\\", 2); break;
case '\b': apr_strbuf_append(buf, "\\b", 2); break;
case '\f': apr_strbuf_append(buf, "\\f", 2); break;
case '\n': apr_strbuf_append(buf, "\\n", 2); break;
case '\r': apr_strbuf_append(buf, "\\r", 2); break;
case '\t': apr_strbuf_append(buf, "\\t", 2); break;
case '"': testbuf_append(buf, "\\\"", 2); break;
case '\\': testbuf_append(buf, "\\\\", 2); break;
case '\b': testbuf_append(buf, "\\b", 2); break;
case '\f': testbuf_append(buf, "\\f", 2); break;
case '\n': testbuf_append(buf, "\\n", 2); break;
case '\r': testbuf_append(buf, "\\r", 2); break;
case '\t': testbuf_append(buf, "\\t", 2); break;
default:
if ((unsigned char)c < 0x20) {
char unicode[8];
apr_snprintf(unicode, sizeof(unicode), "\\u%04x", (unsigned char)c);
apr_strbuf_append(buf, unicode, -1);
testbuf_append(buf, unicode, (size_t)-1);
} else {
apr_strbuf_append_char(buf, c);
testbuf_append_char(buf, c);
}
break;
}
@ -46,16 +100,16 @@ static void append_json_string(apr_pool_t *pool, apr_strbuf_t *buf, const char *
static void test_json_escape_empty_string(void **state)
{
apr_pool_t *pool;
testbuf_t buf;
(void)state;
apr_pool_create(&pool, NULL);
apr_strbuf_t buf;
char *initial = apr_palloc(pool, 256);
apr_strbuf_init(pool, &buf, initial, 256);
append_json_string(pool, &buf, "");
assert_string_equal(buf.buf, "");
testbuf_init(&buf, pool, 256);
append_json_string(&buf, "");
assert_string_equal(buf.data, "");
apr_pool_destroy(pool);
}
@ -63,16 +117,16 @@ static void test_json_escape_empty_string(void **state)
static void test_json_escape_simple_string(void **state)
{
apr_pool_t *pool;
testbuf_t buf;
(void)state;
apr_pool_create(&pool, NULL);
apr_strbuf_t buf;
char *initial = apr_palloc(pool, 256);
apr_strbuf_init(pool, &buf, initial, 256);
append_json_string(pool, &buf, "hello world");
assert_string_equal(buf.buf, "hello world");
testbuf_init(&buf, pool, 256);
append_json_string(&buf, "hello world");
assert_string_equal(buf.data, "hello world");
apr_pool_destroy(pool);
}
@ -80,16 +134,16 @@ static void test_json_escape_simple_string(void **state)
static void test_json_escape_quotes(void **state)
{
apr_pool_t *pool;
testbuf_t buf;
(void)state;
apr_pool_create(&pool, NULL);
apr_strbuf_t buf;
char *initial = apr_palloc(pool, 256);
apr_strbuf_init(pool, &buf, initial, 256);
append_json_string(pool, &buf, "hello \"world\"");
assert_string_equal(buf.buf, "hello \\\"world\\\"");
testbuf_init(&buf, pool, 256);
append_json_string(&buf, "hello \"world\"");
assert_string_equal(buf.data, "hello \\\"world\\\"");
apr_pool_destroy(pool);
}
@ -97,16 +151,16 @@ static void test_json_escape_quotes(void **state)
static void test_json_escape_backslashes(void **state)
{
apr_pool_t *pool;
testbuf_t buf;
(void)state;
apr_pool_create(&pool, NULL);
apr_strbuf_t buf;
char *initial = apr_palloc(pool, 256);
apr_strbuf_init(pool, &buf, initial, 256);
append_json_string(pool, &buf, "path\\to\\file");
assert_string_equal(buf.buf, "path\\\\to\\\\file");
testbuf_init(&buf, pool, 256);
append_json_string(&buf, "path\\to\\file");
assert_string_equal(buf.data, "path\\\\to\\\\file");
apr_pool_destroy(pool);
}
@ -114,16 +168,16 @@ static void test_json_escape_backslashes(void **state)
static void test_json_escape_newlines_tabs(void **state)
{
apr_pool_t *pool;
testbuf_t buf;
(void)state;
apr_pool_create(&pool, NULL);
apr_strbuf_t buf;
char *initial = apr_palloc(pool, 256);
apr_strbuf_init(pool, &buf, initial, 256);
append_json_string(pool, &buf, "line1\nline2\ttab");
assert_string_equal(buf.buf, "line1\\nline2\\ttab");
testbuf_init(&buf, pool, 256);
append_json_string(&buf, "line1\nline2\ttab");
assert_string_equal(buf.data, "line1\\nline2\\ttab");
apr_pool_destroy(pool);
}
@ -131,18 +185,18 @@ static void test_json_escape_newlines_tabs(void **state)
static void test_json_escape_control_chars(void **state)
{
apr_pool_t *pool;
testbuf_t buf;
(void)state;
apr_pool_create(&pool, NULL);
apr_strbuf_t buf;
char *initial = apr_palloc(pool, 256);
apr_strbuf_init(pool, &buf, initial, 256);
testbuf_init(&buf, pool, 256);
/* Test with bell character (0x07) */
append_json_string(pool, &buf, "test\bell");
append_json_string(&buf, "test\bell");
/* Should contain unicode escape */
assert_true(strstr(buf.buf, "\\u0007") != NULL);
assert_true(strstr(buf.data, "\\u0007") != NULL);
apr_pool_destroy(pool);
}
@ -150,16 +204,16 @@ static void test_json_escape_control_chars(void **state)
static void test_json_escape_null_string(void **state)
{
apr_pool_t *pool;
testbuf_t buf;
(void)state;
apr_pool_create(&pool, NULL);
apr_strbuf_t buf;
char *initial = apr_palloc(pool, 256);
apr_strbuf_init(pool, &buf, initial, 256);
append_json_string(pool, &buf, NULL);
assert_string_equal(buf.buf, "");
testbuf_init(&buf, pool, 256);
append_json_string(&buf, NULL);
assert_string_equal(buf.data, "");
apr_pool_destroy(pool);
}
@ -167,17 +221,17 @@ static void test_json_escape_null_string(void **state)
static void test_json_escape_user_agent(void **state)
{
apr_pool_t *pool;
apr_pool_create(&pool, NULL);
apr_strbuf_t buf;
char *initial = apr_palloc(pool, 512);
apr_strbuf_init(pool, &buf, initial, 512);
testbuf_t buf;
const char *ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) \"Test\"";
append_json_string(pool, &buf, ua);
assert_true(strstr(buf.buf, "\\\"Test\\\"") != NULL);
(void)state;
apr_pool_create(&pool, NULL);
testbuf_init(&buf, pool, 512);
append_json_string(&buf, ua);
assert_true(strstr(buf.data, "\\\"Test\\\"") != NULL);
apr_pool_destroy(pool);
}
@ -193,6 +247,6 @@ int main(void)
cmocka_unit_test(test_json_escape_null_string),
cmocka_unit_test(test_json_escape_user_agent),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}