systemview 1.16.10 → 1.17.0
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 +154 -38
- package/api/connections.json +1 -1
- package/build/asset-manifest.json +3 -3
- package/build/index.html +1 -1
- package/build/static/js/{main.a2a48889.js → main.e94b392a.js} +3 -3
- package/build/static/js/main.e94b392a.js.map +1 -0
- package/cli/connectService.js +78 -0
- package/cli/index.js +58 -65
- package/cli/launchApp.js +18 -11
- package/cli/logger.js +9 -0
- package/cli/probe.js +71 -0
- package/cli/runTests.js +193 -126
- package/cli/utils/cli.js +46 -40
- package/cli/utils/init.js +8 -15
- package/cli/utils/log.js +1 -5
- package/package.json +3 -9
- package/build/static/js/main.a2a48889.js.map +0 -1
- /package/build/static/js/{main.a2a48889.js.LICENSE.txt → main.e94b392a.js.LICENSE.txt} +0 -0
package/cli/runTests.js
CHANGED
|
@@ -1,168 +1,235 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
1
3
|
const { Client } = require("systemlynx");
|
|
2
4
|
const { initializeSavedTests } = require("../testing-utilities/transformTests");
|
|
3
5
|
const FullTestController = require("../testing-utilities/FullTestController");
|
|
4
|
-
const log = require("./
|
|
6
|
+
const log = require("./logger");
|
|
5
7
|
const validationMessages = require("../testing-utilities/validtionMessages");
|
|
6
8
|
const { runFullTest } = new FullTestController();
|
|
7
9
|
|
|
8
|
-
const
|
|
9
|
-
"
|
|
10
|
+
const chalk = require("chalk");
|
|
11
|
+
const DIVIDER = chalk.dim(" " + "─".repeat(60));
|
|
10
12
|
|
|
11
|
-
module.exports = async function runTests(url, project_code, namespace) {
|
|
13
|
+
module.exports = async function runTests(url, project_code, namespace, { json = false, verbose = false, manifest: manifestPath } = {}) {
|
|
12
14
|
const api = `${url}/systemview/api`;
|
|
15
|
+
|
|
13
16
|
if (!project_code) {
|
|
14
|
-
log("project_code
|
|
15
|
-
|
|
16
|
-
return;
|
|
17
|
+
log.warn("project_code required. Example: systemview test myAPI");
|
|
18
|
+
return 1;
|
|
17
19
|
}
|
|
18
|
-
|
|
19
|
-
const connectedServices = await
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return;
|
|
27
|
-
} else {
|
|
20
|
+
|
|
21
|
+
const connectedServices = await resolveServices(api, project_code, manifestPath);
|
|
22
|
+
if (!connectedServices || !connectedServices.length) {
|
|
23
|
+
log.warn("No connected services found for project: " + project_code);
|
|
24
|
+
return 1;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (!json) {
|
|
28
28
|
connectedServices.forEach(({ serviceId, system }) => {
|
|
29
|
-
log(`connected
|
|
29
|
+
log.success(`connected: ${serviceId} @ ${system.connectionData.serviceUrl}`);
|
|
30
30
|
});
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
const
|
|
34
|
-
const testToRun =
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
if (testToRun.length) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
33
|
+
const allTestLists = await getTests(connectedServices);
|
|
34
|
+
const testToRun = allTestLists
|
|
35
|
+
.map((list) =>
|
|
36
|
+
namespace
|
|
37
|
+
? list.filter(({ namespace: n }) =>
|
|
38
|
+
`${n.serviceId}.${n.moduleName}.${n.methodName}`.includes(namespace)
|
|
39
|
+
)
|
|
40
|
+
: list
|
|
41
|
+
)
|
|
42
|
+
.filter((list) => list.length);
|
|
43
|
+
|
|
44
|
+
if (!testToRun.length) {
|
|
45
|
+
log.warn(`No tests found matching: ${project_code}${namespace ? " " + namespace : ""}`);
|
|
46
|
+
return 0;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const jsonOutput = json ? { projectCode: project_code, passed: 0, failed: 0, tests: [] } : null;
|
|
50
|
+
let totalFailed = 0;
|
|
51
|
+
|
|
52
|
+
for (const testList of testToRun) {
|
|
53
|
+
const { serviceId } = testList[0].namespace;
|
|
54
|
+
if (!json) log.info(`Initializing tests for ${serviceId}...`);
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
const initialized = initializeSavedTests(testList, connectedServices);
|
|
58
|
+
const { passed, failed, jsonTests } = await runAllTests(initialized, url, project_code, json, verbose);
|
|
59
|
+
totalFailed += failed;
|
|
60
|
+
|
|
61
|
+
if (json) {
|
|
62
|
+
jsonOutput.passed += passed;
|
|
63
|
+
jsonOutput.failed += failed;
|
|
64
|
+
jsonOutput.tests.push(...jsonTests);
|
|
65
|
+
} else {
|
|
66
|
+
log[failed ? "error" : "success"](
|
|
67
|
+
`${serviceId}: ${passed + failed} tests, ${passed} passed, ${failed} failed`
|
|
68
|
+
);
|
|
62
69
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
console.log(PARTITION);
|
|
66
|
-
console.log(PARTITION);
|
|
67
|
-
log("TEST COMPLETE!", "info");
|
|
68
|
-
for (let key in summary) {
|
|
69
|
-
const { passed, failed } = summary[key];
|
|
70
|
-
log(
|
|
71
|
-
`tests: ${passed + failed}, passed: ${passed}, failed: ${failed}`,
|
|
72
|
-
failed ? "error" : "success",
|
|
73
|
-
key
|
|
74
|
-
);
|
|
70
|
+
} catch (error) {
|
|
71
|
+
log.error(`Unexpected error for ${serviceId}: ${error.message}`);
|
|
75
72
|
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (json) {
|
|
76
|
+
process.stdout.write(JSON.stringify(jsonOutput, null, 2) + "\n");
|
|
76
77
|
} else {
|
|
77
|
-
log(
|
|
78
|
+
console.log("");
|
|
79
|
+
log.info("TEST COMPLETE");
|
|
80
|
+
console.log("");
|
|
78
81
|
}
|
|
82
|
+
|
|
83
|
+
return totalFailed > 0 ? 1 : 0;
|
|
79
84
|
};
|
|
80
85
|
|
|
81
|
-
const runAllTests = async (
|
|
82
|
-
const sum = { passed: 0, failed: 0 };
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const { namespace } = Main[0];
|
|
86
|
+
const runAllTests = async (savedTests, url, project_code, json, verbose) => {
|
|
87
|
+
const sum = { passed: 0, failed: 0, jsonTests: [] };
|
|
88
|
+
|
|
89
|
+
for (const { Before, Main, Events, After, title, namespace } of savedTests) {
|
|
86
90
|
const { moduleName, methodName, serviceId } = namespace;
|
|
91
|
+
const fullTest = [Before, Main, Events, After];
|
|
87
92
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
console.log(
|
|
95
|
-
`systemview ui -> @${url}/${project_code}/${serviceId}/${moduleName}/${methodName}`
|
|
96
|
-
);
|
|
97
|
-
console.log(PARTITION);
|
|
93
|
+
if (!json) {
|
|
94
|
+
console.log("");
|
|
95
|
+
console.log(" " + chalk.bold.cyan(`${serviceId}.${moduleName}.${methodName}(...)`));
|
|
96
|
+
console.log(chalk.dim(` UI → ${url}/${project_code}/${serviceId}/${moduleName}/${methodName}`));
|
|
97
|
+
console.log("");
|
|
98
|
+
}
|
|
98
99
|
|
|
99
100
|
await runFullTest(fullTest);
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
101
|
+
|
|
102
|
+
const hasFailed =
|
|
103
|
+
countErrors(Before) + countErrors(Main) + countErrors(Events) + countErrors(After) > 0;
|
|
104
|
+
|
|
105
|
+
if (hasFailed) sum.failed++;
|
|
106
|
+
else sum.passed++;
|
|
107
|
+
|
|
108
|
+
if (json) {
|
|
109
|
+
sum.jsonTests.push({
|
|
110
|
+
title,
|
|
111
|
+
serviceId,
|
|
112
|
+
moduleName,
|
|
113
|
+
methodName,
|
|
114
|
+
status: hasFailed ? "failed" : "passed",
|
|
115
|
+
Before: serializePhase(Before),
|
|
116
|
+
Main: serializePhase(Main),
|
|
117
|
+
Events: serializePhase(Events),
|
|
118
|
+
After: serializePhase(After),
|
|
119
|
+
});
|
|
107
120
|
} else {
|
|
108
|
-
|
|
121
|
+
logPhase(Before, "Before", verbose, verbose);
|
|
122
|
+
logPhase(Main, "Main", true, verbose);
|
|
123
|
+
logPhase(Events, "Events", verbose, verbose);
|
|
124
|
+
logPhase(After, "After", verbose, verbose);
|
|
109
125
|
}
|
|
110
|
-
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return sum;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
function countErrors(tests) {
|
|
132
|
+
return tests.reduce((n, t) => n + (t.errors ? t.errors.length : 0), 0);
|
|
133
|
+
}
|
|
111
134
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
135
|
+
function logPhase(tests, section, logSuccess = false, verbose = false) {
|
|
136
|
+
tests.forEach(({ errors, results, namespace, title, args }) => {
|
|
137
|
+
const { serviceId, moduleName, methodName } = namespace;
|
|
138
|
+
const failed = errors && errors.length;
|
|
139
|
+
if (!failed && !logSuccess) return;
|
|
140
|
+
console.log(chalk.dim(` ${section}: ${title}`));
|
|
141
|
+
failed
|
|
142
|
+
? log.error(` ${serviceId}.${moduleName}.${methodName}(...)`)
|
|
143
|
+
: log.success(` ${serviceId}.${moduleName}.${methodName}(...)`);
|
|
144
|
+
if (verbose && args && args.length) {
|
|
145
|
+
console.log("");
|
|
146
|
+
args.forEach(({ name, input }) => {
|
|
147
|
+
console.log(` ${chalk.dim(name)}`, JSON.stringify(input));
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
console.log("");
|
|
151
|
+
console.log(" results:", JSON.stringify(results, null, 2).replace(/\n/g, "\n "));
|
|
152
|
+
console.log("");
|
|
153
|
+
if (failed) {
|
|
154
|
+
log.warn(` ${errors.length} validation error(s)`);
|
|
155
|
+
errors.forEach((err) => console.log(` → ${validationMessages(err)}`));
|
|
156
|
+
console.log("");
|
|
116
157
|
}
|
|
117
|
-
|
|
158
|
+
console.log(DIVIDER);
|
|
118
159
|
});
|
|
160
|
+
}
|
|
119
161
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
let failed = 0;
|
|
124
|
-
tests.forEach(({ errors, results, namespace, title, response_type }) => {
|
|
162
|
+
function serializePhase(tests) {
|
|
163
|
+
return tests.map((test) => {
|
|
164
|
+
const { title, namespace, args, results, errors, evaluations } = test;
|
|
125
165
|
const { serviceId, moduleName, methodName } = namespace;
|
|
126
|
-
const
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
166
|
+
const hasFailed = errors && errors.length > 0;
|
|
167
|
+
const entry = {
|
|
168
|
+
title,
|
|
169
|
+
serviceId,
|
|
170
|
+
moduleName,
|
|
171
|
+
methodName,
|
|
172
|
+
args: args.map((a) => ({ name: a.name, input: a.input })),
|
|
173
|
+
status: hasFailed ? "failed" : "passed",
|
|
174
|
+
response: results,
|
|
175
|
+
};
|
|
176
|
+
if (hasFailed) {
|
|
177
|
+
entry.failedEvaluations = (evaluations || [])
|
|
178
|
+
.filter((e) => e.save && e.errors && e.errors.length)
|
|
179
|
+
.map((e) => ({
|
|
180
|
+
namespace: e.namespace,
|
|
181
|
+
expected_type: e.expected_type,
|
|
182
|
+
validations: e.validations,
|
|
183
|
+
received: e.value,
|
|
184
|
+
}));
|
|
137
185
|
}
|
|
186
|
+
return entry;
|
|
138
187
|
});
|
|
139
|
-
return failed;
|
|
140
188
|
}
|
|
189
|
+
|
|
141
190
|
async function getTests(connectedServices) {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
191
|
+
const results = [];
|
|
192
|
+
for (const { system } of connectedServices) {
|
|
193
|
+
const { connectionData } = system;
|
|
194
|
+
try {
|
|
195
|
+
results.push(await Client.createService(connectionData).Plugin.getTests());
|
|
196
|
+
} catch (error) {
|
|
197
|
+
log.warn(`Failed to retrieve tests from ${connectionData.serviceUrl}`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return results;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async function resolveServices(api, project_code, manifestPath) {
|
|
204
|
+
const manifestFile = manifestPath || path.join(process.cwd(), "systemview.manifest.json");
|
|
205
|
+
|
|
206
|
+
if (fs.existsSync(manifestFile)) {
|
|
207
|
+
try {
|
|
208
|
+
const raw = JSON.parse(fs.readFileSync(manifestFile, "utf8"));
|
|
209
|
+
// Support both { projectCode, services: [] } and single-service { projectCode, serviceId, system, specList }
|
|
210
|
+
const services = raw.services
|
|
211
|
+
? raw.services
|
|
212
|
+
: [{ serviceId: raw.serviceId, system: raw.system, specList: raw.specList }];
|
|
213
|
+
|
|
214
|
+
if (project_code && raw.projectCode && raw.projectCode !== project_code) {
|
|
215
|
+
log.warn(`Manifest projectCode "${raw.projectCode}" doesn't match "${project_code}", falling back to API`);
|
|
216
|
+
} else {
|
|
217
|
+
return services;
|
|
218
|
+
}
|
|
219
|
+
} catch (err) {
|
|
220
|
+
log.warn(`Failed to read manifest: ${err.message}`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return await getConnectedServices(api, project_code);
|
|
158
225
|
}
|
|
226
|
+
|
|
159
227
|
async function getConnectedServices(api, project_code) {
|
|
160
228
|
try {
|
|
161
229
|
const { SystemView } = await Client.loadService(api);
|
|
162
|
-
|
|
163
|
-
return await SystemView.getServices(project_code);
|
|
164
|
-
} catch (error) {}
|
|
230
|
+
return await SystemView.getServices(project_code);
|
|
165
231
|
} catch (error) {
|
|
166
|
-
|
|
232
|
+
log.error("Failed to connect to SystemView: " + error.message);
|
|
233
|
+
return [];
|
|
167
234
|
}
|
|
168
235
|
}
|
package/cli/utils/cli.js
CHANGED
|
@@ -1,48 +1,54 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
1
|
+
const HELP_TEXT = `
|
|
2
|
+
SystemView — Documentation and testing suite for SystemLynx
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
},
|
|
16
|
-
debug: {
|
|
17
|
-
type: `boolean`,
|
|
18
|
-
default: false,
|
|
19
|
-
alias: `d`,
|
|
20
|
-
desc: `Print debug info`,
|
|
21
|
-
},
|
|
22
|
-
version: {
|
|
23
|
-
type: `boolean`,
|
|
24
|
-
alias: `v`,
|
|
25
|
-
desc: `Print CLI version`,
|
|
26
|
-
},
|
|
27
|
-
};
|
|
4
|
+
Usage:
|
|
5
|
+
systemview [command] [args] [flags]
|
|
6
|
+
|
|
7
|
+
Commands:
|
|
8
|
+
start [port] Launch SystemView UI (default port 3000)
|
|
9
|
+
test <projectCode> [namespace] Run saved tests for a project
|
|
10
|
+
connect <serviceId> <url> Register a service and write manifest
|
|
11
|
+
connect Re-probe all services in existing manifest
|
|
12
|
+
probe <ServiceId.Module.method> [args] Call a method ad-hoc
|
|
13
|
+
open [projectCode] [namespace] Open the browser UI
|
|
14
|
+
shutdown [port] Stop a running SystemView instance
|
|
28
15
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
16
|
+
Flags:
|
|
17
|
+
--json Output results as JSON (for agents/CI)
|
|
18
|
+
--verbose Print all phases including Before/After on pass
|
|
19
|
+
--manifest <path> Path to manifest file (default: ./systemview.manifest.json)
|
|
20
|
+
|
|
21
|
+
Examples:
|
|
22
|
+
systemview start
|
|
23
|
+
systemview test buAPI
|
|
24
|
+
systemview test buAPI Users.signUp --json
|
|
25
|
+
systemview connect ProfilesService http://localhost:4100/bu/api/profiles
|
|
26
|
+
systemview probe ProfilesService.Users.getUser '{"userId":"123"}'
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
const rawArgs = process.argv.slice(2);
|
|
30
|
+
|
|
31
|
+
const flags = {
|
|
32
|
+
json: rawArgs.includes("--json"),
|
|
33
|
+
verbose: rawArgs.includes("--verbose") || rawArgs.includes("-v"),
|
|
34
|
+
debug: rawArgs.includes("--debug") || rawArgs.includes("-d"),
|
|
35
|
+
manifest: (() => {
|
|
36
|
+
const i = rawArgs.indexOf("--manifest");
|
|
37
|
+
return i !== -1 ? rawArgs[i + 1] : null;
|
|
38
|
+
})(),
|
|
33
39
|
};
|
|
34
40
|
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
41
|
+
const input = rawArgs.filter((a, i) => {
|
|
42
|
+
if (a.startsWith("-")) return false;
|
|
43
|
+
if (i > 0 && rawArgs[i - 1] === "--manifest") return false;
|
|
44
|
+
return true;
|
|
39
45
|
});
|
|
40
46
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
description: false,
|
|
44
|
-
hardRejection: false,
|
|
47
|
+
module.exports = {
|
|
48
|
+
input,
|
|
45
49
|
flags,
|
|
50
|
+
showHelp() {
|
|
51
|
+
console.log(HELP_TEXT);
|
|
52
|
+
process.exit(0);
|
|
53
|
+
},
|
|
46
54
|
};
|
|
47
|
-
|
|
48
|
-
module.exports = meow(helpText, options);
|
package/cli/utils/init.js
CHANGED
|
@@ -1,17 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
tagLine: `by Odion Edwards`,
|
|
10
|
-
description: pkg.description,
|
|
11
|
-
version: pkg.version,
|
|
12
|
-
bgColor: "#36BB09",
|
|
13
|
-
color: "#000000",
|
|
14
|
-
bold: true,
|
|
15
|
-
clear,
|
|
1
|
+
module.exports = function init() {
|
|
2
|
+
process.on("uncaughtException", (err) => {
|
|
3
|
+
console.error(err);
|
|
4
|
+
process.exit(1);
|
|
5
|
+
});
|
|
6
|
+
process.on("unhandledRejection", (err) => {
|
|
7
|
+
console.error(err);
|
|
8
|
+
process.exit(1);
|
|
16
9
|
});
|
|
17
10
|
};
|
package/cli/utils/log.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "systemview",
|
|
3
3
|
"description": "A documentation and testing suite for SystemLynx",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.17.0",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"bin": {
|
|
7
7
|
"systemview": "cli/index.js"
|
|
@@ -14,16 +14,10 @@
|
|
|
14
14
|
"@testing-library/react": "^11.1.0",
|
|
15
15
|
"@testing-library/user-event": "^12.1.10",
|
|
16
16
|
"chalk": "^4.1.2",
|
|
17
|
-
"cli-alerts": "^1.2.2",
|
|
18
|
-
"cli-handle-error": "^4.4.0",
|
|
19
|
-
"cli-handle-unhandled": "^1.1.1",
|
|
20
|
-
"cli-meow-help": "^3.1.0",
|
|
21
|
-
"cli-welcome": "^2.2.2",
|
|
22
17
|
"express": "^4.18.2",
|
|
23
|
-
"meow": "^9.0.0",
|
|
24
18
|
"moment": "^2.29.1",
|
|
25
|
-
"systemlynx": "^1.
|
|
26
|
-
"systemlynx-client": "^1.
|
|
19
|
+
"systemlynx": "^1.19.12",
|
|
20
|
+
"systemlynx-client": "^1.19.14",
|
|
27
21
|
"web-vitals": "^0.2.4"
|
|
28
22
|
},
|
|
29
23
|
"scripts": {
|