├── .github ├── bug_report.md ├── feature_request.md └── workflows │ ├── build.yml │ └── release.yml ├── .gitignore ├── README.md ├── docs └── aboutus │ └── README.md ├── pom.xml ├── settings.xml └── src ├── main └── java │ └── com │ └── meituan │ └── lyrebird │ ├── Lyrebird.java │ └── client │ ├── LyrebirdClient.java │ ├── LyrebirdService.java │ ├── MockData.java │ ├── api │ ├── BaseResponse.java │ ├── EventDetail.java │ ├── Events.java │ ├── Flow.java │ ├── FlowDetail.java │ ├── LBMockData.java │ ├── Request.java │ ├── Response.java │ ├── Status.java │ └── bandwidth │ │ ├── Bandwidth.java │ │ ├── BandwidthTemplate.java │ │ └── SpeedLimit.java │ ├── exceptions │ └── LyrebirdClientException.java │ └── listeners │ ├── Junit4Listener.java │ ├── Junit4Runner.java │ └── TestNGListener.java └── test └── java └── com └── meituan └── lyrebird └── test ├── TestFunctional.java └── TestLyrebirdClient.java /.github/bug_report.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | Please describe the issue. It may be a bug description. So then please briefly descride steps which you were trying to perform and what happened instead. 4 | If there is the feature you want to see added to Lyrebird Java client so please describe necessity of this feature and the way that it should work. 5 | 6 | ## Environment 7 | 8 | * Lyrebird-java-client build version or git revision if you use some shapshot: 9 | * Lyrebird version: 10 | * Desktop OS/version used to run lyrebird-java-client if necessary: 11 | * JAVA version: 12 | 13 | ## Details 14 | 15 | Please provide more details, if necessary. -------------------------------------------------------------------------------- /.github/feature_request.md: -------------------------------------------------------------------------------- 1 | ## Change list 2 | 3 | Please provide briefly described change list which are you going to propose. 4 | 5 | ## Types of changes 6 | 7 | What types of changes are you proposing/introducing to Lyrebird Java client? 8 | _Put an `x` in the boxes that apply_ 9 | 10 | - [ ] No changes in production code. 11 | - [ ] Bugfix (non-breaking change which fixes an issue) 12 | - [ ] New feature (non-breaking change which adds functionality) 13 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 14 | 15 | 16 | ## Details 17 | 18 | Please provide more details, if necessary. -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Lyrebird Java Client Build and Test 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v1 11 | - name: Set up JDK 1.8 12 | uses: actions/setup-java@v1 13 | with: 14 | java-version: 1.8 15 | - name: Unit Test 16 | run: mvn clean verify 17 | 18 | build: 19 | runs-on: ubuntu-latest 20 | 21 | steps: 22 | - uses: actions/checkout@v1 23 | - name: Set up JDK 1.8 24 | uses: actions/setup-java@v1 25 | with: 26 | java-version: 1.8 27 | - name: Build with Maven 28 | run: mvn package --file pom.xml 29 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Lyrebird Java Client Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-18.04 11 | 12 | steps: 13 | - uses: actions/checkout@v1 14 | - name: Set up JDK 1.8 15 | uses: actions/setup-java@v1 16 | with: 17 | java-version: 1.8 18 | - name: Deploy to The Central Respository 19 | env: 20 | SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} 21 | SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} 22 | GPG_EXECUTABLE: ${{ secrets.GPG_EXECUTABLE }} 23 | GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} 24 | GPG_SECRET_KEYS: ${{ secrets.GPG_SECRET_KEYS }} 25 | run: | 26 | echo "${GPG_SECRET_KEYS}" | gpg --batch --import 27 | mvn --settings settings.xml deploy -B -U -P release -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### macOS template 3 | # General 4 | .DS_Store 5 | .AppleDouble 6 | .LSOverride 7 | 8 | # Icon must end with two \r 9 | Icon 10 | 11 | # Thumbnails 12 | ._* 13 | 14 | # Files that might appear in the root of a volume 15 | .DocumentRevisions-V100 16 | .fseventsd 17 | .Spotlight-V100 18 | .TemporaryItems 19 | .Trashes 20 | .VolumeIcon.icns 21 | .com.apple.timemachine.donotpresent 22 | 23 | # Directories potentially created on remote AFP share 24 | .AppleDB 25 | .AppleDesktop 26 | Network Trash Folder 27 | Temporary Items 28 | .apdisk 29 | ### Java template 30 | # Compiled class file 31 | *.class 32 | 33 | # Log file 34 | *.log 35 | 36 | # BlueJ files 37 | *.ctxt 38 | 39 | # Mobile Tools for Java (J2ME) 40 | .mtj.tmp/ 41 | 42 | # Package Files # 43 | *.jar 44 | *.war 45 | *.ear 46 | *.zip 47 | *.tar.gz 48 | *.rar 49 | 50 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 51 | hs_err_pid* 52 | ### JetBrains template 53 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 54 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 55 | .idea/ 56 | *.iml 57 | 58 | # User-specific stuff: 59 | .idea/**/workspace.xml 60 | .idea/**/tasks.xml 61 | .idea/dictionaries 62 | 63 | # Sensitive or high-churn files: 64 | .idea/**/dataSources/ 65 | .idea/**/dataSources.ids 66 | .idea/**/dataSources.xml 67 | .idea/**/dataSources.local.xml 68 | .idea/**/sqlDataSources.xml 69 | .idea/**/dynamic.xml 70 | .idea/**/uiDesigner.xml 71 | 72 | # Gradle: 73 | .idea/**/gradle.xml 74 | .idea/**/libraries 75 | 76 | # CMake 77 | cmake-build-debug/ 78 | 79 | # Mongo Explorer plugin: 80 | .idea/**/mongoSettings.xml 81 | 82 | ## File-based project format: 83 | *.iws 84 | 85 | ## Plugin-specific files: 86 | 87 | # IntelliJ 88 | out/ 89 | 90 | # mpeltonen/sbt-idea plugin 91 | .idea_modules/ 92 | 93 | # JIRA plugin 94 | atlassian-ide-plugin.xml 95 | 96 | # Cursive Clojure plugin 97 | .idea/replstate.xml 98 | 99 | # Crashlytics plugin (for Android Studio and IntelliJ) 100 | com_crashlytics_export_strings.xml 101 | crashlytics.properties 102 | crashlytics-build.properties 103 | fabric.properties 104 | 105 | ### Maven template 106 | target/ 107 | pom.xml.tag 108 | pom.xml.releaseBackup 109 | pom.xml.versionsBackup 110 | pom.xml.next 111 | release.properties 112 | dependency-reduced-pom.xml 113 | buildNumber.properties 114 | .mvn/timing.properties 115 | 116 | # Avoid ignoring Maven wrapper jar file (.jar files are usually ignored) 117 | !/.mvn/wrapper/maven-wrapper.jar 118 | 119 | ### Python template 120 | # Byte-compiled / optimized / DLL files 121 | __pycache__/ 122 | *.py[cod] 123 | *$py.class 124 | 125 | # C extensions 126 | *.so 127 | 128 | # Distribution / packaging 129 | .Python 130 | build/ 131 | develop-eggs/ 132 | dist/ 133 | downloads/ 134 | eggs/ 135 | .eggs/ 136 | lib/ 137 | lib64/ 138 | parts/ 139 | sdist/ 140 | var/ 141 | wheels/ 142 | *.egg-info/ 143 | .installed.cfg 144 | *.egg 145 | MANIFEST 146 | 147 | # PyInstaller 148 | # Usually these files are written by a python script from a template 149 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 150 | *.manifest 151 | *.spec 152 | 153 | # CommandExecutor logs 154 | pip-log.txt 155 | pip-delete-this-directory.txt 156 | 157 | # Unit test / coverage reports 158 | htmlcov/ 159 | .tox/ 160 | .coverage 161 | .coverage.* 162 | .cache 163 | nosetests.xml 164 | coverage.xml 165 | *.cover 166 | .hypothesis/ 167 | 168 | # Translations 169 | *.mo 170 | *.pot 171 | 172 | # Django stuff: 173 | *.log 174 | .static_storage/ 175 | .media/ 176 | local_settings.py 177 | 178 | # Flask stuff: 179 | instance/ 180 | .webassets-cache 181 | 182 | # Scrapy stuff: 183 | .scrapy 184 | 185 | # Sphinx documentation 186 | docs/_build/ 187 | 188 | # PyBuilder 189 | target/ 190 | 191 | # Jupyter Notebook 192 | .ipynb_checkpoints 193 | 194 | # pyenv 195 | .python-version 196 | 197 | # celery beat schedule file 198 | celerybeat-schedule 199 | 200 | # SageMath parsed files 201 | *.sage.py 202 | 203 | # Environments 204 | .env 205 | .venv 206 | env/ 207 | venv/ 208 | ENV/ 209 | env.bak/ 210 | venv.bak/ 211 | 212 | # Spyder project settings 213 | .spyderproject 214 | .spyproject 215 | 216 | # Rope project settings 217 | .ropeproject 218 | 219 | # mkdocs documentation 220 | /site 221 | 222 | # mypy 223 | .mypy_cache/ 224 | 225 | # lyrebird debug 226 | data/ 227 | 228 | .vscode/ 229 | .classpath 230 | .project 231 | .settings/ 232 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lyrebird-java-client 2 | 3 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.meituan-dianping.lyrebird.sdk/lyrebird-java-client/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.appium/java-client) 4 | [![javadoc](https://javadoc.io/badge2/com.github.meituan-dianping.lyrebird.sdk/lyrebird-java-client/javadoc.svg)](https://javadoc.io/doc/com.github.meituan-dianping.lyrebird.sdk/lyrebird-java-client) 5 | [![Total alerts](https://img.shields.io/lgtm/alerts/g/Meituan-Dianping/lyrebird-java-client.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/Meituan-Dianping/lyrebird-java-client/alerts/) 6 | [![Language grade: Java](https://img.shields.io/lgtm/grade/java/g/Meituan-Dianping/lyrebird-java-client.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/Meituan-Dianping/lyrebird-java-client/context:java) 7 | 8 | --- 9 | 10 | - [简介](#简介) 11 | - [快速开始](#快速开始) 12 | - [环境要求](#环境要求) 13 | - [安装](#安装) 14 | - [使用](#使用) 15 | - [设置 Lyrebird Client](#设置-Lyrebird-Client) 16 | - [获取 Lyrebird Status](#获取-Lyrebird-Status) 17 | - [Mock 数据激活](#Mock-数据激活) 18 | - [使用 groupID 手动激活](#使用-groupID-手动激活) 19 | - [使用注解方式自动激活](#使用注解方式自动激活) 20 | - [TestNG](#TestNG) 21 | - [Junit4](#Junit4) 22 | - [取消激活](#取消激活) 23 | - [读取Mock数据](#读取Mock数据) 24 | - [获取网络数据信息](#获取网络数据信息) 25 | - [获取 flow List](#获取-flow-List) 26 | - [获取 flow ID](#获取-flow-ID) 27 | - [获取请求持续时长](#获取请求持续时长) 28 | - [获取请求开始时间](#获取请求开始时间) 29 | - [获取 flow 数据的详细信息](#获取-flow-数据的详细信息) 30 | - [获取请求对象](#获取请求对象) 31 | - [获取返回对象](#获取返回对象) 32 | - [清空 Flow 数据](#清空-Flow-数据) 33 | - [获取指定频道的消息总线数据](#获取指定频道的消息总线数据) 34 | - [获取Socket对象进行事件监听](#获取Socket对象进行事件监听) 35 | - [设置带宽限速](#设置带宽限速) 36 | - [应用场景](#应用场景) 37 | - [在UI自动化中校验请求参数是否符合预期](#在UI自动化中校验请求参数是否符合预期) 38 | - [在UI自动化中校验返回与客户端展示是否一致](#在UI自动化中校验返回与客户端展示是否一致) 39 | 40 | ## 简介 41 | 42 | lyrebird-java-client 是[Lyrebird](https://github.com/Meituan-Dianping/lyrebird)的一个 Java SDK,通过调用Lyrebird本身提供的[API](https://meituan-dianping.github.io/lyrebird/guide/api.html)实现在Java项目中控制 Lyrebird Services。比如:激活Mock数据;实时查看、验证网络数据等。 43 | 44 | ## 快速开始 45 | 46 | ### 环境要求 47 | 48 | - Java 1.8 49 | - Junit 4 or TestNG 6.14.x 50 | 51 | ### 安装 52 | 53 | - Maven项目添加如下依赖到 pom.xml 文件中 54 | 55 | ```xml 56 | 57 | com.meituan.lyrebird.sdk 58 | lyrebird-java-client 59 | RELEASE 60 | 61 | ``` 62 | 63 | ## 使用 64 | 65 | ### 设置 Lyrebird Client 66 | 67 | - 默认 Lyrebird 端口地址 (9090) 68 | 69 | ```java 70 | Lyrebird lyrebird = new Lyrebird(); 71 | ``` 72 | 73 | - 指定 Lyrebird 端口地址 74 | 75 | ```java 76 | Lyrebird lyrebird = new Lyrebird("http://:"); 77 | ``` 78 | 79 | ### 获取 Lyrebird Status 80 | 81 | ```java 82 | Lyrebird lyrebird = new Lyrebird(); 83 | Status status = lyrebird.status(); 84 | 85 | // 获取当前服务MOCK端口 86 | int mockPort = status.getMockPort(); 87 | 88 | // 获取当前服务代理端口 89 | int proxyPort = status.getPorxyPort(); 90 | 91 | // 获取当前服务IP 92 | String lyrebirdIP = status.getIp(); 93 | ``` 94 | 95 | ### Mock 数据激活 96 | 97 | #### 使用 groupID 手动激活 98 | 99 | > groupID: 89e0426c-9cf9-454a-bbe0-94246fc23b04 100 | 101 | ```java 102 | Lyrebird lyrebird = new Lyrebird(); 103 | 104 | lyrebird.activate("89e0426c-9cf9-454a-bbe0-94246fc23b04"); 105 | ``` 106 | 107 | #### 使用注解方式自动激活 108 | 109 | 在测试类或测试方法上声明 MockData 注解并设置 groupID 和 groupName 110 | 111 | ```java 112 | @MockData(groupID = "89e0426c-9cf9-454a-bbe0-94246fc23b04", groupName = "首页") 113 | public class TestClass { 114 | ... 115 | } 116 | 117 | 118 | @MockData(groupID = "89e0426c-9cf9-454a-bbe0-94246fc23b04", groupName = "首页") 119 | @Test 120 | public void testMethod() { 121 | ... 122 | } 123 | ``` 124 | 125 | 如果 Lyrebird 启动在特定的域名端口下,需要在测试方法执行前设置 Lyrebird 服务地址 126 | 127 | ```java 128 | // import here 129 | 130 | public class DemoCase { 131 | @BeforeMethod 132 | public void setup() { 133 | // Lyrebird server that start on local port 8082 134 | Lyrebird.setRemoteAddress("http://localhost:8082"); 135 | ... 136 | } 137 | } 138 | ``` 139 | 140 | #### TestNG 141 | 142 | 设置监听器 143 | 144 | - 方法一:修改 testng.xml 145 | 146 | ```xml 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | ``` 158 | 159 | - 方法二:源码中直接添加 160 | 161 | ```java 162 | import com.meituan.lyrebird.client.listeners.TestNGListener; 163 | import com.meituan.lyrebird.client.MockData; 164 | ... 165 | 166 | 167 | @MockData(groupID = "89e0426c-9cf9-454a-bbe0-94246fc23b04", groupName = "首页") 168 | @Listeners(TestNGListener.class) 169 | public class TestClass { 170 | ... 171 | } 172 | ``` 173 | 174 | #### Junit4 175 | 176 | 设置监听器 177 | 178 | - 方法一:修改 pom.xml 179 | 180 | ```xml 181 | 182 | 183 | [...] 184 | 185 | org.apache.maven.plugins 186 | maven-surefire-plugin 187 | 188 | 189 | 190 | listener 191 | com.meituan.lyrebird.client.events.Junit4Listener 192 | 193 | 194 | 195 | 196 | [...] 197 | 198 | ``` 199 | 200 | - 方法二:源码中直接添加 201 | 202 | ```java 203 | import org.junit.runner.RunWith; 204 | import com.meituan.lyrebird.client.events.Junit4Runner; 205 | import com.meituan.lyrebird.client.MockData; 206 | 207 | @MockData(groupID = "89e0426c-9cf9-454a-bbe0-94246fc23b04", groupName = "首页") 208 | @RunWith(Junit4Runner.class) 209 | public class TestClass { 210 | ... 211 | } 212 | ``` 213 | 214 | #### 取消激活 215 | 216 | ```java 217 | Lyrebird lyrebird = new Lyrebird(); 218 | 219 | lyrebird.deactivate(); 220 | ``` 221 | 222 | #### 读取Mock数据 223 | 224 | 调用 getMockData(String dataId) 方法可以获取该数据组的 Mock 数据详情 225 | ```java 226 | Lyrebird lyrebird = new Lyrebird(); 227 | 228 | LBMockData lbMockData = lyrebird.getMockData("cfa0c589-8ef0-4885-b4f4-b9688c5af0d5"); 229 | // 读取Mock数据名 230 | String mockDataName = lbMockData.getName(); 231 | // 读取Mock Response (注意:Lyrebird服务端返回的是一个 Json 字符串,并不是一个 Json 对象) 232 | Object mockDataResponse = lbMockData.getResponseData(); 233 | ``` 234 | 235 | ### 获取网络数据信息 236 | 237 | flow 示例 238 | 239 | ```javascript 240 | [ 241 | { 242 | "id": "b193416d-f89c-435f-b158-4e47911cf98b", 243 | "size": 350, 244 | "duration": 0.21155691146850586, 245 | "start_time": 1566805862.385838, 246 | "request": { 247 | "url": "https://lyrebird.java.client.com/api/example", 248 | "path": "/api/example", 249 | "host": "lyrebird.java.client.com", 250 | "method": "POST" 251 | }, 252 | "response": { 253 | "code": 200, 254 | "mock": "proxy" 255 | } 256 | } 257 | ] 258 | ``` 259 | 260 | flow detail 示例 261 | 262 | ```javascript 263 | { 264 | "id": "b193416d-f89c-435f-b158-4e47911cf98b", 265 | "duration": 0.21155691146850586, 266 | "start_time": 1566805862.385838, 267 | "request": { 268 | "headers": { 269 | "Accept-Encoding": "gzip", 270 | "Userid": "-1", 271 | "Content-Type": "application/x-www-form-urlencoded", 272 | "Content-Length": "2942", 273 | "Host": "127.0.0.1:9090", 274 | "Connection": "Keep-Alive" 275 | }, 276 | "method": "POST", 277 | "query": { 278 | "name": "tester", 279 | "city": "beijing" 280 | }, 281 | "url": "https://lyrebird.java.client.com/api/example", 282 | "host": "lyrebird.java.client.com", 283 | "path": "/api/example", 284 | "data": { 285 | "countryCode": "86" 286 | } 287 | }, 288 | "response": { 289 | "code": 200, 290 | "headers": { 291 | "Date": "Mon, 26 Aug 2019 07:51:02 GMT", 292 | "Content-Type": "application/json;charset=utf-8", 293 | "Content-Length": "761", 294 | "Connection": "keep-alive" 295 | }, 296 | "timestamp": 1566805862.6, 297 | "data": { 298 | "user": { 299 | "age": 18, 300 | "username": "lyrebird-java-client" 301 | } 302 | } 303 | } 304 | } 305 | ``` 306 | 307 | Flow 类属性 308 | 309 | | 属性名 | 说明 | 310 | | :---------- | :-------------------------- | 311 | | `id` | 描述 flow data 的唯一 ID 标识 | 312 | | `duration` | 客户端发起请求的持续时长 | 313 | | `startTime` | 客户端发起请求的时间戳 | 314 | | `request` | 客户端请求服务端的请求Java对象 | 315 | | `response` | 远端服务返回的响应报文Java对象 | 316 | 317 | #### 获取 flow List 318 | 319 | > Flow 数据保存为一个 List,单个 flow data 数据详见上面的示例 320 | 321 | ```java 322 | Lyrebird lyrebird = new Lyrebird(); 323 | 324 | Flow[] flowList = lyrebird.getFlowList(); 325 | ``` 326 | 327 | #### 获取 flow ID 328 | 329 | ```java 330 | String flowId = flowList[0].getId(); 331 | ``` 332 | 333 | #### 获取请求持续时长 334 | 335 | ```java 336 | double duration = flowList[0].getDuration(); 337 | ``` 338 | 339 | #### 获取请求开始时间 340 | 341 | ```java 342 | double startTime = flowList[0].getStartTime(); 343 | ``` 344 | 345 | #### 获取 flow 数据的详细信息 346 | 347 | > 默认 flow 中包含的网络数据是概要信息,可以通过 flow id 获取网络数据详细信息 348 | 349 | ```java 350 | FlowDetail flowDetail = lyrebird.getFlowDetail(flowId); 351 | ``` 352 | 353 | #### 获取请求对象 354 | 355 | ```java 356 | Request request = flowDetail.getRequest(); 357 | ``` 358 | 359 | #### 获取返回对象 360 | 361 | ```java 362 | Response response = flowDetail.getResponse(); 363 | ``` 364 | 365 | #### 清空 Flow 数据 366 | 367 | ```java 368 | Lyrebird lyrebird = new Lyrebird(); 369 | 370 | // 清空 flow 列表 371 | lyrebird.clearFlowList(); 372 | ``` 373 | 374 | ### 获取指定频道的消息总线数据 375 | 376 | 接口文档[点击这里](https://meituan-dianping.github.io/lyrebird/guide/api.html#获取已激活mock数据) 377 | 378 | ```java 379 | // 获取 page 频道对应的消息总线数据 380 | EventDetail[] eventList = lyrebird.getEventList("page"); 381 | 382 | // 获取该频道第0个事件的事件ID 383 | String eventID = eventList[0].getEventID(); 384 | 385 | // 获取该频道第0个事件的内容 386 | String content = eventList[0].getContent(); 387 | ``` 388 | 389 | ### 获取Socket对象进行事件监听 390 | 391 | 使用[socket.io client](https://github.com/clwillingham/java-socket.io.client)监听Lyrebird socket事件消息 392 | 393 | ```java 394 | // 获取 socket.io client 395 | Socket socket = lyrebird.getSocketInstance(); 396 | 397 | // 事件监听 398 | socket.on("action", new Emitter.Listener() { 399 | 400 | @Override 401 | public void call(Object... args) { 402 | // do something here... 403 | } 404 | }); 405 | 406 | // 建立连接 407 | socket.connect(); 408 | 409 | // 关闭连接 410 | socket.disconnect(); 411 | ``` 412 | 413 | ### 设置带宽限速 414 | 415 | [Lyrebird 获取/设置网络带宽限速接口文档](https://meituan-dianping.github.io/lyrebird/guide/api.html#%E8%8E%B7%E5%8F%96%E5%BD%93%E5%89%8D%E7%BD%91%E7%BB%9C%E5%B8%A6%E5%AE%BD%E9%99%90%E9%80%9F) 416 | 417 | ```java 418 | // 设置带宽限速为2G 419 | lyrebird.setSpeedLimit(Bandwidth.BANDWIDTH_2G); 420 | // 设置带宽限速为2.5G 421 | lyrebird.setSpeedLimit(Bandwidth.BANDWIDTH_2_5G); 422 | // 设置带宽限速为3G 423 | lyrebird.setSpeedLimit(Bandwidth.BANDWIDTH_3G); 424 | // 设置带宽不限速 425 | lyrebird.setSpeedLimit(Bandwidth.UNLIMITED); 426 | // 获取带宽速度 427 | int bandwidth = lyrebird.getSpeedLimit().getBandwidth(); 428 | ``` 429 | 430 | ## 应用场景 431 | 432 | 在UI自动化中,可将移动设备通过代理的方式将请求数据接入Lyrebird,[操作指南](https://github.com/Meituan-Dianping/lyrebird#连接移动设备),在测试用例中通过调用Lyrebird API来校验网络请求参数是否符合预期。 433 | 434 | Lyrebird Java SDK 分别提供 Request, Response 类描述客户端发起的请求和响应数据 435 | 436 | Request 类 437 | 438 | | 属性名 | 说明 | 439 | | :-------- | :-------------- | 440 | | `headers` | 客户端请求报文头部 | 441 | | `method` | 客户端HTTP请求方法 | 442 | | `query` | query参数 | 443 | | `url` | 客户端请求url | 444 | | `host` | 客户端请求host | 445 | | `path` | 客户端请求path | 446 | | `data` | from-data参数 | 447 | 448 | Response 类 449 | 450 | | 属性名 | 说明 | 451 | | :-------- | :------------------ | 452 | | `code` | HTTP 状态码 | 453 | | `headers` | 服务端返回响应报文头部 | 454 | | `data` | 服务端返回响应报文主体 | 455 | 456 | ### 在UI自动化中校验请求参数是否符合预期 457 | 458 | ```java 459 | // 实例化 Lyrebird 对象 460 | Lyrebird lyrebird = new Lyrebird(); 461 | 462 | // 获取客户端发送的所有 Flow 数据保存为一个 List 463 | Flow[] flowList = lyrebird.getFlowList(); 464 | 465 | // 遍历 flow list 中的每一条 flow 数据 466 | for (Flow flow : flowList) { 467 | // 取出 flow data 的 url 信息 468 | String url = flow.getRequest().getUrl(); 469 | // 断言 http://lyrebird.java.client.com/api/example 接口的客户端请求内容 470 | if (url.equals("http://lyrebird.java.client.com/api/example")) { 471 | // 通过 flow id 查询该条 flow 数据的详细信息 472 | FlowDetail flowDetail = lyrebird.getFlowDetail(flow.getId()); 473 | // 断言请求 headers 中 Content-Type 等于 application/x-www-form-urlencoded 474 | assertEquals("application/x-www-form-urlencoded", flowDetail.getRequest().getHeaders().get("Content-Type")); 475 | // 断言请求方法是 POST 476 | assertEquals("POST", flowDetail.getRequest().getMethod()); 477 | // 断言请求 query 中 name 等于 tester 478 | assertEquals("tester", flowDetail.getRequest().getQuery().get("name")); 479 | // 断言请求 form-data 中 countryCode 等于 86 480 | assertEquals("86", flowDetail.getRequest().getData().get("countryCode")); 481 | } 482 | } 483 | ``` 484 | 485 | ### 在UI自动化中校验返回与客户端展示是否一致 486 | 487 | ```java 488 | // 实例化 Lyrebird 对象 489 | Lyrebird lyrebird = new Lyrebird(); 490 | 491 | // 获取客户端发送的所有 Flow 数据保存为一个 List 492 | Flow[] flowList = lyrebird.getFlowList(); 493 | 494 | // 遍历 flow list 中的每一条 flow 数据 495 | for (Flow flow : flowList) { 496 | // 取出 flow data 的 url 信息 497 | String url = flow.getRequest().getUrl(); 498 | // 断言 http://lyrebird.java.client.com/api/example 接口的客户端请求内容 499 | if (url.equals("http://lyrebird.java.client.com/api/example")) { 500 | // 通过 flow id 查询该条 flow 数据的详细信息 501 | FlowDetail flowDetail = lyrebird.getFlowDetail(flow.getId()); 502 | // 断言 HTTP 状态码为 200 503 | assertEquals("200", flowDetail.getResponse().getCode()); 504 | // 断言返回 headers 中 Content-Type 等于 application/json;charset=utf-8 505 | assertEquals("application/json;charset=utf-8", flowDetail.getResponse().getHeaders().get("Content-Type")); 506 | /* 507 | * 断言 data 中 countryCode 等于 86 508 | * getData() 参数 JsonPath 相关使用方法及说明,详见官方 README: https://github.com/json-path/JsonPath/edit/master/README.md 509 | */ 510 | assertEquals(18, flowDetail.getResponse().getData("$.user.age")); 511 | } 512 | } 513 | ``` 514 | -------------------------------------------------------------------------------- /docs/aboutus/README.md: -------------------------------------------------------------------------------- 1 | # 关于我们 2 | 3 | 美团点评酒旅测试组 4 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.github.meituan-dianping.lyrebird.sdk 7 | lyrebird-java-client 8 | 1.1.6 9 | 10 | lyrebird-java-client 11 | https://github.com/Meituan-Dianping/lyrebird-java-client 12 | lyrebird java client is a Java SDK of Lyrebird 13 | 14 | 15 | 16 | MIT License 17 | http://www.opensource.org/licenses/mit-license.php 18 | 19 | 20 | 21 | 22 | 23 | hbqa 24 | tech@meituan.com 25 | Meituan-Dianping 26 | https://tech.meituan.com/ 27 | 28 | 29 | 30 | 31 | scm:git:git://github.com/Meituan-Dianping/lyrebird-java-client.git 32 | scm:git:ssh://github.com:Meituan-Dianping/lyrebird-java-client.git 33 | https://github.com/Meituan-Dianping/lyrebird-java-client/tree/master 34 | 35 | 36 | 37 | 38 | ossrh 39 | https://oss.sonatype.org/content/repositories/snapshots 40 | 41 | 42 | ossrh 43 | https://oss.sonatype.org/service/local/staging/deploy/maven2 44 | 45 | 46 | 47 | 48 | 1.8 49 | UTF-8 50 | 51 | 52 | 53 | 54 | 55 | org.apache.maven.plugins 56 | maven-compiler-plugin 57 | 58 | 1.8 59 | 1.8 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | release 69 | 70 | 71 | release 72 | 73 | 74 | 75 | 76 | 77 | org.apache.maven.plugins 78 | maven-source-plugin 79 | 2.2.1 80 | 81 | 82 | attach-sources 83 | 84 | jar-no-fork 85 | 86 | 87 | 88 | 89 | 90 | org.apache.maven.plugins 91 | maven-javadoc-plugin 92 | 2.9.1 93 | 94 | 95 | attach-javadocs 96 | 97 | jar 98 | 99 | 100 | 101 | 102 | 103 | org.apache.maven.plugins 104 | maven-gpg-plugin 105 | 1.5 106 | 107 | 108 | sign-artifacts 109 | verify 110 | 111 | sign 112 | 113 | 114 | 115 | 116 | --pinentry-mode 117 | loopback 118 | 119 | 120 | 121 | 122 | 123 | 124 | org.sonatype.plugins 125 | nexus-staging-maven-plugin 126 | 1.6.7 127 | true 128 | 129 | ossrh 130 | https://oss.sonatype.org/ 131 | true 132 | 133 | 134 | 135 | org.apache.maven.plugins 136 | maven-release-plugin 137 | 2.4.2 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | com.squareup.retrofit2 147 | retrofit 148 | [2.4.0,) 149 | 150 | 151 | com.squareup.retrofit2 152 | converter-gson 153 | 2.3.0 154 | 155 | 156 | com.squareup.retrofit2 157 | converter-jackson 158 | 2.5.0 159 | 160 | 161 | junit 162 | junit 163 | 4.12 164 | 165 | 166 | org.hamcrest 167 | hamcrest-all 168 | 1.3 169 | test 170 | 171 | 172 | com.squareup.okhttp3 173 | mockwebserver 174 | 3.9.0 175 | test 176 | 177 | 178 | io.socket 179 | socket.io-client 180 | 1.0.0 181 | 182 | 183 | com.jayway.jsonpath 184 | json-path 185 | 2.3.0 186 | 187 | 188 | org.testng 189 | testng 190 | 7.0.0 191 | 192 | 193 | 194 | -------------------------------------------------------------------------------- /settings.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | ossrh 9 | ${env.SONATYPE_USERNAME} 10 | ${env.SONATYPE_PASSWORD} 11 | 12 | 13 | 14 | 15 | 16 | ossrh 17 | 18 | true 19 | 20 | 21 | ${env.GPG_EXECUTABLE} 22 | ${env.GPG_PASSPHRASE} 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/main/java/com/meituan/lyrebird/Lyrebird.java: -------------------------------------------------------------------------------- 1 | package com.meituan.lyrebird; 2 | 3 | import com.meituan.lyrebird.client.api.bandwidth.Bandwidth; 4 | import com.meituan.lyrebird.client.api.bandwidth.SpeedLimit; 5 | import io.socket.client.Socket; 6 | import java.lang.reflect.Method; 7 | 8 | import com.meituan.lyrebird.client.LyrebirdClient; 9 | import com.meituan.lyrebird.client.exceptions.LyrebirdClientException; 10 | 11 | import com.meituan.lyrebird.client.api.*; 12 | import java.net.URISyntaxException; 13 | import java.util.concurrent.TimeUnit; 14 | 15 | public class Lyrebird { 16 | private LyrebirdClient client; 17 | private static ThreadLocal remoteAddress = ThreadLocal.withInitial(() -> "http://localhost:9090/"); 18 | 19 | public Lyrebird() { 20 | this(remoteAddress.get()); 21 | } 22 | 23 | public Lyrebird(String lyrebirdRemoteAddress) { 24 | this(lyrebirdRemoteAddress,TimeUnit.SECONDS,0,10,10,0); 25 | } 26 | 27 | public Lyrebird( 28 | String lyrebirdRemoteAddress, 29 | TimeUnit timeUnit, 30 | long callTimeout, 31 | long connectTimeout, 32 | long readTimeout, 33 | long writeTimeout 34 | ) { 35 | client = new LyrebirdClient( 36 | lyrebirdRemoteAddress, 37 | timeUnit, 38 | callTimeout, 39 | connectTimeout, 40 | readTimeout, 41 | writeTimeout 42 | ); 43 | } 44 | 45 | /** 46 | * set remote lyrebird server address 47 | * 48 | * @param url e.g http://localhost:9090 49 | */ 50 | public static void setRemoteAddress(String url) { 51 | remoteAddress.set(url); 52 | } 53 | 54 | /** 55 | * get remote lyrebird server address 56 | * 57 | * @return remoteAddress Lyrebird server address 58 | */ 59 | public static String getRemoteAddress() { 60 | return remoteAddress.get(); 61 | } 62 | 63 | /** 64 | * Activate lyrebird mock data group by group ID 65 | * 66 | * @throws LyrebirdClientException 67 | */ 68 | public void activate(String groupID) throws LyrebirdClientException { 69 | if (client == null) { 70 | throw new LyrebirdClientException("Please start lyrebird server before call this function"); 71 | } 72 | client.activate(groupID); 73 | } 74 | 75 | /** 76 | * Activate lyrebird mock data group by @MockData annotation 77 | * 78 | * @param method test method 79 | * @throws LyrebirdClientException 80 | */ 81 | public void activate(Method method) throws LyrebirdClientException { 82 | if (client == null) { 83 | throw new LyrebirdClientException("Please start lyrebird server before call this function"); 84 | } 85 | client.activate(method); 86 | } 87 | 88 | /** 89 | * Deactivate lyrebird mock data group 90 | * 91 | * @throws LyrebirdClientException 92 | */ 93 | public void deactivate() throws LyrebirdClientException { 94 | if (client == null) { 95 | throw new LyrebirdClientException("Please start lyrebird server before call this function"); 96 | } 97 | client.deactivate(); 98 | } 99 | 100 | /** 101 | * Get lyrebird services status 102 | * 103 | * @return Status 104 | * @throws LyrebirdClientException 105 | */ 106 | public Status getLyrebirdStatus() throws LyrebirdClientException { 107 | if (client == null) { 108 | throw new LyrebirdClientException("Please start lyrebird server before call this function"); 109 | } 110 | return client.status(); 111 | } 112 | 113 | /** 114 | * Get a set of flow data from lyrebird API 115 | * 116 | * @return An array list contains Flows data 117 | * @throws LyrebirdClientException 118 | */ 119 | public Flow[] getFlowList() throws LyrebirdClientException { 120 | if (client == null) { 121 | throw new LyrebirdClientException("Please start lyrebird server before call this function"); 122 | } 123 | return client.getFlowList(); 124 | } 125 | 126 | /** 127 | * Get Lyrebird flow data detail 128 | * 129 | * @param flowID The unique indication of the flow data 130 | * @return A flow detail which found by ID 131 | * @throws LyrebirdClientException 132 | */ 133 | public FlowDetail getFlowDetail(String flowID) throws LyrebirdClientException { 134 | if (client == null) { 135 | throw new LyrebirdClientException("Please start lyrebird server before call this function"); 136 | } 137 | return client.getFlowDetail(flowID); 138 | } 139 | 140 | /** 141 | * Clear all flow data 142 | * 143 | * @throws LyrebirdClientException 144 | */ 145 | public void clearFlowList() throws LyrebirdClientException { 146 | if (client == null) { 147 | throw new LyrebirdClientException("Please start lyrebird server before call this function"); 148 | } 149 | client.clearFlowList(); 150 | } 151 | 152 | /** 153 | * Get event list by channel 154 | * 155 | * @param channel channel name 156 | * @return Events a list of event 157 | * @throws LyrebirdClientException 158 | */ 159 | public EventDetail[] getEventList(String channel) throws LyrebirdClientException { 160 | if (client == null) { 161 | throw new LyrebirdClientException("Please start lyrebird server before call this function"); 162 | } 163 | return client.getEventList(channel).getEvents(); 164 | } 165 | 166 | /** 167 | * Get mock data by data id 168 | * 169 | * @param dataId mock data id 170 | * @return 171 | * @throws LyrebirdClientException 172 | */ 173 | public LBMockData getMockData(String dataId) throws LyrebirdClientException { 174 | return client.getMockData(dataId); 175 | } 176 | 177 | /** 178 | * set the speed limit 179 | * 180 | * @param bandwidth BANDWIDTH_2G对应2G;BANDWIDTH_2_5G对应2.5G;BANDWIDTH_3G对应3G;UNLIMITED对应无限制 181 | * @throws LyrebirdClientException 182 | */ 183 | public void setSpeedLimit(Bandwidth bandwidth) throws LyrebirdClientException { 184 | client.setSpeedLimit(bandwidth); 185 | } 186 | 187 | /** 188 | * get the speed limit bandwidth 189 | * 190 | * @return 191 | * @throws LyrebirdClientException 192 | */ 193 | public SpeedLimit getSpeedLimit() throws LyrebirdClientException { 194 | return client.getSpeedLimit(); 195 | } 196 | 197 | /** 198 | * Get an object of socket io 199 | * 200 | * @return 201 | * @throws URISyntaxException 202 | */ 203 | public Socket getSocketInstance() throws URISyntaxException, LyrebirdClientException { 204 | if (client == null) { 205 | throw new LyrebirdClientException("Please start lyrebird server before call this function"); 206 | } 207 | return client.getSocketInstance(remoteAddress.get()); 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /src/main/java/com/meituan/lyrebird/client/LyrebirdClient.java: -------------------------------------------------------------------------------- 1 | package com.meituan.lyrebird.client; 2 | 3 | import com.fasterxml.jackson.databind.DeserializationFeature; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.meituan.lyrebird.client.api.bandwidth.Bandwidth; 6 | import com.meituan.lyrebird.client.api.bandwidth.BandwidthTemplate; 7 | import com.meituan.lyrebird.client.api.bandwidth.SpeedLimit; 8 | import com.meituan.lyrebird.client.exceptions.LyrebirdClientException; 9 | import com.meituan.lyrebird.client.api.*; 10 | import io.socket.client.IO; 11 | import io.socket.client.Socket; 12 | import java.net.URISyntaxException; 13 | import okhttp3.OkHttpClient; 14 | import retrofit2.Retrofit; 15 | import retrofit2.converter.jackson.JacksonConverterFactory; 16 | 17 | import java.io.IOException; 18 | import java.lang.reflect.Method; 19 | import java.util.concurrent.TimeUnit; 20 | 21 | public class LyrebirdClient { 22 | private LyrebirdService lyrebirdService; 23 | private Socket socket; 24 | 25 | public LyrebirdClient( 26 | String lyrebirdRemoteAddress, 27 | TimeUnit timeUnit, 28 | long callTimeout, 29 | long connectTimeout, 30 | long readTimeout, 31 | long writeTimeout 32 | ) { 33 | ObjectMapper mapper = new ObjectMapper(); 34 | mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT); 35 | 36 | OkHttpClient.Builder httpClient = new OkHttpClient.Builder() 37 | .callTimeout(callTimeout, timeUnit) 38 | .connectTimeout(connectTimeout,timeUnit) 39 | .readTimeout(readTimeout,timeUnit) 40 | .writeTimeout(writeTimeout,timeUnit); 41 | 42 | Retrofit.Builder builder = new Retrofit 43 | .Builder() 44 | .baseUrl(lyrebirdRemoteAddress) 45 | .addConverterFactory(JacksonConverterFactory.create(mapper)) 46 | .client(httpClient.build()); 47 | 48 | Retrofit retrofit = builder.build(); 49 | 50 | // create service 51 | lyrebirdService = retrofit.create(LyrebirdService.class); 52 | } 53 | 54 | /** 55 | * Get lyrebird services status 56 | * 57 | * @return Status 58 | * @throws LyrebirdClientException 59 | */ 60 | public Status status() throws LyrebirdClientException { 61 | Status resp; 62 | try { 63 | resp = lyrebirdService.status().execute().body(); 64 | } catch (IOException e) { 65 | throw new LyrebirdClientException("Catch exception while get status", e); 66 | } 67 | if (resp == null) { 68 | throw new LyrebirdClientException("Got none response from status request"); 69 | } 70 | if (resp.getCode() != 1000) { 71 | throw new LyrebirdClientException(resp.getMessage()); 72 | } 73 | return resp; 74 | } 75 | 76 | /** 77 | * Activate lyrebird mock data group by group ID 78 | * 79 | * @param groupID 80 | * @throws LyrebirdClientException 81 | */ 82 | public void activate(String groupID) throws LyrebirdClientException { 83 | BaseResponse resp; 84 | try { 85 | resp = lyrebirdService.activate(groupID).execute().body(); 86 | } catch (IOException e) { 87 | throw new LyrebirdClientException("Catch exception while activate data", e); 88 | } 89 | if (resp == null) { 90 | throw new LyrebirdClientException("Got none response from activate request"); 91 | } 92 | if (resp.getCode() != 1000) { 93 | throw new LyrebirdClientException(resp.getMessage()); 94 | } 95 | } 96 | 97 | /** 98 | * Activate lyrebird mock data group by @MockData annotation 99 | * 100 | * @param method test method 101 | * @throws LyrebirdClientException 102 | */ 103 | public void activate(Method method) throws LyrebirdClientException { 104 | BaseResponse resp; 105 | MockData mockDataDeclareOnMethod = method.getDeclaredAnnotation(MockData.class); 106 | MockData mockDataDeclaredOnClass = method.getDeclaringClass().getAnnotation(MockData.class); 107 | 108 | if (mockDataDeclareOnMethod == null && mockDataDeclaredOnClass == null) { 109 | throw new LyrebirdClientException("Catch exception while activate data, can not found any @MockData annotation declared"); 110 | } 111 | try { 112 | if (mockDataDeclareOnMethod != null) { 113 | resp = lyrebirdService.activate(mockDataDeclareOnMethod.groupID()).execute().body(); 114 | } else { 115 | resp = lyrebirdService.activate(mockDataDeclaredOnClass.groupID()).execute().body(); 116 | } 117 | } catch (IOException e) { 118 | throw new LyrebirdClientException("Catch exception while activate data", e); 119 | } 120 | if (resp == null) { 121 | throw new LyrebirdClientException("Got none response from activate request"); 122 | } 123 | if (resp.getCode() != 1000) { 124 | throw new LyrebirdClientException(resp.getMessage()); 125 | } 126 | } 127 | 128 | /** 129 | * Deactivate lyrebird mock data group 130 | * @throws LyrebirdClientException 131 | */ 132 | public void deactivate() throws LyrebirdClientException { 133 | BaseResponse resp; 134 | try { 135 | resp = lyrebirdService.deactivate().execute().body(); 136 | } catch (IOException e) { 137 | throw new LyrebirdClientException("Catch exception while deactivate data", e); 138 | } 139 | 140 | if (resp == null) { 141 | throw new LyrebirdClientException("Got none response from deactivate request"); 142 | } 143 | if (resp.getCode() != 1000) { 144 | throw new LyrebirdClientException(resp.getMessage()); 145 | } 146 | } 147 | 148 | /** 149 | * Get a set of flow data from lyrebird API 150 | * 151 | * @return An array list contains Flows data 152 | * @throws LyrebirdClientException 153 | */ 154 | public Flow[] getFlowList() throws LyrebirdClientException { 155 | try { 156 | return lyrebirdService.getFlowList().execute().body(); 157 | } catch (IOException e) { 158 | throw new LyrebirdClientException("Catch exception while find flows", e); 159 | } 160 | } 161 | 162 | /** 163 | * Get Lyrebird flow data detail 164 | * 165 | * @param flowId 每条flow data都对应一个ID标识,通过ID可以查询该条数据的详细信息 166 | * @return A flow which found by ID 167 | * @throws LyrebirdClientException 168 | */ 169 | public FlowDetail getFlowDetail(String flowId) throws LyrebirdClientException { 170 | try { 171 | return lyrebirdService.getFlowDetail(flowId).execute().body(); 172 | } catch (IOException e) { 173 | throw new LyrebirdClientException("Catch exception while find flow by ID", e); 174 | } 175 | } 176 | 177 | /** 178 | * Clear all flow data 179 | * 180 | * @throws LyrebirdClientException 181 | */ 182 | public void clearFlowList() throws LyrebirdClientException { 183 | BaseResponse resp; 184 | try{ 185 | resp = lyrebirdService.clearFlowList(new Flow()).execute().body(); 186 | }catch (IOException e){ 187 | throw new LyrebirdClientException("Catch exception while clear flow data",e); 188 | } 189 | if(resp==null){ 190 | throw new LyrebirdClientException("Got none response from delete /api/flow"); 191 | } 192 | if(resp.getCode()!=1000){ 193 | throw new LyrebirdClientException(resp.getMessage()); 194 | } 195 | } 196 | 197 | /** 198 | * Get event list by channel 199 | * 200 | * @param channel channel name 201 | * @return Events a list of event 202 | * @throws LyrebirdClientException 203 | */ 204 | public Events getEventList(String channel) throws LyrebirdClientException { 205 | try{ 206 | return lyrebirdService.getEventList(channel).execute().body(); 207 | } catch (IOException e) { 208 | throw new LyrebirdClientException("Catch exception while find events by channel ", e); 209 | } 210 | } 211 | 212 | /** 213 | * get mock data by data id 214 | * 215 | * @param dataId mock data id 216 | * @return 217 | * @throws LyrebirdClientException 218 | */ 219 | public LBMockData getMockData(String dataId) throws LyrebirdClientException { 220 | try{ 221 | return lyrebirdService.getMockData(dataId).execute().body(); 222 | } catch (IOException e) { 223 | throw new LyrebirdClientException("Catch exception while get mock response data by data id ", e); 224 | } 225 | } 226 | 227 | /** 228 | * set the speed limit 229 | * 230 | * @param bandwidth an enum of Bandwidth 231 | * @throws LyrebirdClientException 232 | */ 233 | public void setSpeedLimit(Bandwidth bandwidth) throws LyrebirdClientException { 234 | BandwidthTemplate bandwidthTemplate = new BandwidthTemplate(bandwidth); 235 | BaseResponse resp; 236 | try{ 237 | resp = lyrebirdService.setSpeedLimit(bandwidthTemplate).execute().body(); 238 | } catch (IOException e) { 239 | throw new LyrebirdClientException("Catch exception while set the speed limit", e); 240 | } 241 | if (resp == null) { 242 | throw new LyrebirdClientException("Got none response from the speed limit request"); 243 | } 244 | if (resp.getCode() != 1000) { 245 | throw new LyrebirdClientException(resp.getMessage()); 246 | } 247 | } 248 | 249 | /** 250 | * get the speed limit bandwidth 251 | * 252 | * @return 253 | * @throws LyrebirdClientException 254 | */ 255 | public SpeedLimit getSpeedLimit() throws LyrebirdClientException { 256 | try { 257 | return lyrebirdService.getSpeedLimit().execute().body(); 258 | } catch (IOException e) { 259 | throw new LyrebirdClientException("Catch exception while get speed limit bandwidth.", e); 260 | } 261 | } 262 | 263 | /** 264 | * Get an object of socket io 265 | * 266 | * @param lyrebirdRemoteAddress 267 | * @return 268 | * @throws URISyntaxException 269 | */ 270 | public Socket getSocketInstance(String lyrebirdRemoteAddress) throws URISyntaxException { 271 | if (socket == null || !socket.connected()) { 272 | socket = IO.socket(lyrebirdRemoteAddress); 273 | } 274 | socket.on(Socket.EVENT_CONNECT, objects -> System.out.println("[Lyrebird Java client Socket IO]: Socket connection established")); 275 | socket.on(Socket.EVENT_CONNECTING, objects -> System.out.println("[Lyrebird Java client Socket IO]: Socket connecting ...")); 276 | socket.on(Socket.EVENT_CONNECT_TIMEOUT, objects -> System.out.println("[Lyrebird Java client Socket IO]: Connection timeout")); 277 | socket.on(Socket.EVENT_CONNECT_ERROR, objects -> System.out.println("[Lyrebird Java client Socket IO]: Failed to connect")); 278 | return socket; 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /src/main/java/com/meituan/lyrebird/client/LyrebirdService.java: -------------------------------------------------------------------------------- 1 | package com.meituan.lyrebird.client; 2 | 3 | import com.meituan.lyrebird.client.api.*; 4 | 5 | import com.meituan.lyrebird.client.api.bandwidth.BandwidthTemplate; 6 | import com.meituan.lyrebird.client.api.bandwidth.SpeedLimit; 7 | import retrofit2.Call; 8 | import retrofit2.http.*; 9 | 10 | public interface LyrebirdService { 11 | @GET("api/status") 12 | Call status(); 13 | 14 | @PUT("api/mock/{group}/activate") 15 | Call activate(@Path("group") String groupID); 16 | 17 | @PUT("api/mock/groups/deactivate") 18 | Call deactivate(); 19 | 20 | @GET("api/flow") 21 | Call getFlowList(); 22 | 23 | @GET("api/flow/{flowId}") 24 | Call getFlowDetail(@Path("flowId") String flowId); 25 | 26 | @Headers("Content-Type: application/json") 27 | @HTTP(method = "DELETE",path = "api/flow",hasBody = true) 28 | Call clearFlowList(@Body Flow flow); 29 | 30 | @GET("api/event/{channel}") 31 | Call getEventList(@Path("channel") String channel); 32 | 33 | @GET("api/data/{dataId}") 34 | Call getMockData(@Path("dataId") String dataId); 35 | 36 | @Headers("Content-Type: application/json") 37 | @PUT("api/bandwidth") 38 | Call setSpeedLimit(@Body BandwidthTemplate template); 39 | 40 | @GET("api/bandwidth") 41 | Call getSpeedLimit(); 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/meituan/lyrebird/client/MockData.java: -------------------------------------------------------------------------------- 1 | package com.meituan.lyrebird.client; 2 | 3 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 4 | import static java.lang.annotation.ElementType.METHOD; 5 | import static java.lang.annotation.ElementType.TYPE; 6 | 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.Target; 9 | 10 | /** 11 | * Used to mark a class or a method on test case to declare which mock data will be used 12 | */ 13 | @Retention(value = RUNTIME) 14 | @Target(value = {METHOD, TYPE}) 15 | public @interface MockData { 16 | /** 17 | * Group ID of mock data 18 | * 19 | * @return group ID of mock data 20 | */ 21 | String groupID() default ""; 22 | 23 | /** 24 | * Group name of mock data 25 | * 26 | * @return group name of mock data 27 | */ 28 | String groupName() default ""; 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/meituan/lyrebird/client/api/BaseResponse.java: -------------------------------------------------------------------------------- 1 | package com.meituan.lyrebird.client.api; 2 | 3 | public class BaseResponse { 4 | private int code = -1; 5 | private String message = ""; 6 | 7 | public int getCode() { 8 | return code; 9 | } 10 | 11 | public void setCode(int code) { 12 | this.code = code; 13 | } 14 | 15 | public String getMessage() { 16 | return message; 17 | } 18 | 19 | public void setMessage(String message) { 20 | this.message = message; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/meituan/lyrebird/client/api/EventDetail.java: -------------------------------------------------------------------------------- 1 | package com.meituan.lyrebird.client.api; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | 6 | @JsonIgnoreProperties(ignoreUnknown = true) 7 | public class EventDetail { 8 | private String channel; 9 | private String content; 10 | @JsonProperty("event_id") 11 | private String eventID; 12 | private String id; 13 | private long timestamp; 14 | 15 | public String getChannel() { 16 | return channel; 17 | } 18 | 19 | public void setChannel(String channel) { 20 | this.channel = channel; 21 | } 22 | 23 | public String getContent() { 24 | return content; 25 | } 26 | 27 | public void setContent(String content) { 28 | this.content = content; 29 | } 30 | 31 | public String getEventID() { 32 | return eventID; 33 | } 34 | 35 | public void setEventID(String eventID) { 36 | this.eventID = eventID; 37 | } 38 | 39 | public String getId() { 40 | return id; 41 | } 42 | 43 | public void setId(String id) { 44 | this.id = id; 45 | } 46 | 47 | public long getTimestamp() { 48 | return timestamp; 49 | } 50 | 51 | public void setTimestamp(long timestamp) { 52 | this.timestamp = timestamp; 53 | } 54 | 55 | @Override 56 | public String toString() { 57 | return String.format("%n[eventId] %s,[channel] %s, [timestamp] %s%n[content] %s%n", eventID, channel, timestamp, content); 58 | } 59 | } -------------------------------------------------------------------------------- /src/main/java/com/meituan/lyrebird/client/api/Events.java: -------------------------------------------------------------------------------- 1 | package com.meituan.lyrebird.client.api; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | 6 | import java.util.Arrays; 7 | 8 | @JsonIgnoreProperties(ignoreUnknown = true) 9 | public class Events extends BaseResponse { 10 | private EventDetail[] eventDetails; 11 | private int page; 12 | @JsonProperty("page_count") 13 | private int pageCount; 14 | @JsonProperty("page_size") 15 | private int pageSize; 16 | 17 | public EventDetail[] getEvents() { 18 | return eventDetails; 19 | } 20 | 21 | public void setEvents(EventDetail[] eventDetails) { 22 | this.eventDetails = eventDetails; 23 | } 24 | 25 | public int getPage() { 26 | return page; 27 | } 28 | 29 | public void setPage(int page) { 30 | this.page = page; 31 | } 32 | 33 | public int getPageCount() { 34 | return pageCount; 35 | } 36 | 37 | public void setPageCount(int pageCount) { 38 | this.pageCount = pageCount; 39 | } 40 | 41 | public int getPageSize() { 42 | return pageSize; 43 | } 44 | 45 | public void setPageSize(int pageSize) { 46 | this.pageSize = pageSize; 47 | } 48 | 49 | @Override 50 | public String toString() { 51 | return String.format("%n[page] %s,[page count] %s, [page size] %s%n%s%n", page, pageCount, pageSize, Arrays.toString(eventDetails)); 52 | } 53 | } -------------------------------------------------------------------------------- /src/main/java/com/meituan/lyrebird/client/api/Flow.java: -------------------------------------------------------------------------------- 1 | package com.meituan.lyrebird.client.api; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | 6 | @JsonIgnoreProperties(ignoreUnknown = true) 7 | public class Flow { 8 | private String id; 9 | private Request request; 10 | private Response response; 11 | private Double duration; 12 | @JsonProperty("start_time") 13 | private Double startTime; 14 | 15 | public void setId(String id) { 16 | this.id = id; 17 | } 18 | 19 | public String getId() { 20 | return id; 21 | } 22 | 23 | public void setRequest(Request request) { 24 | this.request = request; 25 | } 26 | 27 | public Request getRequest() { 28 | return request; 29 | } 30 | 31 | public void setResponse(Response response) { 32 | this.response = response; 33 | } 34 | 35 | public Response getResponse() { 36 | return response; 37 | } 38 | 39 | public void setDuration(Double duration) { 40 | this.duration = duration; 41 | } 42 | 43 | public Double getDuration() { 44 | return duration; 45 | } 46 | 47 | public void setStartTime(Double startTime) { 48 | this.startTime = startTime; 49 | } 50 | 51 | public Double getStartTime() { 52 | return startTime; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/meituan/lyrebird/client/api/FlowDetail.java: -------------------------------------------------------------------------------- 1 | package com.meituan.lyrebird.client.api; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.meituan.lyrebird.client.exceptions.LyrebirdClientException; 6 | 7 | @JsonIgnoreProperties(ignoreUnknown = true) 8 | public class FlowDetail extends BaseResponse { 9 | @JsonProperty("data") 10 | private Flow flow; 11 | 12 | public Request getRequest() throws LyrebirdClientException { 13 | if (super.getCode() != 1000) { 14 | throw new LyrebirdClientException("/api/flow/{flowId} 接口返回异常:" + super.getMessage()); 15 | } 16 | return flow.getRequest(); 17 | } 18 | 19 | public Response getResponse() throws LyrebirdClientException { 20 | if (super.getCode() != 1000) { 21 | throw new LyrebirdClientException("/api/flow/{flowId} 接口返回异常:" + super.getMessage()); 22 | } 23 | return flow.getResponse(); 24 | } 25 | 26 | public Flow getFlow() { 27 | return flow; 28 | } 29 | 30 | public void setFlow(Flow flow) { 31 | this.flow = flow; 32 | } 33 | } -------------------------------------------------------------------------------- /src/main/java/com/meituan/lyrebird/client/api/LBMockData.java: -------------------------------------------------------------------------------- 1 | package com.meituan.lyrebird.client.api; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.jayway.jsonpath.JsonPath; 6 | import java.util.Map; 7 | 8 | @JsonIgnoreProperties(ignoreUnknown = true) 9 | public class LBMockData extends BaseResponse { 10 | @JsonProperty("data") 11 | private Map data; 12 | 13 | public String getId() { 14 | return data.get("id").toString(); 15 | } 16 | 17 | public String getName() { 18 | return data.get("name").toString(); 19 | } 20 | 21 | public T getResponseData() { 22 | return JsonPath.parse(data.get("response")).read("$.data"); 23 | } 24 | 25 | public Map getData() { 26 | return data; 27 | } 28 | 29 | public void setData(Map data) { 30 | this.data = data; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/meituan/lyrebird/client/api/Request.java: -------------------------------------------------------------------------------- 1 | package com.meituan.lyrebird.client.api; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | @JsonIgnoreProperties(ignoreUnknown = true) 9 | public class Request { 10 | private Map headers; 11 | private String method; 12 | private Map query; 13 | private String url; 14 | private String host; 15 | private String path; 16 | private Map data; 17 | 18 | public Map getHeaders() { 19 | return headers; 20 | } 21 | 22 | public void setHeaders(Map headers) { 23 | this.headers = headers; 24 | } 25 | 26 | public String getMethod() { 27 | return method; 28 | } 29 | 30 | public void setMethod(String method) { 31 | this.method = method; 32 | } 33 | 34 | public Map getQuery() { 35 | return query; 36 | } 37 | 38 | public void setQuery(Map query) { 39 | this.query = query; 40 | } 41 | 42 | public String getUrl() { 43 | return url; 44 | } 45 | 46 | public void setUrl(String url) { 47 | this.url = url; 48 | } 49 | 50 | public String getHost() { 51 | return host; 52 | } 53 | 54 | public void setHost(String host) { 55 | this.host = host; 56 | } 57 | 58 | public String getPath() { 59 | return path; 60 | } 61 | 62 | public void setPath(String path) { 63 | this.path = path; 64 | } 65 | 66 | public Map getData() { 67 | return data; 68 | } 69 | 70 | public void setData(Map data) { 71 | this.data = data; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/meituan/lyrebird/client/api/Response.java: -------------------------------------------------------------------------------- 1 | package com.meituan.lyrebird.client.api; 2 | 3 | import java.util.Map; 4 | 5 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 6 | import com.jayway.jsonpath.JsonPath; 7 | 8 | @JsonIgnoreProperties(ignoreUnknown = true) 9 | public class Response { 10 | private int code; 11 | private Map headers; 12 | private Map data; 13 | 14 | public int getCode() { 15 | return code; 16 | } 17 | 18 | public void setCode(int code) { 19 | this.code = code; 20 | } 21 | 22 | public Map getHeaders() { 23 | return headers; 24 | } 25 | 26 | public void setHeaders(Map headers) { 27 | this.headers = headers; 28 | } 29 | 30 | /** 31 | * 直接获取服务端返回数据对象 32 | * 33 | * @return 服务端返回数据映射的 Java 对象 34 | */ 35 | public Map getData() { 36 | return data; 37 | } 38 | 39 | /** 40 | * 传入 jsonPath 查询服务端返回数据中的对应内容 41 | * 42 | * @param 43 | * @param jsonPath refer: https://github.com/json-path/JsonPath/edit/master/README.md 44 | * @return 根据 jsonPath 查询得到的返回数据 45 | */ 46 | public T getData(String jsonPath) { 47 | return JsonPath.parse(data).read(jsonPath); 48 | } 49 | 50 | /** 51 | * 传入 jsonPath, type 限制查询服务端返回数据的内容类型 52 | * 53 | * @param 54 | * @param jsonPath refer: https://github.com/json-path/JsonPath/edit/master/README.md 55 | * @param type expected return type (will try to map) 56 | * @return 根据 jsonPath 查询得到的返回数据 57 | */ 58 | public T getData(String jsonPath, Class type) { 59 | return JsonPath.parse(data).read(jsonPath, type); 60 | } 61 | 62 | public void setData(Map data) { 63 | this.data = data; 64 | } 65 | } -------------------------------------------------------------------------------- /src/main/java/com/meituan/lyrebird/client/api/Status.java: -------------------------------------------------------------------------------- 1 | package com.meituan.lyrebird.client.api; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | 6 | @JsonIgnoreProperties(ignoreUnknown = true) 7 | public class Status extends BaseResponse{ 8 | @JsonProperty("mock.port") 9 | private int mockPort; 10 | @JsonProperty("proxy.port") 11 | private int proxyPort; 12 | private String ip; 13 | 14 | public int getMockPort(){ 15 | return mockPort; 16 | } 17 | 18 | public int getProxyPort(){ 19 | return proxyPort; 20 | } 21 | 22 | public String getIp(){ 23 | return ip; 24 | } 25 | } -------------------------------------------------------------------------------- /src/main/java/com/meituan/lyrebird/client/api/bandwidth/Bandwidth.java: -------------------------------------------------------------------------------- 1 | package com.meituan.lyrebird.client.api.bandwidth; 2 | 3 | /** 4 | * 网络带宽枚举 5 | */ 6 | public enum Bandwidth { 7 | BANDWIDTH_2G, BANDWIDTH_2_5G, BANDWIDTH_3G, UNLIMITED 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/meituan/lyrebird/client/api/bandwidth/BandwidthTemplate.java: -------------------------------------------------------------------------------- 1 | package com.meituan.lyrebird.client.api.bandwidth; 2 | 3 | public class BandwidthTemplate { 4 | private String templateName; 5 | 6 | public BandwidthTemplate(Bandwidth bandwidth) { 7 | switch (bandwidth) { 8 | case BANDWIDTH_2G: 9 | templateName = "2G"; 10 | break; 11 | case BANDWIDTH_2_5G: 12 | templateName = "2.5G"; 13 | break; 14 | case BANDWIDTH_3G: 15 | templateName = "3G"; 16 | break; 17 | default: 18 | templateName = "UNLIMITED"; 19 | break; 20 | } 21 | } 22 | 23 | public String getTemplateName() { 24 | return templateName; 25 | } 26 | 27 | public void setTemplateName(String templateName) { 28 | this.templateName = templateName; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/meituan/lyrebird/client/api/bandwidth/SpeedLimit.java: -------------------------------------------------------------------------------- 1 | package com.meituan.lyrebird.client.api.bandwidth; 2 | 3 | import com.meituan.lyrebird.client.api.BaseResponse; 4 | 5 | public class SpeedLimit extends BaseResponse { 6 | private int bandwidth; 7 | 8 | public int getBandwidth() { 9 | return bandwidth; 10 | } 11 | 12 | public void setBandwidth(int bandwidth) { 13 | this.bandwidth = bandwidth; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/meituan/lyrebird/client/exceptions/LyrebirdClientException.java: -------------------------------------------------------------------------------- 1 | package com.meituan.lyrebird.client.exceptions; 2 | 3 | public class LyrebirdClientException extends Exception{ 4 | private static final long serialVersionUID = 1L; 5 | 6 | public LyrebirdClientException() { 7 | super(); 8 | } 9 | 10 | public LyrebirdClientException(String message) { 11 | super(message); 12 | } 13 | 14 | public LyrebirdClientException(String message, Throwable cause) { 15 | super(message, cause); 16 | } 17 | 18 | public LyrebirdClientException(Throwable cause) { 19 | super(cause); 20 | } 21 | 22 | protected LyrebirdClientException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { 23 | super(message, cause, enableSuppression, writableStackTrace); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/meituan/lyrebird/client/listeners/Junit4Listener.java: -------------------------------------------------------------------------------- 1 | package com.meituan.lyrebird.client.listeners; 2 | 3 | import com.meituan.lyrebird.Lyrebird; 4 | import com.meituan.lyrebird.client.exceptions.LyrebirdClientException; 5 | import org.junit.runner.Description; 6 | import org.junit.runner.notification.RunListener; 7 | 8 | public class Junit4Listener extends RunListener { 9 | 10 | @Override 11 | public void testStarted(Description description) throws SecurityException { 12 | 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/meituan/lyrebird/client/listeners/Junit4Runner.java: -------------------------------------------------------------------------------- 1 | package com.meituan.lyrebird.client.listeners; 2 | 3 | import org.junit.runner.notification.RunNotifier; 4 | import org.junit.runners.BlockJUnit4ClassRunner; 5 | import org.junit.runners.model.InitializationError; 6 | 7 | public class Junit4Runner extends BlockJUnit4ClassRunner { 8 | 9 | public Junit4Runner(Class clazz) throws InitializationError { 10 | super(clazz); 11 | } 12 | 13 | @Override public void run(RunNotifier notifier){ 14 | notifier.addListener(new Junit4Listener()); 15 | notifier.fireTestRunStarted(getDescription()); 16 | super.run(notifier); 17 | } 18 | } -------------------------------------------------------------------------------- /src/main/java/com/meituan/lyrebird/client/listeners/TestNGListener.java: -------------------------------------------------------------------------------- 1 | package com.meituan.lyrebird.client.listeners; 2 | 3 | import org.testng.ITestContext; 4 | import org.testng.ITestListener; 5 | import org.testng.ITestResult; 6 | 7 | public class TestNGListener implements ITestListener { 8 | 9 | @Override 10 | public void onTestStart(ITestResult result) { 11 | 12 | } 13 | 14 | @Override 15 | public void onTestSuccess(ITestResult result) { 16 | 17 | } 18 | 19 | @Override 20 | public void onTestFailure(ITestResult result) { 21 | 22 | } 23 | 24 | @Override 25 | public void onTestSkipped(ITestResult result) { 26 | 27 | } 28 | 29 | @Override 30 | public void onTestFailedButWithinSuccessPercentage(ITestResult result) { 31 | 32 | } 33 | 34 | @Override 35 | public void onStart(ITestContext context) { 36 | 37 | } 38 | 39 | @Override 40 | public void onFinish(ITestContext context) { 41 | 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/com/meituan/lyrebird/test/TestFunctional.java: -------------------------------------------------------------------------------- 1 | package com.meituan.lyrebird.test; 2 | 3 | import com.google.gson.Gson; 4 | import com.jayway.jsonpath.JsonPath; 5 | import com.meituan.lyrebird.Lyrebird; 6 | import com.meituan.lyrebird.client.api.*; 7 | import com.meituan.lyrebird.client.api.bandwidth.Bandwidth; 8 | import com.meituan.lyrebird.client.exceptions.LyrebirdClientException; 9 | import java.util.List; 10 | import okhttp3.mockwebserver.*; 11 | import org.junit.*; 12 | 13 | import static org.junit.Assert.assertEquals; 14 | 15 | import java.io.IOException; 16 | 17 | public class TestFunctional { 18 | private MockWebServer mockServer; 19 | private Gson gson; 20 | private Lyrebird lyrebird; 21 | 22 | @Before 23 | public void setup() throws IOException { 24 | this.gson = new Gson(); 25 | this.mockServer = new MockWebServer(); 26 | this.mockServer.start(9090); 27 | this.lyrebird = new Lyrebird(); 28 | } 29 | 30 | @After 31 | public void teardown() throws IOException { 32 | if(this.mockServer!=null) { 33 | this.mockServer.close(); 34 | this.mockServer = null; 35 | } 36 | } 37 | 38 | private void makeSuccessResponse(){ 39 | BaseResponse resp = new BaseResponse(); 40 | resp.setCode(1000); 41 | resp.setMessage("success"); 42 | this.mockServer.enqueue(new MockResponse().setBody(gson.toJson(resp))); 43 | } 44 | 45 | @Test 46 | public void testActivate() throws InterruptedException, LyrebirdClientException { 47 | this.makeSuccessResponse(); 48 | 49 | this.lyrebird.activate("TestGroupID"); 50 | 51 | RecordedRequest req = this.mockServer.takeRequest(); 52 | Assert.assertEquals("request path not match", "/api/mock/TestGroupID/activate", req.getPath()); 53 | } 54 | 55 | @Test 56 | public void testDeactivate() throws InterruptedException, LyrebirdClientException { 57 | this.makeSuccessResponse(); 58 | 59 | this.lyrebird.deactivate(); 60 | 61 | RecordedRequest req = this.mockServer.takeRequest(); 62 | Assert.assertEquals("request path not match", "/api/mock/groups/deactivate", req.getPath()); 63 | } 64 | 65 | @Test 66 | public void testStatus() throws InterruptedException, LyrebirdClientException { 67 | this.mockServer.enqueue(new MockResponse() 68 | .setBody( 69 | "{\"code\": 1000,\"ip\": \"192.168.164.79\",\"message\": \"success\",\"mock.port\": 9090,\"proxy.port\": 4272}" 70 | )); 71 | 72 | Status res = this.lyrebird.getLyrebirdStatus(); 73 | 74 | Assert.assertEquals("192.168.164.79", res.getIp()); 75 | Assert.assertEquals(9090, res.getMockPort()); 76 | Assert.assertEquals(4272, res.getProxyPort()); 77 | 78 | RecordedRequest req = this.mockServer.takeRequest(); 79 | Assert.assertEquals("", "/api/status", req.getPath()); 80 | } 81 | 82 | @Test 83 | public void testGetFlowList() throws LyrebirdClientException { 84 | this.mockServer.enqueue(new MockResponse() 85 | .setBody( 86 | "[{\"id\": \"67ea0002-9566-41db-8178-ca0c2f82a71a\",\"size\": 9003,\"duration\": \"0.33454108238220215\",\"start_time\": 1560152882.706479,\"request\": {\"url\": \"http://www.lyrebird.java.client.com/api/example\",\"path\": \"/api/example\",\"host\": \"http://www.lyrebird.java.client.com/\",\"method\": \"POST\"},\"response\": {\"code\": 200,\"mock\": \"proxy\"}}]" 87 | )); 88 | 89 | Flow[] flows = this.lyrebird.getFlowList(); 90 | assertEquals("67ea0002-9566-41db-8178-ca0c2f82a71a", flows[0].getId()); 91 | } 92 | 93 | @Test 94 | public void testGetFlow() throws LyrebirdClientException { 95 | this.mockServer.enqueue(new MockResponse() 96 | .setBody( 97 | "{\"code\":1000,\"data\":{\"duration\": \"0.33454108238220215\",\"id\": \"67ea0002-9566-41db-8178-ca0c2f82a71a\",\"start_time\":\"1566805867.517032\",\"request\":{\"url\":\"http://www.lyrebird.java.client.com/api/example\",\"query\":{\"name\":\"tester\",\"age\":18,\"price\":\"15.58\"},\"data\":{\"orderId\":\"0\"}},\"response\":{\"code\":200,\"headers\":{\"name\":\"tester\"},\"data\":{\"name\":\"tester\",\"age\":18,\"price\":\"15.58\"}}},\"message\":\"success\"}" 98 | )); 99 | 100 | FlowDetail flow = this.lyrebird.getFlowDetail("67ea0002-9566-41db-8178-ca0c2f82a71a"); 101 | 102 | assertEquals("0", flow.getRequest().getData().get("orderId")); 103 | } 104 | 105 | @Test 106 | public void testFlowRequest() throws LyrebirdClientException { 107 | this.mockServer.enqueue(new MockResponse() 108 | .setBody( 109 | "{\"code\":1000,\"data\":{\"duration\": \"0.33454108238220215\",\"id\": \"67ea0002-9566-41db-8178-ca0c2f82a71a\",\"start_time\":\"1566805867.517032\",\"request\":{\"url\":\"http://www.lyrebird.java.client.com/api/example\",\"query\":{\"name\":\"tester\",\"age\":18,\"price\":\"15.58\"}},\"response\":{\"code\":200,\"headers\":{\"name\":\"tester\"},\"data\":{\"name\":\"tester\",\"age\":18,\"price\":\"15.58\"}}},\"message\":\"success\"}" 110 | )); 111 | 112 | FlowDetail flow = this.lyrebird.getFlowDetail("67ea0002-9566-41db-8178-ca0c2f82a71a"); 113 | 114 | assertEquals("tester", flow.getRequest().getQuery().get("name")); 115 | assertEquals(null, flow.getRequest().getData()); 116 | assertEquals("http://www.lyrebird.java.client.com/api/example", flow.getRequest().getUrl()); 117 | } 118 | 119 | @Test 120 | public void testFlowResponse() throws LyrebirdClientException { 121 | this.mockServer.enqueue(new MockResponse() 122 | .setBody( 123 | "{\"code\":1000,\"data\":{\"duration\": \"0.33454108238220215\",\"id\": \"67ea0002-9566-41db-8178-ca0c2f82a71a\",\"start_time\":\"1566805867.517032\",\"request\":{\"query\":{\"name\":\"tester\",\"age\":18,\"price\":\"15.58\"}},\"response\":{\"code\":200,\"headers\":{\"name\":\"tester\"},\"data\":{\"name\":\"tester\",\"age\":18,\"price\":\"15.58\"}}},\"message\":\"success\"}" 124 | )); 125 | 126 | FlowDetail flow = this.lyrebird.getFlowDetail("67ea0002-9566-41db-8178-ca0c2f82a71a"); 127 | 128 | assertEquals(200, flow.getResponse().getCode()); 129 | assertEquals("tester", flow.getResponse().getHeaders().get("name")); 130 | assertEquals(Integer.valueOf(18), flow.getResponse().getData("$.age")); 131 | assertEquals("15.58", flow.getResponse().getData("$.price", String.class)); 132 | } 133 | 134 | @Test 135 | public void testFlowDuration() throws LyrebirdClientException { 136 | this.mockServer.enqueue(new MockResponse() 137 | .setBody( 138 | "{\"code\":1000,\"data\":{\"duration\": \"0.33454108238220215\",\"id\": \"67ea0002-9566-41db-8178-ca0c2f82a71a\",\"start_time\":\"1566805867.517032\",\"request\":{\"query\":{\"name\":\"tester\",\"age\":18,\"price\":\"15.58\"}},\"response\":{\"code\":200,\"headers\":{\"name\":\"tester\"},\"data\":{\"name\":\"tester\",\"age\":18,\"price\":\"15.58\"}}},\"message\":\"success\"}" 139 | )); 140 | 141 | FlowDetail flow = this.lyrebird.getFlowDetail("67ea0002-9566-41db-8178-ca0c2f82a71a"); 142 | assertEquals(0.33, flow.getFlow().getDuration(), 2); 143 | } 144 | 145 | @Test 146 | public void testFlowStartTime() throws LyrebirdClientException { 147 | this.mockServer.enqueue(new MockResponse() 148 | .setBody( 149 | "{\"code\":1000,\"data\":{\"duration\": \"0.33454108238220215\",\"id\": \"67ea0002-9566-41db-8178-ca0c2f82a71a\",\"start_time\":\"1566805867.517032\",\"request\":{\"query\":{\"name\":\"tester\",\"age\":18,\"price\":\"15.58\"}},\"response\":{\"code\":200,\"headers\":{\"name\":\"tester\"},\"data\":{\"name\":\"tester\",\"age\":18,\"price\":\"15.58\"}}},\"message\":\"success\"}" 150 | )); 151 | 152 | FlowDetail flow = this.lyrebird.getFlowDetail("67ea0002-9566-41db-8178-ca0c2f82a71a"); 153 | assertEquals(1566805867.51, flow.getFlow().getStartTime(), 2); 154 | } 155 | 156 | @Test 157 | public void testEventList() throws LyrebirdClientException, InterruptedException { 158 | this.mockServer.enqueue(new MockResponse() 159 | .setBody( 160 | "{\"code\": 1000,\"events\": [{\"channel\": \"page\",\"content\":\"{\\\"page\\\": \\\"com.lyrebird.java.client\\\", \\\"url\\\": null}\",\"event_id\": \"81825f43-5fd6-45f7-b22b-83493ca99e46\",\"id\": 44463,\"timestamp\": 1568277444.738399},{\"channel\": \"page\",\"content\":\"{\\\"page\\\": \\\"com.lyrebird.java.client\\\", \\\"url\\\": \\\"www.lyrebird.com\\\"}\",\"event_id\": \"1b399d1b-b53f-4da9-b2fc-d5576d3d9e58\",\"id\": 44410,\"timestamp\": 1568277440.43741}],\"message\": \"success\",\"page\": 0,\"page_count\": 82,\"page_size\": 20}" 161 | )); 162 | 163 | EventDetail[] eventList = this.lyrebird.getEventList("page"); 164 | RecordedRequest req = this.mockServer.takeRequest(); 165 | 166 | Assert.assertEquals("request path not match", "/api/event/page", req.getPath()); 167 | Assert.assertTrue(eventList.length > 0); 168 | Assert.assertEquals("page", eventList[0].getChannel()); 169 | Assert.assertEquals("com.lyrebird.java.client", JsonPath.parse(eventList[0].getContent()).read("$.page")); 170 | Assert.assertEquals("81825f43-5fd6-45f7-b22b-83493ca99e46", eventList[0].getEventID()); 171 | } 172 | 173 | @Test 174 | public void testLBMockData() throws LyrebirdClientException { 175 | this.mockServer.enqueue(new MockResponse() 176 | .setBody( 177 | "{\"code\": 1000,\"data\": {\"id\": \"cfa0c589-8ef0-4885-b4f4-b9688c5af0d5\", \"name\": \"test-data\", \"response\": {\"data\": \"[{\\\"type\\\": \\\"scheme\\\", \\\"info\\\":{\\\"value\\\": \\\"test://www.lyrebird.java.sdk.com\\\"}, \\\"desc\\\": \\\"The scheme of target page\\\"}]\"}}, \"message\": \"success\"}" 178 | )); 179 | 180 | LBMockData lbMockData = this.lyrebird.getMockData("cfa0c589-8ef0-4885-b4f4-b9688c5af0d5"); 181 | assertEquals("cfa0c589-8ef0-4885-b4f4-b9688c5af0d5", lbMockData.getId()); 182 | assertEquals("test-data", lbMockData.getName()); 183 | 184 | List urlScheme = JsonPath.parse(lbMockData.getResponseData().toString()).read("$[?(@.type == 'scheme')].info.value"); 185 | assertEquals(1, urlScheme.size()); 186 | assertEquals("test://www.lyrebird.java.sdk.com", urlScheme.get(0)); 187 | } 188 | 189 | @Test 190 | public void test2GSpeedLimit() throws LyrebirdClientException, InterruptedException { 191 | this.mockServer.enqueue(new MockResponse() 192 | .setBody("{\"code\": 1000, \"message\": \"success\", \"bandwidth\": 10}" 193 | )); 194 | lyrebird.setSpeedLimit(Bandwidth.BANDWIDTH_2G); 195 | RecordedRequest request = this.mockServer.takeRequest(); 196 | assertEquals("{\"templateName\":\"2G\"}", request.getBody().readUtf8()); 197 | 198 | this.mockServer.enqueue(new MockResponse() 199 | .setBody("{\"code\": 1000, \"message\": \"success\", \"bandwidth\": 10}" 200 | )); 201 | int bandwidth = lyrebird.getSpeedLimit().getBandwidth(); 202 | assertEquals(10, bandwidth); 203 | } 204 | 205 | @Test 206 | public void test25GSpeedLimit() throws LyrebirdClientException, InterruptedException { 207 | this.mockServer.enqueue(new MockResponse() 208 | .setBody("{\"code\": 1000, \"message\": \"success\", \"bandwidth\": 35}" 209 | )); 210 | lyrebird.setSpeedLimit(Bandwidth.BANDWIDTH_2_5G); 211 | RecordedRequest request = this.mockServer.takeRequest(); 212 | assertEquals("{\"templateName\":\"2.5G\"}", request.getBody().readUtf8()); 213 | 214 | this.mockServer.enqueue(new MockResponse() 215 | .setBody("{\"code\": 1000, \"message\": \"success\", \"bandwidth\": 35}" 216 | )); 217 | int bandwidth = lyrebird.getSpeedLimit().getBandwidth(); 218 | assertEquals(35, bandwidth); 219 | } 220 | 221 | @Test 222 | public void test3GSpeedLimit() throws LyrebirdClientException, InterruptedException { 223 | this.mockServer.enqueue(new MockResponse() 224 | .setBody("{\"code\": 1000, \"message\": \"success\", \"bandwidth\": 120}" 225 | )); 226 | lyrebird.setSpeedLimit(Bandwidth.BANDWIDTH_3G); 227 | RecordedRequest request = this.mockServer.takeRequest(); 228 | assertEquals("{\"templateName\":\"3G\"}", request.getBody().readUtf8()); 229 | 230 | this.mockServer.enqueue(new MockResponse() 231 | .setBody("{\"code\": 1000, \"message\": \"success\", \"bandwidth\": 120}" 232 | )); 233 | int bandwidth = lyrebird.getSpeedLimit().getBandwidth(); 234 | assertEquals(120, bandwidth); 235 | } 236 | 237 | @Test 238 | public void testUnlimitedSpeedLimit() throws LyrebirdClientException, InterruptedException { 239 | this.mockServer.enqueue(new MockResponse() 240 | .setBody("{\"code\": 1000, \"message\": \"success\", \"bandwidth\": -1}" 241 | )); 242 | lyrebird.setSpeedLimit(Bandwidth.UNLIMITED); 243 | RecordedRequest request = this.mockServer.takeRequest(); 244 | assertEquals("{\"templateName\":\"UNLIMITED\"}", request.getBody().readUtf8()); 245 | 246 | this.mockServer.enqueue(new MockResponse() 247 | .setBody("{\"code\": 1000, \"message\": \"success\", \"bandwidth\": -1}" 248 | )); 249 | int bandwidth = lyrebird.getSpeedLimit().getBandwidth(); 250 | assertEquals(-1, bandwidth); 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /src/test/java/com/meituan/lyrebird/test/TestLyrebirdClient.java: -------------------------------------------------------------------------------- 1 | package com.meituan.lyrebird.test; 2 | 3 | import com.meituan.lyrebird.Lyrebird; 4 | import com.meituan.lyrebird.client.exceptions.LyrebirdClientException; 5 | import okhttp3.mockwebserver.*; 6 | import org.junit.*; 7 | 8 | import java.io.IOException; 9 | 10 | public class TestLyrebirdClient { 11 | private MockWebServer mockServer; 12 | private Lyrebird lyrebird; 13 | 14 | @Before 15 | public void setup() { 16 | this.mockServer = new MockWebServer(); 17 | } 18 | 19 | @After 20 | public void teardown() throws IOException { 21 | if (this.mockServer != null) { 22 | this.mockServer.close(); 23 | } 24 | } 25 | 26 | @Test 27 | public void testLyrebirdClientWithDefaultPort() throws IOException, LyrebirdClientException { 28 | this.mockServer.enqueue(new MockResponse() 29 | .setBody( 30 | "{\"code\": 1000,\"ip\": \"localhost\",\"message\": \"success\",\"mock.port\": 9090,\"proxy.port\": 4272}" 31 | )); 32 | 33 | this.mockServer.start(9090); 34 | this.lyrebird = new Lyrebird(); 35 | 36 | Assert.assertEquals("success", lyrebird.getLyrebirdStatus().getMessage()); 37 | } 38 | 39 | @Test 40 | public void testLyrebirdClientWithManualPort() throws IOException, LyrebirdClientException { 41 | this.mockServer.enqueue(new MockResponse() 42 | .setBody( 43 | "{\"code\": 1000,\"ip\": \"localhost\",\"message\": \"success\",\"mock.port\": 8080,\"proxy.port\": 4272}" 44 | )); 45 | 46 | this.mockServer.start(); 47 | this.lyrebird = new Lyrebird("http://localhost:" + this.mockServer.getPort()); 48 | 49 | Assert.assertEquals("success", lyrebird.getLyrebirdStatus().getMessage()); 50 | } 51 | } 52 | --------------------------------------------------------------------------------