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/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("./utils/log");
6
+ const log = require("./logger");
5
7
  const validationMessages = require("../testing-utilities/validtionMessages");
6
8
  const { runFullTest } = new FullTestController();
7
9
 
8
- const PARTITION =
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 and/or service_url are required", "warning", "warning");
15
- console.log(`Example -> systemview test myAPI Users`);
16
- return;
17
+ log.warn("project_code required. Example: systemview test myAPI");
18
+ return 1;
17
19
  }
18
- // get connected services from the systemview api
19
- const connectedServices = await getConnectedServices(api, project_code);
20
- //get all the test for each service or the targeted services
21
- if (!connectedServices.length) {
22
- log("No connected services found!", "warning");
23
- console.log(
24
- "You may have to refresh your systemlynx apps for project_code: " + project_code
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! @${system.connectionData.serviceUrl}`, "success", serviceId);
29
+ log.success(`connected: ${serviceId} @ ${system.connectionData.serviceUrl}`);
30
30
  });
31
31
  }
32
32
 
33
- const testsReceived = await getTests(connectedServices);
34
- const testToRun = !namespace
35
- ? testsReceived
36
- : testsReceived
37
- .map((testList) => {
38
- return testList.filter(({ namespace: n }) => {
39
- return `${n.serviceId}.${n.moduleName}.${n.methodName}`.includes(namespace);
40
- });
41
- })
42
- .filter((testList) => testList.length);
43
-
44
- if (testToRun.length) {
45
- const summary = {};
46
- await new Promise((resolve) => {
47
- async function recursiveExecuteTest(i = 0) {
48
- if (i === testToRun.length) resolve();
49
- else {
50
- const { serviceId } = testToRun[i][0].namespace;
51
- log(`Initializing Tests...`, "info", serviceId);
52
-
53
- try {
54
- const tests = initializeSavedTests(testToRun[i], connectedServices);
55
- summary[serviceId] = await runAllTests(tests, url, project_code);
56
- } catch (error) {
57
- log(`Unexpected error halting test`, "error", serviceId);
58
- } finally {
59
- recursiveExecuteTest(i + 1);
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
- recursiveExecuteTest();
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(`No tests found matching ${project_code} ${namespace}`, "warning", "warning");
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 (savedTest, url, project_code) => {
82
- const sum = { passed: 0, failed: 0 };
83
- const runTest = async ({ Before, Main, Events, After }) => {
84
- const fullTest = [Before, Main, Events, After];
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
- console.log(PARTITION);
89
- log(
90
- `testing -> ${serviceId}.${moduleName}.${methodName}(...)`,
91
- "info",
92
- namespace.serviceId
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
- if (
101
- logResults(Before, "Before") +
102
- logResults(Main, "Main", true) +
103
- logResults(Events, "Events", true) +
104
- logResults(After, "After")
105
- ) {
106
- sum.failed++;
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
- sum.passed++;
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
- await new Promise((resolve) => {
113
- function recursiveRunTest(i = 0) {
114
- if (i === savedTest.length) resolve();
115
- else runTest(savedTest[i]).then(() => recursiveRunTest(i + 1));
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
- recursiveRunTest();
158
+ console.log(DIVIDER);
118
159
  });
160
+ }
119
161
 
120
- return sum;
121
- };
122
- function logResults(tests, section, logSuccess = false) {
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 LOG_TYPE = errors.length ? "error" : "success";
127
- const LABEL = LOG_TYPE === "error" ? "FAILED" : "PASSED";
128
-
129
- if (LOG_TYPE === "error" || logSuccess) {
130
- log(title, "info", section + ":");
131
- log(`${serviceId}.${moduleName}.${methodName}(...)`, LOG_TYPE, `${LABEL}`);
132
- console.log(`${response_type}:`, results);
133
- errors.length && log(`${errors.length} errors`, "warning", "validations");
134
- errors.forEach((err) => console.log(`-> ${validationMessages(err)}`));
135
- console.log(PARTITION);
136
- if (!failed && errors.length) failed = 1;
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
- return await new Promise(async (resolve) => {
143
- const results = [];
144
-
145
- return (async function recursiveGetTests(i = 0) {
146
- if (i < connectedServices.length) {
147
- const { connectionData } = connectedServices[i].system;
148
- try {
149
- results.push(await Client.createService(connectionData).Plugin.getTests());
150
- } catch (error) {
151
- console.log(`Failed to retrieve test from:${connectionData.serviceUrl}:\n`);
152
- }
153
-
154
- await recursiveGetTests(i + 1);
155
- } else resolve(results);
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
- try {
163
- return await SystemView.getServices(project_code);
164
- } catch (error) {}
230
+ return await SystemView.getServices(project_code);
165
231
  } catch (error) {
166
- console.log("Failed to connect to systemview", error);
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 meow = require("meow");
2
- const meowHelp = require("cli-meow-help");
1
+ const HELP_TEXT = `
2
+ SystemView Documentation and testing suite for SystemLynx
3
3
 
4
- const flags = {
5
- clear: {
6
- type: `boolean`,
7
- default: false,
8
- alias: `c`,
9
- desc: `Clear the console`,
10
- },
11
- noClear: {
12
- type: `boolean`,
13
- default: false,
14
- desc: `Don't clear the console`,
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
- const commands = {
30
- help: { desc: `Print help info` },
31
- test: { desc: `Run saved test` },
32
- start: { desc: `Launch SystemView UI` },
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 helpText = meowHelp({
36
- name: `SystemView`,
37
- flags,
38
- commands,
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
- const options = {
42
- inferType: true,
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
- const welcome = require("cli-welcome");
2
- const pkg = require("../../package.json");
3
- const unhandled = require("cli-handle-unhandled");
4
-
5
- module.exports = ({ clear = true }) => {
6
- unhandled();
7
- welcome({
8
- title: `SystemView`,
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
@@ -1,5 +1 @@
1
- const alert = require("cli-alerts");
2
-
3
- module.exports = (msg, type = "info", name = "SystemView") => {
4
- alert({ type, name, msg });
5
- };
1
+ module.exports = require("../logger");
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.16.10",
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.18.12",
26
- "systemlynx-client": "^1.18.12",
19
+ "systemlynx": "^1.19.12",
20
+ "systemlynx-client": "^1.19.14",
27
21
  "web-vitals": "^0.2.4"
28
22
  },
29
23
  "scripts": {