-
Notifications
You must be signed in to change notification settings - Fork 0
/
post.integration.test.ts
146 lines (113 loc) · 7.57 KB
/
post.integration.test.ts
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
import { Peerbit } from '@dao-xyz/peerbit';
import { Post, Posts } from '../post';
import { Range } from '@dao-xyz/peerbit-string';
import { PageQueryRequest, Results, ResultWithSource } from '@dao-xyz/peerbit-anysearch';
import { Session } from '@dao-xyz/peerbit-test-utils';
import { Access, AccessType, DynamicAccessController, PublicKeyAccessCondition } from '@dao-xyz/peerbit-dynamic-access-controller';
import { waitFor } from '@dao-xyz/peerbit-time';
import { X25519PublicKey, Ed25519PublicKey } from '@dao-xyz/peerbit-crypto'
import { HeadsMessage, LogQueryRequest, LogEntryEncryptionQuery } from '@dao-xyz/peerbit-logindex';
// @ts-ignore
import { v4 as uuid } from 'uuid';
import { ProgramContent } from '../content';
import { CollaborativeText } from '../post-types';
describe('node', () => {
let session: Session, peer: Peerbit, peer2: Peerbit, peer3: Peerbit
beforeEach(async () => {
session = await Session.connected(3, 'js-ipfs')
peer = await Peerbit.create(session.peers[0].ipfs, { directory: './tmp/' + uuid(), waitForKeysTimout: 0 });
peer2 = await Peerbit.create(session.peers[1].ipfs, { directory: './tmp/' + uuid(), waitForKeysTimout: 0 });
peer3 = await Peerbit.create(session.peers[2].ipfs, { directory: './tmp/' + uuid(), waitForKeysTimout: 0 });
})
afterEach(async () => {
try {
await peer?.disconnect();
await peer2?.disconnect();
await peer3?.disconnect();
await session?.stop();
} catch (error) {
}
})
it('can create post with 2 editors and 1 relay', async () => {
const replicationTopic = uuid();
// Relay
await peer2.subscribeToReplicationTopic(replicationTopic);
// Peer 1 initiates a "conversation"
let posts = await peer.open(new Posts(), { replicationTopic });
let post = new Post({ acl: new DynamicAccessController({ rootTrust: peer.identity.publicKey }), content: new CollaborativeText() }); // new DString({ search: new AnySearch({ query: new DQuery({}) }) })
await posts.posts.put(post);
post = await peer.open(post, { replicationTopic });
await post.getContent<ProgramContent>().getProgram<CollaborativeText>().text.add("hello", new Range({ offset: 0, length: "hello".length }))
// Add access policy so peer 3 can edit
await post.acl?.access.put(new Access({ accessTypes: [AccessType.Any], accessCondition: new PublicKeyAccessCondition({ key: peer3.identity.publicKey }) }))
// Peer 3 joins later, and will not get messages directly, because it has not yet started subscribing
// hence instead Peer3 lists all posts and edits using a query
const posts3 = await peer3.open<Posts>(posts.address, { replicate: false, replicationTopic })
let results: Results = undefined as any;
await posts3.posts.index.search.query(new PageQueryRequest({ queries: [] }), (r) => {
results = r;
}, {
waitForAmount: 1,
});
// Peer 3 now opens the database with the post that it has found
let postFromResult = (results.results[0] as ResultWithSource).source as Post;
postFromResult = await peer3.open<Post>(postFromResult, { replicate: false, replicationTopic })
// Peer 3 modifies the post
await (postFromResult.getContent<ProgramContent>()).getProgram<CollaborativeText>().text.add(" world", new Range({ offset: "hello".length, length: " world".length }))
// Peer 1 gets the change immidiately since it is already replicating
await waitFor(() => (post.getContent<ProgramContent>().getProgram<CollaborativeText>().text.store.oplog.values.length) === 2);
expect(await (post.getContent<ProgramContent>().getProgram<CollaborativeText>().text.toString())).toEqual('hello world');
})
it('can create E2EE post with 2 two peers and relays', async () => {
const replicationTopic = uuid();
// Relay
await peer2.subscribeToReplicationTopic(replicationTopic);
const encryptionReaders = [peer.identity.publicKey as Ed25519PublicKey, peer3.identity.publicKey as Ed25519PublicKey];
// Peer 1 initiates a "conversation"
let posts = await peer.open(new Posts(), { replicationTopic });
let post = new Post({ acl: new DynamicAccessController({ rootTrust: peer.identity.publicKey }), content: new CollaborativeText() });
await posts.posts.put(post, { reciever: { clock: undefined, signature: encryptionReaders, payload: encryptionReaders } });
// Wait for the replicator to recieve the encrypted post
await waitFor(() => (peer2.programs[replicationTopic][posts.address.toString()]?.program as Posts)?.posts.store.oplog.values.length === 1);
// Write encrypted message
post = await peer.open(post, { replicate: false, replicationTopic });
await (post.getContent<ProgramContent>().getProgram<CollaborativeText>()).text.add("hello", new Range({ offset: 0, length: "hello".length }), {
reciever: {
payload: encryptionReaders,
signature: encryptionReaders,
clock: undefined,
}
})
// Add access policy so peer 3 can edit
await post.acl?.access.put(new Access({ accessTypes: [AccessType.Any], accessCondition: new PublicKeyAccessCondition({ key: peer3.identity.publicKey }) }))
await waitFor(() => (peer2.programs[replicationTopic][posts.address.toString()]?.program as Posts)?.posts.store.oplog.values.length === 1);
// Peer 3 joins later and posts which can be red
const posts3 = await peer3.open<Posts>(posts.address, { replicate: false, replicationTopic })
// Peer 3 joins later, and will not get messages directly, because it has not yet started subscribing
// hence instead Peer3 lists all posts and edits using a query. This time however, in comparison to the unencrypted test above,
// we have to query for meta data ("find all logs which I can read")
let heads: HeadsMessage = undefined as any;
await posts3.posts.logIndex.query.query(new LogQueryRequest({
queries: [
new LogEntryEncryptionQuery({ clock: [], payload: [await X25519PublicKey.from(peer3.identity.publicKey as Ed25519PublicKey)], signature: [await X25519PublicKey.from(peer3.identity.publicKey as Ed25519PublicKey)] })
]
}), (r) => {
heads = r;
}, {
waitForAmount: 1
});
expect(posts3.posts.store.oplog.values).toHaveLength(0);
await posts3.posts.store.sync(heads.heads) // update the state of the new intel
await waitFor(() => posts3.posts.index.size === 1);
// Peer 3 modifies the post
// manually find the new posts (will only be one), and adds content to the text
for (let post of posts3.posts.index._index.values()) {
let openPost = await peer3.open<Post>(post.value, { replicate: false, replicationTopic })
await (openPost.getContent<ProgramContent>().getProgram<CollaborativeText>()).text.add(" world", new Range({ offset: "hello".length, length: " world".length }), { reciever: { clock: undefined, signature: undefined, payload: encryptionReaders } })
}
// Peer 1 gets the change immidiately since it is already replicating
await waitFor(() => ((post.getContent<ProgramContent>().getProgram<CollaborativeText>()).text.store.oplog.values.length) === 2);
const toString = await ((post.getContent<ProgramContent>().getProgram<CollaborativeText>()).text.toString());
expect(toString).toEqual('hello world');
})
});