1010 * 类名**字面量**的镜像一致性另由 `motion/env.test.ts` 的 V6 对拍守住(两者分工不重叠)。
1111 * @ai -context 副作用:无(纯函数用例,不读 store、不发请求、不写盘)。
1212 */
13+ import { dirname , join } from "node:path" ;
14+ import { fileURLToPath } from "node:url" ;
1315import { describe , expect , it } from "vitest" ;
16+ import { walkSources , relOf , readLines } from "../ui/primitives/sliceScan" ;
1417import { lowConfidenceClass } from "./lowConfidence" ;
1518
1619describe ( "lowConfidenceClass" , ( ) => {
@@ -24,3 +27,60 @@ describe("lowConfidenceClass", () => {
2427 expect ( lowConfidenceClass ( undefined ) ) . toBe ( "" ) ;
2528 } ) ;
2629} ) ;
30+
31+ /**
32+ * C4.2 的**加宽判据**:`lowConfidenceClass` 的**生产调用点 ≥2 个文件**(批 7 T20 落地第 2 点)。
33+ *
34+ * @ai -context Why 这条判据必须存在:C4.2 逐字「留 + **加宽到 ≥2 生产调用点**」是本批对 R12.4
35+ * 环境层第 ③ 件的**唯一可机器核对的口径**;若只数「有没有人 import」而不数**调用形态**,
36+ * 把调用点删掉只留 import 也能绿(真空判据)。故本判据**两形态并列**(import 形态 ∧ 调用形态),
37+ * 并要求两形态命中的**文件集逐字相等** —— 只删一边必红。
38+ * @ai -context 域口径:`app/src/**` 的 `.ts`/`.tsx` **减** `*.test.*` **减**定义模块自身
39+ * (`utils/lowConfidence.ts`)—— 定义不是调用点;`ui/primitives/**` **不排除**(它是原语层,
40+ * 若真在那里调用,那也是一个真实调用点)。
41+ * @ai -context 读法:经 `sliceScan.readLines` 的 `stripped`(**先剥注释、抹为等长空白**)⇒
42+ * 文件头注释里提到本函数名不产生幻影调用点(B9 第 9 条:整文件扫描器最常被注释误伤)。
43+ * @ai -context 正控 / 负控:正控 = `session-detail/SessionRawView.tsx`(批 6 T28 的**第 1 点**,
44+ * 它掉了说明扫描器坏了,而不是"第 2 点没做");负控 = 一个不存在于全仓的标识符(必须 0 命中)
45+ * ⇒ 两个方向都能红,判据非空转。
46+ */
47+ describe ( "C4.2 · lowConfidenceClass 的生产调用点 ≥2(多形态并列 + 正控/负控)" , ( ) => {
48+ const SRC = join ( dirname ( fileURLToPath ( import . meta. url ) ) , ".." ) ;
49+ const SELF = "utils/lowConfidence.ts" ;
50+ const DEF_FILE = "utils/lowConfidence.ts" ;
51+ /** 两形态:① import 说明符形态(`from ".../lowConfidence"`)② 调用形态 `lowConfidenceClass(` */
52+ const IMPORT_FORM = / f r o m \s + [ " ' ] [ ^ " ' ] * \/ l o w C o n f i d e n c e [ " ' ] / g;
53+ const CALL_FORM = / \b l o w C o n f i d e n c e C l a s s \s * \( / g;
54+
55+ /** 命中文件集(相对 `app/src` 的正斜杠路径;**先剥注释**) */
56+ const filesMatching = ( re : RegExp ) : string [ ] => {
57+ const out : string [ ] = [ ] ;
58+ for ( const abs of walkSources ( SRC ) ) {
59+ const rel = relOf ( SRC , abs ) ;
60+ if ( / \. t e s t \. t s x ? $ / . test ( rel ) || rel === DEF_FILE || rel === SELF ) continue ;
61+ const text = readLines ( abs ) . stripped . join ( "\n" ) ;
62+ if ( new RegExp ( re . source , "g" ) . test ( text ) ) out . push ( rel ) ;
63+ }
64+ return out . sort ( ) ;
65+ } ;
66+
67+ it ( "① 调用形态命中 ≥2 个生产文件,且 import 形态命中的文件集与它逐字相等" , ( ) => {
68+ const byCall = filesMatching ( CALL_FORM ) ;
69+ const byImport = filesMatching ( IMPORT_FORM ) ;
70+ expect (
71+ byCall . length ,
72+ `C4.2「低置信 ≥2 生产调用点」未达成:调用形态只命中 ${ byCall . length } 个文件(${ byCall . join ( " · " ) } )` ,
73+ ) . toBeGreaterThanOrEqual ( 2 ) ;
74+ expect ( byImport , "只删 import 或只删调用都会在这里红(两形态的文件集必须逐字相等)" ) . toEqual ( byCall ) ;
75+ } ) ;
76+
77+ it ( "② 正控(第 1 点仍在)· 负控(不存在的标识符 0 命中)· 定义模块与测试不计入" , ( ) => {
78+ const byCall = filesMatching ( CALL_FORM ) ;
79+ expect ( byCall , "批 6 T28 的第 1 个调用点消失了 ⇒ 先查扫描器,再查代码" ) . toContain (
80+ "components/session-detail/SessionRawView.tsx" ,
81+ ) ;
82+ expect ( byCall , "批 7 T20 的第 2 个调用点不在命中集里" ) . toContain ( "views/session/SessionTriTrackView.tsx" ) ;
83+ expect ( filesMatching ( / \b z z z N o S u c h S y m b o l Z z z \s * \( / ) , "负控 0 命中 ⇒ 扫描器不是恒真" ) . toEqual ( [ ] ) ;
84+ expect ( byCall , "定义模块自身不算调用点" ) . not . toContain ( DEF_FILE ) ;
85+ } ) ;
86+ } ) ;
0 commit comments