1 #include "boost_formatters.hpp"
2 #include "http/server_sent_event.hpp"
3 
4 #include <boost/asio/buffer.hpp>
5 #include <boost/asio/io_context.hpp>
6 #include <boost/asio/read.hpp>
7 #include <boost/beast/_experimental/test/stream.hpp>
8 
9 #include <chrono>
10 #include <memory>
11 #include <string>
12 #include <string_view>
13 #include <utility>
14 
15 #include "gtest/gtest.h"
16 namespace crow
17 {
18 namespace sse_socket
19 {
20 
21 namespace
22 {
23 
24 TEST(ServerSentEvent, SseWorks)
25 {
26     boost::asio::io_context io;
27     boost::beast::test::stream stream(io);
28     boost::beast::test::stream out(io);
29     stream.connect(out);
30 
31     Request req;
32 
33     bool openCalled = false;
34     auto openHandler =
35         [&openCalled](Connection&, const Request& /*handedReq*/) {
36             openCalled = true;
37         };
38     bool closeCalled = false;
39     auto closeHandler = [&closeCalled](Connection&) { closeCalled = true; };
40 
41     std::shared_ptr<ConnectionImpl<boost::beast::test::stream>> conn =
42         std::make_shared<ConnectionImpl<boost::beast::test::stream>>(
43             std::move(stream), openHandler, closeHandler);
44     conn->start(req);
45     // Connect
46     {
47         constexpr std::string_view expected =
48             "HTTP/1.1 200 OK\r\n"
49             "Content-Type: text/event-stream\r\n"
50             "\r\n";
51 
52         while (out.str().size() != expected.size())
53         {
54             io.run_for(std::chrono::milliseconds(1));
55         }
56 
57         std::string eventContent;
58         eventContent.resize(expected.size());
59         boost::asio::read(out, boost::asio::buffer(eventContent));
60 
61         EXPECT_EQ(eventContent, expected);
62         EXPECT_TRUE(openCalled);
63         EXPECT_FALSE(closeCalled);
64         EXPECT_TRUE(out.str().empty());
65     }
66     // Send one event
67     {
68         conn->sendEvent("TestEventId", "TestEventContent");
69         std::string_view expected = "id: TestEventId\n"
70                                     "data: TestEventContent\n"
71                                     "\n";
72 
73         while (out.str().size() < expected.size())
74         {
75             io.run_for(std::chrono::milliseconds(1));
76         }
77         EXPECT_EQ(out.str(), expected);
78 
79         std::string eventContent;
80         eventContent.resize(expected.size());
81         boost::asio::read(out, boost::asio::buffer(eventContent));
82 
83         EXPECT_EQ(eventContent, expected);
84         EXPECT_TRUE(out.str().empty());
85     }
86     // Send second event
87     {
88         conn->sendEvent("TestEventId2", "TestEvent\nContent2");
89         constexpr std::string_view expected =
90             "id: TestEventId2\n"
91             "data: TestEvent\n"
92             "data: Content2\n"
93             "\n";
94 
95         while (out.str().size() < expected.size())
96         {
97             io.run_for(std::chrono::milliseconds(1));
98         }
99 
100         std::string eventContent;
101         eventContent.resize(expected.size());
102         boost::asio::read(out, boost::asio::buffer(eventContent));
103         EXPECT_EQ(eventContent, expected);
104         EXPECT_TRUE(out.str().empty());
105     }
106     // close the remote
107     {
108         out.close();
109         while (!closeCalled)
110         {
111             io.run_for(std::chrono::milliseconds(1));
112         }
113     }
114 }
115 } // namespace
116 
117 } // namespace sse_socket
118 } // namespace crow
119