@sasjs/cli 6.8.1 → 6.8.2

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.
@@ -332,17 +332,23 @@ var FSCommand = /** @class */ (function (_super) {
332
332
  return __generator(this, function (_c) {
333
333
  switch (_c.label) {
334
334
  case 0: return [4 /*yield*/, (0, utils_1.saveLog)(log, path_1.default.join(outputFolder, 'deploy.log'), '', false)
335
- // after the log file is written: a deploy whose log fails the check is
336
- // the deploy whose record someone needs to read
335
+ // After the log file is written: a deploy whose log fails the check is
336
+ // the deploy whose record someone needs to read. Fatal, because a
337
+ // deploy that aborted still has to fail the build - reporting it and
338
+ // then printing "successfully deployed" is the bug in sasjs/cli#1535.
337
339
  ];
338
340
  case 1:
339
341
  _c.sent();
340
- // after the log file is written: a deploy whose log fails the check is
341
- // the deploy whose record someone needs to read
342
- return [4 /*yield*/, (0, logcheck_1.checkLog)(log, 'fs deploy')];
342
+ // After the log file is written: a deploy whose log fails the check is
343
+ // the deploy whose record someone needs to read. Fatal, because a
344
+ // deploy that aborted still has to fail the build - reporting it and
345
+ // then printing "successfully deployed" is the bug in sasjs/cli#1535.
346
+ return [4 /*yield*/, (0, logcheck_1.checkLog)(log, 'fs deploy', { fatal: true })];
343
347
  case 2:
344
- // after the log file is written: a deploy whose log fails the check is
345
- // the deploy whose record someone needs to read
348
+ // After the log file is written: a deploy whose log fails the check is
349
+ // the deploy whose record someone needs to read. Fatal, because a
350
+ // deploy that aborted still has to fail the build - reporting it and
351
+ // then printing "successfully deployed" is the bug in sasjs/cli#1535.
346
352
  _c.sent();
347
353
  return [2 /*return*/];
348
354
  }
@@ -103,6 +103,8 @@ var FileModule = __importStar(require("@sasjs/utils/file"));
103
103
  var configUtils = __importStar(require("../../../utils/config"));
104
104
  var setConstantsUtils = __importStar(require("../../../utils/setConstants"));
105
105
  var executeCodeModule = __importStar(require("../internal/executeCode"));
106
+ var logcheckModule = __importStar(require("../../../utils/logcheck"));
107
+ var logcheck_1 = require("../../../utils/logcheck");
106
108
  var defaultArgs = ['node', 'sasjs', 'fs'];
107
109
  var target = new utils_1.Target({
108
110
  name: 'test',
@@ -227,6 +229,28 @@ describe('FSCommand', function () {
227
229
  }
228
230
  });
229
231
  }); });
232
+ it('should return the logcheck code when the deploy log fails the check', function () { return __awaiter(void 0, void 0, void 0, function () {
233
+ var returnCode;
234
+ return __generator(this, function (_a) {
235
+ switch (_a.label) {
236
+ case 0:
237
+ jest
238
+ .spyOn(logcheckModule, 'checkLog')
239
+ .mockImplementation(function () {
240
+ return Promise.reject(new logcheck_1.LogCheckFailure('2 errors'));
241
+ });
242
+ return [4 /*yield*/, executeCommandWrapper([
243
+ 'deploy',
244
+ 'localFolder',
245
+ 'remoteFolder'
246
+ ])];
247
+ case 1:
248
+ returnCode = _a.sent();
249
+ expect(returnCode).toEqual(command_1.ReturnCode.LogCheckError);
250
+ return [2 /*return*/];
251
+ }
252
+ });
253
+ }); });
230
254
  });
231
255
  describe('sync', function () {
232
256
  beforeEach(function () {
@@ -335,6 +359,102 @@ describe('FSCommand', function () {
335
359
  }
336
360
  });
337
361
  }); });
