@tellescope/sdk 1.253.0 → 1.253.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.
Files changed (44) hide show
  1. package/lib/cjs/tests/api_tests/beluga_manual_sync.test.d.ts +6 -0
  2. package/lib/cjs/tests/api_tests/beluga_manual_sync.test.d.ts.map +1 -0
  3. package/lib/cjs/tests/api_tests/beluga_manual_sync.test.js +256 -0
  4. package/lib/cjs/tests/api_tests/beluga_manual_sync.test.js.map +1 -0
  5. package/lib/cjs/tests/api_tests/gcal_sync_retry.test.d.ts +43 -0
  6. package/lib/cjs/tests/api_tests/gcal_sync_retry.test.d.ts.map +1 -0
  7. package/lib/cjs/tests/api_tests/gcal_sync_retry.test.js +168 -0
  8. package/lib/cjs/tests/api_tests/gcal_sync_retry.test.js.map +1 -0
  9. package/lib/cjs/tests/api_tests/security/F-0106-F-0110-enduser-write-restrictions.test.d.ts +23 -0
  10. package/lib/cjs/tests/api_tests/security/F-0106-F-0110-enduser-write-restrictions.test.d.ts.map +1 -0
  11. package/lib/cjs/tests/api_tests/security/F-0106-F-0110-enduser-write-restrictions.test.js +325 -0
  12. package/lib/cjs/tests/api_tests/security/F-0106-F-0110-enduser-write-restrictions.test.js.map +1 -0
  13. package/lib/cjs/tests/api_tests/user_portal_settings.test.d.ts.map +1 -1
  14. package/lib/cjs/tests/api_tests/user_portal_settings.test.js +104 -28
  15. package/lib/cjs/tests/api_tests/user_portal_settings.test.js.map +1 -1
  16. package/lib/cjs/tests/tests.d.ts.map +1 -1
  17. package/lib/cjs/tests/tests.js +444 -174
  18. package/lib/cjs/tests/tests.js.map +1 -1
  19. package/lib/esm/tests/api_tests/beluga_manual_sync.test.d.ts +6 -0
  20. package/lib/esm/tests/api_tests/beluga_manual_sync.test.d.ts.map +1 -0
  21. package/lib/esm/tests/api_tests/beluga_manual_sync.test.js +252 -0
  22. package/lib/esm/tests/api_tests/beluga_manual_sync.test.js.map +1 -0
  23. package/lib/esm/tests/api_tests/gcal_sync_retry.test.d.ts +43 -0
  24. package/lib/esm/tests/api_tests/gcal_sync_retry.test.d.ts.map +1 -0
  25. package/lib/esm/tests/api_tests/gcal_sync_retry.test.js +164 -0
  26. package/lib/esm/tests/api_tests/gcal_sync_retry.test.js.map +1 -0
  27. package/lib/esm/tests/api_tests/security/F-0106-F-0110-enduser-write-restrictions.test.d.ts +23 -0
  28. package/lib/esm/tests/api_tests/security/F-0106-F-0110-enduser-write-restrictions.test.d.ts.map +1 -0
  29. package/lib/esm/tests/api_tests/security/F-0106-F-0110-enduser-write-restrictions.test.js +321 -0
  30. package/lib/esm/tests/api_tests/security/F-0106-F-0110-enduser-write-restrictions.test.js.map +1 -0
  31. package/lib/esm/tests/api_tests/user_portal_settings.test.d.ts.map +1 -1
  32. package/lib/esm/tests/api_tests/user_portal_settings.test.js +104 -28
  33. package/lib/esm/tests/api_tests/user_portal_settings.test.js.map +1 -1
  34. package/lib/esm/tests/tests.d.ts.map +1 -1
  35. package/lib/esm/tests/tests.js +444 -174
  36. package/lib/esm/tests/tests.js.map +1 -1
  37. package/lib/tsconfig.tsbuildinfo +1 -1
  38. package/package.json +10 -10
  39. package/src/tests/api_tests/beluga_manual_sync.test.ts +159 -0
  40. package/src/tests/api_tests/gcal_sync_retry.test.ts +104 -0
  41. package/src/tests/api_tests/security/F-0106-F-0110-enduser-write-restrictions.test.ts +214 -0
  42. package/src/tests/api_tests/user_portal_settings.test.ts +71 -1
  43. package/src/tests/tests.ts +222 -6
  44. package/test_generated.pdf +0 -0
