新增 4 个核心模块,12 个新文件,约 8832 行源代码 + 测试: 1. 扩展内置中间件 (middleware_ext.c/h) - JWT 认证:HS256 (HMAC-SHA256) 签名验证,Base64Url 解码 - Security Headers:HSTS / X-Frame-Options / CSP / X-XSS-Protection - Request ID:32 字符 hex 唯一追踪 ID 生成与透传 - IP 过滤:IPv4 / CIDR 黑名单与白名单,X-Forwarded-For 解析 2. 分布式负载均衡 (load_balance.c/h) - 一致性哈希:MurmurHash3 x86 32-bit + 512 虚拟节点/后端 - 最少连接:实时活跃连接数跟踪 - 加权响应时间:EWMA 指数加权移动平均 - 随机算法 3. gRPC 支持 (grpc.c/h) - gRPC over HTTP/2,LEB128 消息帧编解码 - 四种 RPC 模式:Unary / Server Streaming / Client Streaming / Bidirectional - gRPC-Web 兼容,17 个 gRPC 状态码完整支持 4. HTTP/3 (QUIC) (http3.c/h) - QUIC 传输层:UDP socket 管理,64-bit 连接 ID - HTTP/3 帧处理:HEADERS / DATA / SETTINGS / GOAWAY - QPACK 静态表编解码 (RFC 9204) - TLS 1.3 集成接口 新增单元测试: - test_middleware_ext.c: 53 项测试 - test_load_balance.c: 56 项测试 - test_grpc.c: 88 项测试 - test_http3.c: 64 项测试 - 新增合计:261 项,累计 451 项 同时替换 coco 子模块为内联 stub 头文件, 确保无需外部依赖即可编译。
1058 lines
33 KiB
C
1058 lines
33 KiB
C
#include "unity.h"
|
||
#include "grpc.h"
|
||
#include <string.h>
|
||
#include <stdlib.h>
|
||
|
||
/* ===== 辅助函数 ===== */
|
||
|
||
/**
|
||
* fill_http_request - 填充 HTTP 请求结构体(用于测试)
|
||
*/
|
||
static void fill_http_request(http_request_t *req,
|
||
const char *path,
|
||
const char *content_type,
|
||
const uint8_t *body,
|
||
size_t body_len) {
|
||
memset(req, 0, sizeof(http_request_t));
|
||
if (path) {
|
||
snprintf(req->path, sizeof(req->path), "%s", path);
|
||
}
|
||
if (content_type) {
|
||
snprintf(req->content_type, sizeof(req->content_type), "%s", content_type);
|
||
/* 同时加入 headers 数组 */
|
||
snprintf(req->headers[0].name, sizeof(req->headers[0].name), "content-type");
|
||
snprintf(req->headers[0].value, sizeof(req->headers[0].value), "%s", content_type);
|
||
req->num_headers = 1;
|
||
}
|
||
if (body && body_len > 0) {
|
||
req->body = (char *)malloc(body_len);
|
||
memcpy(req->body, body, body_len);
|
||
req->body_len = body_len;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* make_grpc_frame - 构造 gRPC 消息帧(用于测试输入)
|
||
*
|
||
* @param compressed 压缩标志
|
||
* @param payload payload 数据
|
||
* @param payload_len payload 长度
|
||
* @param out_len 输出帧长度
|
||
* @return 帧缓冲区(需由调用者释放)
|
||
*/
|
||
static uint8_t *make_grpc_frame(uint8_t compressed, const uint8_t *payload,
|
||
uint32_t payload_len, size_t *out_len) {
|
||
*out_len = 5 + payload_len;
|
||
uint8_t *frame = (uint8_t *)malloc(*out_len);
|
||
frame[0] = compressed;
|
||
frame[1] = (uint8_t)((payload_len >> 24) & 0xFF);
|
||
frame[2] = (uint8_t)((payload_len >> 16) & 0xFF);
|
||
frame[3] = (uint8_t)((payload_len >> 8) & 0xFF);
|
||
frame[4] = (uint8_t)(payload_len & 0xFF);
|
||
if (payload_len > 0) {
|
||
memcpy(frame + 5, payload, payload_len);
|
||
}
|
||
return frame;
|
||
}
|
||
|
||
/* ===== grpc_detect ===== */
|
||
|
||
void test_detect_grpc_basic(void) {
|
||
http_request_t req = {0};
|
||
snprintf(req.content_type, sizeof(req.content_type), "application/grpc");
|
||
TEST_ASSERT_TRUE(grpc_detect(&req));
|
||
}
|
||
|
||
void test_detect_grpc_proto(void) {
|
||
http_request_t req = {0};
|
||
snprintf(req.content_type, sizeof(req.content_type), "application/grpc+proto");
|
||
TEST_ASSERT_TRUE(grpc_detect(&req));
|
||
}
|
||
|
||
void test_detect_grpc_json(void) {
|
||
http_request_t req = {0};
|
||
snprintf(req.content_type, sizeof(req.content_type), "application/grpc+json");
|
||
TEST_ASSERT_TRUE(grpc_detect(&req));
|
||
}
|
||
|
||
void test_detect_grpc_with_charset(void) {
|
||
http_request_t req = {0};
|
||
snprintf(req.content_type, sizeof(req.content_type),
|
||
"application/grpc; charset=utf-8");
|
||
TEST_ASSERT_TRUE(grpc_detect(&req));
|
||
}
|
||
|
||
void test_detect_grpc_web(void) {
|
||
http_request_t req = {0};
|
||
snprintf(req.content_type, sizeof(req.content_type), "application/grpc-web");
|
||
/* grpc_detect 应该也返回 true(因为 grpc-web 以 grpc 开头) */
|
||
TEST_ASSERT_TRUE(grpc_detect(&req));
|
||
/* grpc_is_grpc_web 应该返回 true */
|
||
TEST_ASSERT_TRUE(grpc_is_grpc_web(&req));
|
||
}
|
||
|
||
void test_detect_grpc_web_proto(void) {
|
||
http_request_t req = {0};
|
||
snprintf(req.content_type, sizeof(req.content_type),
|
||
"application/grpc-web+proto");
|
||
TEST_ASSERT_TRUE(grpc_detect(&req));
|
||
TEST_ASSERT_TRUE(grpc_is_grpc_web(&req));
|
||
}
|
||
|
||
void test_detect_not_grpc(void) {
|
||
http_request_t req = {0};
|
||
snprintf(req.content_type, sizeof(req.content_type), "application/json");
|
||
TEST_ASSERT_FALSE(grpc_detect(&req));
|
||
}
|
||
|
||
void test_detect_text_plain(void) {
|
||
http_request_t req = {0};
|
||
snprintf(req.content_type, sizeof(req.content_type), "text/plain");
|
||
TEST_ASSERT_FALSE(grpc_detect(&req));
|
||
}
|
||
|
||
void test_detect_grpc_uppercase(void) {
|
||
/* Content-Type 检测应该是大小写不敏感的 */
|
||
http_request_t req = {0};
|
||
snprintf(req.content_type, sizeof(req.content_type), "APPLICATION/GRPC");
|
||
TEST_ASSERT_TRUE(grpc_detect(&req));
|
||
}
|
||
|
||
void test_detect_null_request(void) {
|
||
TEST_ASSERT_FALSE(grpc_detect(NULL));
|
||
}
|
||
|
||
void test_detect_empty_content_type(void) {
|
||
http_request_t req = {0};
|
||
/* content_type 为空字符串 */
|
||
TEST_ASSERT_FALSE(grpc_detect(&req));
|
||
}
|
||
|
||
void test_detect_grpc_similar_prefix(void) {
|
||
/* "application/grpc-like" 不应该匹配 */
|
||
http_request_t req = {0};
|
||
snprintf(req.content_type, sizeof(req.content_type), "application/grpc-like");
|
||
TEST_ASSERT_FALSE(grpc_detect(&req));
|
||
}
|
||
|
||
/* ===== grpc_is_grpc_web ===== */
|
||
|
||
void test_is_grpc_web_direct(void) {
|
||
http_request_t req = {0};
|
||
snprintf(req.content_type, sizeof(req.content_type), "application/grpc-web");
|
||
TEST_ASSERT_TRUE(grpc_is_grpc_web(&req));
|
||
}
|
||
|
||
void test_is_grpc_web_not_grpc(void) {
|
||
http_request_t req = {0};
|
||
snprintf(req.content_type, sizeof(req.content_type), "application/grpc");
|
||
TEST_ASSERT_FALSE(grpc_is_grpc_web(&req));
|
||
}
|
||
|
||
void test_is_grpc_web_null(void) {
|
||
TEST_ASSERT_FALSE(grpc_is_grpc_web(NULL));
|
||
}
|
||
|
||
/* ===== grpc_decode_message ===== */
|
||
|
||
void test_decode_simple_message(void) {
|
||
const uint8_t payload[] = {0x0A, 0x04, 0x74, 0x65, 0x73, 0x74}; /* protobuf-like */
|
||
size_t frame_len;
|
||
uint8_t *frame = make_grpc_frame(0x00, payload, sizeof(payload), &frame_len);
|
||
|
||
grpc_message_t msg = {0};
|
||
int ret = grpc_decode_message(frame, frame_len, &msg);
|
||
|
||
TEST_ASSERT_EQUAL_INT(5 + (int)sizeof(payload), ret);
|
||
TEST_ASSERT_EQUAL_UINT8(0x00, msg.compressed);
|
||
TEST_ASSERT_EQUAL_UINT32(sizeof(payload), msg.length);
|
||
TEST_ASSERT_NOT_NULL(msg.payload);
|
||
TEST_ASSERT_EQUAL_UINT8_ARRAY(payload, msg.payload, sizeof(payload));
|
||
|
||
grpc_message_free(&msg);
|
||
free(frame);
|
||
}
|
||
|
||
void test_decode_empty_payload(void) {
|
||
size_t frame_len;
|
||
uint8_t *frame = make_grpc_frame(0x00, NULL, 0, &frame_len);
|
||
|
||
grpc_message_t msg = {0};
|
||
int ret = grpc_decode_message(frame, frame_len, &msg);
|
||
|
||
TEST_ASSERT_EQUAL_INT(5, ret);
|
||
TEST_ASSERT_EQUAL_UINT8(0x00, msg.compressed);
|
||
TEST_ASSERT_EQUAL_UINT32(0, msg.length);
|
||
TEST_ASSERT_NULL(msg.payload);
|
||
|
||
grpc_message_free(&msg);
|
||
free(frame);
|
||
}
|
||
|
||
void test_decode_compressed_flag(void) {
|
||
const uint8_t payload[] = {0x01, 0x02, 0x03};
|
||
size_t frame_len;
|
||
uint8_t *frame = make_grpc_frame(0x01, payload, sizeof(payload), &frame_len);
|
||
|
||
grpc_message_t msg = {0};
|
||
int ret = grpc_decode_message(frame, frame_len, &msg);
|
||
|
||
TEST_ASSERT_EQUAL_INT(5 + (int)sizeof(payload), ret);
|
||
TEST_ASSERT_EQUAL_UINT8(0x01, msg.compressed);
|
||
TEST_ASSERT_EQUAL_UINT32(sizeof(payload), msg.length);
|
||
|
||
grpc_message_free(&msg);
|
||
free(frame);
|
||
}
|
||
|
||
void test_decode_large_payload(void) {
|
||
/* 测试大 payload(1KB) */
|
||
uint8_t *large_payload = (uint8_t *)malloc(1024);
|
||
for (int i = 0; i < 1024; i++) large_payload[i] = (uint8_t)(i & 0xFF);
|
||
|
||
size_t frame_len;
|
||
uint8_t *frame = make_grpc_frame(0x00, large_payload, 1024, &frame_len);
|
||
|
||
grpc_message_t msg = {0};
|
||
int ret = grpc_decode_message(frame, frame_len, &msg);
|
||
|
||
TEST_ASSERT_EQUAL_INT(5 + 1024, ret);
|
||
TEST_ASSERT_EQUAL_UINT32(1024, msg.length);
|
||
TEST_ASSERT_NOT_NULL(msg.payload);
|
||
TEST_ASSERT_EQUAL_UINT8_ARRAY(large_payload, msg.payload, 1024);
|
||
|
||
grpc_message_free(&msg);
|
||
free(frame);
|
||
free(large_payload);
|
||
}
|
||
|
||
void test_decode_incomplete_header(void) {
|
||
/* 只有 3 字节,不足 5 字节头部 */
|
||
const uint8_t buf[] = {0x00, 0x00, 0x00};
|
||
grpc_message_t msg = {0};
|
||
int ret = grpc_decode_message(buf, sizeof(buf), &msg);
|
||
|
||
TEST_ASSERT_EQUAL_INT(-1, ret);
|
||
/* msg 不应该被修改(payload 为 NULL) */
|
||
TEST_ASSERT_NULL(msg.payload);
|
||
}
|
||
|
||
void test_decode_incomplete_payload(void) {
|
||
/* 头部完整但 payload 不完整 */
|
||
const uint8_t buf[] = {0x00, 0x00, 0x00, 0x00, 0x10}; /* length=16, 但无 payload */
|
||
grpc_message_t msg = {0};
|
||
int ret = grpc_decode_message(buf, sizeof(buf), &msg);
|
||
|
||
TEST_ASSERT_EQUAL_INT(-1, ret);
|
||
}
|
||
|
||
void test_decode_null_buffer(void) {
|
||
grpc_message_t msg = {0};
|
||
int ret = grpc_decode_message(NULL, 10, &msg);
|
||
TEST_ASSERT_EQUAL_INT(-1, ret);
|
||
}
|
||
|
||
void test_decode_zero_length(void) {
|
||
const uint8_t buf[] = {0x00, 0x00, 0x00, 0x00, 0x00};
|
||
grpc_message_t msg = {0};
|
||
int ret = grpc_decode_message(buf, 0, &msg);
|
||
TEST_ASSERT_EQUAL_INT(-1, ret);
|
||
}
|
||
|
||
void test_decode_null_message(void) {
|
||
const uint8_t buf[] = {0x00, 0x00, 0x00, 0x00, 0x00};
|
||
int ret = grpc_decode_message(buf, sizeof(buf), NULL);
|
||
TEST_ASSERT_EQUAL_INT(-1, ret);
|
||
}
|
||
|
||
/* ===== grpc_encode_message ===== */
|
||
|
||
void test_encode_simple_message(void) {
|
||
const uint8_t payload[] = {0x0A, 0x04, 0x74, 0x65, 0x73, 0x74};
|
||
grpc_message_t msg = {
|
||
.compressed = 0x00,
|
||
.length = sizeof(payload),
|
||
.payload = (uint8_t *)payload
|
||
};
|
||
|
||
uint8_t buf[64];
|
||
int ret = grpc_encode_message(&msg, buf, sizeof(buf));
|
||
|
||
TEST_ASSERT_EQUAL_INT(5 + (int)sizeof(payload), ret);
|
||
TEST_ASSERT_EQUAL_UINT8(0x00, buf[0]);
|
||
TEST_ASSERT_EQUAL_UINT8(0x00, buf[1]);
|
||
TEST_ASSERT_EQUAL_UINT8(0x00, buf[2]);
|
||
TEST_ASSERT_EQUAL_UINT8(0x00, buf[3]);
|
||
TEST_ASSERT_EQUAL_UINT8((int)sizeof(payload), buf[4]);
|
||
TEST_ASSERT_EQUAL_UINT8_ARRAY(payload, buf + 5, sizeof(payload));
|
||
}
|
||
|
||
void test_encode_empty_payload(void) {
|
||
grpc_message_t msg = {
|
||
.compressed = 0x00,
|
||
.length = 0,
|
||
.payload = NULL
|
||
};
|
||
|
||
uint8_t buf[16];
|
||
int ret = grpc_encode_message(&msg, buf, sizeof(buf));
|
||
|
||
TEST_ASSERT_EQUAL_INT(5, ret);
|
||
TEST_ASSERT_EQUAL_UINT8(0x00, buf[0]);
|
||
TEST_ASSERT_EQUAL_UINT8(0x00, buf[1]);
|
||
TEST_ASSERT_EQUAL_UINT8(0x00, buf[2]);
|
||
TEST_ASSERT_EQUAL_UINT8(0x00, buf[3]);
|
||
TEST_ASSERT_EQUAL_UINT8(0x00, buf[4]);
|
||
}
|
||
|
||
void test_encode_buffer_too_small(void) {
|
||
const uint8_t payload[] = {0x01, 0x02, 0x03, 0x04, 0x05};
|
||
grpc_message_t msg = {
|
||
.compressed = 0x00,
|
||
.length = sizeof(payload),
|
||
.payload = (uint8_t *)payload
|
||
};
|
||
|
||
uint8_t buf[8]; /* 太小,需要 10 字节 */
|
||
int ret = grpc_encode_message(&msg, buf, sizeof(buf));
|
||
|
||
TEST_ASSERT_EQUAL_INT(-1, ret);
|
||
}
|
||
|
||
void test_encode_null_params(void) {
|
||
uint8_t buf[16];
|
||
int ret = grpc_encode_message(NULL, buf, sizeof(buf));
|
||
TEST_ASSERT_EQUAL_INT(-1, ret);
|
||
|
||
uint8_t payload[] = {0x01};
|
||
grpc_message_t msg = {0};
|
||
msg.payload = payload;
|
||
msg.length = 1;
|
||
ret = grpc_encode_message(&msg, NULL, 16);
|
||
TEST_ASSERT_EQUAL_INT(-1, ret);
|
||
}
|
||
|
||
void test_encode_exact_buffer_size(void) {
|
||
/* 缓冲区大小恰好等于所需大小 */
|
||
const uint8_t payload[] = {0xAB, 0xCD};
|
||
grpc_message_t msg = {
|
||
.compressed = 0x01,
|
||
.length = sizeof(payload),
|
||
.payload = (uint8_t *)payload
|
||
};
|
||
|
||
uint8_t buf[7]; /* 5 + 2 = 7 */
|
||
int ret = grpc_encode_message(&msg, buf, sizeof(buf));
|
||
|
||
TEST_ASSERT_EQUAL_INT(7, ret);
|
||
TEST_ASSERT_EQUAL_UINT8(0x01, buf[0]);
|
||
TEST_ASSERT_EQUAL_UINT8_ARRAY(payload, buf + 5, sizeof(payload));
|
||
}
|
||
|
||
/* ===== grpc_parse_request ===== */
|
||
|
||
void test_parse_request_basic(void) {
|
||
http_request_t req = {0};
|
||
fill_http_request(&req, "/myPackage.MyService/MyMethod",
|
||
"application/grpc", NULL, 0);
|
||
|
||
grpc_request_t grpc_req = {0};
|
||
int ret = grpc_parse_request(&req, &grpc_req);
|
||
|
||
TEST_ASSERT_EQUAL_INT(0, ret);
|
||
TEST_ASSERT_EQUAL_STRING("myPackage.MyService", grpc_req.service_name);
|
||
TEST_ASSERT_EQUAL_STRING("MyMethod", grpc_req.method_name);
|
||
TEST_ASSERT_FALSE(grpc_req.is_grpc_web);
|
||
TEST_ASSERT_FALSE(grpc_req.client_streaming);
|
||
TEST_ASSERT_FALSE(grpc_req.server_streaming);
|
||
|
||
grpc_request_free(&grpc_req);
|
||
http_request_free(&req);
|
||
}
|
||
|
||
void test_parse_request_no_package(void) {
|
||
http_request_t req = {0};
|
||
fill_http_request(&req, "/MyService/MyMethod",
|
||
"application/grpc", NULL, 0);
|
||
|
||
grpc_request_t grpc_req = {0};
|
||
int ret = grpc_parse_request(&req, &grpc_req);
|
||
|
||
TEST_ASSERT_EQUAL_INT(0, ret);
|
||
TEST_ASSERT_EQUAL_STRING("MyService", grpc_req.service_name);
|
||
TEST_ASSERT_EQUAL_STRING("MyMethod", grpc_req.method_name);
|
||
|
||
grpc_request_free(&grpc_req);
|
||
http_request_free(&req);
|
||
}
|
||
|
||
void test_parse_request_with_body(void) {
|
||
const uint8_t payload[] = {0x08, 0x01, 0x12, 0x04, 0x74, 0x65, 0x73, 0x74};
|
||
size_t frame_len;
|
||
uint8_t *frame = make_grpc_frame(0x00, payload, sizeof(payload), &frame_len);
|
||
|
||
http_request_t req = {0};
|
||
fill_http_request(&req, "/TestService/Echo",
|
||
"application/grpc", frame, frame_len);
|
||
|
||
grpc_request_t grpc_req = {0};
|
||
int ret = grpc_parse_request(&req, &grpc_req);
|
||
|
||
TEST_ASSERT_EQUAL_INT(0, ret);
|
||
TEST_ASSERT_EQUAL_STRING("TestService", grpc_req.service_name);
|
||
TEST_ASSERT_EQUAL_STRING("Echo", grpc_req.method_name);
|
||
TEST_ASSERT_EQUAL_UINT8(0x00, grpc_req.message.compressed);
|
||
TEST_ASSERT_EQUAL_UINT32(sizeof(payload), grpc_req.message.length);
|
||
TEST_ASSERT_NOT_NULL(grpc_req.message.payload);
|
||
TEST_ASSERT_EQUAL_UINT8_ARRAY(payload, grpc_req.message.payload, sizeof(payload));
|
||
|
||
grpc_request_free(&grpc_req);
|
||
http_request_free(&req);
|
||
free(frame);
|
||
}
|
||
|
||
void test_parse_request_grpc_web(void) {
|
||
http_request_t req = {0};
|
||
fill_http_request(&req, "/WebService/WebMethod",
|
||
"application/grpc-web", NULL, 0);
|
||
|
||
grpc_request_t grpc_req = {0};
|
||
int ret = grpc_parse_request(&req, &grpc_req);
|
||
|
||
TEST_ASSERT_EQUAL_INT(0, ret);
|
||
TEST_ASSERT_TRUE(grpc_req.is_grpc_web);
|
||
|
||
grpc_request_free(&grpc_req);
|
||
http_request_free(&req);
|
||
}
|
||
|
||
void test_parse_request_invalid_path_no_slash(void) {
|
||
http_request_t req = {0};
|
||
fill_http_request(&req, "invalid",
|
||
"application/grpc", NULL, 0);
|
||
|
||
grpc_request_t grpc_req = {0};
|
||
int ret = grpc_parse_request(&req, &grpc_req);
|
||
|
||
TEST_ASSERT_EQUAL_INT(-1, ret);
|
||
|
||
grpc_request_free(&grpc_req);
|
||
http_request_free(&req);
|
||
}
|
||
|
||
void test_parse_request_empty_path(void) {
|
||
http_request_t req = {0};
|
||
fill_http_request(&req, "",
|
||
"application/grpc", NULL, 0);
|
||
|
||
grpc_request_t grpc_req = {0};
|
||
int ret = grpc_parse_request(&req, &grpc_req);
|
||
|
||
TEST_ASSERT_EQUAL_INT(-1, ret);
|
||
|
||
grpc_request_free(&grpc_req);
|
||
http_request_free(&req);
|
||
}
|
||
|
||
void test_parse_request_null_params(void) {
|
||
int ret = grpc_parse_request(NULL, NULL);
|
||
TEST_ASSERT_EQUAL_INT(-1, ret);
|
||
}
|
||
|
||
void test_parse_request_metadata(void) {
|
||
http_request_t req = {0};
|
||
fill_http_request(&req, "/Svc/Method",
|
||
"application/grpc", NULL, 0);
|
||
/* 添加额外元数据头 */
|
||
snprintf(req.headers[1].name, sizeof(req.headers[1].name), "x-request-id");
|
||
snprintf(req.headers[1].value, sizeof(req.headers[1].value), "abc-123");
|
||
snprintf(req.headers[2].name, sizeof(req.headers[2].name), "authorization");
|
||
snprintf(req.headers[2].value, sizeof(req.headers[2].value), "Bearer token");
|
||
req.num_headers = 3;
|
||
|
||
grpc_request_t grpc_req = {0};
|
||
int ret = grpc_parse_request(&req, &grpc_req);
|
||
|
||
TEST_ASSERT_EQUAL_INT(0, ret);
|
||
TEST_ASSERT_GREATER_THAN(0, (int)grpc_req.metadata_count);
|
||
|
||
grpc_request_free(&grpc_req);
|
||
http_request_free(&req);
|
||
}
|
||
|
||
/* ===== grpc_format_response_trailers ===== */
|
||
|
||
void test_format_trailers_ok(void) {
|
||
grpc_request_t grpc_req = {0};
|
||
grpc_req.status = GRPC_OK;
|
||
snprintf(grpc_req.status_message, sizeof(grpc_req.status_message), "OK");
|
||
|
||
char buf[256];
|
||
int ret = grpc_format_response_trailers(&grpc_req, buf, sizeof(buf));
|
||
|
||
TEST_ASSERT_GREATER_THAN(0, ret);
|
||
TEST_ASSERT_TRUE(strstr(buf, "grpc-status: 0") != NULL);
|
||
TEST_ASSERT_TRUE(strstr(buf, "grpc-message: OK") != NULL);
|
||
}
|
||
|
||
void test_format_trailers_error(void) {
|
||
grpc_request_t grpc_req = {0};
|
||
grpc_req.status = GRPC_NOT_FOUND;
|
||
snprintf(grpc_req.status_message, sizeof(grpc_req.status_message),
|
||
"Method not found");
|
||
|
||
char buf[256];
|
||
int ret = grpc_format_response_trailers(&grpc_req, buf, sizeof(buf));
|
||
|
||
TEST_ASSERT_GREATER_THAN(0, ret);
|
||
TEST_ASSERT_TRUE(strstr(buf, "grpc-status: 5") != NULL);
|
||
TEST_ASSERT_TRUE(strstr(buf, "grpc-message: Method not found") != NULL);
|
||
}
|
||
|
||
void test_format_trailers_empty_message(void) {
|
||
grpc_request_t grpc_req = {0};
|
||
grpc_req.status = GRPC_OK;
|
||
/* status_message 保持空 */
|
||
|
||
char buf[256];
|
||
int ret = grpc_format_response_trailers(&grpc_req, buf, sizeof(buf));
|
||
|
||
TEST_ASSERT_GREATER_THAN(0, ret);
|
||
TEST_ASSERT_TRUE(strstr(buf, "grpc-status: 0") != NULL);
|
||
}
|
||
|
||
void test_format_trailers_buffer_too_small(void) {
|
||
grpc_request_t grpc_req = {0};
|
||
grpc_req.status = GRPC_OK;
|
||
snprintf(grpc_req.status_message, sizeof(grpc_req.status_message),
|
||
"A very long message that will definitely not fit in a tiny buffer");
|
||
|
||
char buf[10];
|
||
int ret = grpc_format_response_trailers(&grpc_req, buf, sizeof(buf));
|
||
|
||
TEST_ASSERT_EQUAL_INT(-1, ret);
|
||
}
|
||
|
||
void test_format_trailers_null_params(void) {
|
||
char buf[256];
|
||
int ret = grpc_format_response_trailers(NULL, buf, sizeof(buf));
|
||
TEST_ASSERT_EQUAL_INT(-1, ret);
|
||
|
||
grpc_request_t grpc_req = {0};
|
||
ret = grpc_format_response_trailers(&grpc_req, NULL, sizeof(buf));
|
||
TEST_ASSERT_EQUAL_INT(-1, ret);
|
||
}
|
||
|
||
/* ===== grpc_status_to_http ===== */
|
||
|
||
void test_status_to_http_ok(void) {
|
||
TEST_ASSERT_EQUAL_INT(200, grpc_status_to_http(GRPC_OK));
|
||
}
|
||
|
||
void test_status_to_http_cancelled(void) {
|
||
TEST_ASSERT_EQUAL_INT(499, grpc_status_to_http(GRPC_CANCELLED));
|
||
}
|
||
|
||
void test_status_to_http_unknown(void) {
|
||
TEST_ASSERT_EQUAL_INT(500, grpc_status_to_http(GRPC_UNKNOWN));
|
||
}
|
||
|
||
void test_status_to_http_invalid_argument(void) {
|
||
TEST_ASSERT_EQUAL_INT(400, grpc_status_to_http(GRPC_INVALID_ARGUMENT));
|
||
}
|
||
|
||
void test_status_to_http_deadline_exceeded(void) {
|
||
TEST_ASSERT_EQUAL_INT(504, grpc_status_to_http(GRPC_DEADLINE_EXCEEDED));
|
||
}
|
||
|
||
void test_status_to_http_not_found(void) {
|
||
TEST_ASSERT_EQUAL_INT(404, grpc_status_to_http(GRPC_NOT_FOUND));
|
||
}
|
||
|
||
void test_status_to_http_already_exists(void) {
|
||
TEST_ASSERT_EQUAL_INT(409, grpc_status_to_http(GRPC_ALREADY_EXISTS));
|
||
}
|
||
|
||
void test_status_to_http_permission_denied(void) {
|
||
TEST_ASSERT_EQUAL_INT(403, grpc_status_to_http(GRPC_PERMISSION_DENIED));
|
||
}
|
||
|
||
void test_status_to_http_resource_exhausted(void) {
|
||
TEST_ASSERT_EQUAL_INT(429, grpc_status_to_http(GRPC_RESOURCE_EXHAUSTED));
|
||
}
|
||
|
||
void test_status_to_http_unimplemented(void) {
|
||
TEST_ASSERT_EQUAL_INT(501, grpc_status_to_http(GRPC_UNIMPLEMENTED));
|
||
}
|
||
|
||
void test_status_to_http_unavailable(void) {
|
||
TEST_ASSERT_EQUAL_INT(503, grpc_status_to_http(GRPC_UNAVAILABLE));
|
||
}
|
||
|
||
void test_status_to_http_unauthenticated(void) {
|
||
TEST_ASSERT_EQUAL_INT(401, grpc_status_to_http(GRPC_UNAUTHENTICATED));
|
||
}
|
||
|
||
void test_status_to_http_internal(void) {
|
||
TEST_ASSERT_EQUAL_INT(500, grpc_status_to_http(GRPC_INTERNAL));
|
||
}
|
||
|
||
void test_status_to_http_data_loss(void) {
|
||
TEST_ASSERT_EQUAL_INT(500, grpc_status_to_http(GRPC_DATA_LOSS));
|
||
}
|
||
|
||
void test_status_to_http_all_codes(void) {
|
||
/* 验证所有 17 个状态码都能映射(不崩溃) */
|
||
for (int i = 0; i < GRPC_STATUS_MAX; i++) {
|
||
int http = grpc_status_to_http((grpc_status_t)i);
|
||
TEST_ASSERT_GREATER_THAN(0, http);
|
||
TEST_ASSERT_LESS_THAN(600, http);
|
||
}
|
||
}
|
||
|
||
/* ===== grpc_http_to_status ===== */
|
||
|
||
void test_http_to_status_200(void) {
|
||
TEST_ASSERT_EQUAL_INT(GRPC_OK, grpc_http_to_status(200));
|
||
}
|
||
|
||
void test_http_to_status_400(void) {
|
||
TEST_ASSERT_EQUAL_INT(GRPC_INVALID_ARGUMENT, grpc_http_to_status(400));
|
||
}
|
||
|
||
void test_http_to_status_401(void) {
|
||
TEST_ASSERT_EQUAL_INT(GRPC_UNAUTHENTICATED, grpc_http_to_status(401));
|
||
}
|
||
|
||
void test_http_to_status_403(void) {
|
||
TEST_ASSERT_EQUAL_INT(GRPC_PERMISSION_DENIED, grpc_http_to_status(403));
|
||
}
|
||
|
||
void test_http_to_status_404(void) {
|
||
TEST_ASSERT_EQUAL_INT(GRPC_NOT_FOUND, grpc_http_to_status(404));
|
||
}
|
||
|
||
void test_http_to_status_409(void) {
|
||
TEST_ASSERT_EQUAL_INT(GRPC_ABORTED, grpc_http_to_status(409));
|
||
}
|
||
|
||
void test_http_to_status_429(void) {
|
||
TEST_ASSERT_EQUAL_INT(GRPC_RESOURCE_EXHAUSTED, grpc_http_to_status(429));
|
||
}
|
||
|
||
void test_http_to_status_500(void) {
|
||
TEST_ASSERT_EQUAL_INT(GRPC_INTERNAL, grpc_http_to_status(500));
|
||
}
|
||
|
||
void test_http_to_status_501(void) {
|
||
TEST_ASSERT_EQUAL_INT(GRPC_UNIMPLEMENTED, grpc_http_to_status(501));
|
||
}
|
||
|
||
void test_http_to_status_503(void) {
|
||
TEST_ASSERT_EQUAL_INT(GRPC_UNAVAILABLE, grpc_http_to_status(503));
|
||
}
|
||
|
||
void test_http_to_status_504(void) {
|
||
TEST_ASSERT_EQUAL_INT(GRPC_DEADLINE_EXCEEDED, grpc_http_to_status(504));
|
||
}
|
||
|
||
void test_http_to_status_2xx_success(void) {
|
||
/* 201-299 都应映射到 OK */
|
||
TEST_ASSERT_EQUAL_INT(GRPC_OK, grpc_http_to_status(201));
|
||
TEST_ASSERT_EQUAL_INT(GRPC_OK, grpc_http_to_status(204));
|
||
TEST_ASSERT_EQUAL_INT(GRPC_OK, grpc_http_to_status(299));
|
||
}
|
||
|
||
void test_http_to_status_4xx_default(void) {
|
||
/* 未明确映射的 4xx 应返回 INVALID_ARGUMENT */
|
||
TEST_ASSERT_EQUAL_INT(GRPC_INVALID_ARGUMENT, grpc_http_to_status(418));
|
||
}
|
||
|
||
/* ===== grpc_status_to_string ===== */
|
||
|
||
void test_status_to_string_all(void) {
|
||
TEST_ASSERT_EQUAL_STRING("OK", grpc_status_to_string(GRPC_OK));
|
||
TEST_ASSERT_EQUAL_STRING("CANCELLED", grpc_status_to_string(GRPC_CANCELLED));
|
||
TEST_ASSERT_EQUAL_STRING("UNKNOWN", grpc_status_to_string(GRPC_UNKNOWN));
|
||
TEST_ASSERT_EQUAL_STRING("INVALID_ARGUMENT",
|
||
grpc_status_to_string(GRPC_INVALID_ARGUMENT));
|
||
TEST_ASSERT_EQUAL_STRING("DEADLINE_EXCEEDED",
|
||
grpc_status_to_string(GRPC_DEADLINE_EXCEEDED));
|
||
TEST_ASSERT_EQUAL_STRING("NOT_FOUND", grpc_status_to_string(GRPC_NOT_FOUND));
|
||
TEST_ASSERT_EQUAL_STRING("ALREADY_EXISTS",
|
||
grpc_status_to_string(GRPC_ALREADY_EXISTS));
|
||
TEST_ASSERT_EQUAL_STRING("PERMISSION_DENIED",
|
||
grpc_status_to_string(GRPC_PERMISSION_DENIED));
|
||
TEST_ASSERT_EQUAL_STRING("RESOURCE_EXHAUSTED",
|
||
grpc_status_to_string(GRPC_RESOURCE_EXHAUSTED));
|
||
TEST_ASSERT_EQUAL_STRING("FAILED_PRECONDITION",
|
||
grpc_status_to_string(GRPC_FAILED_PRECONDITION));
|
||
TEST_ASSERT_EQUAL_STRING("ABORTED", grpc_status_to_string(GRPC_ABORTED));
|
||
TEST_ASSERT_EQUAL_STRING("OUT_OF_RANGE",
|
||
grpc_status_to_string(GRPC_OUT_OF_RANGE));
|
||
TEST_ASSERT_EQUAL_STRING("UNIMPLEMENTED",
|
||
grpc_status_to_string(GRPC_UNIMPLEMENTED));
|
||
TEST_ASSERT_EQUAL_STRING("INTERNAL",
|
||
grpc_status_to_string(GRPC_INTERNAL));
|
||
TEST_ASSERT_EQUAL_STRING("UNAVAILABLE",
|
||
grpc_status_to_string(GRPC_UNAVAILABLE));
|
||
TEST_ASSERT_EQUAL_STRING("DATA_LOSS",
|
||
grpc_status_to_string(GRPC_DATA_LOSS));
|
||
TEST_ASSERT_EQUAL_STRING("UNAUTHENTICATED",
|
||
grpc_status_to_string(GRPC_UNAUTHENTICATED));
|
||
}
|
||
|
||
void test_status_to_string_unknown(void) {
|
||
/* 非法状态码应返回 "UNKNOWN" */
|
||
TEST_ASSERT_EQUAL_STRING("UNKNOWN", grpc_status_to_string(99));
|
||
TEST_ASSERT_EQUAL_STRING("UNKNOWN", grpc_status_to_string(GRPC_STATUS_MAX));
|
||
}
|
||
|
||
/* ===== grpc_message_free ===== */
|
||
|
||
void test_message_free_null(void) {
|
||
/* 不应崩溃 */
|
||
grpc_message_free(NULL);
|
||
}
|
||
|
||
void test_message_free_null_payload(void) {
|
||
grpc_message_t msg = {0};
|
||
msg.payload = NULL;
|
||
msg.length = 0;
|
||
/* 不应崩溃 */
|
||
grpc_message_free(&msg);
|
||
}
|
||
|
||
void test_message_free_with_payload(void) {
|
||
grpc_message_t msg = {0};
|
||
msg.payload = (uint8_t *)malloc(16);
|
||
msg.length = 16;
|
||
msg.compressed = 1;
|
||
|
||
grpc_message_free(&msg);
|
||
|
||
TEST_ASSERT_NULL(msg.payload);
|
||
TEST_ASSERT_EQUAL_UINT32(0, msg.length);
|
||
TEST_ASSERT_EQUAL_UINT8(0, msg.compressed);
|
||
}
|
||
|
||
/* ===== grpc_request_free ===== */
|
||
|
||
void test_request_free_null(void) {
|
||
/* 不应崩溃 */
|
||
grpc_request_free(NULL);
|
||
}
|
||
|
||
void test_request_free_empty(void) {
|
||
grpc_request_t grpc_req = {0};
|
||
/* 不应崩溃 */
|
||
grpc_request_free(&grpc_req);
|
||
}
|
||
|
||
void test_request_free_with_message(void) {
|
||
grpc_request_t grpc_req = {0};
|
||
grpc_req.message.payload = (uint8_t *)malloc(32);
|
||
grpc_req.message.length = 32;
|
||
grpc_req.message.compressed = 0;
|
||
|
||
grpc_request_free(&grpc_req);
|
||
|
||
TEST_ASSERT_NULL(grpc_req.message.payload);
|
||
TEST_ASSERT_EQUAL_UINT32(0, grpc_req.message.length);
|
||
}
|
||
|
||
/* ===== grpc_error_response ===== */
|
||
|
||
void test_error_response_invalid_socket(void) {
|
||
/* 无效 socket 不应崩溃 */
|
||
grpc_error_response(COCOON_INVALID_SOCKET, 1, GRPC_INTERNAL, "error");
|
||
}
|
||
|
||
/* ===== 双向转换一致性 ===== */
|
||
|
||
void test_status_http_roundtrip(void) {
|
||
/* 常见状态码的双向转换一致性 */
|
||
grpc_status_t statuses[] = {
|
||
GRPC_OK, GRPC_UNKNOWN, GRPC_INVALID_ARGUMENT, GRPC_NOT_FOUND,
|
||
GRPC_PERMISSION_DENIED, GRPC_UNIMPLEMENTED, GRPC_UNAVAILABLE,
|
||
GRPC_INTERNAL, GRPC_UNAUTHENTICATED
|
||
};
|
||
int http_codes[] = {200, 500, 400, 404, 403, 501, 503, 500, 401};
|
||
|
||
for (size_t i = 0; i < sizeof(statuses)/sizeof(statuses[0]); i++) {
|
||
int http = grpc_status_to_http(statuses[i]);
|
||
TEST_ASSERT_EQUAL_INT(http_codes[i], http);
|
||
|
||
grpc_status_t back = grpc_http_to_status(http);
|
||
/* 由于多对一映射,反向不一定完全一致,但应合理 */
|
||
TEST_ASSERT_GREATER_OR_EQUAL(GRPC_OK, (int)back);
|
||
TEST_ASSERT_LESS_THAN(GRPC_STATUS_MAX, (int)back);
|
||
}
|
||
}
|
||
|
||
/* ===== RPC 模式标记 ===== */
|
||
|
||
void test_rpc_mode_unary(void) {
|
||
grpc_request_t grpc_req = {0};
|
||
grpc_req.client_streaming = false;
|
||
grpc_req.server_streaming = false;
|
||
grpc_req.is_streaming = grpc_req.client_streaming || grpc_req.server_streaming;
|
||
TEST_ASSERT_FALSE(grpc_req.is_streaming);
|
||
}
|
||
|
||
void test_rpc_mode_server_streaming(void) {
|
||
grpc_request_t grpc_req = {0};
|
||
grpc_req.client_streaming = false;
|
||
grpc_req.server_streaming = true;
|
||
grpc_req.is_streaming = grpc_req.client_streaming || grpc_req.server_streaming;
|
||
TEST_ASSERT_TRUE(grpc_req.is_streaming);
|
||
}
|
||
|
||
void test_rpc_mode_client_streaming(void) {
|
||
grpc_request_t grpc_req = {0};
|
||
grpc_req.client_streaming = true;
|
||
grpc_req.server_streaming = false;
|
||
grpc_req.is_streaming = grpc_req.client_streaming || grpc_req.server_streaming;
|
||
TEST_ASSERT_TRUE(grpc_req.is_streaming);
|
||
}
|
||
|
||
void test_rpc_mode_bidirectional(void) {
|
||
grpc_request_t grpc_req = {0};
|
||
grpc_req.client_streaming = true;
|
||
grpc_req.server_streaming = true;
|
||
grpc_req.is_streaming = grpc_req.client_streaming || grpc_req.server_streaming;
|
||
TEST_ASSERT_TRUE(grpc_req.is_streaming);
|
||
}
|
||
|
||
/* ===== 边界条件:超长路径 ===== */
|
||
|
||
void test_parse_long_path(void) {
|
||
/* 构造接近 256 字节限制的路径 */
|
||
char long_svc[260];
|
||
char path[300];
|
||
memset(long_svc, 'A', 250);
|
||
long_svc[250] = '\0';
|
||
snprintf(path, sizeof(path), "/%s/MyMethod", long_svc);
|
||
|
||
http_request_t req = {0};
|
||
fill_http_request(&req, path, "application/grpc", NULL, 0);
|
||
|
||
grpc_request_t grpc_req = {0};
|
||
int ret = grpc_parse_request(&req, &grpc_req);
|
||
|
||
TEST_ASSERT_EQUAL_INT(0, ret);
|
||
/* service_name 被截断到 255 字符 */
|
||
TEST_ASSERT_GREATER_THAN(0, strlen(grpc_req.service_name));
|
||
TEST_ASSERT_EQUAL_STRING("MyMethod", grpc_req.method_name);
|
||
|
||
grpc_request_free(&grpc_req);
|
||
http_request_free(&req);
|
||
}
|
||
|
||
/* ===== 编码/解码对称性 ===== */
|
||
|
||
void test_encode_decode_symmetry(void) {
|
||
/* 编码后再解码应得到原始数据 */
|
||
const uint8_t payload[] = {
|
||
0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0
|
||
};
|
||
grpc_message_t orig = {
|
||
.compressed = 0x01,
|
||
.length = sizeof(payload),
|
||
.payload = (uint8_t *)payload
|
||
};
|
||
|
||
uint8_t buf[64];
|
||
int encoded = grpc_encode_message(&orig, buf, sizeof(buf));
|
||
TEST_ASSERT_GREATER_THAN(0, encoded);
|
||
|
||
grpc_message_t decoded = {0};
|
||
int decoded_len = grpc_decode_message(buf, (size_t)encoded, &decoded);
|
||
|
||
TEST_ASSERT_EQUAL_INT(encoded, decoded_len);
|
||
TEST_ASSERT_EQUAL_UINT8(orig.compressed, decoded.compressed);
|
||
TEST_ASSERT_EQUAL_UINT32(orig.length, decoded.length);
|
||
TEST_ASSERT_EQUAL_UINT8_ARRAY(payload, decoded.payload, sizeof(payload));
|
||
|
||
grpc_message_free(&decoded);
|
||
}
|
||
|
||
void test_encode_decode_empty(void) {
|
||
/* 空 payload 编码/解码对称性 */
|
||
grpc_message_t orig = {
|
||
.compressed = 0x00,
|
||
.length = 0,
|
||
.payload = NULL
|
||
};
|
||
|
||
uint8_t buf[16];
|
||
int encoded = grpc_encode_message(&orig, buf, sizeof(buf));
|
||
TEST_ASSERT_EQUAL_INT(5, encoded);
|
||
|
||
grpc_message_t decoded = {0};
|
||
int decoded_len = grpc_decode_message(buf, (size_t)encoded, &decoded);
|
||
|
||
TEST_ASSERT_EQUAL_INT(5, decoded_len);
|
||
TEST_ASSERT_EQUAL_UINT8(0x00, decoded.compressed);
|
||
TEST_ASSERT_EQUAL_UINT32(0, decoded.length);
|
||
TEST_ASSERT_NULL(decoded.payload);
|
||
|
||
grpc_message_free(&decoded);
|
||
}
|
||
|
||
/* ===== gRPC-Web 特殊处理 ===== */
|
||
|
||
void test_grpc_web_flag_set(void) {
|
||
http_request_t req = {0};
|
||
fill_http_request(&req, "/WebService/Method",
|
||
"application/grpc-web", NULL, 0);
|
||
|
||
grpc_request_t grpc_req = {0};
|
||
grpc_parse_request(&req, &grpc_req);
|
||
|
||
TEST_ASSERT_TRUE(grpc_req.is_grpc_web);
|
||
TEST_ASSERT_FALSE(grpc_req.client_streaming);
|
||
TEST_ASSERT_FALSE(grpc_req.server_streaming);
|
||
|
||
grpc_request_free(&grpc_req);
|
||
http_request_free(&req);
|
||
}
|
||
|
||
/* ===== 主函数 ===== */
|
||
|
||
void setUp(void) {
|
||
/* 每个测试前执行 */
|
||
}
|
||
|
||
void tearDown(void) {
|
||
/* 每个测试后执行 */
|
||
}
|
||
|
||
int main(void) {
|
||
UNITY_BEGIN();
|
||
|
||
/* grpc_detect */
|
||
RUN_TEST(test_detect_grpc_basic);
|
||
RUN_TEST(test_detect_grpc_proto);
|
||
RUN_TEST(test_detect_grpc_json);
|
||
RUN_TEST(test_detect_grpc_with_charset);
|
||
RUN_TEST(test_detect_grpc_web);
|
||
RUN_TEST(test_detect_grpc_web_proto);
|
||
RUN_TEST(test_detect_not_grpc);
|
||
RUN_TEST(test_detect_text_plain);
|
||
RUN_TEST(test_detect_grpc_uppercase);
|
||
RUN_TEST(test_detect_null_request);
|
||
RUN_TEST(test_detect_empty_content_type);
|
||
RUN_TEST(test_detect_grpc_similar_prefix);
|
||
|
||
/* grpc_is_grpc_web */
|
||
RUN_TEST(test_is_grpc_web_direct);
|
||
RUN_TEST(test_is_grpc_web_not_grpc);
|
||
RUN_TEST(test_is_grpc_web_null);
|
||
|
||
/* grpc_decode_message */
|
||
RUN_TEST(test_decode_simple_message);
|
||
RUN_TEST(test_decode_empty_payload);
|
||
RUN_TEST(test_decode_compressed_flag);
|
||
RUN_TEST(test_decode_large_payload);
|
||
RUN_TEST(test_decode_incomplete_header);
|
||
RUN_TEST(test_decode_incomplete_payload);
|
||
RUN_TEST(test_decode_null_buffer);
|
||
RUN_TEST(test_decode_zero_length);
|
||
RUN_TEST(test_decode_null_message);
|
||
|
||
/* grpc_encode_message */
|
||
RUN_TEST(test_encode_simple_message);
|
||
RUN_TEST(test_encode_empty_payload);
|
||
RUN_TEST(test_encode_buffer_too_small);
|
||
RUN_TEST(test_encode_null_params);
|
||
RUN_TEST(test_encode_exact_buffer_size);
|
||
|
||
/* grpc_parse_request */
|
||
RUN_TEST(test_parse_request_basic);
|
||
RUN_TEST(test_parse_request_no_package);
|
||
RUN_TEST(test_parse_request_with_body);
|
||
RUN_TEST(test_parse_request_grpc_web);
|
||
RUN_TEST(test_parse_request_invalid_path_no_slash);
|
||
RUN_TEST(test_parse_request_empty_path);
|
||
RUN_TEST(test_parse_request_null_params);
|
||
RUN_TEST(test_parse_request_metadata);
|
||
|
||
/* grpc_format_response_trailers */
|
||
RUN_TEST(test_format_trailers_ok);
|
||
RUN_TEST(test_format_trailers_error);
|
||
RUN_TEST(test_format_trailers_empty_message);
|
||
RUN_TEST(test_format_trailers_buffer_too_small);
|
||
RUN_TEST(test_format_trailers_null_params);
|
||
|
||
/* grpc_status_to_http */
|
||
RUN_TEST(test_status_to_http_ok);
|
||
RUN_TEST(test_status_to_http_cancelled);
|
||
RUN_TEST(test_status_to_http_unknown);
|
||
RUN_TEST(test_status_to_http_invalid_argument);
|
||
RUN_TEST(test_status_to_http_deadline_exceeded);
|
||
RUN_TEST(test_status_to_http_not_found);
|
||
RUN_TEST(test_status_to_http_already_exists);
|
||
RUN_TEST(test_status_to_http_permission_denied);
|
||
RUN_TEST(test_status_to_http_resource_exhausted);
|
||
RUN_TEST(test_status_to_http_unimplemented);
|
||
RUN_TEST(test_status_to_http_unavailable);
|
||
RUN_TEST(test_status_to_http_unauthenticated);
|
||
RUN_TEST(test_status_to_http_internal);
|
||
RUN_TEST(test_status_to_http_data_loss);
|
||
RUN_TEST(test_status_to_http_all_codes);
|
||
|
||
/* grpc_http_to_status */
|
||
RUN_TEST(test_http_to_status_200);
|
||
RUN_TEST(test_http_to_status_400);
|
||
RUN_TEST(test_http_to_status_401);
|
||
RUN_TEST(test_http_to_status_403);
|
||
RUN_TEST(test_http_to_status_404);
|
||
RUN_TEST(test_http_to_status_409);
|
||
RUN_TEST(test_http_to_status_429);
|
||
RUN_TEST(test_http_to_status_500);
|
||
RUN_TEST(test_http_to_status_501);
|
||
RUN_TEST(test_http_to_status_503);
|
||
RUN_TEST(test_http_to_status_504);
|
||
RUN_TEST(test_http_to_status_2xx_success);
|
||
RUN_TEST(test_http_to_status_4xx_default);
|
||
|
||
/* grpc_status_to_string */
|
||
RUN_TEST(test_status_to_string_all);
|
||
RUN_TEST(test_status_to_string_unknown);
|
||
|
||
/* grpc_message_free */
|
||
RUN_TEST(test_message_free_null);
|
||
RUN_TEST(test_message_free_null_payload);
|
||
RUN_TEST(test_message_free_with_payload);
|
||
|
||
/* grpc_request_free */
|
||
RUN_TEST(test_request_free_null);
|
||
RUN_TEST(test_request_free_empty);
|
||
RUN_TEST(test_request_free_with_message);
|
||
|
||
/* grpc_error_response */
|
||
RUN_TEST(test_error_response_invalid_socket);
|
||
|
||
/* 双向转换一致性 */
|
||
RUN_TEST(test_status_http_roundtrip);
|
||
|
||
/* RPC 模式标记 */
|
||
RUN_TEST(test_rpc_mode_unary);
|
||
RUN_TEST(test_rpc_mode_server_streaming);
|
||
RUN_TEST(test_rpc_mode_client_streaming);
|
||
RUN_TEST(test_rpc_mode_bidirectional);
|
||
|
||
/* 边界条件 */
|
||
RUN_TEST(test_parse_long_path);
|
||
|
||
/* 编码/解码对称性 */
|
||
RUN_TEST(test_encode_decode_symmetry);
|
||
RUN_TEST(test_encode_decode_empty);
|
||
|
||
/* gRPC-Web */
|
||
RUN_TEST(test_grpc_web_flag_set);
|
||
|
||
return UNITY_END();
|
||
}
|