• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

realm / realm-core / 1669

13 Sep 2023 06:44PM UTC coverage: 91.193% (-0.07%) from 91.258%
1669

push

Evergreen

GitHub
Update History Command tool to work with realms with file format version 23 (#6970)

95936 of 175880 branches covered (0.0%)

233596 of 256155 relevant lines covered (91.19%)

6735051.02 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

76.57
/test/sync_fixtures.hpp
1
#include <algorithm>
2
#include <stdexcept>
3
#include <string>
4
#include <vector>
5

6
#include <realm/impl/simulated_failure.hpp>
7
#include <realm/string_data.hpp>
8
#include <realm/sync/client.hpp>
9
#include <realm/sync/network/default_socket.hpp>
10
#include <realm/sync/network/http.hpp>
11
#include <realm/sync/network/network.hpp>
12
#include <realm/sync/noinst/client_history_impl.hpp>
13
#include <realm/sync/noinst/protocol_codec.hpp>
14
#include <realm/sync/noinst/server/server.hpp>
15
#include <realm/sync/noinst/server/server_dir.hpp>
16
#include <realm/transaction.hpp>
17
#include <realm/version.hpp>
18

19
#include "test.hpp"
20

21
namespace realm::fixtures {
22

23
using namespace realm::sync;
24
using namespace realm::test_util;
25

26
// This public key must match the private key used to sign the token
27
// below (test.pem).
28
const char g_test_server_key_path[] = "test_pubkey.pem";
29

30
inline std::string test_server_key_path()
31
{
334✔
32
    return get_test_resource_path() + g_test_server_key_path;
334✔
33
}
334✔
34

35
// The Base64-encoded user token is generated by the following command:
36
//     cat test_token.json | base64
37
// The Base64-encoded signature is generated by the following command:
38
//     cat test_token.json | openssl dgst -sha256 -binary -sign test.pem | base64
39
// The two are concatenated with a ':'.
40
// This token does not contain a "path" field, and therefore grants access to
41
// all Realms.
42
const char g_signed_test_user_token[] = "ewogICAgImlkZW50aXR5IjogInRlc3QiLAogICAgImFjY2VzcyI6IFsiZG93bmxvYWQiLCAidXBs"
43
                                        "b2FkIl0sCiAgICAidGltZXN0YW1wIjogMTQ1NTUzMDYxNCwKICAgICJleHBpcmVzIjogbnVsbCwK"
44
                                        "ICAgICJhcHBfaWQiOiAiaW8ucmVhbG0uVGVzdCIKfQo="
45
                                        ":"
46
                                        "kPQwXUUFFVoDkmw02ouA1g7OlXcZ/IJPpqwJs9lIi1azpyuakBWgQ8VhnInCXh90CQXYhnteZlMw"
47
                                        "HYUZgt3/ED1jLu+nK2HlRKsmsOuAI20jMnHGGIZkql4/Ck9PEsvZ3huHGk5Jv9vpFp/dtnl1JXK2"
48
                                        "9XjdO8+1hU4boeJuKpTMDTPwGI9dxa8sTtvMMN7AVoPkKb1uqHZVsb5uRGE86Cyv58cvuj/EvZ1A"
49
                                        "yOCt5NGJwjTxydPgfX3QPcNMwDTHCRWYuoi2oTCINQHy8ebzXVLT1iy3adV4rM5bJukCnpLqHGlZ"
50
                                        "MIslk07zKdoj3igMIT47W9QwIuCw8x5f5cRIAg==";
51

52
const char g_unsigned_test_user_token[] =
53
    "ewogICAgImlkZW50aXR5IjogInRlc3QiLAogICAgImFjY2VzcyI6IFsiZG93bmxvYWQiLCAidXBs"
54
    "b2FkIl0sCiAgICAidGltZXN0YW1wIjogMTQ1NTUzMDYxNCwKICAgICJleHBpcmVzIjogbnVsbCwK"
55
    "ICAgICJhcHBfaWQiOiAiaW8ucmVhbG0uVGVzdCIKfQo=";
56

57

58
// Token for user_0.
59
//{
60
//    "identity": "user_0",
61
//    "admin": false,
62
//    "timestamp": 1455530614,
63
//    "expires": null,
64
//    "app_id": "io.realm.Test"
65
//}
66
const char g_user_0_token[] = "ewogICAgImlkZW50aXR5IjogInVzZXJfMCIsCiAgICAiYWRtaW4iOiBmYWxzZSwKICAgICJ0aW"
67
                              "1lc3RhbXAiOiAxNDU1NTMwNjE0LAogICAgImV4cGlyZXMiOiBudWxsLAogICAgImFwcF9pZCI6"
68
                              "ICJpby5yZWFsbS5UZXN0Igp9Cg=="
69
                              ":"
70
                              "YC3MVU7lmF6uRbd72nJ8AWBI9/BmdLTutlAMy7+tkh0X08do3SDi3Kq++93b2+FWG8IOrVQge+"
71
                              "gvSMbaBEYgmV0QD7OL9Y29kjk8Ty7bvdFd2KoeQRb9IfSJXS0yd9d4OI0K/TfmGMjCh3j2gWkp"
72
                              "V5PTWg/V+T5oRfXXwUMIkSCAeCnd23YTr3NucdvkTcyjz0isW+E4uUcHePQA3Qeq0+/qPFrv4a"
73
                              "LGagtbYYnx2rCHZ8zZ1khMpKu/chF1kOit+eZqYB4Jgui5gXK3iSIWCwBlMOhofsNA9h5qmIFp"
74
                              "2SWQOk4s4bi962vJxIerIbYrsX4yzviz+yDX0UZRAStIjw==";
75

76

77
// Token for user_1.
78
// {
79
//    "identity": "user_1",
80
//    "admin": false,
81
//    "timestamp": 1455530614,
82
//    "expires": null,
83
//    "app_id": "io.realm.Test"
84
//}
85
const char g_user_1_token[] = "ewogICAgImlkZW50aXR5IjogInVzZXJfMSIsCiAgICAiYWRtaW4iOiBmYWxzZSwKICAgICJ0aW"
86
                              "1lc3RhbXAiOiAxNDU1NTMwNjE0LAogICAgImV4cGlyZXMiOiBudWxsLAogICAgImFwcF9pZCI6"
87
                              "ICJpby5yZWFsbS5UZXN0Igp9Cg=="
88
                              ":"
89
                              "iqFfuBuUx2i8nogJ2+ixA/vVUpAGaELIWktDlFyF5ZoE5xo+Jd+ElpK8Kiq7PQMd3ZwxwBBhMZ"
90
                              "M+PkgfLFcTA4hutZ5aCkbzB+DIuUCR7z0iDq4+rutIyQdvd9ujCOWYQXeE0F5TtSVWqf8baDG/"
91
                              "HXDGx6sASy+2PwUoZrllJbRPeclGQAsKZ6jq46ahH8mmvjKX0GCJHED4i20ZEr2VgeTPZ+9gYo"
92
                              "okj3RIbEzHF3SNLvae1ENY8O/mB5g+Hf71E6fnU/AuFTpbJlom0Lm72C7wgk88HjIqjfpo0L4l"
93
                              "9r54Q2rkIffdAbMRCrIqrT81AMZuTIGDkRbMLDLtsUifgQ==";
94

95

96
// Token for user_2.
97
// {
98
//    "identity": "user_2",
99
//    "admin": false,
100
//    "timestamp": 1455530614,
101
//    "expires": null,
102
//    "app_id": "io.realm.Test"
103
// }
104
const char g_user_2_token[] = "ewogICAgImlkZW50aXR5IjogInVzZXJfMiIsCiAgICAiYWRtaW4iOiBmYWxzZSwKICAgICJ0aW"
105
                              "1lc3RhbXAiOiAxNDU1NTMwNjE0LAogICAgImV4cGlyZXMiOiBudWxsLAogICAgImFwcF9pZCI6"
106
                              "ICJpby5yZWFsbS5UZXN0Igp9Cg=="
107
                              ":"
108
                              "q68rj8I66E10+EQ8+OuaSeD2U1zu1mWpRgLpt+fAX4JPYZssfUsOq7adY2IJRcBEYAwS4LjkgM"
109
                              "QGU9hwQ0PTLZzoYQQXqGfqsNHF/LvNv1P1DwGkf6fp5jsRcx+IauhmKiCKdg+SItaTuaZ1Duxs"
110
                              "9sMrL7NIa59fQDtefltNlbNBDMJDUFlNRdSQj4FEdhAkeNu9Qv2xMqDHKhcAZhvBcPxMYVno9E"
111
                              "69lEkxM0mIfoy5IFg81YRaB1gbSkhPs2HVo++j1jbtFRIv/cEA+PqeeHN+fDiMHQRRNtYRbJPh"
112
                              "hExS3rsZhsQSZPiU0urVw4fBFn9X5NuHuUJQO7vSdqJbwQ==";
113

114

115
// {
116
//     "identity": "user_0",
117
//     "admin": false,
118
//     "timestamp": 1455530614,
119
//     "expires": null,
120
//     "app_id": "io.realm.Test",
121
//     "path": "/test",
122
//     "access": ["download", "upload", "manage"]
123
// }
124
const char g_user_0_path_test_token[] = "ewogICAgImlkZW50aXR5IjogInVzZXJfMCIsCiAgICAiYWRtaW4iOiBmYWxzZSwKICAgICJ0aW1l"
125
                                        "c3RhbXAiOiAxNDU1NTMwNjE0LAogICAgImV4cGlyZXMiOiBudWxsLAogICAgImFwcF9pZCI6ICJp"
126
                                        "by5yZWFsbS5UZXN0IiwKICAgICJwYXRoIjogIi90ZXN0IiwKICAgICJhY2Nlc3MiOiBbImRvd25s"
127
                                        "b2FkIiwgInVwbG9hZCIsICJtYW5hZ2UiXQp9Cg=="
128
                                        ":"
129
                                        "E3hiuWjFFDUExrz5osiXFoDVSoX0168kdBNVBSEre/mYbr1s0A+mlvEH24ibklC8bh9K3BBtwjVQ"
130
                                        "v+vvFGThmZggbWDl/SAeeIP0MpQJR4FFAqkiB93Ax1Gi9b1i25lD5lGXc9CVsSNpWMV1LRD9I6Y9"
131
                                        "N70ENUd7vBuz0y4+y6k0A1HtaB6pzVx90kTtFaOVCz/UiMxsZOMKdRoNibIpmFU5Q5eP4UTsrZq/"
132
                                        "4rVOisRWno3eA01IQvz/ECtnt13KBc6rEzRzpA5tWMmwJpoykEpAUUFnx8N6LAqObeij/3c5iLwp"
133
                                        "1l5pFAzmTmbqeWbeCtLPyLM+baEo65kbPP5nPg==";
134

135

136
// {
137
//     "identity": "user_1",
138
//     "admin": false,
139
//     "timestamp": 1455530614,
140
//     "expires": null,
141
//     "app_id": "io.realm.Test",
142
//     "path": "/test",
143
//     "access": ["download", "upload", "manage"]
144
// }
145
const char g_user_1_path_test_token[] = "ewogICAgImlkZW50aXR5IjogInVzZXJfMSIsCiAgICAiYWRtaW4iOiBmYWxzZSwKICAgICJ0aW1l"
146
                                        "c3RhbXAiOiAxNDU1NTMwNjE0LAogICAgImV4cGlyZXMiOiBudWxsLAogICAgImFwcF9pZCI6ICJp"
147
                                        "by5yZWFsbS5UZXN0IiwKICAgICJwYXRoIjogIi90ZXN0IiwKICAgICJhY2Nlc3MiOiBbImRvd25s"
148
                                        "b2FkIiwgInVwbG9hZCIsICJtYW5hZ2UiXQp9Cg=="
149
                                        ":"
150
                                        "3QsZ3qKFwtf8FisKy3vrOImtFt7gOnuKQl92/Ckmjq6ux9OFG74d6sj6UrvXPce4lgeqPbT4qTNd"
151
                                        "WgrXh8vBqS/R34a9v2dhV5Ys9xkQuEp04+FMswwx3hFbsxNOPaMVwtFHDzEgQus3ZP5rnpFUj/xZ"
152
                                        "Rh6IKIQ5EksIZTGnQscBdSM/n1Qlpa7SPx94SrtIwZvPO+OcqJj367PdnS+Ii0TvGj6WFIz44gJT"
153
                                        "GX03+qMqSdAuW/91xX013efiG+nYKKPMT4Z6+pQkyJ9C9eyvXXiJigXlBXb8wxrWBzpGoaPcWYYj"
154
                                        "q9wI3gXQ9i7DT+cH7gbDFWnweiorMvmAPkocSw==";
155

156

157
// Generated from test_token_readonly.json
158
// This token does not contain a "path" field, and therefore grants access to
159
// all Realms.
160
const char g_signed_test_user_token_readonly[] =
161
    "ewogICAgImlkZW50aXR5IjogInRlc3QiLAogICAgImFjY2VzcyI6IFsiZG93bmxvYWQiXSwKICAg"
162
    "ICJ0aW1lc3RhbXAiOiAxNDU1NTMwNjE0LAogICAgImV4cGlyZXMiOiBudWxsLAogICAgImFwcF9p"
163
    "ZCI6ICJpby5yZWFsbS5UZXN0Igp9Cg=="
164
    ":"
165
    "e8VAY6/GO+JVi4D+inmKpPc0rgegKGlQ5gT9mpC+4DdWawiDZIyIyu6OfeNbDXYlSSryzQWJQ7zt"
166
    "ro/czrz0Q8bHVrUwzwI5jwogOPU4X64FthJ4LwPeP3DYKP3oeaZfn0m3ONQCcqbAjSXo/uMxgfRi"
167
    "ydYOWK6Vuoxxr8M7om7Y9nbdWp/ElNpYW2vbxZe87CDHt5WyV7qR8WH+xroNxMcngRd8lNquPW4g"
168
    "kSv8TltuIK/RY1Fwz0duhduqPDhmXL0tB/BiFytwZw4g4+Ag/N//3oUA+yMJl0zFvxnI5eRZBmqK"
169
    "5m6h1of9T5WRA2lW4X5HSo/Gi9h7cMCz8Nhpew==";
170

171
// Generated from test_token_for_path.json
172
// This token only grants access to the server path "/valid".
173
const char g_signed_test_user_token_for_path[] =
174
    "ewogICAgImlkZW50aXR5IjogInRlc3QiLAogICAgImFjY2VzcyI6IFsiZG93bmxvYWQiLCAidXBs"
175
    "b2FkIl0sCiAgICAidGltZXN0YW1wIjogMTQ1NTUzMDYxNCwKICAgICJleHBpcmVzIjogbnVsbCwK"
176
    "ICAgICJhcHBfaWQiOiAiaW8ucmVhbG0uVGVzdCIsCiAgICAicGF0aCI6ICIvdmFsaWQiCn0K"
177
    ":"
178
    "U4mUfQuK2qA/uFIKXS1Sjx8PFmaR0P8FTB3wu5ybldWJAKcVeiOInW23ZBFqdHvG//VO7eb3QgeR"
179
    "4C99I09O6CZg/zP0FM4sdhYr4bQXo2y8nbJy4sxLr0EYLxpcrDCoXPmQr7LD+txmzcc4/rHnZfiA"
180
    "0Ujai7I66mBejZyjwNGIqZ2iShX7NQJE9MxA6vvgCtsUCdJJjQFCeT46+V2g2ggU9jjX/lVFlJF+"
181
    "si9NGnisRqiK9loHlU/duhB9C2dai2If+1ZxW5xlrYSuLQhubwNcL9jRwB2GpCaZBnZBBxa0g4ZG"
182
    "a2wKgKA7oLGQZU7+OPSu3mwsGDnuuHYNoZ9KiA==";
183

184
// Generated from test_token_expiration_unspecified.json
185
const char g_signed_test_user_token_expiration_unspecified[] =
186
    "ewogICAgImlkZW50aXR5IjogInRlc3QiLAogICAgImFjY2VzcyI6IFsiZG93bmxvYWQiLCAidXBs"
187
    "b2FkIl0sCiAgICAidGltZXN0YW1wIjogMTQ1NTUzMDYxNCwKICAgICJhcHBfaWQiOiAiaW8ucmVh"
188
    "bG0uVGVzdCIKfQo="
189
    ":"
190
    "POoY6mR2LuEMmZ2nmzpx33QbaG213ZeP51pBihqFhosTWg/xszncQf1rCntY6tbnw7qNL1Sj8v/T"
191
    "tGjpGayGGgaHWRIOVw8X+Oije7YI5zRTWyCQBO4S3rpgiqxgnuqFVwfflfa++CslaVDy8TZOLwJp"
192
    "07x/57tjm0qVooDklA+IuQvWWCgwwaFgP1KRCXQ1UjFED3H5hClfZA4yT082BEUlCK+TbxMD4DjZ"
193
    "MY4hApqs19sTw04/EdL9Mw7DqXYyGZYrjd7hMwYxsaIOLaiuzS8t2yXElvj79q9hnjtxhYW7Fgeh"
194
    "52l91uWGnlj0EkGTdNLrH6EWQh9tEFFQxdf/1w==";
195

196
// Generated from test_token_expiration_null.json
197
const char g_signed_test_user_token_expiration_null[] =
198
    "ewogICAgImlkZW50aXR5IjogInRlc3QiLAogICAgImFjY2VzcyI6IFsiZG93bmxvYWQiLCAidXBs"
199
    "b2FkIl0sCiAgICAidGltZXN0YW1wIjogMTQ1NTUzMDYxNCwKICAgICJleHBpcmVzIjogbnVsbCwK"
200
    "ICAgICJhcHBfaWQiOiAiaW8ucmVhbG0uVGVzdCIKfQo="
201
    ":"
202
    "kPQwXUUFFVoDkmw02ouA1g7OlXcZ/IJPpqwJs9lIi1azpyuakBWgQ8VhnInCXh90CQXYhnteZlMw"
203
    "HYUZgt3/ED1jLu+nK2HlRKsmsOuAI20jMnHGGIZkql4/Ck9PEsvZ3huHGk5Jv9vpFp/dtnl1JXK2"
204
    "9XjdO8+1hU4boeJuKpTMDTPwGI9dxa8sTtvMMN7AVoPkKb1uqHZVsb5uRGE86Cyv58cvuj/EvZ1A"
205
    "yOCt5NGJwjTxydPgfX3QPcNMwDTHCRWYuoi2oTCINQHy8ebzXVLT1iy3adV4rM5bJukCnpLqHGlZ"
206
    "MIslk07zKdoj3igMIT47W9QwIuCw8x5f5cRIAg==";
207

208
// Generated from test_token_expiration_specified.json
209
const char g_signed_test_user_token_expiration_specified[] =
210
    "ewogICAgImlkZW50aXR5IjogInRlc3QiLAogICAgImFjY2VzcyI6IFsiZG93bmxvYWQiLCAidXBs"
211
    "b2FkIl0sCiAgICAidGltZXN0YW1wIjogMTQ1NTUzMDYxNCwKICAgICJleHBpcmVzIjogICAzMDAw"
212
    "MDAwMDAwLAogICAgImFwcF9pZCI6ICJpby5yZWFsbS5UZXN0Igp9Cg=="
213
    ":"
214
    "Y82y0/oFnVGDcQAHIw59QVMnK9ji9byj6e3h1kwMwZeCDq2aQZvGhHsAfHjfFrJe7VfBFhW7o3Op"
215
    "JiSG3X8QAQwosUJMLLNQBxCbF+FOE5p2lD8ET0huDsafBKSszm4YrUrpgC6KkOlQqsaCH8bHhYqk"
216
    "YhQzyBEK2hc9hwuzic2RSLtpEttvz1Ew/QKlMqB9TVRpINdHaPfzh0EcWq72yza/Q+JWnLy+D24F"
217
    "2Thv0yKASw40Do56N8yoE/3gOPbrUrdmtjGClVbbR/lQ//z244qmik+mtEBuemYDTzO9/KrmBkJP"
218
    "YI2MJlgWBspWqWv9JxslYzh/gfD2uoyo1mJ2aQ==";
219

220
// Generated from test_token_sync_label_default.json
221
const char g_signed_test_user_token_sync_label_default[] =
222
    "ewogICAgImlkZW50aXR5IjogInRlc3QiLAogICAgImFjY2VzcyI6IFsiZG93bmxvYWQiLCAidXBs"
223
    "b2FkIl0sCiAgICAidGltZXN0YW1wIjogMTQ1NTUzMDYxNCwKICAgICJleHBpcmVzIjogbnVsbCwK"
224
    "ICAgICJhcHBfaWQiOiAiaW8ucmVhbG0uVGVzdCIsCiAgICAic3luY19sYWJlbCI6ICJkZWZhdWx0"
225
    "Igp9Cg=="
226
    ":"
227
    "myXAxeVDbu+RhbcrCHPCll0xExQOqIj9TlZ9RXlRQGhfcRvAqVgO4FZQLl+qp+DGICrG7I5OYXXA"
228
    "K+cfhAJ5G/xQxdQdUuuYfvzE3dg6xuNkL7a41/SXcnVu1U49mGtZF+55+S2DpEmI4TfVlNdTLEci"
229
    "mizXRZYeq8OdgI2kBGisKw7wSVCaeJ7pV5gzs8dRbZg6OF3OkCPJiWPxvCWEUhIVEn49pNQ0Q2E6"
230
    "F9x2Ckba7LMUY2VupahVir/+4u4Y9hbwUJ3fEnKkzxUErh9Gao4+0jLK042y5+cfJLhnzGHOVIXt"
231
    "2/PkHx3oDpcl5O9Gc1qAlt7O6lN8qOXS4p1yIQ==";
232

233
// Generated from test_token_sync_label_custom.json
234
const char g_signed_test_user_token_sync_label_custom[] =
235
    "ewogICAgImlkZW50aXR5IjogInRlc3QiLAogICAgImFjY2VzcyI6IFsiZG93bmxvYWQiLCAidXBs"
236
    "b2FkIl0sCiAgICAidGltZXN0YW1wIjogMTQ1NTUzMDYxNCwKICAgICJleHBpcmVzIjogbnVsbCwK"
237
    "ICAgICJhcHBfaWQiOiAiaW8ucmVhbG0uVGVzdCIsCiAgICAic3luY19sYWJlbCI6ICJjdXN0b20i"
238
    "Cn0K"
239
    ":"
240
    "T4Ajej3JD3wRjbcPVXWdPzgPngf0aCIDN6JiFKkvbSyh5cDOibx6XzUVQrjHGDI/IU1VT2Hs+V0B"
241
    "KVHKVKVJV22zaCOdIGOQ4QtOh9InnqAGghz+8IJHZawZIqdPByy4WtsPX6EITU2yYUfZ0YIzVMmD"
242
    "f5bB5JY532vifQsc0rpJaFztKekQCx7j02opvNeyXg6jEoFwY62uIGbS83FnLkVOE0uZ8XK6JuWK"
243
    "69RzxOGLyh97goWFtx+8Xp+fz82slRCxlFas8SIkxdj0vfIHcr8BnhhDgbsXDBPvbW55fDnwTlzA"
244
    "1TvQPRWX12PXn0jJKSTFyC73RO26oorfQewTeA==";
245

246
const std::string developer_token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0OTI2MTEzMDN9.cf2s0bUM/da"
247
                                    "Hq/yp7PG6csktIgObeVeUvmObD8JOUCwC2uA3rMLGjEHPKxxmvZJtSoQq49PkhTpvCSk1ewKR"
248
                                    "R67U9J/AE1czBQPiit4FgTj/oujXIpNVio9t5oeSd3XqNg66HZhi5F+wsMOJ2hmxL9S+OBjQU"
249
                                    "yUchEPksKubiFKUPEktdmjewNp2VmdnPNjAdqmIhRyeHSQhl494lOlK/gyae2RUh2wWO1j9K0"
250
                                    "o24nd0VIr4+61UD/aMDsF70vXEkqPxboB83yu51CpkmqFJ0xklA9f9M1uKUgsOwX07Uf4plRE"
251
                                    "tYb1QxmoPdnQH3VPvVt4i0Yko/dMgpnuSJj30gQ==";
252

253
const std::string enterprise_token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJBZGFwdGVyIjp0cnVlLCJCYWNrdXAiOnRyd"
254
                                     "WUsIkxvYWRCYWxhbmNpbmciOnRydWUsIk5vdGlmaWVyIjp0cnVlLCJTeW5jIjp0cnVlLCJpYXQ"
255
                                     "iOjE1MDc4MzM4MDJ9.SJn2ScgPXXJgRrXzKg-7HlCcwGQ5pQVLPXHiyJhRN54f4dAFsVDjyoqf"
256
                                     "yV2TO6CibP9UytIaztnnqjFrYq47YsZas9IlkZ6xv3wZRUF4Op_OcCdmuY9kAuGG-fNqBKWaM_"
257
                                     "woDzrFBigo1os7_FqFU1aC1Jmnh1NZjEPTP5euwWNF92BGcfAdHBkJU86MLvvXLHzGnedkm4Y5"
258
                                     "Sh5GlHz_UkC7OHflJoXbMUIUi9NMbqH3BbtvCwKHE1Vs-XShNFqbrAJTG6PJxJxIOXfSvgbnxe"
259
                                     "N3gpm69p-9cfM9O4TjNPoYZy4cPR5UG6NSJ4XDuSg7Wz8kdn9l3IzMiwi-a6Oofw";
260

261
const StringData g_partial_sync_result_sets_table_name = "class___ResultSets";
262

263

264
class FakeClock : public sync::Clock {
265
public:
266
    template <class R, class P>
267
    void add_time(std::chrono::duration<R, P> duration) noexcept
268
    {
×
269
        m_now += std::chrono::duration_cast<time_point::duration>(duration).count();
×
270
    }
×
271

272
    FakeClock() noexcept
273
        : m_now(0)
274
    {
332✔
275
    }
332✔
276

277
    template <class R, class P>
278
    FakeClock(std::chrono::duration<R, P> initial_value) noexcept
279
        : m_now(std::chrono::duration_cast<time_point::duration>(initial_value).count())
280
    {
281
    }
282

283
    time_point now() const noexcept override
284
    {
×
285
        return time_point{time_point::duration{m_now}};
×
286
    }
×
287

288
private:
289
    std::atomic<time_point::rep> m_now;
290
};
291

292

293
// HTTPRequestClient is used to send a HTTP request to a server, typically the
294
// Sync server, and wait for the response. HTTPRequestClient is constructed
295
// with the server endpoint and request. fetch_response() is a blocking
296
// function that fetches the HTTP response on the network. get_response()
297
// is used to retrieve the result afterwards.
298
class HTTPRequestClient {
299
public:
300
    using WriteCompletionHandler = util::UniqueFunction<void(std::error_code, size_t num_bytes_transferred)>;
301
    using ReadCompletionHandler = util::UniqueFunction<void(std::error_code, size_t num_bytes_transferred)>;
302

303
    util::PrefixLogger logger;
304

305
    HTTPRequestClient(const std::shared_ptr<util::Logger>& logger_ptr, const network::Endpoint& endpoint,
306
                      const HTTPRequest& request)
307
        : logger{"HTTP client: ", logger_ptr}
308
        , m_endpoint{endpoint}
309
        , m_http_client{*this, logger_ptr}
310
        , m_request{request}
311
    {
2✔
312
    }
2✔
313

314
    // The network activity happens in fetch_response().
315
    // fetch_response() is blocking.
316
    void fetch_response()
317
    {
2✔
318
        initiate_tcp_connect();
2✔
319
        m_service.run();
2✔
320
    }
2✔
321

322
    // get_response is used to retrieve the response after
323
    // fetch_response returns.
324
    HTTPResponse& get_response()
325
    {
2✔
326
        return m_response;
2✔
327
    }
2✔
328

329
    void async_write(const char* data, size_t size, WriteCompletionHandler handler)
330
    {
2✔
331
        m_socket.async_write(data, size, std::move(handler));
2✔
332
    }
2✔
333

334
    void async_read_until(char* buffer, size_t size, char delim, ReadCompletionHandler handler)
335
    {
12✔
336
        m_socket.async_read_until(buffer, size, delim, m_read_ahead_buffer, std::move(handler));
12✔
337
    }
12✔
338

339
    void async_read(char* buffer, size_t size, ReadCompletionHandler handler)
340
    {
2✔
341
        m_socket.async_read(buffer, size, m_read_ahead_buffer, std::move(handler));
2✔
342
    }
2✔
343

344
private:
345
    network::Service m_service;
346
    network::Socket m_socket{m_service};
347
    network::ReadAheadBuffer m_read_ahead_buffer;
348
    network::Endpoint m_endpoint;
349
    HTTPClient<HTTPRequestClient> m_http_client;
350
    HTTPRequest m_request;
351
    HTTPResponse m_response;
352

353
    void initiate_tcp_connect()
354
    {
2✔
355
        auto handler = [this](std::error_code ec) mutable {
2✔
356
            if (ec != util::error::operation_aborted)
2✔
357
                handle_tcp_connect(ec);
2✔
358
        };
2✔
359
        m_socket.async_connect(m_endpoint, std::move(handler));
2✔
360
        logger.info("Connecting to endpoint '%1:%2'", m_endpoint.address(), m_endpoint.port());
2✔
361
    }
2✔
362

363
    void handle_tcp_connect(std::error_code ec)
364
    {
2✔
365
        if (ec) {
2✔
366
            logger.debug("Failed to connect to endpoint '%1:%2': %3", m_endpoint.address(), m_endpoint.port(),
×
367
                         ec.message());
×
368
            stop();
×
369
            return;
×
370
        }
×
371

1✔
372
        m_socket.set_option(network::SocketBase::no_delay(true));
2✔
373
        logger.debug("Connected to endpoint '%1:%2'", m_endpoint.address(), m_endpoint.port());
2✔
374

1✔
375

1✔
376
        initiate_http_request();
2✔
377
    }
2✔
378

379
    void initiate_http_request()
380
    {
2✔
381
        auto handler = [this](HTTPResponse response, std::error_code ec) {
2✔
382
            if (ec != util::error::operation_aborted) {
2✔
383
                if (ec) {
2✔
384
                    logger.debug("HTTP response error, ec = %1", ec.message());
×
385
                    stop();
×
386
                    return;
×
387
                }
×
388
                handle_http_response(response);
2✔
389
            }
2✔
390
        };
2✔
391
        m_http_client.async_request(m_request, handler);
2✔
392
    }
2✔
393

394
    void handle_http_response(const HTTPResponse response)
395
    {
2✔
396
        logger.debug("HTTP response received, status = %1", int(response.status));
2✔
397
        m_response = response;
2✔
398
        stop();
2✔
399
    }
2✔
400

401
    void stop()
402
    {
2✔
403
        m_socket.close();
2✔
404
        m_service.stop();
2✔
405
    }
2✔
406
};
407

408

409
class MultiClientServerFixture {
410
public:
411
    enum class ClusterTopology { separate_nodes, two_tiers, one_node_per_tier };
412

413
    struct Config {
414
        Config() {}
324✔
415

416
        // Optional thread-safe logger. If none is specified, the one available
417
        // through unit_test::TestContext will be used.
418
        std::shared_ptr<util::Logger> logger;
419

420
        // These values will disable the heartbeats by default.
421
        milliseconds_type client_ping_period = 100000000;  // do not send pings
422
        milliseconds_type client_pong_timeout = 100000000; // do not expect pongs
423
        milliseconds_type server_connection_reaper_timeout = 100000000;
424
        milliseconds_type server_connection_reaper_interval = 100000000;
425

426
        long server_max_open_files = 64;
427

428
        bool enable_server_ssl = false;
429

430
        std::string server_ssl_certificate_path = get_test_resource_path() + "test_sync_ca.pem";
431
        std::string server_ssl_certificate_key_path = get_test_resource_path() + "test_sync_key.pem";
432

433
        bool disable_download_compaction = false;
434
        bool disable_upload_compaction = false;
435

436
        bool disable_history_compaction = false;
437
        std::chrono::seconds history_ttl = std::chrono::seconds::max();
438
        std::chrono::seconds history_compaction_interval = std::chrono::seconds{3600};
439
        const Clock* history_compaction_clock = nullptr;
440

441
        size_t max_download_size = 0x1000000; // 16 MB as in Server::Config
442

443
#if REALM_DISABLE_SYNC_MULTIPLEXING
444
        bool one_connection_per_session = true;
445
#else
446
        bool one_connection_per_session = false;
447
#endif
448

449
        bool disable_upload_activation_delay = false;
450

451
        ClusterTopology cluster_topology = ClusterTopology::separate_nodes;
452

453
        std::string authorization_header_name = "Authorization";
454

455
        // Run servers without public key if `server_public_key_path` is the
456
        // empty string.
457
        std::string server_public_key_path = test_server_key_path();
458

459
        // Must be empty (encryption disabled) or contain 64 bytes.
460
        std::string server_encryption_key;
461

462
        int server_max_protocol_version = 0;
463

464
        std::set<file_ident_type> server_disable_download_for;
465

466
        std::function<Server::SessionBootstrapCallback> server_session_bootstrap_callback;
467
    };
468

469

470
    MultiClientServerFixture(int num_clients, int num_servers, std::string server_dir,
471
                             unit_test::TestContext& test_context, Config config = {})
472
        : m_logger{config.logger ? config.logger : test_context.logger}
473
        , m_num_servers{num_servers}
474
        , m_num_clients{num_clients}
475
        , m_enable_server_ssl{config.enable_server_ssl}
476
        , m_test_context{test_context}
477
    {
332✔
478
        REALM_ASSERT(num_servers >= 1);
332✔
479

167✔
480
        m_server_loggers.resize(num_servers);
332✔
481
        m_client_loggers.resize(num_clients);
332✔
482

167✔
483
        if (num_servers == 1) {
332✔
484
            m_server_loggers[0] = std::make_shared<util::PrefixLogger>("Server: ", m_logger);
330✔
485
        }
330✔
486
        else {
2✔
487
            for (int i = 0; i < num_servers; ++i) {
6✔
488
                std::string prefix = "Server[" + std::to_string(i + 1) + "]: ";
4✔
489
                m_server_loggers[i] = std::make_shared<util::PrefixLogger>(std::move(prefix), m_logger);
4✔
490
            }
4✔
491
        }
2✔
492

167✔
493
        if (num_clients == 1) {
332✔
494
            m_client_loggers[0] = std::make_shared<util::PrefixLogger>("Client: ", m_logger);
304✔
495
        }
304✔
496
        else {
28✔
497
            for (int i = 0; i < num_clients; ++i) {
96✔
498
                std::string prefix = "Client[" + std::to_string(i + 1) + "]: ";
68✔
499
                m_client_loggers[i] = std::make_shared<util::PrefixLogger>(std::move(prefix), m_logger);
68✔
500
            }
68✔
501
        }
28✔
502

167✔
503
        m_servers.resize(num_servers);
332✔
504
        m_server_ports.resize(num_servers);
332✔
505
        std::string listen_address = "localhost";
332✔
506
        std::string listen_port = ""; // Assign automatically
332✔
507
        for (int i = 0; i < num_servers; ++i) {
666✔
508
            std::string dir_name = "server";
334✔
509
            if (num_servers > 1) {
334✔
510
                dir_name += "-" + std::to_string(i + 1);
4✔
511
            }
4✔
512
            std::string dir = util::File::resolve(dir_name, server_dir);
334✔
513
            util::try_make_dir(dir);
334✔
514
            std::optional<PKey> public_key;
334✔
515
            if (!config.server_public_key_path.empty())
334✔
516
                public_key = PKey::load_public(config.server_public_key_path);
324✔
517
            Server::Config config_2;
334✔
518
            config_2.max_open_files = config.server_max_open_files;
334✔
519
            config_2.logger = m_server_loggers[i];
334✔
520
            config_2.token_expiration_clock = &m_fake_token_expiration_clock;
334✔
521
            config_2.ssl = m_enable_server_ssl;
334✔
522
            config_2.ssl_certificate_path = config.server_ssl_certificate_path;
334✔
523
            config_2.ssl_certificate_key_path = config.server_ssl_certificate_key_path;
334✔
524
            config_2.connection_reaper_timeout = config.server_connection_reaper_timeout;
334✔
525
            config_2.connection_reaper_interval = config.server_connection_reaper_interval;
334✔
526
            config_2.max_download_size = config.max_download_size;
334✔
527
            config_2.disable_download_compaction = config.disable_download_compaction;
334✔
528
            config_2.tcp_no_delay = true;
334✔
529
            config_2.authorization_header_name = config.authorization_header_name;
334✔
530
            config_2.encryption_key = make_crypt_key(config.server_encryption_key);
334✔
531
            config_2.max_protocol_version = config.server_max_protocol_version;
334✔
532
            config_2.disable_download_for = std::move(config.server_disable_download_for);
334✔
533
            config_2.session_bootstrap_callback = std::move(config.server_session_bootstrap_callback);
334✔
534
            m_servers[i] = std::make_unique<Server>(std::move(dir), std::move(public_key), std::move(config_2));
334✔
535
            m_servers[i]->start(listen_address, listen_port);
334✔
536
            m_server_ports[i] = m_servers[i]->listen_endpoint().port();
334✔
537
        }
334✔
538

167✔
539
        m_clients.resize(num_clients);
332✔
540
        for (int i = 0; i < num_clients; ++i) {
704✔
541
            Client::Config config_2;
372✔
542

187✔
543
            m_client_socket_providers.push_back(std::make_shared<websocket::DefaultSocketProvider>(
372✔
544
                m_client_loggers[i], "", nullptr, websocket::DefaultSocketProvider::AutoStart{false}));
372✔
545
            config_2.socket_provider = m_client_socket_providers.back();
372✔
546
            config_2.logger = m_client_loggers[i];
372✔
547
            config_2.reconnect_mode = ReconnectMode::testing;
372✔
548
            config_2.ping_keepalive_period = config.client_ping_period;
372✔
549
            config_2.pong_keepalive_timeout = config.client_pong_timeout;
372✔
550
            config_2.disable_upload_compaction = config.disable_upload_compaction;
372✔
551
            config_2.one_connection_per_session = config.one_connection_per_session;
372✔
552
            config_2.disable_upload_activation_delay = config.disable_upload_activation_delay;
372✔
553
            config_2.fix_up_object_ids = true;
372✔
554
            m_clients[i] = std::make_unique<Client>(std::move(config_2));
372✔
555
        }
372✔
556

167✔
557
        m_server_threads.resize(num_servers);
332✔
558

167✔
559
        m_simulated_server_error_rates.resize(num_servers);
332✔
560
        m_simulated_client_error_rates.resize(num_clients);
332✔
561

167✔
562
        m_allow_server_errors.resize(num_servers, 0);
332✔
563

167✔
564
        m_connection_state_change_listeners.resize(num_clients);
332✔
565
    }
332✔
566

567
    MultiClientServerFixture(const MultiClientServerFixture&) = delete;
568

569
    ~MultiClientServerFixture()
570
    {
332✔
571
        unit_test::TestContext& test_context = m_test_context;
332✔
572
        stop();
332✔
573
        for (int i = 0; i < m_num_clients; ++i) {
704✔
574
            m_clients[i]->shutdown_and_wait();
372✔
575
        }
372✔
576
        m_client_socket_providers.clear();
332✔
577
        for (int i = 0; i < m_num_servers; ++i) {
666✔
578
            if (m_server_threads[i].joinable())
334✔
579
                CHECK(!m_server_threads[i].join());
334✔
580
            CHECK_LESS_EQUAL(m_servers[i]->errors_seen(), m_allow_server_errors[i]);
334✔
581
        }
334✔
582
    }
332✔
583

584
    using ErrorHandler = void(Status status, bool is_fatal);
585

586
    // Set an error handler to be used for all sessions of the specified client
587
    // (\a handler will be copied for each session). Must be called before
588
    // make_session().
589
    void set_client_side_error_handler(int client_index, std::function<ErrorHandler> handler)
590
    {
31✔
591
        auto handler_wrapped = [handler = std::move(handler)](ConnectionState state,
31✔
592
                                                              std::optional<SessionErrorInfo> error_info) {
2,784✔
593
            if (state != ConnectionState::disconnected)
2,784✔
594
                return;
1,863✔
595
            REALM_ASSERT(error_info);
921✔
596
            handler(error_info->status, error_info->is_fatal);
921✔
597
        };
921✔
598
        m_connection_state_change_listeners[client_index] = std::move(handler_wrapped);
31✔
599
    }
31✔
600

601
    void set_client_side_error_rate(int client_index, int n, int m)
602
    {
4✔
603
        REALM_ASSERT(client_index >= 0 && client_index < m_num_clients);
4✔
604
        auto sim = std::make_pair(n, m);
4✔
605
        // Save the simulated error rate
2✔
606
        m_simulated_client_error_rates[client_index] = sim;
4✔
607

2✔
608
        // Post the new simulated error rate
2✔
609
        using sf = _impl::SimulatedFailure;
4✔
610
        // Post it onto the event loop to update the event loop thread
2✔
611
        m_client_socket_providers[client_index]->post([sim = std::move(sim)](Status) {
4✔
612
            sf::prime_random(sf::sync_client__read_head, sim.first, sim.second,
4✔
613
                             random_int<uint_fast64_t>()); // Seed from global generator
4✔
614
        });
4✔
615
    }
4✔
616

617
    // Must be called before start().
618
    void set_server_side_error_rate(int server_index, int n, int m)
619
    {
2✔
620
        REALM_ASSERT(server_index >= 0 && server_index < m_num_servers);
2✔
621
        m_simulated_server_error_rates[server_index] = std::make_pair(n, m);
2✔
622
    }
2✔
623

624
    void start()
625
    {
330✔
626
        for (int i = 0; i < m_num_servers; ++i)
662✔
627
            m_server_threads[i].start([this, i] {
332✔
628
                run_server(i);
332✔
629
            });
332✔
630

166✔
631
        for (int i = 0; i < m_num_clients; ++i) {
698✔
632
            m_client_socket_providers[i]->start();
368✔
633
        }
368✔
634
    }
330✔
635

636
    void start_client(int index)
637
    {
4✔
638
        REALM_ASSERT(index >= 0 && index < m_num_clients);
4✔
639
        m_client_socket_providers[index]->start();
4✔
640
    }
4✔
641

642
    // Use either the methods below or `start()`.
643
    void start_server(int index)
644
    {
2✔
645
        REALM_ASSERT(index >= 0 && index < m_num_servers);
2✔
646
        m_server_threads[index].start([this, index] {
2✔
647
            run_server(index);
2✔
648
        });
2✔
649
    }
2✔
650

651
    void stop_server(int index)
652
    {
×
653
        REALM_ASSERT(index >= 0 && index < m_num_servers);
×
654
        m_servers[index]->stop();
×
655
        unit_test::TestContext& test_context = m_test_context;
×
656
        if (m_server_threads[index].joinable())
×
657
            CHECK(!m_server_threads[index].join());
×
658
        CHECK_LESS_EQUAL(m_servers[index]->errors_seen(), m_allow_server_errors[index]);
×
659
    }
×
660

661
    void stop_client(int index)
662
    {
2✔
663
        REALM_ASSERT(index >= 0 && index < m_num_clients);
2✔
664
        auto& client = get_client(index);
2✔
665
        auto sim = m_simulated_client_error_rates[index];
2✔
666
        if (sim.first != 0) {
2✔
667
            using sf = _impl::SimulatedFailure;
×
668
            // If we're using a simulated failure, clear it by posting onto the event loop
669
            m_client_socket_providers[index]->post([](Status) mutable {
×
670
                sf::unprime(sf::sync_client__read_head); // Clear the sim failure set when started
×
671
            });
×
672
        }
×
673
        // We can't wait for clearing the simulated failure since some tests stop the client early
1✔
674
        client.shutdown_and_wait();
2✔
675
    }
2✔
676

677
    void stop()
678
    {
367✔
679
        for (int i = 0; i < m_num_clients; ++i)
786✔
680
            m_clients[i]->shutdown();
419✔
681
        for (int i = 0; i < m_num_servers; ++i)
736✔
682
            m_servers[i]->stop();
369✔
683
    }
367✔
684

685
    Client& get_client(int client_index) noexcept
686
    {
906✔
687
        return *m_clients[client_index];
906✔
688
    }
906✔
689

690
    Server& get_server(int server_index) noexcept
691
    {
2,439✔
692
        return *m_servers[server_index];
2,439✔
693
    }
2,439✔
694

695
    Session make_session(int client_index, int server_index, DBRef db, std::string realm_identifier,
696
                         Session::Config config = {})
697
    {
3,790✔
698
        //  *ClientServerFixture uses the service identifier "/realm-sync" to distinguish Sync
1,896✔
699
        //  connections, while the MongoDB/Stitch-based Sync server does not.
1,896✔
700
        config.service_identifier = "/realm-sync";
3,790✔
701
        config.realm_identifier = std::move(realm_identifier);
3,790✔
702
        config.server_port = m_server_ports[server_index];
3,790✔
703
        config.server_address = "localhost";
3,790✔
704

1,896✔
705
        Session session{*m_clients[client_index], std::move(db), nullptr, nullptr, std::move(config)};
3,790✔
706
        if (m_connection_state_change_listeners[client_index]) {
3,790✔
707
            session.set_connection_state_change_listener(m_connection_state_change_listeners[client_index]);
35✔
708
        }
35✔
709
        else {
3,755✔
710
            auto fallback_listener = [this](ConnectionState state, std::optional<SessionErrorInfo> error) {
6,062✔
711
                if (state != ConnectionState::disconnected)
6,062✔
712
                    return;
6,062✔
713
                REALM_ASSERT(error);
×
714
                unit_test::TestContext& test_context = m_test_context;
×
715
                test_context.logger->error("Client disconnect: %1 (is_fatal=%2)", error->status, error->is_fatal);
×
716
                bool client_error_occurred = true;
×
717
                CHECK_NOT(client_error_occurred);
×
718
                stop();
×
719
            };
×
720
            session.set_connection_state_change_listener(fallback_listener);
3,755✔
721
        }
3,755✔
722
        return session;
3,790✔
723
    }
3,790✔
724

725
    Session make_bound_session(int client_index, DBRef db, int server_index, std::string server_path,
726
                               Session::Config config = {})
727
    {
2,488✔
728
        return make_bound_session(client_index, std::move(db), server_index, std::move(server_path),
2,488✔
729
                                  g_signed_test_user_token, std::move(config));
2,488✔
730
    }
2,488✔
731

732
    Session make_bound_session(int client_index, DBRef db, int server_index, std::string server_path,
733
                               std::string signed_user_token, Session::Config config = {})
734
    {
2,499✔
735
        config.signed_user_token = std::move(signed_user_token);
2,499✔
736
        Session session =
2,499✔
737
            make_session(client_index, server_index, std::move(db), std::move(server_path), std::move(config));
2,499✔
738
        session.bind();
2,499✔
739
        return session;
2,499✔
740
    }
2,499✔
741

742
    void cancel_reconnect_delay(int client_index)
743
    {
904✔
744
        get_client(client_index).cancel_reconnect_delay();
904✔
745
    }
904✔
746

747
    void allow_server_errors(int server_index, uint_fast64_t max_num_errors)
748
    {
12✔
749
        m_allow_server_errors[server_index] = uint_least64_t(max_num_errors);
12✔
750
    }
12✔
751

752
    void set_fake_token_expiration_time(std::int_fast64_t seconds_since_epoch)
753
    {
×
754
        using time_point = sync::Clock::time_point;
×
755
        auto duration_1 = std::chrono::seconds(seconds_since_epoch);
×
756
        auto duration_2 = std::chrono::duration_cast<time_point::duration>(duration_1);
×
757
        auto now_1 = m_fake_token_expiration_clock.now();
×
758
        auto now_2 = time_point{duration_2};
×
759
        REALM_ASSERT(now_2 >= now_1);
×
760
        m_fake_token_expiration_clock.add_time(now_2 - now_1);
×
761
    }
×
762

763
    void set_server_connection_reaper_timeout(milliseconds_type timeout)
764
    {
2✔
765
        for (int i = 0; i < m_num_servers; ++i)
4✔
766
            m_servers[i]->set_connection_reaper_timeout(timeout);
2✔
767
    }
2✔
768

769
    void close_server_side_connections()
770
    {
10✔
771
        for (int i = 0; i < m_num_servers; ++i)
20✔
772
            m_servers[i]->close_connections();
10✔
773
    }
10✔
774

775
    bool wait_for_session_terminations_or_client_stopped()
776
    {
6✔
777
        for (int i = 0; i < m_num_clients; ++i) {
12✔
778
            if (!m_clients[i]->wait_for_session_terminations_or_client_stopped())
6✔
779
                return false;
×
780
        }
6✔
781
        return true;
6✔
782
    }
6✔
783

784
    std::string map_virtual_to_real_path(int server_index, const std::string& virt_path)
785
    {
36✔
786
        std::string real_path;
36✔
787
        if (get_server(server_index).map_virtual_to_real_path(virt_path, real_path)) // Throws
36✔
788
            return real_path;
36✔
789
        throw std::runtime_error("Bad virtual path");
×
790
    }
×
791

792
    void inform_server_about_external_change(int server_index, const std::string& virt_path)
793
    {
2,400✔
794
        get_server(server_index).recognize_external_change(virt_path); // Throws
2,400✔
795
    }
2,400✔
796

797
private:
798
    using ConnectionStateChangeListener = Session::ConnectionStateChangeListener;
799
    using port_type = Session::port_type;
800
    std::shared_ptr<util::Logger> m_logger;
801
    const int m_num_servers;
802
    const int m_num_clients;
803
    const bool m_enable_server_ssl;
804
    unit_test::TestContext& m_test_context;
805
    std::vector<std::shared_ptr<util::Logger>> m_server_loggers;
806
    std::vector<std::shared_ptr<util::Logger>> m_client_loggers;
807
    std::vector<std::unique_ptr<Server>> m_servers;
808
    std::vector<std::unique_ptr<Client>> m_clients;
809
    std::vector<std::function<ConnectionStateChangeListener>> m_connection_state_change_listeners;
810
    std::vector<port_type> m_server_ports;
811
    std::vector<ThreadWrapper> m_server_threads;
812
    std::vector<std::shared_ptr<websocket::DefaultSocketProvider>> m_client_socket_providers;
813
    std::vector<std::pair<int, int>> m_simulated_server_error_rates;
814
    std::vector<std::pair<int, int>> m_simulated_client_error_rates;
815
    std::vector<uint_least64_t> m_allow_server_errors;
816
    FakeClock m_fake_token_expiration_clock;
817

818
    static std::optional<std::array<char, 64>> make_crypt_key(const std::string& key)
819
    {
333✔
820
        if (!key.empty()) {
333✔
821
            if (key.size() != 64)
2✔
822
                throw std::runtime_error("Encryption key has wrong size");
×
823
            std::array<char, 64> key_2;
2✔
824
            std::copy(key.begin(), key.end(), key_2.data());
2✔
825
            return key_2;
2✔
826
        }
2✔
827
        return {};
331✔
828
    }
331✔
829

830
    void run_server(int i)
831
    {
334✔
832
        auto do_run_server = [this, i] {
334✔
833
            auto sim = m_simulated_server_error_rates[i];
334✔
834
            if (sim.first != 0) {
334✔
835
                using sf = _impl::SimulatedFailure;
2✔
836
                sf::RandomPrimeGuard pg(sf::sync_server__read_head, sim.first, sim.second,
2✔
837
                                        random_int<uint_fast64_t>()); // Seed from global generator
2✔
838
                m_servers[i]->run();
2✔
839
            }
2✔
840
            else {
332✔
841
                m_servers[i]->run();
332✔
842
            }
332✔
843
            m_servers[i]->stop();
334✔
844
        };
334✔
845
        unit_test::TestContext& test_context = m_test_context;
334✔
846
        if (CHECK_NOTHROW(do_run_server()))
334✔
847
            return;
334✔
848
        stop();
×
849
        m_server_loggers[i]->error("Exception was throw from server[%1]'s event loop", i + 1);
×
850
    }
×
851
};
852

853

854
class ClientServerFixture : public MultiClientServerFixture {
855
public:
856
    using Config = MultiClientServerFixture::Config;
857

858
    ClientServerFixture(std::string server_dir, unit_test::TestContext& test_context, Config config = {})
859
        : MultiClientServerFixture{1, 1, std::move(server_dir), test_context, std::move(config)}
860
    {
302✔
861
    }
302✔
862

863
    // Set an error handler to be used for all sessions of the client (\a
864
    // handler will be copied for each session). Must be called before
865
    // make_session().
866
    void set_client_side_error_handler(std::function<ErrorHandler> handler)
867
    {
31✔
868
        MultiClientServerFixture::set_client_side_error_handler(0, std::move(handler));
31✔
869
    }
31✔
870

871
    // Must be called before start().
872
    void set_client_side_error_rate(int n, int m)
873
    {
4✔
874
        MultiClientServerFixture::set_client_side_error_rate(0, n, m);
4✔
875
    }
4✔
876

877
    // Must be called before start().
878
    void set_server_side_error_rate(int n, int m)
879
    {
2✔
880
        MultiClientServerFixture::set_server_side_error_rate(0, n, m);
2✔
881
    }
2✔
882

883
    Client& get_client() noexcept
884
    {
×
885
        return MultiClientServerFixture::get_client(0);
×
886
    }
×
887

888
    Server& get_server() noexcept
889
    {
3✔
890
        return MultiClientServerFixture::get_server(0);
3✔
891
    }
3✔
892

893
    Session make_session(DBRef db, std::string realm_identifier, Session::Config&& config = {})
894
    {
893✔
895
        return MultiClientServerFixture::make_session(0, 0, std::move(db), std::move(realm_identifier),
893✔
896
                                                      std::move(config));
893✔
897
    }
893✔
898
    Session make_session(std::string const& path, std::string realm_identifier, Session::Config&& config = {})
899
    {
44✔
900
        auto db = DB::create(make_client_replication(), path);
44✔
901
        return MultiClientServerFixture::make_session(0, 0, std::move(db), std::move(realm_identifier),
44✔
902
                                                      std::move(config));
44✔
903
    }
44✔
904

905
    Session make_bound_session(DBRef db, std::string server_path = "/test", Session::Config&& config = {})
906
    {
2,480✔
907
        return MultiClientServerFixture::make_bound_session(0, std::move(db), 0, std::move(server_path),
2,480✔
908
                                                            std::move(config));
2,480✔
909
    }
2,480✔
910

911
    Session make_bound_session(DBRef db, std::string server_path, std::string signed_user_token,
912
                               Session::Config&& config = {})
913
    {
11✔
914
        return MultiClientServerFixture::make_bound_session(0, std::move(db), 0, std::move(server_path),
11✔
915
                                                            std::move(signed_user_token), std::move(config));
11✔
916
    }
11✔
917

918
    void cancel_reconnect_delay()
919
    {
904✔
920
        MultiClientServerFixture::cancel_reconnect_delay(0); // Throws
904✔
921
    }
904✔
922

923
    void allow_server_errors(uint_fast64_t max_num_errors)
924
    {
×
925
        MultiClientServerFixture::allow_server_errors(0, max_num_errors);
×
926
    }
×
927

928
    std::string map_virtual_to_real_path(const std::string& virt_path)
929
    {
36✔
930
        return MultiClientServerFixture::map_virtual_to_real_path(0, virt_path); // Throws
36✔
931
    }
36✔
932

933
    void inform_server_about_external_change(const std::string& virt_path)
934
    {
2,400✔
935
        MultiClientServerFixture::inform_server_about_external_change(0, virt_path); // Throws
2,400✔
936
    }
2,400✔
937
};
938

939

940
class RealmFixture {
941
public:
942
    using ErrorHandler = MultiClientServerFixture::ErrorHandler;
943

944
    struct Config : Session::Config {
945
        std::function<ErrorHandler> error_handler;
946
    };
947

948
    RealmFixture(ClientServerFixture&, const std::string& real_path, const std::string& virt_path, Config = {});
949
    RealmFixture(MultiClientServerFixture&, int client_index, int server_index, const std::string& real_path,
950
                 const std::string& virt_path, Config = {});
951
    ~RealmFixture() noexcept;
952

953
    void empty_transact();
954
    void nonempty_transact();
955

956
    using TransactFunc = util::FunctionRef<bool(Transaction&)>;
957

958
    /// Perform a non-serialized transaction synchronously.
959
    bool transact(TransactFunc);
960

961
    bool wait_for_upload_complete_or_client_stopped();
962
    bool wait_for_download_complete_or_client_stopped();
963

964
    using WaitOperCompletionHandler = Session::WaitOperCompletionHandler;
965

966
    void async_wait_for_sync_completion(WaitOperCompletionHandler);
967
    void async_wait_for_upload_completion(WaitOperCompletionHandler);
968
    void async_wait_for_download_completion(WaitOperCompletionHandler);
969

970
private:
971
    struct SelfRef {
972
        util::Mutex mutex;
973
        RealmFixture* ref = nullptr;
974
        SelfRef(RealmFixture* r)
975
            : ref{r}
976
        {
×
977
        }
×
978
    };
979

980
    const std::shared_ptr<SelfRef> m_self_ref;
981
    DBRef m_db;
982
    sync::Session m_session;
983

984
    void setup_error_handler(util::UniqueFunction<ErrorHandler>);
985
};
986

987

988
inline RealmFixture::RealmFixture(ClientServerFixture& client_server_fixture, const std::string& real_path,
989
                                  const std::string& virt_path, Config config)
990
    : m_self_ref{std::make_shared<SelfRef>(this)}                                       // Throws
991
    , m_db{DB::create(make_client_replication(), real_path)}                            // Throws
992
    , m_session{client_server_fixture.make_session(m_db, virt_path, std::move(config))} // Throws
993
{
994
    if (config.error_handler)
995
        setup_error_handler(std::move(config.error_handler));
996
    m_session.bind();
997
}
998

999

1000
inline RealmFixture::RealmFixture(MultiClientServerFixture& client_server_fixture, int client_index, int server_index,
1001
                                  const std::string& real_path, const std::string& virt_path, Config config)
1002
    : m_self_ref{std::make_shared<SelfRef>(this)}            // Throws
1003
    , m_db{DB::create(make_client_replication(), real_path)} // Throws
1004
    , m_session{client_server_fixture.make_session(client_index, server_index, m_db, virt_path, std::move(config))}
1005
// Throws
1006
{
1007
    if (config.error_handler)
1008
        setup_error_handler(std::move(config.error_handler));
1009
    m_session.bind();
1010
}
1011

1012
inline RealmFixture::~RealmFixture() noexcept
1013
{
1014
    util::LockGuard lock{m_self_ref->mutex};
1015
    m_self_ref->ref = nullptr;
1016
}
1017

1018
inline void RealmFixture::empty_transact()
1019
{
×
1020
    transact([](Transaction&) {
×
1021
        return true;
×
1022
    });
×
1023
}
×
1024

1025
inline void RealmFixture::nonempty_transact()
1026
{
×
1027
    auto func = [](Transaction& tr) {
×
1028
        TableRef table = tr.get_or_add_table_with_primary_key("class_Table", type_Int, "id");
×
1029
        int id = 1;
×
1030
        bool did_create = false;
×
1031
        while (!did_create)
×
1032
            table->create_object_with_primary_key(id++, &did_create);
×
1033
        return true;
×
1034
    };
×
1035
    transact(func);
×
1036
}
×
1037

1038
inline bool RealmFixture::transact(TransactFunc transact_func)
1039
{
×
1040
    auto tr = m_db->start_write(); // Throws
×
1041
    if (!transact_func(*tr))       // Throws
×
1042
        return false;
×
1043
    version_type new_version = tr->commit();        // Throws
×
1044
    m_session.nonsync_transact_notify(new_version); // Throws
×
1045
    return true;
×
1046
}
×
1047

1048
inline bool RealmFixture::wait_for_upload_complete_or_client_stopped()
1049
{
×
1050
    return m_session.wait_for_upload_complete_or_client_stopped();
×
1051
}
×
1052

1053
inline bool RealmFixture::wait_for_download_complete_or_client_stopped()
1054
{
×
1055
    return m_session.wait_for_download_complete_or_client_stopped();
×
1056
}
×
1057

1058
inline void RealmFixture::async_wait_for_sync_completion(WaitOperCompletionHandler handler)
1059
{
×
1060
    m_session.async_wait_for_sync_completion(std::move(handler));
×
1061
}
×
1062

1063
inline void RealmFixture::async_wait_for_upload_completion(WaitOperCompletionHandler handler)
1064
{
×
1065
    m_session.async_wait_for_upload_completion(std::move(handler));
×
1066
}
×
1067

1068
inline void RealmFixture::async_wait_for_download_completion(WaitOperCompletionHandler handler)
1069
{
×
1070
    m_session.async_wait_for_download_completion(std::move(handler));
×
1071
}
×
1072

1073
inline void RealmFixture::setup_error_handler(util::UniqueFunction<ErrorHandler> handler)
1074
{
×
1075
    auto listener = [handler = std::move(handler)](ConnectionState state,
×
1076
                                                   const std::optional<SessionErrorInfo>& error_info) {
×
1077
        if (state != ConnectionState::disconnected)
×
1078
            return;
×
1079
        REALM_ASSERT(error_info);
×
1080
        handler(error_info->status, error_info->is_fatal);
×
1081
    };
×
1082
    m_session.set_connection_state_change_listener(std::move(listener));
×
1083
}
×
1084
} // namespace realm::fixtures
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc