99import static java .util .concurrent .TimeUnit .SECONDS ;
1010import static org .hamcrest .MatcherAssert .assertThat ;
1111import static org .hamcrest .Matchers .greaterThanOrEqualTo ;
12+ import static org .junit .jupiter .api .Assertions .assertEquals ;
1213import static org .springframework .test .web .servlet .request .MockMvcRequestBuilders .get ;
1314import static org .springframework .test .web .servlet .result .MockMvcResultMatchers .request ;
15+ import static org .springframework .test .web .servlet .result .MockMvcResultMatchers .status ;
1416import static org .tailormap .api .TestRequestProcessor .setServletPath ;
1517
1618import java .lang .invoke .MethodHandles ;
2628import org .springframework .beans .factory .annotation .Value ;
2729import org .springframework .boot .webmvc .test .autoconfigure .AutoConfigureMockMvc ;
2830import org .springframework .http .MediaType ;
31+ import org .springframework .security .test .context .support .WithMockUser ;
2932import org .springframework .test .web .servlet .MockMvc ;
3033import org .springframework .test .web .servlet .MvcResult ;
34+ import org .springframework .test .web .servlet .setup .MockMvcBuilders ;
35+ import org .springframework .web .context .WebApplicationContext ;
3136import org .tailormap .api .annotation .PostgresIntegrationTest ;
32- import org .tailormap .api .viewer . model . ServerSentEventResponse ;
37+ import org .tailormap .api .persistence . Group ;
3338
3439@ PostgresIntegrationTest
3540@ AutoConfigureMockMvc
3641@ Execution (ExecutionMode .CONCURRENT )
37- class ServerSentEventsControllerIntegrationTest {
42+ class ServerSentEventsControllerIntegrationTest extends SseParsingUtils {
3843 private static final Logger logger =
3944 LoggerFactory .getLogger (MethodHandles .lookup ().lookupClass ());
4045 // Unique id avoids interference with parallel/other tests.
@@ -46,6 +51,12 @@ class ServerSentEventsControllerIntegrationTest {
4651 @ Value ("${tailormap-api.base-path}" )
4752 private String apiBasePath ;
4853
54+ @ Value ("${tailormap-api.admin.base-path}" )
55+ private String adminBasePath ;
56+
57+ @ Autowired
58+ private WebApplicationContext context ;
59+
4960 private MvcResult sseResult ;
5061
5162 @ BeforeEach
@@ -55,6 +66,7 @@ void start_sse_stream() throws Exception {
5566 .accept (MediaType .TEXT_EVENT_STREAM )
5667 .with (setServletPath (sseUrl ))
5768 .acceptCharset (StandardCharsets .UTF_8 ))
69+ .andExpect (status ().isOk ())
5870 .andExpect (request ().asyncStarted ())
5971 .andReturn ();
6072 }
@@ -71,18 +83,67 @@ void should_send_keep_alive_messages_for_two_minutes() {
7183 .logging (logPrinter -> logger .debug ("Checking for keep-alive messages in SSE stream... {}" , logPrinter ))
7284 .untilAsserted (() -> {
7385 final String stream = sseResult .getResponse ().getContentAsString ();
74- assertThat (count_keep_alive_messages (stream ), greaterThanOrEqualTo (2 ));
86+ assertThat (count_all_keep_alive_messages (stream ), greaterThanOrEqualTo (2 ));
7587 });
7688 }
7789
78- private int count_keep_alive_messages (String stream ) {
79- int count = 0 ;
80- int index = 0 ;
81- final String marker = "\" eventType\" :\" " + ServerSentEventResponse .EventTypeEnum .KEEP_ALIVE + "\" " ;
82- while ((index = stream .indexOf (marker , index )) != -1 ) {
83- count ++;
84- index += marker .length ();
85- }
86- return count ;
90+ /** Check that at least 2 keep-alive messages arrive in 130 seconds. */
91+ @ Test
92+ @ WithMockUser (
93+ username = "admin" ,
94+ authorities = {Group .ADMIN })
95+ void admin_and_viewer_should_use_separate_sse_streams () throws Exception {
96+ // start admin sse stream
97+ MockMvc adminMockMvc = MockMvcBuilders .webAppContextSetup (context ).build ();
98+ final String adminSseUrl = adminBasePath + "/events/" + sseClientId ;
99+ MvcResult adminSseResult = adminMockMvc
100+ .perform (get (adminSseUrl )
101+ .accept (MediaType .TEXT_EVENT_STREAM )
102+ .with (setServletPath (adminSseUrl ))
103+ .acceptCharset (StandardCharsets .UTF_8 ))
104+ .andExpect (status ().isOk ())
105+ .andExpect (request ().asyncStarted ())
106+ .andReturn ();
107+
108+ Awaitility .await ("Waiting at least 2 minutes for any keep-alive messages" )
109+ .pollDelay (45 , SECONDS )
110+ .pollInterval (15 , SECONDS )
111+ .atLeast (1 , MINUTES )
112+ .atMost (130 , SECONDS )
113+ .logging (
114+ logPrinter -> logger .debug ("Checking for keep-alive messages in SSE streams... {}" , logPrinter ))
115+ .untilAsserted (() -> {
116+ // check admin stream
117+ final String adminStream = adminSseResult .getResponse ().getContentAsString ();
118+ logger .debug ("admin stream: {}" , adminStream );
119+ assertThat (
120+ "There should be at least 2 keep-alive messages for the admin" ,
121+ count_all_keep_alive_messages (adminStream ),
122+ greaterThanOrEqualTo (2 ));
123+ assertEquals (
124+ 0 ,
125+ count_viewer_keep_alive_messages (adminStream ),
126+ "There should be no keep-alive messages for the viewer in the admin" );
127+ assertEquals (
128+ count_all_keep_alive_messages (adminStream ),
129+ count_admin_keep_alive_messages (adminStream ),
130+ "We should only get admin keep-alive messages in the admin SSE stream" );
131+
132+ // and viewer stream
133+ final String stream = sseResult .getResponse ().getContentAsString ();
134+ logger .debug ("viewer stream: {}" , stream );
135+ assertThat (
136+ "There should be at least 2 keep-alive messages for the viewer" ,
137+ count_all_keep_alive_messages (stream ),
138+ greaterThanOrEqualTo (2 ));
139+ assertEquals (
140+ count_all_keep_alive_messages (stream ),
141+ count_viewer_keep_alive_messages (stream ),
142+ "Admin keep-alive messages should not be sent to viewer SSE stream" );
143+ assertEquals (
144+ 0 ,
145+ count_admin_keep_alive_messages (stream ),
146+ "There should be no keep-alive messages for the admin in the viewer SSE stream" );
147+ });
87148 }
88149}
0 commit comments