Files
ligent/tests/test_capability.c
T
2026-10-04 13:50:33 +08:00

92 lines
4.3 KiB
C

/* test_capability.c - 能力探测的纯逻辑部分
*
* 探测序列本身 (基线 -> 合并 -> 拆开)要发 HTTP, 属于端到端范畴,
* 由 e2e 第 51 段对着 mock 覆盖: 正常模型 / 不收图片 / 不收 tools /
* 探测失败 (401)/ --no-probe 五种走法都在那儿验.
*
* 这里只测不需要网络的: 名字映射、关闭探测时的语义、缓存查询.
* 其中"关闭探测必须给未测而不是不支持"那条是回归测试 —— 那句话看着像废话,
* 但 CAP_NO 恰好是 0, 而 Agent 是 memset 出来的, 忘了显式初始化就会一声不响地
* 把所有工具摘掉. 这个 bug 真的发生过一次.
*/
#include "capability.h"
#include "llm.h"
#include <stdio.h>
#include <string.h>
static int failures = 0;
static int checks = 0;
#define CHECK(cond, ...) \
do { \
checks++; \
if (!(cond)) { \
failures++; \
printf(" ✗ %s:%d: ", __FILE__, __LINE__); \
printf(__VA_ARGS__); \
printf("\n"); \
} \
} while (0)
int main(void) {
/* CAP_NO 必须是 0 —— Agent 是 memset 出来的, 整个 capability 模块的
* 设计都建立在"零值不能是某个真实结论"之上. 这条断言是那个 bug 的护栏. */
CHECK(CAP_NO == 0, "CAP_NO 应为 0 (零值即'不支持'是事故的根源)");
CHECK(CAP_UNKNOWN != CAP_NO, "未知与不支持必须是两个不同的值");
CHECK(CAP_UNKNOWN != CAP_YES, "未知与支持必须是两个不同的值");
puts("名字映射");
CHECK(strcmp(cap_name(CAP_YES), "支持") == 0, "YES");
CHECK(strcmp(cap_name(CAP_NO), "不支持") == 0, "NO");
CHECK(strcmp(cap_name(CAP_UNKNOWN), "未测") == 0, "UNKNOWN");
/* 任何不是 YES/NO 的值都该落到"未测", 而不是碰巧落到某一个 */
CHECK(strcmp(cap_name(42), "未测") == 0, "越界值应落到未测");
puts("关掉探测: 一律未测, 绝不冒充结论");
/* 用可变数组而不是字面量: LlmConfig 的这些字段是 char * (真要释放的),
* 而 -Wwrite-strings 下 "字面量" 是 const char * —— 直接赋值会被警告. */
char model[] = "some-model";
char base_url[] = "http://127.0.0.1:1/v1";
LlmConfig cfg;
memset(&cfg, 0, sizeof cfg);
cfg.model = model;
cfg.base_url = base_url;
cfg.timeout = 1;
cap_cache_clear();
Capabilities c;
memset(&c, 0xAB, sizeof c); /* 故意填脏, 验证会被完整覆盖 */
int probed = cap_get(&cfg, 0, &c, 0, NULL, 0);
CHECK(probed == 0, "被禁用时不该声称探测过");
CHECK(c.probed == 0, "probed 应为 0");
CHECK(c.vision == CAP_UNKNOWN, "禁用时 vision 应为未测, 实际 %d", c.vision);
CHECK(c.tools == CAP_UNKNOWN, "禁用时 tools 应为未测, 实际 %d", c.tools);
CHECK(c.conclusive == 0, "禁用时不该声称有结论");
/* 关键回归: 禁用时的结果拿去决定工具表, 必须"全都留着" */
CHECK(c.vision != CAP_NO, "禁用时不能表现为不支持 (否则会藏掉 read_image)");
CHECK(c.tools != CAP_NO, "禁用时不能表现为不支持 (否则会摘掉整张工具表)");
puts("缓存查询");
Capabilities got;
memset(&got, 0xAB, sizeof got);
CHECK(cap_cached(&cfg, &got) == 0, "没测过时缓存应未命中");
/* 关掉探测不会写入缓存 */
CHECK(cap_cached(&cfg, NULL) == 0, "禁用不该往缓存里写东西");
cap_cache_clear();
CHECK(cap_cached(&cfg, &got) == 0, "清空之后仍应未命中");
/* 空指针不该崩 */
cap_get(NULL, 0, &c, 0, NULL, 0);
CHECK(c.vision == CAP_UNKNOWN, "cfg 为空也要给未测");
cap_get(&cfg, 0, NULL, 0, NULL, 0);
cap_cache_clear();
CHECK(1, "空指针路径没有崩");
printf("\n%d 项检查, %d 项失败\n", checks, failures);
return failures ? 1 : 0;
}