]> git.basschouten.com Git - openhab-addons.git/blob
4c73389df2a7da74d5a980e5635b1644b789f0d0
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.http;
14
15 import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
16 import static com.github.tomakehurst.wiremock.client.WireMock.get;
17 import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
18 import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
19 import static org.hamcrest.CoreMatchers.allOf;
20 import static org.hamcrest.CoreMatchers.anyOf;
21 import static org.hamcrest.MatcherAssert.assertThat;
22 import static org.hamcrest.Matchers.equalTo;
23 import static org.hamcrest.Matchers.greaterThanOrEqualTo;
24 import static org.hamcrest.Matchers.lessThan;
25 import static org.junit.jupiter.api.Assertions.assertEquals;
26 import static org.junit.jupiter.api.Assertions.assertNotEquals;
27
28 import java.net.URI;
29 import java.util.List;
30 import java.util.concurrent.CompletableFuture;
31 import java.util.concurrent.CopyOnWriteArrayList;
32
33 import org.eclipse.jdt.annotation.NonNullByDefault;
34 import org.eclipse.jetty.client.api.ContentResponse;
35 import org.eclipse.jetty.client.api.Request;
36 import org.eclipse.jetty.http.HttpMethod;
37 import org.junit.jupiter.api.AfterEach;
38 import org.junit.jupiter.api.Test;
39 import org.junit.jupiter.api.TestInstance;
40 import org.openhab.binding.http.internal.http.RateLimitedHttpClient;
41
42 /**
43  * The {@link RateLimitedHttpClientTest} implements tests for the {@link RateLimitedHttpClient}
44  *
45  * @author Jan N. Klug - Initial contribution
46  */
47 @NonNullByDefault
48 @TestInstance(TestInstance.Lifecycle.PER_CLASS)
49 public class RateLimitedHttpClientTest extends AbstractWireMockTest {
50     private static final String TEST_LOCATION = "/testlocation";
51     private static final String TEST_CONTENT = "TESTCONTENT";
52
53     private List<Response> responses = new CopyOnWriteArrayList<>();
54
55     @AfterEach
56     public void cleanUpTest() {
57         responses.clear();
58         super.cleanUpTest();
59     }
60
61     @Test
62     public void testWithoutLimit() {
63         doLimitTest(0, List.of(false, false));
64
65         // we except to receive the responses in the correct order
66         assertEquals(0, responses.get(0).seqNumber);
67         assertEquals(1, responses.get(1).seqNumber);
68
69         // we expect a short delay between both requests, but less than 100ms
70         long msBetween = responses.get(1).time - responses.get(0).time;
71         assertThat((int) msBetween, allOf(greaterThanOrEqualTo(0), lessThan(100)));
72     }
73
74     @Test
75     public void testWithLimit() {
76         doLimitTest(500, List.of(false, false));
77         // we except to receive the responses in the correct order
78         assertEquals(0, responses.get(0).seqNumber);
79         assertEquals(1, responses.get(1).seqNumber);
80
81         // we expect at least 500ms delay between both requests, but less than 500+100=600ms
82         long msBetween = responses.get(1).time - responses.get(0).time;
83         assertThat((int) msBetween, allOf(greaterThanOrEqualTo(500), lessThan(600)));
84     }
85
86     @Test
87     public void testWithLimitAndPriority() {
88         doLimitTest(500, List.of(false, false, true));
89
90         // we expect to receive the responses of request 3 before request two, exact order of 1 and 3 depends on timing,
91         // so accept both
92         assertThat(responses.get(0).seqNumber, anyOf(equalTo(0), equalTo(2)));
93         assertThat(responses.get(1).seqNumber, anyOf(equalTo(0), equalTo(2)));
94         assertNotEquals(responses.get(1).seqNumber, responses.get(0).seqNumber);
95         assertEquals(1, responses.get(2).seqNumber);
96
97         // we expect at least 2*500=1000ms delay between the first and last request, but less than 2*500+100=1100 ms
98         long msBetween = responses.get(2).time - responses.get(0).time;
99         assertThat((int) msBetween, allOf(greaterThanOrEqualTo(1000), lessThan(1100)));
100     }
101
102     private List<Response> doLimitTest(int setDelay, List<Boolean> config) {
103         stubFor(get(urlEqualTo(TEST_LOCATION)).willReturn(aResponse().withBody(TEST_CONTENT)));
104
105         RateLimitedHttpClient rateLimitedHttpClient = new RateLimitedHttpClient(httpClient, scheduler);
106         rateLimitedHttpClient.setDelay(setDelay);
107
108         URI url = URI.create("http://localhost:" + port + TEST_LOCATION);
109         int seqNumber = 0;
110
111         for (boolean priority : config) {
112             int nextSeqNumber = seqNumber++;
113             CompletableFuture<Request> requestFuture;
114
115             if (priority) {
116                 requestFuture = rateLimitedHttpClient.newPriorityRequest(url, HttpMethod.GET, "", null);
117             } else {
118                 requestFuture = rateLimitedHttpClient.newRequest(url, HttpMethod.GET, "", null);
119             }
120
121             requestFuture.thenAccept(request -> {
122                 try {
123                     responses.add(new Response(nextSeqNumber, request.send()));
124                 } catch (Exception e) {
125                 }
126             });
127         }
128
129         // wait until we got all results
130         waitForAssert(() -> assertEquals(config.size(), responses.size()));
131         rateLimitedHttpClient.shutdown();
132
133         return responses;
134     }
135
136     private static class Response {
137         public final int seqNumber;
138         public final long time = System.currentTimeMillis();
139         public final String content;
140
141         public Response(int seqNumber, ContentResponse contentResponse) {
142             this.seqNumber = seqNumber;
143             this.content = contentResponse.getContentAsString();
144         }
145     }
146 }