Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/gitpod-db/src/workspace-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
import { fail } from "assert";
11
12
import { WorkspaceInstance, Workspace, PrebuiltWorkspace, CommitContext } from "@gitpod/gitpod-protocol";
13
import { testContainer } from "./test-container";
14
import { TypeORMWorkspaceDBImpl } from "./typeorm/workspace-db-impl";
15
import { TypeORM } from "./typeorm/typeorm";
16
import { DBPrebuiltWorkspace } from "./typeorm/entity/db-prebuilt-workspace";
17
import { secondsBefore } from "@gitpod/gitpod-protocol/lib/util/timeutil";
18
import { resetDB } from "./test/reset-db";
19
import { v4 } from "uuid";
20
21
@suite
22
class WorkspaceDBSpec {
23
db = testContainer.get<TypeORMWorkspaceDBImpl>(TypeORMWorkspaceDBImpl);
24
typeorm = testContainer.get<TypeORM>(TypeORM);
25
26
readonly timeWs = new Date(2018, 2, 16, 10, 0, 0).toISOString();
27
readonly timeBefore = new Date(2018, 2, 16, 11, 5, 10).toISOString();
28
readonly timeAfter = new Date(2019, 2, 16, 11, 5, 10).toISOString();
29
readonly timeAfter2 = new Date(2019, 2, 17, 4, 20, 10).toISOString();
30
readonly userId = "12345";
31
readonly projectAID = "projectA";
32
readonly projectBID = "projectB";
33
readonly orgidA = "orgA";
34
readonly orgidB = "orgB";
35
readonly ws: Workspace = {
36
id: "1",
37
type: "regular",
38
creationTime: this.timeWs,
39
config: {
40
ports: [],
41
image: "",
42
tasks: [],
43
},
44
projectId: this.projectAID,
45
context: <CommitContext>{
46
title: "example",
47
repository: {
48
cloneUrl: "https://github.com/gitpod-io/gitpod",
49
},
50
revision: "abc",
51
},
52
contextURL: "example.org",
53
description: "blabla",
54
ownerId: this.userId,
55
organizationId: this.orgidA,
56
};
57
readonly wsi1: WorkspaceInstance = {
58
workspaceId: this.ws.id,
59
id: "123",
60
ideUrl: "example.org",
61
region: "unknown",
62
workspaceClass: undefined,
63
workspaceImage: "abc.io/test/image:123",
64
creationTime: this.timeBefore,
65
startedTime: undefined,
66
deployedTime: undefined,
67
stoppingTime: undefined,
68
stoppedTime: undefined,
69
status: {
70
version: 1,
71
phase: "preparing",
72
conditions: {},
73
},
74
configuration: {
75
theiaVersion: "unknown",
76
ideImage: "unknown",
77
},
78
deleted: false,
79
usageAttributionId: undefined,
80
};
81
readonly wsi2: WorkspaceInstance = {
82
workspaceId: this.ws.id,
83
id: "1234",
84
ideUrl: "example.org",
85
region: "unknown",
86
workspaceClass: undefined,
87
workspaceImage: "abc.io/test/image:123",
88
creationTime: this.timeAfter,
89
startedTime: undefined,
90
deployedTime: undefined,
91
stoppingTime: undefined,
92
stoppedTime: undefined,
93
status: {
94
version: 1,
95
phase: "running",
96
conditions: {},
97
},
98
configuration: {
99
theiaVersion: "unknown",
100
ideImage: "unknown",
101
},
102
deleted: false,
103
usageAttributionId: undefined,
104
};
105
readonly wsi3: WorkspaceInstance = {
106
workspaceId: this.ws.id,
107
id: "12345",
108
ideUrl: "example.org",
109
region: "unknown",
110
workspaceClass: undefined,
111
workspaceImage: "abc.io/test/image:123",
112
creationTime: this.timeAfter2,
113
startedTime: undefined,
114
deployedTime: undefined,
115
stoppingTime: undefined,
116
stoppedTime: undefined,
117
status: {
118
version: 1,
119
phase: "stopped",
120
conditions: {},
121
},
122
configuration: {
123
theiaVersion: "unknown",
124
ideImage: "unknown",
125
},
126
deleted: false,
127
usageAttributionId: undefined,
128
};
129
readonly ws2: Workspace = {
130
id: "2",
131
type: "regular",
132
creationTime: this.timeWs,
133
config: {
134
ports: [],
135
image: "",
136
tasks: [],
137
},
138
projectId: this.projectBID,
139
context: <CommitContext>{
140
title: "example",
141
repository: {
142
cloneUrl: "https://github.com/gitpod-io/gitpod",
143
},
144
revision: "abc",
145
},
146
contextURL: "https://github.com/gitpod-io/gitpod",
147
description: "Gitpod",
148
ownerId: this.userId,
149
organizationId: this.orgidA,
150
};
151
readonly ws2i1: WorkspaceInstance = {
152
workspaceId: this.ws2.id,
153
id: "4",
154
ideUrl: "example.org",
155
region: "unknown",
156
workspaceClass: undefined,
157
workspaceImage: "abc.io/test/image:123",
158
creationTime: this.timeBefore,
159
startedTime: undefined,
160
deployedTime: undefined,
161
stoppingTime: undefined,
162
stoppedTime: undefined,
163
status: {
164
version: 1,
165
phase: "preparing",
166
conditions: {},
167
},
168
configuration: {
169
theiaVersion: "unknown",
170
ideImage: "unknown",
171
},
172
deleted: false,
173
usageAttributionId: undefined,
174
};
175
176
readonly ws3: Workspace = {
177
id: "3",
178
type: "regular",
179
creationTime: this.timeWs,
180
config: {
181
ports: [],
182
image: "",
183
tasks: [],
184
},
185
context: <CommitContext>{
186
title: "example",
187
repository: {
188
cloneUrl: "https://github.com/gitpod-io/gitpod",
189
},
190
revision: "abc",
191
},
192
contextURL: "example.org",
193
description: "blabla",
194
ownerId: this.userId,
195
organizationId: this.orgidB,
196
};
197
readonly ws3i1: WorkspaceInstance = {
198
workspaceId: this.ws3.id,
199
id: "3_1",
200
ideUrl: "example.org",
201
region: "unknown",
202
workspaceClass: undefined,
203
workspaceImage: "abc.io/test/image:123",
204
creationTime: this.timeBefore,
205
startedTime: undefined,
206
deployedTime: undefined,
207
stoppingTime: undefined,
208
stoppedTime: undefined,
209
status: {
210
version: 1,
211
phase: "preparing",
212
conditions: {},
213
},
214
configuration: {
215
theiaVersion: "unknown",
216
ideImage: "unknown",
217
},
218
deleted: false,
219
usageAttributionId: undefined,
220
};
221
222
async before() {
223
await this.wipeRepo();
224
}
225
226
async after() {
227
await this.wipeRepo();
228
}
229
230
async wipeRepo() {
231
await resetDB(this.typeorm);
232
}
233
234
@test(timeout(10000))
235
public async testFindInstancesLast() {
236
try {
237
await this.db.transaction(async (db) => {
238
await Promise.all([db.store(this.ws), db.storeInstance(this.wsi1), db.storeInstance(this.wsi2)]);
239
const dbResult = await db.findInstances(this.ws.id);
240
expect(dbResult).to.have.deep.members([this.wsi1, this.wsi2]);
241
throw "rollback";
242
});
243
} catch (e) {
244
if (e !== "rollback") throw e;
245
const dbResult = await this.db.findInstances(this.ws.id);
246
expect(dbResult).to.not.have.deep.members([this.wsi1, this.wsi2]);
247
return;
248
}
249
fail("Rollback failed");
250
}
251
252
@test(timeout(10000))
253
public async testFindByInstanceId() {
254
await this.db.transaction(async (db) => {
255
await Promise.all([db.store(this.ws), db.storeInstance(this.wsi1)]);
256
const dbResult = await db.findByInstanceId(this.wsi1.id);
257
const expected = await db.findById(this.wsi1.workspaceId);
258
expect(dbResult).to.deep.eq(expected);
259
});
260
}
261
262
@test(timeout(10000))
263
public async testFindEligibleWorkspacesForSoftDeletion_markedEligible_Prebuild() {
264
const { ws } = await this.createPrebuild(20, 15);
265
const dbResult = await this.db.findEligibleWorkspacesForSoftDeletion(new Date(), 10, "prebuild");
266
expect(dbResult.length).to.equal(1);
267
expect(dbResult[0].id).to.eq(ws.id);
268
expect(dbResult[0].ownerId).to.eq(ws.ownerId);
269
}
270
271
@test(timeout(10000))
272
public async testFindEligibleWorkspacesForSoftDeletion_notMarkedEligible_Prebuild() {
273
await this.createPrebuild(20, -7);
274
const dbResult = await this.db.findEligibleWorkspacesForSoftDeletion(new Date(), 10, "prebuild");
275
expect(dbResult.length).to.eq(0);
276
}
277
278
@test(timeout(10000))
279
public async testPrebuildGarbageCollection() {
280
const { pbws } = await this.createPrebuild(20, 15);
281
282
// mimic the behavior of the Garbage Collector
283
const gcWorkspaces = await this.db.findEligibleWorkspacesForSoftDeletion(new Date(), 10, "prebuild");
284
expect(gcWorkspaces.length).to.equal(1);
285
286
const now = new Date().toISOString();
287
await this.db.updatePartial(gcWorkspaces[0].id, {
288
contentDeletedTime: now,
289
softDeletedTime: now,
290
softDeleted: "gc",
291
});
292
293
// next cycle is empty
294
const nextGcCycle = await this.db.findEligibleWorkspacesForSoftDeletion(new Date(), 10, "prebuild");
295
expect(nextGcCycle.length).to.equal(0);
296
297
// prebuild can't be discovered anymore because it's workspace has been GC'ed
298
const prebuild = await this.db.findPrebuildByID(pbws.id);
299
expect(prebuild).to.be.undefined;
300
}
301
302
protected async createPrebuild(createdDaysAgo: number, deletionEligibilityTimeDaysAgo?: number) {
303
const now = new Date();
304
now.setDate(now.getDate() - createdDaysAgo);
305
const creationTime = now.toISOString();
306
const ws = await this.db.store({
307
id: "12345",
308
creationTime,
309
description: "something",
310
contextURL: "https://github.com/foo/bar",
311
ownerId: "1221423",
312
organizationId: "org123",
313
context: {
314
title: "my title",
315
},
316
config: {},
317
type: "prebuild",
318
});
319
const pbws = await this.db.storePrebuiltWorkspace({
320
id: "prebuild123",
321
buildWorkspaceId: "12345",
322
creationTime,
323
cloneURL: "https://github.com/foo/bar",
324
commit: "",
325
state: "available",
326
statusVersion: 0,
327
});
328
329
if (deletionEligibilityTimeDaysAgo !== undefined) {
330
const deletionEligibilityTime = new Date();
331
deletionEligibilityTime.setDate(deletionEligibilityTime.getDate() - deletionEligibilityTimeDaysAgo);
332
await this.db.updatePartial(ws.id, { deletionEligibilityTime: deletionEligibilityTime.toISOString() });
333
}
334
335
return { ws, pbws };
336
}
337
338
@test(timeout(10000))
339
public async testFindEligibleWorkspacesForSoftDeletion_markedEligible() {
340
this.ws.deletionEligibilityTime = this.timeWs;
341
await Promise.all([
342
this.db.store(this.ws),
343
this.db.storeInstance(this.wsi1),
344
this.db.storeInstance(this.wsi2),
345
this.db.storeInstance(this.wsi3),
346
]);
347
const dbResult = await this.db.findEligibleWorkspacesForSoftDeletion(new Date(this.timeAfter), 10);
348
expect(dbResult[0].id).to.eq(this.ws.id);
349
expect(dbResult[0].ownerId).to.eq(this.ws.ownerId);
350
}
351
352
@test(timeout(10000))
353
public async testFindEligibleWorkspacesForSoftDeletion_notMarkedEligible() {
354
await Promise.all([
355
this.db.store(this.ws),
356
this.db.storeInstance(this.wsi1),
357
this.db.storeInstance(this.wsi2),
358
this.db.storeInstance(this.wsi3),
359
]);
360
const dbResult = await this.db.findEligibleWorkspacesForSoftDeletion(new Date(this.timeAfter), 10);
361
expect(dbResult.length).to.eq(0);
362
}
363
364
@test(timeout(10000))
365
public async testFindAllWorkspaceAndInstances_workspaceId() {
366
await Promise.all([
367
this.db.store(this.ws),
368
this.db.storeInstance(this.wsi1),
369
this.db.store(this.ws2),
370
this.db.storeInstance(this.ws2i1),
371
]);
372
const dbResult = await this.db.findAllWorkspaceAndInstances(0, 10, "workspaceId", "DESC", {
373
workspaceId: this.ws2.id,
374
});
375
// It should only find one workspace instance
376
expect(dbResult.total).to.eq(1);
377
378
// It should find the workspace with the queried id
379
const workspaceAndInstance = dbResult.rows[0];
380
expect(workspaceAndInstance.workspaceId).to.eq(this.ws2.id);
381
}
382
383
@test(timeout(10000))
384
public async testFindAllWorkspaceAndInstances_workspaceIdOrInstanceId() {
385
await Promise.all([
386
this.db.store(this.ws),
387
this.db.storeInstance(this.wsi1),
388
this.db.store(this.ws2),
389
this.db.storeInstance(this.ws2i1),
390
]);
391
const dbResult = await this.db.findAllWorkspaceAndInstances(0, 10, "workspaceId", "DESC", {
392
instanceIdOrWorkspaceId: this.ws2.id,
393
});
394
// It should only find one workspace instance
395
expect(dbResult.total).to.eq(1);
396
397
// It should find the workspace with the queried id
398
const workspaceAndInstance = dbResult.rows[0];
399
expect(workspaceAndInstance.workspaceId).to.eq(this.ws2.id);
400
}
401
402
@test(timeout(10000))
403
public async testFindAllWorkspaceAndInstances_instanceId() {
404
await Promise.all([
405
this.db.store(this.ws),
406
this.db.storeInstance(this.wsi1),
407
this.db.storeInstance(this.wsi2),
408
this.db.store(this.ws2),
409
this.db.storeInstance(this.ws2i1),
410
]);
411
const dbResult = await this.db.findAllWorkspaceAndInstances(0, 10, "instanceId", "DESC", {
412
instanceId: this.wsi1.id,
413
});
414
415
// It should only find one workspace instance
416
expect(dbResult.total).to.eq(1);
417
418
// It should find the workspace with the queried id
419
const workspaceAndInstance = dbResult.rows[0];
420
expect(workspaceAndInstance.workspaceId).to.eq(this.ws.id);
421
422
// It should select the workspace instance that was queried, not the most recent one
423
expect(workspaceAndInstance.instanceId).to.eq(this.wsi1.id);
424
}
425
426
@test(timeout(10000))
427
public async testFind_ByProjectIds() {
428
await Promise.all([
429
this.db.store(this.ws),
430
this.db.storeInstance(this.wsi1),
431
this.db.storeInstance(this.wsi2),
432
this.db.store(this.ws2),
433
this.db.storeInstance(this.ws2i1),
434
]);
435
const dbResult = await this.db.find({
436
userId: this.userId,
437
includeHeadless: false,
438
projectId: [this.projectAID],
439
includeWithoutProject: false,
440
});
441
442
// It should only find one workspace instance
443
expect(dbResult.length).to.eq(1);
444
445
expect(dbResult[0].workspace.id).to.eq(this.ws.id);
446
}
447
448
@test(timeout(10000))
449
public async testFind_ByProjectIds_01() {
450
await Promise.all([
451
this.db.store(this.ws),
452
this.db.storeInstance(this.wsi1),
453
this.db.storeInstance(this.wsi2),
454
this.db.store(this.ws2),
455
this.db.storeInstance(this.ws2i1),
456
]);
457
const dbResult = await this.db.find({
458
userId: this.userId,
459
includeHeadless: false,
460
projectId: [this.projectBID],
461
includeWithoutProject: false,
462
});
463
464
// It should only find one workspace instance
465
expect(dbResult.length).to.eq(1);
466
467
expect(dbResult[0].workspace.id).to.eq(this.ws2.id);
468
}
469
470
@test(timeout(10000))
471
public async testFind_ByProjectIds_02() {
472
await Promise.all([
473
this.db.store(this.ws),
474
this.db.storeInstance(this.wsi1),
475
this.db.storeInstance(this.wsi2),
476
this.db.store(this.ws2),
477
this.db.storeInstance(this.ws2i1),
478
]);
479
const dbResult = await this.db.find({
480
userId: this.userId,
481
includeHeadless: false,
482
projectId: [this.projectAID, this.projectBID],
483
includeWithoutProject: false,
484
});
485
486
expect(dbResult.length).to.eq(2);
487
488
expect(dbResult[0].workspace.id).to.eq(this.ws.id);
489
expect(dbResult[1].workspace.id).to.eq(this.ws2.id);
490
}
491
492
@test(timeout(10000))
493
public async testFind_ByProjectIds_03() {
494
await Promise.all([
495
this.db.store(this.ws),
496
this.db.storeInstance(this.wsi1),
497
this.db.storeInstance(this.wsi2),
498
this.db.store(this.ws2),
499
this.db.storeInstance(this.ws2i1),
500
]);
501
const dbResult = await this.db.find({
502
userId: this.userId,
503
includeHeadless: false,
504
projectId: [],
505
includeWithoutProject: false,
506
});
507
508
expect(dbResult.length).to.eq(0);
509
510
// expect(dbResult[0].workspace.id).to.eq(this.ws.id);
511
// expect(dbResult[1].workspace.id).to.eq(this.ws2.id);
512
}
513
514
@test(timeout(10000))
515
public async testFind_ByProjectIds_04() {
516
await Promise.all([
517
this.db.store(this.ws),
518
this.db.storeInstance(this.wsi1),
519
this.db.storeInstance(this.wsi2),
520
this.db.store(this.ws2),
521
this.db.storeInstance(this.ws2i1),
522
this.db.store(this.ws3),
523
this.db.storeInstance(this.ws3i1),
524
]);
525
const dbResult = await this.db.find({
526
userId: this.userId,
527
includeHeadless: false,
528
projectId: [],
529
includeWithoutProject: true,
530
});
531
532
expect(dbResult.length).to.eq(1);
533
534
expect(dbResult[0].workspace.id).to.eq(this.ws3.id);
535
}
536
537
@test(timeout(10000))
538
public async testFind_ByProjectIds_05() {
539
await Promise.all([
540
this.db.store(this.ws),
541
this.db.storeInstance(this.wsi1),
542
this.db.storeInstance(this.wsi2),
543
this.db.store(this.ws2),
544
this.db.storeInstance(this.ws2i1),
545
this.db.store(this.ws3),
546
this.db.storeInstance(this.ws3i1),
547
]);
548
const dbResult = await this.db.find({
549
userId: this.userId,
550
includeHeadless: false,
551
projectId: [this.projectBID],
552
includeWithoutProject: true,
553
});
554
555
expect(dbResult.length).to.eq(2);
556
557
expect(dbResult[0].workspace.id).to.eq(this.ws2.id);
558
expect(dbResult[1].workspace.id).to.eq(this.ws3.id);
559
}
560
561
@test(timeout(10000))
562
public async testCountUnabortedPrebuildsSince() {
563
const now = new Date();
564
const cloneURL = "https://github.com/gitpod-io/gitpod";
565
const projectId = v4();
566
567
await Promise.all([
568
// Created now, and queued
569
this.storePrebuiltWorkspace({
570
id: "prebuild123",
571
buildWorkspaceId: "apples",
572
creationTime: now.toISOString(),
573
cloneURL: cloneURL,
574
projectId,
575
commit: "",
576
state: "queued",
577
statusVersion: 0,
578
}),
579
// now and aborted
580
this.storePrebuiltWorkspace({
581
id: "prebuild456",
582
buildWorkspaceId: "bananas",
583
creationTime: now.toISOString(),
584
cloneURL: cloneURL,
585
projectId,
586
commit: "",
587
state: "aborted",
588
statusVersion: 0,
589
}),
590
// completed over a minute ago
591
this.storePrebuiltWorkspace({
592
id: "prebuild789",
593
buildWorkspaceId: "oranges",
594
creationTime: secondsBefore(now.toISOString(), 62),
595
cloneURL: cloneURL,
596
projectId,
597
commit: "",
598
state: "available",
599
statusVersion: 0,
600
}),
601
// different project now and queued
602
this.storePrebuiltWorkspace({
603
id: "prebuild123-other",
604
buildWorkspaceId: "apples",
605
creationTime: now.toISOString(),
606
cloneURL: cloneURL,
607
projectId: "other-projectId",
608
commit: "",
609
state: "queued",
610
statusVersion: 0,
611
}),
612
]);
613
614
const minuteAgo = secondsBefore(now.toISOString(), 60);
615
const unabortedCount = await this.db.countUnabortedPrebuildsSince(projectId, new Date(minuteAgo));
616
expect(unabortedCount).to.eq(1);
617
}
618
619
@test(timeout(10000))
620
public async testGetWorkspaceCountForCloneURL() {
621
const now = new Date();
622
const eightDaysAgo = new Date();
623
eightDaysAgo.setDate(eightDaysAgo.getDate() - 8);
624
const activeRepo = "http://github.com/myorg/active.git";
625
const inactiveRepo = "http://github.com/myorg/inactive.git";
626
await Promise.all([
627
this.db.store({
628
id: "12345",
629
creationTime: eightDaysAgo.toISOString(),
630
description: "something",
631
contextURL: "http://github.com/myorg/inactive",
632
ownerId: "1221423",
633
organizationId: "org123",
634
context: <CommitContext>{
635
title: "my title",
636
repository: {
637
cloneUrl: inactiveRepo,
638
},
639
revision: "abc",
640
},
641
config: {},
642
type: "regular",
643
}),
644
this.db.store({
645
id: "12346",
646
creationTime: now.toISOString(),
647
description: "something",
648
contextURL: "http://github.com/myorg/active",
649
ownerId: "1221423",
650
organizationId: "org123",
651
context: <CommitContext>{
652
title: "my title",
653
repository: {
654
cloneUrl: activeRepo,
655
},
656
revision: "abc",
657
},
658
config: {},
659
type: "regular",
660
}),
661
]);
662
663
const inactiveCount = await this.db.getWorkspaceCountByCloneURL(inactiveRepo, 7, "regular");
664
expect(inactiveCount).to.eq(0, "there should be no regular workspaces in the past 7 days");
665
const activeCount = await this.db.getWorkspaceCountByCloneURL(activeRepo, 7, "regular");
666
expect(activeCount).to.eq(1, "there should be exactly one regular workspace");
667
}
668
669
@test(timeout(10000))
670
public async testGetUnresolvedUpdatables() {
671
{
672
// setup ws, wsi, pws, and updatables
673
const timeWithOffset = (offsetInHours: number) => {
674
const date = new Date();
675
date.setHours(date.getHours() - offsetInHours);
676
return date;
677
};
678
679
for (let i = 1; i <= 10; i++) {
680
const ws = await this.db.store({
681
...this.ws,
682
id: `ws-${i}`,
683
creationTime: timeWithOffset(i).toISOString(),
684
});
685
const pws = await this.db.storePrebuiltWorkspace({
686
buildWorkspaceId: ws.id,
687
cloneURL: ws.cloneUrl!,
688
commit: "abc",
689
creationTime: ws.creationTime,
690
id: ws.id + "-pws",
691
state: "queued",
692
statusVersion: 123,
693
});
694
await this.db.storeInstance({
695
...this.wsi1,
696
workspaceId: ws.id,
697
id: ws.id + "-wsi",
698
});
699
await this.db.attachUpdatableToPrebuild("pwsid-which-is-ignored-anyways", {
700
id: `pwu-${i}`,
701
installationId: "foobar",
702
isResolved: false,
703
owner: "owner",
704
repo: "repo",
705
prebuiltWorkspaceId: pws.id,
706
});
707
}
708
}
709
710
expect((await this.db.getUnresolvedUpdatables()).length).to.eq(10, "there should be 10 updatables in total");
711
expect((await this.db.getUnresolvedUpdatables(5)).length).to.eq(5, "there should be 5 updatables");
712
}
713
714
private async storePrebuiltWorkspace(pws: PrebuiltWorkspace) {
715
// store the creationTime directly, before it is modified by the store function in the ORM layer
716
const creationTime = pws.creationTime;
717
await this.db.storePrebuiltWorkspace(pws);
718
719
const conn = await this.typeorm.getConnection();
720
const repo = conn.getRepository(DBPrebuiltWorkspace);
721
722
if (!!creationTime) {
723
// MySQL requires the time format to be 2022-03-07 15:44:01.746141
724
// Looks almost like an ISO time string, hack it a bit.
725
const mysqlTimeFormat = creationTime.replace("T", " ").replace("Z", "");
726
await repo.query("UPDATE d_b_prebuilt_workspace SET creationTime = ? WHERE id = ?", [
727
mysqlTimeFormat,
728
pws.id,
729
]);
730
}
731
}
732
733
@test(timeout(10000))
734
public async findWorkspacesForPurging() {
735
const creationTime = "2018-01-01T00:00:00.000Z";
736
const ownerId = "1221423";
737
const organizationId = "org123";
738
const purgeDate = new Date("2019-02-01T00:00:00.000Z");
739
const d20180202 = "2018-02-02T00:00:00.000Z";
740
const d20180201 = "2018-02-01T00:00:00.000Z";
741
const d20180131 = "2018-01-31T00:00:00.000Z";
742
await Promise.all([
743
this.db.store({
744
id: "1",
745
creationTime,
746
description: "something",
747
contextURL: "http://github.com/myorg/inactive",
748
ownerId,
749
organizationId,
750
context: {
751
title: "my title",
752
},
753
config: {},
754
type: "regular",
755
contentDeletedTime: d20180131,
756
}),
757
this.db.store({
758
id: "2",
759
creationTime,
760
description: "something",
761
contextURL: "http://github.com/myorg/active",
762
ownerId,
763
organizationId,
764
context: {
765
title: "my title",
766
},
767
config: {},
768
type: "regular",
769
contentDeletedTime: d20180201,
770
}),
771
this.db.store({
772
id: "3",
773
creationTime,
774
description: "something",
775
contextURL: "http://github.com/myorg/active",
776
ownerId,
777
organizationId,
778
context: {
779
title: "my title",
780
},
781
config: {},
782
type: "regular",
783
contentDeletedTime: d20180202,
784
}),
785
this.db.store({
786
id: "4",
787
creationTime,
788
description: "something",
789
contextURL: "http://github.com/myorg/active",
790
ownerId,
791
organizationId,
792
context: {
793
title: "my title",
794
},
795
config: {},
796
type: "regular",
797
contentDeletedTime: undefined,
798
}),
799
]);
800
801
const wsIds = await this.db.findWorkspacesForPurging(365, 1000, purgeDate);
802
expect(wsIds).to.deep.equal([
803
{
804
id: "1",
805
ownerId,
806
contentDeletedTime: d20180131,
807
},
808
]);
809
}
810
811
@test(timeout(10000))
812
public async findWorkspacesByOrganizationId() {
813
await this.db.store(this.ws);
814
await this.db.store(this.ws2);
815
await this.db.store(this.ws3);
816
let result = await this.db.find({
817
userId: this.userId,
818
organizationId: this.orgidA,
819
});
820
821
expect(result.length).to.eq(2);
822
for (const ws of result) {
823
expect(ws.workspace.organizationId).to.equal(this.orgidA);
824
}
825
826
result = await this.db.find({
827
userId: this.userId,
828
organizationId: this.orgidB,
829
});
830
831
expect(result.length).to.eq(1);
832
for (const ws of result) {
833
expect(ws.workspace.organizationId).to.equal(this.orgidB);
834
}
835
836
result = await this.db.find({
837
userId: this.userId,
838
organizationId: "no-org",
839
});
840
841
expect(result.length).to.eq(0);
842
}
843
844
@test(timeout(10000))
845
public async hardDeleteWorkspace() {
846
await this.db.store(this.ws);
847
await this.db.storeInstance(this.wsi1);
848
await this.db.storeInstance(this.wsi2);
849
let result = await this.db.findInstances(this.ws.id);
850
expect(result.length).to.eq(2);
851
await this.db.hardDeleteWorkspace(this.ws.id);
852
result = await this.db.findInstances(this.ws.id);
853
expect(result.length).to.eq(0);
854
}
855
856
@test()
857
public async storeAndUpdateGitStatus() {
858
const inst = {
859
...this.wsi1,
860
gitstatus: undefined,
861
};
862
863
await this.db.storeInstance(inst);
864
let result = await this.db.findInstances(inst.workspaceId);
865
expect(!result[0].gitStatus).to.be.true;
866
867
inst.gitStatus = {
868
branch: "my/branch",
869
};
870
await this.db.storeInstance(inst);
871
872
result = await this.db.findInstances(inst.workspaceId);
873
expect(result[0].gitStatus?.branch).to.eq("my/branch");
874
}
875
}
876
module.exports = new WorkspaceDBSpec();
877
878