@memoarchitect/tools 0.4.4 → 0.4.5
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/README.md +17 -9
- package/package.json +19 -18
- package/packages/tools/lib/bin/memo.js +0 -0
- package/packages/tools/lib/dhf/template-resolver.d.ts +1 -1
- package/packages/tools/lib/dhf/template-resolver.d.ts.map +1 -1
- package/packages/tools/lib/dhf/template-resolver.js +20 -16
- package/packages/tools/lib/dhf/template-resolver.js.map +1 -1
- package/packages/tools/lib/model/ontology-loader.js +1 -1
- package/packages/tools/lib/model/ontology-loader.js.map +1 -1
- package/packages/tools/lib/model/paths.d.ts +6 -4
- package/packages/tools/lib/model/paths.d.ts.map +1 -1
- package/packages/tools/lib/model/paths.js +20 -9
- package/packages/tools/lib/model/paths.js.map +1 -1
- package/packages/tools/lib/__tests__/archetype-parsing.test.d.ts +0 -2
- package/packages/tools/lib/__tests__/archetype-parsing.test.d.ts.map +0 -1
- package/packages/tools/lib/__tests__/archetype-parsing.test.js +0 -42
- package/packages/tools/lib/__tests__/archetype-parsing.test.js.map +0 -1
- package/packages/tools/lib/__tests__/architecture-discovery.test.d.ts +0 -2
- package/packages/tools/lib/__tests__/architecture-discovery.test.d.ts.map +0 -1
- package/packages/tools/lib/__tests__/architecture-discovery.test.js +0 -82
- package/packages/tools/lib/__tests__/architecture-discovery.test.js.map +0 -1
- package/packages/tools/lib/__tests__/behavior.test.d.ts +0 -2
- package/packages/tools/lib/__tests__/behavior.test.d.ts.map +0 -1
- package/packages/tools/lib/__tests__/behavior.test.js +0 -766
- package/packages/tools/lib/__tests__/behavior.test.js.map +0 -1
- package/packages/tools/lib/__tests__/builder.test.d.ts +0 -2
- package/packages/tools/lib/__tests__/builder.test.d.ts.map +0 -1
- package/packages/tools/lib/__tests__/builder.test.js +0 -728
- package/packages/tools/lib/__tests__/builder.test.js.map +0 -1
- package/packages/tools/lib/__tests__/config-loader.test.d.ts +0 -2
- package/packages/tools/lib/__tests__/config-loader.test.d.ts.map +0 -1
- package/packages/tools/lib/__tests__/config-loader.test.js +0 -196
- package/packages/tools/lib/__tests__/config-loader.test.js.map +0 -1
- package/packages/tools/lib/__tests__/conformance.test.d.ts +0 -2
- package/packages/tools/lib/__tests__/conformance.test.d.ts.map +0 -1
- package/packages/tools/lib/__tests__/conformance.test.js +0 -467
- package/packages/tools/lib/__tests__/conformance.test.js.map +0 -1
- package/packages/tools/lib/__tests__/constraint-eval.test.d.ts +0 -2
- package/packages/tools/lib/__tests__/constraint-eval.test.d.ts.map +0 -1
- package/packages/tools/lib/__tests__/constraint-eval.test.js +0 -195
- package/packages/tools/lib/__tests__/constraint-eval.test.js.map +0 -1
- package/packages/tools/lib/__tests__/constraint-grammar.test.d.ts +0 -2
- package/packages/tools/lib/__tests__/constraint-grammar.test.d.ts.map +0 -1
- package/packages/tools/lib/__tests__/constraint-grammar.test.js +0 -163
- package/packages/tools/lib/__tests__/constraint-grammar.test.js.map +0 -1
- package/packages/tools/lib/__tests__/constraint-loader.test.d.ts +0 -2
- package/packages/tools/lib/__tests__/constraint-loader.test.d.ts.map +0 -1
- package/packages/tools/lib/__tests__/constraint-loader.test.js +0 -150
- package/packages/tools/lib/__tests__/constraint-loader.test.js.map +0 -1
- package/packages/tools/lib/__tests__/csv-io.test.d.ts +0 -2
- package/packages/tools/lib/__tests__/csv-io.test.d.ts.map +0 -1
- package/packages/tools/lib/__tests__/csv-io.test.js +0 -317
- package/packages/tools/lib/__tests__/csv-io.test.js.map +0 -1
- package/packages/tools/lib/__tests__/dhf-doc-store.test.d.ts +0 -2
- package/packages/tools/lib/__tests__/dhf-doc-store.test.d.ts.map +0 -1
- package/packages/tools/lib/__tests__/dhf-doc-store.test.js +0 -94
- package/packages/tools/lib/__tests__/dhf-doc-store.test.js.map +0 -1
- package/packages/tools/lib/__tests__/dhf-template-resolver.test.d.ts +0 -2
- package/packages/tools/lib/__tests__/dhf-template-resolver.test.d.ts.map +0 -1
- package/packages/tools/lib/__tests__/dhf-template-resolver.test.js +0 -39
- package/packages/tools/lib/__tests__/dhf-template-resolver.test.js.map +0 -1
- package/packages/tools/lib/__tests__/dhf.test.d.ts +0 -2
- package/packages/tools/lib/__tests__/dhf.test.d.ts.map +0 -1
- package/packages/tools/lib/__tests__/dhf.test.js +0 -393
- package/packages/tools/lib/__tests__/dhf.test.js.map +0 -1
- package/packages/tools/lib/__tests__/dimension-filter.test.d.ts +0 -2
- package/packages/tools/lib/__tests__/dimension-filter.test.d.ts.map +0 -1
- package/packages/tools/lib/__tests__/dimension-filter.test.js +0 -85
- package/packages/tools/lib/__tests__/dimension-filter.test.js.map +0 -1
- package/packages/tools/lib/__tests__/dsm.test.d.ts +0 -2
- package/packages/tools/lib/__tests__/dsm.test.d.ts.map +0 -1
- package/packages/tools/lib/__tests__/dsm.test.js +0 -112
- package/packages/tools/lib/__tests__/dsm.test.js.map +0 -1
- package/packages/tools/lib/__tests__/e2e-workflow.test.d.ts +0 -2
- package/packages/tools/lib/__tests__/e2e-workflow.test.d.ts.map +0 -1
- package/packages/tools/lib/__tests__/e2e-workflow.test.js +0 -707
- package/packages/tools/lib/__tests__/e2e-workflow.test.js.map +0 -1
- package/packages/tools/lib/__tests__/file-watcher.test.d.ts +0 -2
- package/packages/tools/lib/__tests__/file-watcher.test.d.ts.map +0 -1
- package/packages/tools/lib/__tests__/file-watcher.test.js +0 -31
- package/packages/tools/lib/__tests__/file-watcher.test.js.map +0 -1
- package/packages/tools/lib/__tests__/impact.test.d.ts +0 -2
- package/packages/tools/lib/__tests__/impact.test.d.ts.map +0 -1
- package/packages/tools/lib/__tests__/impact.test.js +0 -82
- package/packages/tools/lib/__tests__/impact.test.js.map +0 -1
- package/packages/tools/lib/__tests__/importer.test.d.ts +0 -2
- package/packages/tools/lib/__tests__/importer.test.d.ts.map +0 -1
- package/packages/tools/lib/__tests__/importer.test.js +0 -336
- package/packages/tools/lib/__tests__/importer.test.js.map +0 -1
- package/packages/tools/lib/__tests__/kind-registry.test.d.ts +0 -2
- package/packages/tools/lib/__tests__/kind-registry.test.d.ts.map +0 -1
- package/packages/tools/lib/__tests__/kind-registry.test.js +0 -132
- package/packages/tools/lib/__tests__/kind-registry.test.js.map +0 -1
- package/packages/tools/lib/__tests__/llm.test.d.ts +0 -2
- package/packages/tools/lib/__tests__/llm.test.d.ts.map +0 -1
- package/packages/tools/lib/__tests__/llm.test.js +0 -275
- package/packages/tools/lib/__tests__/llm.test.js.map +0 -1
- package/packages/tools/lib/__tests__/manifest-resolver.test.d.ts +0 -2
- package/packages/tools/lib/__tests__/manifest-resolver.test.d.ts.map +0 -1
- package/packages/tools/lib/__tests__/manifest-resolver.test.js +0 -97
- package/packages/tools/lib/__tests__/manifest-resolver.test.js.map +0 -1
- package/packages/tools/lib/__tests__/markdown-to-ir.test.d.ts +0 -2
- package/packages/tools/lib/__tests__/markdown-to-ir.test.d.ts.map +0 -1
- package/packages/tools/lib/__tests__/markdown-to-ir.test.js +0 -74
- package/packages/tools/lib/__tests__/markdown-to-ir.test.js.map +0 -1
- package/packages/tools/lib/__tests__/methodology-scope.test.d.ts +0 -2
- package/packages/tools/lib/__tests__/methodology-scope.test.d.ts.map +0 -1
- package/packages/tools/lib/__tests__/methodology-scope.test.js +0 -59
- package/packages/tools/lib/__tests__/methodology-scope.test.js.map +0 -1
- package/packages/tools/lib/__tests__/parser.test.d.ts +0 -2
- package/packages/tools/lib/__tests__/parser.test.d.ts.map +0 -1
- package/packages/tools/lib/__tests__/parser.test.js +0 -938
- package/packages/tools/lib/__tests__/parser.test.js.map +0 -1
- package/packages/tools/lib/__tests__/plugin.test.d.ts +0 -2
- package/packages/tools/lib/__tests__/plugin.test.d.ts.map +0 -1
- package/packages/tools/lib/__tests__/plugin.test.js +0 -308
- package/packages/tools/lib/__tests__/plugin.test.js.map +0 -1
- package/packages/tools/lib/__tests__/relationship-registry.test.d.ts +0 -2
- package/packages/tools/lib/__tests__/relationship-registry.test.d.ts.map +0 -1
- package/packages/tools/lib/__tests__/relationship-registry.test.js +0 -96
- package/packages/tools/lib/__tests__/relationship-registry.test.js.map +0 -1
- package/packages/tools/lib/__tests__/rule-registry.test.d.ts +0 -2
- package/packages/tools/lib/__tests__/rule-registry.test.d.ts.map +0 -1
- package/packages/tools/lib/__tests__/rule-registry.test.js +0 -53
- package/packages/tools/lib/__tests__/rule-registry.test.js.map +0 -1
- package/packages/tools/lib/__tests__/toolchain.test.d.ts +0 -2
- package/packages/tools/lib/__tests__/toolchain.test.d.ts.map +0 -1
- package/packages/tools/lib/__tests__/toolchain.test.js +0 -94
- package/packages/tools/lib/__tests__/toolchain.test.js.map +0 -1
- package/packages/tools/lib/__tests__/view-deriver-samples.test.d.ts +0 -2
- package/packages/tools/lib/__tests__/view-deriver-samples.test.d.ts.map +0 -1
- package/packages/tools/lib/__tests__/view-deriver-samples.test.js +0 -39
- package/packages/tools/lib/__tests__/view-deriver-samples.test.js.map +0 -1
- package/packages/tools/lib/__tests__/view-kinds.test.d.ts +0 -2
- package/packages/tools/lib/__tests__/view-kinds.test.d.ts.map +0 -1
- package/packages/tools/lib/__tests__/view-kinds.test.js +0 -263
- package/packages/tools/lib/__tests__/view-kinds.test.js.map +0 -1
- package/packages/tools/lib/__tests__/view-layout-store.test.d.ts +0 -2
- package/packages/tools/lib/__tests__/view-layout-store.test.d.ts.map +0 -1
- package/packages/tools/lib/__tests__/view-layout-store.test.js +0 -61
- package/packages/tools/lib/__tests__/view-layout-store.test.js.map +0 -1
|
@@ -1,766 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { EmptyFileSystem } from 'langium';
|
|
3
|
-
import { parseHelper } from 'langium/test';
|
|
4
|
-
import { createMemoSysMLServices } from '../language/memo-sysml-module.js';
|
|
5
|
-
import { buildMemoModel } from '../model/builder.js';
|
|
6
|
-
import { validateBehavior } from '../validator/behavior-validator.js';
|
|
7
|
-
const services = createMemoSysMLServices({ ...EmptyFileSystem }).MemoSysML;
|
|
8
|
-
const parse = parseHelper(services);
|
|
9
|
-
async function parseDoc(source, filePath = 'test.sysml') {
|
|
10
|
-
const doc = await parse(source);
|
|
11
|
-
return { document: doc, filePath };
|
|
12
|
-
}
|
|
13
|
-
/** Minimal config for behavior testing */
|
|
14
|
-
const behaviorConfig = {
|
|
15
|
-
projectName: 'test-behavior',
|
|
16
|
-
projectType: 'device',
|
|
17
|
-
kinds: {
|
|
18
|
-
Subsystem: { label: 'Subsystem', layer: 'logical', sysmlConstruct: 'part def' },
|
|
19
|
-
SystemFunction: { label: 'System Function', layer: 'functional', sysmlConstruct: 'action def' },
|
|
20
|
-
},
|
|
21
|
-
relationshipTypes: [
|
|
22
|
-
{ name: 'allocateTo', label: 'Allocate To', layer: 'functional', color: '#E67E22' },
|
|
23
|
-
{ name: 'flow', label: 'Flow', layer: 'behavior', color: '#3498DB' },
|
|
24
|
-
{ name: 'succession', label: 'Succession', layer: 'behavior', color: '#95A5A6' },
|
|
25
|
-
],
|
|
26
|
-
};
|
|
27
|
-
// ─── Grammar Parsing Tests ─────────────────────────────────────────────────
|
|
28
|
-
describe('Behavior grammar: item def', () => {
|
|
29
|
-
it('parses item def with empty body', async () => {
|
|
30
|
-
const doc = await parse(`
|
|
31
|
-
package Test {
|
|
32
|
-
item def PrescriptionData;
|
|
33
|
-
}
|
|
34
|
-
`);
|
|
35
|
-
expect(doc.parseResult.lexerErrors).toHaveLength(0);
|
|
36
|
-
expect(doc.parseResult.parserErrors).toHaveLength(0);
|
|
37
|
-
});
|
|
38
|
-
it('parses item def with body', async () => {
|
|
39
|
-
const doc = await parse(`
|
|
40
|
-
package Test {
|
|
41
|
-
item def WaterFlowRate {
|
|
42
|
-
attribute rate : Real;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
`);
|
|
46
|
-
expect(doc.parseResult.parserErrors).toHaveLength(0);
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
describe('Behavior grammar: action def with parameters', () => {
|
|
50
|
-
it('accepts operator and function behavior keywords', async () => {
|
|
51
|
-
const doc = await parse(`
|
|
52
|
-
package Behaviors {
|
|
53
|
-
operator def PreparePatient;
|
|
54
|
-
function def RegulateFlow;
|
|
55
|
-
operator preparePatient : PreparePatient;
|
|
56
|
-
function regulateFlow : RegulateFlow;
|
|
57
|
-
}
|
|
58
|
-
`);
|
|
59
|
-
expect(doc.parseResult.parserErrors).toHaveLength(0);
|
|
60
|
-
const model = buildMemoModel([{ document: doc, filePath: 'behaviors.sysml' }], behaviorConfig);
|
|
61
|
-
expect(model.elements.get('preparePatient')?.kind).toBe('OperatorUsage');
|
|
62
|
-
expect(model.elements.get('regulateFlow')?.kind).toBe('FunctionUsage');
|
|
63
|
-
});
|
|
64
|
-
it('parses action def with out parameter', async () => {
|
|
65
|
-
const doc = await parse(`
|
|
66
|
-
package Test {
|
|
67
|
-
action def ReceivePrescription {
|
|
68
|
-
out prescription : PrescriptionData;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
`);
|
|
72
|
-
expect(doc.parseResult.parserErrors).toHaveLength(0);
|
|
73
|
-
});
|
|
74
|
-
it('parses action def with in and out parameters', async () => {
|
|
75
|
-
const doc = await parse(`
|
|
76
|
-
package Test {
|
|
77
|
-
action def ValidatePrescription {
|
|
78
|
-
in prescription : PrescriptionData;
|
|
79
|
-
out validatedOrder : InfusionOrder;
|
|
80
|
-
out validationError : ErrorCode;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
`);
|
|
84
|
-
expect(doc.parseResult.parserErrors).toHaveLength(0);
|
|
85
|
-
});
|
|
86
|
-
it('parses action def with inout parameter', async () => {
|
|
87
|
-
const doc = await parse(`
|
|
88
|
-
package Test {
|
|
89
|
-
action def ProcessData {
|
|
90
|
-
inout buffer : DataBuffer;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
`);
|
|
94
|
-
expect(doc.parseResult.parserErrors).toHaveLength(0);
|
|
95
|
-
});
|
|
96
|
-
it('parses empty action def', async () => {
|
|
97
|
-
const doc = await parse(`
|
|
98
|
-
package Test {
|
|
99
|
-
action def SimpleAction;
|
|
100
|
-
}
|
|
101
|
-
`);
|
|
102
|
-
expect(doc.parseResult.parserErrors).toHaveLength(0);
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
describe('Behavior grammar: composite action usage', () => {
|
|
106
|
-
it('parses action usage without type (composite action)', async () => {
|
|
107
|
-
const doc = await parse(`
|
|
108
|
-
package Test {
|
|
109
|
-
action performInfusion {
|
|
110
|
-
action receive : ReceivePrescription;
|
|
111
|
-
action validate : ValidatePrescription;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
`);
|
|
115
|
-
expect(doc.parseResult.parserErrors).toHaveLength(0);
|
|
116
|
-
});
|
|
117
|
-
it('parses typed action usage (existing syntax)', async () => {
|
|
118
|
-
const doc = await parse(`
|
|
119
|
-
package Test {
|
|
120
|
-
action myFunc : SystemFunction;
|
|
121
|
-
}
|
|
122
|
-
`);
|
|
123
|
-
expect(doc.parseResult.parserErrors).toHaveLength(0);
|
|
124
|
-
});
|
|
125
|
-
});
|
|
126
|
-
describe('Behavior grammar: flow connection usage', () => {
|
|
127
|
-
it('parses flow of item between action ports', async () => {
|
|
128
|
-
const doc = await parse(`
|
|
129
|
-
package Test {
|
|
130
|
-
action performInfusion {
|
|
131
|
-
action receive : ReceivePrescription;
|
|
132
|
-
action validate : ValidatePrescription;
|
|
133
|
-
flow of PrescriptionData from receive.prescription to validate.prescription;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
`);
|
|
137
|
-
expect(doc.parseResult.parserErrors).toHaveLength(0);
|
|
138
|
-
});
|
|
139
|
-
it('parses multiple flows', async () => {
|
|
140
|
-
const doc = await parse(`
|
|
141
|
-
package Test {
|
|
142
|
-
action cycle {
|
|
143
|
-
action sense : SenseMoisture;
|
|
144
|
-
action evaluate : EvaluateNeed;
|
|
145
|
-
action activate : ActivatePump;
|
|
146
|
-
flow of MoistureReading from sense.moistureLevel to evaluate.moistureLevel;
|
|
147
|
-
flow of IrrigationCommand from evaluate.decision to activate.command;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
`);
|
|
151
|
-
expect(doc.parseResult.parserErrors).toHaveLength(0);
|
|
152
|
-
});
|
|
153
|
-
});
|
|
154
|
-
describe('Behavior grammar: succession usage', () => {
|
|
155
|
-
it('parses succession with start and done', async () => {
|
|
156
|
-
const doc = await parse(`
|
|
157
|
-
package Test {
|
|
158
|
-
action performInfusion {
|
|
159
|
-
action receive : ReceivePrescription;
|
|
160
|
-
action validate : ValidatePrescription;
|
|
161
|
-
first start then receive then validate then done;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
`);
|
|
165
|
-
expect(doc.parseResult.parserErrors).toHaveLength(0);
|
|
166
|
-
});
|
|
167
|
-
it('parses multiple succession chains', async () => {
|
|
168
|
-
const doc = await parse(`
|
|
169
|
-
package Test {
|
|
170
|
-
action cycle {
|
|
171
|
-
action sense : SenseMoisture;
|
|
172
|
-
action fetch : FetchWeather;
|
|
173
|
-
action evaluate : EvaluateNeed;
|
|
174
|
-
first start then sense;
|
|
175
|
-
first start then fetch;
|
|
176
|
-
first sense then evaluate;
|
|
177
|
-
first fetch then evaluate;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
`);
|
|
181
|
-
expect(doc.parseResult.parserErrors).toHaveLength(0);
|
|
182
|
-
});
|
|
183
|
-
});
|
|
184
|
-
describe('Behavior grammar: allocate usage', () => {
|
|
185
|
-
it('parses allocate statement', async () => {
|
|
186
|
-
const doc = await parse(`
|
|
187
|
-
package Test {
|
|
188
|
-
action validate : ValidateAction;
|
|
189
|
-
part sw : Subsystem;
|
|
190
|
-
allocate validate to sw;
|
|
191
|
-
}
|
|
192
|
-
`);
|
|
193
|
-
expect(doc.parseResult.parserErrors).toHaveLength(0);
|
|
194
|
-
});
|
|
195
|
-
it('parses multiple allocate statements', async () => {
|
|
196
|
-
const doc = await parse(`
|
|
197
|
-
package Test {
|
|
198
|
-
action validate : ValidateAction;
|
|
199
|
-
action deliver : DeliverAction;
|
|
200
|
-
part sw : Subsystem;
|
|
201
|
-
part hw : Subsystem;
|
|
202
|
-
allocate validate to sw;
|
|
203
|
-
allocate deliver to hw;
|
|
204
|
-
}
|
|
205
|
-
`);
|
|
206
|
-
expect(doc.parseResult.parserErrors).toHaveLength(0);
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
|
-
describe('Behavior grammar: fork/join control nodes', () => {
|
|
210
|
-
it('parses fork and join inside an action, wired by successions', async () => {
|
|
211
|
-
const doc = await parse(`
|
|
212
|
-
package Test {
|
|
213
|
-
action prepare {
|
|
214
|
-
action verify : VerifyAction;
|
|
215
|
-
fork splitPrep;
|
|
216
|
-
action primePump : PrimeAction;
|
|
217
|
-
action selfTest : TestAction;
|
|
218
|
-
join syncPrep;
|
|
219
|
-
action program : ProgramAction;
|
|
220
|
-
|
|
221
|
-
first start then verify;
|
|
222
|
-
first verify then splitPrep;
|
|
223
|
-
first splitPrep then primePump;
|
|
224
|
-
first splitPrep then selfTest;
|
|
225
|
-
first primePump then syncPrep;
|
|
226
|
-
first selfTest then syncPrep;
|
|
227
|
-
first syncPrep then program;
|
|
228
|
-
first program then done;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
`);
|
|
232
|
-
expect(doc.parseResult.lexerErrors).toHaveLength(0);
|
|
233
|
-
expect(doc.parseResult.parserErrors).toHaveLength(0);
|
|
234
|
-
});
|
|
235
|
-
});
|
|
236
|
-
// ─── Full Example Parsing ──────────────────────────────────────────────────
|
|
237
|
-
describe('Behavior grammar: full infusion pump example', () => {
|
|
238
|
-
it('parses the complete infusion pump behavior model', async () => {
|
|
239
|
-
const doc = await parse(`
|
|
240
|
-
package InfusionPump_Behavior {
|
|
241
|
-
import InfusionPump::*;
|
|
242
|
-
|
|
243
|
-
action def ReceivePrescription {
|
|
244
|
-
out prescription : PrescriptionData;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
action def ValidatePrescription {
|
|
248
|
-
in prescription : PrescriptionData;
|
|
249
|
-
out validatedOrder : InfusionOrder;
|
|
250
|
-
out validationError : ErrorCode;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
action def PreparePump {
|
|
254
|
-
in order : InfusionOrder;
|
|
255
|
-
out pumpReady : ReadySignal;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
action def DeliverInfusion {
|
|
259
|
-
in pumpReady : ReadySignal;
|
|
260
|
-
in order : InfusionOrder;
|
|
261
|
-
out infusionStatus : StatusReport;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
action def MonitorPatient {
|
|
265
|
-
in infusionStatus : StatusReport;
|
|
266
|
-
out vitalSigns : PatientVitals;
|
|
267
|
-
out alarm : AlarmSignal;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
action def HandleAlarm {
|
|
271
|
-
in alarm : AlarmSignal;
|
|
272
|
-
out response : ClinicalResponse;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
action performInfusion {
|
|
276
|
-
action receive : ReceivePrescription;
|
|
277
|
-
action validate : ValidatePrescription;
|
|
278
|
-
action prepare : PreparePump;
|
|
279
|
-
action deliver : DeliverInfusion;
|
|
280
|
-
action monitor : MonitorPatient;
|
|
281
|
-
action handleAlarm : HandleAlarm;
|
|
282
|
-
|
|
283
|
-
flow of PrescriptionData from receive.prescription to validate.prescription;
|
|
284
|
-
flow of InfusionOrder from validate.validatedOrder to prepare.order;
|
|
285
|
-
flow of InfusionOrder from validate.validatedOrder to deliver.order;
|
|
286
|
-
flow of ReadySignal from prepare.pumpReady to deliver.pumpReady;
|
|
287
|
-
flow of StatusReport from deliver.infusionStatus to monitor.infusionStatus;
|
|
288
|
-
flow of AlarmSignal from monitor.alarm to handleAlarm.alarm;
|
|
289
|
-
|
|
290
|
-
first start then receive then validate then prepare then deliver then done;
|
|
291
|
-
first deliver then monitor;
|
|
292
|
-
first monitor then handleAlarm;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
allocate validate to SoftwareSubsystem;
|
|
296
|
-
allocate prepare to PumpMechanism;
|
|
297
|
-
allocate deliver to PumpMechanism;
|
|
298
|
-
allocate monitor to SensorSubsystem;
|
|
299
|
-
allocate handleAlarm to SoftwareSubsystem;
|
|
300
|
-
}
|
|
301
|
-
`);
|
|
302
|
-
expect(doc.parseResult.lexerErrors).toHaveLength(0);
|
|
303
|
-
expect(doc.parseResult.parserErrors).toHaveLength(0);
|
|
304
|
-
});
|
|
305
|
-
});
|
|
306
|
-
// ─── Model Building Tests ──────────────────────────────────────────────────
|
|
307
|
-
describe('Behavior builder: action definitions', () => {
|
|
308
|
-
it('extracts action definition as MemoElement with parameters', async () => {
|
|
309
|
-
const doc = await parseDoc(`
|
|
310
|
-
package Test {
|
|
311
|
-
action def ValidatePrescription {
|
|
312
|
-
in prescription : PrescriptionData;
|
|
313
|
-
out validatedOrder : InfusionOrder;
|
|
314
|
-
out validationError : ErrorCode;
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
`);
|
|
318
|
-
const model = buildMemoModel([doc], behaviorConfig);
|
|
319
|
-
const el = model.elements.get('ValidatePrescription');
|
|
320
|
-
expect(el).toBeDefined();
|
|
321
|
-
expect(el.kind).toBe('ActionDefinition');
|
|
322
|
-
expect(el.construct).toBe('action');
|
|
323
|
-
expect(el.layer).toBe('behavior');
|
|
324
|
-
expect(el.parameters).toHaveLength(3);
|
|
325
|
-
expect(el.parameters[0]).toEqual({ name: 'prescription', direction: 'in', type: 'PrescriptionData' });
|
|
326
|
-
expect(el.parameters[1]).toEqual({ name: 'validatedOrder', direction: 'out', type: 'InfusionOrder' });
|
|
327
|
-
expect(el.parameters[2]).toEqual({ name: 'validationError', direction: 'out', type: 'ErrorCode' });
|
|
328
|
-
});
|
|
329
|
-
it('extracts action def without parameters', async () => {
|
|
330
|
-
const doc = await parseDoc(`
|
|
331
|
-
package Test {
|
|
332
|
-
action def SimpleAction;
|
|
333
|
-
}
|
|
334
|
-
`);
|
|
335
|
-
const model = buildMemoModel([doc], behaviorConfig);
|
|
336
|
-
const el = model.elements.get('SimpleAction');
|
|
337
|
-
expect(el).toBeDefined();
|
|
338
|
-
expect(el.kind).toBe('ActionDefinition');
|
|
339
|
-
expect(el.parameters).toBeUndefined();
|
|
340
|
-
});
|
|
341
|
-
});
|
|
342
|
-
describe('Behavior builder: item definitions', () => {
|
|
343
|
-
it('extracts item definition as MemoElement', async () => {
|
|
344
|
-
const doc = await parseDoc(`
|
|
345
|
-
package Test {
|
|
346
|
-
item def PrescriptionData;
|
|
347
|
-
item def InfusionOrder;
|
|
348
|
-
}
|
|
349
|
-
`);
|
|
350
|
-
const model = buildMemoModel([doc], behaviorConfig);
|
|
351
|
-
expect(model.elements.get('PrescriptionData')).toBeDefined();
|
|
352
|
-
expect(model.elements.get('PrescriptionData').kind).toBe('ItemDefinition');
|
|
353
|
-
expect(model.elements.get('PrescriptionData').construct).toBe('item');
|
|
354
|
-
expect(model.elements.get('PrescriptionData').layer).toBe('behavior');
|
|
355
|
-
expect(model.elements.get('InfusionOrder')).toBeDefined();
|
|
356
|
-
});
|
|
357
|
-
});
|
|
358
|
-
describe('Behavior builder: composite action usage', () => {
|
|
359
|
-
it('extracts composite action and nested actions', async () => {
|
|
360
|
-
const doc = await parseDoc(`
|
|
361
|
-
package Test {
|
|
362
|
-
action def ReceivePrescription {
|
|
363
|
-
out prescription : PrescriptionData;
|
|
364
|
-
}
|
|
365
|
-
action def ValidatePrescription {
|
|
366
|
-
in prescription : PrescriptionData;
|
|
367
|
-
out validatedOrder : InfusionOrder;
|
|
368
|
-
}
|
|
369
|
-
action performInfusion {
|
|
370
|
-
action receive : ReceivePrescription;
|
|
371
|
-
action validate : ValidatePrescription;
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
`);
|
|
375
|
-
const model = buildMemoModel([doc], behaviorConfig);
|
|
376
|
-
// Composite action
|
|
377
|
-
const composite = model.elements.get('performInfusion');
|
|
378
|
-
expect(composite).toBeDefined();
|
|
379
|
-
expect(composite.kind).toBe('ActionUsage');
|
|
380
|
-
expect(composite.construct).toBe('action');
|
|
381
|
-
// Nested actions
|
|
382
|
-
const receive = model.elements.get('receive');
|
|
383
|
-
expect(receive).toBeDefined();
|
|
384
|
-
expect(receive.parentAction).toBe('performInfusion');
|
|
385
|
-
const validate = model.elements.get('validate');
|
|
386
|
-
expect(validate).toBeDefined();
|
|
387
|
-
expect(validate.parentAction).toBe('performInfusion');
|
|
388
|
-
});
|
|
389
|
-
it('extracts typed action usage with config kind', async () => {
|
|
390
|
-
const doc = await parseDoc(`
|
|
391
|
-
package Test {
|
|
392
|
-
action myFunc : SystemFunction;
|
|
393
|
-
}
|
|
394
|
-
`);
|
|
395
|
-
const model = buildMemoModel([doc], behaviorConfig);
|
|
396
|
-
const el = model.elements.get('myFunc');
|
|
397
|
-
expect(el).toBeDefined();
|
|
398
|
-
expect(el.kind).toBe('SystemFunction');
|
|
399
|
-
expect(el.layer).toBe('functional');
|
|
400
|
-
});
|
|
401
|
-
});
|
|
402
|
-
describe('Behavior builder: fork/join control nodes', () => {
|
|
403
|
-
it('extracts fork/join as behavior-layer elements and wires successions through them', async () => {
|
|
404
|
-
const doc = await parseDoc(`
|
|
405
|
-
package Test {
|
|
406
|
-
action prepare {
|
|
407
|
-
action verify : VerifyAction;
|
|
408
|
-
fork splitPrep;
|
|
409
|
-
action primePump : PrimeAction;
|
|
410
|
-
action selfTest : TestAction;
|
|
411
|
-
join syncPrep;
|
|
412
|
-
|
|
413
|
-
first verify then splitPrep;
|
|
414
|
-
first splitPrep then primePump;
|
|
415
|
-
first splitPrep then selfTest;
|
|
416
|
-
first primePump then syncPrep;
|
|
417
|
-
first selfTest then syncPrep;
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
`);
|
|
421
|
-
const model = buildMemoModel([doc], behaviorConfig);
|
|
422
|
-
// Control nodes become behavior-layer action-construct elements
|
|
423
|
-
const fork = model.elements.get('splitPrep');
|
|
424
|
-
expect(fork).toBeDefined();
|
|
425
|
-
expect(fork.kind).toBe('ForkNode');
|
|
426
|
-
expect(fork.construct).toBe('action');
|
|
427
|
-
expect(fork.layer).toBe('behavior');
|
|
428
|
-
expect(fork.attributes['controlKind']).toBe('fork');
|
|
429
|
-
expect(fork.parentAction).toBe('prepare');
|
|
430
|
-
const join = model.elements.get('syncPrep');
|
|
431
|
-
expect(join).toBeDefined();
|
|
432
|
-
expect(join.kind).toBe('JoinNode');
|
|
433
|
-
expect(join.attributes['controlKind']).toBe('join');
|
|
434
|
-
// Successions reference the control nodes by id
|
|
435
|
-
const succ = model.relationships.filter(r => r.type === 'succession');
|
|
436
|
-
expect(succ).toContainEqual(expect.objectContaining({ sourceId: 'splitPrep', targetId: 'primePump' }));
|
|
437
|
-
expect(succ).toContainEqual(expect.objectContaining({ sourceId: 'splitPrep', targetId: 'selfTest' }));
|
|
438
|
-
expect(succ).toContainEqual(expect.objectContaining({ sourceId: 'primePump', targetId: 'syncPrep' }));
|
|
439
|
-
expect(succ).toContainEqual(expect.objectContaining({ sourceId: 'selfTest', targetId: 'syncPrep' }));
|
|
440
|
-
});
|
|
441
|
-
});
|
|
442
|
-
describe('Behavior builder: flow connections', () => {
|
|
443
|
-
it('creates flow relationships between nested actions', async () => {
|
|
444
|
-
const doc = await parseDoc(`
|
|
445
|
-
package Test {
|
|
446
|
-
action def ReceivePrescription {
|
|
447
|
-
out prescription : PrescriptionData;
|
|
448
|
-
}
|
|
449
|
-
action def ValidatePrescription {
|
|
450
|
-
in prescription : PrescriptionData;
|
|
451
|
-
out validatedOrder : InfusionOrder;
|
|
452
|
-
}
|
|
453
|
-
action performInfusion {
|
|
454
|
-
action receive : ReceivePrescription;
|
|
455
|
-
action validate : ValidatePrescription;
|
|
456
|
-
flow of PrescriptionData from receive.prescription to validate.prescription;
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
`);
|
|
460
|
-
const model = buildMemoModel([doc], behaviorConfig);
|
|
461
|
-
const flowRels = model.relationships.filter(r => r.type === 'flow');
|
|
462
|
-
expect(flowRels).toHaveLength(1);
|
|
463
|
-
expect(flowRels[0].sourceId).toBe('receive');
|
|
464
|
-
expect(flowRels[0].sourceEnd).toBe('prescription');
|
|
465
|
-
expect(flowRels[0].targetId).toBe('validate');
|
|
466
|
-
expect(flowRels[0].targetEnd).toBe('prescription');
|
|
467
|
-
expect(flowRels[0].flowItem).toBe('PrescriptionData');
|
|
468
|
-
});
|
|
469
|
-
it('creates multiple flow relationships', async () => {
|
|
470
|
-
const doc = await parseDoc(`
|
|
471
|
-
package Test {
|
|
472
|
-
action def A { out x : TypeX; }
|
|
473
|
-
action def B { in x : TypeX; out y : TypeY; }
|
|
474
|
-
action def C { in y : TypeY; }
|
|
475
|
-
action process {
|
|
476
|
-
action a : A;
|
|
477
|
-
action b : B;
|
|
478
|
-
action c : C;
|
|
479
|
-
flow of TypeX from a.x to b.x;
|
|
480
|
-
flow of TypeY from b.y to c.y;
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
`);
|
|
484
|
-
const model = buildMemoModel([doc], behaviorConfig);
|
|
485
|
-
const flowRels = model.relationships.filter(r => r.type === 'flow');
|
|
486
|
-
expect(flowRels).toHaveLength(2);
|
|
487
|
-
});
|
|
488
|
-
});
|
|
489
|
-
describe('Behavior builder: successions', () => {
|
|
490
|
-
it('creates succession relationships from chain', async () => {
|
|
491
|
-
const doc = await parseDoc(`
|
|
492
|
-
package Test {
|
|
493
|
-
action def A { out x : TypeX; }
|
|
494
|
-
action def B { in x : TypeX; }
|
|
495
|
-
action process {
|
|
496
|
-
action a : A;
|
|
497
|
-
action b : B;
|
|
498
|
-
first start then a then b then done;
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
`);
|
|
502
|
-
const model = buildMemoModel([doc], behaviorConfig);
|
|
503
|
-
const succRels = model.relationships.filter(r => r.type === 'succession');
|
|
504
|
-
// start→a, a→b, b→done = 3 succession relationships
|
|
505
|
-
expect(succRels).toHaveLength(3);
|
|
506
|
-
// start→a
|
|
507
|
-
expect(succRels[0].sourceId).toBe('process__start');
|
|
508
|
-
expect(succRels[0].targetId).toBe('a');
|
|
509
|
-
// a→b
|
|
510
|
-
expect(succRels[1].sourceId).toBe('a');
|
|
511
|
-
expect(succRels[1].targetId).toBe('b');
|
|
512
|
-
// b→done
|
|
513
|
-
expect(succRels[2].sourceId).toBe('b');
|
|
514
|
-
expect(succRels[2].targetId).toBe('process__done');
|
|
515
|
-
});
|
|
516
|
-
it('creates successions for parallel branches', async () => {
|
|
517
|
-
const doc = await parseDoc(`
|
|
518
|
-
package Test {
|
|
519
|
-
action def A { out x : TypeX; }
|
|
520
|
-
action def B { out y : TypeY; }
|
|
521
|
-
action def C { in x : TypeX; in y : TypeY; }
|
|
522
|
-
action process {
|
|
523
|
-
action a : A;
|
|
524
|
-
action b : B;
|
|
525
|
-
action c : C;
|
|
526
|
-
first start then a;
|
|
527
|
-
first start then b;
|
|
528
|
-
first a then c;
|
|
529
|
-
first b then c;
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
`);
|
|
533
|
-
const model = buildMemoModel([doc], behaviorConfig);
|
|
534
|
-
const succRels = model.relationships.filter(r => r.type === 'succession');
|
|
535
|
-
// start→a, start→b, a→c, b→c = 4
|
|
536
|
-
expect(succRels).toHaveLength(4);
|
|
537
|
-
});
|
|
538
|
-
});
|
|
539
|
-
describe('Behavior builder: allocations', () => {
|
|
540
|
-
it('creates allocateTo relationships', async () => {
|
|
541
|
-
const doc = await parseDoc(`
|
|
542
|
-
package Test {
|
|
543
|
-
action validate : SystemFunction;
|
|
544
|
-
part sw : Subsystem;
|
|
545
|
-
allocate validate to sw;
|
|
546
|
-
}
|
|
547
|
-
`);
|
|
548
|
-
const model = buildMemoModel([doc], behaviorConfig);
|
|
549
|
-
const allocRels = model.relationships.filter(r => r.type === 'allocateTo');
|
|
550
|
-
expect(allocRels).toHaveLength(1);
|
|
551
|
-
expect(allocRels[0].sourceId).toBe('validate');
|
|
552
|
-
expect(allocRels[0].targetId).toBe('sw');
|
|
553
|
-
});
|
|
554
|
-
it('sets allocatedTo on the source element', async () => {
|
|
555
|
-
const doc = await parseDoc(`
|
|
556
|
-
package Test {
|
|
557
|
-
action validate : SystemFunction;
|
|
558
|
-
part sw : Subsystem;
|
|
559
|
-
allocate validate to sw;
|
|
560
|
-
}
|
|
561
|
-
`);
|
|
562
|
-
const model = buildMemoModel([doc], behaviorConfig);
|
|
563
|
-
const el = model.elements.get('validate');
|
|
564
|
-
expect(el).toBeDefined();
|
|
565
|
-
expect(el.allocatedTo).toBe('sw');
|
|
566
|
-
});
|
|
567
|
-
it('creates multiple allocation relationships', async () => {
|
|
568
|
-
const doc = await parseDoc(`
|
|
569
|
-
package Test {
|
|
570
|
-
action validate : SystemFunction;
|
|
571
|
-
action deliver : SystemFunction;
|
|
572
|
-
part sw : Subsystem;
|
|
573
|
-
part hw : Subsystem;
|
|
574
|
-
allocate validate to sw;
|
|
575
|
-
allocate deliver to hw;
|
|
576
|
-
}
|
|
577
|
-
`);
|
|
578
|
-
const model = buildMemoModel([doc], behaviorConfig);
|
|
579
|
-
const allocRels = model.relationships.filter(r => r.type === 'allocateTo');
|
|
580
|
-
expect(allocRels).toHaveLength(2);
|
|
581
|
-
});
|
|
582
|
-
});
|
|
583
|
-
// ─── Index Tests ───────────────────────────────────────────────────────────
|
|
584
|
-
describe('Behavior builder: model indexes', () => {
|
|
585
|
-
it('indexes behavior elements by kind', async () => {
|
|
586
|
-
const doc = await parseDoc(`
|
|
587
|
-
package Test {
|
|
588
|
-
item def PrescriptionData;
|
|
589
|
-
action def ReceivePrescription {
|
|
590
|
-
out prescription : PrescriptionData;
|
|
591
|
-
}
|
|
592
|
-
action performInfusion {
|
|
593
|
-
action receive : ReceivePrescription;
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
`);
|
|
597
|
-
const model = buildMemoModel([doc], behaviorConfig);
|
|
598
|
-
expect(model.elementsByKind.get('ItemDefinition')).toHaveLength(1);
|
|
599
|
-
expect(model.elementsByKind.get('ActionDefinition')).toHaveLength(1);
|
|
600
|
-
expect(model.elementsByKind.get('ActionUsage')).toHaveLength(2); // performInfusion + receive
|
|
601
|
-
});
|
|
602
|
-
it('indexes behavior elements by layer', async () => {
|
|
603
|
-
const doc = await parseDoc(`
|
|
604
|
-
package Test {
|
|
605
|
-
item def PrescriptionData;
|
|
606
|
-
action def ReceivePrescription {
|
|
607
|
-
out prescription : PrescriptionData;
|
|
608
|
-
}
|
|
609
|
-
action performInfusion {
|
|
610
|
-
action receive : ReceivePrescription;
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
`);
|
|
614
|
-
const model = buildMemoModel([doc], behaviorConfig);
|
|
615
|
-
const behaviorElements = model.elementsByLayer.get('behavior') || [];
|
|
616
|
-
expect(behaviorElements.length).toBeGreaterThanOrEqual(3);
|
|
617
|
-
});
|
|
618
|
-
it('indexes flow and succession relationships by type', async () => {
|
|
619
|
-
const doc = await parseDoc(`
|
|
620
|
-
package Test {
|
|
621
|
-
action def A { out x : TypeX; }
|
|
622
|
-
action def B { in x : TypeX; }
|
|
623
|
-
action process {
|
|
624
|
-
action a : A;
|
|
625
|
-
action b : B;
|
|
626
|
-
flow of TypeX from a.x to b.x;
|
|
627
|
-
first start then a then b then done;
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
`);
|
|
631
|
-
const model = buildMemoModel([doc], behaviorConfig);
|
|
632
|
-
expect(model.relationshipsByType.get('flow')).toHaveLength(1);
|
|
633
|
-
expect(model.relationshipsByType.get('succession')).toHaveLength(3);
|
|
634
|
-
});
|
|
635
|
-
});
|
|
636
|
-
// ─── Behavior Validation Tests ──────────────────────────────────────────────
|
|
637
|
-
describe('Behavior validation', () => {
|
|
638
|
-
it('BV-001: warns on unallocated action usage', async () => {
|
|
639
|
-
const doc = await parseDoc(`
|
|
640
|
-
package Test {
|
|
641
|
-
action def DoWork { out result : Data; }
|
|
642
|
-
action process {
|
|
643
|
-
action work : DoWork;
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
`);
|
|
647
|
-
const model = buildMemoModel([doc], behaviorConfig);
|
|
648
|
-
const violations = validateBehavior(model);
|
|
649
|
-
const bv001 = violations.filter(v => v.ruleId === 'BV-001');
|
|
650
|
-
expect(bv001.length).toBeGreaterThan(0);
|
|
651
|
-
expect(bv001[0].description).toContain('not allocated');
|
|
652
|
-
});
|
|
653
|
-
it('BV-001: no warning when action is allocated', async () => {
|
|
654
|
-
const doc = await parseDoc(`
|
|
655
|
-
package Test {
|
|
656
|
-
part sw : Subsystem;
|
|
657
|
-
action work : SystemFunction;
|
|
658
|
-
allocate work to sw;
|
|
659
|
-
}
|
|
660
|
-
`);
|
|
661
|
-
const model = buildMemoModel([doc], behaviorConfig);
|
|
662
|
-
const workEl = model.elements.get('work');
|
|
663
|
-
expect(workEl?.allocatedTo).toBe('sw');
|
|
664
|
-
const violations = validateBehavior(model);
|
|
665
|
-
const bv001 = violations.filter(v => v.ruleId === 'BV-001' && v.elementName === 'work');
|
|
666
|
-
expect(bv001).toHaveLength(0);
|
|
667
|
-
});
|
|
668
|
-
it('BV-001/BV-002: composite actions with nested steps are exempt', async () => {
|
|
669
|
-
const doc = await parseDoc(`
|
|
670
|
-
package Test {
|
|
671
|
-
part sw : Subsystem;
|
|
672
|
-
action def DoWork { out result : Data; }
|
|
673
|
-
action process {
|
|
674
|
-
action work : DoWork;
|
|
675
|
-
first start then work then done;
|
|
676
|
-
}
|
|
677
|
-
allocate work to sw;
|
|
678
|
-
}
|
|
679
|
-
`);
|
|
680
|
-
const model = buildMemoModel([doc], behaviorConfig);
|
|
681
|
-
const violations = validateBehavior(model);
|
|
682
|
-
// 'process' is allocated and connected through its nested step
|
|
683
|
-
const wrapper = violations.filter(v => v.elementName === 'process');
|
|
684
|
-
expect(wrapper).toHaveLength(0);
|
|
685
|
-
});
|
|
686
|
-
it('BV-002: warns on orphan action (no flow/succession)', async () => {
|
|
687
|
-
const doc = await parseDoc(`
|
|
688
|
-
package Test {
|
|
689
|
-
action def A { out x : X; }
|
|
690
|
-
action def B { in x : X; }
|
|
691
|
-
action def Orphan;
|
|
692
|
-
action process {
|
|
693
|
-
action a : A;
|
|
694
|
-
action b : B;
|
|
695
|
-
action orphan : Orphan;
|
|
696
|
-
flow of X from a.x to b.x;
|
|
697
|
-
first start then a then b then done;
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
`);
|
|
701
|
-
const model = buildMemoModel([doc], behaviorConfig);
|
|
702
|
-
const violations = validateBehavior(model);
|
|
703
|
-
const bv002 = violations.filter(v => v.ruleId === 'BV-002');
|
|
704
|
-
// 'orphan' has no flow or succession connections
|
|
705
|
-
const orphanViolation = bv002.find(v => v.elementName === 'orphan');
|
|
706
|
-
expect(orphanViolation).toBeDefined();
|
|
707
|
-
});
|
|
708
|
-
it('BV-002: no warning when action has flow connections', async () => {
|
|
709
|
-
const doc = await parseDoc(`
|
|
710
|
-
package Test {
|
|
711
|
-
action def A { out x : X; }
|
|
712
|
-
action def B { in x : X; }
|
|
713
|
-
action process {
|
|
714
|
-
action a : A;
|
|
715
|
-
action b : B;
|
|
716
|
-
flow of X from a.x to b.x;
|
|
717
|
-
first start then a then b then done;
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
`);
|
|
721
|
-
const model = buildMemoModel([doc], behaviorConfig);
|
|
722
|
-
const violations = validateBehavior(model);
|
|
723
|
-
const bv002 = violations.filter(v => v.ruleId === 'BV-002');
|
|
724
|
-
// a and b both have flow connections, should not be flagged
|
|
725
|
-
const aOrB = bv002.filter(v => v.elementName === 'a' || v.elementName === 'b');
|
|
726
|
-
expect(aOrB).toHaveLength(0);
|
|
727
|
-
});
|
|
728
|
-
it('BV-003: errors on incompatible flow type', async () => {
|
|
729
|
-
const doc = await parseDoc(`
|
|
730
|
-
package Test {
|
|
731
|
-
action def Sender { out msg : MessageType; }
|
|
732
|
-
action def Receiver { in data : DifferentType; }
|
|
733
|
-
action process {
|
|
734
|
-
action send : Sender;
|
|
735
|
-
action recv : Receiver;
|
|
736
|
-
flow of MessageType from send.msg to recv.data;
|
|
737
|
-
first start then send then recv then done;
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
`);
|
|
741
|
-
const model = buildMemoModel([doc], behaviorConfig);
|
|
742
|
-
const violations = validateBehavior(model);
|
|
743
|
-
const bv003 = violations.filter(v => v.ruleId === 'BV-003');
|
|
744
|
-
// Receiver has no 'in' param of type MessageType → error
|
|
745
|
-
expect(bv003.length).toBeGreaterThan(0);
|
|
746
|
-
expect(bv003[0].severity).toBe('error');
|
|
747
|
-
});
|
|
748
|
-
it('BV-003: no error when flow types match', async () => {
|
|
749
|
-
const doc = await parseDoc(`
|
|
750
|
-
package Test {
|
|
751
|
-
action def Sender { out msg : DataType; }
|
|
752
|
-
action def Receiver { in msg : DataType; }
|
|
753
|
-
action process {
|
|
754
|
-
action send : Sender;
|
|
755
|
-
action recv : Receiver;
|
|
756
|
-
flow of DataType from send.msg to recv.msg;
|
|
757
|
-
}
|
|
758
|
-
}
|
|
759
|
-
`);
|
|
760
|
-
const model = buildMemoModel([doc], behaviorConfig);
|
|
761
|
-
const violations = validateBehavior(model);
|
|
762
|
-
const bv003 = violations.filter(v => v.ruleId === 'BV-003');
|
|
763
|
-
expect(bv003).toHaveLength(0);
|
|
764
|
-
});
|
|
765
|
-
});
|
|
766
|
-
//# sourceMappingURL=behavior.test.js.map
|