Add metadata validation script and module documentation
- Introduced a new Python script `check_agents_metadata.py` for validating agent metadata, including YAML parsing, rating ranges, and cross-references. - Added usage instructions and exit codes for the script. - Created a new markdown file `模块目录和功能说明.md` to outline the directory structure and functionality of the modules. - Added a text file `说明此文件不可AI修改.txt` to specify that certain files should not be modified by AI, including important information about the `dstalk` framework and its modules.
This commit is contained in:
@@ -1,9 +1,12 @@
|
||||
// ============================================================================
|
||||
// smoke_test.cpp — 插件化架构烟雾测试
|
||||
// ============================================================================
|
||||
// 测试: 核心初始化、插件加载、服务查询、file_io、session 功能
|
||||
// W13.6 (qa-xu 徐磊): 新增 R1-R4 回归保护点,覆盖 W11.7/W12 已修 bug
|
||||
// ============================================================================
|
||||
/*
|
||||
* @file smoke_test.cpp
|
||||
* @brief Basic smoke test: verifies dstalk_init/shutdown cycle, service queries,
|
||||
* file_io, session, null-safety, escape boundaries, tool chain, and
|
||||
* regression protections R1-R4 (W13.6 qa-xu).
|
||||
* 基础冒烟测试:验证 dstalk_init/shutdown 生命周期、服务查询、file_io、session、
|
||||
* 空指针安全、转义边界、工具链调用,以及回归保护 R1-R4 (W13.6 qa-xu)。
|
||||
* Copyright (c) 2026 dstalk contributors. GPLv3.
|
||||
*/
|
||||
|
||||
#include "dstalk/dstalk_host.h"
|
||||
|
||||
@@ -14,6 +17,7 @@
|
||||
#include <string>
|
||||
|
||||
// ---- 回归测试断言 (W13.6 qa-xu) ----
|
||||
// Regression test assertion macro (W13.6 qa-xu): prints [OK]/[FAIL] and tracks failures
|
||||
static int g_regression_failures = 0;
|
||||
#define REGCHECK(cond, msg) do { \
|
||||
if (cond) { \
|
||||
@@ -24,19 +28,26 @@ static int g_regression_failures = 0;
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
// ---- W21.5 mock tool handler (qa-xu) ----
|
||||
// W21.5 mock tool handler (qa-xu): increments call counter and returns mock result JSON
|
||||
// W21.5 模拟工具处理函数 (qa-xu):递增调用计数器并返回模拟结果 JSON
|
||||
static int g_mock_tool_called = 0;
|
||||
static char* mock_tool_handler(const char* /*args_json*/) {
|
||||
g_mock_tool_called++;
|
||||
return dstalk_strdup("{\"mock_result\":\"ok\"}");
|
||||
}
|
||||
|
||||
// 冒烟测试主流程:init → 服务查询 → file_io → session → ai → config,
|
||||
// 然后是扩展测试(空指针安全、转义边界、工具链、session 健壮性),
|
||||
// 接着是回归保护 R1-R3、W21.5 工具调用边界和 R4 生命周期循环。
|
||||
// Smoke test main: init -> service queries -> file_io -> session -> ai -> config,
|
||||
// then extended tests (null-safety, escape, tool chain, session robustness),
|
||||
// then regression protections R1-R3, W21.5 tool-call boundaries, and R4 lifecycle cycles.
|
||||
int main()
|
||||
{
|
||||
const auto dir = std::filesystem::temp_directory_path() / "dstalk-smoke-test";
|
||||
std::filesystem::create_directories(dir);
|
||||
|
||||
// 写一个配置文件用于初始化
|
||||
// 写一个配置文件用于初始化 / Write a config file for initialization
|
||||
const auto config_path = dir / "config.toml";
|
||||
{
|
||||
std::ofstream config(config_path);
|
||||
@@ -47,14 +58,14 @@ int main()
|
||||
<< "model = \"deepseek-v4-pro\"\n";
|
||||
}
|
||||
|
||||
// 初始化主机(会自动扫描 plugins/ 加载插件)
|
||||
// 初始化主机(会自动扫描 plugins/ 加载插件)/ Init host (auto-scans plugins/ to load plugins)
|
||||
if (dstalk_init(config_path.string().c_str()) != 0) {
|
||||
std::cerr << "dstalk_init failed\n";
|
||||
return 1;
|
||||
}
|
||||
std::cout << "[OK] dstalk_init succeeded\n";
|
||||
|
||||
// 验证插件列表
|
||||
// 验证插件列表 / Verify plugin list
|
||||
{
|
||||
char* list_json = nullptr;
|
||||
int ret = dstalk_plugin_list(&list_json);
|
||||
@@ -66,13 +77,13 @@ int main()
|
||||
}
|
||||
}
|
||||
|
||||
// 测试服务查询: file_io
|
||||
// 测试服务查询: file_io / Test service query: file_io
|
||||
auto* file_io = static_cast<const dstalk_file_io_service_t*>(
|
||||
dstalk_service_query("file_io", 1));
|
||||
if (file_io) {
|
||||
std::cout << "[OK] file_io service found\n";
|
||||
|
||||
// 测试写入
|
||||
// 测试写入 / Test write
|
||||
const auto file_path = dir / "sample.txt";
|
||||
constexpr const char* sample_content = "hello dstalk\nquote=\"yes\" tab=\t slash=\\";
|
||||
if (file_io->write(file_path.string().c_str(), sample_content) == 0) {
|
||||
@@ -83,7 +94,7 @@ int main()
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 测试读取
|
||||
// 测试读取 / Test read
|
||||
char* content = nullptr;
|
||||
if (file_io->read(file_path.string().c_str(), &content) == 0 && content) {
|
||||
bool ok = std::strcmp(content, sample_content) == 0;
|
||||
@@ -104,13 +115,13 @@ int main()
|
||||
std::cerr << "[WARN] file_io service not found (plugin may not be in plugins/ dir)\n";
|
||||
}
|
||||
|
||||
// 测试服务查询: session
|
||||
// 测试服务查询: session / Test service query: session
|
||||
auto* session = static_cast<const dstalk_session_service_t*>(
|
||||
dstalk_service_query("session", 1));
|
||||
if (session) {
|
||||
std::cout << "[OK] session service found\n";
|
||||
|
||||
// 测试 session save/load
|
||||
// 测试 session save/load / Test session save/load
|
||||
const auto session_path = dir / "session.jsonl";
|
||||
const auto saved_path = dir / "session-saved.jsonl";
|
||||
constexpr const char* session_content =
|
||||
@@ -137,7 +148,7 @@ int main()
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 验证保存的内容
|
||||
// 验证保存的内容 / Verify saved content
|
||||
if (file_io) {
|
||||
char* saved = nullptr;
|
||||
if (file_io->read(saved_path.string().c_str(), &saved) == 0 && saved) {
|
||||
@@ -153,16 +164,16 @@ int main()
|
||||
}
|
||||
}
|
||||
|
||||
// 测试 token 计数
|
||||
// 测试 token 计数 / Test token count
|
||||
int tokens = session->token_count();
|
||||
std::cout << "[OK] session->token_count: " << tokens << "\n";
|
||||
|
||||
// 测试 history
|
||||
// 测试 history / Test history
|
||||
int count = 0;
|
||||
session->history(&count);
|
||||
std::cout << "[OK] session->history count: " << count << "\n";
|
||||
|
||||
// 测试 clear
|
||||
// 测试 clear / Test clear
|
||||
session->clear();
|
||||
session->history(&count);
|
||||
if (count == 0) {
|
||||
@@ -173,6 +184,7 @@ int main()
|
||||
}
|
||||
|
||||
// 测试服务查询: ai(可能因为没有真实 API key 而失败,但服务应存在)
|
||||
// Test service query: ai (may fail without real API key, but service should exist)
|
||||
const char* ai_provider = dstalk_config_get("ai.provider");
|
||||
if (!ai_provider) ai_provider = "ai.deepseek";
|
||||
auto* ai = static_cast<const dstalk_ai_service_t*>(
|
||||
@@ -183,7 +195,7 @@ int main()
|
||||
std::cerr << "[WARN] ai service not found\n";
|
||||
}
|
||||
|
||||
// 测试服务查询: config
|
||||
// 测试服务查询: config / Test service query: config
|
||||
auto* config_svc = static_cast<const dstalk_config_service_t*>(
|
||||
dstalk_service_query("config", 1));
|
||||
if (config_svc) {
|
||||
@@ -196,21 +208,22 @@ int main()
|
||||
std::cerr << "[WARN] config service not found\n";
|
||||
}
|
||||
|
||||
// 测试 dstalk_config_get(主机级配置 API)
|
||||
// 测试 dstalk_config_get(主机级配置 API)/ Test dstalk_config_get (host-level config API)
|
||||
const char* model = dstalk_config_get("api.model");
|
||||
if (model) {
|
||||
std::cout << "[OK] dstalk_config_get(\"api.model\"): " << model << "\n";
|
||||
}
|
||||
|
||||
// 测试 dstalk_log
|
||||
// 测试 dstalk_log / Test dstalk_log
|
||||
dstalk_log(DSTALK_LOG_INFO, "Smoke test completed successfully");
|
||||
|
||||
// ========================================================================
|
||||
// 扩展测试块 C2: null-safety / 转义边界 / tools 调用链 / session 健壮性
|
||||
// Extended test block C2: null-safety / escape boundaries / tools chain / session robustness
|
||||
// ========================================================================
|
||||
std::cout << "\n--- Extended Smoke Tests (C2) ---\n";
|
||||
|
||||
// 提前查询 tools 服务,供后续测试块使用
|
||||
// 提前查询 tools 服务,供后续测试块使用 / Pre-query tools service for subsequent test blocks
|
||||
auto* tools = static_cast<const dstalk_tools_service_t*>(
|
||||
dstalk_service_query("tools", 1));
|
||||
|
||||
@@ -234,7 +247,7 @@ int main()
|
||||
std::cerr << "[FAIL] file_io->write(nullptr, ...) should return error\n";
|
||||
}
|
||||
|
||||
// read 的 content 参数也为 null
|
||||
// read 的 content 参数也为 null / read's content param also null
|
||||
ret = file_io->read("dummy_path", nullptr);
|
||||
if (ret != 0) {
|
||||
std::cout << "[OK] file_io->read(path, nullptr) returned error (" << ret << ")\n";
|
||||
@@ -242,7 +255,7 @@ int main()
|
||||
std::cerr << "[FAIL] file_io->read(path, nullptr) should return error\n";
|
||||
}
|
||||
|
||||
// write 的 content 参数为 null
|
||||
// write 的 content 参数为 null / write's content param is null
|
||||
ret = file_io->write("dummy_path", nullptr);
|
||||
if (ret != 0) {
|
||||
std::cout << "[OK] file_io->write(path, nullptr) returned error (" << ret << ")\n";
|
||||
@@ -278,6 +291,7 @@ int main()
|
||||
char* result = tools->execute(nullptr, nullptr);
|
||||
if (result) {
|
||||
// 实现返回了错误字符串(如 {"error":"tool name is null"}),未崩溃
|
||||
// Implementation returned error string (e.g. {"error":"tool name is null"}), no crash
|
||||
std::cout << "[OK] tools->execute(nullptr, nullptr) did not crash"
|
||||
<< " (returned: " << result << ")\n";
|
||||
dstalk_free(result);
|
||||
@@ -303,7 +317,7 @@ int main()
|
||||
std::cerr << "[FAIL] config->set(nullptr, nullptr) should return error\n";
|
||||
}
|
||||
|
||||
// set 的 value 为 null
|
||||
// set 的 value 为 null / set's value is null
|
||||
ret = config_svc->set("some.key", nullptr);
|
||||
if (ret != 0) {
|
||||
std::cout << "[OK] config->set(key, nullptr) returned error (" << ret << ")\n";
|
||||
@@ -316,6 +330,8 @@ int main()
|
||||
|
||||
// ---- 2. 转义边界测试 ----
|
||||
// 写入含特殊字符的内容,读回后验证内容一致
|
||||
// ---- Escape boundary tests ----
|
||||
// Write content with special chars, verify round-trip integrity
|
||||
std::cout << "\n[Block] Escape boundary tests\n";
|
||||
|
||||
if (file_io) {
|
||||
@@ -325,6 +341,12 @@ int main()
|
||||
// - 实际反斜杠 (0x5C)
|
||||
// - 实际制表符 (0x09)
|
||||
// - 以及字面上的 \n \" \\ \t 转义序列文本
|
||||
// Build content with various special bytes:
|
||||
// - literal newline (0x0A)
|
||||
// - literal double-quote (0x22)
|
||||
// - literal backslash (0x5C)
|
||||
// - literal tab (0x09)
|
||||
// - plus textual \n \" \\ \t escape sequences
|
||||
constexpr const char* escape_content =
|
||||
"line1\nline2\n"
|
||||
"quote=\"yes\"\n"
|
||||
@@ -363,22 +385,25 @@ int main()
|
||||
|
||||
// ---- 3. Tools 调用链测试 ----
|
||||
// 通过 tools->execute("file_read", ...) 验证内置工具可正确调用 file_io
|
||||
// ---- Tools call chain tests ----
|
||||
// Verify built-in tools correctly call file_io via tools->execute("file_read", ...)
|
||||
std::cout << "\n[Block] Tools call chain tests\n";
|
||||
|
||||
if (tools && file_io) {
|
||||
// 准备测试文件
|
||||
// 准备测试文件 / Prepare test file
|
||||
const auto chain_path = dir / "tool_chain_test.txt";
|
||||
constexpr const char* chain_content = "tools-chain-ok\n";
|
||||
file_io->write(chain_path.string().c_str(), chain_content);
|
||||
|
||||
// 用 generic_string() 获取正斜杠路径,避免 JSON 中反斜杠转义问题
|
||||
// Use generic_string() for forward-slash paths to avoid backslash escaping in JSON
|
||||
std::string generic_path = chain_path.generic_string();
|
||||
std::string args_json = "{\"path\":\"" + generic_path + "\"}";
|
||||
|
||||
char* result = tools->execute("file_read", args_json.c_str());
|
||||
if (result) {
|
||||
std::cout << "[OK] tools->execute(\"file_read\", ...) returned result\n";
|
||||
// 验证返回的 JSON 中包含原始文件内容
|
||||
// 验证返回的 JSON 中包含原始文件内容 / Verify returned JSON contains original file content
|
||||
if (std::strstr(result, "tools-chain-ok")) {
|
||||
std::cout << "[OK] tools->execute chain correctly called file_io\n";
|
||||
} else {
|
||||
@@ -391,7 +416,7 @@ int main()
|
||||
<< " (tool may not be registered)\n";
|
||||
}
|
||||
|
||||
// 额外测试:查询 tools 返回的工具列表
|
||||
// 额外测试:查询 tools 返回的工具列表 / Additional test: query tools list
|
||||
char* tools_json = tools->get_tools_json();
|
||||
if (tools_json) {
|
||||
std::cout << "[OK] tools->get_tools_json() returned: " << tools_json << "\n";
|
||||
@@ -406,14 +431,17 @@ int main()
|
||||
// ---- 4. Session 健壮性测试 ----
|
||||
// session->add(nullptr) 后验证 history 不变
|
||||
// session->clear 后验证 token_count 为 0
|
||||
// ---- Session robustness tests ----
|
||||
// Verify history unchanged after session->add(nullptr)
|
||||
// Verify token_count == 0 after session->clear
|
||||
std::cout << "\n[Block] Session robustness tests\n";
|
||||
|
||||
if (session) {
|
||||
// 记录 add(nullptr) 前的 history 计数
|
||||
// 记录 add(nullptr) 前的 history 计数 / Record history count before add(nullptr)
|
||||
int count_before = 0;
|
||||
session->history(&count_before);
|
||||
|
||||
// 传 null 不应改变 history
|
||||
// 传 null 不应改变 history / Passing null should not change history
|
||||
session->add(nullptr);
|
||||
|
||||
int count_after = 0;
|
||||
@@ -427,7 +455,7 @@ int main()
|
||||
<< count_before << " -> " << count_after << "\n";
|
||||
}
|
||||
|
||||
// clear 后 token_count 应为 0
|
||||
// clear 后 token_count 应为 0 / token_count should be 0 after clear
|
||||
session->clear();
|
||||
int tokens = session->token_count();
|
||||
if (tokens == 0) {
|
||||
@@ -443,6 +471,8 @@ int main()
|
||||
// ========================================================================
|
||||
// W13.6 回归保护点 R1-R3 (qa-xu 徐磊)
|
||||
// 覆盖: W11.7 BUG-2/3/4 + W11.1 Discovery 2/3 + W12.2/W12.3 修复
|
||||
// W13.6 regression protections R1-R3 (qa-xu)
|
||||
// Covers: W11.7 BUG-2/3/4 + W11.1 Discovery 2/3 + W12.2/W12.3 fixes
|
||||
// ========================================================================
|
||||
std::cout << "\n--- Regression Tests (R1-R3: W11.7/W12 bug protection) ---\n";
|
||||
|
||||
@@ -450,6 +480,10 @@ int main()
|
||||
// 回归: W11.1 Discovery 3 (g_max_tokens 死变量 — W12.3 已修, W18.1 彻底移除)
|
||||
// W11.7 BUG-3 (/context 静默 — W12.3 已修)
|
||||
// 验证: trim 能正确裁剪消息数,调用链完整不崩溃
|
||||
// ---- R1: context max_tokens takes effect ----
|
||||
// Regression: W11.1 Discovery 3 (g_max_tokens dead var — fixed W12.3, removed W18.1)
|
||||
// W11.7 BUG-3 (/context silent — fixed W12.3)
|
||||
// Verify: trim reduces message count correctly, full call chain without crash
|
||||
{
|
||||
auto* ctx = static_cast<const dstalk_context_service_t*>(
|
||||
dstalk_service_query("context", 1));
|
||||
@@ -457,6 +491,7 @@ int main()
|
||||
std::cout << "[OK] R1: context service found\n";
|
||||
|
||||
// 构造 5 条消息,每条 ~50 字符 / ~15 token,总计 ~75 token > 50 max
|
||||
// Build 5 messages, each ~50 chars / ~15 tokens, total ~75 tokens > 50 max
|
||||
dstalk_message_t msgs[5];
|
||||
msgs[0] = {"user", "Hello this is message one with enough text to count tokens", nullptr, nullptr};
|
||||
msgs[1] = {"assistant", "Message two also has sufficient length for token counting", nullptr, nullptr};
|
||||
@@ -476,6 +511,7 @@ int main()
|
||||
dstalk_free(out);
|
||||
} else if (ret >= 0) {
|
||||
// 首条消息即超 max_tokens 时 trim 可能返回空,这也是合法路径
|
||||
// When first message exceeds max_tokens, trim may return empty; also valid
|
||||
std::cout << "[WARN] R1: trim returned null output (single msg exceeds max?)\n";
|
||||
}
|
||||
} else {
|
||||
@@ -487,15 +523,19 @@ int main()
|
||||
// 回归: W11.2 Discovery 2 (双 ConfigStore 数据孤岛 — W12.2 已修)
|
||||
// W11.2 Discovery 3 (c_str() 悬垂 — W12.2 已修)
|
||||
// 验证: dstalk_config_set 写入后,dstalk_config_get 和 config_service->get 返回一致值
|
||||
// ---- R2: config dual-store consistency ----
|
||||
// Regression: W11.2 Discovery 2 (dual ConfigStore islands — fixed W12.2)
|
||||
// W11.2 Discovery 3 (c_str() dangling — fixed W12.2)
|
||||
// Verify: after dstalk_config_set write, dstalk_config_get and config_service->get return same value
|
||||
{
|
||||
constexpr const char* k = "__regr_w13_6_dual";
|
||||
constexpr const char* v = "dual_ok_42";
|
||||
|
||||
// 通过 host API 写入
|
||||
// 通过 host API 写入 / Write via host API
|
||||
int set_ret = dstalk_config_set(k, v);
|
||||
REGCHECK(set_ret == 0, "R2: dstalk_config_set returned 0");
|
||||
|
||||
// 通过 host API 读回
|
||||
// 通过 host API 读回 / Read back via host API
|
||||
const char* host_val = dstalk_config_get(k);
|
||||
REGCHECK(host_val && std::strcmp(host_val, v) == 0,
|
||||
"R2: dstalk_config_get matches written value");
|
||||
@@ -503,6 +543,9 @@ int main()
|
||||
// 通过 plugin config 服务读回 — 验证双 store 整合后数据可见性一致
|
||||
// 注: W12.2 双 store 整合尚未部署,跨 store 可见性当前为已知 gap;
|
||||
// 本检查用 WARN 记录现状,待 W12.2 fix 落地后改为 REGCHECK
|
||||
// Read back via plugin config service — verify visibility after dual-store merge
|
||||
// Note: W12.2 dual-store merge not yet deployed; cross-store visibility is a known gap;
|
||||
// this check uses WARN to record status, upgrade to REGCHECK after W12.2 lands
|
||||
auto* cfg_svc = static_cast<const dstalk_config_service_t*>(
|
||||
dstalk_service_query("config", 1));
|
||||
if (cfg_svc) {
|
||||
@@ -520,7 +563,7 @@ int main()
|
||||
std::cerr << "[WARN] R2: config service not found, partial skip\n";
|
||||
}
|
||||
|
||||
// 清理测试 key
|
||||
// 清理测试 key / Clean up test key
|
||||
dstalk_config_set(k, "");
|
||||
}
|
||||
|
||||
@@ -529,6 +572,11 @@ int main()
|
||||
// W11.7 BUG-4 (/file write 落空) 同类的错误路径静默问题
|
||||
// 验证: http post_json 到不可达目标返回错误而不崩溃;
|
||||
// 若 http 服务不可用,回退测 ai 服务错误路径
|
||||
// ---- R3: HTTP / AI service error paths do not crash ----
|
||||
// Regression: W12.1 removed TLS/http_client code (removed rewritten network layer)
|
||||
// W11.7 BUG-4 (/file write miss) similar error-path silent issues
|
||||
// Verify: http post_json to unreachable target returns error without crash;
|
||||
// fall back to ai service error path if http unavailable
|
||||
{
|
||||
auto* http = static_cast<const dstalk_http_service_t*>(
|
||||
dstalk_service_query("http", 1));
|
||||
@@ -536,6 +584,8 @@ int main()
|
||||
std::cout << "[OK] R3: http service found\n";
|
||||
// 向 127.0.0.1:1 发请求 — 端口 1 在 Windows 上几乎肯定无服务监听
|
||||
// 连接拒绝应立即返回错误而非崩溃
|
||||
// Send request to 127.0.0.1:1 — port 1 on Windows almost certainly has no listener
|
||||
// Connection refused should return error immediately, not crash
|
||||
char* body = nullptr;
|
||||
int status = 0;
|
||||
int ret = http->post_json("127.0.0.1", "1", "/",
|
||||
@@ -549,6 +599,7 @@ int main()
|
||||
}
|
||||
} else {
|
||||
// 回退:测 AI 服务 (ai.deepseek) 错误路径
|
||||
// Fallback: test AI service (ai.deepseek) error path
|
||||
auto* ai_svc = static_cast<const dstalk_ai_service_t*>(
|
||||
dstalk_service_query("ai.deepseek", 1));
|
||||
if (ai_svc) {
|
||||
@@ -556,6 +607,7 @@ int main()
|
||||
dstalk_message_t msg = {"user", "hi", nullptr, nullptr};
|
||||
dstalk_chat_result_t r = ai_svc->chat(&msg, 1, "", nullptr);
|
||||
// api_key="test-key" 为无效 key,应返回 error result 而非崩溃
|
||||
// api_key="test-key" is invalid, should return error result, not crash
|
||||
REGCHECK(r.ok == 0 || r.error != nullptr,
|
||||
"R3: ai->chat with invalid key returned error result (no crash)");
|
||||
if (r.content) dstalk_free((void*)r.content);
|
||||
@@ -570,11 +622,14 @@ int main()
|
||||
// ========================================================================
|
||||
// W21.5 Tool Calls 边界测试 (qa-xu 徐磊)
|
||||
// 覆盖: null tool_calls_json / 空数组 "[]" / 有效 tool_calls mock 验证
|
||||
// W21.5 Tool Calls boundary tests (qa-xu)
|
||||
// Covers: null tool_calls_json / empty array "[]" / valid tool_calls mock verification
|
||||
// ========================================================================
|
||||
std::cout << "\n--- Tool Calls Boundary Tests (W21.5) ---\n";
|
||||
|
||||
if (tools && session) {
|
||||
// ---- W21.5-1: null tool_calls_json → 正常处理(不崩溃)----
|
||||
// ---- W21.5-1: null tool_calls_json → handle normally (no crash) ----
|
||||
{
|
||||
int before = 0;
|
||||
session->history(&before);
|
||||
@@ -595,6 +650,7 @@ int main()
|
||||
}
|
||||
|
||||
// ---- W21.5-2: 空 JSON 数组 "[]" → 正常处理(不崩溃)----
|
||||
// ---- W21.5-2: empty JSON array "[]" → handle normally (no crash) ----
|
||||
{
|
||||
int before = 0;
|
||||
session->history(&before);
|
||||
@@ -616,6 +672,7 @@ int main()
|
||||
}
|
||||
|
||||
// ---- W21.5-3: 有效 tool_calls JSON → 验证 execute 被调用 (mock) ----
|
||||
// ---- W21.5-3: valid tool_calls JSON → verify execute is called (mock) ----
|
||||
{
|
||||
g_mock_tool_called = 0;
|
||||
int reg = tools->register_tool(
|
||||
@@ -638,6 +695,7 @@ int main()
|
||||
tools->unregister_tool("__w21_5_mock");
|
||||
|
||||
// 验证已注销的工具返回 error 而非崩溃
|
||||
// Verify unregistered tool returns error, not crash
|
||||
char* err_result = tools->execute("__w21_5_mock", "{}");
|
||||
REGCHECK(err_result && std::strstr(err_result, "error") != nullptr,
|
||||
"W21.5-3d: unregistered tool returns error (not crash)");
|
||||
@@ -645,6 +703,7 @@ int main()
|
||||
}
|
||||
|
||||
// ---- W21.5-4: save/load 往返保留 tool_calls_json ----
|
||||
// ---- W21.5-4: save/load round-trip preserves tool_calls_json ----
|
||||
if (file_io) {
|
||||
const auto rtt_path = dir / "w21_5_tc_rtt.jsonl";
|
||||
int ret = session->save(rtt_path.string().c_str());
|
||||
@@ -664,23 +723,28 @@ int main()
|
||||
std::cerr << "[WARN] W21.5: tools or session service not available\n";
|
||||
}
|
||||
|
||||
// 清理
|
||||
// 清理 / Cleanup
|
||||
dstalk_shutdown();
|
||||
std::cout << "[OK] dstalk_shutdown succeeded\n";
|
||||
|
||||
// ========================================================================
|
||||
// W13.6 回归保护点 R4 (qa-xu 徐磊)
|
||||
// W13.6 regression protection R4 (qa-xu)
|
||||
// ========================================================================
|
||||
|
||||
// ---- R4: 重复 init / shutdown 生命周期 ----
|
||||
// 回归: W9.8 initialize_all 容错 (插件生命周期健壮性)
|
||||
// W11.7 BUG-1 [CRITICAL] build/bin/ 损坏副本 (stale state 残留)
|
||||
// 验证: 多次 dstalk_init/dstalk_shutdown 循环不崩溃,每次 reload 正常
|
||||
// ---- R4: repeat init/shutdown lifecycle ----
|
||||
// Regression: W9.8 initialize_all fault tolerance (plugin lifecycle robustness)
|
||||
// W11.7 BUG-1 [CRITICAL] build/bin/ corrupt copy (stale state residue)
|
||||
// Verify: multiple dstalk_init/dstalk_shutdown cycles without crash, each reload ok
|
||||
{
|
||||
std::cout << "\n[Block] R4: Repeat init/shutdown lifecycle\n";
|
||||
constexpr int cycles = 3;
|
||||
for (int i = 0; i < cycles; i++) {
|
||||
// 每轮重写配置(模拟独立启动)
|
||||
// 每轮重写配置(模拟独立启动)/ Rewrite config each cycle (simulate independent start)
|
||||
{
|
||||
std::ofstream c(config_path);
|
||||
c << "[api]\n"
|
||||
@@ -700,7 +764,7 @@ int main()
|
||||
break;
|
||||
}
|
||||
|
||||
// 快速验证服务可用
|
||||
// 快速验证服务可用 / Quick verify service is available
|
||||
void* q = dstalk_service_query("config", 1);
|
||||
REGCHECK(q != nullptr, "R4: service query ok after init");
|
||||
|
||||
@@ -710,7 +774,7 @@ int main()
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 最终结果 ----
|
||||
// ---- 最终结果 / Final result ----
|
||||
std::cout << "\n";
|
||||
if (g_regression_failures == 0) {
|
||||
std::cout << "=== All smoke tests passed ===\n";
|
||||
|
||||
Reference in New Issue
Block a user