362
+ it('should return the logcheck code when the sync log fails the check', function () { return __awaiter(void 0, void 0, void 0, function () {
363
+ var returnCode;
364
+ return __generator(this, function (_a) {
365
+ switch (_a.label) {
366
+ case 0:
367
+ jest
368
+ .spyOn(logcheckModule, 'checkLog')
369
+ .mockImplementation(function () {
370
+ return Promise.reject(new logcheck_1.LogCheckFailure('1 error'));
371
+ });
372
+ jest.spyOn(FSModule, 'extractHashArray').mockImplementationOnce(function () { return []; });
373
+ return [4 /*yield*/, executeCommandWrapper([
374
+ 'sync',
375
+ '-l',
376
+ 'localFolder',
377
+ '-r',
378
+ 'remoteFolder'
379
+ ])];
380
+ case 1:
381
+ returnCode = _a.sent();
382
+ expect(returnCode).toEqual(command_1.ReturnCode.LogCheckError);
383
+ return [2 /*return*/];
384
+ }
385
+ });
386
+ }); });
387
+ });
388
+ describe('log check mode', function () {
389
+ beforeEach(function () {
390
+ process.logger = new utils_1.Logger(utils_1.LogLevel.Off);
391
+ jest.spyOn(process.logger, 'info');
392
+ setupMocks();
393
+ jest
394
+ .spyOn(FSModule, 'generateCompileProgram')
395
+ .mockImplementation(function () { return Promise.resolve(''); });
396
+ jest
397
+ .spyOn(FileModule, 'createFile')
398
+ .mockImplementation(function () { return Promise.resolve(); });
399
+ jest
400
+ .spyOn(executeCodeModule, 'executeCode')
401
+ .mockImplementation(function () { return Promise.resolve({ log: '' }); });
402
+ jest
403
+ .spyOn(logcheckModule, 'checkLog')
404
+ .mockImplementation(function () { return Promise.resolve(); });
405
+ });
406
+ it('should switch the log check on for --logcheck', function () { return __awaiter(void 0, void 0, void 0, function () {
407
+ var spy;
408
+ return __generator(this, function (_a) {
409
+ switch (_a.label) {
410
+ case 0:
411
+ spy = jest.spyOn(logcheckModule, 'setLogCheckMode');
412
+ return [4 /*yield*/, executeCommandWrapper([
413
+ 'deploy',
414
+ 'localFolder',
415
+ 'remoteFolder',
416
+ '--logcheck'
417
+ ])];
418
+ case 1:
419
+ _a.sent();
420
+ expect(spy).toHaveBeenCalledWith('on');
421
+ return [2 /*return*/];
422
+ }
423
+ });
424
+ }); });
425
+ it('should switch the log check off for --no-logcheck', function () { return __awaiter(void 0, void 0, void 0, function () {
426
+ var spy;
427
+ return __generator(this, function (_a) {
428
+ switch (_a.label) {
429
+ case 0:
430
+ spy = jest.spyOn(logcheckModule, 'setLogCheckMode');
431
+ return [4 /*yield*/, executeCommandWrapper([
432
+ 'deploy',
433
+ 'localFolder',
434
+ 'remoteFolder',
435
+ '--no-logcheck'
436
+ ])];
437
+ case 1:
438
+ _a.sent();
439
+ expect(spy).toHaveBeenCalledWith('off');
440
+ return [2 /*return*/];
441
+ }
442
+ });
443
+ }); });
444
+ it('should leave the log check on auto when no flag is given', function () { return __awaiter(void 0, void 0, void 0, function () {
445
+ var spy;
446
+ return __generator(this, function (_a) {
447
+ switch (_a.label) {
448
+ case 0:
449
+ spy = jest.spyOn(logcheckModule, 'setLogCheckMode');
450
+ return [4 /*yield*/, executeCommandWrapper(['deploy', 'localFolder', 'remoteFolder'])];
451
+ case 1:
452
+ _a.sent();
453
+ expect(spy).toHaveBeenCalledWith('auto');
454
+ return [2 /*return*/];
455
+ }
456
+ });
457
+ }); });
338
458
  });
