xref: /OpenGrok/opengrok-web/src/test/java/org/opengrok/web/api/v1/controller/ConfigurationControllerTest.java (revision 1c258122d17e56599843d69a69f1445a01e0cd68)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * See LICENSE.txt included in this distribution for the specific
9  * language governing permissions and limitations under the License.
10  *
11  * When distributing Covered Code, include this CDDL HEADER in each
12  * file and include the License file at LICENSE.txt.
13  * If applicable, add the following below this CDDL HEADER, with the
14  * fields enclosed by brackets "[]" replaced with your own identifying
15  * information: Portions Copyright [yyyy] [name of copyright owner]
16  *
17  * CDDL HEADER END
18  */
19 
20 /*
21  * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
22  * Portions Copyright (c) 2020, Chris Fraire <cfraire@me.com>.
23  */
24 package org.opengrok.web.api.v1.controller;
25 
26 import static org.junit.jupiter.api.Assertions.assertEquals;
27 import static org.junit.jupiter.api.Assertions.assertFalse;
28 import static org.junit.jupiter.api.Assertions.assertTrue;
29 import static org.mockito.Mockito.reset;
30 import static org.mockito.Mockito.verify;
31 import static org.opengrok.web.api.v1.controller.ApiUtils.waitForTask;
32 
33 import java.util.concurrent.CountDownLatch;
34 
35 import jakarta.servlet.http.HttpServletRequest;
36 import jakarta.ws.rs.client.Entity;
37 import jakarta.ws.rs.core.Response;
38 import org.glassfish.jersey.internal.inject.AbstractBinder;
39 import org.glassfish.jersey.server.ResourceConfig;
40 import org.glassfish.jersey.servlet.ServletContainer;
41 import org.glassfish.jersey.test.DeploymentContext;
42 import org.glassfish.jersey.test.ServletDeploymentContext;
43 import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
44 import org.glassfish.jersey.test.spi.TestContainerException;
45 import org.glassfish.jersey.test.spi.TestContainerFactory;
46 import org.junit.jupiter.api.BeforeAll;
47 import org.junit.jupiter.api.Test;
48 import org.junit.jupiter.api.extension.ExtendWith;
49 import org.mockito.Mock;
50 import org.mockito.junit.jupiter.MockitoExtension;
51 import org.opengrok.indexer.configuration.Configuration;
52 import org.opengrok.indexer.configuration.RuntimeEnvironment;
53 import org.opengrok.indexer.web.DummyHttpServletRequest;
54 import org.opengrok.web.PageConfig;
55 import org.opengrok.web.api.ApiTaskManager;
56 import org.opengrok.web.api.v1.suggester.provider.service.SuggesterService;
57 
58 @ExtendWith(MockitoExtension.class)
59 class ConfigurationControllerTest extends OGKJerseyTest {
60 
61     private final RuntimeEnvironment env = RuntimeEnvironment.getInstance();
62 
63     @Mock
64     private SuggesterService suggesterService;
65 
66     @BeforeAll
setup()67     static void setup() {
68         ApiTaskManager.getInstance().addPool("configuration", 1);
69     }
70 
71     @Override
getTestContainerFactory()72     protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
73         return new GrizzlyWebTestContainerFactory();
74     }
75 
76     @Override
configureDeployment()77     protected DeploymentContext configureDeployment() {
78         return ServletDeploymentContext.
79                 forServlet(new ServletContainer(new ResourceConfig(ConfigurationController.class)
80                         .register(new AbstractBinder() {
81                             @Override
82                             protected void configure() {
83                                 bind(suggesterService).to(SuggesterService.class);
84                             }
85                         }))).build();
86     }
87 
88     @Test
89     void testApplySetAndGetBasicConfig() {
90         Configuration config = new Configuration();
91         String srcRoot = "/foo";
92         config.setSourceRoot(srcRoot);
93 
94         String configStr = config.getXMLRepresentationAsString();
95 
96         Response response = target("configuration")
97                 .request()
98                 .put(Entity.xml(configStr));
99         waitForTask(response);
100 
101         assertEquals(srcRoot, env.getSourceRootPath());
102 
103         String returnedConfig = target("configuration")
104                 .request()
105                 .get(String.class);
106 
107         assertEquals(configStr, returnedConfig);
108     }
109 
110     @Test
111     void testApplySetInvalidMethod() {
112         Response r = setValue("noMethodExists", "1000");
113 
114         assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), r.getStatus());
115     }
116 
117     private Response setValue(final String field, final String value) {
118         Response response = target("configuration")
119                 .path(field)
120                 .request()
121                 .put(Entity.text(value));
122 
123         return waitForTask(response);
124     }
125 
126     @Test
127     void testApplyGetInvalidMethod() {
128         Response r = target("configuration")
129                 .path("FooBar")
130                 .request()
131                 .get();
132 
133         assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), r.getStatus());
134     }
135 
136     @Test
137     void testApplySetInvalidMethodParameter() {
138         Response r = setValue("setDefaultProjects", "1000"); // expecting Set
139 
140         assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), r.getStatus());
141     }
142 
143     @Test
144     void testApplySetOptionInteger() {
145         assertEquals(25, env.getHitsPerPage());
146 
147         setValue("hitsPerPage", "1000");
148 
149         assertEquals(1000, env.getHitsPerPage());
150 
151         env.setHitsPerPage(25);
152     }
153 
154     @Test
155     void testApplySetOptionInvalidInteger() {
156         Response r = setValue("hitsPerPage", "abcd");
157 
158         assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), r.getStatus());
159     }
160 
161     @Test
162     void testApplySetOptionBooleanTrue() {
163         testSetChattyStatusPageTrue("true");
164         testSetChattyStatusPageTrue("on");
165         testSetChattyStatusPageTrue("1");
166     }
167 
168     private void testSetChattyStatusPageTrue(final String value) {
169         env.setChattyStatusPage(false);
170 
171         setValue("chattyStatusPage", value);
172 
173         assertTrue(env.isChattyStatusPage());
174     }
175 
176     @Test
177     void testApplySetOptionBooleanFalse() {
178         testSetChattyStatusPageFalse("false");
179         testSetChattyStatusPageFalse("off");
180         testSetChattyStatusPageFalse("0");
181     }
182 
183     private void testSetChattyStatusPageFalse(final String value) {
184         env.setChattyStatusPage(true);
185 
186         setValue("chattyStatusPage", value);
187 
188         assertFalse(env.isChattyStatusPage());
189     }
190 
191     @Test
192     void testApplySetOptionInvalidBoolean1() {
193         Response r = setValue("chattyStatusPage", "1000"); // only 1 is accepted as true
194 
195         assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), r.getStatus());
196     }
197 
198     @Test
199     void testApplySetOptionInvalidBoolean2() {
200         Response r = setValue("chattyStatusPage", "anything");
201 
202         assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), r.getStatus());
203     }
204 
205     @Test
206     void testApplySetOptionString() {
207         String old = env.getUserPage();
208 
209         setValue("userPage", "http://users.portal.com?user=");
210 
211         assertEquals("http://users.portal.com?user=", env.getUserPage());
212 
213         setValue("userPage", "some complicated \"string\" with &#~Đ`[đ\\ characters");
214 
215         assertEquals("some complicated \"string\" with &#~Đ`[đ\\ characters", env.getUserPage());
216 
217         env.setUserPage(old);
218     }
219 
220     @Test
221     void testApplyGetOptionString() {
222         env.setSourceRoot("/foo/bar");
223         String response = target("configuration")
224                 .path("sourceRoot")
225                 .request()
226                 .get(String.class);
227 
228         assertEquals(response, env.getSourceRootPath());
229     }
230 
231     @Test
232     void testApplyGetOptionInteger() {
233         int hitsPerPage = target("configuration")
234                 .path("hitsPerPage")
235                 .request()
236                 .get(int.class);
237 
238         assertEquals(env.getHitsPerPage(), hitsPerPage);
239     }
240 
241     @Test
242     void testApplyGetOptionBoolean() {
243         boolean response = target("configuration")
244                 .path("historyCache")
245                 .request()
246                 .get(boolean.class);
247 
248         assertEquals(env.isHistoryCache(), response);
249     }
250 
251     @Test
252     void testSuggesterServiceNotifiedOnConfigurationFieldChange() {
253         reset(suggesterService);
254         setValue("sourceRoot", "test");
255         verify(suggesterService).refresh();
256     }
257 
258     @Test
259     void testSuggesterServiceNotifiedOnConfigurationChange() throws InterruptedException {
260         reset(suggesterService);
261         Response response = target("configuration")
262                 .request()
263                 .put(Entity.xml(new Configuration().getXMLRepresentationAsString()));
264         waitForTask(response);
265         verify(suggesterService).refresh();
266     }
267 
268     @Test
269     void testConfigValueSetVsThread() throws InterruptedException {
270         int origValue = env.getHitsPerPage();
271         final int[] threadValue = new int[1];
272         final CountDownLatch startLatch = new CountDownLatch(1);
273         final CountDownLatch endLatch = new CountDownLatch(1);
274 
275         Thread thread = new Thread(() -> {
276             HttpServletRequest req = new DummyHttpServletRequest();
277             PageConfig pageConfig = PageConfig.get(req);
278             RuntimeEnvironment e = pageConfig.getEnv();
279             startLatch.countDown();
280             // Wait for hint of termination, save the value and exit.
281             try {
282                 endLatch.await();
283             } catch (InterruptedException ex) {
284                 ex.printStackTrace();
285             }
286             threadValue[0] = e.getHitsPerPage();
287         });
288 
289         thread.start();
290         startLatch.await();
291 
292         // Set brand-new configuration.
293         int newValue = origValue + 42;
294         Configuration config = new Configuration();
295         config.setHitsPerPage(newValue);
296         String configStr = config.getXMLRepresentationAsString();
297         Response response = target("configuration")
298                 .request()
299                 .put(Entity.xml(configStr));
300         waitForTask(response);
301 
302         // Unblock the thread.
303         endLatch.countDown();
304         thread.join();
305 
306         // Check thread's view of the variable.
307         assertEquals(newValue, threadValue[0]);
308 
309         // Revert the value back to the default.
310         env.setHitsPerPage(origValue);
311     }
312 }
313