agentic-workflow-manager 3.4.0 → 3.6.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/dist/src/commands/job/exec-wrapper.js +136 -0
- package/dist/src/commands/job/export.js +94 -0
- package/dist/src/commands/job/gate.js +118 -0
- package/dist/src/commands/job/heartbeat.js +15 -0
- package/dist/src/commands/job/index.js +246 -0
- package/dist/src/commands/job/query.js +37 -0
- package/dist/src/commands/job/reap.js +24 -0
- package/dist/src/commands/job/reconcile.js +112 -0
- package/dist/src/commands/job/request.js +27 -0
- package/dist/src/commands/sensors/exec.js +121 -0
- package/dist/src/commands/sensors/index.js +4 -4
- package/dist/src/commands/sensors/run.js +124 -71
- package/dist/src/commands/watch/apply.js +352 -0
- package/dist/src/commands/watch/generations.js +249 -0
- package/dist/src/commands/watch/index.js +49 -0
- package/dist/src/commands/watch/init.js +72 -0
- package/dist/src/commands/watch/lock.js +89 -0
- package/dist/src/commands/watch/runner.js +191 -0
- package/dist/src/commands/watch/supervisor.js +266 -0
- package/dist/src/core/atomic-file.js +31 -0
- package/dist/src/core/export/pack.js +7 -1
- package/dist/src/core/journal/adapter.js +27 -0
- package/dist/src/core/journal/fingerprint.js +80 -0
- package/dist/src/core/journal/paths.js +56 -0
- package/dist/src/core/journal/process.js +284 -0
- package/dist/src/core/journal/redact.js +142 -0
- package/dist/src/core/journal/requests.js +132 -0
- package/dist/src/core/journal/store.js +107 -0
- package/dist/src/core/journal/types.js +165 -0
- package/dist/src/index.js +4 -0
- package/dist/tests/commands/job/exec-wrapper.test.js +85 -0
- package/dist/tests/commands/job/export.test.js +76 -0
- package/dist/tests/commands/job/gate-reconcile.test.js +297 -0
- package/dist/tests/commands/job/reap-cli.test.js +101 -0
- package/dist/tests/commands/job/verbs.test.js +56 -0
- package/dist/tests/commands/job/verdict-determinism.test.js +138 -0
- package/dist/tests/commands/sensors/exec-fixtures.js +24 -0
- package/dist/tests/commands/sensors/exec.test.js +91 -0
- package/dist/tests/commands/sensors/run-inconclusive.test.js +55 -66
- package/dist/tests/commands/sensors/run-partial.test.js +225 -0
- package/dist/tests/commands/sensors/run-tool-missing.test.js +6 -6
- package/dist/tests/commands/sensors/run.test.js +64 -81
- package/dist/tests/commands/watch/apply.test.js +397 -0
- package/dist/tests/commands/watch/e2e-crash.test.js +157 -0
- package/dist/tests/commands/watch/generations.test.js +115 -0
- package/dist/tests/commands/watch/integration.test.js +124 -0
- package/dist/tests/commands/watch/lock.test.js +60 -0
- package/dist/tests/commands/watch/runner.test.js +239 -0
- package/dist/tests/commands/watch/supervisor-loop.test.js +203 -0
- package/dist/tests/commands/watch/watch-init.test.js +43 -0
- package/dist/tests/core/atomic-file-durable.test.js +42 -0
- package/dist/tests/core/journal/adapter.test.js +27 -0
- package/dist/tests/core/journal/fingerprint.test.js +164 -0
- package/dist/tests/core/journal/paths.test.js +35 -0
- package/dist/tests/core/journal/process.test.js +213 -0
- package/dist/tests/core/journal/redact.test.js +59 -0
- package/dist/tests/core/journal/requests.test.js +134 -0
- package/dist/tests/core/journal/store.test.js +88 -0
- package/dist/tests/core/journal/types.test.js +78 -0
- package/dist/tests/structural/exec-invocation-explicit-stdio.test.js +94 -0
- package/package.json +1 -1
|
@@ -11,10 +11,11 @@ function mkTmp() {
|
|
|
11
11
|
return fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-sensors-'));
|
|
12
12
|
}
|
|
13
13
|
// Define stable mock before jest.mock hoisting
|
|
14
|
-
const
|
|
15
|
-
jest.mock('
|
|
16
|
-
|
|
14
|
+
const mockRunCommand = jest.fn();
|
|
15
|
+
jest.mock('../../../src/commands/sensors/exec', () => ({
|
|
16
|
+
runCommand: (...args) => mockRunCommand(...args),
|
|
17
17
|
}));
|
|
18
|
+
const { ok, exited, timedOut } = require('./exec-fixtures');
|
|
18
19
|
const MANIFEST = {
|
|
19
20
|
pack: 'js-ts',
|
|
20
21
|
sensors: {
|
|
@@ -33,15 +34,15 @@ describe('runSensors', () => {
|
|
|
33
34
|
tmpDir = fs_1.default.mkdtempSync(path.join(os.tmpdir(), 'awm-run-test-'));
|
|
34
35
|
fs_1.default.mkdirSync(path.join(tmpDir, '.awm'), { recursive: true });
|
|
35
36
|
fs_1.default.writeFileSync(path.join(tmpDir, '.awm', 'sensors.json'), JSON.stringify(MANIFEST));
|
|
36
|
-
|
|
37
|
+
mockRunCommand.mockReset();
|
|
37
38
|
});
|
|
38
39
|
afterEach(() => { fs_1.default.rmSync(tmpDir, { recursive: true }); });
|
|
39
40
|
const load = () => require('../../../src/commands/sensors/run');
|
|
40
|
-
it('returns not_certified output when manifest does not exist', () => {
|
|
41
|
+
it('returns not_certified output when manifest does not exist', async () => {
|
|
41
42
|
const emptyDir = fs_1.default.mkdtempSync(path.join(os.tmpdir(), 'awm-empty-'));
|
|
42
43
|
try {
|
|
43
44
|
const { runSensors } = load();
|
|
44
|
-
const result = runSensors({ fast: true, cwd: emptyDir });
|
|
45
|
+
const result = await runSensors({ fast: true, cwd: emptyDir });
|
|
45
46
|
expect(result.overall).toBe('not_certified');
|
|
46
47
|
expect(result.sensors).toHaveLength(0);
|
|
47
48
|
}
|
|
@@ -49,80 +50,80 @@ describe('runSensors', () => {
|
|
|
49
50
|
fs_1.default.rmSync(emptyDir, { recursive: true });
|
|
50
51
|
}
|
|
51
52
|
});
|
|
52
|
-
it('runs only fast sensors with --fast flag', () => {
|
|
53
|
-
|
|
53
|
+
it('runs only fast sensors with --fast flag', async () => {
|
|
54
|
+
mockRunCommand.mockResolvedValue(ok());
|
|
54
55
|
const { runSensors } = load();
|
|
55
|
-
const result = runSensors({ fast: true, cwd: tmpDir });
|
|
56
|
-
expect(
|
|
56
|
+
const result = await runSensors({ fast: true, cwd: tmpDir });
|
|
57
|
+
expect(mockRunCommand).toHaveBeenCalledTimes(2); // typecheck + lint (security disabled, mutation disabled)
|
|
57
58
|
expect(result.sensors.some((s) => s.name === 'security')).toBe(false);
|
|
58
59
|
expect(result.overall).toBe('pass');
|
|
59
60
|
});
|
|
60
|
-
it('returns fail when a fast sensor has errors', () => {
|
|
61
|
-
|
|
62
|
-
.
|
|
63
|
-
.
|
|
61
|
+
it('returns fail when a fast sensor has errors', async () => {
|
|
62
|
+
mockRunCommand
|
|
63
|
+
.mockResolvedValueOnce(exited(1, 'src/a.ts(1,1): error TS0001: Bad type.'))
|
|
64
|
+
.mockResolvedValueOnce(ok());
|
|
64
65
|
const { runSensors } = load();
|
|
65
|
-
const result = runSensors({ fast: true, cwd: tmpDir });
|
|
66
|
+
const result = await runSensors({ fast: true, cwd: tmpDir });
|
|
66
67
|
expect(result.overall).toBe('fail');
|
|
67
68
|
const tc = result.sensors.find((s) => s.name === 'typecheck');
|
|
68
69
|
expect(tc.status).toBe('fail');
|
|
69
70
|
expect(tc.errors[0].message).toMatch('SENSOR[typecheck]');
|
|
70
71
|
});
|
|
71
|
-
it('marks sensor as inconclusive on timeout', () => {
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
it('marks sensor as inconclusive on timeout', async () => {
|
|
73
|
+
mockRunCommand.mockResolvedValueOnce(timedOut());
|
|
74
|
+
mockRunCommand.mockResolvedValueOnce(ok());
|
|
74
75
|
const { runSensors } = load();
|
|
75
|
-
const result = runSensors({ fast: true, cwd: tmpDir });
|
|
76
|
+
const result = await runSensors({ fast: true, cwd: tmpDir });
|
|
76
77
|
const tc = result.sensors.find((s) => s.name === 'typecheck');
|
|
77
78
|
expect(tc.status).toBe('inconclusive');
|
|
78
79
|
expect(tc.skipReason).toMatch('timeout');
|
|
79
80
|
});
|
|
80
|
-
it('skips disabled sensors', () => {
|
|
81
|
-
|
|
81
|
+
it('skips disabled sensors', async () => {
|
|
82
|
+
mockRunCommand.mockResolvedValue(ok());
|
|
82
83
|
const { runSensors } = load();
|
|
83
|
-
const result = runSensors({ all: true, cwd: tmpDir });
|
|
84
|
+
const result = await runSensors({ all: true, cwd: tmpDir });
|
|
84
85
|
const sec = result.sensors.find((s) => s.name === 'security');
|
|
85
86
|
expect(sec.status).toBe('skipped');
|
|
86
87
|
expect(sec.skipReason).toBe('disabled');
|
|
87
88
|
});
|
|
88
|
-
const tcError = () =>
|
|
89
|
-
it('baseline suppresses accepted findings — sensor passes on no NEW findings', () => {
|
|
89
|
+
const tcError = () => exited(1, 'src/a.ts(1,1): error TS0001: Bad type.');
|
|
90
|
+
it('baseline suppresses accepted findings — sensor passes on no NEW findings', async () => {
|
|
90
91
|
const { runSensors } = load();
|
|
91
92
|
const { buildBaseline, writeBaseline } = require('../../../src/commands/sensors/baseline');
|
|
92
93
|
// Run 1 (no baseline): typecheck reports a TS error → fail.
|
|
93
|
-
|
|
94
|
-
const first = runSensors({ fast: true, cwd: tmpDir });
|
|
94
|
+
mockRunCommand.mockResolvedValueOnce(tcError()).mockResolvedValueOnce(ok());
|
|
95
|
+
const first = await runSensors({ fast: true, cwd: tmpDir });
|
|
95
96
|
expect(first.overall).toBe('fail');
|
|
96
97
|
// Accept the current findings as baseline.
|
|
97
98
|
writeBaseline(tmpDir, buildBaseline(first.sensors.map((s) => ({ name: s.name, errors: s.errors }))));
|
|
98
99
|
// Run 2 (same finding): baseline-suppressed → pass.
|
|
99
|
-
|
|
100
|
-
const second = runSensors({ fast: true, cwd: tmpDir });
|
|
100
|
+
mockRunCommand.mockResolvedValueOnce(tcError()).mockResolvedValueOnce(ok());
|
|
101
|
+
const second = await runSensors({ fast: true, cwd: tmpDir });
|
|
101
102
|
const tc = second.sensors.find((s) => s.name === 'typecheck');
|
|
102
103
|
expect(tc.status).toBe('pass');
|
|
103
104
|
expect(tc.baselineCount).toBe(1);
|
|
104
105
|
expect(second.overall).toBe('pass');
|
|
105
106
|
});
|
|
106
|
-
it('baseline lets NEW findings through (still fails)', () => {
|
|
107
|
+
it('baseline lets NEW findings through (still fails)', async () => {
|
|
107
108
|
const { runSensors } = load();
|
|
108
109
|
const { writeBaseline } = require('../../../src/commands/sensors/baseline');
|
|
109
110
|
writeBaseline(tmpDir, { typecheck: ['some-unrelated-fingerprint'] });
|
|
110
|
-
|
|
111
|
-
const result = runSensors({ fast: true, cwd: tmpDir });
|
|
111
|
+
mockRunCommand.mockResolvedValueOnce(tcError()).mockResolvedValueOnce(ok());
|
|
112
|
+
const result = await runSensors({ fast: true, cwd: tmpDir });
|
|
112
113
|
const tc = result.sensors.find((s) => s.name === 'typecheck');
|
|
113
114
|
expect(tc.status).toBe('fail');
|
|
114
115
|
expect(result.overall).toBe('fail');
|
|
115
116
|
});
|
|
116
|
-
it('--ignore-baseline reports all findings even when a baseline exists', () => {
|
|
117
|
+
it('--ignore-baseline reports all findings even when a baseline exists', async () => {
|
|
117
118
|
const { runSensors } = load();
|
|
118
119
|
const { buildBaseline, writeBaseline } = require('../../../src/commands/sensors/baseline');
|
|
119
120
|
// First capture + accept the finding.
|
|
120
|
-
|
|
121
|
-
const first = runSensors({ fast: true, cwd: tmpDir });
|
|
121
|
+
mockRunCommand.mockResolvedValueOnce(tcError()).mockResolvedValueOnce(ok());
|
|
122
|
+
const first = await runSensors({ fast: true, cwd: tmpDir });
|
|
122
123
|
writeBaseline(tmpDir, buildBaseline(first.sensors.map((s) => ({ name: s.name, errors: s.errors }))));
|
|
123
124
|
// With ignoreBaseline, the accepted finding still counts → fail.
|
|
124
|
-
|
|
125
|
-
const result = runSensors({ fast: true, cwd: tmpDir, ignoreBaseline: true });
|
|
125
|
+
mockRunCommand.mockResolvedValueOnce(tcError()).mockResolvedValueOnce(ok());
|
|
126
|
+
const result = await runSensors({ fast: true, cwd: tmpDir, ignoreBaseline: true });
|
|
126
127
|
expect(result.overall).toBe('fail');
|
|
127
128
|
});
|
|
128
129
|
});
|
|
@@ -133,32 +134,26 @@ describe('runSensors — missing tool is a fail, not a skip', () => {
|
|
|
133
134
|
fs_1.default.rmSync(root, { recursive: true, force: true });
|
|
134
135
|
});
|
|
135
136
|
beforeEach(() => {
|
|
136
|
-
|
|
137
|
+
mockRunCommand.mockReset();
|
|
137
138
|
// What Node's execSync actually throws when the binary is absent and `/bin/sh`
|
|
138
139
|
// is dash (Debian/Ubuntu, hence most CI runners): status 127, `not found`
|
|
139
140
|
// rather than bash's `command not found`, and no `code` — ENOENT is set only
|
|
140
141
|
// when spawning the shell itself fails, not the command inside it.
|
|
141
|
-
|
|
142
|
-
throw Object.assign(new Error('Command failed: awm-nonexistent-binary-xyz .'), {
|
|
143
|
-
stdout: '',
|
|
144
|
-
stderr: '/bin/sh: 1: awm-nonexistent-binary-xyz: not found\n',
|
|
145
|
-
status: 127,
|
|
146
|
-
});
|
|
147
|
-
});
|
|
142
|
+
mockRunCommand.mockResolvedValue(exited(127, '', '/bin/sh: 1: awm-nonexistent-binary-xyz: not found\n'));
|
|
148
143
|
});
|
|
149
144
|
// The sensor is named `security` so it uses the semgrep formatter, which returns
|
|
150
145
|
// zero findings for unparseable shell noise and lets execution reach the
|
|
151
146
|
// tool-missing branch. Under the generic formatter any stderr becomes a finding,
|
|
152
147
|
// so a sensor named `ghost` would report `fail` without that branch ever running —
|
|
153
148
|
// green for a reason unrelated to what this test claims to cover.
|
|
154
|
-
it('marks a sensor whose binary is missing as fail', () => {
|
|
149
|
+
it('marks a sensor whose binary is missing as fail', async () => {
|
|
155
150
|
root = mkTmp();
|
|
156
151
|
fs_1.default.mkdirSync(path_1.default.join(root, '.awm'));
|
|
157
152
|
fs_1.default.writeFileSync(path_1.default.join(root, '.awm', 'sensors.json'), JSON.stringify({
|
|
158
153
|
pack: 'js-ts',
|
|
159
154
|
sensors: { security: { cmd: 'awm-nonexistent-binary-xyz .', fast: true } },
|
|
160
155
|
}));
|
|
161
|
-
const out = (0, run_1.runSensors)({ cwd: root });
|
|
156
|
+
const out = await (0, run_1.runSensors)({ cwd: root });
|
|
162
157
|
const security = out.sensors.find((s) => s.name === 'security');
|
|
163
158
|
expect(security?.status).toBe('fail');
|
|
164
159
|
expect(security?.errors[0].message).toMatch(/not available/i);
|
|
@@ -168,8 +163,8 @@ describe('runSensors — missing tool is a fail, not a skip', () => {
|
|
|
168
163
|
describe('runSensors — not_certified + auto-discovery', () => {
|
|
169
164
|
let tmpDir;
|
|
170
165
|
beforeEach(() => {
|
|
171
|
-
|
|
172
|
-
|
|
166
|
+
mockRunCommand.mockReset();
|
|
167
|
+
mockRunCommand.mockResolvedValue(ok());
|
|
173
168
|
});
|
|
174
169
|
afterEach(() => {
|
|
175
170
|
if (tmpDir) {
|
|
@@ -177,19 +172,19 @@ describe('runSensors — not_certified + auto-discovery', () => {
|
|
|
177
172
|
tmpDir = undefined;
|
|
178
173
|
}
|
|
179
174
|
});
|
|
180
|
-
it('returns not_certified when no manifest exists anywhere up the tree', () => {
|
|
175
|
+
it('returns not_certified when no manifest exists anywhere up the tree', async () => {
|
|
181
176
|
tmpDir = mkTmp();
|
|
182
|
-
const out = (0, run_1.runSensors)({ cwd: tmpDir });
|
|
177
|
+
const out = await (0, run_1.runSensors)({ cwd: tmpDir });
|
|
183
178
|
expect(out.overall).toBe('not_certified');
|
|
184
179
|
expect(out.sensors).toEqual([]);
|
|
185
180
|
});
|
|
186
|
-
it('discovers .awm/sensors.json in a parent directory (walk-up)', () => {
|
|
181
|
+
it('discovers .awm/sensors.json in a parent directory (walk-up)', async () => {
|
|
187
182
|
tmpDir = mkTmp();
|
|
188
183
|
fs_1.default.mkdirSync(path_1.default.join(tmpDir, '.awm'));
|
|
189
184
|
fs_1.default.writeFileSync(path_1.default.join(tmpDir, '.awm', 'sensors.json'), JSON.stringify({ pack: 'test', sensors: { noop: { cmd: 'echo ok', fast: true } } }));
|
|
190
185
|
const nested = path_1.default.join(tmpDir, 'a', 'b');
|
|
191
186
|
fs_1.default.mkdirSync(nested, { recursive: true });
|
|
192
|
-
const out = (0, run_1.runSensors)({ cwd: nested });
|
|
187
|
+
const out = await (0, run_1.runSensors)({ cwd: nested });
|
|
193
188
|
expect(out.overall).toBe('pass');
|
|
194
189
|
expect(out.sensors.length).toBe(1);
|
|
195
190
|
});
|
|
@@ -234,7 +229,7 @@ describe('reconcilePack', () => {
|
|
|
234
229
|
fs_1.default.writeFileSync(path_1.default.join(dir, 'package.json'), '{}');
|
|
235
230
|
return dir;
|
|
236
231
|
}
|
|
237
|
-
it('upgrades generic→js-ts when package.json is present', () => {
|
|
232
|
+
it('upgrades generic→js-ts when package.json is present', async () => {
|
|
238
233
|
const { reconcilePack } = require('../../../src/commands/sensors/run');
|
|
239
234
|
const dir = tmpProject('generic', true);
|
|
240
235
|
try {
|
|
@@ -253,7 +248,7 @@ describe('reconcilePack', () => {
|
|
|
253
248
|
fs_1.default.rmSync(dir, { recursive: true });
|
|
254
249
|
}
|
|
255
250
|
});
|
|
256
|
-
it('is a no-op when pack is already real (idempotent)', () => {
|
|
251
|
+
it('is a no-op when pack is already real (idempotent)', async () => {
|
|
257
252
|
const { reconcilePack } = require('../../../src/commands/sensors/run');
|
|
258
253
|
const dir = tmpProject('js-ts', true);
|
|
259
254
|
try {
|
|
@@ -266,7 +261,7 @@ describe('reconcilePack', () => {
|
|
|
266
261
|
fs_1.default.rmSync(dir, { recursive: true });
|
|
267
262
|
}
|
|
268
263
|
});
|
|
269
|
-
it('does not upgrade a truly generic project (no indicators)', () => {
|
|
264
|
+
it('does not upgrade a truly generic project (no indicators)', async () => {
|
|
270
265
|
const { reconcilePack } = require('../../../src/commands/sensors/run');
|
|
271
266
|
const dir = tmpProject('generic', false);
|
|
272
267
|
try {
|
|
@@ -288,44 +283,32 @@ describe('runSensors — test sensor (exit-code)', () => {
|
|
|
288
283
|
jest.resetModules();
|
|
289
284
|
tmpDir = fs_1.default.mkdtempSync(path.join(os.tmpdir(), 'awm-run-test-'));
|
|
290
285
|
fs_1.default.mkdirSync(path.join(tmpDir, '.awm'), { recursive: true });
|
|
291
|
-
|
|
286
|
+
mockRunCommand.mockReset();
|
|
292
287
|
});
|
|
293
288
|
afterEach(() => { fs_1.default.rmSync(tmpDir, { recursive: true }); });
|
|
294
289
|
const load = () => require('../../../src/commands/sensors/run');
|
|
295
|
-
it('test sensor: passing run (exit 0 with output) is pass, not fail', () => {
|
|
290
|
+
it('test sensor: passing run (exit 0 with output) is pass, not fail', async () => {
|
|
296
291
|
fs_1.default.writeFileSync(path.join(tmpDir, '.awm', 'sensors.json'), JSON.stringify({ pack: 'js-ts', sensors: { test: { cmd: 'npm test', fast: false } } }));
|
|
297
|
-
|
|
292
|
+
mockRunCommand.mockResolvedValue(ok('Tests: 6 passed, 6 total\n')); // runner prints on success
|
|
298
293
|
const { runSensors } = load();
|
|
299
|
-
const result = runSensors({ all: true, cwd: tmpDir });
|
|
294
|
+
const result = await runSensors({ all: true, cwd: tmpDir });
|
|
300
295
|
const test = result.sensors.find((s) => s.name === 'test');
|
|
301
296
|
expect(test.status).toBe('pass');
|
|
302
297
|
});
|
|
303
|
-
it('test sensor: failing run (non-zero exit) is fail, not skipped', () => {
|
|
298
|
+
it('test sensor: failing run (non-zero exit) is fail, not skipped', async () => {
|
|
304
299
|
fs_1.default.writeFileSync(path.join(tmpDir, '.awm', 'sensors.json'), JSON.stringify({ pack: 'js-ts', sensors: { test: { cmd: 'npm test', fast: false } } }));
|
|
305
|
-
|
|
306
|
-
const err = new Error('jest failed');
|
|
307
|
-
err.status = 1;
|
|
308
|
-
err.stdout = 'Tests: 1 failed, 5 passed\n';
|
|
309
|
-
err.stderr = '';
|
|
310
|
-
throw err;
|
|
311
|
-
});
|
|
300
|
+
mockRunCommand.mockResolvedValue(exited(1, 'Tests: 1 failed, 5 passed\n'));
|
|
312
301
|
const { runSensors } = load();
|
|
313
|
-
const result = runSensors({ all: true, cwd: tmpDir });
|
|
302
|
+
const result = await runSensors({ all: true, cwd: tmpDir });
|
|
314
303
|
const test = result.sensors.find((s) => s.name === 'test');
|
|
315
304
|
expect(test.status).toBe('fail');
|
|
316
305
|
expect(result.overall).toBe('fail');
|
|
317
306
|
});
|
|
318
|
-
it('test sensor: missing npm test script exits non-zero → fail, not skipped', () => {
|
|
307
|
+
it('test sensor: missing npm test script exits non-zero → fail, not skipped', async () => {
|
|
319
308
|
fs_1.default.writeFileSync(path.join(tmpDir, '.awm', 'sensors.json'), JSON.stringify({ pack: 'js-ts', sensors: { test: { cmd: 'npm test', fast: false } } }));
|
|
320
|
-
|
|
321
|
-
const err = new Error('npm test: missing script');
|
|
322
|
-
err.status = 1;
|
|
323
|
-
err.stdout = 'npm error Missing script: test\n';
|
|
324
|
-
err.stderr = '';
|
|
325
|
-
throw err;
|
|
326
|
-
});
|
|
309
|
+
mockRunCommand.mockResolvedValue(exited(1, 'npm error Missing script: test\n'));
|
|
327
310
|
const { runSensors } = load();
|
|
328
|
-
const result = runSensors({ all: true, cwd: tmpDir });
|
|
311
|
+
const result = await runSensors({ all: true, cwd: tmpDir });
|
|
329
312
|
const test = result.sensors.find((s) => s.name === 'test');
|
|
330
313
|
expect(test.status).toBe('fail');
|
|
331
314
|
expect(result.overall).toBe('fail');
|
|
@@ -335,10 +318,10 @@ describe('runSensors — honest floor (not_certified over real stack)', () => {
|
|
|
335
318
|
const path = require('path');
|
|
336
319
|
const os = require('os');
|
|
337
320
|
beforeEach(() => {
|
|
338
|
-
|
|
339
|
-
|
|
321
|
+
mockRunCommand.mockReset();
|
|
322
|
+
mockRunCommand.mockResolvedValue(ok());
|
|
340
323
|
});
|
|
341
|
-
it('returns not_certified (not skipped) for a generic manifest over a real stack', () => {
|
|
324
|
+
it('returns not_certified (not skipped) for a generic manifest over a real stack', async () => {
|
|
342
325
|
const dir = fs_1.default.mkdtempSync(path.join(os.tmpdir(), 'awm-floor-'));
|
|
343
326
|
fs_1.default.mkdirSync(path.join(dir, '.awm'), { recursive: true });
|
|
344
327
|
fs_1.default.writeFileSync(path.join(dir, '.awm', 'sensors.json'), JSON.stringify({ pack: 'generic', sensors: { security: { cmd: 'semgrep .', fast: false } } }));
|
|
@@ -349,7 +332,7 @@ describe('runSensors — honest floor (not_certified over real stack)', () => {
|
|
|
349
332
|
try {
|
|
350
333
|
jest.resetModules();
|
|
351
334
|
const { runSensors } = require('../../../src/commands/sensors/run');
|
|
352
|
-
const result = runSensors({ fast: true, cwd: dir }); // --fast filters the fast:false security sensor → empty
|
|
335
|
+
const result = await runSensors({ fast: true, cwd: dir }); // --fast filters the fast:false security sensor → empty
|
|
353
336
|
expect(result.overall).toBe('not_certified');
|
|
354
337
|
}
|
|
355
338
|
finally {
|