From 62a2a14bedc8c5b7aa49b61ba7c759c5d88711df Mon Sep 17 00:00:00 2001 From: jack ning Date: Tue, 24 Dec 2024 14:01:02 +0800 Subject: [PATCH] Sync from bytedesk-private: update --- jmeter/readme.md | 15 + jmeter/requestThread接口测试.jmx | 308 ++++++++++++++++++ .../com/bytedesk/core/enums/ClientEnum.java | 4 +- .../visitor/VisitorAnonymousController.java | 2 +- .../service/visitor/VisitorRequest.java | 8 +- ...torAnonymousControllerIntegrationTest.java | 173 ++++++---- 6 files changed, 436 insertions(+), 74 deletions(-) create mode 100644 jmeter/readme.md create mode 100644 jmeter/requestThread接口测试.jmx diff --git a/jmeter/readme.md b/jmeter/readme.md new file mode 100644 index 0000000000..36f0454ae8 --- /dev/null +++ b/jmeter/readme.md @@ -0,0 +1,15 @@ + +# jmeter diff --git a/jmeter/requestThread接口测试.jmx b/jmeter/requestThread接口测试.jmx new file mode 100644 index 0000000000..f4278a8513 --- /dev/null +++ b/jmeter/requestThread接口测试.jmx @@ -0,0 +1,308 @@ + + + + + + + + false + false + + + + 10 + 10 + true + continue + + 10 + false + + + + + 127.0.0.1 + 9003 + http + /visitor/api/v1/thread + true + GET + true + false + + + + false + 0 + = + true + type + + + false + default_agent_uid + = + true + sid + + + false + 1395890719817857 + = + true + uid + + + false + LOCAL9686 + = + true + nickname + + + false + https://chainsnow.oss-cn-shenzhen.aliyuncs.com/avatars/visitor_default_avatar.png + = + true + avatar + + + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + true + + + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + true + + + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + true + + + + + + + + false + false + false + false + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + true + + + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + true + + + + + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + true + + + + + + + + + diff --git a/modules/core/src/main/java/com/bytedesk/core/enums/ClientEnum.java b/modules/core/src/main/java/com/bytedesk/core/enums/ClientEnum.java index 02aa3b689e..440a201b43 100644 --- a/modules/core/src/main/java/com/bytedesk/core/enums/ClientEnum.java +++ b/modules/core/src/main/java/com/bytedesk/core/enums/ClientEnum.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-05-25 13:07:20 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-12-19 15:52:30 + * @LastEditTime: 2024-12-24 12:49:28 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -109,6 +109,8 @@ public enum ClientEnum { SHOPIFY, // Shopify LAZADA, // 来赞达 SHOPEE, // 虾皮 + // + TEST, ; // 根据字符串查找对应的枚举常量 diff --git a/modules/service/src/main/java/com/bytedesk/service/visitor/VisitorAnonymousController.java b/modules/service/src/main/java/com/bytedesk/service/visitor/VisitorAnonymousController.java index 73e1296039..534e793c8e 100644 --- a/modules/service/src/main/java/com/bytedesk/service/visitor/VisitorAnonymousController.java +++ b/modules/service/src/main/java/com/bytedesk/service/visitor/VisitorAnonymousController.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:24 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-12-24 10:55:34 + * @LastEditTime: 2024-12-24 13:04:17 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. diff --git a/modules/service/src/main/java/com/bytedesk/service/visitor/VisitorRequest.java b/modules/service/src/main/java/com/bytedesk/service/visitor/VisitorRequest.java index 622ef1ba5e..ba786a6dca 100644 --- a/modules/service/src/main/java/com/bytedesk/service/visitor/VisitorRequest.java +++ b/modules/service/src/main/java/com/bytedesk/service/visitor/VisitorRequest.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-04-04 17:05:48 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-12-24 09:46:10 + * @LastEditTime: 2024-12-24 13:01:36 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -47,9 +47,11 @@ public class VisitorRequest extends BaseRequest { private String lang = LanguageEnum.ZH_CN.name(); // location info - private String ip; + @Builder.Default + private String ip = "127.0.0.1"; - private String ipLocation; + @Builder.Default + private String ipLocation = "localhost"; // device info private String browser; diff --git a/starter/src/test/java/com/bytedesk/starter/VisitorAnonymousControllerIntegrationTest.java b/starter/src/test/java/com/bytedesk/starter/VisitorAnonymousControllerIntegrationTest.java index ad10c6d71a..cd41970743 100644 --- a/starter/src/test/java/com/bytedesk/starter/VisitorAnonymousControllerIntegrationTest.java +++ b/starter/src/test/java/com/bytedesk/starter/VisitorAnonymousControllerIntegrationTest.java @@ -4,11 +4,13 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; - +import com.bytedesk.core.enums.ClientEnum; import com.bytedesk.service.visitor.VisitorRequest; import com.bytedesk.service.visitor.VisitorResponse; import static org.assertj.core.api.Assertions.assertThat; @@ -17,11 +19,11 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import java.util.Collections; -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, -properties = { - "bytedesk.socket.enabled=false", // 禁用MQTT服务器 - "server.port=0" // 使用随机端口 +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = { + "bytedesk.socket.enabled=false", // 禁用MQTT服务器 + "server.port=0" // 使用随机端口 }) public class VisitorAnonymousControllerIntegrationTest { @@ -36,38 +38,60 @@ public class VisitorAnonymousControllerIntegrationTest { public void testVisitorFlowWithRealServer() { // Step 1: Create visitor VisitorRequest initRequest = VisitorRequest.builder() - .sid(AGENT_UID) - .build(); + .sid(AGENT_UID) + .nickname("Test Visitor") + .build(); + initRequest.setUid("visitor_test_uid"); initRequest.setOrgUid(ORG_UID); initRequest.setType(TYPE); + initRequest.setClient(ClientEnum.TEST.name()); - ResponseEntity visitorResponse = restTemplate.postForEntity( - "/visitor/api/v1/init", - initRequest, - VisitorResponse.class - ); + // 创建HTTP Headers并添加必要的信息 + HttpHeaders headers = new HttpHeaders(); + headers.set("User-Agent", "Mozilla/5.0 (Test Browser)"); + headers.set("X-Real-IP", "127.0.0.1"); + headers.set("X-Forwarded-For", "127.0.0.1"); + headers.set("Referer", "https://test.bytedesk.com"); + headers.setContentType(MediaType.APPLICATION_JSON); // 添加Content-Type + headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); // 添加Accept + + // 使用HttpEntity包装请求 + HttpEntity requestEntity = new HttpEntity<>(initRequest, headers); + + // 然后再进行实际的测试请求 + ResponseEntity visitorResponse = restTemplate.exchange( + "/visitor/api/v1/init", + HttpMethod.GET, + requestEntity, + VisitorResponse.class); + + // 打印原始响应内容 + System.out.println("Response Status: " + visitorResponse.getStatusCode()); + System.out.println("Response Headers: " + visitorResponse.getHeaders()); + System.out.println("Response Body: " + visitorResponse.getBody()); // Verify visitor creation response assertThat(visitorResponse.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(visitorResponse.getBody()).isNotNull(); + assertThat(visitorResponse.getBody()).as("Response body should not be null").isNotNull(); assertThat(visitorResponse.getBody().getUid()).isNotNull(); assertThat(visitorResponse.getBody().getNickname()).isNotNull(); assertThat(visitorResponse.getBody().getAvatar()).isNotNull(); // Step 2: Request thread with visitor info VisitorRequest threadRequest = VisitorRequest.builder() - .sid(AGENT_UID) - .nickname(visitorResponse.getBody().getNickname()) - .avatar(visitorResponse.getBody().getAvatar()) - .build(); + .sid(AGENT_UID) + .nickname(visitorResponse.getBody().getNickname()) + .avatar(visitorResponse.getBody().getAvatar()) + .build(); threadRequest.setUid(visitorResponse.getBody().getUid()); threadRequest.setOrgUid(ORG_UID); threadRequest.setType(TYPE); + threadRequest.setClient(ClientEnum.TEST.name()); + ResponseEntity threadResponse = restTemplate.postForEntity( - "/visitor/api/v1/request", - threadRequest, - String.class - ); + "/visitor/api/v1/request", + threadRequest, + String.class); // Verify thread request response assertThat(threadResponse.getStatusCode()).isEqualTo(HttpStatus.OK); @@ -78,24 +102,37 @@ public class VisitorAnonymousControllerIntegrationTest { public void testConcurrentVisitorRequests() throws InterruptedException { int concurrentRequests = 10; Thread[] threads = new Thread[concurrentRequests]; - + for (int i = 0; i < concurrentRequests; i++) { + final int index = i; threads[i] = new Thread(() -> { VisitorRequest initRequest = VisitorRequest.builder() - .sid(AGENT_UID) - .build(); + .sid(AGENT_UID) + .build(); + initRequest.setUid("visitor_test_uid_" + index); initRequest.setOrgUid(ORG_UID); initRequest.setType(TYPE); + initRequest.setClient(ClientEnum.TEST.name()); - ResponseEntity response = restTemplate.postForEntity( - "/visitor/api/v1/init", - initRequest, - VisitorResponse.class - ); + // 创建HTTP Headers并添加必要的信息 + HttpHeaders headers = new HttpHeaders(); + headers.set("User-Agent", "Mozilla/5.0 (Test Browser)"); + headers.set("X-Real-IP", "127.0.0.1"); + headers.set("X-Forwarded-For", "127.0.0.1"); + headers.set("Referer", "https://test.bytedesk.com"); - assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(response.getBody()).isNotNull(); - assertThat(response.getBody().getUid()).isNotNull(); + // 使用HttpEntity包装请求 + HttpEntity requestEntity = new HttpEntity<>(initRequest, headers); + + ResponseEntity visitorResponse = restTemplate.exchange( + "/visitor/api/v1/init", + HttpMethod.GET, + requestEntity, + VisitorResponse.class); + + assertThat(visitorResponse.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(visitorResponse.getBody()).isNotNull(); + assertThat(visitorResponse.getBody().getUid()).isNotNull(); }); threads[i].start(); } @@ -106,46 +143,44 @@ public class VisitorAnonymousControllerIntegrationTest { } } - @Test - public void testVisitorInitWithInvalidParams() { - // Test with missing required parameters - MultiValueMap params = new LinkedMultiValueMap<>(); - params.add("type", TYPE); // Missing orgUid and sid - - ResponseEntity response = restTemplate.postForEntity( - "/visible/api/v1/init", - params, - VisitorResponse.class - ); - - assertThat(response.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST); - } - @Test public void testMultipleVisitorsWithMultipleRequests() throws InterruptedException { int visitorCount = 100; int requestsPerVisitor = 100; - + // Create a thread pool for managing concurrent visitors ExecutorService executorService = Executors.newFixedThreadPool(20); CountDownLatch completionLatch = new CountDownLatch(visitorCount); - + // Create visitors and their requests for (int i = 0; i < visitorCount; i++) { + final int index = i; executorService.submit(() -> { try { // Step 1: Create visitor VisitorRequest initRequest = VisitorRequest.builder() - .sid(AGENT_UID) - .build(); + .sid(AGENT_UID) + .build(); + initRequest.setUid("visitor_test_uid_" + index); initRequest.setOrgUid(ORG_UID); initRequest.setType(TYPE); + initRequest.setClient(ClientEnum.TEST.name()); - ResponseEntity visitorResponse = restTemplate.postForEntity( - "/visitor/api/v1/init", - initRequest, - VisitorResponse.class - ); + // 创建HTTP Headers并添加必要的信息 + HttpHeaders headers = new HttpHeaders(); + headers.set("User-Agent", "Mozilla/5.0 (Test Browser)"); + headers.set("X-Real-IP", "127.0.0.1"); + headers.set("X-Forwarded-For", "127.0.0.1"); + headers.set("Referer", "https://test.bytedesk.com"); + + // 使用HttpEntity包装请求 + HttpEntity requestEntity = new HttpEntity<>(initRequest, headers); + + ResponseEntity visitorResponse = restTemplate.exchange( + "/visitor/api/v1/init", + HttpMethod.GET, + requestEntity, + VisitorResponse.class); assertThat(visitorResponse.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(visitorResponse.getBody()).isNotNull(); @@ -154,19 +189,19 @@ public class VisitorAnonymousControllerIntegrationTest { // Step 2: Make multiple thread requests for this visitor for (int j = 0; j < requestsPerVisitor; j++) { VisitorRequest threadRequest = VisitorRequest.builder() - .sid(AGENT_UID) - .nickname(visitor.getNickname()) - .avatar(visitor.getAvatar()) - .build(); + .sid(AGENT_UID) + .nickname(visitor.getNickname()) + .avatar(visitor.getAvatar()) + .build(); threadRequest.setUid(visitor.getUid()); threadRequest.setOrgUid(ORG_UID); threadRequest.setType(TYPE); + threadRequest.setClient(ClientEnum.TEST.name()); ResponseEntity threadResponse = restTemplate.postForEntity( - "/visitor/api/v1/request", - threadRequest, - String.class - ); + "/visitor/api/v1/request", + threadRequest, + String.class); assertThat(threadResponse.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(threadResponse.getBody()).isNotNull(); @@ -180,9 +215,9 @@ public class VisitorAnonymousControllerIntegrationTest { // Wait for all visitors to complete their requests boolean completed = completionLatch.await(5, TimeUnit.MINUTES); executorService.shutdown(); - + assertThat(completed).isTrue() - .withFailMessage("Not all visitors completed their requests within the timeout period"); + .withFailMessage("Not all visitors completed their requests within the timeout period"); } -} \ No newline at end of file +} \ No newline at end of file