[READ-ONLY] Mirror of https://github.com/VibeDevelopers/Comet.
0

Configure Feed

Select the types of activity you want to include in your feed.

Add tests for labeled-response

authored by

Ryan Schmidt and committed by
Aaron Jones
(Jul 3, 2026, 5:28 PM UTC) 3f498678 cb7cd7fe

+774
+1
tests/Makefile.am
··· 5 5 msgbuf_parse1 \ 6 6 msgbuf_unparse1 \ 7 7 hostmask1 \ 8 + labeled_response1 \ 8 9 privilege1 \ 9 10 rb_dictionary1 \ 10 11 rb_snprintf_append1 \
+726
tests/labeled_response1.c
··· 1 + /* 2 + * Copyright (c) 2026 Ryan Schmidt <skizzerz@skizzerz.net> 3 + * 4 + * This program is free software; you can redistribute it and/or modify 5 + * it under the terms of the GNU General Public License as published by 6 + * the Free Software Foundation; either version 2 of the License, or 7 + * (at your option) any later version. 8 + * 9 + * This program is distributed in the hope that it will be useful, 10 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 + * GNU General Public License for more details. 13 + * 14 + * You should have received a copy of the GNU General Public License 15 + * along with this program; if not, write to the Free Software 16 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 + */ 18 + 19 + #include <math.h> 20 + 21 + #include "stdinc.h" 22 + #include "tap/basic.h" 23 + #include "ircd_util.h" 24 + #include "client_util.h" 25 + 26 + #include "batch.h" 27 + #include "hash.h" 28 + #include "hostmask.h" 29 + #include "send.h" 30 + #include "s_serv.h" 31 + #include "newconf.h" 32 + #include "response.h" 33 + #include "s_conf.h" 34 + #include "s_newconf.h" 35 + #include "s_user.h" 36 + 37 + #define MSG "%s:%d (%s)", __FILE__, __LINE__, __FUNCTION__ 38 + #define LONG_VALUE_50 "12345678901234567890123456789012345678901234567890" 39 + #define LONG_VALUE_100 LONG_VALUE_50 LONG_VALUE_50 40 + #define LONG_VALUE_200 LONG_VALUE_100 LONG_VALUE_100 41 + 42 + // What time is it? 43 + #define ADVENTURE_TIME "2017-07-14T02:40:00.000Z" 44 + 45 + int rb_gettimeofday(struct timeval *tv, void *tz) 46 + { 47 + if (tv == NULL) { 48 + errno = EFAULT; 49 + return -1; 50 + } 51 + tv->tv_sec = 1500000000; 52 + tv->tv_usec = 0; 53 + return 0; 54 + } 55 + 56 + static struct Client *user; 57 + static struct Client *server; 58 + static struct Client *remote; 59 + static struct Client *server2; 60 + static struct Client *remote2; 61 + static struct Client *server3; 62 + static struct Client *remote3; 63 + static struct Channel *channel; 64 + static struct Channel *lchannel; 65 + 66 + static struct Client *local_chan_o; 67 + static struct Client *local_chan_ov; 68 + static struct Client *local_chan_v; 69 + static struct Client *local_chan_p; 70 + static struct Client *local_no_chan; 71 + 72 + static struct Client *remote_chan_o; 73 + static struct Client *remote_chan_ov; 74 + static struct Client *remote_chan_v; 75 + static struct Client *remote_chan_p; 76 + 77 + static struct Client *remote2_chan_p; 78 + static struct Client *remote2_no_chan; 79 + 80 + static char batch1[BATCH_ID_LEN]; 81 + static char batch2[BATCH_ID_LEN]; 82 + static char batch3[BATCH_ID_LEN]; 83 + static char batch4[BATCH_ID_LEN]; 84 + static char batch5[BATCH_ID_LEN]; 85 + static char batch6[BATCH_ID_LEN]; 86 + static char batch7[BATCH_ID_LEN]; 87 + static char batch8[BATCH_ID_LEN]; 88 + 89 + static void standard_init(void) 90 + { 91 + user = make_local_person_id(TEST_NICK, TEST_ID); 92 + server = make_remote_server_full(&me, TEST_SERVER_NAME, TEST_SERVER_ID); 93 + remote = make_remote_person_id(server, TEST_REMOTE_NICK, TEST_REMOTE_ID); 94 + server2 = make_remote_server_full(&me, TEST_SERVER2_NAME, TEST_SERVER2_ID); 95 + remote2 = make_remote_person_id(server2, TEST_REMOTE2_NICK, TEST_REMOTE2_ID); 96 + server3 = make_remote_server_full(&me, TEST_SERVER3_NAME, TEST_SERVER3_ID); 97 + remote3 = make_remote_person_id(server3, TEST_REMOTE3_NICK, TEST_REMOTE3_ID); 98 + 99 + SetServerCap(server, CAP_STAG | CAP_ENCAP); 100 + SetServerCap(server2, CAP_STAG | CAP_ENCAP); 101 + SetServerCap(server3, CAP_STAG | CAP_ENCAP); 102 + 103 + local_chan_o = make_local_person_id("LChanOp", TEST_ME_ID "90001"); 104 + local_chan_ov = make_local_person_id("LChanOpVoice", TEST_ME_ID "90002"); 105 + local_chan_v = make_local_person_id("LChanVoice", TEST_ME_ID "90003"); 106 + local_chan_p = make_local_person_id("LChanPeon", TEST_ME_ID "90004"); 107 + local_no_chan = make_local_person_id("LNoChan", TEST_ME_ID "90005"); 108 + 109 + remote_chan_o = make_remote_person_id(server, "RChanOp", TEST_SERVER_ID "90101"); 110 + remote_chan_ov = make_remote_person_id(server, "RChanOpVoice", TEST_SERVER_ID "90102"); 111 + remote_chan_v = make_remote_person_id(server, "RChanVoice", TEST_SERVER_ID "90103"); 112 + remote_chan_p = make_remote_person_id(server, "RChanPeon", TEST_SERVER_ID "90104"); 113 + 114 + remote2_chan_p = make_remote_person_id(server2, "R2ChanPeon", TEST_SERVER2_ID "90204"); 115 + remote2_no_chan = make_remote_person_id(server2, "R2NoChan", TEST_SERVER2_ID "90205"); 116 + 117 + channel = make_channel(); 118 + 119 + add_user_to_channel(channel, local_chan_o, CHFL_CHANOP); 120 + add_user_to_channel(channel, local_chan_ov, CHFL_CHANOP | CHFL_VOICE); 121 + add_user_to_channel(channel, local_chan_v, CHFL_VOICE); 122 + add_user_to_channel(channel, local_chan_p, CHFL_PEON); 123 + 124 + add_user_to_channel(channel, remote_chan_o, CHFL_CHANOP); 125 + add_user_to_channel(channel, remote_chan_ov, CHFL_CHANOP | CHFL_VOICE); 126 + add_user_to_channel(channel, remote_chan_v, CHFL_VOICE); 127 + add_user_to_channel(channel, remote_chan_p, CHFL_PEON); 128 + 129 + add_user_to_channel(channel, remote2_chan_p, CHFL_PEON); 130 + 131 + lchannel = get_or_create_channel(&me, "&test", NULL); 132 + 133 + add_user_to_channel(lchannel, user, CHFL_PEON); 134 + add_user_to_channel(lchannel, remote, CHFL_PEON); 135 + add_user_to_channel(lchannel, remote2, CHFL_PEON); 136 + add_user_to_channel(lchannel, remote3, CHFL_PEON); 137 + 138 + /* for consistent batch IDs */ 139 + srand(0); 140 + } 141 + 142 + static void init_large_list(void) 143 + { 144 + char chname[CHANNELLEN]; 145 + 146 + SetClientCap(user, CLICAP_LABELED_RESPONSE | CLICAP_BATCH); 147 + attach_conf(user, find_conf_by_address(user->host, user->sockhost, NULL, 148 + (struct sockaddr *)&user->localClient->ip, CONF_CLIENT, GET_SS_FAMILY(&user->localClient->ip), 149 + user->username, user->localClient->auth_user)); 150 + 151 + for (int i = 0; i < 100; i++) 152 + { 153 + snprintf(chname, sizeof(chname), "#safelist%02d", i); 154 + struct Channel *c = get_or_create_channel(user, chname, NULL); 155 + add_user_to_channel(c, user, CHFL_PEON); 156 + set_channel_topic(c, LONG_VALUE_200, "test!test@test", 0); 157 + } 158 + } 159 + 160 + static void make_local_person_admin(struct Client *client_p) 161 + { 162 + struct oper_conf *oper_p = find_oper_conf(client_p->username, client_p->orighost, client_p->sockhost, "admin"); 163 + oper_up(client_p, oper_p); 164 + drain_client_sendq(client_p); 165 + drain_client_sendq(server); 166 + drain_client_sendq(server2); 167 + drain_client_sendq(server3); 168 + } 169 + 170 + static void standard_free(void) 171 + { 172 + remove_remote_person(remote2_chan_p); 173 + remove_remote_person(remote2_no_chan); 174 + 175 + remove_remote_person(remote_chan_o); 176 + remove_remote_person(remote_chan_ov); 177 + remove_remote_person(remote_chan_v); 178 + remove_remote_person(remote_chan_p); 179 + 180 + remove_local_person(local_chan_o); 181 + remove_local_person(local_chan_ov); 182 + remove_local_person(local_chan_v); 183 + remove_local_person(local_chan_p); 184 + remove_local_person(local_no_chan); 185 + 186 + remove_remote_person(remote3); 187 + remove_remote_server(server3); 188 + remove_remote_person(remote2); 189 + remove_remote_server(server2); 190 + remove_remote_person(remote); 191 + remove_remote_server(server); 192 + 193 + if (user != NULL) 194 + remove_local_person(user); 195 + } 196 + 197 + static void single_response(void) 198 + { 199 + char expected[BUFSIZE]; 200 + 201 + standard_init(); 202 + SetClientCap(user, CLICAP_LABELED_RESPONSE | CLICAP_BATCH | CLICAP_ECHO_MESSAGE); 203 + 204 + client_util_parse(user, "@label=foo PRIVMSG &test :hello"); 205 + snprintf(expected, sizeof(expected), "@label=foo :%s!%s@%s PRIVMSG &test :hello" CRLF, user->name, user->username, user->host); 206 + is_client_sendq(expected, user, MSG); 207 + 208 + /* echo-message self-PMs generate the unlabeled delivery first and the labeled echo second */ 209 + client_util_parse(user, "@label=foo PRIVMSG " TEST_NICK " :hello"); 210 + snprintf(expected, sizeof(expected), ":%s!%s@%s PRIVMSG %s :hello" CRLF, user->name, user->username, user->host, user->name); 211 + is_client_sendq_one(expected, user, MSG); 212 + snprintf(expected, sizeof(expected), "@label=foo :%s!%s@%s PRIVMSG %s :hello" CRLF, user->name, user->username, user->host, user->name); 213 + is_client_sendq(expected, user, MSG); 214 + 215 + ClearClientCap(user, CLICAP_ECHO_MESSAGE); 216 + 217 + /* without echo-message we expect a labeled ACK instead, still after the delivered message */ 218 + client_util_parse(user, "@label=foo PRIVMSG " TEST_NICK " :hello"); 219 + snprintf(expected, sizeof(expected), ":%s!%s@%s PRIVMSG %s :hello" CRLF, user->name, user->username, user->host, user->name); 220 + is_client_sendq_one(expected, user, MSG); 221 + snprintf(expected, sizeof(expected), "@label=foo :%s ACK" CRLF, me.name); 222 + is_client_sendq(expected, user, MSG); 223 + 224 + client_util_parse(user, "@label=foo PRIVMSG #notachannel :hello"); 225 + snprintf(expected, sizeof(expected), "@label=foo :%s 401 %s #notachannel :No such nick/channel" CRLF, me.name, user->name); 226 + is_client_sendq(expected, user, MSG); 227 + 228 + /* other (non-PRIVMSG) commands that return single-line responses */ 229 + client_util_parse(user, "@label=foo USERHOST " TEST_NICK); 230 + snprintf(expected, sizeof(expected), "@label=foo :%s 302 %s :%s=+%s@%s " CRLF, me.name, user->name, user->name, user->username, user->sockhost); 231 + is_client_sendq(expected, user, MSG); 232 + 233 + client_util_parse(user, "@label=foo TOPIC " TEST_CHANNEL); 234 + snprintf(expected, sizeof(expected), "@label=foo :%s 331 %s %s :No topic is set." CRLF, me.name, user->name, channel->chname); 235 + is_client_sendq(expected, user, MSG); 236 + 237 + standard_free(); 238 + } 239 + 240 + static void single_response__multi_client(void) 241 + { 242 + char expected[BUFSIZE]; 243 + rb_dictionary *dict = rb_dictionary_get_for_tests("remote labeled responses"); 244 + 245 + standard_init(); 246 + SetClientCap(user, CLICAP_LABELED_RESPONSE | CLICAP_BATCH); 247 + SetClientCap(local_chan_p, CLICAP_LABELED_RESPONSE | CLICAP_BATCH); 248 + SetClientCap(local_chan_v, CLICAP_LABELED_RESPONSE | CLICAP_BATCH); 249 + /* remote WHOIS has a server-wide rate limit; ensure we don't trip it */ 250 + make_local_person_admin(local_chan_p); 251 + make_local_person_admin(local_chan_v); 252 + 253 + client_util_parse(local_chan_p, "@label=foo WHOIS RChanOp RChanOp"); 254 + client_util_parse(local_chan_v, "@label=foo WHOIS RChanOp RChanOp"); 255 + is_int(1, rb_dlink_list_length(&local_chan_p->localClient->pending_remote_responses), MSG); 256 + is_int(1, rb_dlink_list_length(&local_chan_v->localClient->pending_remote_responses), MSG); 257 + is_int(2, rb_dictionary_size(dict), MSG); 258 + 259 + client_util_parse(user, "@label=foo PRIVMSG #notachannel :hello"); 260 + snprintf(expected, sizeof(expected), "@label=foo :%s 401 %s #notachannel :No such nick/channel" CRLF, me.name, user->name); 261 + is_client_sendq(expected, user, MSG); 262 + 263 + is_int(1, rb_dlink_list_length(&local_chan_p->localClient->pending_remote_responses), MSG); 264 + is_int(1, rb_dlink_list_length(&local_chan_v->localClient->pending_remote_responses), MSG); 265 + is_int(2, rb_dictionary_size(dict), MSG); 266 + 267 + standard_free(); 268 + } 269 + 270 + static void multi_response(void) 271 + { 272 + char expected[BUFSIZE]; 273 + char *line; 274 + 275 + standard_init(); 276 + SetClientCap(user, CLICAP_LABELED_RESPONSE | CLICAP_BATCH); 277 + 278 + client_util_parse(user, "@label=foo CAP LIST"); 279 + snprintf(expected, sizeof(expected), "@label=foo :%s BATCH +%s labeled-response" CRLF, me.name, batch1); 280 + is_client_sendq_one(expected, user, MSG); 281 + snprintf(expected, sizeof(expected), "@batch=%s :%s CAP %s LIST :batch labeled-response" CRLF, batch1, me.name, user->name); 282 + is_client_sendq_one(expected, user, MSG); 283 + snprintf(expected, sizeof(expected), ":%s BATCH -%s" CRLF, me.name, batch1); 284 + is_client_sendq(expected, user, MSG); 285 + 286 + client_util_parse(user, "@label=foo LIST >0"); 287 + snprintf(expected, sizeof(expected), "@label=foo :%s BATCH +%s labeled-response" CRLF, me.name, batch2); 288 + is_client_sendq_one(expected, user, MSG); 289 + snprintf(expected, sizeof(expected), "@batch=%s :%s 321 %s Channel :Users Name" CRLF, batch2, me.name, user->name); 290 + is_client_sendq_one(expected, user, MSG); 291 + snprintf(expected, sizeof(expected), "@batch=%s :%s 322 %s #test 9 :" CRLF, batch2, me.name, user->name); 292 + is_client_sendq_one(expected, user, MSG); 293 + snprintf(expected, sizeof(expected), "@batch=%s :%s 322 %s &test 4 :" CRLF, batch2, me.name, user->name); 294 + is_client_sendq_one(expected, user, MSG); 295 + snprintf(expected, sizeof(expected), "@batch=%s :%s 323 %s :End of /LIST" CRLF, batch2, me.name, user->name); 296 + is_client_sendq_one(expected, user, MSG); 297 + snprintf(expected, sizeof(expected), ":%s BATCH -%s" CRLF, me.name, batch2); 298 + is_client_sendq(expected, user, MSG); 299 + 300 + client_util_parse(user, "@label=foo WHOIS " TEST_REMOTE_NICK); 301 + snprintf(expected, sizeof(expected), "@label=foo :%s BATCH +%s labeled-response" CRLF, me.name, batch3); 302 + is_client_sendq_one(expected, user, MSG); 303 + snprintf(expected, sizeof(expected), "@batch=%s ", batch3); 304 + while (*(line = get_client_sendq(user)) != '\0') 305 + { 306 + if (*line == '@') 307 + { 308 + *(line + strlen(expected)) = '\0'; 309 + is_string(expected, line, MSG); 310 + } 311 + else 312 + break; 313 + } 314 + snprintf(expected, sizeof(expected), ":%s BATCH -%s" CRLF, me.name, batch3); 315 + is_string(expected, line, MSG); 316 + is_client_sendq_empty(user, MSG); 317 + 318 + client_util_parse(user, "@label=foo JOIN " TEST_CHANNEL); 319 + snprintf(expected, sizeof(expected), "@label=foo :%s BATCH +%s labeled-response" CRLF, me.name, batch4); 320 + is_client_sendq_one(expected, user, MSG); 321 + snprintf(expected, sizeof(expected), "@batch=%s ", batch4); 322 + while (*(line = get_client_sendq(user)) != '\0') 323 + { 324 + if (*line == '@') 325 + { 326 + *(line + strlen(expected)) = '\0'; 327 + is_string(expected, line, MSG); 328 + } 329 + else 330 + break; 331 + } 332 + snprintf(expected, sizeof(expected), ":%s BATCH -%s" CRLF, me.name, batch4); 333 + is_string(expected, line, MSG); 334 + is_client_sendq_empty(user, MSG); 335 + 336 + client_util_parse(user, "@label=foo WHO " TEST_CHANNEL); 337 + snprintf(expected, sizeof(expected), "@label=foo :%s BATCH +%s labeled-response" CRLF, me.name, batch5); 338 + is_client_sendq_one(expected, user, MSG); 339 + snprintf(expected, sizeof(expected), "@batch=%s ", batch5); 340 + while (*(line = get_client_sendq(user)) != '\0') 341 + { 342 + if (*line == '@') 343 + { 344 + *(line + strlen(expected)) = '\0'; 345 + is_string(expected, line, MSG); 346 + } 347 + else 348 + break; 349 + } 350 + snprintf(expected, sizeof(expected), ":%s BATCH -%s" CRLF, me.name, batch5); 351 + is_string(expected, line, MSG); 352 + is_client_sendq_empty(user, MSG); 353 + 354 + standard_free(); 355 + } 356 + 357 + static void remote_response__hunted_server(void) 358 + { 359 + char expected[BUFSIZE]; 360 + rb_dictionary *dict = rb_dictionary_get_for_tests("remote labeled responses"); 361 + 362 + standard_init(); 363 + SetClientCap(user, CLICAP_LABELED_RESPONSE | CLICAP_BATCH); 364 + 365 + client_util_parse(user, "@label=foo TIME " TEST_SERVER_NAME); 366 + snprintf(expected, sizeof(expected), "@label=foo :%s BATCH +%s labeled-response" CRLF, me.name, batch1); 367 + is_client_sendq(expected, user, MSG); 368 + snprintf(expected, sizeof(expected), 369 + "@time=" ADVENTURE_TIME ";solanum.chat/response=%s,%s,%s :%s TIME :" TEST_SERVER_ID CRLF, 370 + user->id, batch1, server->name, user->id); 371 + is_client_sendq(expected, server, MSG); 372 + is_int(1, rb_dictionary_size(dict), MSG); 373 + is_int(1, rb_dlink_list_length(&user->localClient->pending_remote_responses), MSG); 374 + 375 + snprintf(expected, sizeof(expected), 376 + "@solanum.chat/response=%s,%s,%s :%s 391 %s %s :Friday July 14 2017 -- 02:40:00 +00:00" CRLF, 377 + user->id, batch1, server->name, server->id, user->id, server->name); 378 + client_util_parse(server, expected); 379 + snprintf(expected, sizeof(expected), 380 + "@batch=%s :%s 391 %s %s :Friday July 14 2017 -- 02:40:00 +00:00" CRLF, 381 + batch1, server->name, user->name, server->name); 382 + is_client_sendq(expected, user, MSG); 383 + is_int(1, rb_dictionary_size(dict), MSG); 384 + is_int(1, rb_dlink_list_length(&user->localClient->pending_remote_responses), MSG); 385 + 386 + snprintf(expected, sizeof(expected), "@solanum.chat/response=%s,%s,%s :%s ENCAP %s ACK", user->id, batch1, server->name, server->id, me.name); 387 + client_util_parse(server, expected); 388 + snprintf(expected, sizeof(expected), ":%s BATCH -%s" CRLF, me.name, batch1); 389 + is_client_sendq(expected, user, MSG); 390 + is_int(0, rb_dictionary_size(dict), MSG); 391 + is_int(0, rb_dlink_list_length(&user->localClient->pending_remote_responses), MSG); 392 + 393 + standard_free(); 394 + } 395 + 396 + static void remote_response__hunted_client(void) 397 + { 398 + char expected[BUFSIZE]; 399 + rb_dictionary *dict = rb_dictionary_get_for_tests("remote labeled responses"); 400 + 401 + standard_init(); 402 + SetClientCap(user, CLICAP_LABELED_RESPONSE | CLICAP_BATCH); 403 + 404 + client_util_parse(user, "@label=foo TIME " TEST_REMOTE_NICK); 405 + snprintf(expected, sizeof(expected), "@label=foo :%s BATCH +%s labeled-response" CRLF, me.name, batch1); 406 + is_client_sendq(expected, user, MSG); 407 + snprintf(expected, sizeof(expected), 408 + "@time=" ADVENTURE_TIME ";solanum.chat/response=%s,%s,%s :%s TIME :" TEST_REMOTE_ID CRLF, 409 + user->id, batch1, server->name, user->id); 410 + is_client_sendq(expected, server, MSG); 411 + is_int(1, rb_dictionary_size(dict), MSG); 412 + is_int(1, rb_dlink_list_length(&user->localClient->pending_remote_responses), MSG); 413 + is_int(1, ((struct ResponseInfo *)user->localClient->pending_remote_responses.head->data)->remote_response, MSG); 414 + 415 + snprintf(expected, sizeof(expected), 416 + "@solanum.chat/response=%s,%s,%s :%s 391 %s %s :Friday July 14 2017 -- 02:40:00 +00:00" CRLF, 417 + user->id, batch1, server->name, server->id, user->id, server->name); 418 + client_util_parse(server, expected); 419 + snprintf(expected, sizeof(expected), 420 + "@batch=%s :%s 391 %s %s :Friday July 14 2017 -- 02:40:00 +00:00" CRLF, 421 + batch1, server->name, user->name, server->name); 422 + is_client_sendq(expected, user, MSG); 423 + is_int(1, rb_dictionary_size(dict), MSG); 424 + is_int(1, rb_dlink_list_length(&user->localClient->pending_remote_responses), MSG); 425 + 426 + snprintf(expected, sizeof(expected), "@solanum.chat/response=%s,%s,%s :%s ENCAP %s ACK", user->id, batch1, server->name, server->id, me.name); 427 + client_util_parse(server, expected); 428 + snprintf(expected, sizeof(expected), ":%s BATCH -%s" CRLF, me.name, batch1); 429 + is_client_sendq(expected, user, MSG); 430 + is_int(0, rb_dictionary_size(dict), MSG); 431 + is_int(0, rb_dlink_list_length(&user->localClient->pending_remote_responses), MSG); 432 + 433 + standard_free(); 434 + } 435 + 436 + static void remote_response__mask(void) 437 + { 438 + char expected[BUFSIZE]; 439 + rb_dictionary *dict = rb_dictionary_get_for_tests("remote labeled responses"); 440 + 441 + standard_init(); 442 + SetClientCap(user, CLICAP_LABELED_RESPONSE | CLICAP_BATCH); 443 + make_local_person_admin(user); 444 + 445 + client_util_parse(user, "@label=foo MODLIST foo remote*"); 446 + snprintf(expected, sizeof(expected), "@label=foo :%s BATCH +%s labeled-response" CRLF, me.name, batch1); 447 + is_client_sendq(expected, user, MSG); 448 + snprintf(expected, sizeof(expected), 449 + "@time=" ADVENTURE_TIME ";solanum.chat/response=%s,%s,remote* :%s ENCAP remote* MODLIST foo" CRLF, 450 + user->id, batch1, user->id); 451 + is_client_sendq(expected, server, MSG); 452 + is_int(1, rb_dictionary_size(dict), MSG); 453 + is_int(1, rb_dlink_list_length(&user->localClient->pending_remote_responses), MSG); 454 + is_int(3, ((struct ResponseInfo *)user->localClient->pending_remote_responses.head->data)->remote_response, MSG); 455 + 456 + snprintf(expected, sizeof(expected), "@solanum.chat/response=%s,%s,remote* :%s ENCAP %s ACK", user->id, batch1, server->id, me.name); 457 + client_util_parse(server, expected); 458 + snprintf(expected, sizeof(expected), "@solanum.chat/response=%s,%s,remote* :%s ENCAP %s ACK", user->id, batch1, server2->id, me.name); 459 + client_util_parse(server2, expected); 460 + snprintf(expected, sizeof(expected), "@solanum.chat/response=%s,%s,remote* :%s ENCAP %s ACK", user->id, batch1, server3->id, me.name); 461 + client_util_parse(server3, expected); 462 + 463 + snprintf(expected, sizeof(expected), ":%s BATCH -%s" CRLF, me.name, batch1); 464 + is_client_sendq(expected, user, MSG); 465 + is_int(0, rb_dictionary_size(dict), MSG); 466 + is_int(0, rb_dlink_list_length(&user->localClient->pending_remote_responses), MSG); 467 + 468 + standard_free(); 469 + } 470 + 471 + static void remote_response__timeout(void) 472 + { 473 + char expected[BUFSIZE]; 474 + rb_dictionary *dict = rb_dictionary_get_for_tests("remote labeled responses"); 475 + 476 + standard_init(); 477 + SetClientCap(user, CLICAP_LABELED_RESPONSE | CLICAP_BATCH); 478 + 479 + client_util_parse(user, "@label=foo WHOIS " TEST_REMOTE_NICK " " TEST_REMOTE_NICK); 480 + snprintf(expected, sizeof(expected), "@label=foo :%s BATCH +%s labeled-response" CRLF, me.name, batch1); 481 + is_client_sendq(expected, user, MSG); 482 + is_int(1, rb_dictionary_size(dict), MSG); 483 + is_int(1, rb_dlink_list_length(&user->localClient->pending_remote_responses), MSG); 484 + 485 + /* backdate the expiration so the event cleans it up */ 486 + struct ResponseInfo *info = user->localClient->pending_remote_responses.head->data; 487 + info->expires = 1; 488 + rb_run_one_event_for_tests("cleanup_pending_responses"); 489 + snprintf(expected, sizeof(expected), ":%s BATCH -%s" CRLF, me.name, batch1); 490 + is_client_sendq(expected, user, MSG); 491 + is_int(0, rb_dictionary_size(dict), MSG); 492 + is_int(0, rb_dlink_list_length(&user->localClient->pending_remote_responses), MSG); 493 + 494 + /* late response doesn't carry batch tag and late ACK does nothing */ 495 + snprintf(expected, sizeof(expected), 496 + "@solanum.chat/response=%s,%s,%s :%s 318 %s %s :End of /WHOIS list.", 497 + user->id, batch1, server->name, server->id, user->name, remote->name); 498 + client_util_parse(server, expected); 499 + snprintf(expected, sizeof(expected), ":%s 318 %s %s :End of /WHOIS list." CRLF, server->name, user->name, remote->name); 500 + is_client_sendq(expected, user, MSG); 501 + snprintf(expected, sizeof(expected), 502 + "@solanum.chat/response=%s,%s,%s :%s ENCAP %s ACK", 503 + user->id, batch1, server->name, server->id, me.name); 504 + client_util_parse(server, expected); 505 + is_client_sendq_empty(user, MSG); 506 + 507 + standard_free(); 508 + } 509 + 510 + static void response_tag(void) 511 + { 512 + char expected[BUFSIZE]; 513 + 514 + standard_init(); 515 + 516 + /* if we match the mask (3rd part of the tag), we need to send back ENCAP ACK */ 517 + client_util_parse(server, "@solanum.chat/response=" TEST_REMOTE_ID ",foo,* :" TEST_REMOTE_ID " TIME :" TEST_ME_ID); 518 + snprintf(expected, sizeof(expected), 519 + "@time=" ADVENTURE_TIME ";solanum.chat/response=%s,foo,* :%s 391 %s %s :Friday July 14 2017 -- 02:40:00 +00:00" CRLF, 520 + remote->id, me.id, remote->id, me.name); 521 + is_client_sendq_one(expected, server, MSG); 522 + snprintf(expected, sizeof(expected), 523 + "@time=" ADVENTURE_TIME ";solanum.chat/response=%s,foo,* :%s ENCAP %s ACK" CRLF, 524 + remote->id, me.id, server->name); 525 + is_client_sendq(expected, server, MSG); 526 + 527 + /* no ACK if we don't match the mask */ 528 + client_util_parse(server, "@solanum.chat/response=" TEST_REMOTE_ID ",foo,badmask :" TEST_REMOTE_ID " TIME :" TEST_ME_ID); 529 + snprintf(expected, sizeof(expected), 530 + "@time=" ADVENTURE_TIME ";solanum.chat/response=%s,foo,badmask :%s 391 %s %s :Friday July 14 2017 -- 02:40:00 +00:00" CRLF, 531 + remote->id, me.id, remote->id, me.name); 532 + is_client_sendq(expected, server, MSG); 533 + 534 + /* invalid tags don't get passed (and also have no ACK) */ 535 + client_util_parse(server, "@solanum.chat/response=invalid,value :" TEST_REMOTE_ID " TIME :" TEST_ME_ID); 536 + snprintf(expected, sizeof(expected), 537 + "@time=" ADVENTURE_TIME " :%s 391 %s %s :Friday July 14 2017 -- 02:40:00 +00:00" CRLF, 538 + me.id, remote->id, me.name); 539 + is_client_sendq(expected, server, MSG); 540 + 541 + /* we still do an ACK if we get an ENCAP not destined for us */ 542 + client_util_parse(server, "@solanum.chat/response=" TEST_REMOTE_ID ",foo,* :" TEST_REMOTE_ID " ENCAP badmask FOO"); 543 + snprintf(expected, sizeof(expected), 544 + "@time=" ADVENTURE_TIME ";solanum.chat/response=%s,foo,* :%s ENCAP %s ACK" CRLF, 545 + remote->id, me.id, server->name); 546 + is_client_sendq(expected, server, MSG); 547 + 548 + standard_free(); 549 + } 550 + 551 + static void safelist_response(void) 552 + { 553 + char expected[BUFSIZE]; 554 + int refills = 0; 555 + 556 + standard_init(); 557 + init_large_list(); 558 + 559 + client_util_parse(user, "@label=foo LIST #safelist*,>0"); 560 + snprintf(expected, sizeof(expected), "@label=foo :%s BATCH +%s labeled-response" CRLF, me.name, batch1); 561 + is_client_sendq_one(expected, user, MSG); 562 + snprintf(expected, sizeof(expected), "@batch=%s :%s 321 %s Channel :Users Name" CRLF, batch1, me.name, user->name); 563 + is_client_sendq_one(expected, user, MSG); 564 + 565 + for (int i = 0; i < 100; i++) 566 + { 567 + snprintf(expected, sizeof(expected), "@batch=%s :%s 322 %s #safelist%02d 1 :" LONG_VALUE_200 CRLF, batch1, me.name, user->name, i); 568 + is_client_sendq_one(expected, user, MSG); 569 + 570 + if (rb_linebuf_len(&user->localClient->buf_sendq) == 0 && user->localClient->safelist_data != NULL) 571 + { 572 + rb_run_one_event_for_tests("safelist_iterate_clients"); 573 + if (rb_linebuf_len(&user->localClient->buf_sendq) == 0) 574 + { 575 + ok(0, "safelist_iterate_clients failed; " MSG); 576 + return; 577 + } 578 + 579 + refills++; 580 + } 581 + } 582 + 583 + snprintf(expected, sizeof(expected), "@batch=%s :%s 323 %s :End of /LIST" CRLF, batch1, me.name, user->name); 584 + is_client_sendq_one(expected, user, MSG); 585 + snprintf(expected, sizeof(expected), ":%s BATCH -%s" CRLF, me.name, batch1); 586 + is_client_sendq(expected, user, MSG); 587 + ok(user->localClient->safelist_data == NULL, MSG); 588 + ok(refills > 0, MSG); 589 + 590 + standard_free(); 591 + } 592 + 593 + static void safelist_response__exit(void) 594 + { 595 + char expected[BUFSIZE]; 596 + 597 + standard_init(); 598 + init_large_list(); 599 + 600 + client_util_parse(user, "@label=foo LIST #safelist*,>0"); 601 + snprintf(expected, sizeof(expected), "@label=foo :%s BATCH +%s labeled-response" CRLF, me.name, batch1); 602 + is_client_sendq_one(expected, user, MSG); 603 + 604 + /* empty the sendq to make room for more messages. 605 + * we're already testing the contents in a different test, so for this one just clear it */ 606 + drain_client_sendq(user); 607 + 608 + /* verify the user still has LIST in progress */ 609 + ok(user->localClient->safelist_data != NULL, MSG); 610 + 611 + /* this should trip ASAN (and probably cause a crash even on non-ASAN builds) 612 + * if the client wasn't fully cleaned up */ 613 + remove_local_person(user); 614 + rb_run_one_event_for_tests("free_exited_clients"); 615 + rb_run_one_event_for_tests("safelist_iterate_clients"); 616 + 617 + user = NULL; 618 + standard_free(); 619 + } 620 + 621 + static void safelist_response__part(void) 622 + { 623 + char expected[BUFSIZE]; 624 + 625 + standard_init(); 626 + init_large_list(); 627 + 628 + client_util_parse(user, "@label=foo LIST #safelist*,>0"); 629 + snprintf(expected, sizeof(expected), "@label=foo :%s BATCH +%s labeled-response" CRLF, me.name, batch1); 630 + is_client_sendq_one(expected, user, MSG); 631 + 632 + /* empty the sendq to make room for more messages. 633 + * we're already testing the contents in a different test, so for this one just clear it */ 634 + drain_client_sendq(user); 635 + 636 + /* verify the user still has LIST in progress */ 637 + ok(user->localClient->safelist_data != NULL, MSG); 638 + 639 + /* this will destroy every #safelist* channel since user is the only member, 640 + * meaning there should be no more channels left to iterate after next event execution */ 641 + remove_user_from_channels(user); 642 + rb_run_one_event_for_tests("safelist_iterate_clients"); 643 + snprintf(expected, sizeof(expected), "@batch=%s :%s 323 %s :End of /LIST" CRLF, batch1, me.name, user->name); 644 + is_client_sendq_one(expected, user, MSG); 645 + snprintf(expected, sizeof(expected), ":%s BATCH -%s" CRLF, me.name, batch1); 646 + is_client_sendq(expected, user, MSG); 647 + 648 + is_hex(0, (uintptr_t)user->localClient->safelist_data, MSG); 649 + 650 + standard_free(); 651 + } 652 + 653 + static void safelist_response__abort(void) 654 + { 655 + char expected[BUFSIZE]; 656 + 657 + standard_init(); 658 + init_large_list(); 659 + 660 + client_util_parse(user, "@label=foo LIST #safelist*,>0"); 661 + snprintf(expected, sizeof(expected), "@label=foo :%s BATCH +%s labeled-response" CRLF, me.name, batch1); 662 + is_client_sendq_one(expected, user, MSG); 663 + 664 + /* empty the sendq to make room for more messages. 665 + * we're already testing the contents in a different test, so for this one just clear it */ 666 + drain_client_sendq(user); 667 + 668 + /* verify the user still has LIST in progress */ 669 + ok(user->localClient->safelist_data != NULL, MSG); 670 + 671 + /* sending another list aborts the first and returns no response for the second */ 672 + client_util_parse(user, "@label=bar LIST >0"); 673 + snprintf(expected, sizeof(expected), "@batch=%s :%s NOTICE %s :/LIST aborted" CRLF, batch1, me.name, user->name); 674 + is_client_sendq_one(expected, user, MSG); 675 + snprintf(expected, sizeof(expected), "@batch=%s :%s 323 %s :End of /LIST" CRLF, batch1, me.name, user->name); 676 + is_client_sendq_one(expected, user, MSG); 677 + snprintf(expected, sizeof(expected), ":%s BATCH -%s" CRLF, me.name, batch1); 678 + is_client_sendq_one(expected, user, MSG); 679 + snprintf(expected, sizeof(expected), "@label=bar :%s ACK" CRLF, me.name); 680 + is_client_sendq(expected, user, MSG); 681 + 682 + is_hex(0, (uintptr_t)user->localClient->safelist_data, MSG); 683 + 684 + standard_free(); 685 + } 686 + 687 + int main(int argc, char *argv[]) 688 + { 689 + /* we call TIME which uses localtime(), so ensure we're in UTC */ 690 + rb_setenv("TZ", "UTC", 1); 691 + tzset(); 692 + 693 + plan_lazy(); 694 + 695 + ircd_util_init(__FILE__); 696 + client_util_init(); 697 + 698 + srand(0); 699 + generate_batch_id(batch1, sizeof(batch1)); 700 + generate_batch_id(batch2, sizeof(batch2)); 701 + generate_batch_id(batch3, sizeof(batch3)); 702 + generate_batch_id(batch4, sizeof(batch4)); 703 + generate_batch_id(batch5, sizeof(batch5)); 704 + generate_batch_id(batch6, sizeof(batch6)); 705 + generate_batch_id(batch7, sizeof(batch7)); 706 + generate_batch_id(batch8, sizeof(batch8)); 707 + 708 + single_response(); 709 + single_response__multi_client(); 710 + multi_response(); 711 + remote_response__hunted_server(); 712 + remote_response__hunted_client(); 713 + remote_response__mask(); 714 + remote_response__timeout(); 715 + 716 + response_tag(); 717 + 718 + safelist_response(); 719 + safelist_response__exit(); 720 + safelist_response__part(); 721 + safelist_response__abort(); 722 + 723 + client_util_free(); 724 + ircd_util_free(); 725 + return 0; 726 + }
+46
tests/labeled_response1.conf
··· 1 + serverinfo { 2 + sid = "0AA"; 3 + name = "me.test"; 4 + description = "Test server"; 5 + network_name = "Test network"; 6 + }; 7 + 8 + connect "remote.test" { 9 + host = "::1"; 10 + fingerprint = "test"; 11 + class = "default"; 12 + }; 13 + 14 + connect "remote2.test" { 15 + host = "::1"; 16 + fingerprint = "test"; 17 + class = "default"; 18 + }; 19 + 20 + connect "remote3.test" { 21 + host = "::1"; 22 + fingerprint = "test"; 23 + class = "default"; 24 + }; 25 + 26 + privset "admin" { 27 + privs = oper:general, oper:admin; 28 + }; 29 + 30 + operator "admin" { 31 + user = "*@*"; 32 + password = "test"; 33 + flags = ~encrypted; 34 + privset = "admin"; 35 + }; 36 + 37 + class "lowq" { 38 + sendq = 8 kbytes; 39 + }; 40 + 41 + /* not used by default, fake clients normally have no attached iline 42 + * needed to test async production via SAFELIST */ 43 + auth { 44 + user = "*@*"; 45 + class = "lowq"; 46 + };
+1
tests/meson.build
··· 34 34 'msgbuf_parse1': 'msgbuf_parse1.c', 35 35 'msgbuf_unparse1': 'msgbuf_unparse1.c', 36 36 'hostmask1': 'hostmask1.c', 37 + 'labeled_response1': 'labeled_response1.c', 37 38 'privilege1': 'privilege1.c', 38 39 'rb_dictionary1': 'rb_dictionary1.c', 39 40 'rb_snprintf_append1': 'rb_snprintf_append1.c',