eyeling 1.5.17 → 1.5.18

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyeling",
3
- "version": "1.5.17",
3
+ "version": "1.5.18",
4
4
  "description": "A minimal Notation3 (N3) reasoner in JavaScript.",
5
5
  "main": "./index.js",
6
6
  "keywords": [
@@ -15,16 +15,6 @@ function ok(msg) { console.log(`${C.g}OK${C.n} ${msg}`); }
15
15
  function fail(msg) { console.error(`${C.r}FAIL${C.n} ${msg}`); }
16
16
  function info(msg) { console.log(`${C.y}==${C.n} ${msg}`); }
17
17
 
18
- function padRight(s, n) {
19
- s = String(s);
20
- return s.length >= n ? s : (s + ' '.repeat(n - s.length));
21
- }
22
-
23
- function padLeft(s, n) {
24
- s = String(s);
25
- return s.length >= n ? s : (' '.repeat(n - s.length) + s);
26
- }
27
-
28
18
  function run(cmd, args, opts = {}) {
29
19
  return cp.spawnSync(cmd, args, {
30
20
  encoding: 'utf8',
@@ -44,8 +34,8 @@ function inGitWorktree(cwd) {
44
34
  return r.status === 0 && String(r.stdout).trim() === 'true';
45
35
  }
46
36
 
47
- // Expectation logic (same as bash version):
48
- // 1) If file contains a comment like: # expect-exit: 2 -> use that
37
+ // Expectation logic:
38
+ // 1) If file contains: # expect-exit: N -> use N
49
39
  // 2) Else, if it contains "=> false" -> expect exit 2
50
40
  // 3) Else -> expect exit 0
51
41
  function expectedExitCode(n3Text) {
@@ -57,25 +47,45 @@ function expectedExitCode(n3Text) {
57
47
 
58
48
  function getEyelingVersion(nodePath, eyelingJsPath, cwd) {
59
49
  const r = run(nodePath, [eyelingJsPath, '-v'], { cwd });
60
- // eyeling prints version to stdout in your CLI
61
50
  const s = (r.stdout || r.stderr || '').trim();
62
51
  return s || 'eyeling (unknown version)';
63
52
  }
64
53
 
65
- function mkTmpFile() {
54
+ function mkTmpDir() {
66
55
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'eyeling-examples-'));
67
- const file = path.join(dir, 'generated.n3');
68
- return { dir, file };
56
+ return dir;
69
57
  }
70
58
 
71
59
  function rmrf(p) {
72
60
  try { fs.rmSync(p, { recursive: true, force: true }); } catch {}
73
61
  }
74
62
 
63
+ function showDiff({ IN_GIT, examplesDir, expectedPath, generatedPath, relExpectedPosix }) {
64
+ if (hasGit()) {
65
+ if (IN_GIT) {
66
+ // Show repo diff for the overwritten golden file
67
+ const d = run('git', ['diff', '--', relExpectedPosix], { cwd: examplesDir });
68
+ if (d.stdout) process.stdout.write(d.stdout);
69
+ if (d.stderr) process.stderr.write(d.stderr);
70
+ } else {
71
+ // Show no-index diff between packaged golden and generated tmp
72
+ const d = run('git', ['diff', '--no-index', expectedPath, generatedPath], { cwd: examplesDir });
73
+ // Replace tmp path in output (nice UX)
74
+ if (d.stdout) process.stdout.write(String(d.stdout).replaceAll(generatedPath, 'generated'));
75
+ if (d.stderr) process.stderr.write(String(d.stderr).replaceAll(generatedPath, 'generated'));
76
+ }
77
+ } else {
78
+ // Fallback: diff -u
79
+ const d = run('diff', ['-u', expectedPath, generatedPath], { cwd: examplesDir });
80
+ if (d.stdout) process.stdout.write(d.stdout);
81
+ if (d.stderr) process.stderr.write(d.stderr);
82
+ }
83
+ }
84
+
75
85
  function main() {
76
86
  const suiteStart = Date.now();
77
87
 
78
- // package root: .../test/examples.test.js -> root is one level up
88
+ // test/examples.test.js -> repo root is one level up
79
89
  const root = path.resolve(__dirname, '..');
80
90
  const examplesDir = path.join(root, 'examples');
81
91
  const outputDir = path.join(examplesDir, 'output');
@@ -83,107 +93,100 @@ function main() {
83
93
  const nodePath = process.execPath;
84
94
 
85
95
  if (!fs.existsSync(examplesDir)) {
86
- fail(`Missing examples directory: ${examplesDir}`);
96
+ fail(`Cannot find examples directory: ${examplesDir}`);
87
97
  process.exit(1);
88
98
  }
89
99
  if (!fs.existsSync(eyelingJsPath)) {
90
- fail(`Missing eyeling.js: ${eyelingJsPath}`);
100
+ fail(`Cannot find eyeling.js: ${eyelingJsPath}`);
91
101
  process.exit(1);
92
102
  }
93
103
 
94
104
  const IN_GIT = inGitWorktree(root);
95
105
 
96
- // Header
97
- console.log(`${C.y}-------------------------------------------------${C.n}`);
98
- console.log(`${C.y}running eyeling examples${C.n}`);
99
- console.log(
100
- `${C.y}using ${getEyelingVersion(nodePath, eyelingJsPath, root)} and node ${process.version}${C.n}`
101
- );
102
- console.log(`${C.y}-------------------------------------------------${C.n}`);
103
- console.log('');
104
-
105
- // In maintainer mode we write expected outputs (tracked) to examples/output/
106
- if (IN_GIT) fs.mkdirSync(outputDir, { recursive: true });
107
-
108
106
  const files = fs.readdirSync(examplesDir)
109
107
  .filter(f => f.endsWith('.n3'))
110
108
  .sort((a, b) => a.localeCompare(b));
111
109
 
110
+ info(
111
+ `Running ${files.length} examples tests (${IN_GIT ? 'git worktree mode' : 'npm-installed mode'})`
112
+ );
113
+ console.log(`${C.dim}${getEyelingVersion(nodePath, eyelingJsPath, root)}; node ${process.version}${C.n}`);
114
+
112
115
  if (files.length === 0) {
113
- info('No .n3 files found in examples/');
116
+ ok('No .n3 files found in examples/');
114
117
  process.exit(0);
115
118
  }
116
119
 
117
- let OK = 0;
118
- let DIFF = 0;
120
+ // In maintainer mode we overwrite tracked goldens in examples/output/
121
+ if (IN_GIT) fs.mkdirSync(outputDir, { recursive: true });
119
122
 
120
- for (const file of files) {
121
- const filePath = path.join(examplesDir, file);
122
- const expectedPath = path.join(outputDir, file); // examples/output/<file>
123
+ let passed = 0;
124
+ let failed = 0;
125
+
126
+ for (let i = 0; i < files.length; i++) {
127
+ const idx = String(i + 1).padStart(2, '0');
128
+ const file = files[i];
123
129
 
124
130
  const start = Date.now();
125
131
 
126
- let n3Text = '';
132
+ const filePath = path.join(examplesDir, file);
133
+ const expectedPath = path.join(outputDir, file);
134
+ const relExpectedPosix = path.posix.join('output', file); // for git diff inside examplesDir
135
+
136
+ let n3Text;
127
137
  try {
128
138
  n3Text = fs.readFileSync(filePath, 'utf8');
129
139
  } catch (e) {
130
140
  const ms = Date.now() - start;
131
- process.stdout.write(padRight(file, 36));
132
- process.stdout.write(`${C.y}${padLeft(`${ms} ms`, 10)}${C.n} `);
133
- console.log(`${C.r}DIFF${C.n} (cannot read input: ${e.message})`);
134
- DIFF++;
141
+ fail(`${idx} ${file} (${ms} ms)`);
142
+ fail(`Cannot read input: ${e.message}`);
143
+ failed++;
135
144
  continue;
136
145
  }
137
146
 
138
147
  const expectedRc = expectedExitCode(n3Text);
139
148
 
140
- // Decide where to write generated output
141
- let tmp = null;
149
+ // Decide where generated output goes
150
+ let tmpDir = null;
142
151
  let generatedPath = expectedPath;
143
152
 
144
153
  if (!IN_GIT) {
145
154
  // npm-installed / no .git: never modify output/ in node_modules
146
155
  if (!fs.existsSync(expectedPath)) {
147
156
  const ms = Date.now() - start;
148
- process.stdout.write(padRight(file, 36));
149
- process.stdout.write(`${C.y}${padLeft(`${ms} ms`, 10)}${C.n} `);
150
- console.log(`${C.r}MISSING expected output/${file}${C.n}`);
151
- DIFF++;
157
+ fail(`${idx} ${file} (${ms} ms)`);
158
+ fail(`Missing expected output/${file}`);
159
+ failed++;
152
160
  continue;
153
161
  }
154
- tmp = mkTmpFile();
155
- generatedPath = tmp.file;
162
+ tmpDir = mkTmpDir();
163
+ generatedPath = path.join(tmpDir, 'generated.n3');
156
164
  }
157
165
 
158
- // Run eyeling, capture exit code without aborting the suite
159
- // We run `node eyeling.js <file>` from examplesDir so relative paths match old behavior.
166
+ // Run eyeling on this file (cwd examplesDir so relative behavior matches old script)
160
167
  const r = run(nodePath, [eyelingJsPath, file], { cwd: examplesDir });
161
168
  const rc = (r.status == null) ? 1 : r.status;
162
169
 
163
- // Write stdout to the chosen output file (expected in git mode, tmp in npm mode)
170
+ // Write stdout to file (expectedPath in git mode; tmp in npm mode)
164
171
  try {
165
172
  fs.writeFileSync(generatedPath, r.stdout || '', 'utf8');
166
173
  } catch (e) {
167
174
  const ms = Date.now() - start;
168
- process.stdout.write(padRight(file, 36));
169
- process.stdout.write(`${C.y}${padLeft(`${ms} ms`, 10)}${C.n} `);
170
- console.log(`${C.r}DIFF${C.n} (cannot write output: ${e.message})`);
171
- DIFF++;
172
- if (tmp) rmrf(tmp.dir);
175
+ fail(`${idx} ${file} (${ms} ms)`);
176
+ fail(`Cannot write output: ${e.message}`);
177
+ failed++;
178
+ if (tmpDir) rmrf(tmpDir);
173
179
  continue;
174
180
  }
175
181
 
176
182
  const ms = Date.now() - start;
177
183
 
178
- // Compare outputs
184
+ // Compare output
179
185
  let diffOk = false;
180
-
181
186
  if (IN_GIT) {
182
- // Compare expectedPath against HEAD using git diff
183
- const d = run('git', ['diff', '--quiet', '--', path.posix.join('output', file)], { cwd: examplesDir });
187
+ const d = run('git', ['diff', '--quiet', '--', relExpectedPosix], { cwd: examplesDir });
184
188
  diffOk = (d.status === 0);
185
189
  } else {
186
- // Compare expectedPath vs generatedPath without needing a repo
187
190
  if (hasGit()) {
188
191
  const d = run('git', ['diff', '--no-index', '--quiet', expectedPath, generatedPath], { cwd: examplesDir });
189
192
  diffOk = (d.status === 0);
@@ -193,49 +196,51 @@ function main() {
193
196
  }
194
197
  }
195
198
 
196
- // Decide pass/fail
197
- process.stdout.write(padRight(file, 36));
198
- process.stdout.write(`${C.y}${padLeft(`${ms} ms`, 10)}${C.n} `);
199
+ const rcOk = (rc === expectedRc);
199
200
 
200
- if (diffOk && rc === expectedRc) {
201
- if (rc === 0) {
202
- console.log(`${C.g}OK${C.n}`);
201
+ if (diffOk && rcOk) {
202
+ if (expectedRc === 0) {
203
+ ok(`${idx} ${file} (${ms} ms)`);
203
204
  } else {
204
- console.log(`${C.g}OK${C.n} (exit ${rc})`);
205
+ ok(`${idx} ${file} (expected exit ${expectedRc}, ${ms} ms)`);
205
206
  }
206
- OK++;
207
+ passed++;
207
208
  } else {
208
- if (rc !== expectedRc) {
209
- console.log(`${C.r}DIFF${C.n} (exit ${rc}, expected ${expectedRc})`);
210
- } else {
211
- console.log(`${C.r}DIFF${C.n}`);
209
+ fail(`${idx} ${file} (${ms} ms)`);
210
+ if (!rcOk) {
211
+ fail(`Exit code ${rc}, expected ${expectedRc}`);
212
212
  }
213
- DIFF++;
214
-
215
- // In npm mode, show a diff (nice UX) without modifying node_modules
216
- if (!IN_GIT) {
217
- if (hasGit()) {
218
- const d = run('git', ['diff', '--no-index', expectedPath, generatedPath], { cwd: examplesDir });
219
- if (d.stdout) process.stdout.write(d.stdout);
220
- if (d.stderr) process.stderr.write(d.stderr);
221
- } else {
222
- const d = run('diff', ['-u', expectedPath, generatedPath], { cwd: examplesDir });
223
- if (d.stdout) process.stdout.write(d.stdout);
224
- if (d.stderr) process.stderr.write(d.stderr);
225
- }
213
+ if (!diffOk) {
214
+ fail('Output differs');
226
215
  }
216
+
217
+ // Show diffs (both modes), because this is a test runner
218
+ showDiff({
219
+ IN_GIT,
220
+ examplesDir,
221
+ expectedPath,
222
+ generatedPath,
223
+ relExpectedPosix,
224
+ });
225
+
226
+ failed++;
227
227
  }
228
228
 
229
- // cleanup tmp file
230
- if (tmp) rmrf(tmp.dir);
229
+ if (tmpDir) rmrf(tmpDir);
231
230
  }
232
231
 
233
232
  console.log('');
234
233
  const suiteMs = Date.now() - suiteStart;
235
- console.log(`${C.y}==${C.n} Total elapsed: ${suiteMs} ms (${(suiteMs / 1000).toFixed(2)} s)`);
236
- console.log(`${C.y}==${C.n} ${C.g}${OK} OK${C.n} ${C.r}${DIFF} DIFF${C.n}`);
234
+ info(`Total elapsed: ${suiteMs} ms (${(suiteMs / 1000).toFixed(2)} s)`);
237
235
 
238
- process.exit(DIFF === 0 ? 0 : 2);
236
+ if (failed === 0) {
237
+ ok(`All examples tests passed (${passed}/${files.length})`);
238
+ process.exit(0);
239
+ } else {
240
+ fail(`Some examples tests failed (${passed}/${files.length})`);
241
+ // keep exit code 2 (matches historical behavior of examples/test)
242
+ process.exit(2);
243
+ }
239
244
  }
240
245
 
241
246
  main();