xdbc 1.0.217 → 1.0.219

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 (120) hide show
  1. package/.gitattributes +16 -8
  2. package/.vscode/settings.json +3 -3
  3. package/.vscode/tasks.json +23 -23
  4. package/ASSESSMENT.md +249 -0
  5. package/README.md +538 -408
  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 +786 -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 +45 -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 +123 -0
  38. package/dist/DBC/DOM.js +362 -0
  39. package/dist/DBC/EQ/DIFFERENT.js +34 -0
  40. package/dist/DBC/EQ.js +101 -0
  41. package/dist/DBC/GREATER.js +99 -0
  42. package/dist/DBC/HasAttribute.js +101 -0
  43. package/dist/DBC/IF.js +96 -0
  44. package/dist/DBC/INSTANCE.js +122 -0
  45. package/dist/DBC/JSON.OP.js +120 -0
  46. package/dist/DBC/JSON.Parse.js +104 -0
  47. package/dist/DBC/OR.js +125 -0
  48. package/dist/DBC/REGEX.js +136 -0
  49. package/dist/DBC/TYPE.js +112 -0
  50. package/dist/DBC/UNDEFINED.js +87 -0
  51. package/dist/DBC/ZOD.js +99 -0
  52. package/dist/DBC.d.ts +18 -4
  53. package/dist/DBC.js +645 -0
  54. package/dist/Demo.d.ts +10 -0
  55. package/dist/Demo.js +713 -0
  56. package/dist/Test.html +18 -0
  57. package/dist/bundle.js +6140 -405
  58. package/dist/index.d.ts +22 -0
  59. package/dist/index.html +18 -0
  60. package/dist/index.js +22 -0
  61. package/docs/assets/highlight.css +22 -22
  62. package/docs/assets/icons.js +17 -17
  63. package/docs/assets/main.js +60 -60
  64. package/docs/assets/style.css +1640 -1640
  65. package/docs/classes/DBC.DBC.html +98 -98
  66. package/docs/classes/DBC_AE.AE.html +160 -160
  67. package/docs/classes/DBC_EQ.EQ.html +131 -131
  68. package/docs/classes/DBC_GREATER.GREATER.html +139 -139
  69. package/docs/classes/DBC_INSTANCE.INSTANCE.html +130 -130
  70. package/docs/classes/DBC_JSON.OP.JSON_OP.html +138 -138
  71. package/docs/classes/DBC_JSON.Parse.JSON_Parse.html +129 -129
  72. package/docs/classes/DBC_OR.OR.html +137 -137
  73. package/docs/classes/DBC_REGEX.REGEX.html +136 -136
  74. package/docs/classes/DBC_TYPE.TYPE.html +130 -130
  75. package/docs/classes/Demo.Demo.html +14 -14
  76. package/docs/hierarchy.html +1 -1
  77. package/docs/index.html +1 -1
  78. package/docs/modules/DBC.html +1 -1
  79. package/docs/modules/DBC_AE.html +1 -1
  80. package/docs/modules/DBC_EQ.html +1 -1
  81. package/docs/modules/DBC_GREATER.html +1 -1
  82. package/docs/modules/DBC_INSTANCE.html +1 -1
  83. package/docs/modules/DBC_JSON.OP.html +1 -1
  84. package/docs/modules/DBC_JSON.Parse.html +1 -1
  85. package/docs/modules/DBC_OR.html +1 -1
  86. package/docs/modules/DBC_REGEX.html +1 -1
  87. package/docs/modules/DBC_TYPE.html +1 -1
  88. package/docs/modules/Demo.html +1 -1
  89. package/jest.config.js +32 -32
  90. package/package.json +71 -55
  91. package/src/DBC/AE.ts +269 -288
  92. package/src/DBC/ARR/PLAIN_OBJECT.ts +122 -133
  93. package/src/DBC/ARRAY.ts +117 -127
  94. package/src/DBC/COMPARISON/GREATER.ts +41 -46
  95. package/src/DBC/COMPARISON/GREATER_OR_EQUAL.ts +41 -45
  96. package/src/DBC/COMPARISON/LESS.ts +41 -45
  97. package/src/DBC/COMPARISON/LESS_OR_EQUAL.ts +41 -45
  98. package/src/DBC/COMPARISON.ts +149 -159
  99. package/src/DBC/DEFINED.ts +117 -122
  100. package/src/DBC/DOM.ts +453 -0
  101. package/src/DBC/EQ/DIFFERENT.ts +51 -57
  102. package/src/DBC/EQ.ts +154 -163
  103. package/src/DBC/HasAttribute.ts +149 -154
  104. package/src/DBC/IF.ts +173 -179
  105. package/src/DBC/INSTANCE.ts +168 -171
  106. package/src/DBC/JSON.OP.ts +178 -186
  107. package/src/DBC/JSON.Parse.ts +150 -157
  108. package/src/DBC/OR.ts +183 -187
  109. package/src/DBC/REGEX.ts +195 -196
  110. package/src/DBC/TYPE.ts +142 -149
  111. package/src/DBC/UNDEFINED.ts +115 -117
  112. package/src/DBC/ZOD.ts +130 -135
  113. package/src/DBC.ts +902 -904
  114. package/src/Demo.ts +537 -404
  115. package/src/index.ts +22 -0
  116. package/tsconfig.json +18 -18
  117. package/tsconfig.test.json +7 -7
  118. package/typedoc.json +16 -16
  119. package/webpack.config.js +27 -27
  120. 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
+ });