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
1712 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: 500, publish: 500 });
19
});
20
21
describe("create a client and server and socket, verify it works, restart conat server, then confirm that socket still works", () => {
22
const SUBJECT = "reconnect.one";
23
24
let client,
25
server,
26
cn1,
27
cn2,
28
sockets: any[] = [];
29
30
it("creates the client and server and confirms it works", async () => {
31
cn1 = connect();
32
server = cn1.socket.listen(SUBJECT);
33
server.on("connection", (socket) => {
34
sockets.push(socket);
35
socket.on("data", (data) => {
36
socket.write(`${data}`.repeat(2));
37
});
38
socket.on("request", (mesg) => {
39
mesg.respond("hello");
40
});
41
});
42
cn2 = connect();
43
client = cn2.socket.connect(SUBJECT);
44
45
const iter = client.iter();
46
client.write("cocalc");
47
const { value } = await iter.next();
48
expect(value[0]).toBe("cocalccocalc");
49
50
expect((await client.request(null)).data).toBe("hello");
51
});
52
53
async function waitForClientsToReconnect() {
54
for (const client of [cn1, cn2]) {
55
if (client.state != "connected") {
56
await once(client, "connected");
57
}
58
}
59
}
60
61
it("restarts the conat socketio server, wait for clients to reconnect, and test sending data over socket", async () => {
62
await restartServer();
63
await waitForClientsToReconnect();
64
// sending data over socket
65
const iter = client.iter();
66
client.write("test");
67
const { value, done } = await iter.next();
68
expect(done).toBe(false);
69
expect(value[0]).toBe("testtest");
70
});
71
72
let socketDisconnects: string[] = [];
73
it("also request/respond immediately works", async () => {
74
expect((await client.request(null)).data).toBe("hello");
75
});
76
77
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 () => {
78
expect(socketDisconnects.length).toBe(0);
79
});
80
81
// this test should take several seconds due to having to missed-packet detection logic
82
it("restart connection right when message is being sent; dropped message eventually gets through automatically without waiting for reconnect", async () => {
83
const iter = client.iter();
84
client.write("conat ");
85
await restartServer();
86
const { value } = await iter.next();
87
expect(value[0]).toBe("conat conat ");
88
});
89
90
it("cleans up", () => {
91
cn1.close();
92
cn2.close();
93
});
94
});
95
96
describe("test of socket and restarting server -- restart while sending data from server to the client", () => {
97
const SUBJECT = "reconnect.two";
98
99
let client,
100
server,
101
cn1,
102
cn2,
103
sockets: any[] = [];
104
105
it("creates the client and server and confirms it works", async () => {
106
cn1 = connect();
107
server = cn1.socket.listen(SUBJECT);
108
server.on("connection", (socket) => {
109
sockets.push(socket);
110
socket.on("data", (data) => {
111
socket.write(`${data}`.repeat(2));
112
});
113
});
114
cn2 = connect();
115
client = cn2.socket.connect(SUBJECT);
116
const iter = client.iter();
117
client.write("cocalc");
118
const { value } = await iter.next();
119
expect(value[0]).toBe("cocalccocalc");
120
});
121
122
// this test should take several seconds due to having to missed-packet detection logic
123
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 () => {
124
const socket = sockets[0];
125
const iter = client.iter();
126
socket.write("sneaky");
127
await restartServer();
128
const { value } = await iter.next();
129
expect(value[0]).toBe("sneaky");
130
});
131
132
it("cleans up", () => {
133
cn1.close();
134
cn2.close();
135
});
136
});
137
138
describe("another restart test: sending data while reconnecting to try to screw with order of arrival of messages", () => {
139
const SUBJECT = "reconnect.three";
140
141
let client,
142
server,
143
cn1,
144
cn2,
145
sockets: any[] = [],
146
iter;
147
it("creates the client and server and confirms it works", async () => {
148
cn1 = connect();
149
server = cn1.socket.listen(SUBJECT);
150
server.on("connection", (socket) => {
151
sockets.push(socket);
152
socket.on("data", (data) => {
153
socket.write(`${data}`.repeat(2));
154
});
155
});
156
cn2 = connect();
157
client = cn2.socket.connect(SUBJECT);
158
iter = client.iter();
159
client.write("one ");
160
const { value } = await iter.next();
161
expect(value[0]).toBe("one one ");
162
});
163
164
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 () => {
165
client.write("four ");
166
await restartServer();
167
168
// write another message to socket to cause out of order message deliver
169
// to the other end
170
client.write("five ");
171
const { value } = await iter.next();
172
expect(value[0]).toBe("four four ");
173
174
// also checking ordering is correct too -- we next
175
// next get the foofoo response;
176
const { value: value1 } = await iter.next();
177
expect(value1[0]).toBe("five five ");
178
});
179
180
it("cleans up", () => {
181
cn1.close();
182
cn2.close();
183
});
184
});
185
186
afterAll(after);
187
188