339
459
  describe('invalid subCommand', function () {
340
460
  it('should return ReturnCode.InvalidCommand when subCommand is unknown', function () { return __awaiter(void 0, void 0, void 0, function () {
@@ -68,7 +68,7 @@ var utils_4 = require("../utils");
68
68
  */
69
69
  function executeJobViya(sasjs, authConfig, jobPath, target, waitForJob, output, logFile, statusFile, ignoreWarnings, source, streamLog) {
70
70
  return __awaiter(this, void 0, void 0, function () {
71
- var pollOptions, result, startTime, contextName, macroVars, submittedJob, endTime, outputJson, currentDirPath, outputPath, folderPath, parentFolderPath, logObj, logUrl, logCount, logData, error_1;
71
+ var pollOptions, result, startTime, contextName, macroVars, submittedJob, endTime, wantsVerdict, outputJson, currentDirPath, outputPath, folderPath, parentFolderPath, error_1, logObj, logUrl, logCount, logData, error_2;
72
72
  var _this = this;
73
73
  var _a, _b;
74
74
  return __generator(this, function (_c) {
@@ -176,15 +176,18 @@ function executeJobViya(sasjs, authConfig, jobPath, target, waitForJob, output,
176
176
  _c.sent();
177
177
  _c.label = 8;
178
178
  case 8:
179
- if (!(submittedJob && submittedJob.links)) return [3 /*break*/, 22];
179
+ if (!(submittedJob && submittedJob.links)) return [3 /*break*/, 28];
180
180
  if (!result)
181
181
  result = true;
182
- if (!(output || logFile)) return [3 /*break*/, 21];
183
- _c.label = 9;
182
+ return [4 /*yield*/, (0, logcheck_1.logCheckWantsVerdict)()];
184
183
  case 9:
185
- _c.trys.push([9, 20, , 21]);
184
+ wantsVerdict = _c.sent();
185
+ if (!output) return [3 /*break*/, 18];
186
+ _c.label = 10;
187
+ case 10:
188
+ _c.trys.push([10, 17, , 18]);
186
189
  outputJson = JSON.stringify(submittedJob, null, 2);
187
- if (!(typeof output === 'string')) return [3 /*break*/, 14];
190
+ if (!(typeof output === 'string')) return [3 /*break*/, 15];
188
191
  currentDirPath = path_1.default.isAbsolute(output)
189
192
  ? ''
190
193
  : process.projectDir;
@@ -195,55 +198,71 @@ function executeJobViya(sasjs, authConfig, jobPath, target, waitForJob, output,
195
198
  folderPath.pop();
196
199
  parentFolderPath = folderPath.join(path_1.default.sep);
197
200
  return [4 /*yield*/, (0, utils_2.folderExists)(parentFolderPath)];
198
- case 10:
199
- if (!!(_c.sent())) return [3 /*break*/, 12];
200
- return [4 /*yield*/, (0, utils_2.createFolder)(parentFolderPath)];
201
201
  case 11:
202
+ if (!!(_c.sent())) return [3 /*break*/, 13];
203
+ return [4 /*yield*/, (0, utils_2.createFolder)(parentFolderPath)];
204
+ case 12:
202
205
  _c.sent();
203
- _c.label = 12;
204
- case 12: return [4 /*yield*/, (0, utils_2.createFile)(outputPath, outputJson)];
205
- case 13:
206
+ _c.label = 13;
207
+ case 13: return [4 /*yield*/, (0, utils_2.createFile)(outputPath, outputJson)];
208
+ case 14:
206
209
  _c.sent();
207
210
  (0, displayResult_1.displaySuccess)("Output saved to: ".concat(outputPath));
208
- return [3 /*break*/, 15];
209
- case 14:
211
+ return [3 /*break*/, 16];
212
+ case 15:
210
213
  if (output) {
211
214
  // log job object to the console
212
215
  ;
213
216
  (process.logger || console).log(outputJson);
214
217
  }
215
- _c.label = 15;
216
- case 15:
217
- if (!(logFile && !pollOptions.streamLog)) return [3 /*break*/, 19];
218
+ _c.label = 16;
219
+ case 16:
220
+ result = submittedJob;
221
+ return [3 /*break*/, 18];
222
+ case 17:
223
+ error_1 = _c.sent();
224
+ // handle use case when submitted job can't be parsed
225
+ result = false;
226
+ (0, displayResult_1.displayError)(null, 'An error has occurred when parsing an output of the job.');
227
+ return [3 /*break*/, 18];
228
+ case 18:
229
+ if (!(!pollOptions.streamLog && (Boolean(logFile) || wantsVerdict))) return [3 /*break*/, 27];
218
230
  logObj = submittedJob.links.find(function (link) { return link.rel === 'log' && link.method === 'GET'; });
219
- if (!logObj) return [3 /*break*/, 19];
231
+ if (!logObj) return [3 /*break*/, 26];
232
+ _c.label = 19;
233
+ case 19:
234
+ _c.trys.push([19, 24, , 25]);
220
235
  logUrl = target.serverUrl + logObj.href;
221
236
  logCount = submittedJob.logStatistics.lineCount;
222
237
  return [4 /*yield*/, (0, fetchLogFileContent_1.fetchLogFileContent)(sasjs, authConfig.access_token, logUrl, logCount)];
223
- case 16:
238
+ case 20:
224
239
  logData = _c.sent();
240
+ if (!logFile) return [3 /*break*/, 22];
225
241
  return [4 /*yield*/, (0, utils_4.saveLog)(logData, logFile, jobPath)];
226
- case 17:
242
+ case 21:
227
243
  _c.sent();
228
- return [4 /*yield*/, (0, logcheck_1.checkLog)(typeof logData === 'string' ? logData : (0, utils_1.parseLogLines)(logData), jobPath)];
229
- case 18:
244
+ _c.label = 22;
245
+ case 22: return [4 /*yield*/, (0, logcheck_1.checkLog)(typeof logData === 'string' ? logData : (0, utils_1.parseLogLines)(logData), jobPath)];
246
+ case 23:
230
247
  _c.sent();
231
- _c.label = 19;
232
- case 19:
233
- result = submittedJob;
234
- return [3 /*break*/, 21];
235
- case 20:
236
- error_1 = _c.sent();
237
- // A log-check failure is the verdict the caller asked for, not a parse
238
- // error: swallowing it here printed "Logcheck failed" and then exited 0,
239
- // which is the green pipeline the feature exists to prevent.
240
- if ((0, logcheck_1.isLogCheckFailure)(error_1))
241
- throw error_1;
242
- // handle use case when submitted job can't be parsed
248
+ return [3 /*break*/, 25];
249
+ case 24:
250
+ error_2 = _c.sent();
251
+ // the verdict the caller asked for, not a failed fetch
252
+ if ((0, logcheck_1.isLogCheckFailure)(error_2))
253
+ throw error_2;
243
254
  result = false;
244
- (0, displayResult_1.displayError)(null, 'An error has occurred when parsing an output of the job.');
245
- return [3 /*break*/, 21];
246
- case 21:
255
+ (0, displayResult_1.displayError)(null, 'An error has occurred when retrieving the job log.');
256
+ return [3 /*break*/, 25];
257
+ case 25: return [3 /*break*/, 27];
258
+ case 26:
259
+ // A log was asked for and the job carries none. Staying quiet here is
260
+ // the same "flag that does nothing" this block exists to remove: it
261
+ // reaches the end with the job's own status and exits 0.
262
+ result = false;
263
+ (0, displayResult_1.displayError)(null, 'An error has occurred: the job returned no log to check or save.');
264
+ _c.label = 27;
265
+ case 27:
247
266
  // wait for job state and return status(0,1,2) based on it
248
267
  if (waitForJob && submittedJob.state) {
249
268
  switch (submittedJob.state) {
@@ -259,13 +278,13 @@ function executeJobViya(sasjs, authConfig, jobPath, target, waitForJob, output,
259
278
  break;
260
279
  }
261
280
  }
262
- return [3 /*break*/, 23];
263
- case 22:
281
+ return [3 /*break*/, 29];
282
+ case 28:
264
283
  if (result) {
265
284
  (0, utils_3.terminateProcess)(command_1.ReturnCode.InternalError);
266
285
  }
267
- _c.label = 23;
268
- case 23:
286
+ _c.label = 29;
287
+ case 29:
269
288
  (_b = process.logger) === null || _b === void 0 ? void 0 : _b.info("This job was executed in ".concat((endTime - startTime) / 1000, " seconds"));
270
289
  return [2 /*return*/, result];
271
290
  }
@@ -82,6 +82,8 @@ var configUtils = __importStar(require("../../../utils/config"));
82
82
  var logcheckModule = __importStar(require("../../../utils/logcheck"));
83
83
  var fetchLogFileContentModule = __importStar(require("../../shared/fetchLogFileContent"));
84
84
  var logcheck_1 = require("../../../utils/logcheck");
85
+ var jobUtils = __importStar(require("../internal/utils"));
86
+ var displayResultModule = __importStar(require("../../../utils/displayResult"));
85
87
  var execute_1 = require("../internal/execute");
86
88
  var mocks_1 = require("./mocks");
87
89
  var sasjs = new node_1.default();
@@ -194,6 +196,92 @@ describe('executeJobViya', function () {
194
196
  }
195
197
  });
196
198
  }); });
199
+ it('fetches the log under --logcheck even when no log file was named', function () { return __awaiter(void 0, void 0, void 0, function () {
200
+ var saveSpy, fetchSpy, failure;
201
+ return __generator(this, function (_a) {
202
+ switch (_a.label) {
203
+ case 0:
204
+ // otherwise the flag prints nothing and exits 0, which reads as a clean log
205
+ jest.spyOn(logcheckModule, 'logCheckWantsVerdict').mockResolvedValue(true);
206
+ saveSpy = jest.spyOn(jobUtils, 'saveLog').mockResolvedValue(undefined);
207
+ jest.spyOn(sasjs, 'startComputeJob').mockResolvedValue({
208
+ job: {
209
+ state: 'completed',
210
+ links: [{ rel: 'log', method: 'GET', href: '/log' }],
211
+ logStatistics: { lineCount: 5 }
212
+ }
213
+ });
214
+ fetchSpy = jest
215
+ .spyOn(fetchLogFileContentModule, 'fetchLogFileContent')
216
+ .mockResolvedValue('ERROR: deliberate');
217
+ failure = new logcheck_1.LogCheckFailure('SAS log check failed for test/job');
218
+ jest.spyOn(logcheckModule, 'checkLog').mockRejectedValue(failure);
219
+ return [4 /*yield*/, expect((0, execute_1.executeJobViya)(sasjs, mocks_1.mockAuthConfig, 'test/job', target, true, false, undefined, undefined, false, undefined, false)).rejects.toBe(failure)];
220
+ case 1:
221
+ _a.sent();
222
+ expect(fetchSpy).toHaveBeenCalled();
223
+ // the log was fetched for the verdict, not asked for: writing it would
224
+ // leave a file in the project directory that nobody requested
225
+ expect(saveSpy).not.toHaveBeenCalled();
226
+ return [2 /*return*/];
227
+ }
228
+ });
229
+ }); });
230
+ it('names the log fetch, not the job output, when the log cannot be retrieved', function () { return __awaiter(void 0, void 0, void 0, function () {
231
+ var checkSpy, displaySpy, result;
232
+ return __generator(this, function (_a) {
233
+ switch (_a.label) {
234
+ case 0:
235
+ jest.spyOn(logcheckModule, 'logCheckWantsVerdict').mockResolvedValue(true);
236
+ jest.spyOn(sasjs, 'startComputeJob').mockResolvedValue({
237
+ job: {
238
+ state: 'completed',
239
+ links: [{ rel: 'log', method: 'GET', href: '/log' }],
240
+ logStatistics: { lineCount: 5 }
241
+ }
242
+ });
243
+ jest
244
+ .spyOn(fetchLogFileContentModule, 'fetchLogFileContent')
245
+ .mockRejectedValue(new Error('502 Bad Gateway'));
246
+ checkSpy = jest.spyOn(logcheckModule, 'checkLog');
247
+ displaySpy = jest.spyOn(displayResultModule, 'displayError');
248
+ return [4 /*yield*/, (0, execute_1.executeJobViya)(sasjs, mocks_1.mockAuthConfig, 'test/job', target, false, false, undefined, undefined, false, undefined, false)];
249
+ case 1:
250
+ result = _a.sent();
251
+ expect(result).toBe(false);
252
+ expect(checkSpy).not.toHaveBeenCalled();
253
+ expect(displaySpy).toHaveBeenCalledWith(null, 'An error has occurred when retrieving the job log.');
254
+ return [2 /*return*/];
255
+ }
256
+ });
257
+ }); });
258
+ it('fails a verdict asked for from a job that carries no log link', function () { return __awaiter(void 0, void 0, void 0, function () {
259
+ var fetchSpy, checkSpy, displaySpy, result;
260
+ return __generator(this, function (_a) {
261
+ switch (_a.label) {
262
+ case 0:
263
+ jest.spyOn(logcheckModule, 'logCheckWantsVerdict').mockResolvedValue(true);
264
+ jest.spyOn(sasjs, 'startComputeJob').mockResolvedValue({
265
+ job: {
266
+ state: 'completed',
267
+ links: [],
268
+ logStatistics: { lineCount: 0 }
269
+ }
270
+ });
271
+ fetchSpy = jest.spyOn(fetchLogFileContentModule, 'fetchLogFileContent');
272
+ checkSpy = jest.spyOn(logcheckModule, 'checkLog');
273
+ displaySpy = jest.spyOn(displayResultModule, 'displayError');
274
+ return [4 /*yield*/, (0, execute_1.executeJobViya)(sasjs, mocks_1.mockAuthConfig, 'test/job', target, false, false, undefined, undefined, false, undefined, false)];
275
+ case 1:
276
+ result = _a.sent();
277
+ expect(result).toBe(false);
278
+ expect(fetchSpy).not.toHaveBeenCalled();
279
+ expect(checkSpy).not.toHaveBeenCalled();
280
+ expect(displaySpy).toHaveBeenCalledWith(null, 'An error has occurred: the job returned no log to check or save.');
281
+ return [2 /*return*/];
282
+ }
283
+ });
284
+ }); });
197
285
  });
198
286
  describe('executeJobSasjs', function () {
199
287
  beforeEach(function () { return __awaiter(void 0, void 0, void 0, function () {
@@ -32,6 +32,15 @@ export declare class LogCheckFailure extends Error {
32
32
  readonly logCheckFailure = true;
33
33
  constructor(message: string);
34
34
  }
35
+ /**
36
+ * Whether a log reaching this command has to be fetched at all.
37
+ *
38
+ * Reporting is free once a command owns a log, but a command that never
39
+ * receives one - a Viya job run without `--log` - has to ask for it. It asks
40
+ * when the verdict counts: `--logcheck`, or `logCheck.autoCheck` in the project
41
+ * config. A check nobody asked for is not worth an extra request.
42
+ */
43
+ export declare const logCheckWantsVerdict: () => Promise<boolean>;
35
44
  export declare const isLogCheckFailure: (error: unknown) => error is LogCheckFailure;
36
45
  /**
37
46
  * Reports the issues in a log a command just received, and throws when the
@@ -41,4 +50,6 @@ export declare const isLogCheckFailure: (error: unknown) => error is LogCheckFai
41
50
  * have their own success output, and a check that announces itself on every
42
51
  * green run is noise that gets switched off.
43
52
  */
44
- export declare const checkLog: (logText: string | undefined | null, label: string) => Promise<void>;
53
+ export declare const checkLog: (logText: string | undefined | null, label: string, options?: {
54
+ fatal?: boolean;
55
+ }) => Promise<void>;
@@ -90,7 +90,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
90
90
  return (mod && mod.__esModule) ? mod : { "default": mod };
91
91
  };
92
92
  Object.defineProperty(exports, "__esModule", { value: true });
93
- exports.checkLog = exports.isLogCheckFailure = exports.LogCheckFailure = exports.logCheckModeOf = exports.getLogCheckMode = exports.setLogCheckMode = void 0;
93
+ exports.checkLog = exports.isLogCheckFailure = exports.logCheckWantsVerdict = exports.LogCheckFailure = exports.logCheckModeOf = exports.getLogCheckMode = exports.setLogCheckMode = void 0;
94
94
  var logs_1 = require("@sasjs/utils/logs");
95
95
  var chalk_1 = __importDefault(require("chalk"));
96
96
  var collectLogs_1 = require("../commands/logcheck/collectLogs");
@@ -135,6 +135,36 @@ var LogCheckFailure = /** @class */ (function (_super) {
135
135
  return LogCheckFailure;
136
136
  }(Error));
137
137
  exports.LogCheckFailure = LogCheckFailure;
138
+ /**
139
+ * Whether a log reaching this command has to be fetched at all.
140
+ *
141
+ * Reporting is free once a command owns a log, but a command that never
142
+ * receives one - a Viya job run without `--log` - has to ask for it. It asks
143
+ * when the verdict counts: `--logcheck`, or `logCheck.autoCheck` in the project
144
+ * config. A check nobody asked for is not worth an extra request.
145
+ */
146
+ var logCheckWantsVerdict = function () { return __awaiter(void 0, void 0, void 0, function () {
147
+ var _a;
148
+ return __generator(this, function (_b) {
149
+ switch (_b.label) {
150
+ case 0:
151
+ if (mode === 'off')
152
+ return [2 /*return*/, false];
153
+ if (mode === 'on')
154
+ return [2 /*return*/, true];
155
+ _b.label = 1;
156
+ case 1:
157
+ _b.trys.push([1, 3, , 4]);
158
+ return [4 /*yield*/, (0, logCheckConfig_1.readLogCheckConfig)()];
159
+ case 2: return [2 /*return*/, (_b.sent()).autoCheck === true];
160
+ case 3:
161
+ _a = _b.sent();
162
+ return [2 /*return*/, false];
163
+ case 4: return [2 /*return*/];
164
+ }
165
+ });
166
+ }); };
167
+ exports.logCheckWantsVerdict = logCheckWantsVerdict;
138
168
  var isLogCheckFailure = function (error) {
139
169
  return (error === null || error === void 0 ? void 0 : error.logCheckFailure) === true;
140
170
  };
@@ -147,43 +177,50 @@ exports.isLogCheckFailure = isLogCheckFailure;
147
177
  * have their own success output, and a check that announces itself on every
148
178
  * green run is noise that gets switched off.
149
179
  */
150
- var checkLog = function (logText, label) { return __awaiter(void 0, void 0, void 0, function () {
151
- var config, error_1, _a, issues, suppressed, counts, failing, totals, failure;
152
- var _b;
153
- return __generator(this, function (_c) {
154
- switch (_c.label) {
155
- case 0:
156
- if (mode === 'off' || !logText)
180
+ var checkLog = function (logText_1, label_1) {
181
+ var args_1 = [];
182
+ for (var _i = 2; _i < arguments.length; _i++) {
183
+ args_1[_i - 2] = arguments[_i];
184
+ }
185
+ return __awaiter(void 0, __spreadArray([logText_1, label_1], __read(args_1), false), void 0, function (logText, label, options) {
186
+ var config, error_1, _a, issues, suppressed, counts, failing, totals, failure;
187
+ var _b;
188
+ if (options === void 0) { options = {}; }
189
+ return __generator(this, function (_c) {
190
+ switch (_c.label) {
191
+ case 0:
192
+ if (mode === 'off' || !logText)
193
+ return [2 /*return*/];
194
+ _c.label = 1;
195
+ case 1:
196
+ _c.trys.push([1, 3, , 4]);
197
+ return [4 /*yield*/, (0, logCheckConfig_1.readLogCheckConfig)()];
198
+ case 2:
199
+ config = _c.sent();
200
+ return [3 /*break*/, 4];
201
+ case 3:
202
+ error_1 = _c.sent();
203
+ // An automatic check must never break the command it is riding on: a
204
+ // config it cannot read is worth a warning, not a failed run.
205
+ (_b = process.logger) === null || _b === void 0 ? void 0 : _b.warn("Skipping the SAS log check: ".concat(error_1.message));
157
206
  return [2 /*return*/];
158
- _c.label = 1;
159
- case 1:
160
- _c.trys.push([1, 3, , 4]);
161
- return [4 /*yield*/, (0, logCheckConfig_1.readLogCheckConfig)()];
162
- case 2:
163
- config = _c.sent();
164
- return [3 /*break*/, 4];
165
- case 3:
166
- error_1 = _c.sent();
167
- // An automatic check must never break the command it is riding on: a
168
- // config it cannot read is worth a warning, not a failed run.
169
- (_b = process.logger) === null || _b === void 0 ? void 0 : _b.warn("Skipping the SAS log check: ".concat(error_1.message));
170
- return [2 /*return*/];
171
- case 4:
172
- _a = (0, collectLogs_1.partitionIssues)((0, logs_1.parseLog)(logText), config.ignoreCodes), issues = _a.issues, suppressed = _a.suppressed;
173
- if (!issues.length)
207
+ case 4:
208
+ _a = (0, collectLogs_1.partitionIssues)((0, logs_1.parseLog)(logText), config.ignoreCodes), issues = _a.issues, suppressed = _a.suppressed;
209
+ if (!issues.length)
210
+ return [2 /*return*/];
211
+ counts = (0, collectLogs_1.countIssues)(issues);
212
+ failing = options.fatal === true || mode === 'on' || config.autoCheck === true;
213
+ totals = __assign(__assign({}, counts), { suppressed: suppressed.length, logs: 1, skipped: 0 });
214
+ failure = failing ? (0, processLogCheck_1.describeFailure)(totals, config) : undefined;
215
+ renderInline(label, issues, counts, suppressed.length, config, failure);
216
+ if (failure) {
217
+ throw new LogCheckFailure("SAS log check failed for ".concat(label, ": ").concat(failure));
218
+ }
174
219
  return [2 /*return*/];
175
- counts = (0, collectLogs_1.countIssues)(issues);
176
- failing = mode === 'on' || config.autoCheck === true;
177
- totals = __assign(__assign({}, counts), { suppressed: suppressed.length, logs: 1, skipped: 0 });
178
- failure = failing ? (0, processLogCheck_1.describeFailure)(totals, config) : undefined;
179
- renderInline(label, issues, counts, suppressed.length, config, failure);
180
- if (failure) {
181
- throw new LogCheckFailure("SAS log check failed for ".concat(label, ": ").concat(failure));
182
- }
183
- return [2 /*return*/];
184
- }
220
+ }
221
+ });
185
222
  });
186
- }); };
223
+ };
187
224
  exports.checkLog = checkLog;
188
225
  /**
189
226
  * The inline report: the heading, the issues, and the verdict when there is
@@ -208,6 +208,19 @@ describe('checkLog', function () {
208
208
  }
209
209
  });
210
210
  }); });
211
+ it('fails a command that cannot succeed on a bad log, whatever the config says', function () { return __awaiter(void 0, void 0, void 0, function () {
212
+ return __generator(this, function (_a) {
213
+ switch (_a.label) {
214
+ case 0: return [4 /*yield*/, withProject()];
215
+ case 1:
216
+ _a.sent();
217
+ return [4 /*yield*/, expect((0, logcheck_1.checkLog)(logWithError, 'fs deploy', { fatal: true })).rejects.toBeInstanceOf(logcheck_1.LogCheckFailure)];
218
+ case 2:
219
+ _a.sent();
220
+ return [2 /*return*/];
221
+ }
222
+ });
223
+ }); });
211
224
  it('does not fail on a note under the default policy', function () { return __awaiter(void 0, void 0, void 0, function () {
212
225
  return __generator(this, function (_a) {
213
226
  switch (_a.label) {
@@ -269,6 +282,99 @@ describe('checkLog', function () {
269
282
  });
270
283
  }); });
271
284
  });
285
+ describe('logCheckWantsVerdict', function () {
286
+ var originalProjectDir = process.projectDir;
287
+ var withProject = function (logCheck) { return __awaiter(void 0, void 0, void 0, function () {
288
+ var folder;
289
+ return __generator(this, function (_a) {
290
+ switch (_a.label) {
291
+ case 0: return [4 /*yield*/, tempFolder()];
292
+ case 1:
293
+ folder = _a.sent();
294
+ process.projectDir = folder;
295
+ if (!(logCheck !== undefined)) return [3 /*break*/, 3];
296
+ return [4 /*yield*/, fs_1.promises.writeFile(path_1.default.join(folder, 'sasjsconfig.json'), JSON.stringify({ logCheck: logCheck }))];
297
+ case 2:
298
+ _a.sent();
299
+ _a.label = 3;
300
+ case 3: return [2 /*return*/];
301
+ }
302
+ });
303
+ }); };
304
+ afterEach(function () {
305
+ process.projectDir = originalProjectDir;
306
+ (0, logcheck_1.setLogCheckMode)('auto');
307
+ });
308
+ it('is true under --logcheck and false under --no-logcheck', function () { return __awaiter(void 0, void 0, void 0, function () {
309
+ var _a, _b;
310
+ return __generator(this, function (_c) {
311
+ switch (_c.label) {
312
+ case 0: return [4 /*yield*/, withProject()];
313
+ case 1:
314
+ _c.sent();
315
+ (0, logcheck_1.setLogCheckMode)('on');
316
+ _a = expect;
317
+ return [4 /*yield*/, (0, logcheck_1.logCheckWantsVerdict)()];
318
+ case 2:
319
+ _a.apply(void 0, [_c.sent()]).toBe(true);
320
+ (0, logcheck_1.setLogCheckMode)('off');
321
+ _b = expect;
322
+ return [4 /*yield*/, (0, logcheck_1.logCheckWantsVerdict)()];
323
+ case 3:
324
+ _b.apply(void 0, [_c.sent()]).toBe(false);
325
+ return [2 /*return*/];
326
+ }
327
+ });
328
+ }); });
329
+ it('follows autoCheck when the mode is auto', function () { return __awaiter(void 0, void 0, void 0, function () {
330
+ var _a, _b, _c;
331
+ return __generator(this, function (_d) {
332
+ switch (_d.label) {
333
+ case 0: return [4 /*yield*/, withProject({ autoCheck: true })];
334
+ case 1:
335
+ _d.sent();
336
+ _a = expect;
337
+ return [4 /*yield*/, (0, logcheck_1.logCheckWantsVerdict)()];
338
+ case 2:
339
+ _a.apply(void 0, [_d.sent()]).toBe(true);
340
+ return [4 /*yield*/, withProject()];
341
+ case 3:
342
+ _d.sent();
343
+ _b = expect;
344
+ return [4 /*yield*/, (0, logcheck_1.logCheckWantsVerdict)()];
345
+ case 4:
346
+ _b.apply(void 0, [_d.sent()]).toBe(false);
347
+ return [4 /*yield*/, withProject({ autoCheck: false })];
348
+ case 5:
349
+ _d.sent();
350
+ _c = expect;
351
+ return [4 /*yield*/, (0, logcheck_1.logCheckWantsVerdict)()];
352
+ case 6:
353
+ _c.apply(void 0, [_d.sent()]).toBe(false);
354
+ return [2 /*return*/];
355
+ }
356
+ });
357
+ }); });
358
+ it('asks for no log when the config cannot be read', function () { return __awaiter(void 0, void 0, void 0, function () {
359
+ var folder, _a;
360
+ return __generator(this, function (_b) {
361
+ switch (_b.label) {
362
+ case 0: return [4 /*yield*/, tempFolder()];
363
+ case 1:
364
+ folder = _b.sent();
365
+ process.projectDir = folder;
366
+ return [4 /*yield*/, fs_1.promises.writeFile(path_1.default.join(folder, 'sasjsconfig.json'), '{ "logCheck": ')];
367
+ case 2:
368
+ _b.sent();
369
+ _a = expect;
370
+ return [4 /*yield*/, (0, logcheck_1.logCheckWantsVerdict)()];
371
+ case 3:
372
+ _a.apply(void 0, [_b.sent()]).toBe(false);
373
+ return [2 /*return*/];
374
+ }
375
+ });
376
+ }); });
377
+ });
272
378
  describe('isLogCheckFailure', function () {
273
379
  it('recognises only its own error', function () {
274
380
  expect((0, logcheck_1.isLogCheckFailure)(new logcheck_1.LogCheckFailure('x'))).toBe(true);
package/package.json CHANGED
@@ -123,5 +123,5 @@
123
123
  "ws": "8.21.0",
124
124
  "nanoid": "3.3.18"
125
125
  },
126
- "version": "6.8.1"
126
+ "version": "6.8.2"
127
127
  }