@sasjs/cli 6.24.2 → 6.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/commands/build/build.js +6 -0
- package/build/commands/build/internal/appManifest.d.ts +42 -0
- package/build/commands/build/internal/appManifest.js +85 -0
- package/build/commands/build/internal/mockSite/assets/persist.js +165 -26
- package/build/commands/build/internal/mockSite/assets/sw.js +419 -63
- package/build/commands/build/internal/mockSite/mockSite.d.ts +5 -0
- package/build/commands/build/internal/mockSite/mockSite.js +49 -6
- package/build/commands/build/internal/mockSite/spec/mockSite.spec.js +150 -0
- package/build/commands/build/internal/mockSite/spec/persist.spec.js +335 -15
- package/build/commands/build/internal/mockSite/spec/sw.spec.js +522 -18
- package/build/commands/build/internal/spec/appManifest.spec.d.ts +1 -0
- package/build/commands/build/internal/spec/appManifest.spec.js +71 -0
- package/build/commands/build/spec/build.spec.js +52 -0
- package/build/commands/deploy/internal/appStreamUrl.d.ts +25 -0
- package/build/commands/deploy/internal/appStreamUrl.js +27 -0
- package/build/commands/deploy/internal/deployToSASJSWithServicePack.js +17 -16
- package/build/commands/deploy/internal/executeDeployScriptSasjs.js +14 -13
- package/build/commands/deploy/internal/spec/appStreamUrl.spec.d.ts +1 -0
- package/build/commands/deploy/internal/spec/appStreamUrl.spec.js +42 -0
- package/build/commands/deploy/internal/spec/executeDeployScriptSasjs.spec.js +1 -1
- package/package.json +5 -5
|
@@ -1,4 +1,15 @@
|
|
|
1
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
|
+
};
|
|
2
13
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
14
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
15
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -62,6 +73,15 @@ var __read = (this && this.__read) || function (o, n) {
|
|
|
62
73
|
}
|
|
63
74
|
return ar;
|
|
64
75
|
};
|
|
76
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
77
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
78
|
+
if (ar || !(i in from)) {
|
|
79
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
80
|
+
ar[i] = from[i];
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
84
|
+
};
|
|
65
85
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
66
86
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
67
87
|
};
|
|
@@ -163,7 +183,7 @@ var loadWorker = function (base) {
|
|
|
163
183
|
};
|
|
164
184
|
require('../assets/sw.js');
|
|
165
185
|
global.fetch = jest.fn(function (input) { return __awaiter(void 0, void 0, void 0, function () {
|
|
166
|
-
var target, fixture;
|
|
186
|
+
var target, fixture, fixture;
|
|
167
187
|
var _a;
|
|
168
188
|
return __generator(this, function (_b) {
|
|
169
189
|
target = typeof input === 'string' ? input : (input && input.url) || String(input);
|
|
@@ -175,6 +195,12 @@ var loadWorker = function (base) {
|
|
|
175
195
|
})];
|
|
176
196
|
if (target.includes('index.html'))
|
|
177
197
|
return [2 /*return*/, new Response('<!doctype html><html><head><title>App</title></head><body>home</body></html>', { status: 200, headers: { 'content-type': 'text/html' } })];
|
|
198
|
+
if (target.includes('identity.json')) {
|
|
199
|
+
fixture = global.__sasjsIdentity;
|
|
200
|
+
if (fixture === 'missing')
|
|
201
|
+
return [2 /*return*/, new Response('', { status: 404 })];
|
|
202
|
+
return [2 /*return*/, jsonResponse(fixture || api_1.DEFAULT_IDENTITY)];
|
|
203
|
+
}
|
|
178
204
|
if (target.includes('sas-transcript')) {
|
|
179
205
|
fixture = global.__sasTranscript;
|
|
180
206
|
return [2 /*return*/, fixture ? jsonResponse(fixture) : new Response('', { status: 404 })];
|
|
@@ -262,6 +288,8 @@ describe('service worker host', function () {
|
|
|
262
288
|
delete global.self;
|
|
263
289
|
delete global.importScripts;
|
|
264
290
|
delete global.fetch;
|
|
291
|
+
delete global.__sasjsIdentity;
|
|
292
|
+
delete global.__mockHostClients;
|
|
265
293
|
});
|
|
266
294
|
var seeded = function () {
|
|
267
295
|
var _a;
|
|
@@ -928,44 +956,40 @@ describe('service worker host', function () {
|
|
|
928
956
|
});
|
|
929
957
|
}); });
|
|
930
958
|
it('imports the fixture directory, and unpublishes the site itself', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
931
|
-
var dispatch, synced, _a, unpublished,
|
|
932
|
-
return __generator(this, function (
|
|
933
|
-
switch (
|
|
959
|
+
var dispatch, synced, _a, siteAppId, unpublished, removed, other;
|
|
960
|
+
return __generator(this, function (_b) {
|
|
961
|
+
switch (_b.label) {
|
|
934
962
|
case 0:
|
|
935
963
|
dispatch = seeded().dispatch;
|
|
936
964
|
return [4 /*yield*/, dispatch('/SASjsApi/authConfig/synchroniseWithLDAP', { method: 'POST' })];
|
|
937
965
|
case 1:
|
|
938
|
-
synced =
|
|
966
|
+
synced = _b.sent();
|
|
939
967
|
expect(synced.status).toBe(200);
|
|
940
968
|
_a = expect;
|
|
941
969
|
return [4 /*yield*/, synced.json()];
|
|
942
970
|
case 2:
|
|
943
|
-
_a.apply(void 0, [
|
|
971
|
+
_a.apply(void 0, [_b.sent()]).toEqual({ userCount: 0, groupCount: 0 });
|
|
972
|
+
siteAppId = '6f1c1c1e-6a1e-4c4a-9d1a-1b2c3d4e5f60';
|
|
944
973
|
return [4 /*yield*/, dispatch('/SASjsApi/drive/deploy', {
|
|
945
974
|
method: 'DELETE',
|
|
946
975
|
headers: { 'content-type': 'application/json' },
|
|
947
|
-
body: JSON.stringify({
|
|
948
|
-
streamServiceName: 'Test',
|
|
949
|
-
appLoc: appLoc
|
|
950
|
-
})
|
|
976
|
+
body: JSON.stringify({ id: siteAppId })
|
|
951
977
|
})];
|
|
952
978
|
case 3:
|
|
953
|
-
unpublished =
|
|
979
|
+
unpublished = _b.sent();
|
|
954
980
|
expect(unpublished.status).toBe(200);
|
|
955
|
-
_b = expect;
|
|
956
981
|
return [4 /*yield*/, unpublished.json()];
|
|
957
982
|
case 4:
|
|
958
|
-
|
|
983
|
+
removed = _b.sent();
|
|
984
|
+
expect(removed.status).toBe('success');
|
|
985
|
+
expect(removed.app.id).toBe(siteAppId);
|
|
959
986
|
return [4 /*yield*/, dispatch('/SASjsApi/drive/deploy', {
|
|
960
987
|
method: 'DELETE',
|
|
961
988
|
headers: { 'content-type': 'application/json' },
|
|
962
|
-
body: JSON.stringify({
|
|
963
|
-
streamServiceName: 'Other',
|
|
964
|
-
appLoc: '/Public/app/other'
|
|
965
|
-
})
|
|
989
|
+
body: JSON.stringify({ id: '00000000-0000-4000-8000-000000000000' })
|
|
966
990
|
})];
|
|
967
991
|
case 5:
|
|
968
|
-
other =
|
|
992
|
+
other = _b.sent();
|
|
969
993
|
expect(other.status).toBe(404);
|
|
970
994
|
return [2 /*return*/];
|
|
971
995
|
}
|
|
@@ -2663,4 +2687,484 @@ describe('service worker host', function () {
|
|
|
2663
2687
|
finally { if (e_4) throw e_4.error; }
|
|
2664
2688
|
}
|
|
2665
2689
|
});
|
|
2690
|
+
/**
|
|
2691
|
+
* The identity and record corpora, over the worker's HTTP surface.
|
|
2692
|
+
*
|
|
2693
|
+
* The same cases @sasjs/api runs against its own services, and the server runs
|
|
2694
|
+
* against a live sasjs/server. A failure here is this host's transport, the
|
|
2695
|
+
* fixture it booted, or a route it does not answer - never the decisions, which
|
|
2696
|
+
* are the package's.
|
|
2697
|
+
*/
|
|
2698
|
+
describe('identity and record conformance', function () {
|
|
2699
|
+
var e_8, _a;
|
|
2700
|
+
/**
|
|
2701
|
+
* Boots the worker from a fixture, the way the page does: the case's own
|
|
2702
|
+
* identity, and `subject: null` for no implicit caller, so a case that
|
|
2703
|
+
* expects the answer an anonymous request gets is not silently answered as
|
|
2704
|
+
* the fixture's administrator.
|
|
2705
|
+
*/
|
|
2706
|
+
var boot = function (worker_1, identity_1) {
|
|
2707
|
+
var args_1 = [];
|
|
2708
|
+
for (var _i = 2; _i < arguments.length; _i++) {
|
|
2709
|
+
args_1[_i - 2] = arguments[_i];
|
|
2710
|
+
}
|
|
2711
|
+
return __awaiter(void 0, __spreadArray([worker_1, identity_1], __read(args_1), false), void 0, function (worker, identity, subject) {
|
|
2712
|
+
var pending;
|
|
2713
|
+
if (subject === void 0) { subject = null; }
|
|
2714
|
+
return __generator(this, function (_a) {
|
|
2715
|
+
switch (_a.label) {
|
|
2716
|
+
case 0:
|
|
2717
|
+
pending = worker.message({
|
|
2718
|
+
type: 'sasjs-seed',
|
|
2719
|
+
identity: identity,
|
|
2720
|
+
subject: subject
|
|
2721
|
+
}).pending;
|
|
2722
|
+
return [4 /*yield*/, pending];
|
|
2723
|
+
case 1:
|
|
2724
|
+
_a.sent();
|
|
2725
|
+
return [2 /*return*/];
|
|
2726
|
+
}
|
|
2727
|
+
});
|
|
2728
|
+
});
|
|
2729
|
+
};
|
|
2730
|
+
/** The request a case describes, as HTTP: path parameters filled, token sent. */
|
|
2731
|
+
var httpForCase = function (testCase) {
|
|
2732
|
+
var e_9, _a;
|
|
2733
|
+
var _b = __read(testCase.route.split(' '), 2), method = _b[0], routePath = _b[1];
|
|
2734
|
+
var filled = routePath.replace(/\{(\w+)\}/g, function (_match, name) {
|
|
2735
|
+
return encodeURIComponent(String((testCase.request.params || {})[name]));
|
|
2736
|
+
});
|
|
2737
|
+
var query = new URLSearchParams();
|
|
2738
|
+
try {
|
|
2739
|
+
for (var _c = __values(Object.entries(testCase.request.query || {})), _d = _c.next(); !_d.done; _d = _c.next()) {
|
|
2740
|
+
var _e = __read(_d.value, 2), key = _e[0], value = _e[1];
|
|
2741
|
+
if (value !== undefined)
|
|
2742
|
+
query.set(key, String(value));
|
|
2743
|
+
}
|
|
2744
|
+
}
|
|
2745
|
+
catch (e_9_1) { e_9 = { error: e_9_1 }; }
|
|
2746
|
+
finally {
|
|
2747
|
+
try {
|
|
2748
|
+
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
|
|
2749
|
+
}
|
|
2750
|
+
finally { if (e_9) throw e_9.error; }
|
|
2751
|
+
}
|
|
2752
|
+
var suffix = query.toString() ? "?".concat(query) : '';
|
|
2753
|
+
var headers = {};
|
|
2754
|
+
var init = { method: method, headers: headers };
|
|
2755
|
+
if (testCase.request.token)
|
|
2756
|
+
headers.authorization = 'Bearer ' + testCase.request.token;
|
|
2757
|
+
if (testCase.request.body !== undefined) {
|
|
2758
|
+
init.body = JSON.stringify(testCase.request.body);
|
|
2759
|
+
headers['content-type'] = 'application/json';
|
|
2760
|
+
}
|
|
2761
|
+
return { path: filled + suffix, init: init };
|
|
2762
|
+
};
|
|
2763
|
+
/** The assertions the package's own corpus makes, against an HTTP answer. */
|
|
2764
|
+
var expectAnswer = function (testCase, response) { return __awaiter(void 0, void 0, void 0, function () {
|
|
2765
|
+
var want, contentType, isJson, sent, _a, _b, expected, error, _c, body, _d, required, _e, optional, present_1, allowed_1;
|
|
2766
|
+
return __generator(this, function (_f) {
|
|
2767
|
+
switch (_f.label) {
|
|
2768
|
+
case 0:
|
|
2769
|
+
expect({ case: testCase.id, status: response.status }).toEqual({
|
|
2770
|
+
case: testCase.id,
|
|
2771
|
+
status: testCase.expect.status
|
|
2772
|
+
});
|
|
2773
|
+
want = testCase.expect;
|
|
2774
|
+
contentType = response.headers.get('content-type') || '';
|
|
2775
|
+
isJson = contentType.includes('application/json');
|
|
2776
|
+
if (!(want.contents !== undefined)) return [3 /*break*/, 5];
|
|
2777
|
+
if (!(want.encoding === 'text')) return [3 /*break*/, 2];
|
|
2778
|
+
return [4 /*yield*/, response.text()];
|
|
2779
|
+
case 1:
|
|
2780
|
+
_a = _f.sent();
|
|
2781
|
+
return [3 /*break*/, 4];
|
|
2782
|
+
case 2:
|
|
2783
|
+
_b = Uint8Array.bind;
|
|
2784
|
+
return [4 /*yield*/, response.arrayBuffer()];
|
|
2785
|
+
case 3:
|
|
2786
|
+
_a = new (_b.apply(Uint8Array, [void 0, _f.sent()]))();
|
|
2787
|
+
_f.label = 4;
|
|
2788
|
+
case 4:
|
|
2789
|
+
sent = _a;
|
|
2790
|
+
expected = typeof want.contents === 'string'
|
|
2791
|
+
? want.encoding === 'text'
|
|
2792
|
+
? want.contents
|
|
2793
|
+
: new TextEncoder().encode(want.contents)
|
|
2794
|
+
: want.contents;
|
|
2795
|
+
expect(sent).toEqual(expected);
|
|
2796
|
+
return [2 /*return*/];
|
|
2797
|
+
case 5:
|
|
2798
|
+
if (!(want.error !== undefined)) return [3 /*break*/, 10];
|
|
2799
|
+
if (!isJson) return [3 /*break*/, 7];
|
|
2800
|
+
return [4 /*yield*/, response.json()];
|
|
2801
|
+
case 6:
|
|
2802
|
+
_c = (_f.sent()).message;
|
|
2803
|
+
return [3 /*break*/, 9];
|
|
2804
|
+
case 7: return [4 /*yield*/, response.text()];
|
|
2805
|
+
case 8:
|
|
2806
|
+
_c = _f.sent();
|
|
2807
|
+
_f.label = 9;
|
|
2808
|
+
case 9:
|
|
2809
|
+
error = _c;
|
|
2810
|
+
expect({ case: testCase.id, error: error }).toEqual({
|
|
2811
|
+
case: testCase.id,
|
|
2812
|
+
error: want.error
|
|
2813
|
+
});
|
|
2814
|
+
return [2 /*return*/];
|
|
2815
|
+
case 10:
|
|
2816
|
+
if (want.body === undefined &&
|
|
2817
|
+
want.partial === undefined &&
|
|
2818
|
+
want.keys === undefined &&
|
|
2819
|
+
want.shape === undefined)
|
|
2820
|
+
return [2 /*return*/];
|
|
2821
|
+
return [4 /*yield*/, response.json()];
|
|
2822
|
+
case 11:
|
|
2823
|
+
body = _f.sent();
|
|
2824
|
+
if (want.body !== undefined)
|
|
2825
|
+
expect(body).toEqual(want.body);
|
|
2826
|
+
if (want.partial !== undefined)
|
|
2827
|
+
expect(body).toMatchObject(want.partial);
|
|
2828
|
+
if (want.keys !== undefined)
|
|
2829
|
+
expect(Object.keys(body).sort()).toEqual(want.keys.slice().sort());
|
|
2830
|
+
if (want.shape !== undefined) {
|
|
2831
|
+
_d = want.shape, required = _d.required, _e = _d.optional, optional = _e === void 0 ? [] : _e;
|
|
2832
|
+
present_1 = Object.keys(body);
|
|
2833
|
+
allowed_1 = __spreadArray(__spreadArray([], __read(required), false), __read(optional), false);
|
|
2834
|
+
expect({
|
|
2835
|
+
case: testCase.id,
|
|
2836
|
+
missing: required.filter(function (key) { return !present_1.includes(key); }),
|
|
2837
|
+
unexpected: present_1.filter(function (key) { return !allowed_1.includes(key); })
|
|
2838
|
+
}).toEqual({ case: testCase.id, missing: [], unexpected: [] });
|
|
2839
|
+
}
|
|
2840
|
+
return [2 /*return*/];
|
|
2841
|
+
}
|
|
2842
|
+
});
|
|
2843
|
+
}); };
|
|
2844
|
+
var _loop_2 = function (testCase) {
|
|
2845
|
+
it("".concat(testCase.id, ": ").concat(testCase.description), function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
2846
|
+
var worker, _a, requestPath, init, _b, _c;
|
|
2847
|
+
return __generator(this, function (_d) {
|
|
2848
|
+
switch (_d.label) {
|
|
2849
|
+
case 0:
|
|
2850
|
+
worker = loadWorker();
|
|
2851
|
+
return [4 /*yield*/, boot(worker, testCase.identity || api_1.DEFAULT_IDENTITY)];
|
|
2852
|
+
case 1:
|
|
2853
|
+
_d.sent();
|
|
2854
|
+
_a = httpForCase(testCase), requestPath = _a.path, init = _a.init;
|
|
2855
|
+
_b = expectAnswer;
|
|
2856
|
+
_c = [testCase];
|
|
2857
|
+
return [4 /*yield*/, worker.dispatch(requestPath, init)];
|
|
2858
|
+
case 2: return [4 /*yield*/, _b.apply(void 0, _c.concat([_d.sent()]))];
|
|
2859
|
+
case 3:
|
|
2860
|
+
_d.sent();
|
|
2861
|
+
return [2 /*return*/];
|
|
2862
|
+
}
|
|
2863
|
+
});
|
|
2864
|
+
}); });
|
|
2865
|
+
};
|
|
2866
|
+
try {
|
|
2867
|
+
for (var _b = __values(__spreadArray(__spreadArray([], __read(api_1.IDENTITY_CORPUS), false), __read(api_1.RECORD_CORPUS), false)), _c = _b.next(); !_c.done; _c = _b.next()) {
|
|
2868
|
+
var testCase = _c.value;
|
|
2869
|
+
_loop_2(testCase);
|
|
2870
|
+
}
|
|
2871
|
+
}
|
|
2872
|
+
catch (e_8_1) { e_8 = { error: e_8_1 }; }
|
|
2873
|
+
finally {
|
|
2874
|
+
try {
|
|
2875
|
+
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
|
2876
|
+
}
|
|
2877
|
+
finally { if (e_8) throw e_8.error; }
|
|
2878
|
+
}
|
|
2879
|
+
it('answers as the fixture administrator when no subject was chosen', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
2880
|
+
var worker, response, rules;
|
|
2881
|
+
return __generator(this, function (_a) {
|
|
2882
|
+
switch (_a.label) {
|
|
2883
|
+
case 0:
|
|
2884
|
+
worker = loadWorker();
|
|
2885
|
+
return [4 /*yield*/, worker.dispatch('/SASjsApi/permission')];
|
|
2886
|
+
case 1:
|
|
2887
|
+
response = _a.sent();
|
|
2888
|
+
return [4 /*yield*/, response.json()];
|
|
2889
|
+
case 2:
|
|
2890
|
+
rules = _a.sent();
|
|
2891
|
+
expect(response.status).toBe(200);
|
|
2892
|
+
expect(rules.length).toBe(3);
|
|
2893
|
+
expect(rules.map(function (rule) { return rule.uid; })).toEqual([
|
|
2894
|
+
'rule-routes',
|
|
2895
|
+
'rule-execute',
|
|
2896
|
+
'rule-code'
|
|
2897
|
+
]);
|
|
2898
|
+
return [2 /*return*/];
|
|
2899
|
+
}
|
|
2900
|
+
});
|
|
2901
|
+
}); });
|
|
2902
|
+
it('signs in as another account, changing the permission answer and the token subject', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
2903
|
+
var worker, _a, replies, pending, ack, routes, _b, session, _c;
|
|
2904
|
+
return __generator(this, function (_d) {
|
|
2905
|
+
switch (_d.label) {
|
|
2906
|
+
case 0:
|
|
2907
|
+
worker = loadWorker();
|
|
2908
|
+
_a = worker.message({
|
|
2909
|
+
type: 'sasjs-as',
|
|
2910
|
+
username: 'sasjsuser'
|
|
2911
|
+
}), replies = _a.replies, pending = _a.pending;
|
|
2912
|
+
return [4 /*yield*/, pending];
|
|
2913
|
+
case 1:
|
|
2914
|
+
_d.sent();
|
|
2915
|
+
ack = replies[0];
|
|
2916
|
+
expect(ack.username).toBe('sasjsuser');
|
|
2917
|
+
expect(ack.accessToken).toBeTruthy();
|
|
2918
|
+
return [4 /*yield*/, worker.dispatch('/SASjsApi/info/myAuthorizedRoutes')];
|
|
2919
|
+
case 2:
|
|
2920
|
+
routes = _d.sent();
|
|
2921
|
+
_b = expect;
|
|
2922
|
+
return [4 /*yield*/, routes.json()];
|
|
2923
|
+
case 3:
|
|
2924
|
+
_b.apply(void 0, [(_d.sent()).paths]).toEqual([
|
|
2925
|
+
'/AppStream',
|
|
2926
|
+
'/SASjsApi/stp/execute',
|
|
2927
|
+
'/SASjsApi/drive/file'
|
|
2928
|
+
]);
|
|
2929
|
+
return [4 /*yield*/, worker.dispatch('/SASjsApi/session', {
|
|
2930
|
+
headers: { authorization: 'Bearer ' + ack.accessToken }
|
|
2931
|
+
})];
|
|
2932
|
+
case 4:
|
|
2933
|
+
session = _d.sent();
|
|
2934
|
+
expect(session.status).toBe(200);
|
|
2935
|
+
_c = expect;
|
|
2936
|
+
return [4 /*yield*/, session.json()];
|
|
2937
|
+
case 5:
|
|
2938
|
+
_c.apply(void 0, [(_d.sent()).username]).toBe('sasjsuser');
|
|
2939
|
+
return [2 /*return*/];
|
|
2940
|
+
}
|
|
2941
|
+
});
|
|
2942
|
+
}); });
|
|
2943
|
+
it('returns to the administrator with no account named, or with "admin"', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
2944
|
+
var worker, back, routes, _a;
|
|
2945
|
+
return __generator(this, function (_b) {
|
|
2946
|
+
switch (_b.label) {
|
|
2947
|
+
case 0:
|
|
2948
|
+
worker = loadWorker();
|
|
2949
|
+
return [4 /*yield*/, worker.message({ type: 'sasjs-as', username: 'sasjsuser' }).pending];
|
|
2950
|
+
case 1:
|
|
2951
|
+
_b.sent();
|
|
2952
|
+
back = worker.message({ type: 'sasjs-as', username: 'admin' });
|
|
2953
|
+
return [4 /*yield*/, back.pending];
|
|
2954
|
+
case 2:
|
|
2955
|
+
_b.sent();
|
|
2956
|
+
expect(back.replies[0].username).toBe('sasjsadmin');
|
|
2957
|
+
return [4 /*yield*/, worker.dispatch('/SASjsApi/info/myAuthorizedRoutes')
|
|
2958
|
+
// a route only the administrator's rules reach: the restricted account's
|
|
2959
|
+
// list is the three entries the corpus pins
|
|
2960
|
+
];
|
|
2961
|
+
case 3:
|
|
2962
|
+
routes = _b.sent();
|
|
2963
|
+
// a route only the administrator's rules reach: the restricted account's
|
|
2964
|
+
// list is the three entries the corpus pins
|
|
2965
|
+
_a = expect;
|
|
2966
|
+
return [4 /*yield*/, routes.json()];
|
|
2967
|
+
case 4:
|
|
2968
|
+
// a route only the administrator's rules reach: the restricted account's
|
|
2969
|
+
// list is the three entries the corpus pins
|
|
2970
|
+
_a.apply(void 0, [(_b.sent()).paths]).toContain('/SASjsApi/code/execute');
|
|
2971
|
+
return [2 /*return*/];
|
|
2972
|
+
}
|
|
2973
|
+
});
|
|
2974
|
+
}); });
|
|
2975
|
+
it('refuses an unknown account by name, naming the accounts the fixture holds', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
2976
|
+
var worker, _a, replies, pending;
|
|
2977
|
+
return __generator(this, function (_b) {
|
|
2978
|
+
switch (_b.label) {
|
|
2979
|
+
case 0:
|
|
2980
|
+
worker = loadWorker();
|
|
2981
|
+
_a = worker.message({
|
|
2982
|
+
type: 'sasjs-as',
|
|
2983
|
+
username: 'nobody'
|
|
2984
|
+
}), replies = _a.replies, pending = _a.pending;
|
|
2985
|
+
return [4 /*yield*/, pending];
|
|
2986
|
+
case 1:
|
|
2987
|
+
_b.sent();
|
|
2988
|
+
expect(replies[0].error).toContain('no account named "nobody"');
|
|
2989
|
+
expect(replies[0].error).toContain('sasjsadmin');
|
|
2990
|
+
expect(replies[0].accessToken).toBeUndefined();
|
|
2991
|
+
return [2 /*return*/];
|
|
2992
|
+
}
|
|
2993
|
+
});
|
|
2994
|
+
}); });
|
|
2995
|
+
it('refuses a persisted fixture in a format this build does not read, by name', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
2996
|
+
var worker, _a, replies, pending;
|
|
2997
|
+
return __generator(this, function (_b) {
|
|
2998
|
+
switch (_b.label) {
|
|
2999
|
+
case 0:
|
|
3000
|
+
worker = loadWorker();
|
|
3001
|
+
_a = worker.message({
|
|
3002
|
+
type: 'sasjs-seed',
|
|
3003
|
+
identity: __assign(__assign({}, api_1.DEFAULT_IDENTITY), { format: 0 }),
|
|
3004
|
+
subject: null
|
|
3005
|
+
}), replies = _a.replies, pending = _a.pending;
|
|
3006
|
+
return [4 /*yield*/, pending];
|
|
3007
|
+
case 1:
|
|
3008
|
+
_b.sent();
|
|
3009
|
+
expect(replies[0].error).toContain('states format 0');
|
|
3010
|
+
expect(replies[0].error).toContain('Rebuild the site');
|
|
3011
|
+
// the page drops the stored row ONLY for this kind: a host-side failure
|
|
3012
|
+
// must not take the visitor's accounts with it
|
|
3013
|
+
expect(replies[0].errorKind).toBe('identity');
|
|
3014
|
+
return [2 /*return*/];
|
|
3015
|
+
}
|
|
3016
|
+
});
|
|
3017
|
+
}); });
|
|
3018
|
+
it('refuses a shipped fixture it cannot read, naming the file', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
3019
|
+
var worker, response, body;
|
|
3020
|
+
return __generator(this, function (_a) {
|
|
3021
|
+
switch (_a.label) {
|
|
3022
|
+
case 0:
|
|
3023
|
+
;
|
|
3024
|
+
global.__sasjsIdentity = __assign(__assign({}, api_1.DEFAULT_IDENTITY), { format: 7 });
|
|
3025
|
+
worker = loadWorker();
|
|
3026
|
+
return [4 /*yield*/, worker.dispatch('/SASjsApi/permission')];
|
|
3027
|
+
case 1:
|
|
3028
|
+
response = _a.sent();
|
|
3029
|
+
return [4 /*yield*/, response.json()];
|
|
3030
|
+
case 2:
|
|
3031
|
+
body = _a.sent();
|
|
3032
|
+
expect(response.status).toBe(500);
|
|
3033
|
+
expect(body.message).toContain('host/identity.json cannot be used');
|
|
3034
|
+
expect(body.message).toContain('states format 7');
|
|
3035
|
+
return [2 /*return*/];
|
|
3036
|
+
}
|
|
3037
|
+
});
|
|
3038
|
+
}); });
|
|
3039
|
+
it('replies with the error when a reset cannot read the shipped fixture', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
3040
|
+
var worker, _a, replies, pending;
|
|
3041
|
+
return __generator(this, function (_b) {
|
|
3042
|
+
switch (_b.label) {
|
|
3043
|
+
case 0:
|
|
3044
|
+
// the page waits for this ack: silence is read as success, and a reset is
|
|
3045
|
+
// the way out of a site whose fixture broke
|
|
3046
|
+
;
|
|
3047
|
+
global.__sasjsIdentity = __assign(__assign({}, api_1.DEFAULT_IDENTITY), { format: 4 });
|
|
3048
|
+
worker = loadWorker();
|
|
3049
|
+
// a request first, so the failure is the cached one the site already has
|
|
3050
|
+
return [4 /*yield*/, worker.dispatch('/SASjsApi/permission')];
|
|
3051
|
+
case 1:
|
|
3052
|
+
// a request first, so the failure is the cached one the site already has
|
|
3053
|
+
_b.sent();
|
|
3054
|
+
_a = worker.message({ type: 'sasjs-reset' }), replies = _a.replies, pending = _a.pending;
|
|
3055
|
+
return [4 /*yield*/, pending];
|
|
3056
|
+
case 2:
|
|
3057
|
+
_b.sent();
|
|
3058
|
+
expect(replies[0].type).toBe('sasjs-reset-ack');
|
|
3059
|
+
expect(replies[0].error).toContain('host/identity.json cannot be used');
|
|
3060
|
+
expect(replies[0].boot).toBeUndefined();
|
|
3061
|
+
return [2 /*return*/];
|
|
3062
|
+
}
|
|
3063
|
+
});
|
|
3064
|
+
}); });
|
|
3065
|
+
it('falls back to the packaged fixture when the site ships no identity file', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
3066
|
+
var worker, response, rules;
|
|
3067
|
+
return __generator(this, function (_a) {
|
|
3068
|
+
switch (_a.label) {
|
|
3069
|
+
case 0:
|
|
3070
|
+
;
|
|
3071
|
+
global.__sasjsIdentity = 'missing';
|
|
3072
|
+
worker = loadWorker();
|
|
3073
|
+
return [4 /*yield*/, worker.dispatch('/SASjsApi/permission')];
|
|
3074
|
+
case 1:
|
|
3075
|
+
response = _a.sent();
|
|
3076
|
+
return [4 /*yield*/, response.json()];
|
|
3077
|
+
case 2:
|
|
3078
|
+
rules = _a.sent();
|
|
3079
|
+
expect(response.status).toBe(200);
|
|
3080
|
+
expect(rules.length).toBe(3);
|
|
3081
|
+
return [2 /*return*/];
|
|
3082
|
+
}
|
|
3083
|
+
});
|
|
3084
|
+
}); });
|
|
3085
|
+
it('reports an account created through the routes, so the page can store it', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
3086
|
+
var worker, sent, response, persisted;
|
|
3087
|
+
return __generator(this, function (_a) {
|
|
3088
|
+
switch (_a.label) {
|
|
3089
|
+
case 0:
|
|
3090
|
+
worker = loadWorker();
|
|
3091
|
+
sent = [];
|
|
3092
|
+
global.__mockHostClients = [
|
|
3093
|
+
{ postMessage: function (message) { return sent.push(message); } }
|
|
3094
|
+
];
|
|
3095
|
+
return [4 /*yield*/, worker.dispatch('/SASjsApi/user', {
|
|
3096
|
+
method: 'POST',
|
|
3097
|
+
headers: { 'content-type': 'application/json' },
|
|
3098
|
+
body: JSON.stringify({
|
|
3099
|
+
username: 'newuser',
|
|
3100
|
+
displayName: 'New User',
|
|
3101
|
+
password: 'newpass',
|
|
3102
|
+
isAdmin: false,
|
|
3103
|
+
isActive: true
|
|
3104
|
+
})
|
|
3105
|
+
})];
|
|
3106
|
+
case 1:
|
|
3107
|
+
response = _a.sent();
|
|
3108
|
+
expect(response.status).toBe(200);
|
|
3109
|
+
persisted = sent.find(function (message) { return message.type === 'sasjs-persist' && message.identity; });
|
|
3110
|
+
expect(persisted.identity.users.map(function (user) { return user.username; })).toContain('newuser');
|
|
3111
|
+
delete global.__mockHostClients;
|
|
3112
|
+
return [2 /*return*/];
|
|
3113
|
+
}
|
|
3114
|
+
});
|
|
3115
|
+
}); });
|
|
3116
|
+
it('restores the shipped fixture on reset', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
3117
|
+
var worker, users, _a, routes, _b;
|
|
3118
|
+
return __generator(this, function (_c) {
|
|
3119
|
+
switch (_c.label) {
|
|
3120
|
+
case 0:
|
|
3121
|
+
worker = loadWorker();
|
|
3122
|
+
return [4 /*yield*/, worker.dispatch('/SASjsApi/user', {
|
|
3123
|
+
method: 'POST',
|
|
3124
|
+
headers: { 'content-type': 'application/json' },
|
|
3125
|
+
body: JSON.stringify({
|
|
3126
|
+
username: 'newuser',
|
|
3127
|
+
displayName: 'New User',
|
|
3128
|
+
password: 'newpass',
|
|
3129
|
+
isAdmin: false,
|
|
3130
|
+
isActive: true
|
|
3131
|
+
})
|
|
3132
|
+
})];
|
|
3133
|
+
case 1:
|
|
3134
|
+
_c.sent();
|
|
3135
|
+
return [4 /*yield*/, worker.message({ type: 'sasjs-as', username: 'sasjsuser' }).pending];
|
|
3136
|
+
case 2:
|
|
3137
|
+
_c.sent();
|
|
3138
|
+
return [4 /*yield*/, worker.message({ type: 'sasjs-reset' }).pending];
|
|
3139
|
+
case 3:
|
|
3140
|
+
_c.sent();
|
|
3141
|
+
return [4 /*yield*/, worker.dispatch('/SASjsApi/user')];
|
|
3142
|
+
case 4:
|
|
3143
|
+
users = _c.sent();
|
|
3144
|
+
_a = expect;
|
|
3145
|
+
return [4 /*yield*/, users.json()];
|
|
3146
|
+
case 5:
|
|
3147
|
+
_a.apply(void 0, [(_c.sent()).map(function (user) { return user.username; })]).toEqual([
|
|
3148
|
+
'sasjsadmin',
|
|
3149
|
+
'sasjsuser'
|
|
3150
|
+
]);
|
|
3151
|
+
return [4 /*yield*/, worker.dispatch('/SASjsApi/info/myAuthorizedRoutes')
|
|
3152
|
+
// a route only the administrator's rules reach: the restricted account's
|
|
3153
|
+
// list is the three entries the corpus pins
|
|
3154
|
+
];
|
|
3155
|
+
case 6:
|
|
3156
|
+
routes = _c.sent();
|
|
3157
|
+
// a route only the administrator's rules reach: the restricted account's
|
|
3158
|
+
// list is the three entries the corpus pins
|
|
3159
|
+
_b = expect;
|
|
3160
|
+
return [4 /*yield*/, routes.json()];
|
|
3161
|
+
case 7:
|
|
3162
|
+
// a route only the administrator's rules reach: the restricted account's
|
|
3163
|
+
// list is the three entries the corpus pins
|
|
3164
|
+
_b.apply(void 0, [(_c.sent()).paths]).toContain('/SASjsApi/code/execute');
|
|
3165
|
+
return [2 /*return*/];
|
|
3166
|
+
}
|
|
3167
|
+
});
|
|
3168
|
+
}); });
|
|
3169
|
+
});
|
|
2666
3170
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|