File size: 8,799 Bytes
ff78003 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 | /**
* adapter-coverage — 4 真适配器各自的 task-required 行为测试。
*
* 单元(无 DB):
* - causal extractCI / extractScore — pooled_estimate JSON 多种约定
*
* 集成(真 DB,自动 skip 当 DATABASE_URL 未设):
* - causal bucket_key 不匹配返空 + reason='bucket_key_no_match'
* - drug target_id-only 走 target → drugs 路径(可附带 2-hop outcomes)
* - internal-experience 默认排除 caller(sourceMetadata.excludedSelf=true)
* - internal-experience include_self=true 时不排除
*/
import test from "node:test";
import assert from "node:assert/strict";
import { extractCI, extractScore, causalAdapter } from "../causal.adapter.ts";
import { drugAdapter } from "../drug.adapter.ts";
import { internalExperienceAdapter } from "../internal-experience.adapter.ts";
import { _resetBudgetCounters } from "../index.ts";
const HAS_DB = Boolean(process.env["DATABASE_URL"]);
test.beforeEach(() => {
_resetBudgetCounters();
});
// ---------- causal pure-function unit tests ----------
test("causal.extractCI — { ci: [lo, hi] } 数组形式", () => {
assert.deepEqual(extractCI({ ci: [0.2, 0.8] }), [0.2, 0.8]);
});
test("causal.extractCI — { ci_low, ci_high } 形式", () => {
assert.deepEqual(extractCI({ ci_low: -1.5, ci_high: 2.5 }), [-1.5, 2.5]);
});
test("causal.extractCI — { lo, hi } 简写形式", () => {
assert.deepEqual(extractCI({ lo: 0, hi: 1 }), [0, 1]);
});
test("causal.extractCI — { lower, upper } 学术形式", () => {
assert.deepEqual(extractCI({ lower: 0.1, upper: 0.9 }), [0.1, 0.9]);
});
test("causal.extractCI — 缺失 / 非对象 → null,不假装", () => {
assert.equal(extractCI(null), null);
assert.equal(extractCI(undefined), null);
assert.equal(extractCI({}), null);
assert.equal(extractCI({ ci: [1] }), null); // 长度不对
assert.equal(extractCI({ ci: ["a", "b"] }), null); // 类型不对
assert.equal(extractCI("not_an_object"), null);
});
test("causal.extractScore — value > estimate > mean > score 优先级", () => {
assert.equal(extractScore({ value: 0.42, estimate: 0.99 }), 0.42);
assert.equal(extractScore({ estimate: 0.7 }), 0.7);
assert.equal(extractScore({ mean: -0.1 }), -0.1);
assert.equal(extractScore({ score: 3.14 }), 3.14);
assert.equal(extractScore({}), null);
assert.equal(extractScore({ value: "not-a-number" }), null);
assert.equal(extractScore({ value: NaN }), null);
});
// ---------- DB-backed integration tests (skip when no DATABASE_URL) ----------
test("causal — bucket_key 不匹配返空 + reason='bucket_key_no_match'", { skip: !HAS_DB }, async () => {
// 用一个肯定不存在的 jsonb 做 key
const r = await causalAdapter.query({
kind: "causal_network",
capabilityId: "cap_test_causal_bk",
params: { bucket_key: { __test_marker__: "this_key_does_not_exist_xyzzy_42" } },
});
assert.equal(r.items.length, 0);
assert.equal(
(r.sourceMetadata as Record<string, unknown>)["reason"],
"bucket_key_no_match",
);
});
test("causal — score_min/max 入 SQL 不报错(空结果也 OK)", { skip: !HAS_DB }, async () => {
const r = await causalAdapter.query({
kind: "causal_network",
capabilityId: "cap_test_causal_score",
params: { score_min: -1e9, score_max: 1e9 },
limit: 5,
});
// 不 throw 即通过(items 数随真 DB 状态)
assert.ok(Array.isArray(r.items));
assert.equal(r.kind, "causal_network");
});
test("drug — target_id-only 路径不 throw(走 target → drugs/outcomes)", { skip: !HAS_DB }, async () => {
// 任意不存在的 target_id 也应该走完路径返空,不应该 throw
const r = await drugAdapter.query({
kind: "drug_network",
capabilityId: "cap_test_drug_target",
params: { target_id: "TARGET:nonexistent_xyzzy" },
limit: 5,
});
assert.ok(Array.isArray(r.items));
assert.equal(r.kind, "drug_network");
assert.equal(
((r.sourceMetadata as Record<string, unknown>)["filters"] as Record<string, unknown>)[
"target_id"
],
"TARGET:nonexistent_xyzzy",
);
});
test("internal-experience — 默认 sourceMetadata.excludedSelf=true", { skip: !HAS_DB }, async () => {
const r = await internalExperienceAdapter.query({
kind: "internal_experience",
capabilityId: "cap_caller_self_test",
params: {}, // no include_self
limit: 5,
});
const meta = r.sourceMetadata as Record<string, unknown>;
assert.equal(meta["excludedSelf"], true);
assert.equal(meta["callerCapability"], "cap_caller_self_test");
});
test("internal-experience — include_self=true 时 excludedSelf=false", { skip: !HAS_DB }, async () => {
const r = await internalExperienceAdapter.query({
kind: "internal_experience",
capabilityId: "cap_caller_self_test_2",
params: { include_self: true },
limit: 5,
});
const meta = r.sourceMetadata as Record<string, unknown>;
assert.equal(meta["excludedSelf"], false);
});
test(
"internal-experience — 不存在的 capability_id 参数返空 items 但 sourceMetadata 完整(空表语义)",
{ skip: !HAS_DB },
async () => {
// capability_data_foundation 在 B2 阶段 B7 才写入,所以本任务中绝大多数
// capability_id 都查不到行。验证 adapter 在"空表"语义下不假装有数据,
// sourceMetadata 仍如实返回 caller / kindFilter / excludedSelf。
const bogus = `cap_does_not_exist_${Date.now()}_${Math.random().toString(36).slice(2)}`;
const r = await internalExperienceAdapter.query({
kind: "internal_experience",
capabilityId: "cap_caller_empty_table",
params: { capability_id: bogus },
limit: 5,
});
assert.deepEqual(r.items, []);
assert.equal(r.cursor, null);
const meta = r.sourceMetadata as Record<string, unknown>;
assert.equal(meta["callerCapability"], "cap_caller_empty_table");
assert.equal(meta["filteredCapability"], bogus);
// excludedSelf 默认 true,因为 caller != filteredCapability 时也保留语义
assert.equal(meta["excludedSelf"], true);
},
);
test(
"internal-experience — kind 过滤进 SQL where 子句(不存在的 kind 返空)",
{ skip: !HAS_DB },
async () => {
const r = await internalExperienceAdapter.query({
kind: "internal_experience",
capabilityId: "cap_caller_kind_filter",
params: { kind: `__nonexistent_kind_xyzzy_${Date.now()}__` },
limit: 5,
});
assert.deepEqual(r.items, []);
const meta = r.sourceMetadata as Record<string, unknown>;
// adapter 把 kindFilter 透传到 sourceMetadata,证明 where 子句真的进了 SQL
assert.ok(
typeof meta["kindFilter"] === "string" &&
(meta["kindFilter"] as string).startsWith("__nonexistent_kind_"),
`expected kindFilter passthrough in sourceMetadata, got ${JSON.stringify(meta)}`,
);
},
);
test(
"internal-experience — invalid since 字符串:sinceFilterApplied=false(反 metadata 撒谎)",
{ skip: !HAS_DB },
async () => {
// architect caveat #6 fix:caller 传了 since 但解析失败时,sourceMetadata
// 必须老实告诉 caller "我没用",绝不能 echo 个 sinceFilter 假装应用了。
const r = await internalExperienceAdapter.query({
kind: "internal_experience",
capabilityId: "cap_caller_invalid_since",
params: { since: "this-is-not-a-date" },
limit: 5,
});
const meta = r.sourceMetadata as Record<string, unknown>;
assert.equal(meta["sinceProvided"], "this-is-not-a-date");
assert.equal(
meta["sinceFilterApplied"],
false,
"invalid since 必须 sinceFilterApplied=false,否则 metadata 撒谎",
);
},
);
test(
"internal-experience — 合法 since 字符串:sinceFilterApplied=true",
{ skip: !HAS_DB },
async () => {
const r = await internalExperienceAdapter.query({
kind: "internal_experience",
capabilityId: "cap_caller_valid_since",
params: { since: "2020-01-01T00:00:00Z" },
limit: 5,
});
const meta = r.sourceMetadata as Record<string, unknown>;
assert.equal(meta["sinceProvided"], "2020-01-01T00:00:00Z");
assert.equal(meta["sinceFilterApplied"], true);
},
);
test(
"drug — drug_id-only 路径不 throw(走 drug → targets/outcomes/trials)",
{ skip: !HAS_DB },
async () => {
// 任意不存在的 drug_id 也应该走完路径返空,不应该 throw
const r = await drugAdapter.query({
kind: "drug_network",
capabilityId: "cap_test_drug_only",
params: { drug_id: "DRUG:nonexistent_xyzzy" },
limit: 5,
});
assert.ok(Array.isArray(r.items));
assert.equal(r.kind, "drug_network");
assert.equal(
((r.sourceMetadata as Record<string, unknown>)["filters"] as Record<string, unknown>)[
"drug_id"
],
"DRUG:nonexistent_xyzzy",
);
},
);
|