dal-engine-core-js-lib-dev 0.0.0 → 0.0.1
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/index.cjs +171 -39
- package/dist/index.esm.js +171 -39
- package/package.json +1 -1
- package/src/BehavioralControlGraph/BehavioralControlGraph.js +15 -1
- package/src/DALEngine.js +7 -10
- package/src/Errors/MissingAttributes.js +15 -0
- package/src/Members/Behavior.js +38 -15
- package/src/Members/Invariant.js +68 -9
- package/src/Members/Participant.js +38 -6
- package/tests/DALEngine.test.js +24 -4
- package/tests/Invariant.test.js +9 -0
- package/tests/SimpleDesign.test.js +41 -0
package/dist/index.cjs
CHANGED
|
@@ -54,6 +54,18 @@ ENGINE_TYPES = Object.freeze(ENGINE_TYPES);
|
|
|
54
54
|
|
|
55
55
|
var ENGINE_TYPES$1 = ENGINE_TYPES;
|
|
56
56
|
|
|
57
|
+
class MissingAttributes extends DALEngineError {
|
|
58
|
+
constructor (type, attribute) {
|
|
59
|
+
let msg;
|
|
60
|
+
if (Array.isArray(attribute) && attribute.length > 1) {
|
|
61
|
+
msg = `"${type}" must be initialized with the attributes "${attribute}".`;
|
|
62
|
+
} else {
|
|
63
|
+
msg = `"${type}" must be initialized with the attribute "${attribute}".`;
|
|
64
|
+
}
|
|
65
|
+
super(msg);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
57
69
|
/**
|
|
58
70
|
* Class representing a Invariant in the design.
|
|
59
71
|
*/
|
|
@@ -67,14 +79,32 @@ class Invariant extends Base {
|
|
|
67
79
|
super();
|
|
68
80
|
this.type = ENGINE_TYPES$1.INVARIANT;
|
|
69
81
|
this.invariantViolated = false;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
82
|
+
this.invariantType = null;
|
|
83
|
+
this.traceId = null;
|
|
84
|
+
if (typeof args === "object" && Object.hasOwn(args, "uid")) {
|
|
85
|
+
this.loadInvariantFromJSON(args);
|
|
86
|
+
} else {
|
|
87
|
+
this.loadArgs(args);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Loads the provided arguments.
|
|
93
|
+
* @throws {MissingAttributes} Thrown when required attr is not present.
|
|
94
|
+
* @param {Object} args
|
|
95
|
+
*/
|
|
96
|
+
loadArgs (args) {
|
|
97
|
+
const expectedAttributes = ["name", "rule"];
|
|
98
|
+
if (typeof args !== "object" || args === null || Array.isArray(args)) {
|
|
99
|
+
// Not an object, so all attributes are missing.
|
|
100
|
+
throw new MissingAttributes("Invariant", expectedAttributes);
|
|
77
101
|
}
|
|
102
|
+
expectedAttributes.forEach((attr) => {
|
|
103
|
+
if (!(attr in args)) {
|
|
104
|
+
throw new MissingAttributes("Invariant", attr);
|
|
105
|
+
}
|
|
106
|
+
this[attr] = args[attr];
|
|
107
|
+
});
|
|
78
108
|
}
|
|
79
109
|
|
|
80
110
|
/**
|
|
@@ -109,13 +139,52 @@ class Invariant extends Base {
|
|
|
109
139
|
enforceMinLength (value) {
|
|
110
140
|
if ("keys" in this.rule) {
|
|
111
141
|
for (let i = 0; i < this.rule["keys"].length; i++) {
|
|
112
|
-
console.log(this.rule["keys"][i], value);
|
|
113
142
|
value = value[this.rule["keys"][i]];
|
|
114
143
|
}
|
|
115
144
|
} if (value === null || typeof value !== "string" || value.length < this.rule.value) {
|
|
116
145
|
this.invariantViolated = true;
|
|
117
146
|
}
|
|
118
147
|
}
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Sets the invariant type.
|
|
152
|
+
*
|
|
153
|
+
* The two types of invariants are: Intrinsic and Substrate
|
|
154
|
+
*
|
|
155
|
+
* Substrate invariants are learnt by the design from the
|
|
156
|
+
* environment.
|
|
157
|
+
*
|
|
158
|
+
* Intrinsic invariants are arrived at naturally from the
|
|
159
|
+
* designs control flow, data dependencies and semantic assumptions.
|
|
160
|
+
*
|
|
161
|
+
* @param {String} invariantType
|
|
162
|
+
*/
|
|
163
|
+
setInvariantType (invariantType) {
|
|
164
|
+
// TODO: Add validation for invariantType.
|
|
165
|
+
this.invariantType = invariantType;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* This function adds the trace id which can be used for
|
|
170
|
+
* automated testing.
|
|
171
|
+
*
|
|
172
|
+
* Substrate invariants correspond to a trace that represents
|
|
173
|
+
* an environment that reveals a limitation of the substrate,
|
|
174
|
+
* thus motivating the invariant. It is also the environment
|
|
175
|
+
* in which an implementation can prove that it respects this
|
|
176
|
+
* invariant.
|
|
177
|
+
*
|
|
178
|
+
* Intrisinc invariants correspond to a trace that represents
|
|
179
|
+
* a factory default environment that enables the implementation
|
|
180
|
+
* to prove that it respects the invariant.
|
|
181
|
+
*
|
|
182
|
+
* @param {String} traceId ID of trace used for automated testing.
|
|
183
|
+
*/
|
|
184
|
+
setTraceId (traceId) {
|
|
185
|
+
// TODO: Add validation for traceId.
|
|
186
|
+
this.traceId = traceId;
|
|
187
|
+
}
|
|
119
188
|
}
|
|
120
189
|
|
|
121
190
|
/**
|
|
@@ -131,14 +200,32 @@ class Participant extends Base {
|
|
|
131
200
|
super();
|
|
132
201
|
this.type = ENGINE_TYPES$1.INVARIANT;
|
|
133
202
|
this.invariants = [];
|
|
203
|
+
this.abstractionId = null;
|
|
134
204
|
this.invariantViolated = false;
|
|
135
|
-
if (typeof args === "object" && args
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
205
|
+
if (typeof args === "object" && Object.hasOwn(args, "uid")) {
|
|
206
|
+
this.loadParticipantFromJSON(args);
|
|
207
|
+
} else {
|
|
208
|
+
this.loadArgs(args);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Loads the provided arguments.
|
|
214
|
+
* @throws {MissingAttributes} Thrown when required attr is not present.
|
|
215
|
+
* @param {Object} args
|
|
216
|
+
*/
|
|
217
|
+
loadArgs (args) {
|
|
218
|
+
const expectedAttributes = ["name"];
|
|
219
|
+
if (typeof args !== "object" || args === null || Array.isArray(args)) {
|
|
220
|
+
// Not an object, so all attributes are missing.
|
|
221
|
+
throw new MissingAttributes("Participant", expectedAttributes);
|
|
141
222
|
}
|
|
223
|
+
expectedAttributes.forEach((attr) => {
|
|
224
|
+
if (!(attr in args)) {
|
|
225
|
+
throw new MissingAttributes("Participant", attr);
|
|
226
|
+
}
|
|
227
|
+
this[attr] = args[attr];
|
|
228
|
+
});
|
|
142
229
|
}
|
|
143
230
|
|
|
144
231
|
/**
|
|
@@ -187,6 +274,18 @@ class Participant extends Base {
|
|
|
187
274
|
}
|
|
188
275
|
return this.invariantViolated;
|
|
189
276
|
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Map the abstraction ID from the execution to the participant.
|
|
280
|
+
*
|
|
281
|
+
* This abstraction id will be used to assign a value to the
|
|
282
|
+
* participant from the execution using the logged abstraction id.
|
|
283
|
+
*
|
|
284
|
+
* @param {String} abstractionId
|
|
285
|
+
*/
|
|
286
|
+
mapAbstraction (abstractionId) {
|
|
287
|
+
this.abstractionId = abstractionId;
|
|
288
|
+
}
|
|
190
289
|
}
|
|
191
290
|
|
|
192
291
|
/**
|
|
@@ -202,14 +301,32 @@ class Behavior extends Base {
|
|
|
202
301
|
super();
|
|
203
302
|
this.type = ENGINE_TYPES$1.BEHAVIOR;
|
|
204
303
|
this.participants = [];
|
|
304
|
+
this.abstractionIds = [];
|
|
205
305
|
this.invalidWorldState = false;
|
|
206
|
-
if (typeof args === "object" && args
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
306
|
+
if (typeof args === "object" && Object.hasOwn(args, "uid")) {
|
|
307
|
+
this.loadBehaviorFromJSON(args);
|
|
308
|
+
} else {
|
|
309
|
+
this.loadArgs(args);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Loads the provided arguments.
|
|
315
|
+
* @throws {MissingAttributes} Thrown when required attr is not present.
|
|
316
|
+
* @param {Object} args
|
|
317
|
+
*/
|
|
318
|
+
loadArgs (args) {
|
|
319
|
+
const expectedAttributes = ["name"];
|
|
320
|
+
if (typeof args !== "object" || args === null || Array.isArray(args)) {
|
|
321
|
+
// Not an object, so all attributes are missing.
|
|
322
|
+
throw new MissingAttributes("Behavior", expectedAttributes);
|
|
212
323
|
}
|
|
324
|
+
expectedAttributes.forEach((attr) => {
|
|
325
|
+
if (!(attr in args)) {
|
|
326
|
+
throw new MissingAttributes("Behavior", attr);
|
|
327
|
+
}
|
|
328
|
+
this[attr] = args[attr];
|
|
329
|
+
});
|
|
213
330
|
}
|
|
214
331
|
|
|
215
332
|
/**
|
|
@@ -241,17 +358,21 @@ class Behavior extends Base {
|
|
|
241
358
|
* @param {*} value
|
|
242
359
|
*/
|
|
243
360
|
setParticipantValue (participantName, value) {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
if (violation) {
|
|
250
|
-
this.invalidWorldState = true;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
361
|
+
const participant = this.participants.find(obj => obj.name === participantName);
|
|
362
|
+
participant.value = value;
|
|
363
|
+
const violation = participant.enforceInvariants();
|
|
364
|
+
if (violation) {
|
|
365
|
+
this.invalidWorldState = true;
|
|
253
366
|
}
|
|
254
367
|
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Maps the abstraction id from execution to the behavior.
|
|
371
|
+
* @param {String} abstractionId
|
|
372
|
+
*/
|
|
373
|
+
addMapping (abstractionId) {
|
|
374
|
+
this.abstractionIds.push(abstractionId);
|
|
375
|
+
}
|
|
255
376
|
}
|
|
256
377
|
|
|
257
378
|
/**
|
|
@@ -412,6 +533,20 @@ class BehavioralControlGraph extends Base {
|
|
|
412
533
|
throw new InvalidTransitionError(this.currentNode.behavior.name, nextBehaviorName);
|
|
413
534
|
}
|
|
414
535
|
}
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Exports the graph represented using mermaid syntax for visualization.
|
|
539
|
+
* @returns {String} Graph as mermaid diagram.
|
|
540
|
+
*/
|
|
541
|
+
exportAsMermaid () {
|
|
542
|
+
let mermaid = "flowchart TD\n";
|
|
543
|
+
this.nodes.forEach((node) => {
|
|
544
|
+
node.goToBehaviors.forEach((behavior) => {
|
|
545
|
+
mermaid += ` ${node.behavior.name} --> ${behavior.name}\n`;
|
|
546
|
+
});
|
|
547
|
+
});
|
|
548
|
+
return mermaid;
|
|
549
|
+
}
|
|
415
550
|
}
|
|
416
551
|
|
|
417
552
|
/**
|
|
@@ -419,9 +554,13 @@ class BehavioralControlGraph extends Base {
|
|
|
419
554
|
* abstraction language. It exposes functions
|
|
420
555
|
* configure the engine through the DAL specification.
|
|
421
556
|
*
|
|
422
|
-
* The
|
|
423
|
-
*
|
|
424
|
-
*
|
|
557
|
+
* The design specified in this engine is mapped onto
|
|
558
|
+
* the implementation using abstraction ids. The
|
|
559
|
+
* implementation is then instrumented and the resulting
|
|
560
|
+
* execution trace is fed back into the engine and is
|
|
561
|
+
* automatically debugged by transforming the execution
|
|
562
|
+
* into the behavior of the design and enforcing the
|
|
563
|
+
* invariants.
|
|
425
564
|
*/
|
|
426
565
|
class DALEngine {
|
|
427
566
|
constructor (args) {
|
|
@@ -454,9 +593,6 @@ class DALEngine {
|
|
|
454
593
|
* @returns {Participant}
|
|
455
594
|
*/
|
|
456
595
|
createParticipant (args) {
|
|
457
|
-
// TODO: Validate that the args have the necessary keys
|
|
458
|
-
// and raise custom error if they are missing.
|
|
459
|
-
// Perhaps it is better to do that in the class itself.
|
|
460
596
|
return new Participant(args);
|
|
461
597
|
}
|
|
462
598
|
|
|
@@ -466,8 +602,6 @@ class DALEngine {
|
|
|
466
602
|
* @returns {Behavior}
|
|
467
603
|
*/
|
|
468
604
|
createBehavior (args) {
|
|
469
|
-
// TODO: Validate that the args have the necessary keys
|
|
470
|
-
// and raise custom error if they are missing.
|
|
471
605
|
return new Behavior(args);
|
|
472
606
|
}
|
|
473
607
|
|
|
@@ -477,8 +611,6 @@ class DALEngine {
|
|
|
477
611
|
* @returns {Invariant}
|
|
478
612
|
*/
|
|
479
613
|
createInvariant (args) {
|
|
480
|
-
// TODO: Validate that the args have the necessary keys
|
|
481
|
-
// and raise custom error if they are missing.
|
|
482
614
|
return new Invariant(args);
|
|
483
615
|
}
|
|
484
616
|
}
|
package/dist/index.esm.js
CHANGED
|
@@ -52,6 +52,18 @@ ENGINE_TYPES$1 = Object.freeze(ENGINE_TYPES$1);
|
|
|
52
52
|
|
|
53
53
|
var ENGINE_TYPES = ENGINE_TYPES$1;
|
|
54
54
|
|
|
55
|
+
class MissingAttributes extends DALEngineError {
|
|
56
|
+
constructor (type, attribute) {
|
|
57
|
+
let msg;
|
|
58
|
+
if (Array.isArray(attribute) && attribute.length > 1) {
|
|
59
|
+
msg = `"${type}" must be initialized with the attributes "${attribute}".`;
|
|
60
|
+
} else {
|
|
61
|
+
msg = `"${type}" must be initialized with the attribute "${attribute}".`;
|
|
62
|
+
}
|
|
63
|
+
super(msg);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
55
67
|
/**
|
|
56
68
|
* Class representing a Invariant in the design.
|
|
57
69
|
*/
|
|
@@ -65,14 +77,32 @@ class Invariant extends Base {
|
|
|
65
77
|
super();
|
|
66
78
|
this.type = ENGINE_TYPES.INVARIANT;
|
|
67
79
|
this.invariantViolated = false;
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
80
|
+
this.invariantType = null;
|
|
81
|
+
this.traceId = null;
|
|
82
|
+
if (typeof args === "object" && Object.hasOwn(args, "uid")) {
|
|
83
|
+
this.loadInvariantFromJSON(args);
|
|
84
|
+
} else {
|
|
85
|
+
this.loadArgs(args);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Loads the provided arguments.
|
|
91
|
+
* @throws {MissingAttributes} Thrown when required attr is not present.
|
|
92
|
+
* @param {Object} args
|
|
93
|
+
*/
|
|
94
|
+
loadArgs (args) {
|
|
95
|
+
const expectedAttributes = ["name", "rule"];
|
|
96
|
+
if (typeof args !== "object" || args === null || Array.isArray(args)) {
|
|
97
|
+
// Not an object, so all attributes are missing.
|
|
98
|
+
throw new MissingAttributes("Invariant", expectedAttributes);
|
|
75
99
|
}
|
|
100
|
+
expectedAttributes.forEach((attr) => {
|
|
101
|
+
if (!(attr in args)) {
|
|
102
|
+
throw new MissingAttributes("Invariant", attr);
|
|
103
|
+
}
|
|
104
|
+
this[attr] = args[attr];
|
|
105
|
+
});
|
|
76
106
|
}
|
|
77
107
|
|
|
78
108
|
/**
|
|
@@ -107,13 +137,52 @@ class Invariant extends Base {
|
|
|
107
137
|
enforceMinLength (value) {
|
|
108
138
|
if ("keys" in this.rule) {
|
|
109
139
|
for (let i = 0; i < this.rule["keys"].length; i++) {
|
|
110
|
-
console.log(this.rule["keys"][i], value);
|
|
111
140
|
value = value[this.rule["keys"][i]];
|
|
112
141
|
}
|
|
113
142
|
} if (value === null || typeof value !== "string" || value.length < this.rule.value) {
|
|
114
143
|
this.invariantViolated = true;
|
|
115
144
|
}
|
|
116
145
|
}
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Sets the invariant type.
|
|
150
|
+
*
|
|
151
|
+
* The two types of invariants are: Intrinsic and Substrate
|
|
152
|
+
*
|
|
153
|
+
* Substrate invariants are learnt by the design from the
|
|
154
|
+
* environment.
|
|
155
|
+
*
|
|
156
|
+
* Intrinsic invariants are arrived at naturally from the
|
|
157
|
+
* designs control flow, data dependencies and semantic assumptions.
|
|
158
|
+
*
|
|
159
|
+
* @param {String} invariantType
|
|
160
|
+
*/
|
|
161
|
+
setInvariantType (invariantType) {
|
|
162
|
+
// TODO: Add validation for invariantType.
|
|
163
|
+
this.invariantType = invariantType;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* This function adds the trace id which can be used for
|
|
168
|
+
* automated testing.
|
|
169
|
+
*
|
|
170
|
+
* Substrate invariants correspond to a trace that represents
|
|
171
|
+
* an environment that reveals a limitation of the substrate,
|
|
172
|
+
* thus motivating the invariant. It is also the environment
|
|
173
|
+
* in which an implementation can prove that it respects this
|
|
174
|
+
* invariant.
|
|
175
|
+
*
|
|
176
|
+
* Intrisinc invariants correspond to a trace that represents
|
|
177
|
+
* a factory default environment that enables the implementation
|
|
178
|
+
* to prove that it respects the invariant.
|
|
179
|
+
*
|
|
180
|
+
* @param {String} traceId ID of trace used for automated testing.
|
|
181
|
+
*/
|
|
182
|
+
setTraceId (traceId) {
|
|
183
|
+
// TODO: Add validation for traceId.
|
|
184
|
+
this.traceId = traceId;
|
|
185
|
+
}
|
|
117
186
|
}
|
|
118
187
|
|
|
119
188
|
/**
|
|
@@ -129,14 +198,32 @@ class Participant extends Base {
|
|
|
129
198
|
super();
|
|
130
199
|
this.type = ENGINE_TYPES.INVARIANT;
|
|
131
200
|
this.invariants = [];
|
|
201
|
+
this.abstractionId = null;
|
|
132
202
|
this.invariantViolated = false;
|
|
133
|
-
if (typeof args === "object" && args
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
203
|
+
if (typeof args === "object" && Object.hasOwn(args, "uid")) {
|
|
204
|
+
this.loadParticipantFromJSON(args);
|
|
205
|
+
} else {
|
|
206
|
+
this.loadArgs(args);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Loads the provided arguments.
|
|
212
|
+
* @throws {MissingAttributes} Thrown when required attr is not present.
|
|
213
|
+
* @param {Object} args
|
|
214
|
+
*/
|
|
215
|
+
loadArgs (args) {
|
|
216
|
+
const expectedAttributes = ["name"];
|
|
217
|
+
if (typeof args !== "object" || args === null || Array.isArray(args)) {
|
|
218
|
+
// Not an object, so all attributes are missing.
|
|
219
|
+
throw new MissingAttributes("Participant", expectedAttributes);
|
|
139
220
|
}
|
|
221
|
+
expectedAttributes.forEach((attr) => {
|
|
222
|
+
if (!(attr in args)) {
|
|
223
|
+
throw new MissingAttributes("Participant", attr);
|
|
224
|
+
}
|
|
225
|
+
this[attr] = args[attr];
|
|
226
|
+
});
|
|
140
227
|
}
|
|
141
228
|
|
|
142
229
|
/**
|
|
@@ -185,6 +272,18 @@ class Participant extends Base {
|
|
|
185
272
|
}
|
|
186
273
|
return this.invariantViolated;
|
|
187
274
|
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Map the abstraction ID from the execution to the participant.
|
|
278
|
+
*
|
|
279
|
+
* This abstraction id will be used to assign a value to the
|
|
280
|
+
* participant from the execution using the logged abstraction id.
|
|
281
|
+
*
|
|
282
|
+
* @param {String} abstractionId
|
|
283
|
+
*/
|
|
284
|
+
mapAbstraction (abstractionId) {
|
|
285
|
+
this.abstractionId = abstractionId;
|
|
286
|
+
}
|
|
188
287
|
}
|
|
189
288
|
|
|
190
289
|
/**
|
|
@@ -200,14 +299,32 @@ class Behavior extends Base {
|
|
|
200
299
|
super();
|
|
201
300
|
this.type = ENGINE_TYPES.BEHAVIOR;
|
|
202
301
|
this.participants = [];
|
|
302
|
+
this.abstractionIds = [];
|
|
203
303
|
this.invalidWorldState = false;
|
|
204
|
-
if (typeof args === "object" && args
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
304
|
+
if (typeof args === "object" && Object.hasOwn(args, "uid")) {
|
|
305
|
+
this.loadBehaviorFromJSON(args);
|
|
306
|
+
} else {
|
|
307
|
+
this.loadArgs(args);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Loads the provided arguments.
|
|
313
|
+
* @throws {MissingAttributes} Thrown when required attr is not present.
|
|
314
|
+
* @param {Object} args
|
|
315
|
+
*/
|
|
316
|
+
loadArgs (args) {
|
|
317
|
+
const expectedAttributes = ["name"];
|
|
318
|
+
if (typeof args !== "object" || args === null || Array.isArray(args)) {
|
|
319
|
+
// Not an object, so all attributes are missing.
|
|
320
|
+
throw new MissingAttributes("Behavior", expectedAttributes);
|
|
210
321
|
}
|
|
322
|
+
expectedAttributes.forEach((attr) => {
|
|
323
|
+
if (!(attr in args)) {
|
|
324
|
+
throw new MissingAttributes("Behavior", attr);
|
|
325
|
+
}
|
|
326
|
+
this[attr] = args[attr];
|
|
327
|
+
});
|
|
211
328
|
}
|
|
212
329
|
|
|
213
330
|
/**
|
|
@@ -239,17 +356,21 @@ class Behavior extends Base {
|
|
|
239
356
|
* @param {*} value
|
|
240
357
|
*/
|
|
241
358
|
setParticipantValue (participantName, value) {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
if (violation) {
|
|
248
|
-
this.invalidWorldState = true;
|
|
249
|
-
}
|
|
250
|
-
}
|
|
359
|
+
const participant = this.participants.find(obj => obj.name === participantName);
|
|
360
|
+
participant.value = value;
|
|
361
|
+
const violation = participant.enforceInvariants();
|
|
362
|
+
if (violation) {
|
|
363
|
+
this.invalidWorldState = true;
|
|
251
364
|
}
|
|
252
365
|
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Maps the abstraction id from execution to the behavior.
|
|
369
|
+
* @param {String} abstractionId
|
|
370
|
+
*/
|
|
371
|
+
addMapping (abstractionId) {
|
|
372
|
+
this.abstractionIds.push(abstractionId);
|
|
373
|
+
}
|
|
253
374
|
}
|
|
254
375
|
|
|
255
376
|
/**
|
|
@@ -410,6 +531,20 @@ class BehavioralControlGraph extends Base {
|
|
|
410
531
|
throw new InvalidTransitionError(this.currentNode.behavior.name, nextBehaviorName);
|
|
411
532
|
}
|
|
412
533
|
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Exports the graph represented using mermaid syntax for visualization.
|
|
537
|
+
* @returns {String} Graph as mermaid diagram.
|
|
538
|
+
*/
|
|
539
|
+
exportAsMermaid () {
|
|
540
|
+
let mermaid = "flowchart TD\n";
|
|
541
|
+
this.nodes.forEach((node) => {
|
|
542
|
+
node.goToBehaviors.forEach((behavior) => {
|
|
543
|
+
mermaid += ` ${node.behavior.name} --> ${behavior.name}\n`;
|
|
544
|
+
});
|
|
545
|
+
});
|
|
546
|
+
return mermaid;
|
|
547
|
+
}
|
|
413
548
|
}
|
|
414
549
|
|
|
415
550
|
/**
|
|
@@ -417,9 +552,13 @@ class BehavioralControlGraph extends Base {
|
|
|
417
552
|
* abstraction language. It exposes functions
|
|
418
553
|
* configure the engine through the DAL specification.
|
|
419
554
|
*
|
|
420
|
-
* The
|
|
421
|
-
*
|
|
422
|
-
*
|
|
555
|
+
* The design specified in this engine is mapped onto
|
|
556
|
+
* the implementation using abstraction ids. The
|
|
557
|
+
* implementation is then instrumented and the resulting
|
|
558
|
+
* execution trace is fed back into the engine and is
|
|
559
|
+
* automatically debugged by transforming the execution
|
|
560
|
+
* into the behavior of the design and enforcing the
|
|
561
|
+
* invariants.
|
|
423
562
|
*/
|
|
424
563
|
class DALEngine {
|
|
425
564
|
constructor (args) {
|
|
@@ -452,9 +591,6 @@ class DALEngine {
|
|
|
452
591
|
* @returns {Participant}
|
|
453
592
|
*/
|
|
454
593
|
createParticipant (args) {
|
|
455
|
-
// TODO: Validate that the args have the necessary keys
|
|
456
|
-
// and raise custom error if they are missing.
|
|
457
|
-
// Perhaps it is better to do that in the class itself.
|
|
458
594
|
return new Participant(args);
|
|
459
595
|
}
|
|
460
596
|
|
|
@@ -464,8 +600,6 @@ class DALEngine {
|
|
|
464
600
|
* @returns {Behavior}
|
|
465
601
|
*/
|
|
466
602
|
createBehavior (args) {
|
|
467
|
-
// TODO: Validate that the args have the necessary keys
|
|
468
|
-
// and raise custom error if they are missing.
|
|
469
603
|
return new Behavior(args);
|
|
470
604
|
}
|
|
471
605
|
|
|
@@ -475,8 +609,6 @@ class DALEngine {
|
|
|
475
609
|
* @returns {Invariant}
|
|
476
610
|
*/
|
|
477
611
|
createInvariant (args) {
|
|
478
|
-
// TODO: Validate that the args have the necessary keys
|
|
479
|
-
// and raise custom error if they are missing.
|
|
480
612
|
return new Invariant(args);
|
|
481
613
|
}
|
|
482
614
|
}
|
package/package.json
CHANGED
|
@@ -106,6 +106,20 @@ class BehavioralControlGraph extends Base {
|
|
|
106
106
|
throw new InvalidTransitionError(this.currentNode.behavior.name, nextBehaviorName);
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
|
-
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Exports the graph represented using mermaid syntax for visualization.
|
|
112
|
+
* @returns {String} Graph as mermaid diagram.
|
|
113
|
+
*/
|
|
114
|
+
exportAsMermaid () {
|
|
115
|
+
let mermaid = "flowchart TD\n";
|
|
116
|
+
this.nodes.forEach((node) => {
|
|
117
|
+
node.goToBehaviors.forEach((behavior) => {
|
|
118
|
+
mermaid += ` ${node.behavior.name} --> ${behavior.name}\n`;
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
return mermaid;
|
|
122
|
+
}
|
|
123
|
+
};
|
|
110
124
|
|
|
111
125
|
export default BehavioralControlGraph;
|
package/src/DALEngine.js
CHANGED
|
@@ -8,9 +8,13 @@ import Participant from "./Members/Participant";
|
|
|
8
8
|
* abstraction language. It exposes functions
|
|
9
9
|
* configure the engine through the DAL specification.
|
|
10
10
|
*
|
|
11
|
-
* The
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* The design specified in this engine is mapped onto
|
|
12
|
+
* the implementation using abstraction ids. The
|
|
13
|
+
* implementation is then instrumented and the resulting
|
|
14
|
+
* execution trace is fed back into the engine and is
|
|
15
|
+
* automatically debugged by transforming the execution
|
|
16
|
+
* into the behavior of the design and enforcing the
|
|
17
|
+
* invariants.
|
|
14
18
|
*/
|
|
15
19
|
export class DALEngine {
|
|
16
20
|
constructor (args) {
|
|
@@ -43,9 +47,6 @@ export class DALEngine {
|
|
|
43
47
|
* @returns {Participant}
|
|
44
48
|
*/
|
|
45
49
|
createParticipant (args) {
|
|
46
|
-
// TODO: Validate that the args have the necessary keys
|
|
47
|
-
// and raise custom error if they are missing.
|
|
48
|
-
// Perhaps it is better to do that in the class itself.
|
|
49
50
|
return new Participant(args);
|
|
50
51
|
}
|
|
51
52
|
|
|
@@ -55,8 +56,6 @@ export class DALEngine {
|
|
|
55
56
|
* @returns {Behavior}
|
|
56
57
|
*/
|
|
57
58
|
createBehavior (args) {
|
|
58
|
-
// TODO: Validate that the args have the necessary keys
|
|
59
|
-
// and raise custom error if they are missing.
|
|
60
59
|
return new Behavior(args);
|
|
61
60
|
}
|
|
62
61
|
|
|
@@ -66,8 +65,6 @@ export class DALEngine {
|
|
|
66
65
|
* @returns {Invariant}
|
|
67
66
|
*/
|
|
68
67
|
createInvariant (args) {
|
|
69
|
-
// TODO: Validate that the args have the necessary keys
|
|
70
|
-
// and raise custom error if they are missing.
|
|
71
68
|
return new Invariant(args);
|
|
72
69
|
}
|
|
73
70
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import DALEngineError from "./DALEngineError";
|
|
2
|
+
|
|
3
|
+
class MissingAttributes extends DALEngineError {
|
|
4
|
+
constructor (type, attribute) {
|
|
5
|
+
let msg;
|
|
6
|
+
if (Array.isArray(attribute) && attribute.length > 1) {
|
|
7
|
+
msg = `"${type}" must be initialized with the attributes "${attribute}".`
|
|
8
|
+
} else {
|
|
9
|
+
msg = `"${type}" must be initialized with the attribute "${attribute}".`
|
|
10
|
+
}
|
|
11
|
+
super(msg);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default MissingAttributes;
|
package/src/Members/Behavior.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Base from "../Base";
|
|
2
|
+
import MissingAttributes from "../Errors/MissingAttributes";
|
|
2
3
|
import ENGINE_TYPES from "../TYPES";
|
|
3
4
|
import Participant from "./Participant";
|
|
4
5
|
/**
|
|
@@ -14,16 +15,34 @@ class Behavior extends Base {
|
|
|
14
15
|
super();
|
|
15
16
|
this.type = ENGINE_TYPES.BEHAVIOR;
|
|
16
17
|
this.participants = [];
|
|
18
|
+
this.abstractionIds = [];
|
|
17
19
|
this.invalidWorldState = false;
|
|
18
|
-
if (typeof args === "object" && args
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
this.name = args.name;
|
|
23
|
-
}
|
|
20
|
+
if (typeof args === "object" && Object.hasOwn(args, "uid")) {
|
|
21
|
+
this.loadBehaviorFromJSON(args);
|
|
22
|
+
} else {
|
|
23
|
+
this.loadArgs(args);
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Loads the provided arguments.
|
|
29
|
+
* @throws {MissingAttributes} Thrown when required attr is not present.
|
|
30
|
+
* @param {Object} args
|
|
31
|
+
*/
|
|
32
|
+
loadArgs (args) {
|
|
33
|
+
const expectedAttributes = ["name"];
|
|
34
|
+
if (typeof args !== "object" || args === null || Array.isArray(args)) {
|
|
35
|
+
// Not an object, so all attributes are missing.
|
|
36
|
+
throw new MissingAttributes("Behavior", expectedAttributes);
|
|
37
|
+
}
|
|
38
|
+
expectedAttributes.forEach((attr) => {
|
|
39
|
+
if (!(attr in args)) {
|
|
40
|
+
throw new MissingAttributes("Behavior", attr);
|
|
41
|
+
}
|
|
42
|
+
this[attr] = args[attr];
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
27
46
|
/**
|
|
28
47
|
* Loads the behavior from a JSON object.
|
|
29
48
|
* @param {Object} behaviorJSON
|
|
@@ -54,17 +73,21 @@ class Behavior extends Base {
|
|
|
54
73
|
* @param {*} value
|
|
55
74
|
*/
|
|
56
75
|
setParticipantValue (participantName, value) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if (violation) {
|
|
63
|
-
this.invalidWorldState = true;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
76
|
+
const participant = this.participants.find(obj => obj.name === participantName);
|
|
77
|
+
participant.value = value;
|
|
78
|
+
const violation = participant.enforceInvariants();
|
|
79
|
+
if (violation) {
|
|
80
|
+
this.invalidWorldState = true;
|
|
66
81
|
}
|
|
67
82
|
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Maps the abstraction id from execution to the behavior.
|
|
86
|
+
* @param {String} abstractionId
|
|
87
|
+
*/
|
|
88
|
+
addMapping (abstractionId) {
|
|
89
|
+
this.abstractionIds.push(abstractionId);
|
|
90
|
+
}
|
|
68
91
|
}
|
|
69
92
|
|
|
70
93
|
export default Behavior;
|
package/src/Members/Invariant.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import Base from "../Base";
|
|
2
|
+
import MissingAttributes from "../Errors/MissingAttributes";
|
|
2
3
|
import ENGINE_TYPES from "../TYPES";
|
|
4
|
+
|
|
3
5
|
/**
|
|
4
6
|
* Class representing a Invariant in the design.
|
|
5
7
|
*/
|
|
@@ -12,17 +14,35 @@ class Invariant extends Base {
|
|
|
12
14
|
constructor (args) {
|
|
13
15
|
super();
|
|
14
16
|
this.type = ENGINE_TYPES.INVARIANT;
|
|
15
|
-
this.invariantViolated = false
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
17
|
+
this.invariantViolated = false;
|
|
18
|
+
this.invariantType = null;
|
|
19
|
+
this.traceId = null;
|
|
20
|
+
if (typeof args === "object" && Object.hasOwn(args, "uid")) {
|
|
21
|
+
this.loadInvariantFromJSON(args);
|
|
22
|
+
} else {
|
|
23
|
+
this.loadArgs(args);
|
|
23
24
|
}
|
|
24
25
|
}
|
|
25
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Loads the provided arguments.
|
|
29
|
+
* @throws {MissingAttributes} Thrown when required attr is not present.
|
|
30
|
+
* @param {Object} args
|
|
31
|
+
*/
|
|
32
|
+
loadArgs (args) {
|
|
33
|
+
const expectedAttributes = ["name", "rule"];
|
|
34
|
+
if (typeof args !== "object" || args === null || Array.isArray(args)) {
|
|
35
|
+
// Not an object, so all attributes are missing.
|
|
36
|
+
throw new MissingAttributes("Invariant", expectedAttributes);
|
|
37
|
+
}
|
|
38
|
+
expectedAttributes.forEach((attr) => {
|
|
39
|
+
if (!(attr in args)) {
|
|
40
|
+
throw new MissingAttributes("Invariant", attr);
|
|
41
|
+
}
|
|
42
|
+
this[attr] = args[attr];
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
26
46
|
/**
|
|
27
47
|
* Loads the participant from a JSON object.
|
|
28
48
|
* @param {Object} invariantJSON
|
|
@@ -56,7 +76,6 @@ class Invariant extends Base {
|
|
|
56
76
|
enforceMinLength (value) {
|
|
57
77
|
if ("keys" in this.rule) {
|
|
58
78
|
for (let i = 0; i < this.rule["keys"].length; i++) {
|
|
59
|
-
console.log(this.rule["keys"][i], value)
|
|
60
79
|
value = value[this.rule["keys"][i]];
|
|
61
80
|
}
|
|
62
81
|
};
|
|
@@ -64,6 +83,46 @@ class Invariant extends Base {
|
|
|
64
83
|
this.invariantViolated = true;
|
|
65
84
|
}
|
|
66
85
|
}
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Sets the invariant type.
|
|
90
|
+
*
|
|
91
|
+
* The two types of invariants are: Intrinsic and Substrate
|
|
92
|
+
*
|
|
93
|
+
* Substrate invariants are learnt by the design from the
|
|
94
|
+
* environment.
|
|
95
|
+
*
|
|
96
|
+
* Intrinsic invariants are arrived at naturally from the
|
|
97
|
+
* designs control flow, data dependencies and semantic assumptions.
|
|
98
|
+
*
|
|
99
|
+
* @param {String} invariantType
|
|
100
|
+
*/
|
|
101
|
+
setInvariantType (invariantType) {
|
|
102
|
+
// TODO: Add validation for invariantType.
|
|
103
|
+
this.invariantType = invariantType;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* This function adds the trace id which can be used for
|
|
108
|
+
* automated testing.
|
|
109
|
+
*
|
|
110
|
+
* Substrate invariants correspond to a trace that represents
|
|
111
|
+
* an environment that reveals a limitation of the substrate,
|
|
112
|
+
* thus motivating the invariant. It is also the environment
|
|
113
|
+
* in which an implementation can prove that it respects this
|
|
114
|
+
* invariant.
|
|
115
|
+
*
|
|
116
|
+
* Intrisinc invariants correspond to a trace that represents
|
|
117
|
+
* a factory default environment that enables the implementation
|
|
118
|
+
* to prove that it respects the invariant.
|
|
119
|
+
*
|
|
120
|
+
* @param {String} traceId ID of trace used for automated testing.
|
|
121
|
+
*/
|
|
122
|
+
setTraceId (traceId) {
|
|
123
|
+
// TODO: Add validation for traceId.
|
|
124
|
+
this.traceId = traceId;
|
|
125
|
+
}
|
|
67
126
|
}
|
|
68
127
|
|
|
69
128
|
export default Invariant;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import Base from "../Base";
|
|
2
|
+
import MissingAttributes from "../Errors/MissingAttributes";
|
|
2
3
|
import ENGINE_TYPES from "../TYPES";
|
|
3
4
|
import Invariant from "./Invariant";
|
|
5
|
+
|
|
4
6
|
/**
|
|
5
7
|
* Class representing a participant in the design.
|
|
6
8
|
*/
|
|
@@ -14,16 +16,34 @@ class Participant extends Base {
|
|
|
14
16
|
super();
|
|
15
17
|
this.type = ENGINE_TYPES.INVARIANT;
|
|
16
18
|
this.invariants = [];
|
|
19
|
+
this.abstractionId = null;
|
|
17
20
|
this.invariantViolated = false;
|
|
18
|
-
if (typeof args === "object" && args
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
this.name = args.name;
|
|
23
|
-
}
|
|
21
|
+
if (typeof args === "object" && Object.hasOwn(args, "uid")) {
|
|
22
|
+
this.loadParticipantFromJSON(args);
|
|
23
|
+
} else {
|
|
24
|
+
this.loadArgs(args);
|
|
24
25
|
}
|
|
25
26
|
}
|
|
26
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Loads the provided arguments.
|
|
30
|
+
* @throws {MissingAttributes} Thrown when required attr is not present.
|
|
31
|
+
* @param {Object} args
|
|
32
|
+
*/
|
|
33
|
+
loadArgs (args) {
|
|
34
|
+
const expectedAttributes = ["name"];
|
|
35
|
+
if (typeof args !== "object" || args === null || Array.isArray(args)) {
|
|
36
|
+
// Not an object, so all attributes are missing.
|
|
37
|
+
throw new MissingAttributes("Participant", expectedAttributes);
|
|
38
|
+
}
|
|
39
|
+
expectedAttributes.forEach((attr) => {
|
|
40
|
+
if (!(attr in args)) {
|
|
41
|
+
throw new MissingAttributes("Participant", attr);
|
|
42
|
+
}
|
|
43
|
+
this[attr] = args[attr];
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
27
47
|
/**
|
|
28
48
|
* Loads the participant from a JSON object.
|
|
29
49
|
* @param {Object} participantJSON
|
|
@@ -71,6 +91,18 @@ class Participant extends Base {
|
|
|
71
91
|
}
|
|
72
92
|
return this.invariantViolated;
|
|
73
93
|
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Map the abstraction ID from the execution to the participant.
|
|
97
|
+
*
|
|
98
|
+
* This abstraction id will be used to assign a value to the
|
|
99
|
+
* participant from the execution using the logged abstraction id.
|
|
100
|
+
*
|
|
101
|
+
* @param {String} abstractionId
|
|
102
|
+
*/
|
|
103
|
+
mapAbstraction (abstractionId) {
|
|
104
|
+
this.abstractionId = abstractionId;
|
|
105
|
+
}
|
|
74
106
|
}
|
|
75
107
|
|
|
76
108
|
export default Participant;
|
package/tests/DALEngine.test.js
CHANGED
|
@@ -4,6 +4,7 @@ import {describe, expect, it} from "vitest";
|
|
|
4
4
|
|
|
5
5
|
import {DALEngine} from "../src/DALEngine.js";
|
|
6
6
|
import InvalidTransitionError from "../src/Errors/InvalidTransitionError.js";
|
|
7
|
+
import MissingAttributes from "../src/Errors/MissingAttributes.js";
|
|
7
8
|
import UnknownBehaviorError from "../src/Errors/UnknownBehaviorError.js";
|
|
8
9
|
import ENGINE_TYPES from "../src/TYPES.js";
|
|
9
10
|
|
|
@@ -13,6 +14,16 @@ describe("DALEngine", () => {
|
|
|
13
14
|
expect(dalInstance.name).toBe("Library Manager");
|
|
14
15
|
});
|
|
15
16
|
|
|
17
|
+
it("throws on missing attributes", () => {
|
|
18
|
+
const d = new DALEngine({name: "Library Manager"});
|
|
19
|
+
expect(() => {d.createBehavior()}).toThrow(MissingAttributes);
|
|
20
|
+
expect(() => {d.createBehavior({})}).toThrow(MissingAttributes);
|
|
21
|
+
expect(() => {d.createBehavior({"rule": "adsf"})}).toThrow(MissingAttributes);
|
|
22
|
+
expect(() => {d.createInvariant({"name": "asdf"})}).toThrow(MissingAttributes);
|
|
23
|
+
expect(() => {d.createParticipant()}).toThrow(MissingAttributes);
|
|
24
|
+
expect(() => {d.createParticipant({})}).toThrow(MissingAttributes);
|
|
25
|
+
});
|
|
26
|
+
|
|
16
27
|
it("adds node to graph", () => {
|
|
17
28
|
const d = new DALEngine({name: "Library Manager"});
|
|
18
29
|
const behavior1 = d.createBehavior({name: "AcceptBookFromUser"});
|
|
@@ -45,9 +56,9 @@ describe("DALEngine", () => {
|
|
|
45
56
|
const behavior1 = d.createBehavior({name: "AcceptBookFromUser"});
|
|
46
57
|
const behavior2 = d.createBehavior({name: "AddBookToBasket"});
|
|
47
58
|
const behavior3 = d.createBehavior({name: "AnotherBehavior"});
|
|
48
|
-
d.graph.addNode(behavior1, [behavior2, behavior3])
|
|
49
|
-
d.graph.addNode(behavior2, [])
|
|
50
|
-
d.graph.addNode(behavior3, [])
|
|
59
|
+
d.graph.addNode(behavior1, [behavior2, behavior3]);
|
|
60
|
+
d.graph.addNode(behavior2, []);
|
|
61
|
+
d.graph.addNode(behavior3, []);
|
|
51
62
|
|
|
52
63
|
// Misspell behavior name to trigger unknown behavior error
|
|
53
64
|
expect(() => {
|
|
@@ -82,7 +93,16 @@ describe("DALEngine", () => {
|
|
|
82
93
|
it("add invariant to participant", () => {
|
|
83
94
|
const d = new DALEngine({name: "Library Manager"});
|
|
84
95
|
const book = d.createParticipant({name: "book"});
|
|
85
|
-
const invariant = d.createInvariant(
|
|
96
|
+
const invariant = d.createInvariant(
|
|
97
|
+
{
|
|
98
|
+
"name": "MinLengthConstraint",
|
|
99
|
+
"rule": {
|
|
100
|
+
"type": "minLength",
|
|
101
|
+
"keys": ["value", "name"],
|
|
102
|
+
"value": 1,
|
|
103
|
+
},
|
|
104
|
+
}
|
|
105
|
+
);
|
|
86
106
|
|
|
87
107
|
book.addInvariant(invariant);
|
|
88
108
|
|
package/tests/Invariant.test.js
CHANGED
|
@@ -3,8 +3,17 @@ import {resolve} from "path"
|
|
|
3
3
|
import {describe, expect, it} from "vitest";
|
|
4
4
|
|
|
5
5
|
import {DALEngine} from "../src/DALEngine.js";
|
|
6
|
+
import MissingAttributes from "../src/Errors/MissingAttributes.js";
|
|
6
7
|
|
|
7
8
|
describe("invariantTests", () => {
|
|
9
|
+
|
|
10
|
+
it("invariant throws on missing attributes", () => {
|
|
11
|
+
let d = new DALEngine({name: "Library Manager"});
|
|
12
|
+
expect(() => {d.createInvariant({"name": "asdf"})}).toThrow(MissingAttributes);
|
|
13
|
+
expect(() => {d.createInvariant({})}).toThrow(MissingAttributes);
|
|
14
|
+
expect(() => {d.createInvariant()}).toThrow(MissingAttributes);
|
|
15
|
+
});
|
|
16
|
+
|
|
8
17
|
it("test invariant directly through participants", async () => {
|
|
9
18
|
let d = new DALEngine({name: "Library Manager"});
|
|
10
19
|
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import {readFile, unlink, writeFile} from "fs/promises"
|
|
2
|
+
import {resolve} from "path"
|
|
3
|
+
import {describe, expect, it} from "vitest";
|
|
4
|
+
|
|
5
|
+
import {DALEngine} from "../src/DALEngine.js";
|
|
6
|
+
|
|
7
|
+
describe("SimpleDesignTest", () => {
|
|
8
|
+
|
|
9
|
+
it("create simple design", async () => {
|
|
10
|
+
const d = new DALEngine({name: "Library Manager"});
|
|
11
|
+
let behavior = d.createBehavior({name: "AcceptChoiceToAddBookToBasket"});
|
|
12
|
+
const AcceptBookFromUser = d.createBehavior({name: "AcceptBookFromUser"});
|
|
13
|
+
d.graph.addNode(behavior, [AcceptBookFromUser]);
|
|
14
|
+
const AddBookToBasket = d.createBehavior({name: "AddBookToBasket"});
|
|
15
|
+
d.graph.addNode(AcceptBookFromUser, [AddBookToBasket]);
|
|
16
|
+
|
|
17
|
+
const AcceptChoiceToAuditLibrary = d.createBehavior({name: "AcceptChoiceToAuditLibrary"});
|
|
18
|
+
const GenerateAuditReport = d.createBehavior({name: "GenerateAuditReport"});
|
|
19
|
+
d.graph.addNode(AcceptChoiceToAuditLibrary, [GenerateAuditReport]);
|
|
20
|
+
const HandAuditToUser = d.createBehavior({name: "HandAuditToUser"});
|
|
21
|
+
d.graph.addNode(GenerateAuditReport, [HandAuditToUser]);
|
|
22
|
+
|
|
23
|
+
const AcceptChoiceToPlaceBooksOnShelf = d.createBehavior(
|
|
24
|
+
{name: "AcceptChoiceToPlaceBooksOnShelf"}
|
|
25
|
+
);
|
|
26
|
+
const GetBookFromBasket = d.createBehavior({name: "GetBookFromBasket"});
|
|
27
|
+
d.graph.addNode(AcceptChoiceToPlaceBooksOnShelf, [GetBookFromBasket]);
|
|
28
|
+
const GetFirstLetterOfBookName = d.createBehavior({name: "GetFirstLetterOfBookName"});
|
|
29
|
+
d.graph.addNode(GetBookFromBasket, [GetFirstLetterOfBookName]);
|
|
30
|
+
const CreateSlotOnBookShelf = d.createBehavior({name: "CreateSlotOnBookShelf"});
|
|
31
|
+
const AddBookToShelf = d.createBehavior({name: "AddBookToShelf"});
|
|
32
|
+
d.graph.addNode(GetFirstLetterOfBookName, [CreateSlotOnBookShelf]);
|
|
33
|
+
d.graph.addNode(GetFirstLetterOfBookName, [AddBookToShelf]);
|
|
34
|
+
d.graph.addNode(CreateSlotOnBookShelf, [AddBookToShelf]);
|
|
35
|
+
d.graph.addNode(AddBookToShelf, [GetBookFromBasket]);
|
|
36
|
+
|
|
37
|
+
// Output can be viewed using https://mermaid.live/
|
|
38
|
+
const filePath2 = resolve(__dirname, "./temp/simple_design_mermaid.txt")
|
|
39
|
+
await writeFile(filePath2, d.graph.exportAsMermaid())
|
|
40
|
+
});
|
|
41
|
+
});
|