xdbc 1.0.207 → 1.0.209
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/Assessment.html +350 -0
- package/Assessment.md +507 -51
- package/CHANGELOG.md +55 -0
- package/CONTRIBUTING.md +129 -17
- package/README.md +373 -71
- package/SECURITY.md +60 -18
- package/SUPPORT.md +65 -0
- package/__tests__/DBC/DEFINED.test.ts +53 -0
- package/__tests__/DBC/Decorators.test.ts +365 -0
- package/__tests__/DBC/GREATER.test.ts +8 -6
- package/__tests__/DBC/HasAttribute.test.ts +56 -0
- package/__tests__/DBC/IF.test.ts +52 -0
- package/__tests__/DBC/JSON.Parse.test.ts +1 -1
- package/__tests__/DBC/OR.test.ts +1 -1
- package/__tests__/DBC/REGEX.test.ts +1 -1
- package/__tests__/DBC/TYPE.test.ts +1 -1
- package/__tests__/DBC/UNDEFINED.test.ts +45 -0
- package/__tests__/DBC/ZOD.test.ts +54 -0
- package/jest.config.js +21 -0
- package/package.json +4 -5
- package/src/DBC/AE.ts +10 -6
- package/src/DBC/COMPARISON/GREATER.ts +11 -7
- package/src/DBC/COMPARISON/GREATER_OR_EQUAL.ts +14 -10
- package/src/DBC/COMPARISON/LESS.ts +14 -10
- package/src/DBC/COMPARISON/LESS_OR_EQUAL.ts +14 -10
- package/src/DBC/COMPARISON.ts +20 -43
- package/src/DBC/DEFINED.ts +4 -23
- package/src/DBC/EQ/DIFFERENT.ts +21 -56
- package/src/DBC/EQ.ts +7 -26
- package/src/DBC/HasAttribute.ts +9 -26
- package/src/DBC/IF.ts +8 -27
- package/src/DBC/INSTANCE.ts +5 -22
- package/src/DBC/JSON.OP.ts +4 -34
- package/src/DBC/JSON.Parse.ts +5 -25
- package/src/DBC/OR.ts +5 -14
- package/src/DBC/REGEX.ts +41 -40
- package/src/DBC/TYPE.ts +6 -25
- package/src/DBC/UNDEFINED.ts +3 -22
- package/src/DBC/ZOD.ts +10 -27
- package/src/DBC.ts +223 -55
- package/tsconfig.json +7 -4
- package/tsconfig.test.json +12 -0
- package/.parcel-cache/bf96c58b6061a62a-BundleGraph +0 -0
- package/.parcel-cache/d7c812d65aeeac59-AssetGraph +0 -0
- package/.parcel-cache/data.mdb +0 -0
- package/.parcel-cache/e81759c1f106a17f-RequestGraph +0 -0
- package/.parcel-cache/fe0db3c4eb428be2-AssetGraph +0 -0
- package/.parcel-cache/lock.mdb +0 -0
- package/.parcel-cache/snapshot-e81759c1f106a17f.txt +0 -4609
- package/dist/DBC/AE.js +0 -173
- package/dist/DBC/COMPARISON/GREATER.js +0 -21
- package/dist/DBC/COMPARISON/GREATER_OR_EQUAL.js +0 -21
- package/dist/DBC/COMPARISON/LESS.js +0 -21
- package/dist/DBC/COMPARISON/LESS_OR_EQUAL.js +0 -21
- package/dist/DBC/COMPARISON.js +0 -99
- package/dist/DBC/DEFINED.js +0 -99
- package/dist/DBC/EQ/DIFFERENT.js +0 -21
- package/dist/DBC/EQ.js +0 -100
- package/dist/DBC/GREATER.js +0 -99
- package/dist/DBC/HasAttribute.js +0 -108
- package/dist/DBC/IF.js +0 -99
- package/dist/DBC/INSTANCE.js +0 -93
- package/dist/DBC/JSON.OP.js +0 -133
- package/dist/DBC/JSON.Parse.js +0 -114
- package/dist/DBC/OR.js +0 -113
- package/dist/DBC/REGEX.js +0 -110
- package/dist/DBC/TYPE.js +0 -87
- package/dist/DBC/ZOD.js +0 -114
- package/dist/DBC.js +0 -336
- package/dist/Demo.js +0 -290
- package/dist/Test.html +0 -18
- package/dist/bundle.js +0 -2064
- package/dist/index.html +0 -18
- package/jest.config.ts +0 -20
- package/xpackage-lock.json +0 -122
package/SECURITY.md
CHANGED
|
@@ -1,36 +1,78 @@
|
|
|
1
1
|
# Security Policy
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## Supported Versions
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
| Version | Supported |
|
|
6
|
+
|---|---|
|
|
7
|
+
| 1.0.x (latest) | Yes |
|
|
8
|
+
| < 1.0.0 | No |
|
|
6
9
|
|
|
7
|
-
|
|
10
|
+
Only the latest published version on npm receives security patches. Users are encouraged to stay up to date.
|
|
8
11
|
|
|
9
|
-
|
|
12
|
+
---
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
* Steps to reproduce
|
|
13
|
-
* Affected components/versions
|
|
14
|
-
* Potential impact
|
|
14
|
+
## Reporting a Vulnerability
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
**Do not open a public GitHub issue for security vulnerabilities.**
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
To report a security concern, please email **[Security@WaXCode.net](mailto:Security@WaXCode.net)** with the following details:
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
- Description of the vulnerability
|
|
21
|
+
- Steps to reproduce or a proof of concept
|
|
22
|
+
- Affected component(s) and version(s)
|
|
23
|
+
- Potential impact and severity assessment
|
|
24
|
+
- Suggested fix, if any
|
|
21
25
|
|
|
22
|
-
|
|
26
|
+
### Response Timeline
|
|
23
27
|
|
|
24
|
-
|
|
28
|
+
| Stage | Timeframe |
|
|
29
|
+
|---|---|
|
|
30
|
+
| Acknowledgment of report | Within 48 hours |
|
|
31
|
+
| Initial triage and severity assessment | Within 5 business days |
|
|
32
|
+
| Patch development and internal testing | Dependent on severity |
|
|
33
|
+
| Public disclosure (after fix is available) | Coordinated with reporter |
|
|
25
34
|
|
|
26
|
-
|
|
35
|
+
---
|
|
27
36
|
|
|
28
|
-
|
|
37
|
+
## Vulnerability Handling Process
|
|
38
|
+
|
|
39
|
+
1. **Triage** — The report is reviewed, reproduced, and assigned a severity level (Critical / High / Medium / Low).
|
|
40
|
+
2. **Fix** — A patch is developed and tested against the current release.
|
|
41
|
+
3. **Release** — A patched version is published to npm.
|
|
42
|
+
4. **Disclosure** — A security advisory is published on GitHub after the fix is available. The reporter will be credited (with consent).
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Scope
|
|
47
|
+
|
|
48
|
+
This policy covers the XDBC npm package (`xdbc`) and its source code. It does **not** cover:
|
|
49
|
+
|
|
50
|
+
- Third-party dependencies (report those to the respective maintainers)
|
|
51
|
+
- The generated documentation site
|
|
52
|
+
- User applications that consume XDBC
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Security Best Practices for Users
|
|
57
|
+
|
|
58
|
+
- Keep XDBC updated to the latest version
|
|
59
|
+
- Run `npm audit` regularly to check for transitive dependency vulnerabilities
|
|
60
|
+
- Do not disable contract checking in security-sensitive paths without understanding the implications
|
|
61
|
+
- Avoid passing untrusted input directly to `path` resolution without validation
|
|
62
|
+
|
|
63
|
+
---
|
|
29
64
|
|
|
30
65
|
## Responsible Disclosure
|
|
31
66
|
|
|
32
|
-
We
|
|
67
|
+
We are committed to responsible disclosure and will not pursue legal action against individuals who:
|
|
68
|
+
|
|
69
|
+
- Report vulnerabilities in good faith
|
|
70
|
+
- Allow reasonable time for a fix before public disclosure
|
|
71
|
+
- Do not exploit the vulnerability beyond what is necessary to demonstrate it
|
|
72
|
+
|
|
73
|
+
---
|
|
33
74
|
|
|
34
|
-
##
|
|
75
|
+
## Contact
|
|
35
76
|
|
|
36
|
-
|
|
77
|
+
Security reports: **[Security@WaXCode.net](mailto:Security@WaXCode.net)**
|
|
78
|
+
General inquiries: **[XDBC@WaXCode.net](mailto:XDBC@WaXCode.net)**
|
package/SUPPORT.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Support
|
|
2
|
+
|
|
3
|
+
## Getting Help
|
|
4
|
+
|
|
5
|
+
If you need help using XDBC, there are several resources available:
|
|
6
|
+
|
|
7
|
+
### Documentation
|
|
8
|
+
|
|
9
|
+
- **[API Reference](https://callaris.github.io/XDBC/)** — Full generated API documentation
|
|
10
|
+
- **[README](README.md)** — Quick start guide, contracts reference, and configuration
|
|
11
|
+
- **[Demo.ts](src/Demo.ts)** — Annotated usage examples
|
|
12
|
+
|
|
13
|
+
### Community
|
|
14
|
+
|
|
15
|
+
- **[GitHub Discussions](https://github.com/CallariS/XDBC/discussions)** — Ask questions, share ideas, and connect with other users
|
|
16
|
+
- **[GitHub Issues](https://github.com/CallariS/XDBC/issues)** — Report bugs or request features
|
|
17
|
+
|
|
18
|
+
### Direct Contact
|
|
19
|
+
|
|
20
|
+
- **Email**: [XDBC@WaXCode.net](mailto:XDBC@WaXCode.net)
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Frequently Asked Questions
|
|
25
|
+
|
|
26
|
+
### How do I install XDBC?
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
npm install xdbc
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Ensure your `tsconfig.json` has `experimentalDecorators` and `emitDecoratorMetadata` enabled.
|
|
33
|
+
|
|
34
|
+
### Why do I need `@DBC.ParamvalueProvider`?
|
|
35
|
+
|
|
36
|
+
TypeScript parameter decorators cannot access the actual parameter values at runtime. The `@DBC.ParamvalueProvider` method decorator intercepts the method call and captures parameter values so that `PRE` contracts can validate them.
|
|
37
|
+
|
|
38
|
+
### Can I disable contract checking in production?
|
|
39
|
+
|
|
40
|
+
Yes. Access the DBC instance and toggle execution settings:
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
const dbc = (globalThis as any).WaXCode.DBC;
|
|
44
|
+
dbc.executionSettings.checkPreconditions = false;
|
|
45
|
+
dbc.executionSettings.checkPostconditions = false;
|
|
46
|
+
dbc.executionSettings.checkInvariants = false;
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Does XDBC work in Node.js?
|
|
50
|
+
|
|
51
|
+
Yes. XDBC uses `globalThis` for host resolution and works in both browser and Node.js environments.
|
|
52
|
+
|
|
53
|
+
### How do I report a security vulnerability?
|
|
54
|
+
|
|
55
|
+
Do not open a public issue. Email **[Security@WaXCode.net](mailto:Security@WaXCode.net)** directly. See [SECURITY.md](SECURITY.md) for the full policy.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Sponsoring
|
|
60
|
+
|
|
61
|
+
If XDBC is useful in your work, consider supporting its development:
|
|
62
|
+
|
|
63
|
+
- **[GitHub Sponsors](https://github.com/sponsors/CallariS)**
|
|
64
|
+
- **[Patreon](https://patreon.com/salvatorecallari)**
|
|
65
|
+
- **[PayPal](https://paypal.me/CallariS)**
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { DEFINED } from "../../src/DBC/DEFINED";
|
|
2
|
+
|
|
3
|
+
describe("DEFINED", () => {
|
|
4
|
+
const defined = new DEFINED();
|
|
5
|
+
|
|
6
|
+
test("Should not report infringement with a string value", () => {
|
|
7
|
+
expect(defined.check("hello")).toBe(true);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test("Should not report infringement with a number value", () => {
|
|
11
|
+
expect(defined.check(42)).toBe(true);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test("Should not report infringement with an empty string", () => {
|
|
15
|
+
expect(defined.check("")).toBe(true);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("Should not report infringement with zero", () => {
|
|
19
|
+
expect(defined.check(0)).toBe(true);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("Should not report infringement with false", () => {
|
|
23
|
+
expect(defined.check(false)).toBe(true);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("Should report infringement with null", () => {
|
|
27
|
+
expect(typeof defined.check(null)).toBe("string");
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("Should report infringement with undefined", () => {
|
|
31
|
+
expect(typeof defined.check(undefined)).toBe("string");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe("checkAlgorithm", () => {
|
|
35
|
+
test("Should return true for defined values", () => {
|
|
36
|
+
expect(DEFINED.checkAlgorithm("test")).toBe(true);
|
|
37
|
+
expect(DEFINED.checkAlgorithm(0)).toBe(true);
|
|
38
|
+
expect(DEFINED.checkAlgorithm(false)).toBe(true);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("Should return string for null", () => {
|
|
42
|
+
const result = DEFINED.checkAlgorithm(null);
|
|
43
|
+
expect(typeof result).toBe("string");
|
|
44
|
+
expect(result).toContain("NULL");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("Should return string for undefined", () => {
|
|
48
|
+
const result = DEFINED.checkAlgorithm(undefined);
|
|
49
|
+
expect(typeof result).toBe("string");
|
|
50
|
+
expect(result).toContain("UNDEFINED");
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
});
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
import "reflect-metadata";
|
|
2
|
+
import { DBC } from "../../src/DBC";
|
|
3
|
+
import { REGEX } from "../../src/DBC/REGEX";
|
|
4
|
+
import { TYPE } from "../../src/DBC/TYPE";
|
|
5
|
+
import { EQ } from "../../src/DBC/EQ";
|
|
6
|
+
import { GREATER } from "../../src/DBC/COMPARISON/GREATER";
|
|
7
|
+
import { GREATER_OR_EQUAL } from "../../src/DBC/COMPARISON/GREATER_OR_EQUAL";
|
|
8
|
+
import { LESS } from "../../src/DBC/COMPARISON/LESS";
|
|
9
|
+
import { LESS_OR_EQUAL } from "../../src/DBC/COMPARISON/LESS_OR_EQUAL";
|
|
10
|
+
import { DIFFERENT } from "../../src/DBC/EQ/DIFFERENT";
|
|
11
|
+
import { INSTANCE } from "../../src/DBC/INSTANCE";
|
|
12
|
+
import { AE } from "../../src/DBC/AE";
|
|
13
|
+
import { OR } from "../../src/DBC/OR";
|
|
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(@DIFFERENT.PRE("forbidden", undefined) input: string): string {
|
|
57
|
+
return input;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
@DBC.ParamvalueProvider
|
|
61
|
+
public instancePre(@INSTANCE.PRE(Date) input: unknown): unknown {
|
|
62
|
+
return input;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@DBC.ParamvalueProvider
|
|
66
|
+
public aePre(@AE.PRE([new TYPE("string")]) input: unknown[]): unknown[] {
|
|
67
|
+
return input;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@DBC.ParamvalueProvider
|
|
71
|
+
public orPre(@OR.PRE([new EQ("a"), new EQ("b")]) input: string): string {
|
|
72
|
+
return input;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const subject = new PreTestSubject();
|
|
77
|
+
|
|
78
|
+
// REGEX.PRE
|
|
79
|
+
test("REGEX.PRE passes with matching value", () => {
|
|
80
|
+
expect(() => subject.regexPre("ABC")).not.toThrow();
|
|
81
|
+
});
|
|
82
|
+
test("REGEX.PRE throws on non-matching value", () => {
|
|
83
|
+
expect(() => subject.regexPre("abc123")).toThrow();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// TYPE.PRE
|
|
87
|
+
test("TYPE.PRE passes with correct type", () => {
|
|
88
|
+
expect(() => subject.typePre("hello")).not.toThrow();
|
|
89
|
+
});
|
|
90
|
+
test("TYPE.PRE throws with wrong type", () => {
|
|
91
|
+
expect(() => subject.typePre(42)).toThrow();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// EQ.PRE
|
|
95
|
+
test("EQ.PRE passes with equal value", () => {
|
|
96
|
+
expect(() => subject.eqPre("hello")).not.toThrow();
|
|
97
|
+
});
|
|
98
|
+
test("EQ.PRE throws with non-equal value", () => {
|
|
99
|
+
expect(() => subject.eqPre("world")).toThrow();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// GREATER.PRE
|
|
103
|
+
test("GREATER.PRE passes with value > reference", () => {
|
|
104
|
+
expect(() => subject.greaterPre(10)).not.toThrow();
|
|
105
|
+
});
|
|
106
|
+
test("GREATER.PRE throws with value <= reference", () => {
|
|
107
|
+
expect(() => subject.greaterPre(5)).toThrow();
|
|
108
|
+
});
|
|
109
|
+
test("GREATER.PRE throws with value < reference", () => {
|
|
110
|
+
expect(() => subject.greaterPre(3)).toThrow();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// GREATER_OR_EQUAL.PRE
|
|
114
|
+
test("GREATER_OR_EQUAL.PRE passes with value >= reference", () => {
|
|
115
|
+
expect(() => subject.greaterOrEqualPre(5)).not.toThrow();
|
|
116
|
+
});
|
|
117
|
+
test("GREATER_OR_EQUAL.PRE passes with value > reference", () => {
|
|
118
|
+
expect(() => subject.greaterOrEqualPre(10)).not.toThrow();
|
|
119
|
+
});
|
|
120
|
+
test("GREATER_OR_EQUAL.PRE throws with value < reference", () => {
|
|
121
|
+
expect(() => subject.greaterOrEqualPre(3)).toThrow();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// LESS.PRE
|
|
125
|
+
test("LESS.PRE passes with value < reference", () => {
|
|
126
|
+
expect(() => subject.lessPre(5)).not.toThrow();
|
|
127
|
+
});
|
|
128
|
+
test("LESS.PRE throws with value >= reference", () => {
|
|
129
|
+
expect(() => subject.lessPre(10)).toThrow();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// LESS_OR_EQUAL.PRE
|
|
133
|
+
test("LESS_OR_EQUAL.PRE passes with value <= reference", () => {
|
|
134
|
+
expect(() => subject.lessOrEqualPre(10)).not.toThrow();
|
|
135
|
+
});
|
|
136
|
+
test("LESS_OR_EQUAL.PRE throws with value > reference", () => {
|
|
137
|
+
expect(() => subject.lessOrEqualPre(15)).toThrow();
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// DIFFERENT.PRE
|
|
141
|
+
test("DIFFERENT.PRE passes with different value", () => {
|
|
142
|
+
expect(() => subject.differentPre("allowed")).not.toThrow();
|
|
143
|
+
});
|
|
144
|
+
test("DIFFERENT.PRE throws with equal value", () => {
|
|
145
|
+
expect(() => subject.differentPre("forbidden")).toThrow();
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// INSTANCE.PRE
|
|
149
|
+
test("INSTANCE.PRE passes with correct instance", () => {
|
|
150
|
+
expect(() => subject.instancePre(new Date())).not.toThrow();
|
|
151
|
+
});
|
|
152
|
+
test("INSTANCE.PRE throws with wrong instance", () => {
|
|
153
|
+
expect(() => subject.instancePre("not a date")).toThrow();
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// AE.PRE
|
|
157
|
+
test("AE.PRE passes with all elements matching", () => {
|
|
158
|
+
expect(() => subject.aePre(["a", "b", "c"])).not.toThrow();
|
|
159
|
+
});
|
|
160
|
+
test("AE.PRE throws when an element does not match", () => {
|
|
161
|
+
expect(() => subject.aePre(["a", 42, "c"])).toThrow();
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// OR.PRE
|
|
165
|
+
test("OR.PRE passes when one contract is satisfied", () => {
|
|
166
|
+
expect(() => subject.orPre("a")).not.toThrow();
|
|
167
|
+
});
|
|
168
|
+
test("OR.PRE throws when no contract is satisfied", () => {
|
|
169
|
+
expect(() => subject.orPre("c")).toThrow();
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
describe("Decorator: @POST (Postconditions)", () => {
|
|
174
|
+
class PostTestSubject {
|
|
175
|
+
@REGEX.POST(/^OK:.*$/)
|
|
176
|
+
@DBC.ParamvalueProvider
|
|
177
|
+
public formatResponse(@TYPE.PRE("string") input: string): string {
|
|
178
|
+
return `OK:${input}`;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
@REGEX.POST(/^OK:.*$/)
|
|
182
|
+
public failingPost(): string {
|
|
183
|
+
return "FAIL";
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
@EQ.POST("hello")
|
|
187
|
+
public eqPost(returnThis: string): string {
|
|
188
|
+
return returnThis;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const subject = new PostTestSubject();
|
|
193
|
+
|
|
194
|
+
test("POST passes when return value matches", () => {
|
|
195
|
+
expect(() => subject.formatResponse("test")).not.toThrow();
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
test("POST throws when return value does not match", () => {
|
|
199
|
+
expect(() => subject.failingPost()).toThrow();
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test("EQ.POST passes with matching return value", () => {
|
|
203
|
+
expect(() => subject.eqPost("hello")).not.toThrow();
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
test("EQ.POST throws with non-matching return value", () => {
|
|
207
|
+
expect(() => subject.eqPost("world")).toThrow();
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
describe("Decorator: @INVARIANT (Field contracts)", () => {
|
|
212
|
+
test("INVARIANT allows valid initial value", () => {
|
|
213
|
+
expect(() => {
|
|
214
|
+
class InvariantSubject {
|
|
215
|
+
@REGEX.INVARIANT(/^[A-Z]+$/)
|
|
216
|
+
public code = "ABC";
|
|
217
|
+
}
|
|
218
|
+
new InvariantSubject();
|
|
219
|
+
}).not.toThrow();
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
test("INVARIANT throws on invalid initial value", () => {
|
|
223
|
+
expect(() => {
|
|
224
|
+
class InvariantSubject {
|
|
225
|
+
@REGEX.INVARIANT(/^[A-Z]+$/)
|
|
226
|
+
public code = "abc123";
|
|
227
|
+
}
|
|
228
|
+
new InvariantSubject();
|
|
229
|
+
}).toThrow();
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
test("INVARIANT throws on invalid reassignment", () => {
|
|
233
|
+
class InvariantSubject {
|
|
234
|
+
@REGEX.INVARIANT(/^[A-Z]+$/)
|
|
235
|
+
public code = "ABC";
|
|
236
|
+
}
|
|
237
|
+
const obj = new InvariantSubject();
|
|
238
|
+
expect(() => {
|
|
239
|
+
obj.code = "invalid!";
|
|
240
|
+
}).toThrow();
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
test("INVARIANT allows valid reassignment", () => {
|
|
244
|
+
class InvariantSubject {
|
|
245
|
+
@REGEX.INVARIANT(/^[A-Z]+$/)
|
|
246
|
+
public code = "ABC";
|
|
247
|
+
}
|
|
248
|
+
const obj = new InvariantSubject();
|
|
249
|
+
expect(() => {
|
|
250
|
+
obj.code = "XYZ";
|
|
251
|
+
}).not.toThrow();
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
describe("Decorator: @ParamvalueProvider", () => {
|
|
256
|
+
test("ParamvalueProvider passes multiple parameter contracts", () => {
|
|
257
|
+
class MultiParam {
|
|
258
|
+
@DBC.ParamvalueProvider
|
|
259
|
+
public method(
|
|
260
|
+
@TYPE.PRE("string") a: string,
|
|
261
|
+
@TYPE.PRE("number") b: number,
|
|
262
|
+
): string {
|
|
263
|
+
return `${a}:${b}`;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
const obj = new MultiParam();
|
|
267
|
+
expect(() => obj.method("hello", 42)).not.toThrow();
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
test("ParamvalueProvider catches second parameter violation", () => {
|
|
271
|
+
class MultiParam {
|
|
272
|
+
@DBC.ParamvalueProvider
|
|
273
|
+
public method(
|
|
274
|
+
@TYPE.PRE("string") a: string,
|
|
275
|
+
@TYPE.PRE("number") b: number,
|
|
276
|
+
): string {
|
|
277
|
+
return `${a}:${b}`;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
const obj = new MultiParam();
|
|
281
|
+
expect(() => obj.method("hello", "not a number" as any)).toThrow();
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
describe("DBC infringement settings", () => {
|
|
286
|
+
class InfringementTest {
|
|
287
|
+
@DBC.ParamvalueProvider
|
|
288
|
+
public method(@TYPE.PRE("string") input: unknown): unknown {
|
|
289
|
+
return input;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
test("Violations throw DBC.Infringement by default", () => {
|
|
294
|
+
const obj = new InfringementTest();
|
|
295
|
+
expect(() => obj.method(42)).toThrow(/XDBC Infringement/);
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
test("Error message includes class name, method name, and parameter info", () => {
|
|
299
|
+
const obj = new InfringementTest();
|
|
300
|
+
try {
|
|
301
|
+
obj.method(42);
|
|
302
|
+
fail("Should have thrown");
|
|
303
|
+
} catch (e: any) {
|
|
304
|
+
expect(e.message).toContain("InfringementTest");
|
|
305
|
+
expect(e.message).toContain("method");
|
|
306
|
+
expect(e.message).toContain("1st parameter");
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
describe("DBC.register()", () => {
|
|
312
|
+
test("registers an instance at the default path", () => {
|
|
313
|
+
const custom = new DBC();
|
|
314
|
+
DBC.register(custom);
|
|
315
|
+
expect((window as any).WaXCode.DBC).toBe(custom);
|
|
316
|
+
// Restore original
|
|
317
|
+
DBC.register(dbc);
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
test("registers an instance at a custom path", () => {
|
|
321
|
+
const custom = new DBC();
|
|
322
|
+
DBC.register(custom, "TestVendor.DBC");
|
|
323
|
+
expect((window as any).TestVendor.DBC).toBe(custom);
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
test("constructor does not auto-mount to globalThis", () => {
|
|
327
|
+
const original = (window as any).WaXCode.DBC;
|
|
328
|
+
const orphan = new DBC();
|
|
329
|
+
// Constructor should NOT have replaced the registered instance
|
|
330
|
+
expect((window as any).WaXCode.DBC).toBe(original);
|
|
331
|
+
expect(orphan).not.toBe(original);
|
|
332
|
+
});
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
describe("DBC.isolated()", () => {
|
|
336
|
+
test("provides a temporary DBC instance and restores the original", () => {
|
|
337
|
+
const original = (window as any).WaXCode.DBC;
|
|
338
|
+
let isolatedInstance: DBC | undefined;
|
|
339
|
+
DBC.isolated((tempDbc) => {
|
|
340
|
+
isolatedInstance = tempDbc;
|
|
341
|
+
expect(tempDbc).not.toBe(original);
|
|
342
|
+
expect((window as any).WaXCode.DBC).toBe(tempDbc);
|
|
343
|
+
});
|
|
344
|
+
// After isolated() returns, the original is restored
|
|
345
|
+
expect((window as any).WaXCode.DBC).toBe(original);
|
|
346
|
+
expect(isolatedInstance).toBeDefined();
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
test("restores original even if callback throws", () => {
|
|
350
|
+
const original = (window as any).WaXCode.DBC;
|
|
351
|
+
expect(() => {
|
|
352
|
+
DBC.isolated(() => {
|
|
353
|
+
throw new Error("test error");
|
|
354
|
+
});
|
|
355
|
+
}).toThrow("test error");
|
|
356
|
+
expect((window as any).WaXCode.DBC).toBe(original);
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
test("isolated instance has independent settings", () => {
|
|
360
|
+
DBC.isolated((tempDbc) => {
|
|
361
|
+
tempDbc.executionSettings.checkPreconditions = false;
|
|
362
|
+
expect(dbc.executionSettings.checkPreconditions).toBe(true);
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
});
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { GREATER } from "../../src/DBC/GREATER";
|
|
1
|
+
import { GREATER } from "../../src/DBC/COMPARISON/GREATER";
|
|
2
|
+
import { GREATER_OR_EQUAL } from "../../src/DBC/COMPARISON/GREATER_OR_EQUAL";
|
|
3
|
+
import { LESS_OR_EQUAL } from "../../src/DBC/COMPARISON/LESS_OR_EQUAL";
|
|
2
4
|
|
|
3
5
|
describe("GREATER", () => {
|
|
4
6
|
const greater = new GREATER(1);
|
|
@@ -11,16 +13,16 @@ describe("GREATER", () => {
|
|
|
11
13
|
expect(typeof greater.check(0)).toBe("string");
|
|
12
14
|
});
|
|
13
15
|
|
|
14
|
-
test("Should report infringement with '
|
|
15
|
-
expect(typeof greater.check(
|
|
16
|
+
test("Should report infringement with '1' to check (equality not permitted)", () => {
|
|
17
|
+
expect(typeof greater.check(1)).toBe("string");
|
|
16
18
|
});
|
|
17
19
|
|
|
18
20
|
test("Should not report infringement with '1' and '1' to check since equality is now permitted", () => {
|
|
19
|
-
expect(new
|
|
21
|
+
expect(new GREATER_OR_EQUAL(1).check(1)).toBe(true);
|
|
20
22
|
});
|
|
21
23
|
|
|
22
|
-
test("Should not report infringement with '
|
|
23
|
-
expect(new
|
|
24
|
+
test("Should not report infringement with '1' and '1' to check since inverting the result is now on", () => {
|
|
25
|
+
expect(new LESS_OR_EQUAL(1).check(1)).toBe(true);
|
|
24
26
|
});
|
|
25
27
|
|
|
26
28
|
test("Should not report infringement with 'undefined' and 'undefined' to check", () => {
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { HasAttribute } from "../../src/DBC/HasAttribute";
|
|
2
|
+
|
|
3
|
+
describe("HasAttribute", () => {
|
|
4
|
+
const hasId = new HasAttribute("id");
|
|
5
|
+
|
|
6
|
+
test("Should not report infringement when attribute exists", () => {
|
|
7
|
+
const el = document.createElement("div");
|
|
8
|
+
el.setAttribute("id", "test");
|
|
9
|
+
expect(hasId.check(el)).toBe(true);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test("Should report infringement when attribute is missing", () => {
|
|
13
|
+
const el = document.createElement("div");
|
|
14
|
+
expect(typeof hasId.check(el)).toBe("string");
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("Should report infringement when value is not an HTMLElement", () => {
|
|
18
|
+
expect(typeof hasId.check("not an element")).toBe("string");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("Should report infringement when value is a plain object", () => {
|
|
22
|
+
expect(typeof hasId.check({ id: "test" })).toBe("string");
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe("invert", () => {
|
|
26
|
+
const noId = new HasAttribute("id", true);
|
|
27
|
+
|
|
28
|
+
test("Should not report infringement when attribute is absent", () => {
|
|
29
|
+
const el = document.createElement("div");
|
|
30
|
+
expect(noId.check(el)).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("Should report infringement when forbidden attribute exists", () => {
|
|
34
|
+
const el = document.createElement("div");
|
|
35
|
+
el.setAttribute("id", "test");
|
|
36
|
+
expect(typeof noId.check(el)).toBe("string");
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe("checkAlgorithm", () => {
|
|
41
|
+
test("Should return true when element has the attribute", () => {
|
|
42
|
+
const el = document.createElement("span");
|
|
43
|
+
el.setAttribute("class", "active");
|
|
44
|
+
expect(HasAttribute.checkAlgorithm(el, "class", false)).toBe(true);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("Should return string when element lacks the attribute", () => {
|
|
48
|
+
const el = document.createElement("span");
|
|
49
|
+
expect(typeof HasAttribute.checkAlgorithm(el, "class", false)).toBe("string");
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("Should return string for non-HTMLElement", () => {
|
|
53
|
+
expect(typeof HasAttribute.checkAlgorithm(42, "id", false)).toBe("string");
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
});
|