@@ -0,0 +1,6 @@
1
+ import { Session } from "../../sdk";
2
+ export declare const beluga_manual_sync_tests: ({ sdk, sdkNonAdmin }: {
3
+ sdk: Session;
4
+ sdkNonAdmin: Session;
5
+ }) => Promise<void>;
6
+ //# sourceMappingURL=beluga_manual_sync.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"beluga_manual_sync.test.d.ts","sourceRoot":"","sources":["../../../../src/tests/api_tests/beluga_manual_sync.test.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAkBnC,eAAO,MAAM,wBAAwB;SAAuC,OAAO;iBAAe,OAAO;mBAqHxG,CAAA"}
@@ -0,0 +1,252 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ var __generator = (this && this.__generator) || function (thisArg, body) {
11
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
12
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
13
+ function verb(n) { return function (v) { return step([n, v]); }; }
14
+ function step(op) {
15
+ if (f) throw new TypeError("Generator is already executing.");
16
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
17
+ 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;
18
+ if (y = 0, t) op = [op[0] & 2, t.value];
19
+ switch (op[0]) {
20
+ case 0: case 1: t = op; break;
21
+ case 4: _.label++; return { value: op[1], done: false };
22
+ case 5: _.label++; y = op[1]; op = [0]; continue;
23
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
24
+ default:
25
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
26
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
27
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
28
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
29
+ if (t[2]) _.ops.pop();
30
+ _.trys.pop(); continue;
31
+ }
32
+ op = body.call(thisArg, _);
33
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
34
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
35
+ }
36
+ };
37
+ require('source-map-support').install();
38
+ import { Session } from "../../sdk";
39
+ import { async_test, log_header, } from "@tellescope/testing";
40
+ import { setup_tests } from "../setup";
41
+ import { BELUGA_TITLE } from "@tellescope/constants";
42
+ var host = process.env.API_URL || 'http://localhost:8080';
43
+ // Manual Beluga re-sync guard tests (CU-86e1uxz1n).
44
+ // - form_responses.push_to_EHR with target === BELUGA_TITLE
45
+ // - files.push with destination === BELUGA_TITLE
46
+ //
47
+ // Fast / no-upload: this only verifies the bad-input / guard branches that reject before any
48
+ // Beluga call. It performs NO S3 file uploads and triggers NO actual sync (both slow and require
49
+ // live Beluga sandbox credentials). File records are created via prepare_file_upload alone — that
50
+ // inserts the DB record, which is all the guards need.
51
+ export var beluga_manual_sync_tests = function (_a) {
52
+ var sdk = _a.sdk, sdkNonAdmin = _a.sdkNonAdmin;
53
+ return __awaiter(void 0, void 0, void 0, function () {
54
+ var errorMessage, enduserId, belugaFormId, plainFormId, fileIds, createdBelugaIntegrationId, enduser_1, belugaForm, plainForm, plainField_1, submitForm, createFileRecord, draft_1, plainResponseId_1, belugaIntegrationAvailable, existing, _b, created, e_1, unlinkedFile_1, _i, fileIds_1, id;
55
+ return __generator(this, function (_c) {
56
+ switch (_c.label) {
57
+ case 0:
58
+ log_header("Beluga Manual Sync Guard Tests (FormResponses & Files)");
59
+ errorMessage = function (e) { var _a; return ((e === null || e === void 0 ? void 0 : e.message) || ((_a = e === null || e === void 0 ? void 0 : e.toString) === null || _a === void 0 ? void 0 : _a.call(e)) || JSON.stringify(e)); };
60
+ fileIds = [];
61
+ _c.label = 1;
62
+ case 1:
63
+ _c.trys.push([1, , 22, 35]);
64
+ return [4 /*yield*/, sdk.api.endusers.createOne({
65
+ fname: 'beluga-sync',
66
+ email: "beluga_manual_sync_".concat(Date.now(), "@test.tellescope.com"),
67
+ })];
68
+ case 2:
69
+ enduser_1 = _c.sent();
70
+ enduserId = enduser_1.id;
71
+ return [4 /*yield*/, sdk.api.forms.createOne({ title: 'Beluga Manual Sync Form', belugaVisitType: 'sync' })];
72
+ case 3:
73
+ belugaForm = _c.sent();
74
+ belugaFormId = belugaForm.id;
75
+ return [4 /*yield*/, sdk.api.forms.createOne({ title: 'Non-Beluga Manual Sync Form' })];
76
+ case 4:
77
+ plainForm = _c.sent();
78
+ plainFormId = plainForm.id;
79
+ return [4 /*yield*/, sdk.api.form_fields.createOne({
80
+ formId: plainForm.id, type: 'string', title: 'Field', previousFields: [{ type: 'root', info: {} }],
81
+ })];
82
+ case 5:
83
+ plainField_1 = _c.sent();
84
+ submitForm = function (formId) { return __awaiter(void 0, void 0, void 0, function () {
85
+ var _a, accessCode, response;
86
+ return __generator(this, function (_b) {
87
+ switch (_b.label) {
88
+ case 0: return [4 /*yield*/, sdk.api.form_responses.prepare_form_response({ enduserId: enduser_1.id, formId: formId })];
89
+ case 1:
90
+ _a = _b.sent(), accessCode = _a.accessCode, response = _a.response;
91
+ return [4 /*yield*/, sdk.api.form_responses.submit_form_response({
92
+ accessCode: accessCode,
93
+ responses: [{ fieldId: plainField_1.id, fieldTitle: 'Field', answer: { type: 'string', value: 'x' } }],
94
+ })];
95
+ case 2:
96
+ _b.sent();
97
+ return [2 /*return*/, response.id];
98
+ }
99
+ });
100
+ }); };
101
+ createFileRecord = function (name) { return __awaiter(void 0, void 0, void 0, function () {
102
+ var file;
103
+ return __generator(this, function (_a) {
104
+ switch (_a.label) {
105
+ case 0: return [4 /*yield*/, sdk.api.files.prepare_file_upload({
106
+ name: name,
107
+ type: 'text/plain', size: 1, enduserId: enduser_1.id,
108
+ })];
109
+ case 1:
110
+ file = (_a.sent()).file;
111
+ fileIds.push(file.id);
112
+ return [2 /*return*/, file];
113
+ }
114
+ });
115
+ }); };
116
+ return [4 /*yield*/, sdk.api.form_responses.prepare_form_response({ enduserId: enduser_1.id, formId: belugaForm.id })];
117
+ case 6:
118
+ draft_1 = (_c.sent()).response;
119
+ return [4 /*yield*/, async_test("push_to_EHR(target=BELUGA) rejects an unsubmitted form response", function () { return sdk.api.form_responses.push_to_EHR({ id: draft_1.id, target: BELUGA_TITLE }); }, { shouldError: true, onError: function (e) { return /has not been submitted/i.test(errorMessage(e)); } })
120
+ // Submitted, but the form has no belugaVisitType → rejected as not configured
121
+ ];
122
+ case 7:
123
+ _c.sent();
124
+ return [4 /*yield*/, submitForm(plainForm.id)];
125
+ case 8:
126
+ plainResponseId_1 = _c.sent();
127
+ return [4 /*yield*/, async_test("push_to_EHR(target=BELUGA) rejects a form not configured for Beluga", function () { return sdk.api.form_responses.push_to_EHR({ id: plainResponseId_1, target: BELUGA_TITLE }); }, { shouldError: true, onError: function (e) { return /not configured for Beluga/i.test(errorMessage(e)); } })
128
+ // ──────────────────────────────────────────────────────────────────────────
129
+ // 2. files.push(destination=BELUGA) guard — the endpoint resolves the destination
130
+ // integration first, so a Beluga integration must exist for the branch to be reached.
131
+ // Create a placeholder if the org has none; skip gracefully if that's not permitted.
132
+ // ──────────────────────────────────────────────────────────────────────────
133
+ ];
134
+ case 9:
135
+ _c.sent();
136
+ belugaIntegrationAvailable = false;
137
+ _c.label = 10;
138
+ case 10:
139
+ _c.trys.push([10, 12, , 13]);
140
+ return [4 /*yield*/, sdk.api.integrations.load_redacted({})];
141
+ case 11:
142
+ existing = _c.sent();
143
+ belugaIntegrationAvailable = !!existing.integrations.find(function (i) { return i.title === BELUGA_TITLE; });
144
+ return [3 /*break*/, 13];
145
+ case 12:
146
+ _b = _c.sent();
147
+ return [3 /*break*/, 13];
148
+ case 13:
149
+ if (!!belugaIntegrationAvailable) return [3 /*break*/, 17];
150
+ _c.label = 14;
151
+ case 14:
152
+ _c.trys.push([14, 16, , 17]);
153
+ return [4 /*yield*/, sdk.api.integrations.createOne({
154
+ title: BELUGA_TITLE,
155
+ authentication: {
156
+ type: 'oauth2',
157
+ info: { access_token: 'test-access-token', refresh_token: 'test-refresh-token', scope: '', token_type: 'Bearer', expiry_date: new Date().getTime() },
158
+ },
159
+ })];
160
+ case 15:
161
+ created = _c.sent();
162
+ createdBelugaIntegrationId = created.id;
163
+ belugaIntegrationAvailable = true;
164
+ return [3 /*break*/, 17];
165
+ case 16:
166
+ e_1 = _c.sent();
167
+ console.log("Could not create a Beluga integration for testing; skipping files.push guard:", errorMessage(e_1));
168
+ return [3 /*break*/, 17];
169
+ case 17:
170
+ if (!belugaIntegrationAvailable) return [3 /*break*/, 20];
171
+ return [4 /*yield*/, createFileRecord('beluga-unlinked.txt')];
172
+ case 18:
173
+ unlinkedFile_1 = _c.sent();
174
+ return [4 /*yield*/, async_test("files.push(destination=BELUGA) rejects a file with no associated form response", function () { return sdk.api.files.push({ id: unlinkedFile_1.id, destination: BELUGA_TITLE }); }, { shouldError: true, onError: function (e) { return /not associated with a form response/i.test(errorMessage(e)); } })];
175
+ case 19:
176
+ _c.sent();
177
+ return [3 /*break*/, 21];
178
+ case 20:
179
+ console.log("⏭️ Skipping files.push(destination=BELUGA) guard (no Beluga integration available)");
180
+ _c.label = 21;
181
+ case 21: return [3 /*break*/, 35];
182
+ case 22:
183
+ _i = 0, fileIds_1 = fileIds;
184
+ _c.label = 23;
185
+ case 23:
186
+ if (!(_i < fileIds_1.length)) return [3 /*break*/, 26];
187
+ id = fileIds_1[_i];
188
+ return [4 /*yield*/, sdk.api.files.deleteOne(id).catch(console.error)];
189
+ case 24:
190
+ _c.sent();
191
+ _c.label = 25;
192
+ case 25:
193
+ _i++;
194
+ return [3 /*break*/, 23];
195
+ case 26:
196
+ if (!belugaFormId) return [3 /*break*/, 28];
197
+ return [4 /*yield*/, sdk.api.forms.deleteOne(belugaFormId).catch(console.error)];
198
+ case 27:
199
+ _c.sent();
200
+ _c.label = 28;
201
+ case 28:
202
+ if (!plainFormId) return [3 /*break*/, 30];
203
+ return [4 /*yield*/, sdk.api.forms.deleteOne(plainFormId).catch(console.error)];
204
+ case 29:
205
+ _c.sent();
206
+ _c.label = 30;
207
+ case 30:
208
+ if (!enduserId) return [3 /*break*/, 32];
209
+ return [4 /*yield*/, sdk.api.endusers.deleteOne(enduserId).catch(console.error)];
210
+ case 31:
211
+ _c.sent();
212
+ _c.label = 32;
213
+ case 32:
214
+ if (!createdBelugaIntegrationId) return [3 /*break*/, 34];
215
+ return [4 /*yield*/, sdk.api.integrations.deleteOne(createdBelugaIntegrationId).catch(console.error)];
216
+ case 33:
217
+ _c.sent();
218
+ _c.label = 34;
219
+ case 34: return [7 /*endfinally*/];
220
+ case 35: return [2 /*return*/];
221
+ }
222
+ });
223
+ });
224
+ };
225
+ if (require.main === module) {
226
+ console.log("Using API URL: ".concat(host));
227
+ var sdk_1 = new Session({ host: host });
228
+ var sdkNonAdmin_1 = new Session({ host: host });
229
+ var runTests = function () { return __awaiter(void 0, void 0, void 0, function () {
230
+ return __generator(this, function (_a) {
231
+ switch (_a.label) {
232
+ case 0: return [4 /*yield*/, setup_tests(sdk_1, sdkNonAdmin_1)];
233
+ case 1:
234
+ _a.sent();
235
+ return [4 /*yield*/, beluga_manual_sync_tests({ sdk: sdk_1, sdkNonAdmin: sdkNonAdmin_1 })];
236
+ case 2:
237
+ _a.sent();
238
+ return [2 /*return*/];
239
+ }
240
+ });
241
+ }); };
242
+ runTests()
243
+ .then(function () {
244
+ console.log("✅ Beluga manual sync test suite completed successfully");
245
+ process.exit(0);
246
+ })
247
+ .catch(function (error) {
248
+ console.error("❌ Beluga manual sync test suite failed:", error);
249
+ process.exit(1);
250
+ });
251
+ }
252
+ //# sourceMappingURL=beluga_manual_sync.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"beluga_manual_sync.test.js","sourceRoot":"","sources":["../../../../src/tests/api_tests/beluga_manual_sync.test.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,OAAO,CAAC,oBAAoB,CAAC,CAAC,OAAO,EAAE,CAAC;AAExC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EACL,UAAU,EACV,UAAU,GACX,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAEpD,IAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,uBAAgC,CAAA;AAEpE,oDAAoD;AACpD,8DAA8D;AAC9D,mDAAmD;AACnD,EAAE;AACF,6FAA6F;AAC7F,iGAAiG;AACjG,kGAAkG;AAClG,uDAAuD;AACvD,MAAM,CAAC,IAAM,wBAAwB,GAAG,UAAO,EAA4D;QAA1D,GAAG,SAAA,EAAE,WAAW,iBAAA;;;;;;oBAC/D,UAAU,CAAC,wDAAwD,CAAC,CAAA;oBAE9D,YAAY,GAAG,UAAC,CAAM,YAAK,OAAA,CAAC,CAAA,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,OAAO,MAAI,MAAA,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,QAAQ,iDAAI,CAAA,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAW,CAAA,EAAA,CAAA;oBAKzF,OAAO,GAAa,EAAE,CAAA;;;;oBAIV,qBAAM,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC;4BAC/C,KAAK,EAAE,aAAa;4BACpB,KAAK,EAAE,6BAAsB,IAAI,CAAC,GAAG,EAAE,yBAAsB;yBAC9D,CAAC,EAAA;;oBAHI,YAAU,SAGd;oBACF,SAAS,GAAG,SAAO,CAAC,EAAE,CAAA;oBAGH,qBAAM,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,yBAAyB,EAAE,eAAe,EAAE,MAAM,EAAE,CAAC,EAAA;;oBAAzG,UAAU,GAAG,SAA4F;oBAC/G,YAAY,GAAG,UAAU,CAAC,EAAE,CAAA;oBAEV,qBAAM,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,6BAA6B,EAAE,CAAC,EAAA;;oBAAnF,SAAS,GAAG,SAAuE;oBACzF,WAAW,GAAG,SAAS,CAAC,EAAE,CAAA;oBAEP,qBAAM,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,SAAS,CAAC;4BACrD,MAAM,EAAE,SAAS,CAAC,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;yBACnG,CAAC,EAAA;;oBAFI,eAAa,SAEjB;oBAEI,UAAU,GAAG,UAAO,MAAc;;;;wCACL,qBAAM,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC,qBAAqB,CAAC,EAAE,SAAS,EAAE,SAAO,CAAC,EAAE,EAAE,MAAM,QAAA,EAAE,CAAC,EAAA;;oCAAhH,KAA2B,SAAqF,EAA9G,UAAU,gBAAA,EAAE,QAAQ,cAAA;oCAC5B,qBAAM,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC,oBAAoB,CAAC;4CAChD,UAAU,YAAA;4CACV,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,YAAU,CAAC,EAAE,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC;yCACrG,CAAC,EAAA;;oCAHF,SAGE,CAAA;oCACF,sBAAO,QAAQ,CAAC,EAAE,EAAA;;;yBACnB,CAAA;oBAKK,gBAAgB,GAAG,UAAO,IAAY;;;;wCACzB,qBAAM,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,mBAAmB,CAAC;wCACvD,IAAI,MAAA;wCAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,EAAE,SAAS,EAAE,SAAO,CAAC,EAAE;qCACzD,CAAC,EAAA;;oCAFM,IAAI,GAAK,CAAA,SAEf,CAAA,KAFU;oCAGZ,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;oCACrB,sBAAO,IAAI,EAAA;;;yBACZ,CAAA;oBAO2B,qBAAM,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC,qBAAqB,CAAC,EAAE,SAAS,EAAE,SAAO,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,EAAE,CAAC,EAAA;;oBAAxH,UAAoB,CAAA,SAAoG,CAAA,SAAzG;oBACvB,qBAAM,UAAU,CACd,iEAAiE,EACjE,cAAM,OAAA,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC,WAAW,CAAC,EAAE,EAAE,EAAE,OAAK,CAAC,EAAE,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,EAA1E,CAA0E,EAChF,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE,UAAC,CAAM,IAAK,OAAA,yBAAyB,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,EAA/C,CAA+C,EAAE,CAC5F;wBAED,8EAA8E;sBAF7E;;oBAJD,SAIC,CAAA;oBAGuB,qBAAM,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC,EAAA;;oBAAhD,oBAAkB,SAA8B;oBACtD,qBAAM,UAAU,CACd,qEAAqE,EACrE,cAAM,OAAA,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC,WAAW,CAAC,EAAE,EAAE,EAAE,iBAAe,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,EAAjF,CAAiF,EACvF,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE,UAAC,CAAM,IAAK,OAAA,4BAA4B,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,EAAlD,CAAkD,EAAE,CAC/F;wBAED,6EAA6E;wBAC7E,kFAAkF;wBAClF,yFAAyF;wBACzF,wFAAwF;wBACxF,6EAA6E;sBAN5E;;oBAJD,SAIC,CAAA;oBAQG,0BAA0B,GAAG,KAAK,CAAA;;;;oBAEnB,qBAAM,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,aAAa,CAAC,EAAE,CAAC,EAAA;;oBAAvD,QAAQ,GAAG,SAA4C;oBAC7D,0BAA0B,GAAG,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,CAAC,UAAC,CAAM,IAAK,OAAA,CAAC,CAAC,KAAK,KAAK,YAAY,EAAxB,CAAwB,CAAC,CAAA;;;;;;yBAG7F,CAAC,0BAA0B,EAA3B,yBAA2B;;;;oBAEX,qBAAM,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,SAAS,CAAC;4BACnD,KAAK,EAAE,YAAY;4BACnB,cAAc,EAAE;gCACd,IAAI,EAAE,QAAQ;gCACd,IAAI,EAAE,EAAE,YAAY,EAAE,mBAAmB,EAAE,aAAa,EAAE,oBAAoB,EAAE,KAAK,EAAE,EAAE,EAAE,UAAU,EAAE,QAAQ,EAAE,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,OAAO,EAAE,EAAE;6BACrJ;yBACF,CAAC,EAAA;;oBANI,OAAO,GAAG,SAMd;oBACF,0BAA0B,GAAG,OAAO,CAAC,EAAE,CAAA;oBACvC,0BAA0B,GAAG,IAAI,CAAA;;;;oBAEjC,OAAO,CAAC,GAAG,CAAC,+EAA+E,EAAE,YAAY,CAAC,GAAC,CAAC,CAAC,CAAA;;;yBAI7G,0BAA0B,EAA1B,yBAA0B;oBAEP,qBAAM,gBAAgB,CAAC,qBAAqB,CAAC,EAAA;;oBAA5D,iBAAe,SAA6C;oBAClE,qBAAM,UAAU,CACd,gFAAgF,EAChF,cAAM,OAAA,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,cAAY,CAAC,EAAE,EAAE,WAAW,EAAE,YAAY,EAAE,CAAC,EAAtE,CAAsE,EAC5E,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE,UAAC,CAAM,IAAK,OAAA,sCAAsC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,EAA5D,CAA4D,EAAE,CACzG,EAAA;;oBAJD,SAIC,CAAA;;;oBAED,OAAO,CAAC,GAAG,CAAC,qFAAqF,CAAC,CAAA;;;;0BAG5E,EAAP,mBAAO;;;yBAAP,CAAA,qBAAO,CAAA;oBAAb,EAAE;oBACX,qBAAM,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAA;;oBAAtD,SAAsD,CAAA;;;oBADvC,IAAO,CAAA;;;yBAGpB,YAAY,EAAZ,yBAAY;oBAAE,qBAAM,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAA;;oBAAhE,SAAgE,CAAA;;;yBAC9E,WAAW,EAAX,yBAAW;oBAAE,qBAAM,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAA;;oBAA/D,SAA+D,CAAA;;;yBAC5E,SAAS,EAAT,yBAAS;oBAAE,qBAAM,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAA;;oBAAhE,SAAgE,CAAA;;;yBAC3E,0BAA0B,EAA1B,yBAA0B;oBAAE,qBAAM,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,SAAS,CAAC,0BAA0B,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAA;;oBAArF,SAAqF,CAAA;;;;;;;CAExH,CAAA;AAED,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE;IAC3B,OAAO,CAAC,GAAG,CAAC,yBAAkB,IAAI,CAAE,CAAC,CAAA;IACrC,IAAM,KAAG,GAAG,IAAI,OAAO,CAAC,EAAE,IAAI,MAAA,EAAE,CAAC,CAAA;IACjC,IAAM,aAAW,GAAG,IAAI,OAAO,CAAC,EAAE,IAAI,MAAA,EAAE,CAAC,CAAA;IAEzC,IAAM,QAAQ,GAAG;;;wBACf,qBAAM,WAAW,CAAC,KAAG,EAAE,aAAW,CAAC,EAAA;;oBAAnC,SAAmC,CAAA;oBACnC,qBAAM,wBAAwB,CAAC,EAAE,GAAG,OAAA,EAAE,WAAW,eAAA,EAAE,CAAC,EAAA;;oBAApD,SAAoD,CAAA;;;;SACrD,CAAA;IAED,QAAQ,EAAE;SACP,IAAI,CAAC;QACJ,OAAO,CAAC,GAAG,CAAC,wDAAwD,CAAC,CAAA;QACrE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC,CAAC;SACD,KAAK,CAAC,UAAC,KAAK;QACX,OAAO,CAAC,KAAK,CAAC,yCAAyC,EAAE,KAAK,CAAC,CAAA;QAC/D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC,CAAC,CAAA;CACL"}
@@ -0,0 +1,43 @@
1
+ import { Session } from "../../sdk";
2
+ /**
3
+ * Google Calendar sync retry — integration coverage.
4
+ *
5
+ * ┌─ ARCHITECTURE NOTE (why the full mock-Google scenarios are gated) ──────────┐
6
+ * │ SDK api_tests run in a SEPARATE process from the API server (they talk to │
7
+ * │ host = API_URL / http://localhost:8080). The retry scheduler, the Google │
8
+ * │ retryable-error predicates, and the google.calendar() client all live in the │
9
+ * │ API SERVER process. A stub installed here (in the SDK test process) cannot │
10
+ * │ replace the server's in-process google client, so we cannot deterministically│
11
+ * │ inject 429 / 500 / ECONNRESET responses from this test alone. │
12
+ * │ │
13
+ * │ Two ways to run the full fail-429-then-succeed / exhaustion / cap scenarios: │
14
+ * │ 1. Run the API server with NODE_ENV=test and RETRY_SCHEDULER_DELAYS_MS=10,20 │
15
+ * │ (already honored by the singleton constructor) plus a server-side │
16
+ * │ fault-injection hook on sdk.events.* that reads e.g. │
17
+ * │ process.env.GCAL_TEST_FORCE_ERROR — that hook does not exist yet. │
18
+ * │ 2. Drive the scheduler directly with the in-process unit tests, which DO │
19
+ * │ cover all retry mechanics deterministically: │
20
+ * │ packages/private/api/api/modules/retry_scheduler.test.ts │
21
+ * │ packages/private/api/api/integrations/google.test.ts │
22
+ * │ │
23
+ * │ The scenario matrix below is recorded for when a server-side hook is added. │
24
+ * └──────────────────────────────────────────────────────────────────────────────┘
25
+ *
26
+ * Scenario matrix (requires server-side Google fault injection — see note):
27
+ * 1. create retry: fail 429 once then succeed -> gcal reference written, NO background_errors
28
+ * 2. exhaustion: fail 429 on every call -> exactly one "Google Calendar Push Error" background_errors row
29
+ * 3. non-retryable: fail 403 -> background_errors written immediately, no retries
30
+ * 4. create idempotency: fail ECONNRESET (network) -> NON-retryable for create (duplicate risk),
31
+ * background_errors written, no retry, no duplicate insert
32
+ * 5. update + delete: same as (1) for events.patch and events.delete
33
+ * 6. cap exceeded: maxOpenRetries=2, 3 events all fail retryably -> 3rd -> background_errors immediately
34
+ *
35
+ * The runnable assertions below cover what IS observable end-to-end without a
36
+ * connected Google account: the refactored call sites must not regress the common
37
+ * "user has no Google integration" path (no sync attempt, no background_errors, no retry).
38
+ */
39
+ export declare const gcal_sync_retry_tests: ({ sdk }: {
40
+ sdk: Session;
41
+ sdkNonAdmin: Session;
42
+ }) => Promise<void>;
43
+ //# sourceMappingURL=gcal_sync_retry.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gcal_sync_retry.test.d.ts","sourceRoot":"","sources":["../../../../src/tests/api_tests/gcal_sync_retry.test.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAUnC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,eAAO,MAAM,qBAAqB;SAA2B,OAAO;iBAAe,OAAO;mBAwCzF,CAAA"}
@@ -0,0 +1,164 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ var __generator = (this && this.__generator) || function (thisArg, body) {
11
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
12
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
13
+ function verb(n) { return function (v) { return step([n, v]); }; }
14
+ function step(op) {
15
+ if (f) throw new TypeError("Generator is already executing.");
16
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
17
+ 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;
18
+ if (y = 0, t) op = [op[0] & 2, t.value];
19
+ switch (op[0]) {
20
+ case 0: case 1: t = op; break;
21
+ case 4: _.label++; return { value: op[1], done: false };
22
+ case 5: _.label++; y = op[1]; op = [0]; continue;
23
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
24
+ default:
25
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
26
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
27
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
28
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
29
+ if (t[2]) _.ops.pop();
30
+ _.trys.pop(); continue;
31
+ }
32
+ op = body.call(thisArg, _);
33
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
34
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
35
+ }
36
+ };
37
+ require('source-map-support').install();
38
+ import { Session } from "../../sdk";
39
+ import { async_test, log_header, wait, } from "@tellescope/testing";
40
+ import { setup_tests } from "../setup";
41
+ var host = process.env.API_URL || 'http://localhost:8080';
42
+ /**
43
+ * Google Calendar sync retry — integration coverage.
44
+ *
45
+ * ┌─ ARCHITECTURE NOTE (why the full mock-Google scenarios are gated) ──────────┐
46
+ * │ SDK api_tests run in a SEPARATE process from the API server (they talk to │
47
+ * │ host = API_URL / http://localhost:8080). The retry scheduler, the Google │
48
+ * │ retryable-error predicates, and the google.calendar() client all live in the │
49
+ * │ API SERVER process. A stub installed here (in the SDK test process) cannot │
50
+ * │ replace the server's in-process google client, so we cannot deterministically│
51
+ * │ inject 429 / 500 / ECONNRESET responses from this test alone. │
52
+ * │ │
53
+ * │ Two ways to run the full fail-429-then-succeed / exhaustion / cap scenarios: │
54
+ * │ 1. Run the API server with NODE_ENV=test and RETRY_SCHEDULER_DELAYS_MS=10,20 │
55
+ * │ (already honored by the singleton constructor) plus a server-side │
56
+ * │ fault-injection hook on sdk.events.* that reads e.g. │
57
+ * │ process.env.GCAL_TEST_FORCE_ERROR — that hook does not exist yet. │
58
+ * │ 2. Drive the scheduler directly with the in-process unit tests, which DO │
59
+ * │ cover all retry mechanics deterministically: │
60
+ * │ packages/private/api/api/modules/retry_scheduler.test.ts │
61
+ * │ packages/private/api/api/integrations/google.test.ts │
62
+ * │ │
63
+ * │ The scenario matrix below is recorded for when a server-side hook is added. │
64
+ * └──────────────────────────────────────────────────────────────────────────────┘
65
+ *
66
+ * Scenario matrix (requires server-side Google fault injection — see note):
67
+ * 1. create retry: fail 429 once then succeed -> gcal reference written, NO background_errors
68
+ * 2. exhaustion: fail 429 on every call -> exactly one "Google Calendar Push Error" background_errors row
69
+ * 3. non-retryable: fail 403 -> background_errors written immediately, no retries
70
+ * 4. create idempotency: fail ECONNRESET (network) -> NON-retryable for create (duplicate risk),
71
+ * background_errors written, no retry, no duplicate insert
72
+ * 5. update + delete: same as (1) for events.patch and events.delete
73
+ * 6. cap exceeded: maxOpenRetries=2, 3 events all fail retryably -> 3rd -> background_errors immediately
74
+ *
75
+ * The runnable assertions below cover what IS observable end-to-end without a
76
+ * connected Google account: the refactored call sites must not regress the common
77
+ * "user has no Google integration" path (no sync attempt, no background_errors, no retry).
78
+ */
79
+ export var gcal_sync_retry_tests = function (_a) {
80
+ var sdk = _a.sdk;
81
+ return __awaiter(void 0, void 0, void 0, function () {
82
+ var enduser;
83
+ return __generator(this, function (_b) {
84
+ switch (_b.label) {
85
+ case 0:
86
+ log_header("Google Calendar Sync Retry");
87
+ if (process.env.GCAL_RETRY_INTEGRATION !== '1') {
88
+ console.log("ℹ️ Skipping mock-Google scenarios (set GCAL_RETRY_INTEGRATION=1 with a server-side "
89
+ + "fault-injection hook to run scenarios 1-6). Running no-integration regression checks only.");
90
+ }
91
+ return [4 /*yield*/, sdk.api.endusers.createOne({ fname: 'GcalRetry', lname: 'Test' })];
92
+ case 1:
93
+ enduser = _b.sent();
94
+ return [4 /*yield*/, async_test('create event for user without Google integration does not produce a background error', function () { return __awaiter(void 0, void 0, void 0, function () {
95
+ var event, errors;
96
+ return __generator(this, function (_a) {
97
+ switch (_a.label) {
98
+ case 0: return [4 /*yield*/, sdk.api.calendar_events.createOne({
99
+ title: 'Gcal Retry Regression',
100
+ startTimeInMS: Date.now() + 1000 * 60 * 60,
101
+ durationInMinutes: 30,
102
+ attendees: [{ type: 'user', id: sdk.userInfo.id }, { type: 'enduser', id: enduser.id }],
103
+ })
104
+ // give side-effect handlers a moment to run
105
+ ];
106
+ case 1:
107
+ event = _a.sent();
108
+ // give side-effect handlers a moment to run
109
+ return [4 /*yield*/, wait(undefined, 750)];
110
+ case 2:
111
+ // give side-effect handlers a moment to run
112
+ _a.sent();
113
+ return [4 /*yield*/, sdk.api.background_errors.getSome({}).catch(function () { return []; })
114
+ // clean up
115
+ ];
116
+ case 3:
117
+ errors = _a.sent();
118
+ // clean up
119
+ return [4 /*yield*/, sdk.api.calendar_events.deleteOne(event.id).catch(function () { })
120
+ // No Google integration on the test user => no push attempt => no error row.
121
+ ];
122
+ case 4:
123
+ // clean up
124
+ _a.sent();
125
+ // No Google integration on the test user => no push attempt => no error row.
126
+ return [2 /*return*/, errors.filter(function (e) { return (e.userId === sdk.userInfo.id && e.title === "Google Calendar Push Error"); }).length];
127
+ }
128
+ });
129
+ }); }, { onResult: function (count) { return count === 0; } })
130
+ // cleanup
131
+ ];
132
+ case 2:
133
+ _b.sent();
134
+ // cleanup
135
+ return [4 /*yield*/, sdk.api.endusers.deleteOne(enduser.id).catch(function () { })];
136
+ case 3:
137
+ // cleanup
138
+ _b.sent();
139
+ return [2 /*return*/];
140
+ }
141
+ });
142
+ });
143
+ };
144
+ if (require.main === module) {
145
+ var sdk_1 = new Session({ host: host });
146
+ var sdkNonAdmin_1 = new Session({ host: host });
147
+ var runTests = function () { return __awaiter(void 0, void 0, void 0, function () {
148
+ return __generator(this, function (_a) {
149
+ switch (_a.label) {
150
+ case 0: return [4 /*yield*/, setup_tests(sdk_1, sdkNonAdmin_1)];
151
+ case 1:
152
+ _a.sent();
153
+ return [4 /*yield*/, gcal_sync_retry_tests({ sdk: sdk_1, sdkNonAdmin: sdkNonAdmin_1 })];
154
+ case 2:
155
+ _a.sent();
156
+ return [2 /*return*/];
157
+ }
158
+ });
159
+ }); };
160
+ runTests()
161
+ .then(function () { console.log("✅ gcal sync retry test suite completed successfully"); process.exit(0); })
162
+ .catch(function (error) { console.error("❌ gcal sync retry test suite failed:", error); process.exit(1); });
163
+ }
164
+ //# sourceMappingURL=gcal_sync_retry.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gcal_sync_retry.test.js","sourceRoot":"","sources":["../../../../src/tests/api_tests/gcal_sync_retry.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import { Session } from "../../../sdk";
2
+ /**
3
+ * Regression test for F-0106 and F-0110
4
+ * (security-audit/findings/F-0106-enduser-self-update-admin-only-fields.md,
5
+ * security-audit/findings/F-0110-form-responses-enduser-update-admin-only-fields.md).
6
+ *
7
+ * Endusers could PATCH admin-only / access-bearing / attribution-bearing fields on
8
+ * their own endusers record (assignedTo, tags, references, ...) and their own
9
+ * form_responses (procedureCodes, submittedBy, markedAsSubmitted, ...). The fix adds
10
+ * the `enduserUpdatesDisabled` field option (schema.ts ModelFieldInfo), enforced for
11
+ * enduser sessions in the generic update handler (routing.ts createDefaultEndpoints).
12
+ *
13
+ * This test asserts, for every flagged field on both models:
14
+ * - an enduser session updating its OWN record gets a 400 "<field> cannot be updated by endusers"
15
+ * - nothing persists (spot-checked on assignedTo, the highest-impact field)
16
+ * - enduser self-updates of allowed fields still work (fname, hideFromEnduserPortal)
17
+ * - staff sessions can still update the restricted fields
18
+ */
19
+ export declare const enduser_write_restrictions_tests: ({ sdk }: {
20
+ sdk: Session;
21
+ sdkNonAdmin: Session;
22
+ }) => Promise<void>;
23
+ //# sourceMappingURL=F-0106-F-0110-enduser-write-restrictions.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"F-0106-F-0110-enduser-write-restrictions.test.d.ts","sourceRoot":"","sources":["../../../../../src/tests/api_tests/security/F-0106-F-0110-enduser-write-restrictions.test.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAkB,MAAM,cAAc,CAAA;AA+DtD;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,gCAAgC;SAA2B,OAAO;iBAAe,OAAO;mBA6GpG,CAAA"}