├── .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 | [](https://maven-badges.herokuapp.com/maven-central/io.appium/java-client)
4 | [](https://javadoc.io/doc/com.github.meituan-dianping.lyrebird.sdk/lyrebird-java-client)
5 | [](https://lgtm.com/projects/g/Meituan-Dianping/lyrebird-java-client/alerts/)
6 | [](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 |
--------------------------------------------------------------------------------