xdbc 1.0.217 → 1.0.218

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.
Files changed (89) hide show
  1. package/.gitattributes +8 -0
  2. package/.vscode/settings.json +3 -3
  3. package/.vscode/tasks.json +23 -23
  4. package/ASSESSMENT.md +249 -0
  5. package/README.md +131 -1
  6. package/__tests__/DBC/AE.test.ts +62 -62
  7. package/__tests__/DBC/ARRAY.test.ts +91 -91
  8. package/__tests__/DBC/DEFINED.test.ts +53 -53
  9. package/__tests__/DBC/DOM.test.ts +481 -0
  10. package/__tests__/DBC/Decorators.test.ts +367 -367
  11. package/__tests__/DBC/EQ.test.ts +13 -13
  12. package/__tests__/DBC/GREATER.test.ts +31 -31
  13. package/__tests__/DBC/HasAttribute.test.ts +60 -60
  14. package/__tests__/DBC/IF.test.ts +62 -62
  15. package/__tests__/DBC/INSTANCE.test.ts +13 -13
  16. package/__tests__/DBC/JSON.OP.test.ts +47 -47
  17. package/__tests__/DBC/JSON.Parse.test.ts +17 -17
  18. package/__tests__/DBC/OR.test.ts +14 -14
  19. package/__tests__/DBC/PLAIN_OBJECT.test.ts +109 -109
  20. package/__tests__/DBC/REGEX.test.ts +17 -17
  21. package/__tests__/DBC/TYPE.test.ts +13 -13
  22. package/__tests__/DBC/UNDEFINED.test.ts +45 -45
  23. package/__tests__/DBC/ZOD.test.ts +54 -54
  24. package/__tests__/DBC/onInfringement.test.ts +262 -0
  25. package/biome.json +40 -40
  26. package/dist/DBC/AE.js +172 -0
  27. package/dist/DBC/ARR/PLAIN_OBJECT.d.ts +0 -3
  28. package/dist/DBC/ARR/PLAIN_OBJECT.js +95 -0
  29. package/dist/DBC/ARRAY.d.ts +0 -3
  30. package/dist/DBC/ARRAY.js +90 -0
  31. package/dist/DBC/COMPARISON/GREATER.js +21 -0
  32. package/dist/DBC/COMPARISON/GREATER_OR_EQUAL.js +21 -0
  33. package/dist/DBC/COMPARISON/LESS.js +21 -0
  34. package/dist/DBC/COMPARISON/LESS_OR_EQUAL.js +21 -0
  35. package/dist/DBC/COMPARISON.js +98 -0
  36. package/dist/DBC/DEFINED.js +87 -0
  37. package/dist/DBC/DOM.d.ts +87 -0
  38. package/dist/DBC/DOM.js +223 -0
  39. package/dist/DBC/EQ/DIFFERENT.js +34 -0
  40. package/dist/DBC/EQ.js +101 -0
  41. package/dist/DBC/HasAttribute.js +101 -0
  42. package/dist/DBC/IF.js +96 -0
  43. package/dist/DBC/INSTANCE.js +122 -0
  44. package/dist/DBC/JSON.OP.js +120 -0
  45. package/dist/DBC/JSON.Parse.js +104 -0
  46. package/dist/DBC/OR.js +125 -0
  47. package/dist/DBC/REGEX.js +136 -0
  48. package/dist/DBC/TYPE.js +112 -0
  49. package/dist/DBC/UNDEFINED.js +87 -0
  50. package/dist/DBC/ZOD.js +99 -0
  51. package/dist/DBC.d.ts +18 -4
  52. package/dist/DBC.js +645 -0
  53. package/dist/Demo.d.ts +10 -0
  54. package/dist/Demo.js +713 -0
  55. package/dist/bundle.js +6140 -405
  56. package/dist/index.d.ts +22 -0
  57. package/dist/index.js +22 -0
  58. package/jest.config.js +32 -32
  59. package/package.json +71 -55
  60. package/src/DBC/AE.ts +269 -288
  61. package/src/DBC/ARR/PLAIN_OBJECT.ts +122 -133
  62. package/src/DBC/ARRAY.ts +117 -127
  63. package/src/DBC/COMPARISON/GREATER.ts +41 -46
  64. package/src/DBC/COMPARISON/GREATER_OR_EQUAL.ts +41 -45
  65. package/src/DBC/COMPARISON/LESS.ts +41 -45
  66. package/src/DBC/COMPARISON/LESS_OR_EQUAL.ts +41 -45
  67. package/src/DBC/COMPARISON.ts +149 -159
  68. package/src/DBC/DEFINED.ts +117 -122
  69. package/src/DBC/DOM.ts +291 -0
  70. package/src/DBC/EQ/DIFFERENT.ts +51 -57
  71. package/src/DBC/EQ.ts +154 -163
  72. package/src/DBC/HasAttribute.ts +149 -154
  73. package/src/DBC/IF.ts +173 -179
  74. package/src/DBC/INSTANCE.ts +168 -171
  75. package/src/DBC/JSON.OP.ts +178 -186
  76. package/src/DBC/JSON.Parse.ts +150 -157
  77. package/src/DBC/OR.ts +183 -187
  78. package/src/DBC/REGEX.ts +195 -196
  79. package/src/DBC/TYPE.ts +142 -149
  80. package/src/DBC/UNDEFINED.ts +115 -117
  81. package/src/DBC/ZOD.ts +130 -135
  82. package/src/DBC.ts +902 -904
  83. package/src/Demo.ts +537 -404
  84. package/src/index.ts +22 -0
  85. package/tsconfig.json +18 -18
  86. package/tsconfig.test.json +7 -7
  87. package/typedoc.json +16 -16
  88. package/webpack.config.js +27 -27
  89. package/Assessment.md +0 -507
