@sasjs/cli 4.18.6 → 4.20.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.
@@ -0,0 +1,665 @@
1
+ "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
13
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ var desc = Object.getOwnPropertyDescriptor(m, k);
16
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
17
+ desc = { enumerable: true, get: function() { return m[k]; } };
18
+ }
19
+ Object.defineProperty(o, k2, desc);
20
+ }) : (function(o, m, k, k2) {
21
+ if (k2 === undefined) k2 = k;
22
+ o[k2] = m[k];
23
+ }));
24
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
25
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
26
+ }) : function(o, v) {
27
+ o["default"] = v;
28
+ });
29
+ var __importStar = (this && this.__importStar) || function (mod) {
30
+ if (mod && mod.__esModule) return mod;
31
+ var result = {};
32
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
33
+ __setModuleDefault(result, mod);
34
+ return result;
35
+ };
36
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
37
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
38
+ return new (P || (P = Promise))(function (resolve, reject) {
39
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
40
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
41
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
42
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
43
+ });
44
+ };
45
+ var __generator = (this && this.__generator) || function (thisArg, body) {
46
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
47
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
48
+ function verb(n) { return function (v) { return step([n, v]); }; }
49
+ function step(op) {
50
+ if (f) throw new TypeError("Generator is already executing.");
51
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
52
+ 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;
53
+ if (y = 0, t) op = [op[0] & 2, t.value];
54
+ switch (op[0]) {
55
+ case 0: case 1: t = op; break;
56
+ case 4: _.label++; return { value: op[1], done: false };
57
+ case 5: _.label++; y = op[1]; op = [0]; continue;
58
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
59
+ default:
60
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
61
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
62
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
63
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
64
+ if (t[2]) _.ops.pop();
65
+ _.trys.pop(); continue;
66
+ }
67
+ op = body.call(thisArg, _);
68
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
69
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
70
+ }
71
+ };
72
+ var __importDefault = (this && this.__importDefault) || function (mod) {
73
+ return (mod && mod.__esModule) ? mod : { "default": mod };
74
+ };
75
+ Object.defineProperty(exports, "__esModule", { value: true });
76
+ var prompts_1 = __importDefault(require("prompts"));
77
+ var utils_1 = require("@sasjs/utils");
78
+ var utilsModule = __importStar(require("../../../utils"));
79
+ var authLoginModule = __importStar(require("../login"));
80
+ // Mock prompts so the password prompt returns immediately without blocking.
81
+ jest.mock('prompts', function () { return ({
82
+ __esModule: true,
83
+ default: jest.fn(function () { return Promise.resolve({ pass: 'test-pass' }); })
84
+ }); });
85
+ // Mock getString from @sasjs/utils so the username prompt doesn't block.
86
+ // We keep the rest of @sasjs/utils real (ServerType, Target, Logger, etc.).
87
+ jest.mock('@sasjs/utils', function () {
88
+ var actual = jest.requireActual('@sasjs/utils');
89
+ return __assign(__assign({}, actual), { getString: jest.fn(function () { return Promise.resolve('test-user'); }) });
90
+ });
91
+ // Re-import after mock so login.ts picks up the mocked getString.
92
+ var utils_2 = require("@sasjs/utils");
93
+ // Mock the three orchestration dependencies that authLogin delegates to.
94
+ // We spy on the utils barrel (../../utils) which re-exports from auth.ts and
95
+ // config.ts, so the spies are the same references login.ts resolves at runtime.
96
+ jest.mock('../../../utils', function () { return (__assign(__assign({}, jest.requireActual('../../../utils')), { getTokensWithPasswordGrant: jest.fn(), fetchLoggedInUser: jest.fn(), saveTokens: jest.fn() })); });
97
+ var viyaTarget = new utils_1.Target({
98
+ name: 'viya-test',
99
+ serverType: utils_1.ServerType.SasViya,
100
+ serverUrl: 'https://viya.example.com',
101
+ appLoc: '/Public/test',
102
+ contextName: 'test context'
103
+ });
104
+ var sas9Target = new utils_1.Target({
105
+ name: 'sas9-test',
106
+ serverType: utils_1.ServerType.Sas9,
107
+ serverUrl: 'https://sas9.example.com',
108
+ appLoc: '/Public/test',
109
+ contextName: 'test context'
110
+ });
111
+ describe('authLogin', function () {
112
+ beforeEach(function () {
113
+ process.logger = new utils_1.Logger(utils_1.LogLevel.Off);
114
+ jest.clearAllMocks();
115
+ utils_2.getString.mockResolvedValue('test-user');
116
+ prompts_1.default.mockResolvedValue({
117
+ pass: 'test-pass'
118
+ });
119
+ utilsModule.getTokensWithPasswordGrant.mockResolvedValue({
120
+ access_token: 'access-123',
121
+ refresh_token: 'refresh-456'
122
+ });
123
+ utilsModule.fetchLoggedInUser.mockResolvedValue({
124
+ id: 'sastest',
125
+ name: 'SAS Test User'
126
+ });
127
+ utilsModule.saveTokens.mockResolvedValue(undefined);
128
+ // Reset env vars and TTY between tests.
129
+ delete process.env.SAS_USERNAME;
130
+ delete process.env.SAS_PASSWORD;
131
+ // Default to TTY=true so interactive prompts are allowed unless a test
132
+ // explicitly overrides.
133
+ Object.defineProperty(process.stdin, 'isTTY', {
134
+ value: true,
135
+ configurable: true
136
+ });
137
+ });
138
+ afterAll(function () {
139
+ jest.restoreAllMocks();
140
+ delete process.env.SAS_USERNAME;
141
+ delete process.env.SAS_PASSWORD;
142
+ Object.defineProperty(process.stdin, 'isTTY', {
143
+ value: true,
144
+ configurable: true
145
+ });
146
+ });
147
+ // ---------------------------------------------------------------------------
148
+ // Guard 1: Non-Viya rejection
149
+ // ---------------------------------------------------------------------------
150
+ it('should reject when target.serverType is not SASVIYA', function () { return __awaiter(void 0, void 0, void 0, function () {
151
+ return __generator(this, function (_a) {
152
+ switch (_a.label) {
153
+ case 0: return [4 /*yield*/, expect(authLoginModule.authLogin(sas9Target)).rejects.toThrow("'sasjs auth login' is only supported for SASVIYA targets.")];
154
+ case 1:
155
+ _a.sent();
156
+ expect(utilsModule.getTokensWithPasswordGrant).not.toHaveBeenCalled();
157
+ expect(utilsModule.saveTokens).not.toHaveBeenCalled();
158
+ return [2 /*return*/];
159
+ }
160
+ });
161
+ }); });
162
+ // ---------------------------------------------------------------------------
163
+ // Guard 2: Missing serverUrl
164
+ // ---------------------------------------------------------------------------
165
+ it('should reject when target.serverUrl is missing/empty', function () { return __awaiter(void 0, void 0, void 0, function () {
166
+ var noUrlTarget;
167
+ return __generator(this, function (_a) {
168
+ switch (_a.label) {
169
+ case 0:
170
+ noUrlTarget = new utils_1.Target({
171
+ name: 'no-url',
172
+ serverType: utils_1.ServerType.SasViya,
173
+ serverUrl: '',
174
+ appLoc: '/Public/test',
175
+ contextName: 'test context'
176
+ });
177
+ return [4 /*yield*/, expect(authLoginModule.authLogin(noUrlTarget)).rejects.toThrow("Target 'no-url' does not have a serverUrl configured.")];
178
+ case 1:
179
+ _a.sent();
180
+ expect(utilsModule.getTokensWithPasswordGrant).not.toHaveBeenCalled();
181
+ expect(utilsModule.saveTokens).not.toHaveBeenCalled();
182
+ return [2 /*return*/];
183
+ }
184
+ });
185
+ }); });
186
+ // ---------------------------------------------------------------------------
187
+ // Guard 3: --insecure httpsAgentOptions mutation
188
+ // ---------------------------------------------------------------------------
189
+ it('should set rejectUnauthorized: false on target.httpsAgentOptions when insecure is true', function () { return __awaiter(void 0, void 0, void 0, function () {
190
+ var callTarget;
191
+ return __generator(this, function (_a) {
192
+ switch (_a.label) {
193
+ case 0: return [4 /*yield*/, authLoginModule.authLogin(viyaTarget, true)];
194
+ case 1:
195
+ _a.sent();
196
+ callTarget = utilsModule.getTokensWithPasswordGrant
197
+ .mock.calls[0][0];
198
+ expect(callTarget.httpsAgentOptions).toMatchObject({
199
+ rejectUnauthorized: false,
200
+ allowInsecureRequests: true
201
+ });
202
+ return [2 /*return*/];
203
+ }
204
+ });
205
+ }); });
206
+ // ---------------------------------------------------------------------------
207
+ // Guard 3b: insecure=false should NOT mutate httpsAgentOptions
208
+ // ---------------------------------------------------------------------------
209
+ it('should not mutate httpsAgentOptions when insecure is false', function () { return __awaiter(void 0, void 0, void 0, function () {
210
+ var callTarget;
211
+ var _a;
212
+ return __generator(this, function (_b) {
213
+ switch (_b.label) {
214
+ case 0: return [4 /*yield*/, authLoginModule.authLogin(viyaTarget, false)];
215
+ case 1:
216
+ _b.sent();
217
+ callTarget = utilsModule.getTokensWithPasswordGrant
218
+ .mock.calls[0][0];
219
+ // The original target has no rejectUnauthorized set at all.
220
+ expect((_a = callTarget.httpsAgentOptions) === null || _a === void 0 ? void 0 : _a.rejectUnauthorized).not.toBe(false);
221
+ return [2 /*return*/];
222
+ }
223
+ });
224
+ }); });
225
+ // ---------------------------------------------------------------------------
226
+ // Guard 4: saveTokens called with correct args on successful login
227
+ // ---------------------------------------------------------------------------
228
+ it('should call saveTokens with the target name, access token, and refresh token after successful login', function () { return __awaiter(void 0, void 0, void 0, function () {
229
+ return __generator(this, function (_a) {
230
+ switch (_a.label) {
231
+ case 0: return [4 /*yield*/, authLoginModule.authLogin(viyaTarget, false)];
232
+ case 1:
233
+ _a.sent();
234
+ expect(utilsModule.saveTokens).toHaveBeenCalledWith(viyaTarget.name, 'access-123', 'refresh-456');
235
+ expect(utilsModule.saveTokens).toHaveBeenCalledTimes(1);
236
+ return [2 /*return*/];
237
+ }
238
+ });
239
+ }); });
240
+ // ---------------------------------------------------------------------------
241
+ // Guard 4b: saveTokens should default refresh_token to '' when omitted
242
+ // ---------------------------------------------------------------------------
243
+ it('should call saveTokens with empty string refresh_token when the server omits it', function () { return __awaiter(void 0, void 0, void 0, function () {
244
+ return __generator(this, function (_a) {
245
+ switch (_a.label) {
246
+ case 0:
247
+ ;
248
+ utilsModule.getTokensWithPasswordGrant.mockResolvedValue({
249
+ access_token: 'access-789',
250
+ refresh_token: ''
251
+ });
252
+ return [4 /*yield*/, authLoginModule.authLogin(viyaTarget, false)];
253
+ case 1:
254
+ _a.sent();
255
+ expect(utilsModule.saveTokens).toHaveBeenCalledWith(viyaTarget.name, 'access-789', '');
256
+ return [2 /*return*/];
257
+ }
258
+ });
259
+ }); });
260
+ // ---------------------------------------------------------------------------
261
+ // Guard 5: fetchLoggedInUser failure — tokens not saved, error propagated
262
+ // ---------------------------------------------------------------------------
263
+ it('should not save tokens and should propagate the error when fetchLoggedInUser fails', function () { return __awaiter(void 0, void 0, void 0, function () {
264
+ return __generator(this, function (_a) {
265
+ switch (_a.label) {
266
+ case 0:
267
+ ;
268
+ utilsModule.fetchLoggedInUser.mockRejectedValue(new Error('Unable to verify the access token'));
269
+ return [4 /*yield*/, expect(authLoginModule.authLogin(viyaTarget, false)).rejects.toThrow('Unable to verify the access token')];
270
+ case 1:
271
+ _a.sent();
272
+ expect(utilsModule.getTokensWithPasswordGrant).toHaveBeenCalled();
273
+ expect(utilsModule.saveTokens).not.toHaveBeenCalled();
274
+ return [2 /*return*/];
275
+ }
276
+ });
277
+ }); });
278
+ // ---------------------------------------------------------------------------
279
+ // Guard 6: getTokensWithPasswordGrant failure — tokens not saved, error propagated
280
+ // ---------------------------------------------------------------------------
281
+ it('should not save tokens and should propagate the error when getTokensWithPasswordGrant fails', function () { return __awaiter(void 0, void 0, void 0, function () {
282
+ return __generator(this, function (_a) {
283
+ switch (_a.label) {
284
+ case 0:
285
+ ;
286
+ utilsModule.getTokensWithPasswordGrant.mockRejectedValue(new Error('Login failed for user'));
287
+ return [4 /*yield*/, expect(authLoginModule.authLogin(viyaTarget, false)).rejects.toThrow('Login failed for user')];
288
+ case 1:
289
+ _a.sent();
290
+ expect(utilsModule.saveTokens).not.toHaveBeenCalled();
291
+ expect(utilsModule.fetchLoggedInUser).not.toHaveBeenCalled();
292
+ return [2 /*return*/];
293
+ }
294
+ });
295
+ }); });
296
+ // ---------------------------------------------------------------------------
297
+ // Callback coverage: username validator rejects empty input
298
+ // ---------------------------------------------------------------------------
299
+ it('should pass a validator to getString that rejects empty usernames', function () { return __awaiter(void 0, void 0, void 0, function () {
300
+ var validator;
301
+ return __generator(this, function (_a) {
302
+ switch (_a.label) {
303
+ case 0: return [4 /*yield*/, authLoginModule.authLogin(viyaTarget, false)];
304
+ case 1:
305
+ _a.sent();
306
+ validator = utils_2.getString.mock.calls[0][1];
307
+ expect(validator('')).toBe('Username is required.');
308
+ expect(validator('someuser')).toBe(true);
309
+ return [2 /*return*/];
310
+ }
311
+ });
312
+ }); });
313
+ // ---------------------------------------------------------------------------
314
+ // Callback coverage: password prompt onCancel throws 'Input cancelled.'
315
+ // ---------------------------------------------------------------------------
316
+ it('should throw Input cancelled when the password prompt is cancelled', function () { return __awaiter(void 0, void 0, void 0, function () {
317
+ return __generator(this, function (_a) {
318
+ switch (_a.label) {
319
+ case 0:
320
+ // Capture the onCancel handler from the prompts() call and invoke it
321
+ // directly, simulating a user pressing Ctrl+C.
322
+ ;
323
+ prompts_1.default.mockImplementation(function (_config, opts) {
324
+ opts.onCancel();
325
+ return Promise.resolve({ pass: '' });
326
+ });
327
+ return [4 /*yield*/, expect(authLoginModule.authLogin(viyaTarget, false)).rejects.toThrow('Input cancelled.')];
328
+ case 1:
329
+ _a.sent();
330
+ expect(utilsModule.saveTokens).not.toHaveBeenCalled();
331
+ return [2 /*return*/];
332
+ }
333
+ });
334
+ }); });
335
+ // ---------------------------------------------------------------------------
336
+ // Branch coverage: id fallback in success log (id || 'unknown user')
337
+ // ---------------------------------------------------------------------------
338
+ it('should log "unknown user" when fetchLoggedInUser returns no id', function () { return __awaiter(void 0, void 0, void 0, function () {
339
+ return __generator(this, function (_a) {
340
+ switch (_a.label) {
341
+ case 0:
342
+ ;
343
+ utilsModule.fetchLoggedInUser.mockResolvedValue({
344
+ id: '',
345
+ name: 'Some Name'
346
+ });
347
+ // The function should still complete — the fallback is in the log message,
348
+ // not a throw. We just verify saveTokens is still called.
349
+ return [4 /*yield*/, authLoginModule.authLogin(viyaTarget, false)];
350
+ case 1:
351
+ // The function should still complete — the fallback is in the log message,
352
+ // not a throw. We just verify saveTokens is still called.
353
+ _a.sent();
354
+ expect(utilsModule.saveTokens).toHaveBeenCalledWith(viyaTarget.name, 'access-123', 'refresh-456');
355
+ return [2 /*return*/];
356
+ }
357
+ });
358
+ }); });
359
+ // ---------------------------------------------------------------------------
360
+ // Branch coverage: name fallback in success log (name ? ` (${name})` : '')
361
+ // ---------------------------------------------------------------------------
362
+ it('should succeed when fetchLoggedInUser returns no name', function () { return __awaiter(void 0, void 0, void 0, function () {
363
+ return __generator(this, function (_a) {
364
+ switch (_a.label) {
365
+ case 0:
366
+ ;
367
+ utilsModule.fetchLoggedInUser.mockResolvedValue({
368
+ id: 'sastest',
369
+ name: undefined
370
+ });
371
+ return [4 /*yield*/, authLoginModule.authLogin(viyaTarget, false)];
372
+ case 1:
373
+ _a.sent();
374
+ expect(utilsModule.saveTokens).toHaveBeenCalledWith(viyaTarget.name, 'access-123', 'refresh-456');
375
+ return [2 /*return*/];
376
+ }
377
+ });
378
+ }); });
379
+ // ===========================================================================
380
+ // Non-interactive credential input paths (env vars, stdin, no-TTY fallback)
381
+ // ===========================================================================
382
+ // ---------------------------------------------------------------------------
383
+ // Env vars: SAS_USERNAME + SAS_PASSWORD skip both prompts
384
+ // ---------------------------------------------------------------------------
385
+ it('should use SAS_USERNAME and SAS_PASSWORD env vars without prompting', function () { return __awaiter(void 0, void 0, void 0, function () {
386
+ return __generator(this, function (_a) {
387
+ switch (_a.label) {
388
+ case 0:
389
+ process.env.SAS_USERNAME = 'env-user';
390
+ process.env.SAS_PASSWORD = 'env-pass';
391
+ return [4 /*yield*/, authLoginModule.authLogin(viyaTarget, false)];
392
+ case 1:
393
+ _a.sent();
394
+ expect(utils_2.getString).not.toHaveBeenCalled();
395
+ expect(prompts_1.default).not.toHaveBeenCalled();
396
+ expect(utilsModule.getTokensWithPasswordGrant).toHaveBeenCalledWith(viyaTarget, 'env-user', 'env-pass');
397
+ expect(utilsModule.saveTokens).toHaveBeenCalledWith(viyaTarget.name, 'access-123', 'refresh-456');
398
+ return [2 /*return*/];
399
+ }
400
+ });
401
+ }); });
402
+ // ---------------------------------------------------------------------------
403
+ // Env var username only — password still prompted interactively
404
+ // ---------------------------------------------------------------------------
405
+ it('should use SAS_USERNAME but prompt for password when only username env var is set', function () { return __awaiter(void 0, void 0, void 0, function () {
406
+ return __generator(this, function (_a) {
407
+ switch (_a.label) {
408
+ case 0:
409
+ process.env.SAS_USERNAME = 'env-user';
410
+ return [4 /*yield*/, authLoginModule.authLogin(viyaTarget, false)];
411
+ case 1:
412
+ _a.sent();
413
+ expect(utils_2.getString).not.toHaveBeenCalled();
414
+ expect(prompts_1.default).toHaveBeenCalled();
415
+ expect(utilsModule.getTokensWithPasswordGrant).toHaveBeenCalledWith(viyaTarget, 'env-user', 'test-pass');
416
+ return [2 /*return*/];
417
+ }
418
+ });
419
+ }); });
420
+ // ---------------------------------------------------------------------------
421
+ // Env var password only — username still prompted interactively
422
+ // ---------------------------------------------------------------------------
423
+ it('should use SAS_PASSWORD but prompt for username when only password env var is set', function () { return __awaiter(void 0, void 0, void 0, function () {
424
+ return __generator(this, function (_a) {
425
+ switch (_a.label) {
426
+ case 0:
427
+ process.env.SAS_PASSWORD = 'env-pass';
428
+ return [4 /*yield*/, authLoginModule.authLogin(viyaTarget, false)];
429
+ case 1:
430
+ _a.sent();
431
+ expect(utils_2.getString).toHaveBeenCalled();
432
+ expect(prompts_1.default).not.toHaveBeenCalled();
433
+ expect(utilsModule.getTokensWithPasswordGrant).toHaveBeenCalledWith(viyaTarget, 'test-user', 'env-pass');
434
+ return [2 /*return*/];
435
+ }
436
+ });
437
+ }); });
438
+ // ---------------------------------------------------------------------------
439
+ // --password-stdin reads password from stdin, username from env
440
+ // ---------------------------------------------------------------------------
441
+ it('should read password from stdin and username from SAS_USERNAME when --password-stdin is set', function () { return __awaiter(void 0, void 0, void 0, function () {
442
+ var stdinData, originalStdin, mockStdin;
443
+ var _a;
444
+ return __generator(this, function (_b) {
445
+ switch (_b.label) {
446
+ case 0:
447
+ process.env.SAS_USERNAME = 'stdin-user';
448
+ stdinData = 'stdin-pa55\n';
449
+ originalStdin = process.stdin;
450
+ mockStdin = (_a = {},
451
+ _a[Symbol.asyncIterator] = function () {
452
+ var done = false;
453
+ return {
454
+ next: function () {
455
+ if (done)
456
+ return Promise.resolve({ done: true, value: undefined });
457
+ done = true;
458
+ return Promise.resolve({
459
+ done: false,
460
+ value: Buffer.from(stdinData)
461
+ });
462
+ }
463
+ };
464
+ },
465
+ _a);
466
+ Object.defineProperty(process, 'stdin', {
467
+ value: mockStdin,
468
+ configurable: true
469
+ });
470
+ _b.label = 1;
471
+ case 1:
472
+ _b.trys.push([1, , 3, 4]);
473
+ return [4 /*yield*/, authLoginModule.authLogin(viyaTarget, false, true)];
474
+ case 2:
475
+ _b.sent();
476
+ expect(utils_2.getString).not.toHaveBeenCalled();
477
+ expect(prompts_1.default).not.toHaveBeenCalled();
478
+ expect(utilsModule.getTokensWithPasswordGrant).toHaveBeenCalledWith(viyaTarget, 'stdin-user', 'stdin-pa55');
479
+ expect(utilsModule.saveTokens).toHaveBeenCalledWith(viyaTarget.name, 'access-123', 'refresh-456');
480
+ return [3 /*break*/, 4];
481
+ case 3:
482
+ Object.defineProperty(process, 'stdin', {
483
+ value: originalStdin,
484
+ configurable: true
485
+ });
486
+ return [7 /*endfinally*/];
487
+ case 4: return [2 /*return*/];
488
+ }
489
+ });
490
+ }); });
491
+ // ---------------------------------------------------------------------------
492
+ // --password-stdin with empty stdin throws a clear error
493
+ // ---------------------------------------------------------------------------
494
+ it('should throw when --password-stdin is set but stdin is empty', function () { return __awaiter(void 0, void 0, void 0, function () {
495
+ var originalStdin, mockStdin;
496
+ var _a;
497
+ return __generator(this, function (_b) {
498
+ switch (_b.label) {
499
+ case 0:
500
+ process.env.SAS_USERNAME = 'stdin-user';
501
+ originalStdin = process.stdin;
502
+ mockStdin = (_a = {},
503
+ _a[Symbol.asyncIterator] = function () {
504
+ return {
505
+ next: function () {
506
+ return Promise.resolve({ done: true, value: undefined });
507
+ }
508
+ };
509
+ },
510
+ _a);
511
+ Object.defineProperty(process, 'stdin', {
512
+ value: mockStdin,
513
+ configurable: true
514
+ });
515
+ _b.label = 1;
516
+ case 1:
517
+ _b.trys.push([1, , 3, 4]);
518
+ return [4 /*yield*/, expect(authLoginModule.authLogin(viyaTarget, false, true)).rejects.toThrow(/no password was read from stdin/)];
519
+ case 2:
520
+ _b.sent();
521
+ expect(utilsModule.getTokensWithPasswordGrant).not.toHaveBeenCalled();
522
+ expect(utilsModule.saveTokens).not.toHaveBeenCalled();
523
+ return [3 /*break*/, 4];
524
+ case 3:
525
+ Object.defineProperty(process, 'stdin', {
526
+ value: originalStdin,
527
+ configurable: true
528
+ });
529
+ return [7 /*endfinally*/];
530
+ case 4: return [2 /*return*/];
531
+ }
532
+ });
533
+ }); });
534
+ // ---------------------------------------------------------------------------
535
+ // --password-stdin without SAS_USERNAME throws (can't prompt interactively)
536
+ // ---------------------------------------------------------------------------
537
+ it('should throw when --password-stdin is set but SAS_USERNAME is not set', function () { return __awaiter(void 0, void 0, void 0, function () {
538
+ var originalStdin, mockStdin;
539
+ var _a;
540
+ return __generator(this, function (_b) {
541
+ switch (_b.label) {
542
+ case 0:
543
+ originalStdin = process.stdin;
544
+ mockStdin = (_a = {},
545
+ _a[Symbol.asyncIterator] = function () {
546
+ return {
547
+ next: function () {
548
+ return Promise.resolve({ done: true, value: undefined });
549
+ }
550
+ };
551
+ },
552
+ _a);
553
+ Object.defineProperty(process, 'stdin', {
554
+ value: mockStdin,
555
+ configurable: true
556
+ });
557
+ _b.label = 1;
558
+ case 1:
559
+ _b.trys.push([1, , 3, 4]);
560
+ return [4 /*yield*/, expect(authLoginModule.authLogin(viyaTarget, false, true)).rejects.toThrow(/SAS_USERNAME/)];
561
+ case 2:
562
+ _b.sent();
563
+ expect(utilsModule.getTokensWithPasswordGrant).not.toHaveBeenCalled();
564
+ return [3 /*break*/, 4];
565
+ case 3:
566
+ Object.defineProperty(process, 'stdin', {
567
+ value: originalStdin,
568
+ configurable: true
569
+ });
570
+ return [7 /*endfinally*/];
571
+ case 4: return [2 /*return*/];
572
+ }
573
+ });
574
+ }); });
575
+ // ---------------------------------------------------------------------------
576
+ // No TTY, no env vars, no --password-stdin → clear error for username
577
+ // ---------------------------------------------------------------------------
578
+ it('should throw a clear error when no TTY and no env vars are available (username)', function () { return __awaiter(void 0, void 0, void 0, function () {
579
+ return __generator(this, function (_a) {
580
+ switch (_a.label) {
581
+ case 0:
582
+ Object.defineProperty(process.stdin, 'isTTY', {
583
+ value: false,
584
+ configurable: true
585
+ });
586
+ return [4 /*yield*/, expect(authLoginModule.authLogin(viyaTarget, false)).rejects.toThrow(/A SAS username is required/)];
587
+ case 1:
588
+ _a.sent();
589
+ expect(utilsModule.getTokensWithPasswordGrant).not.toHaveBeenCalled();
590
+ return [2 /*return*/];
591
+ }
592
+ });
593
+ }); });
594
+ // ---------------------------------------------------------------------------
595
+ // No TTY + SAS_USERNAME set but no password → clear error for password
596
+ // ---------------------------------------------------------------------------
597
+ it('should throw a clear error when SAS_USERNAME is set but no password source is available', function () { return __awaiter(void 0, void 0, void 0, function () {
598
+ return __generator(this, function (_a) {
599
+ switch (_a.label) {
600
+ case 0:
601
+ Object.defineProperty(process.stdin, 'isTTY', {
602
+ value: false,
603
+ configurable: true
604
+ });
605
+ process.env.SAS_USERNAME = 'env-user';
606
+ return [4 /*yield*/, expect(authLoginModule.authLogin(viyaTarget, false)).rejects.toThrow(/A SAS password is required/)];
607
+ case 1:
608
+ _a.sent();
609
+ expect(utilsModule.getTokensWithPasswordGrant).not.toHaveBeenCalled();
610
+ return [2 /*return*/];
611
+ }
612
+ });
613
+ }); });
614
+ // ---------------------------------------------------------------------------
615
+ // Precedence: --password-stdin takes precedence over SAS_PASSWORD
616
+ // ---------------------------------------------------------------------------
617
+ it('should prefer --password-stdin over SAS_PASSWORD env var', function () { return __awaiter(void 0, void 0, void 0, function () {
618
+ var stdinData, originalStdin, mockStdin;
619
+ var _a;
620
+ return __generator(this, function (_b) {
621
+ switch (_b.label) {
622
+ case 0:
623
+ process.env.SAS_USERNAME = 'env-user';
624
+ process.env.SAS_PASSWORD = 'env-pass';
625
+ stdinData = 'stdin-pa55\n';
626
+ originalStdin = process.stdin;
627
+ mockStdin = (_a = {},
628
+ _a[Symbol.asyncIterator] = function () {
629
+ var done = false;
630
+ return {
631
+ next: function () {
632
+ if (done)
633
+ return Promise.resolve({ done: true, value: undefined });
634
+ done = true;
635
+ return Promise.resolve({
636
+ done: false,
637
+ value: Buffer.from(stdinData)
638
+ });
639
+ }
640
+ };
641
+ },
642
+ _a);
643
+ Object.defineProperty(process, 'stdin', {
644
+ value: mockStdin,
645
+ configurable: true
646
+ });
647
+ _b.label = 1;
648
+ case 1:
649
+ _b.trys.push([1, , 3, 4]);
650
+ return [4 /*yield*/, authLoginModule.authLogin(viyaTarget, false, true)];
651
+ case 2:
652
+ _b.sent();
653
+ expect(utilsModule.getTokensWithPasswordGrant).toHaveBeenCalledWith(viyaTarget, 'env-user', 'stdin-pa55');
654
+ return [3 /*break*/, 4];
655
+ case 3:
656
+ Object.defineProperty(process, 'stdin', {
657
+ value: originalStdin,
658
+ configurable: true
659
+ });
660
+ return [7 /*endfinally*/];
661
+ case 4: return [2 /*return*/];
662
+ }
663
+ });
664
+ }); });
665
+ });
@@ -73,7 +73,8 @@ function executeDeployScriptSasViya(deployScriptName, target, linesToExecute, lo
73
73
  fileName: deployScriptName,
74
74
  linesOfCode: linesToExecute,
75
75
  contextName: contextName,
76
- authConfig: authConfig
76
+ authConfig: authConfig,
77
+ onTokensRefreshed: (0, config_1.persistTokensRefreshedByAdapter)(target)
77
78
  })
78
79
  .catch(function (err) {
79
80
  process.logger.error('executeScriptSASViya Error', err);