dal-engine-core-js-lib-dev 0.0.4 → 0.0.6

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.
@@ -1,16 +1,20 @@
1
1
  import Base from "../Base";
2
2
  import MissingAttributes from "../Errors/MissingAttributes";
3
+ import isLoadedFromFile from "../helpers/isLoadedFromFile";
3
4
  import ENGINE_TYPES from "../TYPES";
4
5
  import Invariant from "./Invariant";
5
6
 
6
- /**
7
- * Class representing a participant in the design.
8
- */
9
7
  class Participant extends Base {
10
8
  /**
11
- * Initialize the semantic participant.
12
- * @param {String} name
13
- * @param args
9
+ * Class representing a participant in the design. The participants are
10
+ * entites in deisgn that participate in the behavior. They are mapped onto
11
+ * the implementation and their value is loaded from the execution. The
12
+ * participants define a valid world state for the behavior to happen in
13
+ * through invariants. When the value of the participant violates an
14
+ * invariant, the design has entered a semantically invalid state and is
15
+ * the root cause of downstream failure(s).
16
+ *
17
+ * @param {Object} args The arguments to initialize the participant.
14
18
  */
15
19
  constructor (args) {
16
20
  super();
@@ -18,11 +22,7 @@ class Participant extends Base {
18
22
  this.invariants = [];
19
23
  this.abstractionId = null;
20
24
  this.invariantViolated = false;
21
- if (typeof args === "object" && Object.hasOwn(args, "uid")) {
22
- this._loadParticipantFromJSON(args);
23
- } else {
24
- this._loadArgs(args);
25
- }
25
+ (isLoadedFromFile(args) ? this._loadFromFile(args) : this._loadArgs(args));
26
26
  }
27
27
 
28
28
  /**
@@ -46,9 +46,9 @@ class Participant extends Base {
46
46
 
47
47
  /**
48
48
  * Loads the participant from a JSON object.
49
- * @param {Object} participantJSON
49
+ * @param {Object} participantJSON The JSON object read from file.
50
50
  */
51
- _loadParticipantFromJSON (participantJSON) {
51
+ _loadFromFile (participantJSON) {
52
52
  for (const [key, value] of Object.entries(participantJSON)) {
53
53
  if (key === "invariants") {
54
54
  value.forEach(node => this.invariants.push(new Invariant(node)));
@@ -60,8 +60,8 @@ class Participant extends Base {
60
60
 
61
61
  /**
62
62
  * Adds an invariant to the participant.
63
- * @param {Invariant} invariant
64
- * @returns
63
+ * @param {Invariant} invariant The invariant to add.
64
+ * @returns {Invariant} The invariant that was added.
65
65
  */
66
66
  addInvariant (invariant) {
67
67
  this.invariants.push(invariant);
@@ -69,16 +69,18 @@ class Participant extends Base {
69
69
  }
70
70
 
71
71
  /**
72
- * Sets the value of the participant.
73
- * @param {*} value
72
+ * Sets the value of this participant.
73
+ * @param {*} value The value to set for the participant.
74
74
  */
75
75
  setValue (value) {
76
76
  this.value = value;
77
77
  }
78
78
 
79
79
  /**
80
- * Enforces the particiants invariants.
81
- * @returns {Boolean}
80
+ * Enforces the participant's invariants. Raises a flag indicating if an
81
+ * invariant was violated and counts the number of invariant violations.
82
+ *
83
+ * @returns {Boolean} Returns flag indicating if any invariant was violated.
82
84
  */
83
85
  enforceInvariants () {
84
86
  this.invariantViolated = false
@@ -98,9 +100,15 @@ class Participant extends Base {
98
100
  * This abstraction id will be used to assign a value to the
99
101
  * participant from the execution using the logged abstraction id.
100
102
  *
101
- * @param {String} abstractionId
103
+ * @param {String} abstractionId The abstraction ID to map to the
104
+ * participant.
102
105
  */
103
106
  mapAbstraction (abstractionId) {
107
+ /**
108
+ * TODO: Much like the behavior, I am settling on a clean way to map
109
+ * the participant onto the implementation without introducing new
110
+ * unncessary layers.
111
+ */
104
112
  this.abstractionId = abstractionId;
105
113
  }
106
114
  }
package/src/TYPES.js CHANGED
@@ -2,9 +2,8 @@ let ENGINE_TYPES = {
2
2
  BEHAVIOR: 1,
3
3
  INVARIANT: 2,
4
4
  PARTICIPANT: 3,
5
- PRIMITIVE: 4,
6
- BEHAVIORAL_CONTROL_GRAPH: 5,
7
- GRAPH_NODE: 6,
5
+ BEHAVIORAL_CONTROL_GRAPH: 4,
6
+ GRAPH_NODE: 5,
8
7
  };
9
8
  ENGINE_TYPES = Object.freeze(ENGINE_TYPES);
10
9
 
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Checks if the provided object was loaded from a file. This is determind by
3
+ * checking if the argument is an object and has a "dal_engine_uid" attribute,
4
+ * which is added to all objects when they are created and is written to file
5
+ * when the object is serialized.
6
+ *
7
+ * @param {*} obj The object to check.
8
+ * @returns {Boolean} True if the object was loaded from file, false otherwise.
9
+ */
10
+ const isLoadedFromFile = (obj) => {
11
+ return typeof obj === "object" && obj !== null
12
+ && !Array.isArray(obj) && Object.hasOwn(obj, "dal_engine_uid");
13
+ };
14
+
15
+ export default isLoadedFromFile;
@@ -1,10 +1,13 @@
1
+ // eslint-disable-next-line no-unused-vars
1
2
  import {readFile, unlink, writeFile} from "fs/promises"
2
3
  import {resolve} from "path"
3
4
  import {describe, expect, it} from "vitest";
4
5
 
5
6
  import {DALEngine} from "../src/DALEngine.js";
7
+ import BehaviorAlreadyExistsError from "../src/Errors/BehaviorAlreadyExistsError.js";
6
8
  import InvalidTransitionError from "../src/Errors/InvalidTransitionError.js";
7
9
  import MissingAttributes from "../src/Errors/MissingAttributes.js";
10
+ import TransitionAlreadyExistsError from "../src/Errors/TransitionAlreadyExistsError.js";
8
11
  import UnknownBehaviorError from "../src/Errors/UnknownBehaviorError.js";
9
12
  import ENGINE_TYPES from "../src/TYPES.js";
10
13
 
@@ -36,6 +39,12 @@ describe("DALEngine", () => {
36
39
  expect(node.getGoToBehaviors()).toStrictEqual(goToBehaviorIds);
37
40
  });
38
41
 
42
+ it("throws when a behavior with same name is added to graph", () => {
43
+ const d = new DALEngine({name: "Library Manager"});
44
+ d.addNode("AcceptBookFromUser", []);
45
+ expect(() => {d.addNode("AcceptBookFromUser", [])}).toThrow(BehaviorAlreadyExistsError);
46
+ });
47
+
39
48
  it("find node that was added using behavior name", () => {
40
49
  const d = new DALEngine({name: "Library Manager"});
41
50
  const node = d.addNode("AcceptBookFromUser", []);
@@ -120,6 +129,16 @@ describe("DALEngine", () => {
120
129
  expect(lastInvariant).toBe(invariant);
121
130
  });
122
131
 
132
+ it ("add duplicate transition and check error is raised", () => {
133
+ const d = new DALEngine({name: "Library Manager"});
134
+ const node1 = d.addNode("AcceptBookFromUser", []);
135
+ const node2 = d.addNode("AddBookToBasket", []);
136
+ node1.addGoToBehavior("AddBookToBasket");
137
+ expect(() => {
138
+ node1.addGoToBehavior("AddBookToBasket");
139
+ }).toThrow(TransitionAlreadyExistsError);
140
+ });
141
+
123
142
  it("serialize to file and deseralize from file", async () => {
124
143
  let d = new DALEngine({name: "Library Manager"});
125
144
  const book = d.createParticipant({name: "book"});
@@ -0,0 +1,80 @@
1
+ // eslint-disable-next-line no-unused-vars
2
+ import {readFile, unlink, writeFile} from "fs/promises"
3
+ import {resolve} from "path"
4
+ import {describe, expect, it} from "vitest";
5
+
6
+ import {DALEngine} from "../src/DALEngine.js";
7
+ import GraphWithNameExistsError from "../src/Errors/GraphWithNameExistsError";
8
+
9
+ describe("multiple graphs test", () => {
10
+
11
+ it("create multiple graphs and switch", async () => {
12
+ const d = new DALEngine({name: "Library Manager"});
13
+ d.createGraph("graph 1");
14
+ d.addNode("graph1behavior", []);
15
+
16
+ d.createGraph("graph 2");
17
+ d.addNode("graph2behavior", []);
18
+
19
+ d.selectGraph("graph 1");
20
+ expect(d.getNode("graph1behavior")).toBeTruthy();
21
+ expect(() => d.getNode("graph2behavior")).toThrow();
22
+ await writeFile(resolve(__dirname, "./temp/graph1.json"), d.serialize())
23
+
24
+ d.selectGraph("graph 2");
25
+ expect(d.getNode("graph2behavior")).toBeTruthy();
26
+ expect(() => d.getNode("graph1behavior")).toThrow();
27
+ await writeFile(resolve(__dirname, "./temp/graph2.json"), d.serialize())
28
+ });
29
+
30
+
31
+ it("get list of graphs", async () => {
32
+ const d = new DALEngine({name: "Library Manager"});
33
+ d.createGraph("graph 1");
34
+ d.addNode("graph1behavior", []);
35
+
36
+ d.createGraph("graph 2");
37
+ d.addNode("graph2behavior", []);
38
+
39
+ expect(d.getSelectableGraphs()).toEqual(["default graph", "graph 1", "graph 2"]);
40
+
41
+ d.selectGraph("graph 1");
42
+ expect(d.getNode("graph1behavior")).toBeTruthy();
43
+ expect(() => d.getNode("graph2behavior")).toThrow();
44
+ });
45
+
46
+ it("create graph with existing name", async () => {
47
+ const d = new DALEngine({name: "Library Manager"});
48
+ d.createGraph("graph 1");
49
+ expect(() => d.createGraph("graph 1")).toThrow(GraphWithNameExistsError);
50
+ });
51
+
52
+ it("test that graph is removed", async () => {
53
+ const d = new DALEngine({name: "Library Manager"});
54
+ d.createGraph("graph 1");
55
+ d.addNode("graph1behavior", []);
56
+ d.createGraph("graph 2");
57
+ d.addNode("graph2behavior", []);
58
+ expect(d.getSelectableGraphs()).toEqual(["default graph", "graph 1", "graph 2"]);
59
+ d.removeGraph("graph 1");
60
+
61
+ // After removing a graph, the first graph in the list is active.
62
+ // If there are no graphs, a default graph is created and set as active.
63
+ expect(d.getSelectableGraphs()).toEqual(["default graph", "graph 2"]);
64
+ expect(d.graph.name).toBe("default graph");
65
+ });
66
+
67
+ it("serialize and deserialize", async () => {
68
+ const d = new DALEngine({name: "Library Manager"});
69
+ d.createGraph("graph 1");
70
+ d.addNode("graph1behavior", []);
71
+
72
+ d.createGraph("graph 2");
73
+ d.addNode("graph2behavior", []);
74
+
75
+ await writeFile(resolve(__dirname, "./temp/graphs.json"), d.serialize());
76
+
77
+ d.deserialize(await readFile(resolve(__dirname, "./temp/graphs.json"), "utf-8"));
78
+ expect(d.getSelectableGraphs()).toEqual(["default graph", "graph 1", "graph 2"]);
79
+ });
80
+ });
@@ -1,3 +1,4 @@
1
+ // eslint-disable-next-line no-unused-vars
1
2
  import {readFile, unlink, writeFile} from "fs/promises"
2
3
  import {resolve} from "path"
3
4
  import {describe, expect, it} from "vitest";