@@ -1,367 +1,367 @@
1
- import "reflect-metadata";
2
- import { DBC } from "../../src/DBC";
3
- import { AE } from "../../src/DBC/AE";
4
- import { GREATER } from "../../src/DBC/COMPARISON/GREATER";
5
- import { GREATER_OR_EQUAL } from "../../src/DBC/COMPARISON/GREATER_OR_EQUAL";
6
- import { LESS } from "../../src/DBC/COMPARISON/LESS";
7
- import { LESS_OR_EQUAL } from "../../src/DBC/COMPARISON/LESS_OR_EQUAL";
8
- import { EQ } from "../../src/DBC/EQ";
9
- import { DIFFERENT } from "../../src/DBC/EQ/DIFFERENT";
10
- import { INSTANCE } from "../../src/DBC/INSTANCE";
11
- import { OR } from "../../src/DBC/OR";
12
- import { REGEX } from "../../src/DBC/REGEX";
13
- import { TYPE } from "../../src/DBC/TYPE";
14
-
15
- // Ensure a DBC instance is registered (DBC.ts module-level code does this via DBC.register)
16
- const dbc: DBC = (window as any).WaXCode?.DBC;
17
-
18
- describe("Decorator: @PRE (Preconditions)", () => {
19
- class PreTestSubject {
20
- @DBC.ParamvalueProvider
21
- public regexPre(@REGEX.PRE(/^[A-Z]+$/) input: string): string {
22
- return input;
23
- }
24
-
25
- @DBC.ParamvalueProvider
26
- public typePre(@TYPE.PRE("string") input: unknown): unknown {
27
- return input;
28
- }
29
-
30
- @DBC.ParamvalueProvider
31
- public eqPre(@EQ.PRE("hello") input: string): string {
32
- return input;
33
- }
34
-
35
- @DBC.ParamvalueProvider
36
- public greaterPre(@GREATER.PRE(5) input: number): number {
37
- return input;
38
- }
39
-
40
- @DBC.ParamvalueProvider
41
- public greaterOrEqualPre(@GREATER_OR_EQUAL.PRE(5) input: number): number {
42
- return input;
43
- }
44
-
45
- @DBC.ParamvalueProvider
46
- public lessPre(@LESS.PRE(10) input: number): number {
47
- return input;
48
- }
49
-
50
- @DBC.ParamvalueProvider
51
- public lessOrEqualPre(@LESS_OR_EQUAL.PRE(10) input: number): number {
52
- return input;
53
- }
54
-
55
- @DBC.ParamvalueProvider
56
- public differentPre(
57
- @DIFFERENT.PRE("forbidden", undefined) input: string,
58
- ): string {
59
- return input;
60
- }
61
-
62
- @DBC.ParamvalueProvider
63
- public instancePre(@INSTANCE.PRE(Date) input: unknown): unknown {
64
- return input;
65
- }
66
-
67
- @DBC.ParamvalueProvider
68
- public aePre(@AE.PRE([new TYPE("string")]) input: unknown[]): unknown[] {
69
- return input;
70
- }
71
-
72
- @DBC.ParamvalueProvider
73
- public orPre(@OR.PRE([new EQ("a"), new EQ("b")]) input: string): string {
74
- return input;
75
- }
76
- }
77
-
78
- const subject = new PreTestSubject();
79
-
80
- // REGEX.PRE
81
- test("REGEX.PRE passes with matching value", () => {
82
- expect(() => subject.regexPre("ABC")).not.toThrow();
83
- });
84
- test("REGEX.PRE throws on non-matching value", () => {
85
- expect(() => subject.regexPre("abc123")).toThrow();
86
- });
87
-
88
- // TYPE.PRE
89
- test("TYPE.PRE passes with correct type", () => {
90
- expect(() => subject.typePre("hello")).not.toThrow();
91
- });
92
- test("TYPE.PRE throws with wrong type", () => {
93
- expect(() => subject.typePre(42)).toThrow();
94
- });
95
-
96
- // EQ.PRE
97
- test("EQ.PRE passes with equal value", () => {
98
- expect(() => subject.eqPre("hello")).not.toThrow();
99
- });
100
- test("EQ.PRE throws with non-equal value", () => {
101
- expect(() => subject.eqPre("world")).toThrow();
102
- });
103
-
104
- // GREATER.PRE
105
- test("GREATER.PRE passes with value > reference", () => {
106
- expect(() => subject.greaterPre(10)).not.toThrow();
107
- });
108
- test("GREATER.PRE throws with value <= reference", () => {
109
- expect(() => subject.greaterPre(5)).toThrow();
110
- });
111
- test("GREATER.PRE throws with value < reference", () => {
112
- expect(() => subject.greaterPre(3)).toThrow();
113
- });
114
-
115
- // GREATER_OR_EQUAL.PRE
116
- test("GREATER_OR_EQUAL.PRE passes with value >= reference", () => {
117
- expect(() => subject.greaterOrEqualPre(5)).not.toThrow();
118
- });
119
- test("GREATER_OR_EQUAL.PRE passes with value > reference", () => {
120
- expect(() => subject.greaterOrEqualPre(10)).not.toThrow();
121
- });
122
- test("GREATER_OR_EQUAL.PRE throws with value < reference", () => {
123
- expect(() => subject.greaterOrEqualPre(3)).toThrow();
124
- });
125
-
126
- // LESS.PRE
127
- test("LESS.PRE passes with value < reference", () => {
128
- expect(() => subject.lessPre(5)).not.toThrow();
129
- });
130
- test("LESS.PRE throws with value >= reference", () => {
131
- expect(() => subject.lessPre(10)).toThrow();
132
- });
133
-
134
- // LESS_OR_EQUAL.PRE
135
- test("LESS_OR_EQUAL.PRE passes with value <= reference", () => {
136
- expect(() => subject.lessOrEqualPre(10)).not.toThrow();
137
- });
138
- test("LESS_OR_EQUAL.PRE throws with value > reference", () => {
139
- expect(() => subject.lessOrEqualPre(15)).toThrow();
140
- });
141
-
142
- // DIFFERENT.PRE
143
- test("DIFFERENT.PRE passes with different value", () => {
144
- expect(() => subject.differentPre("allowed")).not.toThrow();
145
- });
146
- test("DIFFERENT.PRE throws with equal value", () => {
147
- expect(() => subject.differentPre("forbidden")).toThrow();
148
- });
149
-
150
- // INSTANCE.PRE
151
- test("INSTANCE.PRE passes with correct instance", () => {
152
- expect(() => subject.instancePre(new Date())).not.toThrow();
153
- });
154
- test("INSTANCE.PRE throws with wrong instance", () => {
155
- expect(() => subject.instancePre("not a date")).toThrow();
156
- });
157
-
158
- // AE.PRE
159
- test("AE.PRE passes with all elements matching", () => {
160
- expect(() => subject.aePre(["a", "b", "c"])).not.toThrow();
161
- });
162
- test("AE.PRE throws when an element does not match", () => {
163
- expect(() => subject.aePre(["a", 42, "c"])).toThrow();
164
- });
165
-
166
- // OR.PRE
167
- test("OR.PRE passes when one contract is satisfied", () => {
168
- expect(() => subject.orPre("a")).not.toThrow();
169
- });
170
- test("OR.PRE throws when no contract is satisfied", () => {
171
- expect(() => subject.orPre("c")).toThrow();
172
- });
173
- });
174
-
175
- describe("Decorator: @POST (Postconditions)", () => {
176
- class PostTestSubject {
177
- @REGEX.POST(/^OK:.*$/)
178
- @DBC.ParamvalueProvider
179
- public formatResponse(@TYPE.PRE("string") input: string): string {
180
- return `OK:${input}`;
181
- }
182
-
183
- @REGEX.POST(/^OK:.*$/)
184
- public failingPost(): string {
185
- return "FAIL";
186
- }
187
-
188
- @EQ.POST("hello")
189
- public eqPost(returnThis: string): string {
190
- return returnThis;
191
- }
192
- }
193
-
194
- const subject = new PostTestSubject();
195
-
196
- test("POST passes when return value matches", () => {
197
- expect(() => subject.formatResponse("test")).not.toThrow();
198
- });
199
-
200
- test("POST throws when return value does not match", () => {
201
- expect(() => subject.failingPost()).toThrow();
202
- });
203
-
204
- test("EQ.POST passes with matching return value", () => {
205
- expect(() => subject.eqPost("hello")).not.toThrow();
206
- });
207
-
208
- test("EQ.POST throws with non-matching return value", () => {
209
- expect(() => subject.eqPost("world")).toThrow();
210
- });
211
- });
212
-
213
- describe("Decorator: @INVARIANT (Field contracts)", () => {
214
- test("INVARIANT allows valid initial value", () => {
215
- expect(() => {
216
- class InvariantSubject {
217
- @REGEX.INVARIANT(/^[A-Z]+$/)
218
- public code = "ABC";
219
- }
220
- new InvariantSubject();
221
- }).not.toThrow();
222
- });
223
-
224
- test("INVARIANT throws on invalid initial value", () => {
225
- expect(() => {
226
- class InvariantSubject {
227
- @REGEX.INVARIANT(/^[A-Z]+$/)
228
- public code = "abc123";
229
- }
230
- new InvariantSubject();
231
- }).toThrow();
232
- });
233
-
234
- test("INVARIANT throws on invalid reassignment", () => {
235
- class InvariantSubject {
236
- @REGEX.INVARIANT(/^[A-Z]+$/)
237
- public code = "ABC";
238
- }
239
- const obj = new InvariantSubject();
240
- expect(() => {
241
- obj.code = "invalid!";
242
- }).toThrow();
243
- });
244
-
245
- test("INVARIANT allows valid reassignment", () => {
246
- class InvariantSubject {
247
- @REGEX.INVARIANT(/^[A-Z]+$/)
248
- public code = "ABC";
249
- }
250
- const obj = new InvariantSubject();
251
- expect(() => {
252
- obj.code = "XYZ";
253
- }).not.toThrow();
254
- });
255
- });
256
-
257
- describe("Decorator: @ParamvalueProvider", () => {
258
- test("ParamvalueProvider passes multiple parameter contracts", () => {
259
- class MultiParam {
260
- @DBC.ParamvalueProvider
261
- public method(
262
- @TYPE.PRE("string") a: string,
263
- @TYPE.PRE("number") b: number,
264
- ): string {
265
- return `${a}:${b}`;
266
- }
267
- }
268
- const obj = new MultiParam();
269
- expect(() => obj.method("hello", 42)).not.toThrow();
270
- });
271
-
272
- test("ParamvalueProvider catches second parameter violation", () => {
273
- class MultiParam {
274
- @DBC.ParamvalueProvider
275
- public method(
276
- @TYPE.PRE("string") a: string,
277
- @TYPE.PRE("number") b: number,
278
- ): string {
279
- return `${a}:${b}`;
280
- }
281
- }
282
- const obj = new MultiParam();
283
- expect(() => obj.method("hello", "not a number" as any)).toThrow();
284
- });
285
- });
286
-
287
- describe("DBC infringement settings", () => {
288
- class InfringementTest {
289
- @DBC.ParamvalueProvider
290
- public method(@TYPE.PRE("string") input: unknown): unknown {
291
- return input;
292
- }
293
- }
294
-
295
- test("Violations throw DBC.Infringement by default", () => {
296
- const obj = new InfringementTest();
297
- expect(() => obj.method(42)).toThrow(/XDBC Infringement/);
298
- });
299
-
300
- test("Error message includes class name, method name, and parameter info", () => {
301
- const obj = new InfringementTest();
302
- try {
303
- obj.method(42);
304
- fail("Should have thrown");
305
- } catch (e: any) {
306
- expect(e.message).toContain("InfringementTest");
307
- expect(e.message).toContain("method");
308
- expect(e.message).toContain("1st parameter");
309
- }
310
- });
311
- });
312
-
313
- describe("DBC.register()", () => {
314
- test("registers an instance at the default path", () => {
315
- const custom = new DBC();
316
- DBC.register(custom);
317
- expect((window as any).WaXCode.DBC).toBe(custom);
318
- // Restore original
319
- DBC.register(dbc);
320
- });
321
-
322
- test("registers an instance at a custom path", () => {
323
- const custom = new DBC();
324
- DBC.register(custom, "TestVendor.DBC");
325
- expect((window as any).TestVendor.DBC).toBe(custom);
326
- });
327
-
328
- test("constructor does not auto-mount to globalThis", () => {
329
- const original = (window as any).WaXCode.DBC;
330
- const orphan = new DBC();
331
- // Constructor should NOT have replaced the registered instance
332
- expect((window as any).WaXCode.DBC).toBe(original);
333
- expect(orphan).not.toBe(original);
334
- });
335
- });
336
-
337
- describe("DBC.isolated()", () => {
338
- test("provides a temporary DBC instance and restores the original", () => {
339
- const original = (window as any).WaXCode.DBC;
340
- let isolatedInstance: DBC | undefined;
341
- DBC.isolated((tempDbc) => {
342
- isolatedInstance = tempDbc;
343
- expect(tempDbc).not.toBe(original);
344
- expect((window as any).WaXCode.DBC).toBe(tempDbc);
345
- });
346
- // After isolated() returns, the original is restored
347
- expect((window as any).WaXCode.DBC).toBe(original);
348
- expect(isolatedInstance).toBeDefined();
349
- });
350
-
351
- test("restores original even if callback throws", () => {
352
- const original = (window as any).WaXCode.DBC;
353
- expect(() => {
354
- DBC.isolated(() => {
355
- throw new Error("test error");
356
- });
357
- }).toThrow("test error");
358
- expect((window as any).WaXCode.DBC).toBe(original);
359
- });
360
-
361
- test("isolated instance has independent settings", () => {
362
- DBC.isolated((tempDbc) => {
363
- tempDbc.executionSettings.checkPreconditions = false;
364
- expect(dbc.executionSettings.checkPreconditions).toBe(true);
365
- });
366
- });
367
- });
1
+ import "reflect-metadata";
2
+ import { DBC } from "../../src/DBC";
3
+ import { AE } from "../../src/DBC/AE";
4
+ import { GREATER } from "../../src/DBC/COMPARISON/GREATER";
5
+ import { GREATER_OR_EQUAL } from "../../src/DBC/COMPARISON/GREATER_OR_EQUAL";
6
+ import { LESS } from "../../src/DBC/COMPARISON/LESS";
7
+ import { LESS_OR_EQUAL } from "../../src/DBC/COMPARISON/LESS_OR_EQUAL";
8
+ import { EQ } from "../../src/DBC/EQ";
9
+ import { DIFFERENT } from "../../src/DBC/EQ/DIFFERENT";
10
+ import { INSTANCE } from "../../src/DBC/INSTANCE";
11
+ import { OR } from "../../src/DBC/OR";
12
+ import { REGEX } from "../../src/DBC/REGEX";
13
+ import { TYPE } from "../../src/DBC/TYPE";
14
+
15
+ // Ensure a DBC instance is registered (DBC.ts module-level code does this via DBC.register)
16
+ const dbc: DBC = (window as any).WaXCode?.DBC;
17
+
18
+ describe("Decorator: @PRE (Preconditions)", () => {
19
+ class PreTestSubject {
20
+ @DBC.ParamvalueProvider
21
+ public regexPre(@REGEX.PRE(/^[A-Z]+$/) input: string): string {
22
+ return input;
23
+ }
24
+
25
+ @DBC.ParamvalueProvider
26
+ public typePre(@TYPE.PRE("string") input: unknown): unknown {
27
+ return input;
28
+ }
29
+
30
+ @DBC.ParamvalueProvider
31
+ public eqPre(@EQ.PRE("hello") input: string): string {
32
+ return input;
33
+ }
34
+
35
+ @DBC.ParamvalueProvider
36
+ public greaterPre(@GREATER.PRE(5) input: number): number {
37
+ return input;
38
+ }
39
+
40
+ @DBC.ParamvalueProvider
41
+ public greaterOrEqualPre(@GREATER_OR_EQUAL.PRE(5) input: number): number {
42
+ return input;
43
+ }
44
+
45
+ @DBC.ParamvalueProvider
46
+ public lessPre(@LESS.PRE(10) input: number): number {
47
+ return input;
48
+ }
49
+
50
+ @DBC.ParamvalueProvider
51
+ public lessOrEqualPre(@LESS_OR_EQUAL.PRE(10) input: number): number {
52
+ return input;
53
+ }
54
+
55
+ @DBC.ParamvalueProvider
56
+ public differentPre(
57
+ @DIFFERENT.PRE("forbidden", undefined) input: string,
58
+ ): string {
59
+ return input;
60
+ }
61
+
62
+ @DBC.ParamvalueProvider
63
+ public instancePre(@INSTANCE.PRE(Date) input: unknown): unknown {
64
+ return input;
65
+ }
66
+
67
+ @DBC.ParamvalueProvider
68
+ public aePre(@AE.PRE([new TYPE("string")]) input: unknown[]): unknown[] {
69
+ return input;
70
+ }
71
+
72
+ @DBC.ParamvalueProvider
73
+ public orPre(@OR.PRE([new EQ("a"), new EQ("b")]) input: string): string {
74
+ return input;
75
+ }
76
+ }
77
+
78
+ const subject = new PreTestSubject();
79
+
80
+ // REGEX.PRE
81
+ test("REGEX.PRE passes with matching value", () => {
82
+ expect(() => subject.regexPre("ABC")).not.toThrow();
83
+ });
84
+ test("REGEX.PRE throws on non-matching value", () => {
85
+ expect(() => subject.regexPre("abc123")).toThrow();
86
+ });
87
+
88
+ // TYPE.PRE
89
+ test("TYPE.PRE passes with correct type", () => {
90
+ expect(() => subject.typePre("hello")).not.toThrow();
91
+ });
92
+ test("TYPE.PRE throws with wrong type", () => {
93
+ expect(() => subject.typePre(42)).toThrow();
94
+ });
95
+
96
+ // EQ.PRE
97
+ test("EQ.PRE passes with equal value", () => {
98
+ expect(() => subject.eqPre("hello")).not.toThrow();
99
+ });
100
+ test("EQ.PRE throws with non-equal value", () => {
101
+ expect(() => subject.eqPre("world")).toThrow();
102
+ });
103
+
104
+ // GREATER.PRE
105
+ test("GREATER.PRE passes with value > reference", () => {
106
+ expect(() => subject.greaterPre(10)).not.toThrow();
107
+ });
108
+ test("GREATER.PRE throws with value <= reference", () => {
109
+ expect(() => subject.greaterPre(5)).toThrow();
110
+ });
111
+ test("GREATER.PRE throws with value < reference", () => {
112
+ expect(() => subject.greaterPre(3)).toThrow();
113
+ });
114
+
115
+ // GREATER_OR_EQUAL.PRE
116
+ test("GREATER_OR_EQUAL.PRE passes with value >= reference", () => {
117
+ expect(() => subject.greaterOrEqualPre(5)).not.toThrow();
118
+ });
119
+ test("GREATER_OR_EQUAL.PRE passes with value > reference", () => {
120
+ expect(() => subject.greaterOrEqualPre(10)).not.toThrow();
121
+ });
122
+ test("GREATER_OR_EQUAL.PRE throws with value < reference", () => {
123
+ expect(() => subject.greaterOrEqualPre(3)).toThrow();
124
+ });
125
+
126
+ // LESS.PRE
127
+ test("LESS.PRE passes with value < reference", () => {
128
+ expect(() => subject.lessPre(5)).not.toThrow();
129
+ });
130
+ test("LESS.PRE throws with value >= reference", () => {
131
+ expect(() => subject.lessPre(10)).toThrow();
132
+ });
133
+
134
+ // LESS_OR_EQUAL.PRE
135
+ test("LESS_OR_EQUAL.PRE passes with value <= reference", () => {
136
+ expect(() => subject.lessOrEqualPre(10)).not.toThrow();
137
+ });
138
+ test("LESS_OR_EQUAL.PRE throws with value > reference", () => {
139
+ expect(() => subject.lessOrEqualPre(15)).toThrow();
140
+ });
141
+
142
+ // DIFFERENT.PRE
143
+ test("DIFFERENT.PRE passes with different value", () => {
144
+ expect(() => subject.differentPre("allowed")).not.toThrow();
145
+ });
146
+ test("DIFFERENT.PRE throws with equal value", () => {
147
+ expect(() => subject.differentPre("forbidden")).toThrow();
148
+ });
149
+
150
+ // INSTANCE.PRE
151
+ test("INSTANCE.PRE passes with correct instance", () => {
152
+ expect(() => subject.instancePre(new Date())).not.toThrow();
153
+ });
154
+ test("INSTANCE.PRE throws with wrong instance", () => {
155
+ expect(() => subject.instancePre("not a date")).toThrow();
156
+ });
157
+
158
+ // AE.PRE
159
+ test("AE.PRE passes with all elements matching", () => {
160
+ expect(() => subject.aePre(["a", "b", "c"])).not.toThrow();
161
+ });
162
+ test("AE.PRE throws when an element does not match", () => {
163
+ expect(() => subject.aePre(["a", 42, "c"])).toThrow();
164
+ });
165
+
166
+ // OR.PRE
167
+ test("OR.PRE passes when one contract is satisfied", () => {
168
+ expect(() => subject.orPre("a")).not.toThrow();
169
+ });
170
+ test("OR.PRE throws when no contract is satisfied", () => {
171
+ expect(() => subject.orPre("c")).toThrow();
172
+ });
173
+ });
174
+
175
+ describe("Decorator: @POST (Postconditions)", () => {
176
+ class PostTestSubject {
177
+ @REGEX.POST(/^OK:.*$/)
178
+ @DBC.ParamvalueProvider
179
+ public formatResponse(@TYPE.PRE("string") input: string): string {
180
+ return `OK:${input}`;
181
+ }
182
+
183
+ @REGEX.POST(/^OK:.*$/)
184
+ public failingPost(): string {
185
+ return "FAIL";
186
+ }
187
+
188
+ @EQ.POST("hello")
189
+ public eqPost(returnThis: string): string {
190
+ return returnThis;
191
+ }
192
+ }
193
+
194
+ const subject = new PostTestSubject();
195
+
196
+ test("POST passes when return value matches", () => {
197
+ expect(() => subject.formatResponse("test")).not.toThrow();
198
+ });
199
+
200
+ test("POST throws when return value does not match", () => {
201
+ expect(() => subject.failingPost()).toThrow();
202
+ });
203
+
204
+ test("EQ.POST passes with matching return value", () => {
205
+ expect(() => subject.eqPost("hello")).not.toThrow();
206
+ });
207
+
208
+ test("EQ.POST throws with non-matching return value", () => {
209
+ expect(() => subject.eqPost("world")).toThrow();
210
+ });
211
+ });
212
+
213
+ describe("Decorator: @INVARIANT (Field contracts)", () => {
214
+ test("INVARIANT allows valid initial value", () => {
215
+ expect(() => {
216
+ class InvariantSubject {
217
+ @REGEX.INVARIANT(/^[A-Z]+$/)
218
+ public code = "ABC";
219
+ }
220
+ new InvariantSubject();
221
+ }).not.toThrow();
222
+ });
223
+
224
+ test("INVARIANT throws on invalid initial value", () => {
225
+ expect(() => {
226
+ class InvariantSubject {
227
+ @REGEX.INVARIANT(/^[A-Z]+$/)
228
+ public code = "abc123";
229
+ }
230
+ new InvariantSubject();
231
+ }).toThrow();
232
+ });
233
+
234
+ test("INVARIANT throws on invalid reassignment", () => {
235
+ class InvariantSubject {
236
+ @REGEX.INVARIANT(/^[A-Z]+$/)
237
+ public code = "ABC";
238
+ }
239
+ const obj = new InvariantSubject();
240
+ expect(() => {
241
+ obj.code = "invalid!";
242
+ }).toThrow();
243
+ });
244
+
245
+ test("INVARIANT allows valid reassignment", () => {
246
+ class InvariantSubject {
247
+ @REGEX.INVARIANT(/^[A-Z]+$/)
248
+ public code = "ABC";
249
+ }
250
+ const obj = new InvariantSubject();
251
+ expect(() => {
252
+ obj.code = "XYZ";
253
+ }).not.toThrow();
254
+ });
255
+ });
256
+
257
+ describe("Decorator: @ParamvalueProvider", () => {
258
+ test("ParamvalueProvider passes multiple parameter contracts", () => {
259
+ class MultiParam {
260
+ @DBC.ParamvalueProvider
261
+ public method(
262
+ @TYPE.PRE("string") a: string,
263
+ @TYPE.PRE("number") b: number,
264
+ ): string {
265
+ return `${a}:${b}`;
266
+ }
267
+ }
268
+ const obj = new MultiParam();
269
+ expect(() => obj.method("hello", 42)).not.toThrow();
270
+ });
271
+
272
+ test("ParamvalueProvider catches second parameter violation", () => {
273
+ class MultiParam {
274
+ @DBC.ParamvalueProvider
275
+ public method(
276
+ @TYPE.PRE("string") a: string,
277
+ @TYPE.PRE("number") b: number,
278
+ ): string {
279
+ return `${a}:${b}`;
280
+ }
281
+ }
282
+ const obj = new MultiParam();
283
+ expect(() => obj.method("hello", "not a number" as any)).toThrow();
284
+ });
285
+ });
286
+
287
+ describe("DBC infringement settings", () => {
288
+ class InfringementTest {
289
+ @DBC.ParamvalueProvider
290
+ public method(@TYPE.PRE("string") input: unknown): unknown {
291
+ return input;
292
+ }
293
+ }
294
+
295
+ test("Violations throw DBC.Infringement by default", () => {
296
+ const obj = new InfringementTest();
297
+ expect(() => obj.method(42)).toThrow(/XDBC Infringement/);
298
+ });
299
+
300
+ test("Error message includes class name, method name, and parameter info", () => {
301
+ const obj = new InfringementTest();
302
+ try {
303
+ obj.method(42);
304
+ fail("Should have thrown");
305
+ } catch (e: any) {
306
+ expect(e.message).toContain("InfringementTest");
307
+ expect(e.message).toContain("method");
308
+ expect(e.message).toContain("1st parameter");
309
+ }
310
+ });
311
+ });
312
+
313
+ describe("DBC.register()", () => {
314
+ test("registers an instance at the default path", () => {
315
+ const custom = new DBC();
316
+ DBC.register(custom);
317
+ expect((window as any).WaXCode.DBC).toBe(custom);
318
+ // Restore original
319
+ DBC.register(dbc);
320
+ });
321
+
322
+ test("registers an instance at a custom path", () => {
323
+ const custom = new DBC();
324
+ DBC.register(custom, "TestVendor.DBC");
325
+ expect((window as any).TestVendor.DBC).toBe(custom);
326
+ });
327
+
328
+ test("constructor does not auto-mount to globalThis", () => {
329
+ const original = (window as any).WaXCode.DBC;
330
+ const orphan = new DBC();
331
+ // Constructor should NOT have replaced the registered instance
332
+ expect((window as any).WaXCode.DBC).toBe(original);
333
+ expect(orphan).not.toBe(original);
334
+ });
335
+ });
336
+
337
+ describe("DBC.isolated()", () => {
338
+ test("provides a temporary DBC instance and restores the original", () => {
339
+ const original = (window as any).WaXCode.DBC;
340
+ let isolatedInstance: DBC | undefined;
341
+ DBC.isolated((tempDbc) => {
342
+ isolatedInstance = tempDbc;
343
+ expect(tempDbc).not.toBe(original);
344
+ expect((window as any).WaXCode.DBC).toBe(tempDbc);
345
+ });
346
+ // After isolated() returns, the original is restored
347
+ expect((window as any).WaXCode.DBC).toBe(original);
348
+ expect(isolatedInstance).toBeDefined();
349
+ });
350
+
351
+ test("restores original even if callback throws", () => {
352
+ const original = (window as any).WaXCode.DBC;
353
+ expect(() => {
354
+ DBC.isolated(() => {
355
+ throw new Error("test error");
356
+ });
357
+ }).toThrow("test error");
358
+ expect((window as any).WaXCode.DBC).toBe(original);
359
+ });
360
+
361
+ test("isolated instance has independent settings", () => {
362
+ DBC.isolated((tempDbc) => {
363
+ tempDbc.executionSettings.checkPreconditions = false;
364
+ expect(dbc.executionSettings.checkPreconditions).toBe(true);
365
+ });
366
+ });
367
+ });