@proteinjs/user-server 1.18.0 → 1.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/dist/generated/index.d.ts.map +1 -1
- package/dist/generated/index.js +3 -1
- package/dist/generated/index.js.map +1 -1
- package/dist/src/authorization/UserActivityStamp.d.ts +17 -15
- package/dist/src/authorization/UserActivityStamp.d.ts.map +1 -1
- package/dist/src/authorization/UserActivityStamp.js +21 -19
- package/dist/src/authorization/UserActivityStamp.js.map +1 -1
- package/dist/src/authorization/userCache.d.ts +9 -0
- package/dist/src/authorization/userCache.d.ts.map +1 -1
- package/dist/src/authorization/userCache.js +9 -9
- package/dist/src/authorization/userCache.js.map +1 -1
- package/dist/src/routes/DevBootstrapRoles.d.ts +21 -0
- package/dist/src/routes/DevBootstrapRoles.d.ts.map +1 -0
- package/dist/src/routes/DevBootstrapRoles.js +64 -0
- package/dist/src/routes/DevBootstrapRoles.js.map +1 -0
- package/dist/src/routes/devLogin.d.ts +11 -0
- package/dist/src/routes/devLogin.d.ts.map +1 -1
- package/dist/src/routes/devLogin.js +23 -3
- package/dist/src/routes/devLogin.js.map +1 -1
- package/dist/src/services/Roles.d.ts +29 -1
- package/dist/src/services/Roles.d.ts.map +1 -1
- package/dist/src/services/Roles.js +88 -14
- package/dist/src/services/Roles.js.map +1 -1
- package/dist/src/services/UserPresence.d.ts +16 -0
- package/dist/src/services/UserPresence.d.ts.map +1 -0
- package/dist/src/services/UserPresence.js +75 -0
- package/dist/src/services/UserPresence.js.map +1 -0
- package/dist/test/DevLogin.test.js +5 -2
- package/dist/test/DevLogin.test.js.map +1 -1
- package/dist/test/DevLoginBootstrapAdmin.test.js +2 -0
- package/dist/test/DevLoginBootstrapAdmin.test.js.map +1 -1
- package/dist/test/DevLoginBootstrapRoles.test.d.ts +2 -0
- package/dist/test/DevLoginBootstrapRoles.test.d.ts.map +1 -0
- package/dist/test/DevLoginBootstrapRoles.test.js +491 -0
- package/dist/test/DevLoginBootstrapRoles.test.js.map +1 -0
- package/dist/test/UserActivityStamp.integration.test.js +57 -90
- package/dist/test/UserActivityStamp.integration.test.js.map +1 -1
- package/dist/test/UserCacheDoesNotStampPresence.integration.test.d.ts +2 -0
- package/dist/test/UserCacheDoesNotStampPresence.integration.test.d.ts.map +1 -0
- package/dist/test/UserCacheDoesNotStampPresence.integration.test.js +117 -0
- package/dist/test/UserCacheDoesNotStampPresence.integration.test.js.map +1 -0
- package/generated/index.ts +3 -1
- package/package.json +3 -3
- package/src/authorization/UserActivityStamp.ts +22 -20
- package/src/authorization/userCache.ts +9 -9
- package/src/routes/DevBootstrapRoles.ts +50 -0
- package/src/routes/devLogin.ts +18 -0
- package/src/services/Roles.ts +61 -1
- package/src/services/UserPresence.ts +26 -0
- package/test/DevLogin.test.ts +5 -2
- package/test/DevLoginBootstrapAdmin.test.ts +2 -0
- package/test/DevLoginBootstrapRoles.test.ts +248 -0
- package/test/UserActivityStamp.integration.test.ts +38 -50
- package/test/UserCacheDoesNotStampPresence.integration.test.ts +44 -0
|
@@ -42,8 +42,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
42
42
|
var moment_1 = __importDefault(require("moment"));
|
|
43
43
|
var db_1 = require("@proteinjs/db");
|
|
44
44
|
var user_1 = require("@proteinjs/user");
|
|
45
|
-
var userCache_1 = require("../src/authorization/userCache");
|
|
46
45
|
var UserActivityStamp_1 = require("../src/authorization/UserActivityStamp");
|
|
46
|
+
var UserPresence_1 = require("../src/services/UserPresence");
|
|
47
47
|
var UserServerTestEnvironment_1 = require("./UserServerTestEnvironment");
|
|
48
48
|
var testEnv = new UserServerTestEnvironment_1.UserServerTestEnvironment();
|
|
49
49
|
var stampInternals = function (stamp) { return stamp.constructor; };
|
|
@@ -53,41 +53,15 @@ var activityRows = function (scope) { return __awaiter(void 0, void 0, void 0, f
|
|
|
53
53
|
case 1: return [2 /*return*/, _a.sent()];
|
|
54
54
|
}
|
|
55
55
|
}); }); };
|
|
56
|
-
/** The stamp write is fire-and-forget off userCache — poll the OUTCOME (row present) briefly. */
|
|
57
|
-
var waitForActivityRow = function (scope) { return __awaiter(void 0, void 0, void 0, function () {
|
|
58
|
-
var attempt, rows;
|
|
59
|
-
return __generator(this, function (_a) {
|
|
60
|
-
switch (_a.label) {
|
|
61
|
-
case 0:
|
|
62
|
-
attempt = 0;
|
|
63
|
-
_a.label = 1;
|
|
64
|
-
case 1:
|
|
65
|
-
if (!(attempt < 50)) return [3 /*break*/, 5];
|
|
66
|
-
return [4 /*yield*/, activityRows(scope)];
|
|
67
|
-
case 2:
|
|
68
|
-
rows = _a.sent();
|
|
69
|
-
if (rows.length > 0) {
|
|
70
|
-
return [2 /*return*/, rows[0]];
|
|
71
|
-
}
|
|
72
|
-
return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 100); })];
|
|
73
|
-
case 3:
|
|
74
|
-
_a.sent();
|
|
75
|
-
_a.label = 4;
|
|
76
|
-
case 4:
|
|
77
|
-
attempt++;
|
|
78
|
-
return [3 /*break*/, 1];
|
|
79
|
-
case 5: throw new Error("No user_activity row appeared for scope ".concat(scope));
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
}); };
|
|
83
56
|
/**
|
|
84
|
-
* LAST ACTIVITY = HUMAN PRESENCE (UserActivityTable's contract): the stamp
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
* keep ONE row per user; the
|
|
57
|
+
* LAST ACTIVITY = HUMAN PRESENCE (UserActivityTable's contract): the stamp is written from the
|
|
58
|
+
* page's human-input report — the UserPresence service door — and from nowhere else (the
|
|
59
|
+
* session build's non-stamp is pinned in UserCacheDoesNotStampPresence). These tests pin the
|
|
60
|
+
* door's outcomes: a person's report lands the presence row for the CALLING user; machine
|
|
61
|
+
* accounts and the guest identity never do; repeated reports keep ONE row per user; the
|
|
62
|
+
* throttle holds writes to the interval.
|
|
89
63
|
*/
|
|
90
|
-
describe('UserActivityStamp —
|
|
64
|
+
describe('UserActivityStamp — human-input presence', function () {
|
|
91
65
|
beforeAll(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
92
66
|
return __generator(this, function (_a) {
|
|
93
67
|
switch (_a.label) {
|
|
@@ -112,26 +86,27 @@ describe('UserActivityStamp — interactive presence', function () {
|
|
|
112
86
|
// Each test controls its own throttle history.
|
|
113
87
|
stampInternals(new UserActivityStamp_1.UserActivityStamp()).lastStampMs.clear();
|
|
114
88
|
});
|
|
115
|
-
it('
|
|
116
|
-
var user,
|
|
89
|
+
it('the presence door stamps the calling user: a page report under her session lands her row', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
90
|
+
var user, rows;
|
|
117
91
|
return __generator(this, function (_a) {
|
|
118
92
|
switch (_a.label) {
|
|
119
93
|
case 0: return [4 /*yield*/, testEnv.createUser({ name: 'Present Human', email: 'present-human@test.local' })];
|
|
120
94
|
case 1:
|
|
121
95
|
user = _a.sent();
|
|
122
|
-
|
|
96
|
+
testEnv.actAs(user);
|
|
97
|
+
return [4 /*yield*/, new UserPresence_1.UserPresence().recordPresence()];
|
|
123
98
|
case 2:
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
return [4 /*yield*/, waitForActivityRow(user.id)];
|
|
99
|
+
_a.sent();
|
|
100
|
+
return [4 /*yield*/, activityRows(user.id)];
|
|
127
101
|
case 3:
|
|
128
|
-
|
|
129
|
-
expect(
|
|
102
|
+
rows = _a.sent();
|
|
103
|
+
expect(rows).toHaveLength(1);
|
|
104
|
+
expect((0, moment_1.default)(rows[0].lastActiveAt).isAfter((0, moment_1.default)().subtract(1, 'minute'))).toBe(true);
|
|
130
105
|
return [2 /*return*/];
|
|
131
106
|
}
|
|
132
107
|
});
|
|
133
108
|
}); });
|
|
134
|
-
it('keeps ONE row per user and advances it on later
|
|
109
|
+
it('keeps ONE row per user and advances it on later reports (scope-unique invariant)', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
135
110
|
var user, stamp, first, rows;
|
|
136
111
|
return __generator(this, function (_a) {
|
|
137
112
|
switch (_a.label) {
|
|
@@ -139,18 +114,19 @@ describe('UserActivityStamp — interactive presence', function () {
|
|
|
139
114
|
case 1:
|
|
140
115
|
user = _a.sent();
|
|
141
116
|
stamp = new UserActivityStamp_1.UserActivityStamp();
|
|
142
|
-
return [4 /*yield*/, stamp.
|
|
117
|
+
return [4 /*yield*/, stamp.recordHumanInput(user)];
|
|
143
118
|
case 2:
|
|
144
119
|
_a.sent();
|
|
145
|
-
return [4 /*yield*/,
|
|
120
|
+
return [4 /*yield*/, activityRows(user.id)];
|
|
146
121
|
case 3:
|
|
147
|
-
first = _a.sent();
|
|
148
|
-
|
|
122
|
+
first = (_a.sent())[0];
|
|
123
|
+
expect(first).toBeDefined();
|
|
124
|
+
// Clear the throttle so the second report stamps immediately.
|
|
149
125
|
stampInternals(stamp).lastStampMs.clear();
|
|
150
126
|
return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 5); })];
|
|
151
127
|
case 4:
|
|
152
128
|
_a.sent();
|
|
153
|
-
return [4 /*yield*/, stamp.
|
|
129
|
+
return [4 /*yield*/, stamp.recordHumanInput(user)];
|
|
154
130
|
case 5:
|
|
155
131
|
_a.sent();
|
|
156
132
|
return [4 /*yield*/, activityRows(user.id)];
|
|
@@ -162,8 +138,8 @@ describe('UserActivityStamp — interactive presence', function () {
|
|
|
162
138
|
}
|
|
163
139
|
});
|
|
164
140
|
}); });
|
|
165
|
-
it('never stamps a machine account
|
|
166
|
-
var machine,
|
|
141
|
+
it('never stamps a machine account (`machine` — the one owner of "is this a machine"), even through the door', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
142
|
+
var machine, _a;
|
|
167
143
|
return __generator(this, function (_b) {
|
|
168
144
|
switch (_b.label) {
|
|
169
145
|
case 0: return [4 /*yield*/, (0, db_1.getDbAsSystem)().insert(user_1.tables.User, {
|
|
@@ -173,32 +149,44 @@ describe('UserActivityStamp — interactive presence', function () {
|
|
|
173
149
|
emailVerified: true,
|
|
174
150
|
roles: [],
|
|
175
151
|
isLoadedFromSource: true,
|
|
152
|
+
machine: true,
|
|
176
153
|
})];
|
|
177
154
|
case 1:
|
|
178
155
|
machine = _b.sent();
|
|
179
|
-
|
|
180
|
-
// the transport seam agrees by resolving the machine session through userCache too.
|
|
181
|
-
return [4 /*yield*/, new UserActivityStamp_1.UserActivityStamp().recordInteractiveRequest(machine)];
|
|
156
|
+
return [4 /*yield*/, new UserActivityStamp_1.UserActivityStamp().recordHumanInput(machine)];
|
|
182
157
|
case 2:
|
|
183
|
-
// Await the stamp DIRECTLY (deterministic absence — no fire-and-forget race), then confirm
|
|
184
|
-
// the transport seam agrees by resolving the machine session through userCache too.
|
|
185
158
|
_b.sent();
|
|
186
|
-
|
|
159
|
+
testEnv.actAs(machine);
|
|
160
|
+
return [4 /*yield*/, new UserPresence_1.UserPresence().recordPresence()];
|
|
187
161
|
case 3:
|
|
188
|
-
resolved = _b.sent();
|
|
189
|
-
expect(resolved.id).toBe(machine.id);
|
|
190
|
-
return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 300); })];
|
|
191
|
-
case 4:
|
|
192
162
|
_b.sent();
|
|
193
163
|
_a = expect;
|
|
194
164
|
return [4 /*yield*/, activityRows(machine.id)];
|
|
195
|
-
case
|
|
165
|
+
case 4:
|
|
196
166
|
_a.apply(void 0, [_b.sent()]).toHaveLength(0);
|
|
197
167
|
return [2 /*return*/];
|
|
198
168
|
}
|
|
199
169
|
});
|
|
200
170
|
}); });
|
|
201
|
-
it('
|
|
171
|
+
it('never stamps the guest identity (a door reached with no signed-in user)', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
172
|
+
var before, _a;
|
|
173
|
+
return __generator(this, function (_b) {
|
|
174
|
+
switch (_b.label) {
|
|
175
|
+
case 0: return [4 /*yield*/, (0, db_1.getDbAsSystem)().query(user_1.tables.UserActivity, {})];
|
|
176
|
+
case 1:
|
|
177
|
+
before = (_b.sent()).length;
|
|
178
|
+
return [4 /*yield*/, new UserActivityStamp_1.UserActivityStamp().recordHumanInput(user_1.guestUser)];
|
|
179
|
+
case 2:
|
|
180
|
+
_b.sent();
|
|
181
|
+
_a = expect;
|
|
182
|
+
return [4 /*yield*/, (0, db_1.getDbAsSystem)().query(user_1.tables.UserActivity, {})];
|
|
183
|
+
case 3:
|
|
184
|
+
_a.apply(void 0, [(_b.sent()).length]).toBe(before);
|
|
185
|
+
return [2 /*return*/];
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
}); });
|
|
189
|
+
it('throttles: a second report inside the interval writes nothing', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
202
190
|
var user, stamp, first, rows;
|
|
203
191
|
return __generator(this, function (_a) {
|
|
204
192
|
switch (_a.label) {
|
|
@@ -206,16 +194,17 @@ describe('UserActivityStamp — interactive presence', function () {
|
|
|
206
194
|
case 1:
|
|
207
195
|
user = _a.sent();
|
|
208
196
|
stamp = new UserActivityStamp_1.UserActivityStamp();
|
|
209
|
-
return [4 /*yield*/, stamp.
|
|
197
|
+
return [4 /*yield*/, stamp.recordHumanInput(user)];
|
|
210
198
|
case 2:
|
|
211
199
|
_a.sent();
|
|
212
|
-
return [4 /*yield*/,
|
|
200
|
+
return [4 /*yield*/, activityRows(user.id)];
|
|
213
201
|
case 3:
|
|
214
|
-
first = _a.sent();
|
|
215
|
-
|
|
216
|
-
|
|
202
|
+
first = (_a.sent())[0];
|
|
203
|
+
expect(first).toBeDefined();
|
|
204
|
+
// Throttle history now holds this user; a second report inside the interval must not write.
|
|
205
|
+
return [4 /*yield*/, stamp.recordHumanInput(user)];
|
|
217
206
|
case 4:
|
|
218
|
-
// Throttle history now holds this user; a second
|
|
207
|
+
// Throttle history now holds this user; a second report inside the interval must not write.
|
|
219
208
|
_a.sent();
|
|
220
209
|
return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 300); })];
|
|
221
210
|
case 5:
|
|
@@ -229,27 +218,5 @@ describe('UserActivityStamp — interactive presence', function () {
|
|
|
229
218
|
}
|
|
230
219
|
});
|
|
231
220
|
}); });
|
|
232
|
-
it('a session for a missing account still resolves guest and stamps nothing (no throw)', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
233
|
-
var before, resolved, _a;
|
|
234
|
-
return __generator(this, function (_b) {
|
|
235
|
-
switch (_b.label) {
|
|
236
|
-
case 0: return [4 /*yield*/, (0, db_1.getDbAsSystem)().query(user_1.tables.UserActivity, {})];
|
|
237
|
-
case 1:
|
|
238
|
-
before = (_b.sent()).length;
|
|
239
|
-
return [4 /*yield*/, userCache_1.userCache.create('stale-session', 'no-such-account@test.local')];
|
|
240
|
-
case 2:
|
|
241
|
-
resolved = _b.sent();
|
|
242
|
-
expect(resolved.id).toBe('guest');
|
|
243
|
-
return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 200); })];
|
|
244
|
-
case 3:
|
|
245
|
-
_b.sent();
|
|
246
|
-
_a = expect;
|
|
247
|
-
return [4 /*yield*/, (0, db_1.getDbAsSystem)().query(user_1.tables.UserActivity, {})];
|
|
248
|
-
case 4:
|
|
249
|
-
_a.apply(void 0, [(_b.sent()).length]).toBe(before);
|
|
250
|
-
return [2 /*return*/];
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
}); });
|
|
254
221
|
});
|
|
255
222
|
//# sourceMappingURL=UserActivityStamp.integration.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UserActivityStamp.integration.test.js","sourceRoot":"","sources":["../../test/UserActivityStamp.integration.test.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,kDAA4B;AAC5B,oCAA8C;AAC9C,
|
|
1
|
+
{"version":3,"file":"UserActivityStamp.integration.test.js","sourceRoot":"","sources":["../../test/UserActivityStamp.integration.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UserCacheDoesNotStampPresence.integration.test.d.ts","sourceRoot":"","sources":["../../test/UserCacheDoesNotStampPresence.integration.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
12
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
13
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
14
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
15
|
+
function step(op) {
|
|
16
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
17
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
18
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
19
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
20
|
+
switch (op[0]) {
|
|
21
|
+
case 0: case 1: t = op; break;
|
|
22
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
23
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
24
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
25
|
+
default:
|
|
26
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
27
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
28
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
29
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
30
|
+
if (t[2]) _.ops.pop();
|
|
31
|
+
_.trys.pop(); continue;
|
|
32
|
+
}
|
|
33
|
+
op = body.call(thisArg, _);
|
|
34
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
35
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
var db_1 = require("@proteinjs/db");
|
|
40
|
+
var user_1 = require("@proteinjs/user");
|
|
41
|
+
var userCache_1 = require("../src/authorization/userCache");
|
|
42
|
+
var UserServerTestEnvironment_1 = require("./UserServerTestEnvironment");
|
|
43
|
+
var testEnv = new UserServerTestEnvironment_1.UserServerTestEnvironment();
|
|
44
|
+
var activityRows = function (scope) { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
|
|
45
|
+
switch (_a.label) {
|
|
46
|
+
case 0: return [4 /*yield*/, (0, db_1.getDbAsSystem)().query(user_1.tables.UserActivity, { scope: scope })];
|
|
47
|
+
case 1: return [2 /*return*/, _a.sent()];
|
|
48
|
+
}
|
|
49
|
+
}); }); };
|
|
50
|
+
/**
|
|
51
|
+
* TRANSPORT IS NOT PRESENCE (UserActivityTable's contract; founder finding 2026-09-12 — every
|
|
52
|
+
* user on the admin usage page "last active today"). The per-request session-cache build
|
|
53
|
+
* (`userCache.create`) runs for EVERY request that rides a session cookie: an open tab's polls,
|
|
54
|
+
* a socket's room re-joins on every reconnect, the reload a deploy pushes onto every idle tab.
|
|
55
|
+
* None of that is a person. The build must therefore never write the presence stamp — the
|
|
56
|
+
* only door is the page's human-input report (UserPresence.recordPresence).
|
|
57
|
+
*
|
|
58
|
+
* Pre-fix, `userCache.create` stamped on every build: this test reads one row where the
|
|
59
|
+
* contract says none.
|
|
60
|
+
*/
|
|
61
|
+
describe('userCache.create — the session build does not stamp presence', function () {
|
|
62
|
+
beforeAll(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
63
|
+
return __generator(this, function (_a) {
|
|
64
|
+
switch (_a.label) {
|
|
65
|
+
case 0: return [4 /*yield*/, testEnv.beforeAll()];
|
|
66
|
+
case 1:
|
|
67
|
+
_a.sent();
|
|
68
|
+
return [2 /*return*/];
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}); });
|
|
72
|
+
afterAll(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
73
|
+
return __generator(this, function (_a) {
|
|
74
|
+
switch (_a.label) {
|
|
75
|
+
case 0: return [4 /*yield*/, testEnv.afterAll()];
|
|
76
|
+
case 1:
|
|
77
|
+
_a.sent();
|
|
78
|
+
return [2 /*return*/];
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}); });
|
|
82
|
+
it('resolving a human session (any request over a session cookie) writes NO user_activity row', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
83
|
+
var user, _i, _a, sessionId, resolved, _b;
|
|
84
|
+
return __generator(this, function (_c) {
|
|
85
|
+
switch (_c.label) {
|
|
86
|
+
case 0: return [4 /*yield*/, testEnv.createUser({ name: 'Idle Tab', email: 'idle-tab@test.local' })];
|
|
87
|
+
case 1:
|
|
88
|
+
user = _c.sent();
|
|
89
|
+
_i = 0, _a = ['poll', 'socket-rejoin', 'deploy-reload'];
|
|
90
|
+
_c.label = 2;
|
|
91
|
+
case 2:
|
|
92
|
+
if (!(_i < _a.length)) return [3 /*break*/, 5];
|
|
93
|
+
sessionId = _a[_i];
|
|
94
|
+
return [4 /*yield*/, userCache_1.userCache.create(sessionId, user.email)];
|
|
95
|
+
case 3:
|
|
96
|
+
resolved = _c.sent();
|
|
97
|
+
expect(resolved.id).toBe(user.id);
|
|
98
|
+
_c.label = 4;
|
|
99
|
+
case 4:
|
|
100
|
+
_i++;
|
|
101
|
+
return [3 /*break*/, 2];
|
|
102
|
+
case 5:
|
|
103
|
+
// The pre-fix stamp was fire-and-forget off the build — give it every chance to land.
|
|
104
|
+
return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 300); })];
|
|
105
|
+
case 6:
|
|
106
|
+
// The pre-fix stamp was fire-and-forget off the build — give it every chance to land.
|
|
107
|
+
_c.sent();
|
|
108
|
+
_b = expect;
|
|
109
|
+
return [4 /*yield*/, activityRows(user.id)];
|
|
110
|
+
case 7:
|
|
111
|
+
_b.apply(void 0, [_c.sent()]).toHaveLength(0);
|
|
112
|
+
return [2 /*return*/];
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}); });
|
|
116
|
+
});
|
|
117
|
+
//# sourceMappingURL=UserCacheDoesNotStampPresence.integration.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UserCacheDoesNotStampPresence.integration.test.js","sourceRoot":"","sources":["../../test/UserCacheDoesNotStampPresence.integration.test.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,oCAA8C;AAC9C,wCAA4D;AAC5D,4DAA2D;AAC3D,yEAAwE;AAExE,IAAM,OAAO,GAAG,IAAI,qDAAyB,EAAE,CAAC;AAEhD,IAAM,YAAY,GAAG,UAAO,KAAa;;gBACvC,qBAAM,IAAA,kBAAa,GAAE,CAAC,KAAK,CAAC,aAAM,CAAC,YAAY,EAAE,EAAE,KAAK,OAAA,EAAE,CAAC,EAAA;gBAA3D,sBAAA,SAA2D,EAAA;;SAAA,CAAC;AAE9D;;;;;;;;;;GAUG;AACH,QAAQ,CAAC,8DAA8D,EAAE;IACvE,SAAS,CAAC;;;wBACR,qBAAM,OAAO,CAAC,SAAS,EAAE,EAAA;;oBAAzB,SAAyB,CAAC;;;;SAC3B,CAAC,CAAC;IAEH,QAAQ,CAAC;;;wBACP,qBAAM,OAAO,CAAC,QAAQ,EAAE,EAAA;;oBAAxB,SAAwB,CAAC;;;;SAC1B,CAAC,CAAC;IAEH,EAAE,CAAC,2FAA2F,EAAE;;;;wBACjF,qBAAM,OAAO,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,qBAAqB,EAAE,CAAC,EAAA;;oBAAnF,IAAI,GAAG,SAA4E;0BAGvB,EAA1C,MAAC,MAAM,EAAE,eAAe,EAAE,eAAe,CAAC;;;yBAA1C,CAAA,cAA0C,CAAA;oBAAvD,SAAS;oBACD,qBAAM,qBAAS,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,EAAA;;oBAAxD,QAAQ,GAAG,SAA6C;oBAC9D,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;;;oBAFZ,IAA0C,CAAA;;;gBAIlE,sFAAsF;gBACtF,qBAAM,IAAI,OAAO,CAAC,UAAC,OAAO,IAAK,OAAA,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,EAAxB,CAAwB,CAAC,EAAA;;oBADxD,sFAAsF;oBACtF,SAAwD,CAAC;oBAEzD,KAAA,MAAM,CAAA;oBAAC,qBAAM,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,EAAA;;oBAAlC,kBAAO,SAA2B,EAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;;;;SACrD,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|