Skip to content

Commit b521bc0

Browse files
authored
feat: template control front page (#49)
* dev: temporary require false set * dev: add UNKNOWN status * dev: template Information model add * dev: random TestResult stubbing * dev: front-page for template runner * dev: add template with json body indentation * chore: edit table css * dev: add enum AgentStatus in TemplateResult model * dev: add enum AgentStatus in TemplateResult model * chore: clarify specific import * test: dto serializer test * dev: remove test randomized TestResult response * fix: failed UserContext test * dev: testing added and gradle setting * dev: test code for sse emitter * dev: undo unused template test
1 parent 114ae25 commit b521bc0

File tree

22 files changed

+620
-95
lines changed

22 files changed

+620
-95
lines changed

bm-agent/build.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ version = '0.0.1-SNAPSHOT'
1111

1212
def excludeJacocoTestCoverageReport = [
1313
// bmagent
14+
'org/benchmarker/bmagent/BmAgentApplication**',
1415
// 'org/benchmarker/bmagent/**',
1516
]
1617

@@ -41,6 +42,8 @@ dependencies {
4142
testImplementation 'org.springframework.boot:spring-boot-starter-test'
4243
testImplementation 'org.springframework.boot:spring-boot-testcontainers'
4344
testImplementation 'org.testcontainers:junit-jupiter'
45+
testImplementation 'com.squareup.okhttp3:mockwebserver:4.9.1'
46+
4447
implementation project(':bm-common')
4548
}
4649

@@ -56,6 +59,7 @@ jacocoTestReport {
5659
reports {
5760
xml.required = true
5861
}
62+
5963
// Set the path to the source files
6064
classDirectories.setFrom(files(classDirectories.files.collect {
6165
fileTree(dir: it, exclude: excludeJacocoTestCoverageReport)

bm-agent/src/main/java/org/benchmarker/bmagent/controller/AgentApiController.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ public class AgentApiController {
3636
* @return SseEmitter
3737
*/
3838
@PostMapping("/templates/{template_id}")
39-
public SseEmitter startSSE(@PathVariable("template_id") Long templateId,
40-
@RequestParam("action") String action, @RequestBody TemplateInfo templateInfo) {
39+
public SseEmitter manageSSE(@PathVariable("template_id") Long templateId,
40+
@RequestParam("action") String action, @RequestBody(required = false) TemplateInfo templateInfo) {
4141

4242
if (action.equals("start")) {
4343
return sseManageService.start(templateId, templateInfo);

bm-agent/src/main/java/org/benchmarker/bmagent/sse/SseManageService.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ public SseEmitter start(Long id, TemplateInfo templateInfo) {
5858

5959
// 1초마다 TestResult 를 보내는 스케줄러 시작
6060
scheduledTaskService.start(id, () -> {
61+
// resultManagerService.save(id, RandomUtils.generateRandomTestResult());
6162
send(id, resultManagerService.find(id));
6263
}, 0, 1, TimeUnit.SECONDS);
6364

bm-agent/src/test/java/org/benchmarker/bmagent/TestBmAgentApplication.java

Lines changed: 0 additions & 13 deletions
This file was deleted.
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package org.benchmarker.bmagent.controller;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
import static org.junit.jupiter.api.Assertions.assertEquals;
5+
import static org.mockito.ArgumentMatchers.any;
6+
import static org.mockito.ArgumentMatchers.eq;
7+
import static org.mockito.Mockito.doAnswer;
8+
import static org.mockito.Mockito.times;
9+
import static org.mockito.Mockito.verify;
10+
11+
import java.io.IOException;
12+
import java.util.HashMap;
13+
import java.util.Map;
14+
import org.benchmarker.bmagent.schedule.SchedulerStatus;
15+
import org.benchmarker.bmagent.service.ISseManageService;
16+
import org.benchmarker.util.MockServer;
17+
import org.junit.jupiter.api.DisplayName;
18+
import org.junit.jupiter.api.Test;
19+
import org.junit.jupiter.api.extension.ExtendWith;
20+
import org.mockito.ArgumentCaptor;
21+
import org.mockito.Captor;
22+
import org.mockito.InjectMocks;
23+
import org.mockito.Mock;
24+
import org.mockito.Mockito;
25+
import org.mockito.junit.jupiter.MockitoExtension;
26+
import org.springframework.core.ParameterizedTypeReference;
27+
import org.springframework.test.web.servlet.MockMvc;
28+
import org.springframework.web.reactive.function.client.WebClient;
29+
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
30+
31+
@ExtendWith(MockitoExtension.class)
32+
public class AgentApiControllerTest extends MockServer {
33+
34+
@Mock
35+
private ISseManageService sseManageService;
36+
37+
@InjectMocks
38+
private AgentApiController agentApiController;
39+
40+
@Captor
41+
private ArgumentCaptor<String> messageCaptor;
42+
43+
private MockMvc mockMvc;
44+
45+
@Test
46+
@DisplayName("SSE start 통신 테스트")
47+
public void testStartSSE() throws IOException {
48+
// given
49+
SseEmitter mockSseEmitter = Mockito.mock(SseEmitter.class);
50+
51+
// when
52+
// sseManageService.start() 메서드의 행동을 설정하는 스텁 설정
53+
doAnswer(invocation -> {
54+
// 클라이언트로 데이터를 보내는 stub
55+
mockSseEmitter.send("Data 1");
56+
mockSseEmitter.send("Data 2");
57+
mockSseEmitter.complete();
58+
return null;
59+
}).when(sseManageService).start(eq(1L), any());
60+
61+
// 호출
62+
agentApiController.manageSSE(1L, "start", null);
63+
64+
// then
65+
// SseEmitter 로 전송된 메시지 모두 캡처
66+
Mockito.verify(mockSseEmitter, times(2)).send(messageCaptor.capture());
67+
assertEquals("Data 1", messageCaptor.getAllValues().get(0));
68+
assertEquals("Data 2", messageCaptor.getAllValues().get(1));
69+
}
70+
71+
@Test
72+
@DisplayName("SSE stop 통신 테스트")
73+
public void testStopSSE() throws IOException {
74+
// given
75+
Long templateId = 1L;
76+
77+
// when
78+
agentApiController.manageSSE(templateId, "stop", null);
79+
80+
// then
81+
// sseManageService.stop() 메서드가 호출되었는지 검증
82+
verify(sseManageService, times(1)).stop(eq(templateId));
83+
}
84+
85+
@Test
86+
@DisplayName("스케줄러 상태 체크 테스트")
87+
public void testCheckSchedulerStatus() throws Exception {
88+
// given
89+
WebClient webClient = WebClient.create(mockServer.url("/api/status").toString());
90+
Map<Long, SchedulerStatus> schedulerStatusMap = new HashMap<>();
91+
schedulerStatusMap.put(1L, SchedulerStatus.RUNNING);
92+
schedulerStatusMap.put(2L, SchedulerStatus.SHUTDOWN);
93+
addMockResponse(schedulerStatusMap);
94+
95+
// when
96+
Map<Long, SchedulerStatus> response = webClient.get()
97+
.uri("/api/status")
98+
.retrieve()
99+
.bodyToMono(new ParameterizedTypeReference<Map<Long, SchedulerStatus>>() {
100+
})
101+
.log()
102+
.block();
103+
104+
// then
105+
assertThat(response.get(1L)).isEqualTo(SchedulerStatus.RUNNING);
106+
assertThat(response.get(2L)).isEqualTo(SchedulerStatus.SHUTDOWN);
107+
}
108+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package org.benchmarker.bmagent.controller;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.mockito.Mockito.mock;
5+
6+
import java.io.IOException;
7+
import org.junit.jupiter.api.Test;
8+
import org.junit.jupiter.api.extension.ExtendWith;
9+
import org.mockito.InjectMocks;
10+
import org.mockito.junit.jupiter.MockitoExtension;
11+
import org.springframework.http.HttpStatus;
12+
import org.springframework.http.ResponseEntity;
13+
import org.springframework.web.context.request.WebRequest;
14+
15+
@ExtendWith(MockitoExtension.class)
16+
public class GlobalExceptionHandlerTest {
17+
18+
@InjectMocks
19+
private GlobalExceptionHandler globalExceptionHandler;
20+
21+
@Test
22+
public void testHandleIOException() {
23+
// Arrange
24+
IOException mockException = mock(IOException.class);
25+
WebRequest mockRequest = mock(WebRequest.class);
26+
27+
// Act
28+
ResponseEntity<Object> responseEntity = globalExceptionHandler.handleIOException(mockException, mockRequest);
29+
30+
// Assert
31+
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseEntity.getStatusCode());
32+
}
33+
}

bm-controller/src/test/java/org/benchmarker/bmagent/pref/ResultManagerServiceTest.java renamed to bm-agent/src/test/java/org/benchmarker/bmagent/pref/ResultManagerServiceTest.java

File renamed without changes.

bm-controller/src/test/java/org/benchmarker/bmagent/schedule/ScheduledTaskServiceTest.java renamed to bm-agent/src/test/java/org/benchmarker/bmagent/schedule/ScheduledTaskServiceTest.java

File renamed without changes.

bm-controller/src/test/java/org/benchmarker/bmagent/sse/SseManageServiceTest.java renamed to bm-agent/src/test/java/org/benchmarker/bmagent/sse/SseManageServiceTest.java

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,21 @@
11
package org.benchmarker.bmagent.sse;
22

3-
import org.benchmarker.bmcommon.dto.TemplateInfo;
4-
import org.benchmarker.bmcommon.dto.TestResult;
5-
import org.benchmarker.bmcommon.util.RandomUtils;
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
65
import org.benchmarker.bmagent.pref.ResultManagerService;
76
import org.benchmarker.bmagent.schedule.ScheduledTaskService;
87
import org.benchmarker.bmagent.schedule.SchedulerStatus;
98
import org.benchmarker.bmagent.service.IScheduledTaskService;
9+
import org.benchmarker.bmcommon.dto.TemplateInfo;
10+
import org.benchmarker.bmcommon.dto.TestResult;
11+
import org.benchmarker.bmcommon.util.RandomUtils;
1012
import org.junit.jupiter.api.BeforeEach;
1113
import org.junit.jupiter.api.DisplayName;
1214
import org.junit.jupiter.api.Test;
1315
import org.junit.jupiter.api.extension.ExtendWith;
1416
import org.mockito.junit.jupiter.MockitoExtension;
1517
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
1618

17-
import static java.lang.Thread.sleep;
18-
import static org.assertj.core.api.Assertions.assertThat;
19-
2019
@ExtendWith(MockitoExtension.class)
2120
class SseManageServiceTest {
2221
private IScheduledTaskService scheduledTaskService;
@@ -49,4 +48,22 @@ void start_ShouldStartSseEmitterAndScheduledTask() throws InterruptedException {
4948
sseManageService.stop(id);
5049
assertThat(scheduledTaskService.getStatus().get(id)).isNull();
5150
}
51+
52+
@Test
53+
@DisplayName("sse 시작 시, id 가 중복된다면(기존 emitter 가 존재하면) null 를 즉시 반환한다")
54+
void startAndShutdown() throws InterruptedException {
55+
// given
56+
Long id = 1L;
57+
TestResult resultStub = RandomUtils.generateRandomTestResult();
58+
resultManagerService.save(id, resultStub);
59+
SseEmitter result = sseManageService.start(id, new TemplateInfo());
60+
61+
// when
62+
SseEmitter res = sseManageService.start(id, new TemplateInfo());
63+
64+
// then
65+
assertThat(res).isNull();
66+
sseManageService.stop(id);
67+
}
68+
5269
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package org.benchmarker.util;
2+
3+
import com.fasterxml.jackson.core.JsonProcessingException;
4+
import com.fasterxml.jackson.databind.ObjectMapper;
5+
import java.io.IOException;
6+
import okhttp3.mockwebserver.MockResponse;
7+
import okhttp3.mockwebserver.MockWebServer;
8+
import org.junit.jupiter.api.AfterEach;
9+
import org.junit.jupiter.api.BeforeEach;
10+
11+
/**
12+
* webClient 요청을 받을 mock 서버를 생성합니다
13+
*/
14+
public class MockServer {
15+
16+
/**
17+
* Mock server instance
18+
*/
19+
public static MockWebServer mockServer;
20+
/**
21+
* Opened mock server url
22+
*/
23+
public String backendUrl;
24+
private ObjectMapper objectMapper = new ObjectMapper();
25+
26+
@BeforeEach
27+
void setUp() throws IOException {
28+
// webClient 요청을 받을 mock 서버를 생성합니다
29+
mockServer = new MockWebServer();
30+
mockServer.start();
31+
backendUrl = String.format(mockServer.url("/").toString());
32+
System.out.println("Mock server url: " + backendUrl);
33+
}
34+
35+
@AfterEach
36+
void tearDown() throws IOException {
37+
mockServer.shutdown();
38+
}
39+
40+
/**
41+
* Mock server에 응답을 추가합니다
42+
*
43+
* <p> 만약 Object 를 Json String 으로 변환할 수 없다면 "" 값이 응답으로 반환됩니다</p>
44+
*
45+
* @param object
46+
*/
47+
public void addMockResponse(Object object) {
48+
String json = "";
49+
try {
50+
json = objectMapper.writeValueAsString(object);
51+
} catch (JsonProcessingException e) {
52+
e.printStackTrace();
53+
}
54+
55+
MockResponse response = new MockResponse()
56+
.setHeader("Content-Type", "application/json")
57+
.setBody(json);
58+
mockServer.enqueue(response);
59+
}
60+
61+
/**
62+
* Mock server에 응답을 추가합니다. 반복 횟수를 지정할 수 있습니다.
63+
*
64+
* <p> 만약 Object 를 Json String 으로 변환할 수 없다면 "" 값이 응답으로 반환됩니다</p>
65+
*
66+
* @param object
67+
* @param repeatCount
68+
*/
69+
public void addMockResponse(Object object, int repeatCount) {
70+
String json = "";
71+
try {
72+
json = objectMapper.writeValueAsString(object);
73+
} catch (JsonProcessingException e) {
74+
e.printStackTrace();
75+
}
76+
77+
MockResponse response = new MockResponse()
78+
.setHeader("Content-Type", "application/json")
79+
.setBody(json);
80+
for (int i = 0; i < repeatCount; i++) {
81+
mockServer.enqueue(response);
82+
}
83+
}
84+
}

0 commit comments

Comments
 (0)