Refactor: thread-safe per-process state and add tests

Major changes:
- Move child state from global variable to server config (reqin_log_server_conf_t)
- Add reqin_log_create_server_conf() for proper per-server initialization
- Fix thread safety for worker/event MPMs
- Add cmocka unit tests (test_module_real.c)
- Add Python integration tests (test_integration.py)
- Update CI workflow and Dockerfiles for test execution
- Fix: Remove child_exit hook (not in architecture.yml)

Tests:
- Unit tests: JSON escaping, ISO8601 formatting, header truncation
- Integration tests: basic_logging, header_limits, socket_unavailable, socket_loss

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
Jacquin Antoine
2026-02-26 23:28:45 +01:00
parent 7cfd14fb65
commit 070c2a7bd2
7 changed files with 1425 additions and 340 deletions

View File

@ -11,9 +11,9 @@
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SOCKET_PATH="/tmp/mod_reqin_log.sock"
SOCKET_PATH="${SOCKET_PATH:-/tmp/mod_reqin_log.sock}"
LOG_FILE="/tmp/mod_reqin_log_test.log"
APACHE_URL="${APACHE_URL:-http://localhost}"
APACHE_URL="${APACHE_URL:-http://localhost:8080}"
# Colors for output
RED='\033[0;31m'
@ -27,17 +27,17 @@ TESTS_PASSED=0
TESTS_FAILED=0
log_info() {
echo -e "${YELLOW}[INFO]${NC} $1"
echo -e "${YELLOW}[INFO]${NC} $1" >&2
}
log_pass() {
echo -e "${GREEN}[PASS]${NC} $1"
((TESTS_PASSED++))
echo -e "${GREEN}[PASS]${NC} $1" >&2
((TESTS_PASSED++)) || true
}
log_fail() {
echo -e "${RED}[FAIL]${NC} $1"
((TESTS_FAILED++))
echo -e "${RED}[FAIL]${NC} $1" >&2
((TESTS_FAILED++)) || true
}
cleanup() {
@ -51,173 +51,175 @@ trap cleanup EXIT
# Check prerequisites
check_prerequisites() {
log_info "Checking prerequisites..."
if ! command -v curl &> /dev/null; then
echo "Error: curl is required but not installed."
exit 1
fi
if ! command -v python3 &> /dev/null; then
echo "Error: python3 is required but not installed."
exit 1
fi
}
# Start socket consumer
start_consumer() {
log_info "Starting socket consumer..."
python3 "$SCRIPT_DIR/socket_consumer.py" "$SOCKET_PATH" -o "$LOG_FILE" &
CONSUMER_PID=$!
sleep 2
if ! kill -0 $CONSUMER_PID 2>/dev/null; then
echo "Error: Failed to start socket consumer"
exit 1
fi
}
# Stop socket consumer
stop_consumer() {
log_info "Stopping socket consumer..."
if [ -n "$CONSUMER_PID" ]; then
kill $CONSUMER_PID 2>/dev/null || true
wait $CONSUMER_PID 2>/dev/null || true
fi
# Strip timestamp prefix from log line
strip_log_prefix() {
# Remove [YYYY-MM-DD HH:MM:SS] prefix from log lines
sed 's/^\[[0-9-]* [0-9:]*\] //'
}
# Test: Basic request logging
test_basic_logging() {
((TESTS_RUN++))
((TESTS_RUN++)) || true
log_info "Test: Basic request logging"
curl -s "$APACHE_URL/" > /dev/null
sleep 1
if grep -q '"method":"GET"' "$LOG_FILE" 2>/dev/null; then
# Strip prefix and check for method
if strip_log_prefix < "$LOG_FILE" 2>/dev/null | grep -q '"method":"GET"'; then
log_pass "Basic logging test"
return 0
else
log_fail "Basic logging test - No GET method found in logs"
return 1
fi
}
# Test: Custom header logging
test_custom_headers() {
((TESTS_RUN++))
((TESTS_RUN++)) || true
log_info "Test: Custom header logging"
curl -s -H "X-Request-Id: test-12345" "$APACHE_URL/" > /dev/null
sleep 1
if grep -q '"header_X-Request-Id":"test-12345"' "$LOG_FILE" 2>/dev/null; then
if strip_log_prefix < "$LOG_FILE" 2>/dev/null | grep -q '"header_X-Request-Id":"test-12345"'; then
log_pass "Custom header logging test"
return 0
else
log_fail "Custom header logging test - X-Request-Id not found in logs"
return 1
fi
}
# Test: Multiple headers
test_multiple_headers() {
((TESTS_RUN++))
((TESTS_RUN++)) || true
log_info "Test: Multiple headers"
curl -s \
-H "X-Request-Id: req-abc" \
-H "X-Trace-Id: trace-xyz" \
"$APACHE_URL/" > /dev/null
sleep 1
local found_request_id=$(grep -c '"header_X-Request-Id":"req-abc"' "$LOG_FILE" 2>/dev/null || echo 0)
local found_trace_id=$(grep -c '"header_X-Trace-Id":"trace-xyz"' "$LOG_FILE" 2>/dev/null || echo 0)
local stripped_logs=$(strip_log_prefix < "$LOG_FILE" 2>/dev/null)
local found_request_id=$(echo "$stripped_logs" | grep -c '"header_X-Request-Id":"req-abc"' || echo 0)
local found_trace_id=$(echo "$stripped_logs" | grep -c '"header_X-Trace-Id":"trace-xyz"' || echo 0)
if [ "$found_request_id" -gt 0 ] && [ "$found_trace_id" -gt 0 ]; then
log_pass "Multiple headers test"
return 0
else
log_fail "Multiple headers test - Not all headers found"
return 1
fi
}
# Test: JSON format validation
test_json_format() {
((TESTS_RUN++))
((TESTS_RUN++)) || true
log_info "Test: JSON format validation"
curl -s "$APACHE_URL/" > /dev/null
sleep 1
# Get last line and validate JSON
local last_line=$(tail -1 "$LOG_FILE" 2>/dev/null | sed 's/^\[.*\] //')
# Get last line, strip prefix, and validate JSON
local last_line=$(tail -1 "$LOG_FILE" 2>/dev/null | strip_log_prefix)
if echo "$last_line" | python3 -m json.tool > /dev/null 2>&1; then
log_pass "JSON format validation test"
return 0
else
log_fail "JSON format validation test - Invalid JSON format"
return 1
fi
}
# Test: Required fields presence
test_required_fields() {
((TESTS_RUN++))
((TESTS_RUN++)) || true
log_info "Test: Required fields presence"
curl -s "$APACHE_URL/" > /dev/null
sleep 1
local last_line=$(tail -1 "$LOG_FILE" 2>/dev/null | sed 's/^\[.*\] //')
local last_line=$(tail -1 "$LOG_FILE" 2>/dev/null | strip_log_prefix)
local required_fields=("time" "timestamp" "src_ip" "dst_ip" "method" "path" "host")
local all_present=true
for field in "${required_fields[@]}"; do
if ! echo "$last_line" | grep -q "\"$field\":"; then
all_present=false
break
fi
done
if $all_present; then
log_pass "Required fields presence test"
return 0
else
log_fail "Required fields presence test - Missing required fields"
return 1
fi
}
# Test: HTTP method variations
test_method_variations() {
((TESTS_RUN++))
((TESTS_RUN++)) || true
log_info "Test: HTTP method variations"
curl -s -X POST "$APACHE_URL/" > /dev/null
curl -s -X PUT "$APACHE_URL/" > /dev/null
curl -s -X DELETE "$APACHE_URL/" > /dev/null
sleep 1
local found_post=$(grep -c '"method":"POST"' "$LOG_FILE" 2>/dev/null || echo 0)
local found_put=$(grep -c '"method":"PUT"' "$LOG_FILE" 2>/dev/null || echo 0)
local found_delete=$(grep -c '"method":"DELETE"' "$LOG_FILE" 2>/dev/null || echo 0)
local stripped_logs=$(strip_log_prefix < "$LOG_FILE" 2>/dev/null)
local found_post=$(echo "$stripped_logs" | grep -c '"method":"POST"' || echo 0)
local found_put=$(echo "$stripped_logs" | grep -c '"method":"PUT"' || echo 0)
local found_delete=$(echo "$stripped_logs" | grep -c '"method":"DELETE"' || echo 0)
if [ "$found_post" -gt 0 ] && [ "$found_put" -gt 0 ] && [ "$found_delete" -gt 0 ]; then
log_pass "HTTP method variations test"
return 0
else
log_fail "HTTP method variations test - Not all methods found"
return 1
fi
}
# Test: Path logging
test_path_logging() {
((TESTS_RUN++))
((TESTS_RUN++)) || true
log_info "Test: Path logging"
curl -s "$APACHE_URL/api/users" > /dev/null
curl -s "$APACHE_URL/foo/bar/baz" > /dev/null
sleep 1
local found_api=$(grep -c '"path":"/api/users"' "$LOG_FILE" 2>/dev/null || echo 0)
local found_foo=$(grep -c '"path":"/foo/bar/baz"' "$LOG_FILE" 2>/dev/null || echo 0)
local stripped_logs=$(strip_log_prefix < "$LOG_FILE" 2>/dev/null)
local found_api=$(echo "$stripped_logs" | grep -c '"path":"/api/users"' || echo 0)
local found_foo=$(echo "$stripped_logs" | grep -c '"path":"/foo/bar/baz"' || echo 0)
if [ "$found_api" -gt 0 ] && [ "$found_foo" -gt 0 ]; then
log_pass "Path logging test"
return 0
else
log_fail "Path logging test - Not all paths found"
return 1
fi
}
@ -227,25 +229,18 @@ main() {
echo "mod_reqin_log Integration Tests"
echo "========================================"
echo ""
check_prerequisites
start_consumer
# Give Apache time to connect to socket
sleep 2
# Run tests
test_basic_logging
test_custom_headers
test_multiple_headers
test_json_format
test_required_fields
test_method_variations
test_path_logging
# Stop consumer
stop_consumer
# Run individual tests
test_basic_logging || true
test_custom_headers || true
test_multiple_headers || true
test_json_format || true
test_required_fields || true
test_method_variations || true
test_path_logging || true
# Summary
echo ""
echo "========================================"
@ -255,11 +250,11 @@ main() {
echo -e "Tests passed: ${GREEN}$TESTS_PASSED${NC}"
echo -e "Tests failed: ${RED}$TESTS_FAILED${NC}"
echo ""
if [ $TESTS_FAILED -gt 0 ]; then
exit 1
fi
echo -e "${GREEN}All tests passed!${NC}"
exit 0
}