scjson 0.1.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/bin/scjson.js +11 -0
- package/index.js +243 -0
- package/package.json +53 -0
- package/tests/cli.test.js +172 -0
package/bin/scjson.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Agent Name: js-cli-runner
|
|
4
|
+
*
|
|
5
|
+
* Part of the scjson project.
|
|
6
|
+
* Developed by Softoboros Technology Inc.
|
|
7
|
+
* Licensed under the BSD 1-Clause License.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const { program } = require('../index.js');
|
|
11
|
+
program.parse(process.argv);
|
package/index.js
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Name: js-cli
|
|
3
|
+
*
|
|
4
|
+
* Part of the scjson project.
|
|
5
|
+
* Developed by Softoboros Technology Inc.
|
|
6
|
+
* Licensed under the BSD 1-Clause License.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const { Command } = require('commander');
|
|
12
|
+
const { XMLParser, XMLBuilder } = require('fast-xml-parser');
|
|
13
|
+
const Ajv = require('ajv');
|
|
14
|
+
|
|
15
|
+
const program = new Command();
|
|
16
|
+
const schema = require('../scjson.schema.json');
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Remove nulls and empty containers from values recursively.
|
|
20
|
+
*
|
|
21
|
+
* @param {*} value - Candidate value.
|
|
22
|
+
* @returns {*} Sanitised value.
|
|
23
|
+
*/
|
|
24
|
+
function removeEmpty(value) {
|
|
25
|
+
if (Array.isArray(value)) {
|
|
26
|
+
const arr = value.map(removeEmpty).filter(v => v !== undefined);
|
|
27
|
+
return arr.length > 0 ? arr : undefined;
|
|
28
|
+
}
|
|
29
|
+
if (value && typeof value === 'object') {
|
|
30
|
+
const obj = {};
|
|
31
|
+
for (const [k, v] of Object.entries(value)) {
|
|
32
|
+
const r = removeEmpty(v);
|
|
33
|
+
if (r !== undefined) obj[k] = r;
|
|
34
|
+
}
|
|
35
|
+
return Object.keys(obj).length > 0 ? obj : undefined;
|
|
36
|
+
}
|
|
37
|
+
if (value === null) {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
if (value === '') {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const ajv = new Ajv({ useDefaults: true, strict: false });
|
|
47
|
+
const validate = ajv.compile(schema);
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Convert an SCXML string to scjson.
|
|
51
|
+
*
|
|
52
|
+
* @param {string} xmlStr - XML input.
|
|
53
|
+
* @param {boolean} [omitEmpty=true] - Remove empty values when true.
|
|
54
|
+
* @returns {string} JSON representation.
|
|
55
|
+
*
|
|
56
|
+
* Removes the XML namespace attribute and injects default values
|
|
57
|
+
* expected by the schema.
|
|
58
|
+
*/
|
|
59
|
+
function xmlToJson(xmlStr, omitEmpty = true) {
|
|
60
|
+
const parser = new XMLParser({ ignoreAttributes: false });
|
|
61
|
+
let obj = parser.parse(xmlStr);
|
|
62
|
+
if (obj.scxml) {
|
|
63
|
+
obj = obj.scxml;
|
|
64
|
+
}
|
|
65
|
+
if (omitEmpty) {
|
|
66
|
+
obj = removeEmpty(obj) || {};
|
|
67
|
+
}
|
|
68
|
+
if (obj['@_xmlns']) {
|
|
69
|
+
delete obj['@_xmlns'];
|
|
70
|
+
}
|
|
71
|
+
if (obj.version === undefined) {
|
|
72
|
+
obj.version = 1.0;
|
|
73
|
+
}
|
|
74
|
+
if (obj.datamodel_attribute === undefined) {
|
|
75
|
+
obj.datamodel_attribute = 'null';
|
|
76
|
+
}
|
|
77
|
+
if (!validate(obj)) {
|
|
78
|
+
throw new Error('Invalid scjson');
|
|
79
|
+
}
|
|
80
|
+
if (omitEmpty) {
|
|
81
|
+
obj = removeEmpty(obj) || {};
|
|
82
|
+
}
|
|
83
|
+
return JSON.stringify(obj, null, 2);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Convert a scjson string to SCXML.
|
|
88
|
+
*
|
|
89
|
+
* @param {string} jsonStr - JSON input.
|
|
90
|
+
* @returns {string} XML output.
|
|
91
|
+
*/
|
|
92
|
+
function jsonToXml(jsonStr) {
|
|
93
|
+
const builder = new XMLBuilder({ ignoreAttributes: false, format: true });
|
|
94
|
+
const obj = JSON.parse(jsonStr);
|
|
95
|
+
if (!validate(obj)) {
|
|
96
|
+
throw new Error('Invalid scjson');
|
|
97
|
+
}
|
|
98
|
+
return builder.build({ scxml: obj });
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
program
|
|
102
|
+
.name('scjson')
|
|
103
|
+
.description('SCXML <-> scjson converter and validator');
|
|
104
|
+
|
|
105
|
+
program
|
|
106
|
+
.command('validate')
|
|
107
|
+
.description('validate a scjson or SCXML file by round-tripping it')
|
|
108
|
+
.argument('<file>', 'file path')
|
|
109
|
+
.option('-r, --recursive', 'recurse into directories')
|
|
110
|
+
.action((file, options) => {
|
|
111
|
+
const src = path.resolve(file);
|
|
112
|
+
let success = true;
|
|
113
|
+
|
|
114
|
+
function validateFile(p) {
|
|
115
|
+
const data = fs.readFileSync(p, 'utf8');
|
|
116
|
+
try {
|
|
117
|
+
if (p.endsWith('.scxml')) {
|
|
118
|
+
const json = xmlToJson(data);
|
|
119
|
+
jsonToXml(json);
|
|
120
|
+
} else if (p.endsWith('.scjson')) {
|
|
121
|
+
const xml = jsonToXml(data);
|
|
122
|
+
xmlToJson(xml);
|
|
123
|
+
} else {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
} catch (e) {
|
|
127
|
+
console.error(`Validation failed for ${p}: ${e.message}`);
|
|
128
|
+
success = false;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (fs.statSync(src).isDirectory()) {
|
|
133
|
+
const pattern = options.recursive ? '**/*' : '*';
|
|
134
|
+
const files = require('glob').sync(pattern, { cwd: src, nodir: true });
|
|
135
|
+
files.forEach(f => validateFile(path.join(src, f)));
|
|
136
|
+
} else {
|
|
137
|
+
validateFile(src);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (!success) {
|
|
141
|
+
process.exitCode = 1;
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
function convertDirectoryJson(inputDir, outputDir, recursive, verify, keepEmpty) {
|
|
146
|
+
const pattern = recursive ? '**/*.scxml' : '*.scxml';
|
|
147
|
+
const files = require('glob').sync(pattern, { cwd: inputDir, nodir: true });
|
|
148
|
+
files.forEach(f => {
|
|
149
|
+
const src = path.join(inputDir, f);
|
|
150
|
+
const dest = path.join(outputDir, f.replace(/\.scxml$/, '.scjson'));
|
|
151
|
+
convertScxmlFile(src, dest, verify, keepEmpty);
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function convertDirectoryXml(inputDir, outputDir, recursive, verify, keepEmpty) {
|
|
156
|
+
const pattern = recursive ? '**/*.scjson' : '*.scjson';
|
|
157
|
+
const files = require('glob').sync(pattern, { cwd: inputDir, nodir: true });
|
|
158
|
+
files.forEach(f => {
|
|
159
|
+
const src = path.join(inputDir, f);
|
|
160
|
+
const dest = path.join(outputDir, f.replace(/\.scjson$/, '.scxml'));
|
|
161
|
+
convertScjsonFile(src, dest, verify, keepEmpty);
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function convertScxmlFile(src, dest, verify, keepEmpty) {
|
|
166
|
+
const xmlStr = fs.readFileSync(src, 'utf8');
|
|
167
|
+
try {
|
|
168
|
+
const jsonStr = xmlToJson(xmlStr, !keepEmpty);
|
|
169
|
+
if (verify) {
|
|
170
|
+
jsonToXml(jsonStr);
|
|
171
|
+
} else {
|
|
172
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
173
|
+
fs.writeFileSync(dest, jsonStr);
|
|
174
|
+
}
|
|
175
|
+
if (verify) console.log(`Verified ${src}`);
|
|
176
|
+
} catch (e) {
|
|
177
|
+
console.error(`Failed to convert ${src}: ${e.message}`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function convertScjsonFile(src, dest, verify) {
|
|
182
|
+
const jsonStr = fs.readFileSync(src, 'utf8');
|
|
183
|
+
try {
|
|
184
|
+
const xmlStr = jsonToXml(jsonStr);
|
|
185
|
+
if (verify) {
|
|
186
|
+
xmlToJson(xmlStr);
|
|
187
|
+
} else {
|
|
188
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
189
|
+
fs.writeFileSync(dest, xmlStr);
|
|
190
|
+
}
|
|
191
|
+
if (verify) console.log(`Verified ${src}`);
|
|
192
|
+
} catch (e) {
|
|
193
|
+
console.error(`Failed to convert ${src}: ${e.message}`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
program
|
|
198
|
+
.command('json')
|
|
199
|
+
.argument('<path>', 'SCXML file or directory')
|
|
200
|
+
.option('-o, --output <path>', 'output file or directory')
|
|
201
|
+
.option('-r, --recursive', 'recurse into directories')
|
|
202
|
+
.option('-v, --verify', 'verify conversion without writing output')
|
|
203
|
+
.option('--keep-empty', 'keep null or empty items when producing JSON')
|
|
204
|
+
.action((p, opts) => {
|
|
205
|
+
const src = path.resolve(p);
|
|
206
|
+
const out = opts.output ? path.resolve(opts.output) : src;
|
|
207
|
+
|
|
208
|
+
if (fs.statSync(src).isDirectory()) {
|
|
209
|
+
convertDirectoryJson(src, out, opts.recursive, opts.verify, opts.keepEmpty);
|
|
210
|
+
} else {
|
|
211
|
+
const dest = opts.output && !opts.output.endsWith('.json') && !opts.output.endsWith('.scjson')
|
|
212
|
+
? path.join(out, path.basename(src).replace(/\.scxml$/, '.scjson'))
|
|
213
|
+
: (opts.output || src.replace(/\.scxml$/, '.scjson'));
|
|
214
|
+
convertScxmlFile(src, dest, opts.verify, opts.keepEmpty);
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
program
|
|
219
|
+
.command('xml')
|
|
220
|
+
.argument('<path>', 'scjson file or directory')
|
|
221
|
+
.option('-o, --output <path>', 'output file or directory')
|
|
222
|
+
.option('-r, --recursive', 'recurse into directories')
|
|
223
|
+
.option('-v, --verify', 'verify conversion without writing output')
|
|
224
|
+
.option('--keep-empty', 'keep null or empty items when producing JSON')
|
|
225
|
+
.action((p, opts) => {
|
|
226
|
+
const src = path.resolve(p);
|
|
227
|
+
const out = opts.output ? path.resolve(opts.output) : src;
|
|
228
|
+
|
|
229
|
+
if (fs.statSync(src).isDirectory()) {
|
|
230
|
+
convertDirectoryXml(src, out, opts.recursive, opts.verify, opts.keepEmpty);
|
|
231
|
+
} else {
|
|
232
|
+
const dest = opts.output && !opts.output.endsWith('.xml') && !opts.output.endsWith('.scxml')
|
|
233
|
+
? path.join(out, path.basename(src).replace(/\.scjson$/, '.scxml'))
|
|
234
|
+
: (opts.output || src.replace(/\.scjson$/, '.scxml'));
|
|
235
|
+
convertScjsonFile(src, dest, opts.verify, opts.keepEmpty);
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
if (require.main === module) {
|
|
240
|
+
program.parse(process.argv);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
module.exports = { program, xmlToJson, jsonToXml };
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "scjson",
|
|
3
|
+
"version": "0.1.5",
|
|
4
|
+
"description": "Convert SCXML to/from scjson (JSON-based representation of state machines)",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"scjson",
|
|
7
|
+
"scxml",
|
|
8
|
+
"state-machine",
|
|
9
|
+
"conversion",
|
|
10
|
+
"json",
|
|
11
|
+
"cli",
|
|
12
|
+
"xml"
|
|
13
|
+
],
|
|
14
|
+
"homepage": "https://github.com/SoftOboros/scjson/tree/main/js",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/SoftOboros/scjson/issues"
|
|
17
|
+
},
|
|
18
|
+
"license": "BSD-1-Clause",
|
|
19
|
+
"author": "Softoboros Technology Inc. <ira@softoboros.com>",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/SoftOboros/scjson.git"
|
|
23
|
+
},
|
|
24
|
+
"bin": {
|
|
25
|
+
"scjson": "bin/scjson.js"
|
|
26
|
+
},
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=18"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"bin/",
|
|
32
|
+
"index.js",
|
|
33
|
+
"tests/"
|
|
34
|
+
],
|
|
35
|
+
"main": "index.js",
|
|
36
|
+
"type": "module",
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"test": "jest",
|
|
42
|
+
"lint": "eslint"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"ajv": "^8.17.1",
|
|
46
|
+
"commander": "^14.0.0",
|
|
47
|
+
"eslint": "^9.31.0",
|
|
48
|
+
"fast-xml-parser": "^5.2.5"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"jest": "^30.0.4"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Name: js-cli-tests
|
|
3
|
+
*
|
|
4
|
+
* Part of the scjson project.
|
|
5
|
+
* Developed by Softoboros Technology Inc.
|
|
6
|
+
* Licensed under the BSD 1-Clause License.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const os = require('os');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const { spawnSync } = require('child_process');
|
|
13
|
+
|
|
14
|
+
const cliPath = path.resolve(__dirname, '../bin/scjson.js');
|
|
15
|
+
|
|
16
|
+
function createScxml() {
|
|
17
|
+
return '<scxml xmlns="http://www.w3.org/2005/07/scxml"/>';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function createScjson() {
|
|
21
|
+
const json = {
|
|
22
|
+
version: 1,
|
|
23
|
+
datamodel_attribute: 'null',
|
|
24
|
+
};
|
|
25
|
+
return JSON.stringify(json, null, 2);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
describe('scjson CLI', () => {
|
|
29
|
+
test('shows help', () => {
|
|
30
|
+
const res = spawnSync('node', [cliPath, '--help'], { encoding: 'utf8' });
|
|
31
|
+
expect(res.stderr).not.toMatch(/Failed to convert/);
|
|
32
|
+
expect(res.status).toBe(0);
|
|
33
|
+
expect(res.stdout).toMatch(/scjson/);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('single json conversion', () => {
|
|
37
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'scjson-'));
|
|
38
|
+
const xmlPath = path.join(dir, 'sample.scxml');
|
|
39
|
+
fs.writeFileSync(xmlPath, createScxml());
|
|
40
|
+
|
|
41
|
+
const res = spawnSync('node', [cliPath, 'json', xmlPath], { encoding: 'utf8' });
|
|
42
|
+
expect(res.stderr).not.toMatch(/Failed to convert/);
|
|
43
|
+
expect(res.status).toBe(0);
|
|
44
|
+
|
|
45
|
+
const outPath = path.join(dir, 'sample.scjson');
|
|
46
|
+
expect(fs.existsSync(outPath)).toBe(true);
|
|
47
|
+
const data = JSON.parse(fs.readFileSync(outPath, 'utf8'));
|
|
48
|
+
expect(data.version).toBe(1);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('directory json conversion', () => {
|
|
52
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'scjson-'));
|
|
53
|
+
const srcDir = path.join(dir, 'src');
|
|
54
|
+
fs.mkdirSync(srcDir);
|
|
55
|
+
['a', 'b'].forEach(n => {
|
|
56
|
+
fs.writeFileSync(path.join(srcDir, `${n}.scxml`), createScxml());
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const res = spawnSync('node', [cliPath, 'json', srcDir], { encoding: 'utf8' });
|
|
60
|
+
expect(res.stderr).not.toMatch(/Failed to convert/);
|
|
61
|
+
expect(res.status).toBe(0);
|
|
62
|
+
|
|
63
|
+
['a', 'b'].forEach(n => {
|
|
64
|
+
expect(fs.existsSync(path.join(srcDir, `${n}.scjson`))).toBe(true);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('single xml conversion', () => {
|
|
69
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'scjson-'));
|
|
70
|
+
const jsonPath = path.join(dir, 'sample.scjson');
|
|
71
|
+
fs.writeFileSync(jsonPath, createScjson());
|
|
72
|
+
|
|
73
|
+
const res = spawnSync('node', [cliPath, 'xml', jsonPath], { encoding: 'utf8' });
|
|
74
|
+
expect(res.stderr).not.toMatch(/Failed to convert/);
|
|
75
|
+
expect(res.status).toBe(0);
|
|
76
|
+
|
|
77
|
+
const outPath = path.join(dir, 'sample.scxml');
|
|
78
|
+
expect(fs.existsSync(outPath)).toBe(true);
|
|
79
|
+
const data = fs.readFileSync(outPath, 'utf8');
|
|
80
|
+
expect(data).toMatch(/scxml/);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('directory xml conversion', () => {
|
|
84
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'scjson-'));
|
|
85
|
+
const srcDir = path.join(dir, 'jsons');
|
|
86
|
+
fs.mkdirSync(srcDir);
|
|
87
|
+
['x', 'y'].forEach(n => {
|
|
88
|
+
fs.writeFileSync(path.join(srcDir, `${n}.scjson`), createScjson());
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const res = spawnSync('node', [cliPath, 'xml', srcDir], { encoding: 'utf8' });
|
|
92
|
+
expect(res.stderr).not.toMatch(/Failed to convert/);
|
|
93
|
+
expect(res.status).toBe(0);
|
|
94
|
+
|
|
95
|
+
['x', 'y'].forEach(n => {
|
|
96
|
+
expect(fs.existsSync(path.join(srcDir, `${n}.scxml`))).toBe(true);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
function buildDataset(base) {
|
|
101
|
+
const d1 = path.join(base, 'level1');
|
|
102
|
+
const d2 = path.join(d1, 'level2');
|
|
103
|
+
fs.mkdirSync(d2, { recursive: true });
|
|
104
|
+
['a', 'b'].forEach(n => {
|
|
105
|
+
fs.writeFileSync(path.join(d1, `${n}.scxml`), createScxml());
|
|
106
|
+
fs.writeFileSync(path.join(d2, `${n}.scxml`), createScxml());
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
test('recursive conversion', () => {
|
|
111
|
+
const dataset = fs.mkdtempSync(path.join(os.tmpdir(), 'scjson-'));
|
|
112
|
+
buildDataset(dataset);
|
|
113
|
+
const scjsonDir = path.join(dataset, 'outjson');
|
|
114
|
+
const scxmlDir = path.join(dataset, 'outxml');
|
|
115
|
+
|
|
116
|
+
let res = spawnSync('node', [cliPath, 'json', dataset, '-o', scjsonDir, '-r'], { encoding: 'utf8' });
|
|
117
|
+
expect(res.stderr).not.toMatch(/Failed to convert/);
|
|
118
|
+
expect(res.status).toBe(0);
|
|
119
|
+
res = spawnSync('node', [cliPath, 'xml', scjsonDir, '-o', scxmlDir, '-r'], { encoding: 'utf8' });
|
|
120
|
+
expect(res.stderr).not.toMatch(/Failed to convert/);
|
|
121
|
+
expect(res.status).toBe(0);
|
|
122
|
+
|
|
123
|
+
const jsonFiles = require('glob').sync('**/*.scjson', { cwd: scjsonDir, nodir: true });
|
|
124
|
+
const xmlFiles = require('glob').sync('**/*.scxml', { cwd: scxmlDir, nodir: true });
|
|
125
|
+
|
|
126
|
+
expect(jsonFiles.length).toBeGreaterThan(0);
|
|
127
|
+
expect(xmlFiles.length).toBeGreaterThan(0);
|
|
128
|
+
expect(xmlFiles.length).toBeLessThanOrEqual(jsonFiles.length);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test('recursive validation', () => {
|
|
132
|
+
const dataset = fs.mkdtempSync(path.join(os.tmpdir(), 'scjson-'));
|
|
133
|
+
buildDataset(dataset);
|
|
134
|
+
const scjsonDir = path.join(dataset, 'outjson');
|
|
135
|
+
const scxmlDir = path.join(dataset, 'outxml');
|
|
136
|
+
|
|
137
|
+
let res = spawnSync('node', [cliPath, 'json', dataset, '-o', scjsonDir, '-r'], { encoding: 'utf8' });
|
|
138
|
+
expect(res.stderr).not.toMatch(/Failed to convert/);
|
|
139
|
+
expect(res.status).toBe(0);
|
|
140
|
+
res = spawnSync('node', [cliPath, 'xml', scjsonDir, '-o', scxmlDir, '-r'], { encoding: 'utf8' });
|
|
141
|
+
expect(res.stderr).not.toMatch(/Failed to convert/);
|
|
142
|
+
expect(res.status).toBe(0);
|
|
143
|
+
|
|
144
|
+
// Corrupt one file to trigger failure
|
|
145
|
+
fs.writeFileSync(path.join(scjsonDir, 'corrupt.scjson'), 'bad');
|
|
146
|
+
|
|
147
|
+
const validateRes = spawnSync('node', [cliPath, 'validate', dataset, '-r'], { encoding: 'utf8' });
|
|
148
|
+
expect(validateRes.stderr).toMatch(/Validation failed/);
|
|
149
|
+
expect(validateRes.status).not.toBe(0);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test('recursive verify', () => {
|
|
153
|
+
const dataset = fs.mkdtempSync(path.join(os.tmpdir(), 'scjson-'));
|
|
154
|
+
buildDataset(dataset);
|
|
155
|
+
const scjsonDir = path.join(dataset, 'outjson');
|
|
156
|
+
const scxmlDir = path.join(dataset, 'outxml');
|
|
157
|
+
|
|
158
|
+
let res = spawnSync('node', [cliPath, 'json', dataset, '-o', scjsonDir, '-r'], { encoding: 'utf8' });
|
|
159
|
+
expect(res.stderr).not.toMatch(/Failed to convert/);
|
|
160
|
+
expect(res.status).toBe(0);
|
|
161
|
+
res = spawnSync('node', [cliPath, 'xml', scjsonDir, '-o', scxmlDir, '-r'], { encoding: 'utf8' });
|
|
162
|
+
expect(res.stderr).not.toMatch(/Failed to convert/);
|
|
163
|
+
expect(res.status).toBe(0);
|
|
164
|
+
|
|
165
|
+
res = spawnSync('node', [cliPath, 'json', scxmlDir, '-r', '-v'], { encoding: 'utf8' });
|
|
166
|
+
expect(res.stderr).not.toMatch(/Failed to convert/);
|
|
167
|
+
expect(res.status).toBe(0);
|
|
168
|
+
res = spawnSync('node', [cliPath, 'xml', scjsonDir, '-r', '-v'], { encoding: 'utf8' });
|
|
169
|
+
expect(res.stderr).not.toMatch(/Failed to convert/);
|
|
170
|
+
expect(res.status).toBe(0);
|
|
171
|
+
});
|
|
172
|
+
});
|