cocoon/tests/unit/test_dashboard.c
xfy911 d01a5d7559
Some checks failed
CI / build (push) Failing after 11s
feat: 实现管理 Dashboard(/_status + SSE 实时指标流)
- 新增 dashboard.c / dashboard.h,提供自包含 HTML 管理面板
- 复用现有 SSE 基础设施(sse.c/sse.h)实现实时指标推送
- 每 2 秒推送 JSON 指标(uptime/connections/requests/2xx/3xx/4xx/5xx),每 6 秒心跳
- 端点:/_status(HTML 面板)、/_status/events(SSE 指标流)
- 将 server.c 中的统计变量改为全局可见,供 dashboard 访问
- 新增 9 个单元测试覆盖 dashboard_handle_request / dashboard_handle_events
- 编译零警告,474 单元测试 + 111 集成测试通过

(4 个集成测试失败为既有反向代理环境问题,与本次改动无关)
2026-06-16 00:27:59 +08:00

111 lines
3.7 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "unity.h"
#include "dashboard.h"
#include "http.h"
#include <string.h>
/* 为测试提供全局指标变量的定义server.c 中定义的副本) */
atomic_int g_active_connections = 0;
time_t g_server_start_time = 0;
uint32_t g_max_connections = 0;
atomic_uint g_total_requests = 0;
atomic_uint g_response_2xx = 0;
atomic_uint g_response_3xx = 0;
atomic_uint g_response_4xx = 0;
atomic_uint g_response_5xx = 0;
atomic_uint g_response_200 = 0;
atomic_uint g_response_404 = 0;
/* ===== dashboard_handle_request 路径匹配测试 ===== */
void test_dashboard_not_match(void) {
http_request_t req = {0};
req.method = HTTP_GET;
strncpy(req.path, "/index.html", sizeof(req.path) - 1);
/* fd = -1 不会实际发送,但匹配逻辑不受影响 */
TEST_ASSERT_FALSE(dashboard_handle_request(-1, &req));
}
void test_dashboard_match_status(void) {
http_request_t req = {0};
req.method = HTTP_GET;
strncpy(req.path, "/_status", sizeof(req.path) - 1);
req.keep_alive = false;
/* fd = -1 会导致 send 失败,但函数应返回 true请求已识别 */
TEST_ASSERT_TRUE(dashboard_handle_request(-1, &req));
}
void test_dashboard_post_not_allowed(void) {
http_request_t req = {0};
req.method = HTTP_POST;
strncpy(req.path, "/_status", sizeof(req.path) - 1);
/* 非 GET 应返回 405但函数仍返回 true请求已处理 */
TEST_ASSERT_TRUE(dashboard_handle_request(-1, &req));
}
void test_dashboard_null_req(void) {
TEST_ASSERT_FALSE(dashboard_handle_request(-1, NULL));
}
/* ===== dashboard_sse_handle_request 路径匹配测试 ===== */
void test_dashboard_sse_not_match(void) {
http_request_t req = {0};
req.method = HTTP_GET;
strncpy(req.path, "/_sse", sizeof(req.path) - 1);
TEST_ASSERT_FALSE(dashboard_sse_handle_request(-1, &req));
}
void test_dashboard_sse_match_events(void) {
http_request_t req = {0};
req.method = HTTP_GET;
strncpy(req.path, "/_status/events", sizeof(req.path) - 1);
/* fd = -1 会导致 send 失败,快速退出循环,返回 true */
TEST_ASSERT_TRUE(dashboard_sse_handle_request(-1, &req));
}
void test_dashboard_sse_post_not_allowed(void) {
http_request_t req = {0};
req.method = HTTP_POST;
strncpy(req.path, "/_status/events", sizeof(req.path) - 1);
TEST_ASSERT_TRUE(dashboard_sse_handle_request(-1, &req));
}
void test_dashboard_sse_null_req(void) {
TEST_ASSERT_FALSE(dashboard_sse_handle_request(-1, NULL));
}
/* ===== 指标变量存在性验证 ===== */
void test_metrics_variables_exist(void) {
/* 确认全局指标变量可被访问(链接期检查) */
TEST_ASSERT_EQUAL_INT(0, atomic_load(&g_active_connections));
TEST_ASSERT_EQUAL_INT(0, atomic_load(&g_total_requests));
TEST_ASSERT_EQUAL_INT(0, atomic_load(&g_response_2xx));
TEST_ASSERT_EQUAL_INT(0, atomic_load(&g_response_3xx));
TEST_ASSERT_EQUAL_INT(0, atomic_load(&g_response_4xx));
TEST_ASSERT_EQUAL_INT(0, atomic_load(&g_response_5xx));
TEST_ASSERT_EQUAL_INT(0, atomic_load(&g_response_200));
TEST_ASSERT_EQUAL_INT(0, atomic_load(&g_response_404));
TEST_ASSERT_EQUAL_UINT(0, g_max_connections);
TEST_ASSERT_EQUAL_INT64(0, g_server_start_time);
}
void setUp(void) {}
void tearDown(void) {}
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_dashboard_not_match);
RUN_TEST(test_dashboard_match_status);
RUN_TEST(test_dashboard_post_not_allowed);
RUN_TEST(test_dashboard_null_req);
RUN_TEST(test_dashboard_sse_not_match);
RUN_TEST(test_dashboard_sse_match_events);
RUN_TEST(test_dashboard_sse_post_not_allowed);
RUN_TEST(test_dashboard_sse_null_req);
RUN_TEST(test_metrics_variables_exist);
return UNITY_END();
}