@sasjs/cli 6.11.0 → 6.12.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/build/commands/build/internal/mockSite/assets/runtime-core.js +163 -241
- package/build/commands/build/internal/mockSite/assets/sw.js +62 -107
- package/build/commands/build/internal/mockSite/mockSite.js +35 -3
- package/build/commands/build/internal/mockSite/spec/mockSite.spec.js +125 -2
- package/build/commands/build/internal/mockSite/spec/runtime-core.spec.js +380 -159
- package/build/commands/build/internal/mockSite/spec/sw.spec.js +240 -20
- package/package.json +3 -2
|
@@ -35,7 +35,12 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
35
35
|
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
36
36
|
}
|
|
37
37
|
};
|
|
38
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
39
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
40
|
+
};
|
|
38
41
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
var fs_1 = __importDefault(require("fs"));
|
|
43
|
+
var path_1 = __importDefault(require("path"));
|
|
39
44
|
var utils_1 = require("@sasjs/utils");
|
|
40
45
|
var mockSite_1 = require("../mockSite");
|
|
41
46
|
/**
|
|
@@ -249,6 +254,83 @@ describe('browser runtime', function () {
|
|
|
249
254
|
expect(result.log).toContain('boom');
|
|
250
255
|
expect(result.webout).toBe('');
|
|
251
256
|
});
|
|
257
|
+
/**
|
|
258
|
+
* The shims a service reaches for are node's, refusals included: a mock that
|
|
259
|
+
* answered a copy from a missing file with an empty file, or accepted
|
|
260
|
+
* `createHash('sha256')`, would let a service pass here and fail on the server.
|
|
261
|
+
*/
|
|
262
|
+
it("refuses what the fs, path and crypto shims refuse, with node's words", function () {
|
|
263
|
+
var runtime = loadRuntime();
|
|
264
|
+
var failure = function (code) { return runtime.run({ code: code }).error; };
|
|
265
|
+
expect(failure("fs.copyFileSync('/missing.csv', '/x.csv')")).toMatch(/ENOENT: no such file, copy/);
|
|
266
|
+
expect(failure("fs.statSync('/missing.csv')")).toMatch(/ENOENT: no such file or directory, stat/);
|
|
267
|
+
expect(failure("fs.readFileSync('/missing.csv')")).toMatch(/ENOENT: no such file or directory, open/);
|
|
268
|
+
expect(failure("fs.writeFileSync('/x.csv', 'x', { flag: 'r' })")).toMatch(/Unsupported flag 'r' in the browser host/);
|
|
269
|
+
expect(failure("require('crypto').createHash('sha256')")).toMatch(/Unsupported hash in browser host: sha256/);
|
|
270
|
+
expect(failure("require('net')")).toMatch(/Cannot find module 'net' in the browser runtime host/);
|
|
271
|
+
});
|
|
272
|
+
it('answers the path and Buffer helpers a service uses', function () {
|
|
273
|
+
var runtime = loadRuntime();
|
|
274
|
+
var result = runtime.run({
|
|
275
|
+
code: [
|
|
276
|
+
"const path = require('path')",
|
|
277
|
+
'_webout = JSON.stringify({',
|
|
278
|
+
" ext: path.extname('/data/a.csv'),",
|
|
279
|
+
" none: path.extname('csv'),",
|
|
280
|
+
" base: path.basename('/data/a.csv'),",
|
|
281
|
+
" dir: path.dirname('/data/a.csv')",
|
|
282
|
+
'})'
|
|
283
|
+
].join('\n')
|
|
284
|
+
});
|
|
285
|
+
expect(JSON.parse(result.webout)).toEqual({
|
|
286
|
+
ext: '.csv',
|
|
287
|
+
none: '',
|
|
288
|
+
base: 'a.csv',
|
|
289
|
+
dir: '/data'
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
it('hashes with the md5 the worker imports, as node does', function () {
|
|
293
|
+
var runtime = loadRuntime();
|
|
294
|
+
// md5.js declares its hash in the global scope of the worker that imports
|
|
295
|
+
// it; a jest module would hide it, so it is evaluated the way the worker
|
|
296
|
+
// does - which is also what makes this the real md5 rather than a stub
|
|
297
|
+
var source = fs_1.default.readFileSync(path_1.default.join(__dirname, '..', 'assets', 'md5.js'), 'utf8');
|
|
298
|
+
global.MD5 = new Function("".concat(source, "\nreturn MD5"))();
|
|
299
|
+
var result = runtime.run({
|
|
300
|
+
code: "_webout = require('crypto').createHash('md5').update('abc').digest('hex')"
|
|
301
|
+
});
|
|
302
|
+
// md5('abc'), the digest node gives for the same call
|
|
303
|
+
expect(result.webout).toBe('900150983cd24fb0d6963f7d28e17f72');
|
|
304
|
+
});
|
|
305
|
+
it('reports a write that fails through the callback node hands it', function () {
|
|
306
|
+
var runtime = loadRuntime();
|
|
307
|
+
var result = runtime.run({
|
|
308
|
+
code: [
|
|
309
|
+
'let reported = null',
|
|
310
|
+
"fs.writeFile('/x.csv', 'x', { flag: 'r' }, (err) => { reported = err && err.message })",
|
|
311
|
+
'_webout = String(reported)'
|
|
312
|
+
].join('\n')
|
|
313
|
+
});
|
|
314
|
+
expect(result.webout).toMatch(/Unsupported flag 'r' in the browser host/);
|
|
315
|
+
});
|
|
316
|
+
it('refuses to boot a worker that has neither a seed nor a snapshot', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
317
|
+
var runtime, info;
|
|
318
|
+
return __generator(this, function (_a) {
|
|
319
|
+
switch (_a.label) {
|
|
320
|
+
case 0:
|
|
321
|
+
runtime = loadRuntime();
|
|
322
|
+
global.fetch = jest.fn(function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
|
|
323
|
+
return [2 /*return*/, ({ ok: false })];
|
|
324
|
+
}); }); });
|
|
325
|
+
return [4 /*yield*/, runtime.seed()];
|
|
326
|
+
case 1:
|
|
327
|
+
info = _a.sent();
|
|
328
|
+
expect(info.booted).toBe(false);
|
|
329
|
+
expect(info.error).toMatch(/No drive to load/);
|
|
330
|
+
return [2 /*return*/];
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
}); });
|
|
252
334
|
it('collects service console output into the log', function () {
|
|
253
335
|
var runtime = loadRuntime();
|
|
254
336
|
var result = runtime.run({
|
|
@@ -847,24 +929,37 @@ describe('browser runtime', function () {
|
|
|
847
929
|
});
|
|
848
930
|
expect(result.webout).toBe('AQID');
|
|
849
931
|
});
|
|
850
|
-
it('
|
|
851
|
-
var runtime, listing,
|
|
852
|
-
var
|
|
853
|
-
return __generator(this, function (
|
|
854
|
-
switch (
|
|
932
|
+
it('hands the API a store over the drive it seeded', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
933
|
+
var runtime, listing, _a, _b;
|
|
934
|
+
var _c;
|
|
935
|
+
return __generator(this, function (_d) {
|
|
936
|
+
switch (_d.label) {
|
|
855
937
|
case 0:
|
|
856
938
|
runtime = loadRuntime();
|
|
857
|
-
return [4 /*yield*/, seedDrive(runtime, (
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
939
|
+
return [4 /*yield*/, seedDrive(runtime, (_c = {},
|
|
940
|
+
_c[drivePath('services/public/getdata.js')] = '_webout = "x";',
|
|
941
|
+
_c[drivePath('data/a.txt')] = 'a',
|
|
942
|
+
_c))
|
|
943
|
+
// drive-relative paths, the form every @sasjs/api service speaks: the app
|
|
944
|
+
// loc is part of the path, the host's own root is not
|
|
945
|
+
];
|
|
861
946
|
case 1:
|
|
862
|
-
|
|
863
|
-
|
|
947
|
+
_d.sent();
|
|
948
|
+
return [4 /*yield*/, runtime.driveStore.list(appLoc + '/services/public')];
|
|
949
|
+
case 2:
|
|
950
|
+
listing = _d.sent();
|
|
864
951
|
expect(listing.files).toEqual(['getdata.js']);
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
952
|
+
_a = expect;
|
|
953
|
+
return [4 /*yield*/, runtime.driveStore.read(appLoc + '/data/a.txt')];
|
|
954
|
+
case 3:
|
|
955
|
+
_a.apply(void 0, [_d.sent()]).toBe('a');
|
|
956
|
+
_b = expect;
|
|
957
|
+
return [4 /*yield*/, runtime.driveStore.stat(appLoc + '/data')];
|
|
958
|
+
case 4:
|
|
959
|
+
_b.apply(void 0, [_d.sent()]).toEqual({
|
|
960
|
+
directory: true,
|
|
961
|
+
size: 0
|
|
962
|
+
});
|
|
868
963
|
return [2 /*return*/];
|
|
869
964
|
}
|
|
870
965
|
});
|
|
@@ -949,9 +1044,11 @@ describe('browser runtime', function () {
|
|
|
949
1044
|
var runtime = loadRuntime();
|
|
950
1045
|
expect(function () { return runtime.run({ program: 'services/public/missing' }); }).toThrow(/Program not found in drive/);
|
|
951
1046
|
});
|
|
952
|
-
describe('drive
|
|
953
|
-
// the
|
|
954
|
-
//
|
|
1047
|
+
describe('drive store', function () {
|
|
1048
|
+
// the store the API is handed: drive-relative paths in, the drive's own
|
|
1049
|
+
// absolute keys behind. What a status or a message should be is the
|
|
1050
|
+
// package's business (spec/sw.spec.ts runs its corpus through this worker),
|
|
1051
|
+
// so these cases are the storage contract only.
|
|
955
1052
|
var root = '/sasjs/drive/files';
|
|
956
1053
|
var seededRuntime = function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
957
1054
|
var runtime;
|
|
@@ -975,220 +1072,344 @@ describe('browser runtime', function () {
|
|
|
975
1072
|
}
|
|
976
1073
|
});
|
|
977
1074
|
}); };
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
catch (e) {
|
|
983
|
-
return e.status;
|
|
984
|
-
}
|
|
985
|
-
return 200;
|
|
986
|
-
};
|
|
987
|
-
it('creates a file, and the page is told to persist it', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
988
|
-
var runtime;
|
|
989
|
-
var _a;
|
|
990
|
-
return __generator(this, function (_b) {
|
|
991
|
-
switch (_b.label) {
|
|
1075
|
+
it('reads text as text and hands back the entry it holds', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1076
|
+
var runtime, store, _a, _b, _c, _d;
|
|
1077
|
+
return __generator(this, function (_e) {
|
|
1078
|
+
switch (_e.label) {
|
|
992
1079
|
case 0: return [4 /*yield*/, seededRuntime()];
|
|
993
1080
|
case 1:
|
|
994
|
-
runtime =
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1081
|
+
runtime = _e.sent();
|
|
1082
|
+
store = runtime.driveStore;
|
|
1083
|
+
_a = expect;
|
|
1084
|
+
return [4 /*yield*/, store.exists('/data/keep.csv')];
|
|
1085
|
+
case 2:
|
|
1086
|
+
_a.apply(void 0, [_e.sent()]).toBe(true);
|
|
1087
|
+
_b = expect;
|
|
1088
|
+
return [4 /*yield*/, store.exists('/data/missing.csv')];
|
|
1089
|
+
case 3:
|
|
1090
|
+
_b.apply(void 0, [_e.sent()]).toBe(false);
|
|
1091
|
+
_c = expect;
|
|
1092
|
+
return [4 /*yield*/, store.read('/data/keep.csv')];
|
|
1093
|
+
case 4:
|
|
1094
|
+
_c.apply(void 0, [_e.sent()]).toBe('a,b\n1,2\n');
|
|
1095
|
+
_d = expect;
|
|
1096
|
+
return [4 /*yield*/, store.stat('/data/keep.csv')];
|
|
1097
|
+
case 5:
|
|
1098
|
+
_d.apply(void 0, [_e.sent()]).toEqual({
|
|
1099
|
+
directory: false,
|
|
1100
|
+
size: 8
|
|
1006
1101
|
});
|
|
1007
1102
|
return [2 /*return*/];
|
|
1008
1103
|
}
|
|
1009
1104
|
});
|
|
1010
1105
|
}); });
|
|
1011
|
-
it('
|
|
1012
|
-
var runtime;
|
|
1013
|
-
return __generator(this, function (
|
|
1014
|
-
switch (
|
|
1106
|
+
it('keeps an entry written as bytes as bytes, and measures it in bytes', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1107
|
+
var runtime, store, _a, _b;
|
|
1108
|
+
return __generator(this, function (_c) {
|
|
1109
|
+
switch (_c.label) {
|
|
1015
1110
|
case 0: return [4 /*yield*/, seededRuntime()];
|
|
1016
1111
|
case 1:
|
|
1017
|
-
runtime =
|
|
1018
|
-
runtime.
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1112
|
+
runtime = _c.sent();
|
|
1113
|
+
store = runtime.driveStore;
|
|
1114
|
+
return [4 /*yield*/, store.write('/data/pack.zip', Uint8Array.from([80, 75, 3, 4]))];
|
|
1115
|
+
case 2:
|
|
1116
|
+
_c.sent();
|
|
1117
|
+
_a = expect;
|
|
1118
|
+
return [4 /*yield*/, store.read('/data/pack.zip')];
|
|
1119
|
+
case 3:
|
|
1120
|
+
_a.apply(void 0, [_c.sent()]).toEqual(Uint8Array.from([80, 75, 3, 4]));
|
|
1121
|
+
_b = expect;
|
|
1122
|
+
return [4 /*yield*/, store.stat('/data/pack.zip')];
|
|
1123
|
+
case 4:
|
|
1124
|
+
_b.apply(void 0, [_c.sent()]).toEqual({
|
|
1125
|
+
directory: false,
|
|
1126
|
+
size: 4
|
|
1022
1127
|
});
|
|
1023
|
-
// and a service reading it gets a Buffer, as it does on the server
|
|
1024
|
-
expect(runtime.run({
|
|
1025
|
-
code: "_webout = typeof require('fs').readFileSync('".concat(root, "/data/pack.zip');")
|
|
1026
|
-
}).webout).toBe('object');
|
|
1027
|
-
expect(runtime.run({
|
|
1028
|
-
code: "_webout = require('fs').readFileSync('".concat(root, "/data/pack.zip', 'base64');")
|
|
1029
|
-
}).webout).toBe('UEsDBA==');
|
|
1030
1128
|
return [2 /*return*/];
|
|
1031
1129
|
}
|
|
1032
1130
|
});
|
|
1033
1131
|
}); });
|
|
1034
|
-
it('
|
|
1132
|
+
it('reports a write to the page, so the entry is persisted', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1035
1133
|
var runtime;
|
|
1036
1134
|
return __generator(this, function (_a) {
|
|
1037
1135
|
switch (_a.label) {
|
|
1038
1136
|
case 0: return [4 /*yield*/, seededRuntime()];
|
|
1039
1137
|
case 1:
|
|
1040
1138
|
runtime = _a.sent();
|
|
1041
|
-
|
|
1139
|
+
return [4 /*yield*/, runtime.driveStore.write('/data/new/deep/up.csv', 'x,y')];
|
|
1140
|
+
case 2:
|
|
1141
|
+
_a.sent();
|
|
1142
|
+
expect(Object.keys(runtime.takeDirty())).toEqual([
|
|
1143
|
+
"".concat(root, "/data/new/deep/up.csv")
|
|
1144
|
+
]);
|
|
1145
|
+
expect(runtime.dumpAll()["".concat(root, "/data/new/deep/up.csv")]).toBe('x,y');
|
|
1042
1146
|
return [2 /*return*/];
|
|
1043
1147
|
}
|
|
1044
1148
|
});
|
|
1045
1149
|
}); });
|
|
1046
|
-
it('
|
|
1047
|
-
var runtime;
|
|
1048
|
-
|
|
1049
|
-
|
|
1150
|
+
it('reports a deletion, so the page drops the entry', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1151
|
+
var runtime, _a;
|
|
1152
|
+
var _b;
|
|
1153
|
+
return __generator(this, function (_c) {
|
|
1154
|
+
switch (_c.label) {
|
|
1050
1155
|
case 0: return [4 /*yield*/, seededRuntime()];
|
|
1051
1156
|
case 1:
|
|
1052
|
-
runtime =
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
expect(runtime.
|
|
1057
|
-
|
|
1157
|
+
runtime = _c.sent();
|
|
1158
|
+
return [4 /*yield*/, runtime.driveStore.remove('/data/keep.csv')];
|
|
1159
|
+
case 2:
|
|
1160
|
+
_c.sent();
|
|
1161
|
+
expect(runtime.takeDirty()).toEqual((_b = {}, _b["".concat(root, "/data/keep.csv")] = null, _b));
|
|
1162
|
+
_a = expect;
|
|
1163
|
+
return [4 /*yield*/, runtime.driveStore.exists('/data/keep.csv')];
|
|
1164
|
+
case 3:
|
|
1165
|
+
_a.apply(void 0, [_c.sent()]).toBe(false);
|
|
1058
1166
|
return [2 /*return*/];
|
|
1059
1167
|
}
|
|
1060
1168
|
});
|
|
1061
1169
|
}); });
|
|
1062
|
-
it('
|
|
1063
|
-
var runtime;
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
switch (_b.label) {
|
|
1170
|
+
it('creates a folder and lists the immediate children of a folder', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1171
|
+
var runtime, store, _a, _b;
|
|
1172
|
+
return __generator(this, function (_c) {
|
|
1173
|
+
switch (_c.label) {
|
|
1067
1174
|
case 0: return [4 /*yield*/, seededRuntime()];
|
|
1068
1175
|
case 1:
|
|
1069
|
-
runtime =
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1176
|
+
runtime = _c.sent();
|
|
1177
|
+
store = runtime.driveStore;
|
|
1178
|
+
return [4 /*yield*/, store.mkdirp('/data/reports/nested')];
|
|
1179
|
+
case 2:
|
|
1180
|
+
_c.sent();
|
|
1181
|
+
return [4 /*yield*/, store.write('/data/reports/a.csv', 'a')];
|
|
1182
|
+
case 3:
|
|
1183
|
+
_c.sent();
|
|
1184
|
+
_a = expect;
|
|
1185
|
+
return [4 /*yield*/, store.list('/data')];
|
|
1186
|
+
case 4:
|
|
1187
|
+
_a.apply(void 0, [_c.sent()]).toEqual({
|
|
1188
|
+
files: ['keep.csv'],
|
|
1189
|
+
folders: ['reports']
|
|
1190
|
+
});
|
|
1191
|
+
_b = expect;
|
|
1192
|
+
return [4 /*yield*/, store.list('/data/reports')];
|
|
1193
|
+
case 5:
|
|
1194
|
+
_b.apply(void 0, [_c.sent()]).toEqual({
|
|
1195
|
+
files: ['a.csv'],
|
|
1196
|
+
folders: ['nested']
|
|
1197
|
+
});
|
|
1076
1198
|
return [2 /*return*/];
|
|
1077
1199
|
}
|
|
1078
1200
|
});
|
|
1079
1201
|
}); });
|
|
1080
|
-
it('
|
|
1081
|
-
var runtime;
|
|
1082
|
-
|
|
1083
|
-
|
|
1202
|
+
it('removes a folder with everything under it, and reports each entry', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1203
|
+
var runtime, store, _a, _b;
|
|
1204
|
+
var _c;
|
|
1205
|
+
return __generator(this, function (_d) {
|
|
1206
|
+
switch (_d.label) {
|
|
1084
1207
|
case 0: return [4 /*yield*/, seededRuntime()];
|
|
1085
1208
|
case 1:
|
|
1086
|
-
runtime =
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1209
|
+
runtime = _d.sent();
|
|
1210
|
+
store = runtime.driveStore;
|
|
1211
|
+
return [4 /*yield*/, store.mkdirp('/data/reports')];
|
|
1212
|
+
case 2:
|
|
1213
|
+
_d.sent();
|
|
1214
|
+
return [4 /*yield*/, store.write('/data/reports/a.csv', 'a')];
|
|
1215
|
+
case 3:
|
|
1216
|
+
_d.sent();
|
|
1217
|
+
return [4 /*yield*/, store.write('/data/reports/nested/b.csv', 'b')];
|
|
1218
|
+
case 4:
|
|
1219
|
+
_d.sent();
|
|
1220
|
+
runtime.takeDirty();
|
|
1221
|
+
return [4 /*yield*/, store.remove('/data/reports')];
|
|
1222
|
+
case 5:
|
|
1223
|
+
_d.sent();
|
|
1224
|
+
_a = expect;
|
|
1225
|
+
return [4 /*yield*/, store.exists('/data/reports')];
|
|
1226
|
+
case 6:
|
|
1227
|
+
_a.apply(void 0, [_d.sent()]).toBe(false);
|
|
1228
|
+
_b = expect;
|
|
1229
|
+
return [4 /*yield*/, store.list('/data')];
|
|
1230
|
+
case 7:
|
|
1231
|
+
_b.apply(void 0, [_d.sent()]).toEqual({
|
|
1232
|
+
files: ['keep.csv'],
|
|
1233
|
+
folders: []
|
|
1234
|
+
});
|
|
1235
|
+
// every file the removal took goes to the page, or IndexedDB keeps it
|
|
1236
|
+
expect(runtime.takeDirty()).toEqual((_c = {},
|
|
1237
|
+
_c["".concat(root, "/data/reports/a.csv")] = null,
|
|
1238
|
+
_c["".concat(root, "/data/reports/nested/b.csv")] = null,
|
|
1239
|
+
_c));
|
|
1090
1240
|
return [2 /*return*/];
|
|
1091
1241
|
}
|
|
1092
1242
|
});
|
|
1093
1243
|
}); });
|
|
1094
|
-
it('
|
|
1095
|
-
var runtime,
|
|
1096
|
-
|
|
1097
|
-
|
|
1244
|
+
it('moves a file, and marks both ends', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1245
|
+
var runtime, _a, _b;
|
|
1246
|
+
var _c;
|
|
1247
|
+
return __generator(this, function (_d) {
|
|
1248
|
+
switch (_d.label) {
|
|
1098
1249
|
case 0: return [4 /*yield*/, seededRuntime()];
|
|
1099
1250
|
case 1:
|
|
1100
|
-
runtime =
|
|
1101
|
-
runtime.
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1251
|
+
runtime = _d.sent();
|
|
1252
|
+
return [4 /*yield*/, runtime.driveStore.move('/data/keep.csv', '/data/kept.csv')];
|
|
1253
|
+
case 2:
|
|
1254
|
+
_d.sent();
|
|
1255
|
+
_a = expect;
|
|
1256
|
+
return [4 /*yield*/, runtime.driveStore.read('/data/kept.csv')];
|
|
1257
|
+
case 3:
|
|
1258
|
+
_a.apply(void 0, [_d.sent()]).toBe('a,b\n1,2\n');
|
|
1259
|
+
_b = expect;
|
|
1260
|
+
return [4 /*yield*/, runtime.driveStore.exists('/data/keep.csv')];
|
|
1261
|
+
case 4:
|
|
1262
|
+
_b.apply(void 0, [_d.sent()]).toBe(false);
|
|
1263
|
+
expect(runtime.takeDirty()).toEqual((_c = {},
|
|
1264
|
+
_c["".concat(root, "/data/kept.csv")] = 'a,b\n1,2\n',
|
|
1265
|
+
_c["".concat(root, "/data/keep.csv")] = null,
|
|
1266
|
+
_c));
|
|
1111
1267
|
return [2 /*return*/];
|
|
1112
1268
|
}
|
|
1113
1269
|
});
|
|
1114
1270
|
}); });
|
|
1115
|
-
it('
|
|
1116
|
-
var runtime;
|
|
1117
|
-
return __generator(this, function (
|
|
1118
|
-
switch (
|
|
1271
|
+
it('moves a folder with its contents, bytes and all', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1272
|
+
var runtime, store, _a, _b, _c;
|
|
1273
|
+
return __generator(this, function (_d) {
|
|
1274
|
+
switch (_d.label) {
|
|
1119
1275
|
case 0: return [4 /*yield*/, seededRuntime()];
|
|
1120
1276
|
case 1:
|
|
1121
|
-
runtime =
|
|
1122
|
-
runtime.
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1277
|
+
runtime = _d.sent();
|
|
1278
|
+
store = runtime.driveStore;
|
|
1279
|
+
return [4 /*yield*/, store.mkdirp('/data/reports')];
|
|
1280
|
+
case 2:
|
|
1281
|
+
_d.sent();
|
|
1282
|
+
return [4 /*yield*/, store.write('/data/reports/a.csv', 'a')];
|
|
1283
|
+
case 3:
|
|
1284
|
+
_d.sent();
|
|
1285
|
+
return [4 /*yield*/, store.write('/data/reports/nested/pack.zip', Uint8Array.from([1, 2, 3]))];
|
|
1286
|
+
case 4:
|
|
1287
|
+
_d.sent();
|
|
1288
|
+
return [4 /*yield*/, store.move('/data/reports', '/archive')];
|
|
1289
|
+
case 5:
|
|
1290
|
+
_d.sent();
|
|
1291
|
+
_a = expect;
|
|
1292
|
+
return [4 /*yield*/, store.list('/archive')];
|
|
1293
|
+
case 6:
|
|
1294
|
+
_a.apply(void 0, [_d.sent()]).toEqual({
|
|
1295
|
+
files: ['a.csv'],
|
|
1296
|
+
folders: ['nested']
|
|
1126
1297
|
});
|
|
1127
|
-
expect
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1298
|
+
_b = expect;
|
|
1299
|
+
return [4 /*yield*/, store.read('/archive/nested/pack.zip')];
|
|
1300
|
+
case 7:
|
|
1301
|
+
_b.apply(void 0, [_d.sent()]).toEqual(Uint8Array.from([1, 2, 3]));
|
|
1302
|
+
_c = expect;
|
|
1303
|
+
return [4 /*yield*/, store.exists('/data/reports')];
|
|
1304
|
+
case 8:
|
|
1305
|
+
_c.apply(void 0, [_d.sent()]).toBe(false);
|
|
1135
1306
|
return [2 /*return*/];
|
|
1136
1307
|
}
|
|
1137
1308
|
});
|
|
1138
1309
|
}); });
|
|
1139
|
-
it('refuses a
|
|
1140
|
-
var runtime;
|
|
1310
|
+
it('refuses a write under a path that is a file, rather than writing through it', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1311
|
+
var runtime, _a;
|
|
1312
|
+
return __generator(this, function (_b) {
|
|
1313
|
+
switch (_b.label) {
|
|
1314
|
+
case 0: return [4 /*yield*/, seededRuntime()
|
|
1315
|
+
// mkdirp would otherwise turn the file into a folder to hold the child
|
|
1316
|
+
];
|
|
1317
|
+
case 1:
|
|
1318
|
+
runtime = _b.sent();
|
|
1319
|
+
// mkdirp would otherwise turn the file into a folder to hold the child
|
|
1320
|
+
return [4 /*yield*/, expect(runtime.driveStore.write('/data/keep.csv/child.csv', 'b')).rejects.toThrow(/an ancestor of the path is a file/)];
|
|
1321
|
+
case 2:
|
|
1322
|
+
// mkdirp would otherwise turn the file into a folder to hold the child
|
|
1323
|
+
_b.sent();
|
|
1324
|
+
_a = expect;
|
|
1325
|
+
return [4 /*yield*/, runtime.driveStore.read('/data/keep.csv')];
|
|
1326
|
+
case 3:
|
|
1327
|
+
_a.apply(void 0, [_b.sent()]).toBe('a,b\n1,2\n');
|
|
1328
|
+
return [2 /*return*/];
|
|
1329
|
+
}
|
|
1330
|
+
});
|
|
1331
|
+
}); });
|
|
1332
|
+
it("refuses a path it does not hold, in the drive's own words", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1333
|
+
var runtime, store;
|
|
1141
1334
|
return __generator(this, function (_a) {
|
|
1142
1335
|
switch (_a.label) {
|
|
1143
1336
|
case 0: return [4 /*yield*/, seededRuntime()];
|
|
1144
1337
|
case 1:
|
|
1145
1338
|
runtime = _a.sent();
|
|
1146
|
-
runtime.
|
|
1147
|
-
|
|
1148
|
-
|
|
1339
|
+
store = runtime.driveStore;
|
|
1340
|
+
// the API checks existence before it reads, so these are the store's own
|
|
1341
|
+
// refusals rather than answers a client normally sees
|
|
1342
|
+
return [4 /*yield*/, expect(store.read('/data/missing.csv')).rejects.toThrow("File doesn't exist.")];
|
|
1343
|
+
case 2:
|
|
1344
|
+
// the API checks existence before it reads, so these are the store's own
|
|
1345
|
+
// refusals rather than answers a client normally sees
|
|
1346
|
+
_a.sent();
|
|
1347
|
+
return [4 /*yield*/, expect(store.stat('/data/missing.csv')).rejects.toThrow(/ENOENT/)];
|
|
1348
|
+
case 3:
|
|
1349
|
+
_a.sent();
|
|
1350
|
+
return [4 /*yield*/, expect(store.move('/data/missing.csv', '/data/x.csv')).rejects.toThrow(/no such folder/)];
|
|
1351
|
+
case 4:
|
|
1352
|
+
_a.sent();
|
|
1149
1353
|
return [2 /*return*/];
|
|
1150
1354
|
}
|
|
1151
1355
|
});
|
|
1152
1356
|
}); });
|
|
1153
|
-
it('refuses
|
|
1154
|
-
var runtime;
|
|
1155
|
-
return __generator(this, function (
|
|
1156
|
-
switch (
|
|
1157
|
-
case 0: return [4 /*yield*/, seededRuntime()
|
|
1158
|
-
// a leading slash is drive-relative, and '..' is resolved before the
|
|
1159
|
-
// containment test - the rule the server applies in resolveWithinDrive
|
|
1160
|
-
];
|
|
1357
|
+
it('refuses a path that climbs out of the drive, whatever the caller', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1358
|
+
var runtime, store, _a;
|
|
1359
|
+
return __generator(this, function (_b) {
|
|
1360
|
+
switch (_b.label) {
|
|
1361
|
+
case 0: return [4 /*yield*/, seededRuntime()];
|
|
1161
1362
|
case 1:
|
|
1162
|
-
runtime =
|
|
1163
|
-
|
|
1164
|
-
//
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
//
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1363
|
+
runtime = _b.sent();
|
|
1364
|
+
store = runtime.driveStore;
|
|
1365
|
+
// the API refuses these first, with the wording a client sees; this is the
|
|
1366
|
+
// storage boundary refusing on its own, because the store is handed out and
|
|
1367
|
+
// a caller that skips the package must not be able to leave the drive
|
|
1368
|
+
return [4 /*yield*/, expect(store.read('/../escape.csv')).rejects.toThrow(/Can't reach a path outside the drive/)];
|
|
1369
|
+
case 2:
|
|
1370
|
+
// the API refuses these first, with the wording a client sees; this is the
|
|
1371
|
+
// storage boundary refusing on its own, because the store is handed out and
|
|
1372
|
+
// a caller that skips the package must not be able to leave the drive
|
|
1373
|
+
_b.sent();
|
|
1374
|
+
return [4 /*yield*/, expect(store.write('/data/../../escape.csv', 'x')).rejects.toThrow(/Can't reach a path outside the drive/)];
|
|
1375
|
+
case 3:
|
|
1376
|
+
_b.sent();
|
|
1377
|
+
return [4 /*yield*/, expect(store.list('/..')).rejects.toThrow(/Can't reach a path outside the drive/)];
|
|
1378
|
+
case 4:
|
|
1379
|
+
_b.sent();
|
|
1380
|
+
_a = expect;
|
|
1381
|
+
return [4 /*yield*/, store.exists('/data/keep.csv')];
|
|
1382
|
+
case 5:
|
|
1383
|
+
_a.apply(void 0, [_b.sent()]).toBe(true);
|
|
1174
1384
|
return [2 /*return*/];
|
|
1175
1385
|
}
|
|
1176
1386
|
});
|
|
1177
1387
|
}); });
|
|
1178
|
-
it('
|
|
1179
|
-
var runtime;
|
|
1180
|
-
return __generator(this, function (
|
|
1181
|
-
switch (
|
|
1388
|
+
it('counts the root as a folder, and its children as the listing', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1389
|
+
var runtime, _a, _b;
|
|
1390
|
+
return __generator(this, function (_c) {
|
|
1391
|
+
switch (_c.label) {
|
|
1182
1392
|
case 0: return [4 /*yield*/, seededRuntime()];
|
|
1183
1393
|
case 1:
|
|
1184
|
-
runtime =
|
|
1185
|
-
|
|
1186
|
-
runtime.
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1394
|
+
runtime = _c.sent();
|
|
1395
|
+
_a = expect;
|
|
1396
|
+
return [4 /*yield*/, runtime.driveStore.stat('')];
|
|
1397
|
+
case 2:
|
|
1398
|
+
_a.apply(void 0, [_c.sent()]).toEqual({
|
|
1399
|
+
directory: true,
|
|
1400
|
+
size: 0
|
|
1401
|
+
});
|
|
1402
|
+
// the seed's own folder sits under the root too, alongside the app loc the
|
|
1403
|
+
// host creates at boot, so the listing is the drive as it stands
|
|
1404
|
+
_b = expect;
|
|
1405
|
+
return [4 /*yield*/, runtime.driveStore.list('')];
|
|
1406
|
+
case 3:
|
|
1407
|
+
// the seed's own folder sits under the root too, alongside the app loc the
|
|
1408
|
+
// host creates at boot, so the listing is the drive as it stands
|
|
1409
|
+
_b.apply(void 0, [_c.sent()]).toEqual({
|
|
1410
|
+
files: [],
|
|
1411
|
+
folders: ['Public', 'data']
|
|
1190
1412
|
});
|
|
1191
|
-
expect(runtime.stats().files).toBe(2);
|
|
1192
1413
|
return [2 /*return*/];
|
|
1193
1414
|
}
|
|
1194
1415
|
});
|