93 lines
4.3 KiB
C
93 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;
|
|
}
|