-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathget-group-tangle.test.js
322 lines (272 loc) · 10.2 KB
/
get-group-tangle.test.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
const { promisify: p } = require('util')
const test = require('tape')
const { Server, replicate } = require('../helpers')
const pull = require('pull-stream')
const paraMap = require('pull-paramap')
const { GetGroupTangle } = require('../../lib')
test('get-group-tangle unit test', t => {
const name = `get-group-tangle-${Date.now()}`
const server = Server({ name, debug: false })
// - creating a group and publishing messages (ssb-tribes)
server.tribes.create(null, (err, data) => {
if (err) throw err
const keystore = {
group: {
get (groupId) {
return { ...data, root: data.groupInitMsg.key } // rootMsgId
}
}
}
// NOTE: Tribes create callback with different data than keystore.group.get :(
// Somebody should probably fix that
// NOTE: Publishing has a queue which means if you publish many things in a row there is a delay before those values are in indexes to be queried.
const _getGroupTangle = GetGroupTangle(server, keystore)
const getGroupTangle = (id, cb) => {
setTimeout(() => _getGroupTangle(id, cb), 300)
}
getGroupTangle(data.groupId, (err, { root, previous }) => {
if (err) throw err
const rootKey = data.groupInitMsg.key
pull(
server.createUserStream({ id: server.id, reverse: true }),
pull.map(m => m.key),
pull.take(1),
pull.collect((err, keys) => {
if (err) throw err
t.deepEqual(
{ root, previous },
{ root: rootKey, previous: [keys[0]] },
'group add-member of admin should be the tip'
)
// publishing to the group:
const content = () => ({
type: 'memo',
root: data.groupId,
message: 'unneccessary',
recps: [data.groupId]
})
server.publish(content(), (err, msg) => {
if (err) throw err
getGroupTangle(data.groupId, (err, { root, previous }) => {
if (err) throw err
t.deepEqual({ root, previous }, { root: data.groupInitMsg.key, previous: [msg.key] }, 'adding message to root')
server.publish(content(), (err, msg) => {
if (err) throw err
getGroupTangle(data.groupId, (err, { root, previous }) => {
if (err) throw err
t.deepEqual({ root, previous }, { root: data.groupInitMsg.key, previous: [msg.key] }, 'adding message to tip')
server.close()
t.end()
})
})
})
})
})
)
})
})
})
test('get-group-tangle (cache)', t => {
const name = `get-group-tangle-cache-${Date.now()}`
const server = Server({ name })
let queryCalls = 0
server.backlinks.read.hook(function (read, args) {
queryCalls += 1
return read(...args)
})
server.tribes.create(null, (err, data) => {
if (err) throw err
t.equal(queryCalls, 1, 'no cache for publishing of group/add-member, a backlink query was run')
const content = { type: 'memo', recps: [data.groupId] }
server.publish(content, (err, msg) => {
if (err) throw err
t.equal(queryCalls, 1, 'cache used for publishing next message')
server.close()
t.end()
})
})
})
const n = 100
test(`get-group-tangle-${n}-publishes`, t => {
const publishArray = new Array(n).fill().map((item, i) => i)
const server = Server()
let count = 0
server.tribes.create(null, (err, data) => {
if (err) throw err
const groupId = data.groupId
pull(
pull.values(publishArray),
paraMap(
(value, cb) => server.publish({ type: 'memo', value, recps: [groupId] }, cb),
4
),
paraMap(
(msg, cb) => server.get({ id: msg.key, private: true, meta: true }, cb),
10
),
pull.drain(
(m) => {
count += (m.value.content.tangles.group.previous.length)
},
() => {
// t.equal(count, n, 'We expect there to be no branches in our groupTangle')
t.true(count < n * 8, 'We expect bounded branching with fast publishing')
server.close()
t.end()
}
)
)
})
})
test('get-group-tangle', t => {
const tests = [
{
plan: 4,
test: (t) => {
const DESCRIPTION = 'auto adds group tangle'
// this is an integration test, as we've hooked get-group-tangle into ssb.publish
const ssb = Server()
ssb.tribes.create(null, (err, data) => {
t.error(err, 'create group')
const groupRoot = data.groupInitMsg.key
const groupId = data.groupId
const content = {
type: 'yep',
recps: [groupId]
}
ssb.publish(content, (err, msg) => {
t.error(err, 'publish a message')
ssb.get({ id: msg.key, private: true }, (err, A) => {
t.error(err, 'get that message back')
t.deepEqual(
A.content.tangles.group, // actual
{ root: groupRoot, previous: [groupRoot] }, // expected
DESCRIPTION + ' (auto added tangles.group)'
)
ssb.close()
})
})
})
}
}
]
const toRun = tests.reduce((acc, round) => {
acc += round.plan || 1
return acc
}, 0)
t.plan(toRun)
tests.forEach(round => round.test(t))
})
test('get-group-tangle with branch', t => {
const alice = Server()
const bob = Server()
const name = (id) => {
switch (id) {
case alice.id: return 'alice'
case bob.id: return 'bob'
}
}
// Alice creates a group
alice.tribes.create(null, (err, data) => {
if (err) throw err
// Prepare to get Alice's group tangle from both servers
const keystore = {
group: {
get (groupId) {
return { ...data, root: data.groupInitMsg.key } // rootMsgId
}
}
}
const DELAY = 200
const _getAliceGroupTangle = GetGroupTangle(alice, keystore)
const getAliceGroupTangle = (id, cb) => {
setTimeout(() => _getAliceGroupTangle(id, cb), DELAY)
}
const _getBobGroupTangle = GetGroupTangle(bob, keystore)
const getBobGroupTangle = (id, cb) => {
setTimeout(() => _getBobGroupTangle(id, cb), DELAY)
}
// Alice invites Bob to the group
const aliceInvite = (...args) => {
// slow this step down so the group tangle cache has time to be update
// and be linear
setTimeout(() => alice.tribes.invite(...args), DELAY)
}
aliceInvite(data.groupId, [bob.id], { text: 'ahoy' }, (err, invite) => {
t.error(err, 'alice adds bob to group') // Not actually an error?
// Alice shares the group creation and invite with Bob.
replicate({ from: alice, to: bob, name }, (err) => {
if (err) throw err
// Both servers should see the same group tangle
getAliceGroupTangle(data.groupId, (err, aliceTangle) => {
if (err) throw err
getBobGroupTangle(data.groupId, (err, bobTangle) => {
if (err) throw err
t.deepEqual(aliceTangle, bobTangle, 'tangles should match')
t.deepEqual(aliceTangle.root, data.groupInitMsg.key, 'the root is the groupId')
t.deepEqual(aliceTangle.previous, [invite.key], 'previous is the invite key')
// Alice and Bob will both publish a message
const content = () => ({
type: 'memo',
message: 'branch',
recps: [data.groupId]
})
alice.publish(content(), (err, msg) => {
t.error(err, 'alice publishes a new message')
// NOTE With the content.recps we are adding we are asking Bob to know about a group before he's
// found out about it for himself
whenBobHasGroup(data.groupId, () => {
bob.publish(content(), (err, msg) => {
if (err) throw err
// Then Bob shares his message with Alice
replicate({ from: bob, to: alice, name }, (err) => {
if (err) throw err
// There should now be a branch in Alice's group tangle
getAliceGroupTangle(data.groupId, (err, aliceTangle) => {
if (err) throw err
t.deepEqual(aliceTangle.previous.length, 2, 'There should be two tips')
alice.close()
bob.close()
t.end()
})
})
})
})
})
})
})
})
})
})
function whenBobHasGroup (groupId, fn) {
bob.tribes.get(groupId, (err, data) => {
if (err) {
setTimeout(() => {
console.log('waiting for bob...')
whenBobHasGroup(groupId, fn)
}, 100)
} else fn()
})
}
})
test('members tangle', async t => {
const alice = Server()
const bob = Server()
const { groupId, root } = await p(alice.tribes.create)({})
const bobInvite = await p(alice.tribes.invite)(groupId, [bob.id], {})
const keystore = { group: { get: () => ({ root }) } }
const getGroupTangle = p(GetGroupTangle(alice, keystore))
const firstGroup = await getGroupTangle(groupId, 'group')
const firstMembers = await getGroupTangle(groupId, 'members')
t.deepEqual(firstGroup, { root, previous: [bobInvite.key] }, 'group tangle generated after add msg is correct')
t.deepEqual(firstMembers, { root, previous: [bobInvite.key] }, 'members tangle generated after add msg is correct')
const { key: bobExcludeKey } = await p(alice.tribes.excludeMembers)(groupId, [bob.id])
const bobExclude = await p(alice.get)({ id: bobExcludeKey, private: true })
t.deepEqual(bobExclude.content.tangles, { group: firstGroup, members: firstMembers }, 'exclude message gets tangles')
const secondGroup = await getGroupTangle(groupId, 'group')
const secondMembers = await getGroupTangle(groupId, 'members')
t.deepEqual(secondGroup, { root, previous: [bobExcludeKey] }, 'group tangle generated after exclude msg is correct')
t.deepEqual(secondMembers, { root, previous: [bobExcludeKey] }, 'members tangle generated after exclude msg is correct')
await Promise.all([p(alice.close)(), p(bob.close)()])
t.end()
})