Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/gitpod-db/src/user-db.spec.db.ts
2497 views
1
/**
2
* Copyright (c) 2020 Gitpod GmbH. All rights reserved.
3
* Licensed under the GNU Affero General Public License (AGPL).
4
* See License.AGPL.txt in the project root for license information.
5
*/
6
7
import * as chai from "chai";
8
const expect = chai.expect;
9
import { suite, test, timeout } from "@testdeck/mocha";
10
11
import { GitpodTokenType, Identity, Workspace, WorkspaceInstance } from "@gitpod/gitpod-protocol";
12
import { testContainer } from "./test-container";
13
import { DBIdentity } from "./typeorm/entity/db-identity";
14
import { TypeORMUserDBImpl } from "./typeorm/user-db-impl";
15
import { TypeORMWorkspaceDBImpl } from "./typeorm/workspace-db-impl";
16
import { TypeORM } from "./typeorm/typeorm";
17
import { resetDB } from "./test/reset-db";
18
19
const _IDENTITY1: Identity = {
20
authProviderId: "GitHub",
21
authId: "1234",
22
authName: "gero",
23
deleted: false,
24
lastSigninTime: undefined,
25
primaryEmail: undefined,
26
readonly: false,
27
};
28
const _IDENTITY2: Identity = {
29
authProviderId: "GitHub",
30
authId: "4321",
31
authName: "gero",
32
deleted: false,
33
lastSigninTime: undefined,
34
primaryEmail: undefined,
35
readonly: false,
36
};
37
const WRONG_ID = "123"; // no uuid
38
39
@suite
40
class UserDBSpec {
41
db = testContainer.get<TypeORMUserDBImpl>(TypeORMUserDBImpl);
42
43
wsDb = testContainer.get<TypeORMWorkspaceDBImpl>(TypeORMWorkspaceDBImpl);
44
45
async before() {
46
await this.wipeRepos();
47
}
48
49
async after() {
50
await this.wipeRepos();
51
}
52
53
async wipeRepos() {
54
const typeorm = testContainer.get<TypeORM>(TypeORM);
55
await resetDB(typeorm);
56
}
57
58
// Copy to avoid pollution
59
get IDENTITY1() {
60
return Object.assign({}, _IDENTITY1);
61
}
62
get IDENTITY2() {
63
return Object.assign({}, _IDENTITY2);
64
}
65
66
@test(timeout(10000))
67
public async createUserAndFindById() {
68
let user = await this.db.newUser();
69
user.identities.push(this.IDENTITY1);
70
user = await this.db.storeUser(user);
71
72
const dbResult = await this.db.findUserById(user.id);
73
// We use 'user' as reference, so clean it
74
// @ts-ignore
75
user.identities.forEach((i) => delete (i as DBIdentity).user);
76
expect(dbResult).to.deep.include(user);
77
}
78
79
@test(timeout(10000))
80
public async createUserAndNotFindByWrongId() {
81
let user = await this.db.newUser();
82
user.identities.push(this.IDENTITY2);
83
user = await this.db.storeUser(user);
84
expect(await this.db.findUserById(WRONG_ID)).to.be.undefined;
85
}
86
87
@test(timeout(10000))
88
public async createUserAndFindByIdentity() {
89
let user = await this.db.newUser();
90
user.identities.push(this.IDENTITY1);
91
user = await this.db.storeUser(user);
92
93
const dbResult = await this.db.findUserByIdentity(this.IDENTITY1);
94
// We use 'user' as reference, so clean it
95
// @ts-ignore
96
user.identities.forEach((i) => delete (i as DBIdentity).user);
97
expect(dbResult).to.deep.include(user);
98
}
99
100
@test(timeout(10000))
101
public async findUserById() {
102
const user = await this.db.newUser();
103
user.identities.push(this.IDENTITY1);
104
await this.db.storeUser(user);
105
106
const foundUser = await this.db.findUserById(user.id);
107
expect(foundUser!.id).to.eq(user.id);
108
}
109
110
@test(timeout(10000))
111
public async findUserById_undefined() {
112
const user = await this.db.newUser();
113
user.identities.push(this.IDENTITY1);
114
await this.db.storeUser(user);
115
116
try {
117
await this.db.findUserById(undefined!);
118
expect.fail("Should have failed");
119
} catch (error) {
120
expect(error.code).to.eq(400);
121
}
122
try {
123
await this.db.findUserById("");
124
expect.fail("Should have failed");
125
} catch (error) {
126
expect(error.code).to.eq(400);
127
}
128
}
129
130
@test(timeout(10000))
131
public async findUsersByEmail_multiple_users_identities() {
132
let user1 = await this.db.newUser();
133
user1.name = "Old";
134
user1.identities.push(TestData.ID1);
135
user1.identities.push(TestData.ID2);
136
user1.identities.push(TestData.ID3);
137
user1 = await this.db.storeUser(user1);
138
139
await this.wsDb.store({
140
...TestData.DEFAULT_WS,
141
id: "1",
142
creationTime: new Date().toISOString(),
143
ownerId: user1.id,
144
});
145
await this.wsDb.storeInstance({
146
...TestData.DEFAULT_WSI,
147
workspaceId: "1",
148
id: "11",
149
creationTime: new Date().toISOString(),
150
});
151
152
// ensure that the second user's last modified is definitely after first one's
153
await new Promise((resolve) => setTimeout(resolve, 100));
154
155
let user2 = await this.db.newUser();
156
user2.name = "New";
157
user2.identities.push(TestData.ID4);
158
user2.identities.push(TestData.ID5);
159
user2 = await this.db.storeUser(user2);
160
161
await this.wsDb.store({
162
...TestData.DEFAULT_WS,
163
id: "2",
164
creationTime: new Date().toISOString(),
165
ownerId: user2.id,
166
});
167
await this.wsDb.storeInstance({
168
...TestData.DEFAULT_WSI,
169
workspaceId: "2",
170
id: "22",
171
creationTime: new Date().toISOString(),
172
});
173
174
const dbResult = await this.db.findUsersByEmail(TestData.primaryEmail);
175
176
expect(dbResult).to.be.an("array");
177
expect(dbResult).to.be.have.length(2);
178
expect(dbResult[0].name).to.be.eq("New");
179
expect(dbResult[1].name).to.be.eq("Old");
180
}
181
182
@test(timeout(10000))
183
public async findUserByIdentity_after_moving_identity() {
184
let user1 = await this.db.newUser();
185
user1.name = "ABC";
186
user1.identities.push(TestData.ID1);
187
user1.identities.push(TestData.ID2);
188
user1 = await this.db.storeUser(user1);
189
190
let user2 = await this.db.newUser();
191
user2.name = "XYZ";
192
user2.identities.push(TestData.ID3);
193
user2 = await this.db.storeUser(user2);
194
user2.identities.push(TestData.ID2);
195
user2 = await this.db.storeUser(user2);
196
197
const r2 = await this.db.findUserByIdentity(TestData.ID1);
198
expect(r2).to.be.not.undefined;
199
expect(r2!.identities).to.have.length(1);
200
201
const r1 = await this.db.findUserByIdentity(TestData.ID2);
202
expect(r1).to.be.not.undefined;
203
expect(r1!.name).to.be.eq("XYZ");
204
}
205
206
@test(timeout(10000))
207
public async findOrgOwnedUser_by_email() {
208
let orgUser1 = await this.db.newUser();
209
orgUser1.organizationId = "org1";
210
orgUser1.name = "Tester";
211
orgUser1.identities.push({
212
authId: "123",
213
authName: "Tester",
214
authProviderId: "oauth2-client-id",
215
primaryEmail: "[email protected]",
216
});
217
orgUser1 = await this.db.storeUser(orgUser1);
218
219
const result = await this.db.findOrgOwnedUser("org1", "[email protected]");
220
expect(result, "organizational user should be found").not.to.be.undefined;
221
expect(result!.identities, "should find a single identity").to.have.length(1);
222
223
const result2 = await this.db.findOrgOwnedUser("org1", "[email protected]");
224
expect(result2, "no user should be found").to.be.undefined;
225
}
226
227
@test(timeout(10000))
228
public async findTokenAndOwner() {
229
let user1 = await this.db.newUser();
230
user1.name = "ABC";
231
user1.identities.push(TestData.ID1);
232
user1.identities.push(TestData.ID2);
233
user1 = await this.db.storeUser(user1);
234
235
await this.db.storeGitpodToken({
236
tokenHash: "tokenhash",
237
created: new Date().toISOString(),
238
scopes: ["read-only"],
239
type: GitpodTokenType.API_AUTH_TOKEN,
240
userId: user1.id,
241
});
242
const result = await this.db.findUserByGitpodToken("tokenhash");
243
expect(result).to.not.be.undefined;
244
expect(result?.user.id).to.eq(user1.id);
245
expect(result?.token.userId).to.eq(user1.id);
246
}
247
248
@test(timeout(10000))
249
public async findGitpodTokenOfUser() {
250
let user1 = await this.db.newUser();
251
user1.name = "ABC";
252
user1.identities.push(TestData.ID1);
253
user1.identities.push(TestData.ID2);
254
user1 = await this.db.storeUser(user1);
255
256
const token = {
257
tokenHash: "tokenhash",
258
created: new Date().toISOString(),
259
scopes: ["read-only"],
260
type: GitpodTokenType.API_AUTH_TOKEN,
261
userId: user1.id,
262
};
263
await this.db.storeGitpodToken(token);
264
const result = await this.db.findGitpodTokensOfUser(user1.id, token.tokenHash);
265
expect(result).to.not.be.undefined;
266
expect(result?.userId).to.eq(user1.id);
267
expect(result?.tokenHash).to.eq(token.tokenHash);
268
}
269
270
@test(timeout(10000))
271
public async findAllGitpodTokensOfUser() {
272
let user1 = await this.db.newUser();
273
user1.name = "ABC";
274
user1.identities.push(TestData.ID1);
275
user1.identities.push(TestData.ID2);
276
user1 = await this.db.storeUser(user1);
277
278
const token = {
279
tokenHash: "tokenhash",
280
created: new Date().toISOString(),
281
scopes: ["read-only"],
282
type: GitpodTokenType.API_AUTH_TOKEN,
283
userId: user1.id,
284
};
285
await this.db.storeGitpodToken(token);
286
const token2 = {
287
tokenHash: "tokenhash2",
288
created: new Date().toISOString(),
289
scopes: ["read-only"],
290
type: GitpodTokenType.API_AUTH_TOKEN,
291
userId: user1.id,
292
};
293
await this.db.storeGitpodToken(token2);
294
const result = await this.db.findAllGitpodTokensOfUser(user1.id);
295
expect(result).to.not.be.undefined;
296
expect(result.length).to.eq(2);
297
expect(result.some((t) => t.tokenHash === token.tokenHash)).to.be.true;
298
expect(result.some((t) => t.tokenHash === token2.tokenHash)).to.be.true;
299
}
300
301
@test(timeout(10000))
302
public async findUserIdsNotYetMigratedToFgaVersion() {
303
let user1 = await this.db.newUser();
304
user1.name = "ABC";
305
user1.fgaRelationshipsVersion = 0;
306
user1 = await this.db.storeUser(user1);
307
308
let user2 = await this.db.newUser();
309
user2.name = "ABC2";
310
user2.fgaRelationshipsVersion = 1;
311
user2 = await this.db.storeUser(user2);
312
313
let user3 = await this.db.newUser();
314
user3.name = "ABC3";
315
user3.fgaRelationshipsVersion = 0;
316
user3 = await this.db.storeUser(user3);
317
318
const result = await this.db.findUserIdsNotYetMigratedToFgaVersion(1, 10);
319
expect(result).to.not.be.undefined;
320
expect(result.length).to.eq(2);
321
expect(result.some((id) => id === user1.id)).to.be.true;
322
expect(result.some((id) => id === user2.id)).to.be.false;
323
expect(result.some((id) => id === user3.id)).to.be.true;
324
325
const result2 = await this.db.findUserIdsNotYetMigratedToFgaVersion(1, 1);
326
expect(result2).to.not.be.undefined;
327
expect(result2.length).to.eq(1);
328
329
const result3 = await this.db.findUserIdsNotYetMigratedToFgaVersion(2, 10);
330
expect(result3).to.not.be.undefined;
331
expect(result3.length).to.eq(3);
332
}
333
}
334
335
namespace TestData {
336
export const primaryEmail = "[email protected]";
337
const DEFAULT: Identity = {
338
authProviderId: "Public-GitHub",
339
primaryEmail,
340
authId: "1234",
341
authName: "Foo Bar",
342
deleted: false,
343
readonly: false,
344
};
345
export const organizationId: string = "org1";
346
export const ID1: Identity = { ...DEFAULT, authId: "2345" };
347
export const ID2: Identity = { ...DEFAULT, authId: "3456", authProviderId: "Public-GitLab" };
348
export const ID3: Identity = { ...DEFAULT, authId: "4567", authProviderId: "ACME" };
349
export const ID4: Identity = { ...DEFAULT, authId: "5678" };
350
export const ID5: Identity = { ...DEFAULT, authId: "6789", authProviderId: "ACME" };
351
export const DEFAULT_WS: Workspace = {
352
id: "1",
353
type: "regular",
354
creationTime: new Date().toISOString(),
355
config: {
356
ports: [],
357
image: "",
358
tasks: [],
359
},
360
organizationId,
361
context: { title: "example" },
362
contextURL: "example.org",
363
description: "blabla",
364
ownerId: "12345",
365
};
366
export const DEFAULT_WSI: WorkspaceInstance = {
367
workspaceId: DEFAULT_WS.id,
368
id: "123",
369
ideUrl: "example.org",
370
region: "unknown",
371
workspaceImage: "abc.io/test/image:123",
372
creationTime: new Date().toISOString(),
373
startedTime: undefined,
374
deployedTime: undefined,
375
stoppedTime: undefined,
376
status: {
377
version: 1,
378
phase: "preparing",
379
conditions: {},
380
},
381
configuration: {
382
theiaVersion: "unknown",
383
ideImage: "unknown",
384
},
385
deleted: false,
386
};
387
}
388
389
module.exports = new UserDBSpec();
390
391