Mercurial
view seobeo/tests/seobeo_sse_server_test.c @ 259:667156fcd3e3
Add dev-only cyberpunk JRPG page
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Wed, 05 Aug 2026 09:19:41 -0700 |
| parents | 609d3c6aff4e |
| children | 1f9877b637e9 |
line wrap: on
line source
#include "seobeo/seobeo.h" #include <assert.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <sys/wait.h> #include <unistd.h> static int reserve_port(void) { int socket_fd = socket(AF_INET, SOCK_STREAM, 0); assert(socket_fd >= 0); struct sockaddr_in address = { .sin_family = AF_INET, .sin_addr.s_addr = htonl(INADDR_LOOPBACK), .sin_port = 0, }; assert(bind( socket_fd, (struct sockaddr *)&address, sizeof(address)) == 0); socklen_t length = sizeof(address); assert(getsockname( socket_fd, (struct sockaddr *)&address, &length) == 0); int port = ntohs(address.sin_port); close(socket_fd); return port; } static int connect_to(int port) { int socket_fd = socket(AF_INET, SOCK_STREAM, 0); if (socket_fd < 0) return -1; struct timeval timeout = { .tv_sec = 3, }; setsockopt( socket_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); struct sockaddr_in address = { .sin_family = AF_INET, .sin_addr.s_addr = htonl(INADDR_LOOPBACK), .sin_port = htons((uint16)port), }; if (connect( socket_fd, (struct sockaddr *)&address, sizeof(address)) != 0) { close(socket_fd); return -1; } return socket_fd; } static void send_request(int socket_fd, const char *path) { char request[512]; int length = snprintf( request, sizeof(request), "GET %s HTTP/1.1\r\n" "Host: 127.0.0.1\r\n" "Connection: keep-alive\r\n" "\r\n", path); assert(length > 0 && (size_t)length < sizeof(request)); assert(send(socket_fd, request, (size_t)length, 0) == length); } static size_t receive_until( int socket_fd, char *buffer, size_t capacity, const char *needle) { size_t used = 0; while (used + 1 < capacity) { ssize_t amount = recv( socket_fd, buffer + used, capacity - used - 1, 0); assert(amount > 0); used += (size_t)amount; buffer[used] = '\0'; if (strstr(buffer, needle)) return used; } assert(FALSE && "response exceeded test buffer"); return 0; } static pid_t start_server( const char *binary, const char *port) { pid_t pid = fork(); assert(pid >= 0); if (pid == 0) { execl(binary, binary, port, NULL); _exit(127); } int numeric_port = atoi(port); for (int attempt = 0; attempt < 100; attempt++) { int socket_fd = connect_to(numeric_port); if (socket_fd >= 0) { close(socket_fd); return pid; } usleep(20000); } kill(pid, SIGTERM); waitpid(pid, NULL, 0); assert(FALSE && "SSE test server did not start"); return -1; } int main(int argc, char **argv) { assert(argc == 2); int port = reserve_port(); char port_text[16]; snprintf(port_text, sizeof(port_text), "%d", port); pid_t server = start_server(argv[1], port_text); int event_socket = connect_to(port); assert(event_socket >= 0); send_request(event_socket, "/events"); char events[4096] = {0}; receive_until( event_socket, events, sizeof(events), "data: later\n\n"); assert(strstr(events, "HTTP/1.1 200 OK\r\n")); assert(strstr(events, "Content-Type: text/event-stream\r\n")); assert(strstr(events, "data: connected\n\n")); int health_socket = connect_to(port); assert(health_socket >= 0); send_request(health_socket, "/health"); assert(shutdown(health_socket, SHUT_WR) == 0); char health[1024] = {0}; receive_until(health_socket, health, sizeof(health), "\r\n\r\nok"); assert(strstr(health, "HTTP/1.1 200 OK\r\n")); close(health_socket); close(event_socket); kill(server, SIGTERM); waitpid(server, NULL, 0); printf("Seobeo SSE server integration test passed\n"); return 0; }