Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Avatar for KuCalc : devops.
Download
50671 views
1
###
2
TESTING of user queries specifically involving changefeeds - part 1 -- accounts, file_use, project_log
3
4
COPYRIGHT : (c) 2017 SageMath, Inc.
5
LICENSE : AGPLv3
6
###
7
8
async = require('async')
9
expect = require('expect')
10
11
pgtest = require('./pgtest')
12
db = undefined
13
setup = (cb) -> (pgtest.setup (err) -> db=pgtest.db; cb(err))
14
teardown = pgtest.teardown
15
{create_accounts, create_projects, changefeed_series} = pgtest
16
misc = require('smc-util/misc')
17
18
describe 'test the accounts table changefeed', ->
19
before(setup)
20
after(teardown)
21
account_id = undefined
22
changefeed_id = misc.uuid()
23
24
it 'writes to user accounts table and verify that change automatically appears in changefeed', (done) ->
25
async.series([
26
(cb) ->
27
db.create_account(first_name:"Sage", last_name:"Math", created_by:"1.2.3.4",\
28
email_address:"[email protected]", password_hash:"blah", cb:\
29
(err, x) -> account_id=x; cb(err))
30
(cb) ->
31
db.user_query
32
account_id : account_id
33
query : {accounts:[{account_id:account_id, first_name:null}]}
34
changes : changefeed_id
35
cb : changefeed_series([
36
(x, cb) ->
37
db.user_query
38
account_id : account_id
39
query : {accounts:{account_id:account_id, first_name:'SAGE!'}}
40
cb : cb
41
(x, cb) ->
42
expect(x).toEqual({ action:'update', new_val: { account_id:account_id, first_name:'SAGE!'}})
43
db.delete_account(account_id:account_id, cb:cb)
44
(x, cb) ->
45
expect(x).toEqual({ action: 'delete', old_val: { account_id:account_id } })
46
db.user_query_cancel_changefeed(id:changefeed_id, cb:cb)
47
], cb)
48
], (err) ->
49
if err
50
done(err)
51
else
52
done()
53
)
54
55
describe 'test changefeeds involving the file_use table on one project with one user', ->
56
before(setup)
57
after(teardown)
58
59
accounts = []
60
projects = []
61
it 'create accounts and projects', (done) ->
62
async.series([
63
(cb) ->
64
create_accounts 1, (err, x) -> accounts=x; cb()
65
(cb) ->
66
create_projects 1, accounts[0], (err, x) -> projects.push(x...); cb(err)
67
], done)
68
69
it 'tests a changefeed on the file_use table for a single project', (done) ->
70
id = misc.uuid()
71
t0 = new Date()
72
t1 = t2 = undefined
73
obj = {project_id:projects[0], path: 'foo.txt', users:{"#{accounts[0]}":{'read':t0}, last_edited:t0}}
74
obj2 = {project_id:projects[0], path: 'foo2.txt', last_edited:new Date()}
75
db.user_query
76
account_id : accounts[0]
77
query : {file_use:[{id: null, project_id:projects[0], path: null, users: null, last_edited: null}]}
78
changes : id
79
cb : changefeed_series([
80
(x, cb) ->
81
expect(x).toEqual({ file_use: [] }) # how it starts
82
db.user_query
83
account_id : accounts[0]
84
query : {file_use:obj}
85
cb : cb
86
(x, cb) ->
87
obj.id = db.sha1(obj.project_id, obj.path)
88
expect(x).toEqual({action:'insert', new_val: obj })
89
90
# now mutate it by chatting on it
91
t1 = new Date()
92
db.user_query
93
account_id : accounts[0]
94
query : {file_use:{project_id:projects[0], path: 'foo.txt', users:{"#{accounts[0]}":{'chat':t1}}}}
95
cb : cb
96
(x, cb) ->
97
# note how chat gets recursively MERGED IN -- not replacing users. (so tricky under the hood to implement...)
98
obj.users["#{accounts[0]}"].chat = t1
99
expect(x).toEqual({action:'update', new_val:obj})
100
101
# now mutate it by updating last_edited
102
t2 = new Date()
103
db.user_query
104
account_id : accounts[0]
105
query : {file_use:{project_id:projects[0], path: 'foo.txt', last_edited:t2}}
106
cb : cb
107
(x, cb) ->
108
# note how chat gets recursively MERGED IN -- not replacing users. (so tricky under the hood to implement...)
109
obj.last_edited = t2
110
expect(x).toEqual({action:'update', new_val: obj})
111
112
# add a second file_use entry
113
db.user_query
114
account_id : accounts[0]
115
query : {file_use:obj2}
116
cb : cb
117
118
(x, cb) ->
119
obj2.id = db.sha1(obj2.project_id, obj2.path)
120
expect(x).toEqual({action:'insert', new_val:obj2})
121
122
# now delete the first entry (not through file_use, but directly)
123
db._query
124
query : "DELETE FROM file_use"
125
where : {'id = $': obj.id}
126
cb : cb
127
(x, cb) ->
128
expect(x).toEqual({action:"delete", old_val:{id:obj.id, project_id:projects[0]}})
129
130
# and the second
131
db._query
132
query : "DELETE FROM file_use"
133
where : {'id = $': obj2.id}
134
cb : cb
135
136
(x, cb) ->
137
expect(x).toEqual({action:"delete", old_val:{id:obj2.id, project_id:projects[0]}})
138
139
db.user_query_cancel_changefeed(id:id, cb:cb)
140
], done)
141
142
describe 'test file_use changefeeds with multiple projects', ->
143
before(setup)
144
after(teardown)
145
146
accounts = []
147
projects = []
148
it 'create account and projects', (done) ->
149
async.series([
150
(cb) ->
151
create_accounts 2, (err, x) -> accounts=x; cb()
152
(cb) ->
153
create_projects 2, accounts[0], (err, x) -> projects.push(x...); cb(err)
154
(cb) ->
155
create_projects 1, accounts[1], (err, x) -> projects.push(x...); cb(err)
156
], done)
157
158
it 'insert into file_use for three separate projects', (done) ->
159
id = misc.uuid()
160
t = [misc.minutes_ago(10), misc.minutes_ago(5), misc.minutes_ago(3)]
161
obj = [
162
{project_id:projects[0], path: 'file-in-project0.txt', users:{"#{accounts[0]}":{'read':t[0]}}},
163
{project_id:projects[1], path: 'file-in-project1.txt', users:{"#{accounts[0]}":{'chat':t[1]}}},
164
{project_id:projects[2], path: 'file-in-project2.txt', users:{"#{accounts[1]}":{'chat':t[2]}}}
165
]
166
for x in obj
167
x.id = db.sha1(x.project_id, x.path)
168
169
db.user_query
170
account_id : accounts[0]
171
query : {file_use:[{id: null, project_id:null, path: null, users: null, last_edited: null}]}
172
changes : id
173
cb : changefeed_series([
174
(x, cb) ->
175
expect(x).toEqual({ file_use: [] }) # how it starts
176
db.user_query # insert first object
177
account_id : accounts[0]
178
query : {file_use:obj[0]}
179
cb : cb
180
(x, cb) ->
181
expect(x).toEqual({action:'insert', new_val: obj[0]})
182
183
db.user_query # insert second object
184
account_id : accounts[0]
185
query : {file_use:obj[1]}
186
cb : cb
187
(x, cb) ->
188
expect(x).toEqual({action:'insert', new_val: obj[1]})
189
190
db.user_query # insert third object, which should NOT trigger change
191
account_id : accounts[1]
192
query : {file_use:obj[2]}
193
cb : () =>
194
obj[1].last_edited = new Date()
195
db.user_query # insert second object again, modified
196
account_id : accounts[0]
197
query : {file_use:obj[1]}
198
cb : cb
199
200
(x, cb) ->
201
expect(x).toEqual({action:'update', new_val: obj[1]})
202
cb()
203
], done)
204
205
describe 'modifying a single file_use record in various ways', ->
206
before(setup)
207
after(teardown)
208
209
accounts = []
210
projects = []
211
it 'create account and projects', (done) ->
212
async.series([
213
(cb) ->
214
# 2 accounts
215
create_accounts 2, (err, x) -> accounts=x; cb()
216
(cb) ->
217
# 1 project with both accounts using it
218
create_projects 1, accounts, (err, x) -> projects.push(x...); cb(err)
219
], done)
220
221
it 'insert a file_use entry and modify in various ways', (done) ->
222
changefeed_id = misc.uuid()
223
time = new Date()
224
obj = {project_id:projects[0], path: 'file-in-project0.txt', users:{"#{accounts[0]}":{'read':time}}}
225
obj.id = db.sha1(obj.project_id, obj.path)
226
227
db.user_query
228
account_id : accounts[0]
229
query : {file_use:[{id: null, project_id:null, path: null, users: null, last_edited: null}]}
230
changes : changefeed_id
231
cb : changefeed_series([
232
(x, cb) ->
233
expect(x).toEqual({ file_use: [] }) # how it starts
234
cb()
235
db.user_query(account_id: accounts[0], query: {file_use:obj}, cb: cb)
236
(x, cb) ->
237
expect(x).toEqual({action:'insert', new_val: obj})
238
239
obj.last_edited = new Date()
240
db.user_query(account_id: accounts[0], query: {file_use:obj}, cb: cb)
241
(x, cb) ->
242
expect(x).toEqual({action:'update', new_val: obj})
243
244
obj.users[accounts[0]].chat = new Date()
245
db.user_query(account_id: accounts[0], query: {file_use:obj}, cb: cb)
246
(x, cb) ->
247
expect(x).toEqual({action:'update', new_val: obj})
248
249
obj.users[accounts[1]] = {seen: new Date()}
250
db.user_query(account_id: accounts[1], query: {file_use:obj}, cb: cb)
251
(x, cb) ->
252
expect(x).toEqual({action:'update', new_val: obj})
253
254
obj.users[accounts[0]] = {chat: new Date(), read: new Date()}
255
db.user_query(account_id: accounts[0], query: {file_use:obj}, cb: cb)
256
(x, cb) ->
257
expect(x).toEqual({action:'update', new_val: obj})
258
259
db._query
260
query : "DELETE FROM file_use"
261
where : {'id = $': obj.id}
262
cb : cb
263
(x, cb) ->
264
expect(x).toEqual({action:"delete", old_val:{id:obj.id, project_id:obj.project_id}})
265
266
# Cancel the changefeed...
267
db.user_query_cancel_changefeed
268
id : changefeed_id
269
cb : cb
270
(x, cb) ->
271
expect(x).toEqual({action:'close'})
272
cb()
273
], done)
274
275
describe 'test changefeeds with project_log', ->
276
before(setup)
277
after(teardown)
278
279
accounts = []
280
projects = []
281
it 'create accounts and projects', (done) ->
282
async.series([
283
(cb) ->
284
create_accounts 2, (err, x) -> accounts=x; cb()
285
(cb) ->
286
create_projects 2, accounts[0], (err, x) -> projects.push(x...); cb(err)
287
(cb) ->
288
create_projects 1, accounts[1], (err, x) -> projects.push(x...); cb(err)
289
], done)
290
291
it 'tests a simple project_log changefeed on a single project', (done) ->
292
obj = {id:misc.uuid(), project_id:projects[0], time:new Date(), account_id:accounts[0], event:{sample:'thing'}}
293
changefeed_id = misc.uuid()
294
db.user_query
295
account_id : accounts[0]
296
query : {project_log:[{id: null, project_id:projects[0], time: null, account_id: null, event: null}]}
297
changes : changefeed_id
298
cb : changefeed_series([
299
(x, cb) ->
300
expect(x).toEqual({project_log : [] }) # how it starts
301
db.user_query
302
account_id : accounts[0]
303
query : {project_log:obj}
304
cb : cb
305
(x, cb) ->
306
expect(x).toEqual({action:'insert', new_val: obj })
307
db.user_query_cancel_changefeed(id : changefeed_id, cb : cb)
308
(x, cb) ->
309
# check that the next thing we get is close, not the insert we just made above.
310
expect(x).toEqual({action:'close'})
311
cb()
312
], done)
313
314
it "tests a project_log changefeed on all of a user's project", (done) ->
315
obj1 = {id:misc.uuid(), project_id:projects[1], time:new Date(), account_id:accounts[0], event:{stuff:[1,2]}}
316
obj2 = {id:misc.uuid(), project_id:projects[2], time:new Date(), account_id:accounts[1], event:{user:1}}
317
changefeed_id = misc.uuid()
318
db.user_query
319
account_id : accounts[0]
320
query : {project_log:[{id: null, project_id:null, time: null, account_id: null, event: null}]}
321
changes : changefeed_id
322
cb : changefeed_series([
323
(x, cb) ->
324
expect(x.project_log.length).toEqual(1) # 1 because of one added in previous test above
325
326
# insert object in projects[1]
327
db.user_query
328
account_id : accounts[0]
329
query : {project_log:obj1}
330
cb : cb
331
(x, cb) ->
332
# and see it appear
333
expect(x).toEqual({action:'insert', new_val: obj1 })
334
335
# insert object in projects[2], which has nothing to do with accounts[0]
336
db.user_query
337
account_id : accounts[1]
338
query : {project_log:obj2}
339
cb : (err) ->
340
if err
341
cb(err)
342
else
343
# close the changefeed
344
db.user_query_cancel_changefeed
345
id : changefeed_id
346
cb : cb
347
(x, cb) ->
348
# check that the next thing we get is close, not the insert we just made above.
349
expect(x).toEqual({action:'close'})
350
cb()
351
], done)
352
353
describe 'test time-constrained changefeed on project_log', ->
354
before(setup)
355
after(teardown)
356
357
accounts = []
358
projects = []
359
it 'create account and project', (done) ->
360
async.series([
361
(cb) ->
362
create_accounts 1, (err, x) -> accounts=x; cb()
363
(cb) ->
364
create_projects 1, accounts[0], (err, x) -> projects.push(x...); cb(err)
365
], done)
366
367
it "creates changefeed with time constraint by making two entries, one satisfying the constraint, and one not", (done) ->
368
changefeed_id = misc.uuid()
369
obj0 = {id:misc.uuid(), project_id:projects[0], time:new Date(), event:{foo:'bar0'}}
370
obj1 = {id:misc.uuid(), project_id:projects[0], time:misc.days_ago(2), event:{foo:'bar1'}}
371
obj2 = {id:misc.uuid(), project_id:projects[0], time:new Date(), event:{foo:'bar2'}}
372
db.user_query
373
account_id : accounts[0]
374
query : {project_log:[{id: null, project_id:null, time:{'>=':misc.days_ago(1)}, event: null}]}
375
changes : changefeed_id
376
cb : changefeed_series([
377
(x, cb) ->
378
expect(x.project_log.length).toEqual(0)
379
# insert object that satisfies time constraint
380
db.user_query(account_id: accounts[0], query: {project_log:obj0}, cb: cb)
381
(x, cb) ->
382
# and see it appear
383
expect(x).toEqual({action:'insert', new_val:obj0})
384
# insert old object that does not satisfy time constraint
385
db.user_query account_id: accounts[0], query: {project_log:obj1}, cb: (err) =>
386
if err
387
cb(err)
388
else
389
# then one that does satisfy constraint
390
db.user_query(account_id: accounts[0], query: {project_log:obj2}, cb: cb)
391
(x, cb) ->
392
# see that *only* the new one appears
393
expect(x).toEqual({action:'insert', new_val:obj2})
394
395
# modify the first obj so its timestamp is old, and see it gets updated to be removed (so only old_val is set)
396
obj3 = misc.deep_copy(obj0)
397
obj3.time = misc.days_ago(60)
398
db.user_query(account_id: accounts[0], query: {project_log:obj3}, cb:cb)
399
(x, cb) ->
400
expect(x).toEqual({action:"delete", old_val:misc.copy_without(obj0, 'event')})
401
cb()
402
], done)
403
404
405
406
407
408