@sasjs/cli 6.7.0 → 6.8.1
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/README.md +1 -1
- package/build/commands/flow/flowCommand.js +11 -1
- package/build/commands/flow/internal/executeFlow.js +44 -19
- package/build/commands/flow/internal/saveLog.d.ts +12 -1
- package/build/commands/flow/internal/saveLog.js +2 -2
- package/build/commands/flow/internal/spec/executeFlow.spec.js +44 -1
- package/build/commands/flow/spec/flowCommand.spec.js +44 -0
- package/build/commands/fs/fsCommand.d.ts +2 -2
- package/build/commands/fs/fsCommand.js +44 -15
- package/build/commands/fs/internal/executeCode.d.ts +6 -0
- package/build/commands/fs/internal/executeCode.js +6 -0
- package/build/commands/job/internal/execute/sas9.js +16 -13
- package/build/commands/job/internal/execute/sasjs.js +12 -9
- package/build/commands/job/internal/execute/viya.js +46 -36
- package/build/commands/job/jobCommand.js +13 -0
- package/build/commands/job/spec/execute.spec.js +29 -0
- package/build/commands/job/spec/executeLogOrder.spec.d.ts +1 -0
- package/build/commands/job/spec/executeLogOrder.spec.js +119 -0
- package/build/commands/job/spec/jobCommand.spec.js +32 -0
- package/build/commands/logcheck/logCheckConfig.d.ts +11 -0
- package/build/commands/logcheck/logCheckConfig.js +86 -26
- package/build/commands/logcheck/renderLogCheck.d.ts +1 -0
- package/build/commands/logcheck/renderLogCheck.js +1 -0
- package/build/commands/logcheck/spec/logCheckConfig.spec.js +63 -2
- package/build/commands/logcheck/types.d.ts +8 -0
- package/build/commands/request/request.js +66 -27
- package/build/commands/request/requestCommand.js +11 -1
- package/build/commands/request/spec/requestCommand.spec.js +40 -0
- package/build/commands/run/run.d.ts +1 -3
- package/build/commands/run/run.js +20 -5
- package/build/commands/run/runCommand.js +9 -0
- package/build/commands/run/spec/runCommand.spec.js +50 -0
- package/build/utils/index.d.ts +1 -0
- package/build/utils/index.js +1 -0
- package/build/utils/logcheck.d.ts +44 -0
- package/build/utils/logcheck.js +224 -0
- package/build/utils/spec/logcheck.spec.d.ts +1 -0
- package/build/utils/spec/logcheck.spec.js +297 -0
- package/package.json +1 -1
|
@@ -100,6 +100,7 @@ var utils_1 = require("@sasjs/utils");
|
|
|
100
100
|
var configUtils = __importStar(require("../../../utils/config"));
|
|
101
101
|
var setConstantsUtils = __importStar(require("../../../utils/setConstants"));
|
|
102
102
|
var command_1 = require("../../../types/command");
|
|
103
|
+
var logcheck_1 = require("../../../utils/logcheck");
|
|
103
104
|
var defaultArgs = ['node', 'sasjs', 'run'];
|
|
104
105
|
var target = new utils_1.Target({
|
|
105
106
|
name: 'test',
|
|
@@ -114,6 +115,55 @@ describe('RunCommand', function () {
|
|
|
114
115
|
beforeEach(function () {
|
|
115
116
|
setupMocks();
|
|
116
117
|
});
|
|
118
|
+
afterEach(function () { return (0, logcheck_1.setLogCheckMode)('auto'); });
|
|
119
|
+
it('should return the logcheck code when the log check failed the run', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
120
|
+
var command, _a;
|
|
121
|
+
return __generator(this, function (_b) {
|
|
122
|
+
switch (_b.label) {
|
|
123
|
+
case 0:
|
|
124
|
+
jest
|
|
125
|
+
.spyOn(runModule, 'runSasCode')
|
|
126
|
+
.mockImplementation(function () { return Promise.reject(new logcheck_1.LogCheckFailure('2 errors')); });
|
|
127
|
+
command = new runCommand_1.RunCommand(__spreadArray(__spreadArray([], __read(defaultArgs), false), [sasFilePath], false));
|
|
128
|
+
_a = expect;
|
|
129
|
+
return [4 /*yield*/, command.execute()];
|
|
130
|
+
case 1:
|
|
131
|
+
_a.apply(void 0, [_b.sent()]).toEqual(command_1.ReturnCode.LogCheckError);
|
|
132
|
+
return [2 /*return*/];
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
}); });
|
|
136
|
+
it('should switch the log check off for --no-logcheck', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
137
|
+
var command;
|
|
138
|
+
return __generator(this, function (_a) {
|
|
139
|
+
switch (_a.label) {
|
|
140
|
+
case 0:
|
|
141
|
+
command = new runCommand_1.RunCommand(__spreadArray(__spreadArray([], __read(defaultArgs), false), [
|
|
142
|
+
sasFilePath,
|
|
143
|
+
'--no-logcheck'
|
|
144
|
+
], false));
|
|
145
|
+
return [4 /*yield*/, command.execute()];
|
|
146
|
+
case 1:
|
|
147
|
+
_a.sent();
|
|
148
|
+
expect((0, logcheck_1.getLogCheckMode)()).toEqual('off');
|
|
149
|
+
return [2 /*return*/];
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
}); });
|
|
153
|
+
it('should switch the log check on for --logcheck', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
154
|
+
var command;
|
|
155
|
+
return __generator(this, function (_a) {
|
|
156
|
+
switch (_a.label) {
|
|
157
|
+
case 0:
|
|
158
|
+
command = new runCommand_1.RunCommand(__spreadArray(__spreadArray([], __read(defaultArgs), false), [sasFilePath, '--logcheck'], false));
|
|
159
|
+
return [4 /*yield*/, command.execute()];
|
|
160
|
+
case 1:
|
|
161
|
+
_a.sent();
|
|
162
|
+
expect((0, logcheck_1.getLogCheckMode)()).toEqual('on');
|
|
163
|
+
return [2 /*return*/];
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
}); });
|
|
117
167
|
it('should parse sasjs run command', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
118
168
|
return __generator(this, function (_a) {
|
|
119
169
|
switch (_a.label) {
|
package/build/utils/index.d.ts
CHANGED
package/build/utils/index.js
CHANGED
|
@@ -19,6 +19,7 @@ __exportStar(require("./config"), exports);
|
|
|
19
19
|
__exportStar(require("./displayResult"), exports);
|
|
20
20
|
__exportStar(require("./file"), exports);
|
|
21
21
|
__exportStar(require("./loadEnvVariables"), exports);
|
|
22
|
+
__exportStar(require("./logcheck"), exports);
|
|
22
23
|
__exportStar(require("./saveLog"), exports);
|
|
23
24
|
__exportStar(require("./setConstants"), exports);
|
|
24
25
|
__exportStar(require("./setProjectDir"), exports);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How the log a command just received is treated.
|
|
3
|
+
*
|
|
4
|
+
* `auto` decides from `logCheck.autoCheck` in the project config, which is off
|
|
5
|
+
* by default; `on` and `off` are the `--logcheck` / `--no-logcheck` flags. The
|
|
6
|
+
* distinction matters because the two halves of the behaviour are separate:
|
|
7
|
+
* reporting what SAS printed is always worth doing, while changing the exit
|
|
8
|
+
* code of a command whose status a pipeline already trusts is not something to
|
|
9
|
+
* do behind a project's back.
|
|
10
|
+
*/
|
|
11
|
+
export type LogCheckMode = 'auto' | 'on' | 'off';
|
|
12
|
+
export declare const setLogCheckMode: (value: LogCheckMode) => void;
|
|
13
|
+
export declare const getLogCheckMode: () => LogCheckMode;
|
|
14
|
+
/** Reads a tri-state yargs boolean: absent is `undefined`, not `false`. */
|
|
15
|
+
export declare const logCheckModeOf: (flag: unknown) => LogCheckMode;
|
|
16
|
+
/**
|
|
17
|
+
* A run whose log broke the policy, once `autoCheck` or `--logcheck` asked for
|
|
18
|
+
* the verdict to count.
|
|
19
|
+
*
|
|
20
|
+
* It is an error rather than a return value because it is raised several
|
|
21
|
+
* frames below the command, from whichever executor received the log, and the
|
|
22
|
+
* command is the only place that can turn it into an exit code.
|
|
23
|
+
*/
|
|
24
|
+
export declare class LogCheckFailure extends Error {
|
|
25
|
+
/**
|
|
26
|
+
* Marker rather than `instanceof`: the CLI is compiled to a target where a
|
|
27
|
+
* subclass of a built-in loses its prototype chain, so `instanceof` answers
|
|
28
|
+
* false in the built artefact while answering true under the test transform.
|
|
29
|
+
* The flag is read by {@link isLogCheckFailure} and cannot be faked by an
|
|
30
|
+
* unrelated error.
|
|
31
|
+
*/
|
|
32
|
+
readonly logCheckFailure = true;
|
|
33
|
+
constructor(message: string);
|
|
34
|
+
}
|
|
35
|
+
export declare const isLogCheckFailure: (error: unknown) => error is LogCheckFailure;
|
|
36
|
+
/**
|
|
37
|
+
* Reports the issues in a log a command just received, and throws when the
|
|
38
|
+
* policy says the run should fail.
|
|
39
|
+
*
|
|
40
|
+
* Nothing is printed for a clean log: the commands this runs inside already
|
|
41
|
+
* have their own success output, and a check that announces itself on every
|
|
42
|
+
* green run is noise that gets switched off.
|
|
43
|
+
*/
|
|
44
|
+
export declare const checkLog: (logText: string | undefined | null, label: string) => Promise<void>;
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __extends = (this && this.__extends) || (function () {
|
|
3
|
+
var extendStatics = function (d, b) {
|
|
4
|
+
extendStatics = Object.setPrototypeOf ||
|
|
5
|
+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
6
|
+
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
7
|
+
return extendStatics(d, b);
|
|
8
|
+
};
|
|
9
|
+
return function (d, b) {
|
|
10
|
+
if (typeof b !== "function" && b !== null)
|
|
11
|
+
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
|
12
|
+
extendStatics(d, b);
|
|
13
|
+
function __() { this.constructor = d; }
|
|
14
|
+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
15
|
+
};
|
|
16
|
+
})();
|
|
17
|
+
var __assign = (this && this.__assign) || function () {
|
|
18
|
+
__assign = Object.assign || function(t) {
|
|
19
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
20
|
+
s = arguments[i];
|
|
21
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
22
|
+
t[p] = s[p];
|
|
23
|
+
}
|
|
24
|
+
return t;
|
|
25
|
+
};
|
|
26
|
+
return __assign.apply(this, arguments);
|
|
27
|
+
};
|
|
28
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
29
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
30
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
31
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
32
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
33
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
34
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
38
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
39
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
40
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
41
|
+
function step(op) {
|
|
42
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
43
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
44
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
45
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
46
|
+
switch (op[0]) {
|
|
47
|
+
case 0: case 1: t = op; break;
|
|
48
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
49
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
50
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
51
|
+
default:
|
|
52
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
53
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
54
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
55
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
56
|
+
if (t[2]) _.ops.pop();
|
|
57
|
+
_.trys.pop(); continue;
|
|
58
|
+
}
|
|
59
|
+
op = body.call(thisArg, _);
|
|
60
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
61
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
var __read = (this && this.__read) || function (o, n) {
|
|
65
|
+
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
66
|
+
if (!m) return o;
|
|
67
|
+
var i = m.call(o), r, ar = [], e;
|
|
68
|
+
try {
|
|
69
|
+
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
70
|
+
}
|
|
71
|
+
catch (error) { e = { error: error }; }
|
|
72
|
+
finally {
|
|
73
|
+
try {
|
|
74
|
+
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
75
|
+
}
|
|
76
|
+
finally { if (e) throw e.error; }
|
|
77
|
+
}
|
|
78
|
+
return ar;
|
|
79
|
+
};
|
|
80
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
81
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
82
|
+
if (ar || !(i in from)) {
|
|
83
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
84
|
+
ar[i] = from[i];
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
88
|
+
};
|
|
89
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
90
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
91
|
+
};
|
|
92
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
93
|
+
exports.checkLog = exports.isLogCheckFailure = exports.LogCheckFailure = exports.logCheckModeOf = exports.getLogCheckMode = exports.setLogCheckMode = void 0;
|
|
94
|
+
var logs_1 = require("@sasjs/utils/logs");
|
|
95
|
+
var chalk_1 = __importDefault(require("chalk"));
|
|
96
|
+
var collectLogs_1 = require("../commands/logcheck/collectLogs");
|
|
97
|
+
var logCheckConfig_1 = require("../commands/logcheck/logCheckConfig");
|
|
98
|
+
var processLogCheck_1 = require("../commands/logcheck/processLogCheck");
|
|
99
|
+
var renderLogCheck_1 = require("../commands/logcheck/renderLogCheck");
|
|
100
|
+
var mode = 'auto';
|
|
101
|
+
var setLogCheckMode = function (value) {
|
|
102
|
+
mode = value;
|
|
103
|
+
};
|
|
104
|
+
exports.setLogCheckMode = setLogCheckMode;
|
|
105
|
+
var getLogCheckMode = function () { return mode; };
|
|
106
|
+
exports.getLogCheckMode = getLogCheckMode;
|
|
107
|
+
/** Reads a tri-state yargs boolean: absent is `undefined`, not `false`. */
|
|
108
|
+
var logCheckModeOf = function (flag) {
|
|
109
|
+
return flag === true ? 'on' : flag === false ? 'off' : 'auto';
|
|
110
|
+
};
|
|
111
|
+
exports.logCheckModeOf = logCheckModeOf;
|
|
112
|
+
/**
|
|
113
|
+
* A run whose log broke the policy, once `autoCheck` or `--logcheck` asked for
|
|
114
|
+
* the verdict to count.
|
|
115
|
+
*
|
|
116
|
+
* It is an error rather than a return value because it is raised several
|
|
117
|
+
* frames below the command, from whichever executor received the log, and the
|
|
118
|
+
* command is the only place that can turn it into an exit code.
|
|
119
|
+
*/
|
|
120
|
+
var LogCheckFailure = /** @class */ (function (_super) {
|
|
121
|
+
__extends(LogCheckFailure, _super);
|
|
122
|
+
function LogCheckFailure(message) {
|
|
123
|
+
var _this = _super.call(this, message) || this;
|
|
124
|
+
/**
|
|
125
|
+
* Marker rather than `instanceof`: the CLI is compiled to a target where a
|
|
126
|
+
* subclass of a built-in loses its prototype chain, so `instanceof` answers
|
|
127
|
+
* false in the built artefact while answering true under the test transform.
|
|
128
|
+
* The flag is read by {@link isLogCheckFailure} and cannot be faked by an
|
|
129
|
+
* unrelated error.
|
|
130
|
+
*/
|
|
131
|
+
_this.logCheckFailure = true;
|
|
132
|
+
_this.name = 'LogCheckFailure';
|
|
133
|
+
return _this;
|
|
134
|
+
}
|
|
135
|
+
return LogCheckFailure;
|
|
136
|
+
}(Error));
|
|
137
|
+
exports.LogCheckFailure = LogCheckFailure;
|
|
138
|
+
var isLogCheckFailure = function (error) {
|
|
139
|
+
return (error === null || error === void 0 ? void 0 : error.logCheckFailure) === true;
|
|
140
|
+
};
|
|
141
|
+
exports.isLogCheckFailure = isLogCheckFailure;
|
|
142
|
+
/**
|
|
143
|
+
* Reports the issues in a log a command just received, and throws when the
|
|
144
|
+
* policy says the run should fail.
|
|
145
|
+
*
|
|
146
|
+
* Nothing is printed for a clean log: the commands this runs inside already
|
|
147
|
+
* have their own success output, and a check that announces itself on every
|
|
148
|
+
* green run is noise that gets switched off.
|
|
149
|
+
*/
|
|
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)
|
|
157
|
+
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)
|
|
174
|
+
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
|
+
}
|
|
185
|
+
});
|
|
186
|
+
}); };
|
|
187
|
+
exports.checkLog = checkLog;
|
|
188
|
+
/**
|
|
189
|
+
* The inline report: the heading, the issues, and the verdict when there is
|
|
190
|
+
* one. Deliberately narrower than the `logcheck` report - there is one log
|
|
191
|
+
* here and its name is already on screen, so there is no file table.
|
|
192
|
+
*/
|
|
193
|
+
var renderInline = function (label, issues, counts, suppressedCount, config, failure) {
|
|
194
|
+
var _a;
|
|
195
|
+
var lines = [
|
|
196
|
+
"".concat(chalk_1.default.bold('SAS log check'), " ").concat(chalk_1.default.dim(label), " - ").concat(describe(counts))
|
|
197
|
+
];
|
|
198
|
+
lines.push.apply(lines, __spreadArray([], __read((0, renderLogCheck_1.columns)(issues.map(function (issue) { return [
|
|
199
|
+
(0, renderLogCheck_1.lineLabel)(issue),
|
|
200
|
+
(0, renderLogCheck_1.severityLabel)(issue.severity),
|
|
201
|
+
issue.code || chalk_1.default.dim('-'),
|
|
202
|
+
(0, renderLogCheck_1.truncate)(issue.message)
|
|
203
|
+
]; }), ['LINE:COL', 'SEVERITY', 'CODE', 'MESSAGE'], ' ', ' ')), false));
|
|
204
|
+
if (suppressedCount) {
|
|
205
|
+
lines.push(chalk_1.default.dim(" ".concat(suppressedCount, " issue").concat(suppressedCount === 1 ? '' : 's', " suppressed by message code").concat(config.ignoreCodes.length ? " (".concat(config.ignoreCodes.join(', '), ")") : '')));
|
|
206
|
+
}
|
|
207
|
+
if (failure) {
|
|
208
|
+
lines.push("".concat(chalk_1.default.red.bold(' Logcheck failed: ')).concat(chalk_1.default.red(failure)));
|
|
209
|
+
}
|
|
210
|
+
(_a = process.logger) === null || _a === void 0 ? void 0 : _a.log(lines.join('\n'));
|
|
211
|
+
};
|
|
212
|
+
var describe = function (counts) {
|
|
213
|
+
var parts = [];
|
|
214
|
+
if (counts.errors)
|
|
215
|
+
parts.push(plural(counts.errors, 'error'));
|
|
216
|
+
if (counts.warnings)
|
|
217
|
+
parts.push(plural(counts.warnings, 'warning'));
|
|
218
|
+
if (counts.notes)
|
|
219
|
+
parts.push(plural(counts.notes, 'note'));
|
|
220
|
+
return parts.join(', ');
|
|
221
|
+
};
|
|
222
|
+
var plural = function (count, noun) {
|
|
223
|
+
return "".concat(count, " ").concat(noun).concat(count === 1 ? '' : 's');
|
|
224
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
12
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
13
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
14
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
15
|
+
function step(op) {
|
|
16
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
17
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
18
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
19
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
20
|
+
switch (op[0]) {
|
|
21
|
+
case 0: case 1: t = op; break;
|
|
22
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
23
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
24
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
25
|
+
default:
|
|
26
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
27
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
28
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
29
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
30
|
+
if (t[2]) _.ops.pop();
|
|
31
|
+
_.trys.pop(); continue;
|
|
32
|
+
}
|
|
33
|
+
op = body.call(thisArg, _);
|
|
34
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
35
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
39
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
40
|
+
};
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
var os_1 = __importDefault(require("os"));
|
|
43
|
+
var path_1 = __importDefault(require("path"));
|
|
44
|
+
var fs_1 = require("fs");
|
|
45
|
+
var utils_1 = require("@sasjs/utils");
|
|
46
|
+
var logcheck_1 = require("../logcheck");
|
|
47
|
+
var sasLog = function (extra) {
|
|
48
|
+
if (extra === void 0) { extra = ''; }
|
|
49
|
+
return "NOTE: Copyright (c) 2016 by SAS Institute Inc., Cary, NC, USA.\n\n1 data work.x;\n2 set work.y;\n3 run;\n".concat(extra);
|
|
50
|
+
};
|
|
51
|
+
var logWithError = sasLog('ERROR 22-322: Syntax error.\n');
|
|
52
|
+
var logWithNote = sasLog('NOTE: Variable flag is uninitialized.\n');
|
|
53
|
+
var cleanLog = sasLog();
|
|
54
|
+
var tempFolder = function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
|
|
55
|
+
switch (_a.label) {
|
|
56
|
+
case 0: return [4 /*yield*/, fs_1.promises.mkdtemp(path_1.default.join(os_1.default.tmpdir(), 'sasjs-logcheck-inline-'))];
|
|
57
|
+
case 1: return [2 /*return*/, _a.sent()];
|
|
58
|
+
}
|
|
59
|
+
}); }); };
|
|
60
|
+
describe('logCheckModeOf', function () {
|
|
61
|
+
it('reads the tri-state a yargs boolean gives', function () {
|
|
62
|
+
expect((0, logcheck_1.logCheckModeOf)(true)).toEqual('on');
|
|
63
|
+
expect((0, logcheck_1.logCheckModeOf)(false)).toEqual('off');
|
|
64
|
+
expect((0, logcheck_1.logCheckModeOf)(undefined)).toEqual('auto');
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
describe('checkLog', function () {
|
|
68
|
+
var originalProjectDir = process.projectDir;
|
|
69
|
+
var logged;
|
|
70
|
+
var withProject = function (logCheck) { return __awaiter(void 0, void 0, void 0, function () {
|
|
71
|
+
var folder;
|
|
72
|
+
return __generator(this, function (_a) {
|
|
73
|
+
switch (_a.label) {
|
|
74
|
+
case 0: return [4 /*yield*/, tempFolder()];
|
|
75
|
+
case 1:
|
|
76
|
+
folder = _a.sent();
|
|
77
|
+
process.projectDir = folder;
|
|
78
|
+
if (!(logCheck !== undefined)) return [3 /*break*/, 3];
|
|
79
|
+
return [4 /*yield*/, fs_1.promises.writeFile(path_1.default.join(folder, 'sasjsconfig.json'), JSON.stringify({ logCheck: logCheck }))];
|
|
80
|
+
case 2:
|
|
81
|
+
_a.sent();
|
|
82
|
+
_a.label = 3;
|
|
83
|
+
case 3: return [2 /*return*/, folder];
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}); };
|
|
87
|
+
beforeEach(function () {
|
|
88
|
+
logged = [];
|
|
89
|
+
process.logger = {
|
|
90
|
+
log: function (message) { return logged.push(message); }
|
|
91
|
+
};
|
|
92
|
+
process.logger.logLevel = utils_1.LogLevel.Off;
|
|
93
|
+
});
|
|
94
|
+
afterEach(function () {
|
|
95
|
+
process.projectDir = originalProjectDir;
|
|
96
|
+
process.logger = undefined;
|
|
97
|
+
(0, logcheck_1.setLogCheckMode)('auto');
|
|
98
|
+
});
|
|
99
|
+
it('says nothing for a clean log', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
100
|
+
return __generator(this, function (_a) {
|
|
101
|
+
switch (_a.label) {
|
|
102
|
+
case 0: return [4 /*yield*/, withProject()];
|
|
103
|
+
case 1:
|
|
104
|
+
_a.sent();
|
|
105
|
+
return [4 /*yield*/, (0, logcheck_1.checkLog)(cleanLog, 'run-job.sas')];
|
|
106
|
+
case 2:
|
|
107
|
+
_a.sent();
|
|
108
|
+
expect(logged).toEqual([]);
|
|
109
|
+
return [2 /*return*/];
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}); });
|
|
113
|
+
it('says nothing when it is given no log at all', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
114
|
+
return __generator(this, function (_a) {
|
|
115
|
+
switch (_a.label) {
|
|
116
|
+
case 0: return [4 /*yield*/, withProject()];
|
|
117
|
+
case 1:
|
|
118
|
+
_a.sent();
|
|
119
|
+
return [4 /*yield*/, (0, logcheck_1.checkLog)(undefined, 'run-job.sas')];
|
|
120
|
+
case 2:
|
|
121
|
+
_a.sent();
|
|
122
|
+
expect(logged).toEqual([]);
|
|
123
|
+
return [2 /*return*/];
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
}); });
|
|
127
|
+
it('reports the issues without failing by default', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
128
|
+
var output;
|
|
129
|
+
return __generator(this, function (_a) {
|
|
130
|
+
switch (_a.label) {
|
|
131
|
+
case 0: return [4 /*yield*/, withProject()];
|
|
132
|
+
case 1:
|
|
133
|
+
_a.sent();
|
|
134
|
+
return [4 /*yield*/, (0, logcheck_1.checkLog)(logWithError, 'run-job.sas')];
|
|
135
|
+
case 2:
|
|
136
|
+
_a.sent();
|
|
137
|
+
output = logged.join('\n');
|
|
138
|
+
expect(output).toContain('SAS log check run-job.sas');
|
|
139
|
+
expect(output).toContain('1 error');
|
|
140
|
+
expect(output).toContain('Syntax error.');
|
|
141
|
+
expect(output).not.toContain('Logcheck failed');
|
|
142
|
+
return [2 /*return*/];
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
}); });
|
|
146
|
+
it('skips the check, with a warning, when the config cannot be read', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
147
|
+
var folder, warn;
|
|
148
|
+
return __generator(this, function (_a) {
|
|
149
|
+
switch (_a.label) {
|
|
150
|
+
case 0: return [4 /*yield*/, tempFolder()];
|
|
151
|
+
case 1:
|
|
152
|
+
folder = _a.sent();
|
|
153
|
+
process.projectDir = folder;
|
|
154
|
+
return [4 /*yield*/, fs_1.promises.writeFile(path_1.default.join(folder, 'sasjsconfig.json'), '{ "logCheck": ')];
|
|
155
|
+
case 2:
|
|
156
|
+
_a.sent();
|
|
157
|
+
warn = jest.fn();
|
|
158
|
+
process.logger.warn = warn;
|
|
159
|
+
return [4 /*yield*/, (0, logcheck_1.checkLog)(logWithError, 'run-job.sas')];
|
|
160
|
+
case 3:
|
|
161
|
+
_a.sent();
|
|
162
|
+
expect(warn).toHaveBeenCalledWith(expect.stringContaining('Skipping the SAS log check'));
|
|
163
|
+
expect(logged).toEqual([]);
|
|
164
|
+
return [2 /*return*/];
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}); });
|
|
168
|
+
it('says nothing at all under --no-logcheck', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
169
|
+
return __generator(this, function (_a) {
|
|
170
|
+
switch (_a.label) {
|
|
171
|
+
case 0: return [4 /*yield*/, withProject({ autoCheck: true })];
|
|
172
|
+
case 1:
|
|
173
|
+
_a.sent();
|
|
174
|
+
(0, logcheck_1.setLogCheckMode)('off');
|
|
175
|
+
return [4 /*yield*/, (0, logcheck_1.checkLog)(logWithError, 'run-job.sas')];
|
|
176
|
+
case 2:
|
|
177
|
+
_a.sent();
|
|
178
|
+
expect(logged).toEqual([]);
|
|
179
|
+
return [2 /*return*/];
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
}); });
|
|
183
|
+
it('fails the run when the project asked for it', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
184
|
+
return __generator(this, function (_a) {
|
|
185
|
+
switch (_a.label) {
|
|
186
|
+
case 0: return [4 /*yield*/, withProject({ autoCheck: true })];
|
|
187
|
+
case 1:
|
|
188
|
+
_a.sent();
|
|
189
|
+
return [4 /*yield*/, expect((0, logcheck_1.checkLog)(logWithError, 'run-job.sas')).rejects.toThrow('SAS log check failed for run-job.sas: 1 error')];
|
|
190
|
+
case 2:
|
|
191
|
+
_a.sent();
|
|
192
|
+
expect(logged.join('\n')).toContain('Logcheck failed: 1 error');
|
|
193
|
+
return [2 /*return*/];
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
}); });
|
|
197
|
+
it('fails the run when the flag asked for it, without the config', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
198
|
+
return __generator(this, function (_a) {
|
|
199
|
+
switch (_a.label) {
|
|
200
|
+
case 0: return [4 /*yield*/, withProject()];
|
|
201
|
+
case 1:
|
|
202
|
+
_a.sent();
|
|
203
|
+
(0, logcheck_1.setLogCheckMode)('on');
|
|
204
|
+
return [4 /*yield*/, expect((0, logcheck_1.checkLog)(logWithError, 'run-job.sas')).rejects.toBeInstanceOf(logcheck_1.LogCheckFailure)];
|
|
205
|
+
case 2:
|
|
206
|
+
_a.sent();
|
|
207
|
+
return [2 /*return*/];
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
}); });
|
|
211
|
+
it('does not fail on a note under the default policy', 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({ autoCheck: true })];
|
|
215
|
+
case 1:
|
|
216
|
+
_a.sent();
|
|
217
|
+
return [4 /*yield*/, (0, logcheck_1.checkLog)(logWithNote, 'run-job.sas')];
|
|
218
|
+
case 2:
|
|
219
|
+
_a.sent();
|
|
220
|
+
expect(logged.join('\n')).toContain('1 note');
|
|
221
|
+
expect(logged.join('\n')).not.toContain('Logcheck failed');
|
|
222
|
+
return [2 /*return*/];
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
}); });
|
|
226
|
+
it('fails on a note when the policy says note', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
227
|
+
return __generator(this, function (_a) {
|
|
228
|
+
switch (_a.label) {
|
|
229
|
+
case 0: return [4 /*yield*/, withProject({ autoCheck: true, failOn: 'note' })];
|
|
230
|
+
case 1:
|
|
231
|
+
_a.sent();
|
|
232
|
+
return [4 /*yield*/, expect((0, logcheck_1.checkLog)(logWithNote, 'run-job.sas')).rejects.toThrow('1 note')];
|
|
233
|
+
case 2:
|
|
234
|
+
_a.sent();
|
|
235
|
+
return [2 /*return*/];
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
}); });
|
|
239
|
+
it('suppresses an issue by message code', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
240
|
+
return __generator(this, function (_a) {
|
|
241
|
+
switch (_a.label) {
|
|
242
|
+
case 0: return [4 /*yield*/, withProject({ ignoreCodes: ['22-322'] })];
|
|
243
|
+
case 1:
|
|
244
|
+
_a.sent();
|
|
245
|
+
return [4 /*yield*/, (0, logcheck_1.checkLog)(logWithError, 'run-job.sas')];
|
|
246
|
+
case 2:
|
|
247
|
+
_a.sent();
|
|
248
|
+
expect(logged).toEqual([]);
|
|
249
|
+
return [2 /*return*/];
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
}); });
|
|
253
|
+
it('reports a suppressed issue alongside the ones that remain', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
254
|
+
var output;
|
|
255
|
+
return __generator(this, function (_a) {
|
|
256
|
+
switch (_a.label) {
|
|
257
|
+
case 0: return [4 /*yield*/, withProject({ ignoreCodes: ['22-322'], autoCheck: true })];
|
|
258
|
+
case 1:
|
|
259
|
+
_a.sent();
|
|
260
|
+
return [4 /*yield*/, (0, logcheck_1.checkLog)(sasLog('ERROR 22-322: Syntax error.\nNOTE: Variable flag is uninitialized.\n'), 'run-job.sas').catch(function () { return undefined; })];
|
|
261
|
+
case 2:
|
|
262
|
+
_a.sent();
|
|
263
|
+
output = logged.join('\n');
|
|
264
|
+
expect(output).toContain('1 note');
|
|
265
|
+
expect(output).toContain('1 issue suppressed by message code (22-322)');
|
|
266
|
+
expect(output).not.toContain('Logcheck failed');
|
|
267
|
+
return [2 /*return*/];
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
}); });
|
|
271
|
+
});
|
|
272
|
+
describe('isLogCheckFailure', function () {
|
|
273
|
+
it('recognises only its own error', function () {
|
|
274
|
+
expect((0, logcheck_1.isLogCheckFailure)(new logcheck_1.LogCheckFailure('x'))).toBe(true);
|
|
275
|
+
expect((0, logcheck_1.isLogCheckFailure)(new Error('x'))).toBe(false);
|
|
276
|
+
expect((0, logcheck_1.isLogCheckFailure)('x')).toBe(false);
|
|
277
|
+
expect((0, logcheck_1.isLogCheckFailure)(undefined)).toBe(false);
|
|
278
|
+
});
|
|
279
|
+
it('is not fooled by an error that only borrows the name', function () {
|
|
280
|
+
// The marker is what the built CLI relies on: the compiled target loses the
|
|
281
|
+
// prototype chain of an Error subclass, so `instanceof` is not available
|
|
282
|
+
// there and the name alone would be a weak claim.
|
|
283
|
+
var impostor = new Error('x');
|
|
284
|
+
impostor.name = 'LogCheckFailure';
|
|
285
|
+
expect((0, logcheck_1.isLogCheckFailure)(impostor)).toBe(false);
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
describe('getLogCheckMode', function () {
|
|
289
|
+
afterEach(function () { return (0, logcheck_1.setLogCheckMode)('auto'); });
|
|
290
|
+
it('starts on auto and follows what it is set to', function () {
|
|
291
|
+
expect((0, logcheck_1.getLogCheckMode)()).toEqual('auto');
|
|
292
|
+
(0, logcheck_1.setLogCheckMode)('on');
|
|
293
|
+
expect((0, logcheck_1.getLogCheckMode)()).toEqual('on');
|
|
294
|
+
(0, logcheck_1.setLogCheckMode)('off');
|
|
295
|
+
expect((0, logcheck_1.getLogCheckMode)()).toEqual('off');
|
|
296
|
+
});
|
|
297
|
+
});
|
package/package.json
CHANGED