Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/backend/conat/test/socket/restarts.test.ts
1451 views
1
/*
2
3
pnpm test `pwd`/restarts.test.ts
4
5
*/
6
7
import {
8
before,
9
after,
10
connect,
11
restartServer,
12
setDefaultTimeouts,
13
} from "@cocalc/backend/conat/test/setup";
14
import { once } from "@cocalc/util/async-utils";
15
16
beforeAll(async () => {
17
await before();
18
setDefaultTimeouts({ request: 750, publish: 750 });
19
});
20
21
jest.setTimeout(15000);
22
23
describe("create a client and server and socket, verify it works, restart conat server, then confirm that socket still works", () => {
24
const SUBJECT = "reconnect.one";
25
26
let client,
27
server,
28
cn1,
29
cn2,
30
sockets: any[] = [];
31
32
it("creates the client and server and confirms it works", async () => {
33
cn1 = connect();
34
server = cn1.socket.listen(SUBJECT);
35
server.on("connection", (socket) => {
36
sockets.push(socket);
37
socket.on("data", (data) => {
38
socket.write(`${data}`.repeat(2));
39
});
40
socket.on("request", (mesg) => {
41
mesg.respond("hello");
42
});
43
});
44
cn2 = connect();
45
client = cn2.socket.connect(SUBJECT);
46
47
const iter = client.iter();
48
client.write("cocalc");
49
const { value } = await iter.next();
50
expect(value[0]).toBe("cocalccocalc");
51
52
expect((await client.request(null)).data).toBe("hello");
53
});
54
55
async function waitForClientsToReconnect() {
56
await Promise.all([once(cn1, "connected"), once(cn2, "connected")]);
57
}
58
59
it("restarts the conat socketio server, wait for clients to reconnect, and test sending data over socket", async () => {
60
await restartServer();
61
await waitForClientsToReconnect();
62
// sending data over socket
63
const iter = client.iter();
64
client.write("test");
65
const { value, done } = await iter.next();
66
expect(done).toBe(false);
67
expect(value[0]).toBe("testtest");
68
});
69
70
let socketDisconnects: string[] = [];
71
it("also request/respond immediately works", async () => {
72
expect((await client.request(null)).data).toBe("hello");
73
});
74
75
it("observes the socket did not disconnect - they never do until a timeout or being explicitly closed, which is the point of sockets -- they are robust to client connection state", async () => {
76
expect(socketDisconnects.length).toBe(0);
77
});
78
79
// this test should take several seconds due to having to missed-packet detection logic
80
it("restart connection right when message is being sent; dropped message eventually gets through automatically without waiting for reconnect", async () => {
81
const iter = client.iter();
82
client.write("conat ");
83
await restartServer();
84
const { value } = await iter.next();
85
expect(value[0]).toBe("conat conat ");
86
});
87
88
it("cleans up", () => {
89
cn1.close();
90
cn2.close();
91
});
92
});
93
94
describe("test of socket and restarting server -- restart while sending data from server to the client", () => {
95
const SUBJECT = "reconnect.two";
96
97
let client,
98
server,
99
cn1,
100
cn2,
101
sockets: any[] = [];
102
103
it("creates the client and server and confirms it works", async () => {
104
cn1 = connect();
105
server = cn1.socket.listen(SUBJECT);
106
server.on("connection", (socket) => {
107
sockets.push(socket);
108
socket.on("data", (data) => {
109
socket.write(`${data}`.repeat(2));
110
});
111
});
112
cn2 = connect();
113
client = cn2.socket.connect(SUBJECT);
114
const iter = client.iter();
115
client.write("cocalc");
116
const { value } = await iter.next();
117
expect(value[0]).toBe("cocalccocalc");
118
});
119
120
// this test should take several seconds due to having to missed-packet detection logic
121
it("restart connection as we are sending data from the server to the client, and see again that nothing is lost - this the server --> client direction of the tests below which was client --> server", async () => {
122
const socket = sockets[0];
123
const iter = client.iter();
124
socket.write("sneaky");
125
await restartServer();
126
const { value } = await iter.next();
127
expect(value[0]).toBe("sneaky");
128
});
129
130
it("cleans up", () => {
131
cn1.close();
132
cn2.close();
133
});
134
});
135
136
describe("another restart test: sending data while reconnecting to try to screw with order of arrival of messages", () => {
137
const SUBJECT = "reconnect.three";
138
139
let client,
140
server,
141
cn1,
142
cn2,
143
sockets: any[] = [],
144
iter;
145
it("creates the client and server and confirms it works", async () => {
146
cn1 = connect();
147
server = cn1.socket.listen(SUBJECT);
148
server.on("connection", (socket) => {
149
sockets.push(socket);
150
socket.on("data", (data) => {
151
socket.write(`${data}`.repeat(2));
152
});
153
});
154
cn2 = connect();
155
client = cn2.socket.connect(SUBJECT);
156
iter = client.iter();
157
client.write("one ");
158
const { value } = await iter.next();
159
expect(value[0]).toBe("one one ");
160
});
161
162
it("now the **HARD CASE**; we do the same as above, but kill the server exactly as the message is being sent, so it is dropped", async () => {
163
client.write("four ");
164
await restartServer();
165
166
// write another message to socket to cause out of order message deliver
167
// to the other end
168
client.write("five ");
169
const { value } = await iter.next();
170
expect(value[0]).toBe("four four ");
171
172
// also checking ordering is correct too -- we next
173
// next get the foofoo response;
174
const { value: value1 } = await iter.next();
175
expect(value1[0]).toBe("five five ");
176
});
177
178
it("cleans up", () => {
179
cn1.close();
180
cn2.close();
181
});
182
});
183
184
afterAll(after);
185
186