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;
|
||
}
|