@aster-cloud/aster-lang-ts 1.0.28 → 1.0.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/scripts/generate_error_codes.js +49 -6
- package/dist/scripts/generate_error_codes.js.map +1 -1
- package/dist/src/ai/providers/openai.d.ts +10 -0
- package/dist/src/ai/providers/openai.d.ts.map +1 -1
- package/dist/src/ai/providers/openai.js +4 -0
- package/dist/src/ai/providers/openai.js.map +1 -1
- package/dist/src/config/lexicons/regex-guard.d.ts.map +1 -1
- package/dist/src/config/lexicons/regex-guard.js +128 -0
- package/dist/src/config/lexicons/regex-guard.js.map +1 -1
- package/dist/src/core/interpreter.d.ts.map +1 -1
- package/dist/src/core/interpreter.js +221 -27
- package/dist/src/core/interpreter.js.map +1 -1
- package/dist/src/cst/cst_builder.js +0 -14
- package/dist/src/cst/cst_builder.js.map +1 -1
- package/dist/src/cst/cst_printer.d.ts.map +1 -1
- package/dist/src/cst/cst_printer.js +20 -3
- package/dist/src/cst/cst_printer.js.map +1 -1
- package/dist/src/diagnostics/error_codes.d.ts +3 -3
- package/dist/src/diagnostics/error_codes.d.ts.map +1 -1
- package/dist/src/diagnostics/error_codes.js +509 -78
- package/dist/src/diagnostics/error_codes.js.map +1 -1
- package/dist/src/formatter.d.ts +7 -0
- package/dist/src/formatter.d.ts.map +1 -1
- package/dist/src/formatter.js +175 -5
- package/dist/src/formatter.js.map +1 -1
- package/dist/src/frontend/canonicalizer.d.ts.map +1 -1
- package/dist/src/frontend/canonicalizer.js +47 -6
- package/dist/src/frontend/canonicalizer.js.map +1 -1
- package/dist/src/frontend/lexer.d.ts +2 -1
- package/dist/src/frontend/lexer.d.ts.map +1 -1
- package/dist/src/frontend/lexer.js +10 -3
- package/dist/src/frontend/lexer.js.map +1 -1
- package/dist/src/frontend/transformers.d.ts.map +1 -1
- package/dist/src/frontend/transformers.js +50 -3
- package/dist/src/frontend/transformers.js.map +1 -1
- package/dist/src/lower_to_core.js +101 -4
- package/dist/src/lower_to_core.js.map +1 -1
- package/dist/src/lsp/canonicalize-options.d.ts +26 -0
- package/dist/src/lsp/canonicalize-options.d.ts.map +1 -0
- package/dist/src/lsp/canonicalize-options.js +14 -0
- package/dist/src/lsp/canonicalize-options.js.map +1 -0
- package/dist/src/lsp/completion.d.ts.map +1 -1
- package/dist/src/lsp/completion.js +22 -4
- package/dist/src/lsp/completion.js.map +1 -1
- package/dist/src/lsp/server.js +29 -1
- package/dist/src/lsp/server.js.map +1 -1
- package/dist/src/lsp/tenant-init.d.ts +55 -0
- package/dist/src/lsp/tenant-init.d.ts.map +1 -0
- package/dist/src/lsp/tenant-init.js +88 -0
- package/dist/src/lsp/tenant-init.js.map +1 -0
- package/dist/src/mapping/candidate-generator.d.ts +84 -0
- package/dist/src/mapping/candidate-generator.d.ts.map +1 -0
- package/dist/src/mapping/candidate-generator.js +114 -0
- package/dist/src/mapping/candidate-generator.js.map +1 -0
- package/dist/src/mapping/entity-proposer.d.ts +85 -0
- package/dist/src/mapping/entity-proposer.d.ts.map +1 -0
- package/dist/src/mapping/entity-proposer.js +162 -0
- package/dist/src/mapping/entity-proposer.js.map +1 -0
- package/dist/src/mapping/mapping-ir.d.ts +85 -0
- package/dist/src/mapping/mapping-ir.d.ts.map +1 -0
- package/dist/src/mapping/mapping-ir.js +195 -0
- package/dist/src/mapping/mapping-ir.js.map +1 -0
- package/dist/src/mapping/proof-ir.d.ts +125 -0
- package/dist/src/mapping/proof-ir.d.ts.map +1 -0
- package/dist/src/mapping/proof-ir.js +48 -0
- package/dist/src/mapping/proof-ir.js.map +1 -0
- package/dist/src/mapping/quantity-ir.d.ts +93 -0
- package/dist/src/mapping/quantity-ir.d.ts.map +1 -0
- package/dist/src/mapping/quantity-ir.js +151 -0
- package/dist/src/mapping/quantity-ir.js.map +1 -0
- package/dist/src/mapping/source-ir.d.ts +82 -0
- package/dist/src/mapping/source-ir.d.ts.map +1 -0
- package/dist/src/mapping/source-ir.js +231 -0
- package/dist/src/mapping/source-ir.js.map +1 -0
- package/dist/src/nodeid/change-impact.d.ts +77 -0
- package/dist/src/nodeid/change-impact.d.ts.map +1 -0
- package/dist/src/nodeid/change-impact.js +132 -0
- package/dist/src/nodeid/change-impact.js.map +1 -0
- package/dist/src/nodeid/node-id-map.d.ts +69 -0
- package/dist/src/nodeid/node-id-map.d.ts.map +1 -0
- package/dist/src/nodeid/node-id-map.js +193 -0
- package/dist/src/nodeid/node-id-map.js.map +1 -0
- package/dist/src/parser/decl-parser.d.ts.map +1 -1
- package/dist/src/parser/decl-parser.js +10 -4
- package/dist/src/parser/decl-parser.js.map +1 -1
- package/dist/src/parser/expr-stmt-parser.d.ts.map +1 -1
- package/dist/src/parser/expr-stmt-parser.js +36 -24
- package/dist/src/parser/expr-stmt-parser.js.map +1 -1
- package/dist/src/parser/span-utils.d.ts +25 -0
- package/dist/src/parser/span-utils.d.ts.map +1 -1
- package/dist/src/parser/span-utils.js +43 -0
- package/dist/src/parser/span-utils.js.map +1 -1
- package/dist/src/parser/type-parser.d.ts.map +1 -1
- package/dist/src/parser/type-parser.js +21 -0
- package/dist/src/parser/type-parser.js.map +1 -1
- package/dist/src/typecheck/expression.d.ts.map +1 -1
- package/dist/src/typecheck/expression.js +26 -0
- package/dist/src/typecheck/expression.js.map +1 -1
- package/dist/test/fuzz/fuzz.test.js +62 -2
- package/dist/test/fuzz/fuzz.test.js.map +1 -1
- package/dist/test/unit/call-arity.test.d.ts +2 -0
- package/dist/test/unit/call-arity.test.d.ts.map +1 -0
- package/dist/test/unit/call-arity.test.js +71 -0
- package/dist/test/unit/call-arity.test.js.map +1 -0
- package/dist/test/unit/canonicalizer/canonicalizer.test.js +42 -4
- package/dist/test/unit/canonicalizer/canonicalizer.test.js.map +1 -1
- package/dist/test/unit/canonicalizer/line-preservation.test.d.ts +2 -0
- package/dist/test/unit/canonicalizer/line-preservation.test.d.ts.map +1 -0
- package/dist/test/unit/canonicalizer/line-preservation.test.js +71 -0
- package/dist/test/unit/canonicalizer/line-preservation.test.js.map +1 -0
- package/dist/test/unit/canonicalizer/marker-restore-linear.test.d.ts +2 -0
- package/dist/test/unit/canonicalizer/marker-restore-linear.test.d.ts.map +1 -0
- package/dist/test/unit/canonicalizer/marker-restore-linear.test.js +98 -0
- package/dist/test/unit/canonicalizer/marker-restore-linear.test.js.map +1 -0
- package/dist/test/unit/canonicalizer/possessive-anchor.test.d.ts +2 -0
- package/dist/test/unit/canonicalizer/possessive-anchor.test.d.ts.map +1 -0
- package/dist/test/unit/canonicalizer/possessive-anchor.test.js +101 -0
- package/dist/test/unit/canonicalizer/possessive-anchor.test.js.map +1 -0
- package/dist/test/unit/formatter-redos.test.d.ts +2 -0
- package/dist/test/unit/formatter-redos.test.d.ts.map +1 -0
- package/dist/test/unit/formatter-redos.test.js +196 -0
- package/dist/test/unit/formatter-redos.test.js.map +1 -0
- package/dist/test/unit/interpreter/interpreter.test.js +7 -2
- package/dist/test/unit/interpreter/interpreter.test.js.map +1 -1
- package/dist/test/unit/lexer/bom.test.d.ts +2 -0
- package/dist/test/unit/lexer/bom.test.d.ts.map +1 -0
- package/dist/test/unit/lexer/bom.test.js +49 -0
- package/dist/test/unit/lexer/bom.test.js.map +1 -0
- package/dist/test/unit/list-get-bounds.test.d.ts +2 -0
- package/dist/test/unit/list-get-bounds.test.d.ts.map +1 -0
- package/dist/test/unit/list-get-bounds.test.js +66 -0
- package/dist/test/unit/list-get-bounds.test.js.map +1 -0
- package/dist/test/unit/long-bigint-exact.test.d.ts +2 -0
- package/dist/test/unit/long-bigint-exact.test.d.ts.map +1 -0
- package/dist/test/unit/long-bigint-exact.test.js +79 -0
- package/dist/test/unit/long-bigint-exact.test.js.map +1 -0
- package/dist/test/unit/lsp/completion-lexicon.test.d.ts +2 -0
- package/dist/test/unit/lsp/completion-lexicon.test.d.ts.map +1 -0
- package/dist/test/unit/lsp/completion-lexicon.test.js +83 -0
- package/dist/test/unit/lsp/completion-lexicon.test.js.map +1 -0
- package/dist/test/unit/lsp/tenant-vocabulary.test.d.ts +2 -0
- package/dist/test/unit/lsp/tenant-vocabulary.test.d.ts.map +1 -0
- package/dist/test/unit/lsp/tenant-vocabulary.test.js +187 -0
- package/dist/test/unit/lsp/tenant-vocabulary.test.js.map +1 -0
- package/dist/test/unit/map-builtins.test.js +163 -3
- package/dist/test/unit/map-builtins.test.js.map +1 -1
- package/dist/test/unit/map-structural-equality.test.d.ts +2 -0
- package/dist/test/unit/map-structural-equality.test.d.ts.map +1 -0
- package/dist/test/unit/map-structural-equality.test.js +64 -0
- package/dist/test/unit/map-structural-equality.test.js.map +1 -0
- package/dist/test/unit/map-type-guard.test.js +8 -5
- package/dist/test/unit/map-type-guard.test.js.map +1 -1
- package/dist/test/unit/mapping/candidate-generator.test.d.ts +2 -0
- package/dist/test/unit/mapping/candidate-generator.test.d.ts.map +1 -0
- package/dist/test/unit/mapping/candidate-generator.test.js +177 -0
- package/dist/test/unit/mapping/candidate-generator.test.js.map +1 -0
- package/dist/test/unit/mapping/entity-proposer.test.d.ts +2 -0
- package/dist/test/unit/mapping/entity-proposer.test.d.ts.map +1 -0
- package/dist/test/unit/mapping/entity-proposer.test.js +168 -0
- package/dist/test/unit/mapping/entity-proposer.test.js.map +1 -0
- package/dist/test/unit/mapping/mapping-ir.test.d.ts +2 -0
- package/dist/test/unit/mapping/mapping-ir.test.d.ts.map +1 -0
- package/dist/test/unit/mapping/mapping-ir.test.js +144 -0
- package/dist/test/unit/mapping/mapping-ir.test.js.map +1 -0
- package/dist/test/unit/mapping/proof-ir.test.d.ts +2 -0
- package/dist/test/unit/mapping/proof-ir.test.d.ts.map +1 -0
- package/dist/test/unit/mapping/proof-ir.test.js +119 -0
- package/dist/test/unit/mapping/proof-ir.test.js.map +1 -0
- package/dist/test/unit/mapping/quantity-ir.test.d.ts +2 -0
- package/dist/test/unit/mapping/quantity-ir.test.d.ts.map +1 -0
- package/dist/test/unit/mapping/quantity-ir.test.js +144 -0
- package/dist/test/unit/mapping/quantity-ir.test.js.map +1 -0
- package/dist/test/unit/mapping/quantity-redos-property.test.d.ts +2 -0
- package/dist/test/unit/mapping/quantity-redos-property.test.d.ts.map +1 -0
- package/dist/test/unit/mapping/quantity-redos-property.test.js +182 -0
- package/dist/test/unit/mapping/quantity-redos-property.test.js.map +1 -0
- package/dist/test/unit/mapping/redos-budget.test.d.ts +2 -0
- package/dist/test/unit/mapping/redos-budget.test.d.ts.map +1 -0
- package/dist/test/unit/mapping/redos-budget.test.js +168 -0
- package/dist/test/unit/mapping/redos-budget.test.js.map +1 -0
- package/dist/test/unit/mapping/source-ir.test.d.ts +2 -0
- package/dist/test/unit/mapping/source-ir.test.d.ts.map +1 -0
- package/dist/test/unit/mapping/source-ir.test.js +113 -0
- package/dist/test/unit/mapping/source-ir.test.js.map +1 -0
- package/dist/test/unit/nodeid/change-impact.test.d.ts +2 -0
- package/dist/test/unit/nodeid/change-impact.test.d.ts.map +1 -0
- package/dist/test/unit/nodeid/change-impact.test.js +152 -0
- package/dist/test/unit/nodeid/change-impact.test.js.map +1 -0
- package/dist/test/unit/nodeid/rename-declaration.test.d.ts +2 -0
- package/dist/test/unit/nodeid/rename-declaration.test.d.ts.map +1 -0
- package/dist/test/unit/nodeid/rename-declaration.test.js +75 -0
- package/dist/test/unit/nodeid/rename-declaration.test.js.map +1 -0
- package/dist/test/unit/parse-type-recursion.test.d.ts +2 -0
- package/dist/test/unit/parse-type-recursion.test.d.ts.map +1 -0
- package/dist/test/unit/parse-type-recursion.test.js +58 -0
- package/dist/test/unit/parse-type-recursion.test.js.map +1 -0
- package/dist/test/unit/regex-guard.test.js +64 -0
- package/dist/test/unit/regex-guard.test.js.map +1 -1
- package/dist/test/unit/strict-int-coercion.test.d.ts +2 -0
- package/dist/test/unit/strict-int-coercion.test.d.ts.map +1 -0
- package/dist/test/unit/strict-int-coercion.test.js +96 -0
- package/dist/test/unit/strict-int-coercion.test.js.map +1 -0
- package/dist/test/unit/ufcs-receiver.test.d.ts +2 -0
- package/dist/test/unit/ufcs-receiver.test.d.ts.map +1 -0
- package/dist/test/unit/ufcs-receiver.test.js +75 -0
- package/dist/test/unit/ufcs-receiver.test.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { describe, test } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { lex } from '../../../src/frontend/lexer.js';
|
|
4
|
+
/**
|
|
5
|
+
* UTF-8 BOM(U+FEFF)处理(issue core#158)。
|
|
6
|
+
*
|
|
7
|
+
* <h2>为什么 TS 侧也要改</h2>
|
|
8
|
+
*
|
|
9
|
+
* TS 一直在剥 BOM,Java 侧从未剥过——这本身是双引擎分叉。但修 Java 时实测发现,
|
|
10
|
+
* TS 的剥离**会递增列号**(`i++; col++`),于是带 BOM 的文件里所有 token 的
|
|
11
|
+
* 列号整体 +1。把 Java 修成「不进位」之后,两边反而在另一个方向上不一致了。
|
|
12
|
+
*
|
|
13
|
+
* 取「不进位」为准:BOM 是**编码标记**,不是源码内容,不该占一列。
|
|
14
|
+
* 列号偏移会带歪诊断位置、span 与 IDE 高亮——而 ProofIR 的 span 是签字锚定
|
|
15
|
+
* 的一部分,不是可以随手偏一列的东西。
|
|
16
|
+
*/
|
|
17
|
+
describe('UTF-8 BOM', () => {
|
|
18
|
+
const BOM = '\uFEFF';
|
|
19
|
+
const SRC = 'Module demo.\n';
|
|
20
|
+
test('BOM 不得让首 token 的列号偏移', () => {
|
|
21
|
+
const withBom = lex(BOM + SRC);
|
|
22
|
+
const without = lex(SRC);
|
|
23
|
+
assert.ok(without.length > 0, '前置失败:没有产出任何 token');
|
|
24
|
+
assert.equal(withBom[0].start.col, without[0].start.col, '带 BOM 时首 token 列号发生了偏移(BOM 是编码标记,不该占一列)');
|
|
25
|
+
assert.equal(withBom[0].end.col, without[0].end.col);
|
|
26
|
+
});
|
|
27
|
+
test('带 BOM 与不带 BOM 产出逐个相同的 token(含位置)', () => {
|
|
28
|
+
const shape = (ts) => ts.map((t) => `${t.kind}:${t.value}@${t.start.line}:${t.start.col}`);
|
|
29
|
+
const withBom = shape(lex(BOM + SRC));
|
|
30
|
+
const without = shape(lex(SRC));
|
|
31
|
+
// 前置:夹具确实产出了 token,否则相等断言在空表上恒真。
|
|
32
|
+
assert.ok(without.length >= 3, `前置失败:只产出 ${without.length} 个 token`);
|
|
33
|
+
assert.deepEqual(withBom, without);
|
|
34
|
+
});
|
|
35
|
+
test('★正文中的 U+FEFF 原样保留(只剥首字符)', () => {
|
|
36
|
+
// 反向守卫:别把剥离写成全文替换。字符串字面量里的 U+FEFF 是**用户数据**,
|
|
37
|
+
// 剥掉就是篡改内容。
|
|
38
|
+
const src = `Module demo.\n\nDefine rule r:\n Return "a${BOM}b".\n`;
|
|
39
|
+
const kept = lex(src).some((t) => typeof t.value === 'string' && t.value.includes(BOM));
|
|
40
|
+
assert.ok(kept, '字符串字面量内的 U+FEFF 属用户数据,不得被剥离');
|
|
41
|
+
});
|
|
42
|
+
test('无 BOM 的源码不受影响(剥离不得误吃首字符)', () => {
|
|
43
|
+
const tokens = lex(SRC);
|
|
44
|
+
assert.ok(tokens.length > 0);
|
|
45
|
+
assert.equal(tokens[0].value, 'Module');
|
|
46
|
+
assert.equal(tokens[0].start.col, 1);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
//# sourceMappingURL=bom.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bom.test.js","sourceRoot":"","sources":["../../../../test/unit/lexer/bom.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,GAAG,EAAE,MAAM,gCAAgC,CAAC;AAErD;;;;;;;;;;;;GAYG;AACH,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;IACzB,MAAM,GAAG,GAAG,QAAQ,CAAC;IACrB,MAAM,GAAG,GAAG,gBAAgB,CAAC;IAE7B,IAAI,CAAC,sBAAsB,EAAE,GAAG,EAAE;QAChC,MAAM,OAAO,GAAG,GAAG,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC;QAC/B,MAAM,OAAO,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;QAEzB,MAAM,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,mBAAmB,CAAC,CAAC;QACnD,MAAM,CAAC,KAAK,CACV,OAAO,CAAC,CAAC,CAAE,CAAC,KAAK,CAAC,GAAG,EACrB,OAAO,CAAC,CAAC,CAAE,CAAC,KAAK,CAAC,GAAG,EACrB,yCAAyC,CAC1C,CAAC;QACF,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC5C,MAAM,KAAK,GAAG,CAAC,EACuD,EAAE,EAAE,CACxE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;QAEvE,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,CAAU,CAAC,CAAC;QAC/C,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAU,CAAC,CAAC;QAEzC,iCAAiC;QACjC,MAAM,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,IAAI,CAAC,EAAE,YAAY,OAAO,CAAC,MAAM,UAAU,CAAC,CAAC;QACrE,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,0BAA0B,EAAE,GAAG,EAAE;QACpC,6CAA6C;QAC7C,YAAY;QACZ,MAAM,GAAG,GAAG,gDAAgD,GAAG,OAAO,CAAC;QACvE,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,CACxB,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,IAAI,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAC5D,CAAC;QAEF,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,6BAA6B,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,0BAA0B,EAAE,GAAG,EAAE;QACpC,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;QAExB,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAC7B,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-get-bounds.test.d.ts","sourceRoot":"","sources":["../../../test/unit/list-get-bounds.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { compile } from '../../src/browser.js';
|
|
4
|
+
import { evaluate } from '../../src/core/interpreter.js';
|
|
5
|
+
/**
|
|
6
|
+
* `List.get` 越界必须抛错(issue aster-dev#32)。
|
|
7
|
+
*
|
|
8
|
+
* ★真实缺陷:TS 侧直接 `arr[idx]`,JS 语义下越界得 `undefined` 且**静默成功**;
|
|
9
|
+
* 而 truffle `Builtins.List.get` 有显式边界检查并抛 `BuiltinException`。
|
|
10
|
+
* 同一段源码两引擎一个静默产 undefined、一个响亮失败,而文档声称
|
|
11
|
+
* 「越界报错且双引擎一致」——文档描述的是 truffle 的行为。
|
|
12
|
+
*
|
|
13
|
+
* 实测(修复前):
|
|
14
|
+
*
|
|
15
|
+
* List.get([1,2,3], 99) → success:true, value:undefined
|
|
16
|
+
* List.get([1,2,3], 0 minus 1) → success:true, value:undefined
|
|
17
|
+
*
|
|
18
|
+
* undefined 随后会一路流进裁决结果,是最危险的一类静默错答案:
|
|
19
|
+
* 既不报错、又不是正确值。
|
|
20
|
+
*/
|
|
21
|
+
function run(body) {
|
|
22
|
+
const c = compile(`Module probe.\n${body}\n`);
|
|
23
|
+
assert.ok(c.core, `compile: ${JSON.stringify(c.parseErrors ?? [])}`);
|
|
24
|
+
const ev = evaluate(c.core, 'main', { seed: 0 });
|
|
25
|
+
return { success: ev.success, value: ev.value, error: String(ev.error ?? '') };
|
|
26
|
+
}
|
|
27
|
+
describe('List.get 越界响亮失败(与 truffle 对齐)', () => {
|
|
28
|
+
it('★索引超出长度(此前 success:true, value:undefined)', () => {
|
|
29
|
+
const r = run('Rule main produce Int:\n Return List.get([1, 2, 3], 99).');
|
|
30
|
+
assert.equal(r.success, false, `必须失败,实际 value=${JSON.stringify(r.value)}`);
|
|
31
|
+
assert.match(r.error, /index out of bounds/, `实际:${r.error}`);
|
|
32
|
+
});
|
|
33
|
+
it('★负索引同样越界(JS 里 arr[-1] 也是 undefined)', () => {
|
|
34
|
+
const r = run('Rule main produce Int:\n Return List.get([1, 2, 3], 0 minus 1).');
|
|
35
|
+
assert.equal(r.success, false, `必须失败,实际 value=${JSON.stringify(r.value)}`);
|
|
36
|
+
assert.match(r.error, /index out of bounds/, `实际:${r.error}`);
|
|
37
|
+
});
|
|
38
|
+
it('★恰好等于长度的索引越界(经典 off-by-one)', () => {
|
|
39
|
+
const r = run('Rule main produce Int:\n Return List.get([1, 2, 3], 3).');
|
|
40
|
+
assert.equal(r.success, false, `size=3 时索引 3 越界,实际 value=${JSON.stringify(r.value)}`);
|
|
41
|
+
});
|
|
42
|
+
it('空列表任何索引都越界', () => {
|
|
43
|
+
const r = run('Rule main produce Int:\n Return List.get([], 0).');
|
|
44
|
+
assert.equal(r.success, false, `实际 value=${JSON.stringify(r.value)}`);
|
|
45
|
+
});
|
|
46
|
+
it('★错误消息须带索引与长度(对齐 truffle collectionIndexOutOfBounds 英文形式)', () => {
|
|
47
|
+
// 只断言「失败了」不够:用户需要知道越界的是哪个索引、列表多长。
|
|
48
|
+
const r = run('Rule main produce Int:\n Return List.get([1, 2, 3], 99).');
|
|
49
|
+
assert.match(r.error, /99/, `须点名越界索引;实际:${r.error}`);
|
|
50
|
+
assert.match(r.error, /size=3/, `须点名列表长度;实际:${r.error}`);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
describe('合法索引行为不变(反向护栏)', () => {
|
|
54
|
+
// ★没有这一组,把 List.get 写成「一律抛错」也能让上面全部变绿。
|
|
55
|
+
it('首元素 / 中间 / 末元素都正常', () => {
|
|
56
|
+
assert.equal(run('Rule main produce Int:\n Return List.get([10, 20, 30], 0).').value, 10);
|
|
57
|
+
assert.equal(run('Rule main produce Int:\n Return List.get([10, 20, 30], 1).').value, 20);
|
|
58
|
+
assert.equal(run('Rule main produce Int:\n Return List.get([10, 20, 30], 2).').value, 30);
|
|
59
|
+
});
|
|
60
|
+
it('单元素列表索引 0 正常', () => {
|
|
61
|
+
const r = run('Rule main produce Int:\n Return List.get([42], 0).');
|
|
62
|
+
assert.equal(r.success, true, r.error);
|
|
63
|
+
assert.equal(r.value, 42);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
//# sourceMappingURL=list-get-bounds.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-get-bounds.test.js","sourceRoot":"","sources":["../../../test/unit/list-get-bounds.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"long-bigint-exact.test.d.ts","sourceRoot":"","sources":["../../../test/unit/long-bigint-exact.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { compile } from '../../src/browser.js';
|
|
4
|
+
import { evaluate, serializeGuestValue } from '../../src/core/interpreter.js';
|
|
5
|
+
/**
|
|
6
|
+
* Long 精确 64 位(issue aster-lang-ts#142)。
|
|
7
|
+
*
|
|
8
|
+
* ★真实缺陷:词法器专门用 BigInt 存字符串以保精度、并硬拒超 Int64 的字面量,
|
|
9
|
+
* 但解释器求值时 `Number(expr.value)` 一步把精度丢回去:
|
|
10
|
+
*
|
|
11
|
+
* 9223372036854775807L → 9223372036854776000 (差 193,静默错答案)
|
|
12
|
+
*
|
|
13
|
+
* 而 JVM 侧 `CoreModel.LongE public long value` 是精确 64 位——**同一源码两引擎
|
|
14
|
+
* 运行值不同**。这是求值正确性问题,不是格式问题。
|
|
15
|
+
*/
|
|
16
|
+
function run(body) {
|
|
17
|
+
const c = compile(`Module probe.\n${body}\n`);
|
|
18
|
+
assert.ok(c.core, `compile: ${JSON.stringify(c.parseErrors ?? [])}`);
|
|
19
|
+
const ev = evaluate(c.core, 'main', { seed: 0 });
|
|
20
|
+
assert.ok(ev.success, `eval: ${ev.error ?? ''}`);
|
|
21
|
+
return ev.value;
|
|
22
|
+
}
|
|
23
|
+
function runErr(body) {
|
|
24
|
+
const c = compile(`Module probe.\n${body}\n`);
|
|
25
|
+
assert.ok(c.core, 'compile should succeed; the failure must come from evaluation');
|
|
26
|
+
const ev = evaluate(c.core, 'main', { seed: 0 });
|
|
27
|
+
assert.ok(!ev.success, 'expected evaluation to fail loudly');
|
|
28
|
+
return String(ev.error ?? '');
|
|
29
|
+
}
|
|
30
|
+
describe('Long = 精确 64 位(BigInt 运行时表示)', () => {
|
|
31
|
+
it('★Long.MAX_VALUE 逐位精确(此前得 9223372036854776000)', () => {
|
|
32
|
+
assert.equal(run('Rule main produce Long:\n Return 9223372036854775807L.'), 9223372036854775807n);
|
|
33
|
+
});
|
|
34
|
+
it('★MIN_VALUE 写成字面量会被词法层正确拒绝(不是缺陷)', () => {
|
|
35
|
+
// -9223372036854775808L 在词法上是「一元负号 + 正字面量 9223372036854775808」,
|
|
36
|
+
// 后者已超 Long.MAX → 词法层硬拒。这是既有的正确行为,测试钉住它以免日后被"修坏"。
|
|
37
|
+
const c = compile('Module probe.\nRule main produce Long:\n Return -9223372036854775808L.\n');
|
|
38
|
+
const errs = JSON.stringify(c.parseErrors ?? []);
|
|
39
|
+
assert.match(errs, /out of range for Long/);
|
|
40
|
+
});
|
|
41
|
+
it('★可表达的最小负值仍逐位精确(经减法构造——前置负号在该语法位置不被接受)', () => {
|
|
42
|
+
assert.equal(run('Rule main produce Long:\n Return 0L minus 9223372036854775807L.'), -9223372036854775807n);
|
|
43
|
+
});
|
|
44
|
+
it('★边界加法不溢出成浮点:MAX-1 + 1 == MAX', () => {
|
|
45
|
+
// 这是最能暴露原缺陷的一条:两侧都在 2^53 之上,浮点会把它们塌成同一个值。
|
|
46
|
+
assert.equal(run('Rule main produce Long:\n Return 9223372036854775806L plus 1L.'), 9223372036854775807n);
|
|
47
|
+
});
|
|
48
|
+
it('★仅差 1 的两个大 Long 必须不相等(浮点下会塌成相等)', () => {
|
|
49
|
+
assert.equal(run('Rule main produce Bool:\n Return 9223372036854775806L equals to 9223372036854775807L.'), false);
|
|
50
|
+
});
|
|
51
|
+
it('Long × Int 混算:整数 number 精确提升为 BigInt', () => {
|
|
52
|
+
assert.equal(run('Rule main produce Long:\n Return 100L times 3.'), 300n);
|
|
53
|
+
});
|
|
54
|
+
it('Long 除法向零截断(与 JVM long 除法一致)', () => {
|
|
55
|
+
assert.equal(run('Rule main produce Long:\n Return 10L integer divided by 3L.'), 3n);
|
|
56
|
+
// 负数走 Let 绑定:`-10L` 直接作被除数在该语法位置不被解析器接受
|
|
57
|
+
assert.equal(run('Rule main produce Long:\n Let n be 0L minus 10L.\n Return n integer divided by 3L.'), -3n);
|
|
58
|
+
});
|
|
59
|
+
it('Long 取模', () => {
|
|
60
|
+
assert.equal(run('Rule main produce Long:\n Return 10L modulo 3L.'), 1n);
|
|
61
|
+
});
|
|
62
|
+
it('★除零响亮失败,不返回 Infinity', () => {
|
|
63
|
+
assert.match(runErr('Rule main produce Long:\n Return 1L integer divided by 0L.'), /Division by zero/);
|
|
64
|
+
});
|
|
65
|
+
it('★与非整数 Double 混算必须响亮失败,不静默降级', () => {
|
|
66
|
+
// 静默把 Long 降成 double 正是本 issue 的病根,宁可失败。
|
|
67
|
+
assert.match(runErr('Rule main produce Long:\n Return 1L plus 1.5.'), /non-integer Double|expected Long\/Int/);
|
|
68
|
+
});
|
|
69
|
+
it('★序列化为无引号十进制字面量(与 JVM Jackson 序列化 long 同形)', () => {
|
|
70
|
+
// JSON.stringify(1n) 会抛 "Do not know how to serialize a BigInt"——
|
|
71
|
+
// serializeGuestValue 必须在委托之前拦下,否则整条 dual-engine 链路崩。
|
|
72
|
+
assert.equal(serializeGuestValue(9223372036854775807n), '9223372036854775807');
|
|
73
|
+
assert.equal(serializeGuestValue(-9223372036854775808n), '-9223372036854775808');
|
|
74
|
+
});
|
|
75
|
+
it('★嵌套在结构里也能序列化(不走 JSON.stringify 兜底)', () => {
|
|
76
|
+
assert.equal(serializeGuestValue([1n, 2n]), '[1,2]');
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
//# sourceMappingURL=long-bigint-exact.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"long-bigint-exact.test.js","sourceRoot":"","sources":["../../../test/unit/long-bigint-exact.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"completion-lexicon.test.d.ts","sourceRoot":"","sources":["../../../../test/unit/lsp/completion-lexicon.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { registerCompletionHandlers } from '../../../src/lsp/completion.js';
|
|
4
|
+
import { KW } from '../../../src/config/semantic.js';
|
|
5
|
+
import { ZH_CN } from '../../../src/config/lexicons/zh-CN.js';
|
|
6
|
+
import { DE_DE } from '../../../src/config/lexicons/de-DE.js';
|
|
7
|
+
/**
|
|
8
|
+
* 补全的关键词必须取自**该文档的 lexicon**,而不是硬编码的英文 `KW`。
|
|
9
|
+
*
|
|
10
|
+
* ★真实缺陷:`connection.onCompletion` 此前忽略入参、直接 `Object.values(KW)`——
|
|
11
|
+
* 而 KW 是 `config/semantic.ts` 里写死的英文规范拼写(module / use / define…)。
|
|
12
|
+
* 于是写 zh-CN 的用户敲出补全,拿到的是 `module, use, as`,
|
|
13
|
+
* 而他实际要写的是 `模块, 引用, 作为`:**补全给的每一个词都是错的**,选中即产生解析错误。
|
|
14
|
+
*
|
|
15
|
+
* 同文件的诊断 / 跳转 / 代码操作三条路径都已正确接收 `getLexiconForDoc`,唯独补全漏了
|
|
16
|
+
* ——属"同类调用在同一模块里不一致",是识别此类遗漏的可靠信号。
|
|
17
|
+
*
|
|
18
|
+
* ★本测试走**真实的 registerCompletionHandlers 接线**(用 stub connection 捕获 handler),
|
|
19
|
+
* 不复刻取词逻辑——否则测的是测试自己的副本,生产代码改坏了也不会红。
|
|
20
|
+
*/
|
|
21
|
+
/** 捕获 onCompletion 回调的最小 connection stub。 */
|
|
22
|
+
function makeStubConnection() {
|
|
23
|
+
let handler;
|
|
24
|
+
const connection = {
|
|
25
|
+
onCompletion(fn) { handler = fn; },
|
|
26
|
+
onCompletionResolve() { },
|
|
27
|
+
// registerCompletionHandlers 还会注册若干自定义请求;这里只需让它们可被调用。
|
|
28
|
+
onRequest() { },
|
|
29
|
+
};
|
|
30
|
+
return {
|
|
31
|
+
connection,
|
|
32
|
+
invoke: (uri) => {
|
|
33
|
+
assert.ok(handler, 'onCompletion 未被注册——接线断了');
|
|
34
|
+
return handler({ textDocument: { uri } });
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function labelsFor(lexicon) {
|
|
39
|
+
const { connection, invoke } = makeStubConnection();
|
|
40
|
+
registerCompletionHandlers(connection, { get: () => undefined }, () => ({ text: '', tokens: [], ast: undefined }), () => lexicon);
|
|
41
|
+
return invoke('file:///probe.aster').map((i) => i.label);
|
|
42
|
+
}
|
|
43
|
+
describe('补全关键词随 lexicon 切换(多语言)', () => {
|
|
44
|
+
it('★zh-CN 文档补全的是中文关键词,而非英文 KW', () => {
|
|
45
|
+
const labels = labelsFor(ZH_CN);
|
|
46
|
+
// 正向:中文规范拼写必须在
|
|
47
|
+
for (const kw of ['模块', '引用', '定义']) {
|
|
48
|
+
assert.ok(labels.includes(kw), `zh-CN 补全应包含「${kw}」,实际前 8 个:${labels.slice(0, 8).join(', ')}`);
|
|
49
|
+
}
|
|
50
|
+
// ★反向:不得再出现英文 KW —— 没有这一条,
|
|
51
|
+
// 把实现写成「中英文全都给」也能让上面变绿,而那同样是错的
|
|
52
|
+
// (用户会在中文文档里选到 `module`,选中即解析失败)。
|
|
53
|
+
assert.ok(!labels.includes('module'), 'zh-CN 补全不得包含英文 `module`');
|
|
54
|
+
assert.ok(!labels.includes('define'), 'zh-CN 补全不得包含英文 `define`');
|
|
55
|
+
});
|
|
56
|
+
it('★de-DE 文档补全的是德文关键词', () => {
|
|
57
|
+
const labels = labelsFor(DE_DE);
|
|
58
|
+
assert.ok(labels.includes('Modul'), `de-DE 补全应含 Modul,实际:${labels.slice(0, 8).join(', ')}`);
|
|
59
|
+
assert.ok(!labels.includes('module'), 'de-DE 补全不得包含英文 `module`');
|
|
60
|
+
});
|
|
61
|
+
it('无 lexicon 上下文时回退到英文 KW(不得变成空补全)', () => {
|
|
62
|
+
const labels = labelsFor(undefined);
|
|
63
|
+
assert.ok(labels.includes('module'), '回退路径应给出英文 KW');
|
|
64
|
+
assert.ok(labels.length >= Object.values(KW).length, '回退不得丢关键词');
|
|
65
|
+
});
|
|
66
|
+
it('★别名一并纳入补全(ADR 0022 识别侧多对一)', () => {
|
|
67
|
+
// 当前四个内置 lexicon 的 aliases 均为空,故用合成 lexicon 验证**接线**本身。
|
|
68
|
+
// 不这么做的话,将来某个 lexicon 一加别名,补全会静默落后而无人发现。
|
|
69
|
+
const withAlias = {
|
|
70
|
+
...ZH_CN,
|
|
71
|
+
aliases: { ...(ZH_CN.aliases ?? {}), MODULE_IS: ['模組'] },
|
|
72
|
+
};
|
|
73
|
+
const labels = labelsFor(withAlias);
|
|
74
|
+
assert.ok(labels.includes('模組'), `别名「模組」应出现在补全里,实际前 8 个:${labels.slice(0, 8).join(', ')}`);
|
|
75
|
+
// 规范拼写不得因加了别名而消失
|
|
76
|
+
assert.ok(labels.includes('模块'), '加别名不得挤掉规范拼写');
|
|
77
|
+
});
|
|
78
|
+
it('补全项去重(关键词与别名可能重叠)', () => {
|
|
79
|
+
const labels = labelsFor(ZH_CN);
|
|
80
|
+
assert.equal(new Set(labels).size, labels.length, '补全列表存在重复项');
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
//# sourceMappingURL=completion-lexicon.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"completion-lexicon.test.js","sourceRoot":"","sources":["../../../../test/unit/lsp/completion-lexicon.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-vocabulary.test.d.ts","sourceRoot":"","sources":["../../../../test/unit/lsp/tenant-vocabulary.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { vocabularyRegistry, initBuiltinVocabularies, } from '../../../src/config/lexicons/identifiers/registry.js';
|
|
4
|
+
import { canonicalize } from '../../../src/frontend/canonicalizer.js';
|
|
5
|
+
import { ZH_CN } from '../../../src/config/lexicons/zh-CN.js';
|
|
6
|
+
import { buildCanonicalizeOptions } from '../../../src/lsp/canonicalize-options.js';
|
|
7
|
+
import { applyTenantInitOptions, resolveTenantContext } from '../../../src/lsp/tenant-init.js';
|
|
8
|
+
/**
|
|
9
|
+
* 租户领域词汇必须参与 LSP 的解析(issue #161)。
|
|
10
|
+
*
|
|
11
|
+
* ★缺口有**两半**,缺任一半都等于没做:
|
|
12
|
+
* 1. LSP 进程不知道自己在为哪个租户服务 —— 网关的 upgrade 只校验
|
|
13
|
+
* Origin + 共享 token,不传租户身份。已改为由客户端在 `initialize` 的
|
|
14
|
+
* `initializationOptions` 里推送 `{ tenantId, domainVocabularies }`。
|
|
15
|
+
* 2. **即便注册了也白搭**:canonicalizer 靠
|
|
16
|
+
* `getWithCustom(tenantId, domain, locale)` 查自定义词汇,
|
|
17
|
+
* 而 LSP 此前调用的是 `canonicalize(text, lexicon)` —— 只传 lexicon,
|
|
18
|
+
* 那条分支(要求 `opts.domain && opts.locale` 同时存在)根本走不到。
|
|
19
|
+
*
|
|
20
|
+
* 本测试两半都锁:
|
|
21
|
+
* · 第 2 半——同一段源码在**传/不传**租户上下文时的行为差异(canonicalizer 契约)
|
|
22
|
+
* · 第 1 半——`applyTenantInitOptions` 是否真的把词汇注册进去
|
|
23
|
+
*
|
|
24
|
+
* ★为什么第 1 半非补不可:原先它只由 `server.ts` 承担,而那个模块顶层就
|
|
25
|
+
* `createConnection()`,单测无法 import。实测把 server.ts 整段注册逻辑改成
|
|
26
|
+
* `if (false)`,本文件 6 条用例**仍然全绿**——契约证明"canonicalizer 会用
|
|
27
|
+
* 租户词汇",证明不了"LSP 真的注册了"。已把决策点抽成无副作用的
|
|
28
|
+
* `src/lsp/tenant-init.ts`(与 aster-lang-ts#162 同一修法)。
|
|
29
|
+
*/
|
|
30
|
+
const TENANT = 'tenant-vocab-test';
|
|
31
|
+
const DOMAIN = 'insurance.vocabtest';
|
|
32
|
+
/** 与生产同形的最小领域词汇:把 `保额` 映射到 `coverageAmount`。 */
|
|
33
|
+
const VOCAB = {
|
|
34
|
+
id: DOMAIN,
|
|
35
|
+
name: '词汇测试',
|
|
36
|
+
locale: 'zh-CN',
|
|
37
|
+
version: '1.0.0',
|
|
38
|
+
structs: [],
|
|
39
|
+
fields: [],
|
|
40
|
+
functions: [{ canonical: 'coverageAmount', localized: '保额', kind: 'function' }],
|
|
41
|
+
};
|
|
42
|
+
const SRC = '模块 probe。\n\n规则 main 产出 Int:\n 返回 保额()。\n';
|
|
43
|
+
describe('租户领域词汇参与解析', () => {
|
|
44
|
+
it('★传租户上下文时,本地化词被规范化', () => {
|
|
45
|
+
initBuiltinVocabularies();
|
|
46
|
+
vocabularyRegistry.registerCustom(TENANT, VOCAB);
|
|
47
|
+
const out = canonicalize(SRC, {
|
|
48
|
+
lexicon: ZH_CN,
|
|
49
|
+
tenantId: TENANT,
|
|
50
|
+
domain: DOMAIN,
|
|
51
|
+
locale: 'zh-CN',
|
|
52
|
+
});
|
|
53
|
+
assert.ok(out.includes('coverageAmount'), `「保额」应被规范化为 coverageAmount,实际输出:${out.slice(0, 120)}`);
|
|
54
|
+
});
|
|
55
|
+
it('★不传租户上下文时保持原样(反向护栏)', () => {
|
|
56
|
+
initBuiltinVocabularies();
|
|
57
|
+
vocabularyRegistry.registerCustom(TENANT, VOCAB);
|
|
58
|
+
// 没有这一条,把实现写成「无条件查所有租户词汇」也能让上面变绿——
|
|
59
|
+
// 而那会造成**跨租户词汇泄露**:A 租户的术语在 B 租户的文档里生效。
|
|
60
|
+
const out = canonicalize(SRC, ZH_CN);
|
|
61
|
+
assert.ok(!out.includes('coverageAmount'), `未传租户时不得应用其词汇(跨租户泄露),实际输出:${out.slice(0, 120)}`);
|
|
62
|
+
});
|
|
63
|
+
it('★另一个租户拿不到本租户的词汇(隔离性)', () => {
|
|
64
|
+
initBuiltinVocabularies();
|
|
65
|
+
vocabularyRegistry.registerCustom(TENANT, VOCAB);
|
|
66
|
+
const out = canonicalize(SRC, {
|
|
67
|
+
lexicon: ZH_CN,
|
|
68
|
+
tenantId: 'some-other-tenant',
|
|
69
|
+
domain: DOMAIN,
|
|
70
|
+
locale: 'zh-CN',
|
|
71
|
+
});
|
|
72
|
+
assert.ok(!out.includes('coverageAmount'), `租户隔离被打破:other-tenant 用到了 ${TENANT} 的词汇`);
|
|
73
|
+
});
|
|
74
|
+
it('无效词汇表被拒绝(registerCustom 有校验,不静默接受)', () => {
|
|
75
|
+
initBuiltinVocabularies();
|
|
76
|
+
// 缺 functions/structs/fields 的畸形词汇——registerCustom 应抛错而非静默存下,
|
|
77
|
+
// 否则损坏的词汇会在解析期以更难定位的方式炸。
|
|
78
|
+
assert.throws(() => vocabularyRegistry.registerCustom(TENANT, { id: 'bad', name: 'x' }), /.+/);
|
|
79
|
+
});
|
|
80
|
+
/* ── LSP 侧接线:证明"真的传了",而不只是"canonicalizer 会用" ────────────
|
|
81
|
+
*
|
|
82
|
+
* ★上面四条只锁 canonicalizer 契约。实测把 LSP 里的租户参数删掉
|
|
83
|
+
* (回到缺陷版)后,那四条**仍然全绿** —— 契约对了不等于调用方传了。
|
|
84
|
+
* 故必须对 LSP 实际构造的参数单独断言。
|
|
85
|
+
*/
|
|
86
|
+
it('★有完整租户上下文时,LSP 传的是带 tenantId/domain 的 options', () => {
|
|
87
|
+
const opts = buildCanonicalizeOptions(ZH_CN, TENANT, DOMAIN);
|
|
88
|
+
assert.ok(opts && typeof opts === 'object' && 'tenantId' in opts, 'LSP 应传 options 对象而非裸 lexicon,否则自定义词汇分支走不到');
|
|
89
|
+
assert.equal(opts.tenantId, TENANT);
|
|
90
|
+
assert.equal(opts.domain, DOMAIN);
|
|
91
|
+
assert.equal(opts.locale, ZH_CN.id, 'locale 必须跟随 lexicon —— canonicalizer 按 (tenant, domain, locale) 三元组查');
|
|
92
|
+
});
|
|
93
|
+
it('★缺任一项即回退裸 lexicon(半套参数会静默走内置,比报错更难查)', () => {
|
|
94
|
+
assert.equal(buildCanonicalizeOptions(ZH_CN, undefined, DOMAIN), ZH_CN, '缺 tenantId 应回退');
|
|
95
|
+
assert.equal(buildCanonicalizeOptions(ZH_CN, TENANT, undefined), ZH_CN, '缺 domain 应回退');
|
|
96
|
+
assert.equal(buildCanonicalizeOptions(undefined, TENANT, DOMAIN), undefined, '无 lexicon 时保持 undefined');
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
/**
|
|
100
|
+
* 第 1 半:initializationOptions → registerCustom 的接线。
|
|
101
|
+
*
|
|
102
|
+
* ★这一块此前无人守卫——server.ts 无法单测,于是「LSP 到底注册没注册」
|
|
103
|
+
* 完全靠人肉阅读保证。抽成 tenant-init.ts 后才有了可断言的决策点。
|
|
104
|
+
*/
|
|
105
|
+
describe('租户上下文注册(applyTenantInitOptions)', () => {
|
|
106
|
+
const T = 'tenant-init-test';
|
|
107
|
+
/** 与生产同形的最小词汇表(字段形状照抄本文件上方的 VOCAB)。 */
|
|
108
|
+
const vocabOf = (id) => ({
|
|
109
|
+
id, name: '接线测试', locale: 'zh-CN', version: '1.0.0',
|
|
110
|
+
structs: [], fields: [],
|
|
111
|
+
functions: [{ canonical: 'premium', localized: '保费', kind: 'function' }],
|
|
112
|
+
});
|
|
113
|
+
it('★完整上下文:词汇被注册,返回 tenantId 与 domain', () => {
|
|
114
|
+
const r = applyTenantInitOptions({ tenantId: T, domainVocabularies: [vocabOf('vi.a')] });
|
|
115
|
+
assert.equal(r.tenantId, T);
|
|
116
|
+
assert.equal(r.domain, 'vi.a');
|
|
117
|
+
assert.equal(r.registered, 1);
|
|
118
|
+
// 真的进了注册表——而不只是返回了对象
|
|
119
|
+
assert.ok(vocabularyRegistry.getWithCustom(T, 'vi.a', 'zh-CN'), '词汇应当能从 registry 查回来');
|
|
120
|
+
});
|
|
121
|
+
it('★缺 tenantId:整块忽略(半套参数查不到任何东西)', () => {
|
|
122
|
+
const r = applyTenantInitOptions({ domainVocabularies: [vocabOf('vi.b')] });
|
|
123
|
+
assert.equal(r.tenantId, undefined);
|
|
124
|
+
assert.equal(r.registered, 0);
|
|
125
|
+
});
|
|
126
|
+
it('★缺 domainVocabularies:整块忽略', () => {
|
|
127
|
+
const r = applyTenantInitOptions({ tenantId: T });
|
|
128
|
+
assert.equal(r.tenantId, undefined);
|
|
129
|
+
assert.equal(r.registered, 0);
|
|
130
|
+
});
|
|
131
|
+
it('★无效词汇表被跳过,但不中断整批(缺几项 ≠ LSP 起不来)', () => {
|
|
132
|
+
const r = applyTenantInitOptions({
|
|
133
|
+
tenantId: T,
|
|
134
|
+
domainVocabularies: [{ id: 'bad' }, vocabOf('vi.c')],
|
|
135
|
+
});
|
|
136
|
+
assert.equal(r.registered, 1, '合法的那份仍应注册成功');
|
|
137
|
+
assert.equal(r.skipped.length, 1, '无效的那份应被记录');
|
|
138
|
+
// ★domain 取第一个**成功注册**的,而非数组首项——
|
|
139
|
+
// 首项若校验失败还拿它当 domain,后续查询会全部落空。
|
|
140
|
+
assert.equal(r.domain, 'vi.c');
|
|
141
|
+
});
|
|
142
|
+
it('★全部无效时不留 tenantId(避免构造出查不到东西的 options)', () => {
|
|
143
|
+
const r = applyTenantInitOptions({
|
|
144
|
+
tenantId: T, domainVocabularies: [{ id: 'bad1' }, { id: 'bad2' }],
|
|
145
|
+
});
|
|
146
|
+
assert.equal(r.tenantId, undefined, '一个都没注册成功就不该带 tenantId');
|
|
147
|
+
assert.equal(r.skipped.length, 2);
|
|
148
|
+
});
|
|
149
|
+
it('★从 InitializeParams **整体**取 initializationOptions(接线本身)', () => {
|
|
150
|
+
// ★这条锁的是 server.ts 里「把 params.initializationOptions 喂进去」那一步。
|
|
151
|
+
// 实测:把那行改成 applyTenantInitOptions(undefined),全量 1819 条仍全绿——
|
|
152
|
+
// 抽出被调用方只证明函数内部对,证明不了它被正确调用
|
|
153
|
+
// (「抽纯函数要抽到底」/「验连线不只验对象」)。
|
|
154
|
+
const r = resolveTenantContext({
|
|
155
|
+
initializationOptions: { tenantId: T, domainVocabularies: [vocabOf('vi.wire')] },
|
|
156
|
+
});
|
|
157
|
+
assert.equal(r.tenantId, T);
|
|
158
|
+
assert.equal(r.domain, 'vi.wire');
|
|
159
|
+
assert.equal(r.registered, 1);
|
|
160
|
+
});
|
|
161
|
+
it('★取错字段即失效(反向守卫:证明上一条不是恒真)', () => {
|
|
162
|
+
// 把同样的内容放在**别的**字段名下,必须拿不到租户上下文。
|
|
163
|
+
const r = resolveTenantContext({
|
|
164
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
165
|
+
initOptions: { tenantId: T, domainVocabularies: [vocabOf('vi.wrong')] },
|
|
166
|
+
});
|
|
167
|
+
assert.equal(r.tenantId, undefined);
|
|
168
|
+
assert.equal(r.registered, 0);
|
|
169
|
+
});
|
|
170
|
+
it('★registered 必须是**计数**而非布尔(两个都合法时应为 2)', () => {
|
|
171
|
+
// 原夹具最多只有 1 个有效项,于是 registered 取值域退化成 {0,1},
|
|
172
|
+
// `registered++` 改成 `registered = 1` 也全绿(夹具维度塌缩)。
|
|
173
|
+
const r = applyTenantInitOptions({
|
|
174
|
+
tenantId: T, domainVocabularies: [vocabOf('vi.two-a'), vocabOf('vi.two-b')],
|
|
175
|
+
});
|
|
176
|
+
assert.equal(r.registered, 2, 'registered 应是数量,不是布尔');
|
|
177
|
+
assert.equal(r.domain, 'vi.two-a', 'domain 取第一个成功注册的');
|
|
178
|
+
assert.ok(vocabularyRegistry.getWithCustom(T, 'vi.two-a', 'zh-CN'));
|
|
179
|
+
assert.ok(vocabularyRegistry.getWithCustom(T, 'vi.two-b', 'zh-CN'));
|
|
180
|
+
});
|
|
181
|
+
it('undefined 入参安全返回空结果', () => {
|
|
182
|
+
const r = applyTenantInitOptions(undefined);
|
|
183
|
+
assert.equal(r.tenantId, undefined);
|
|
184
|
+
assert.equal(r.registered, 0);
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
//# sourceMappingURL=tenant-vocabulary.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-vocabulary.test.js","sourceRoot":"","sources":["../../../../test/unit/lsp/tenant-vocabulary.test.ts"],"names":[],"mappings":"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"}
|