001    /*
002     * SonarQube, open source software quality management tool.
003     * Copyright (C) 2008-2014 SonarSource
004     * mailto:contact AT sonarsource DOT com
005     *
006     * SonarQube is free software; you can redistribute it and/or
007     * modify it under the terms of the GNU Lesser General Public
008     * License as published by the Free Software Foundation; either
009     * version 3 of the License, or (at your option) any later version.
010     *
011     * SonarQube is distributed in the hope that it will be useful,
012     * but WITHOUT ANY WARRANTY; without even the implied warranty of
013     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014     * Lesser General Public License for more details.
015     *
016     * You should have received a copy of the GNU Lesser General Public License
017     * along with this program; if not, write to the Free Software Foundation,
018     * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
019     */
020    package org.sonar.api.server.ws;
021    
022    import org.apache.commons.io.Charsets;
023    import org.apache.commons.io.IOUtils;
024    import org.skyscreamer.jsonassert.JSONAssert;
025    import org.sonar.api.utils.text.JsonWriter;
026    import org.sonar.api.utils.text.XmlWriter;
027    
028    import javax.annotation.CheckForNull;
029    
030    import java.io.ByteArrayOutputStream;
031    import java.io.OutputStream;
032    import java.io.OutputStreamWriter;
033    import java.net.URL;
034    import java.util.HashMap;
035    import java.util.Map;
036    
037    /**
038     * @since 4.2
039     */
040    public class WsTester {
041    
042      public static class TestRequest extends Request {
043    
044        private final WebService.Controller controller;
045        private final WebService.Action action;
046        private String method = "GET";
047        private Map<String, String> params = new HashMap<String, String>();
048    
049        private TestRequest(WebService.Controller controller, WebService.Action action) {
050          this.controller = controller;
051          this.action = action;
052        }
053    
054        @Override
055        public WebService.Action action() {
056          return action;
057        }
058    
059        @Override
060        public String method() {
061          return method;
062        }
063    
064        public TestRequest setMethod(String s) {
065          this.method = s;
066          return this;
067        }
068    
069        public TestRequest setParams(Map<String, String> m) {
070          this.params = m;
071          return this;
072        }
073    
074        public TestRequest setParam(String key, @CheckForNull String value) {
075          if (value != null) {
076            params.put(key, value);
077          }
078          return this;
079        }
080    
081        @Override
082        @CheckForNull
083        public String param(String key) {
084          return params.get(key);
085        }
086    
087        public Result execute() {
088          TestResponse response = new TestResponse();
089          action.handler().handle(this, response);
090          return new Result(response);
091        }
092      }
093    
094      public static class TestResponse implements Response {
095    
096        public class TestStream implements Response.Stream {
097          private String mediaType;
098          private int status;
099    
100          @CheckForNull
101          public String mediaType() {
102            return mediaType;
103          }
104    
105          public int status() {
106            return status;
107          }
108    
109          @Override
110          public Response.Stream setMediaType(String s) {
111            this.mediaType = s;
112            return this;
113          }
114    
115          @Override
116          public Response.Stream setStatus(int i) {
117            this.status = i;
118            return this;
119          }
120    
121          @Override
122          public OutputStream output() {
123            return output;
124          }
125        }
126    
127        private final ByteArrayOutputStream output = new ByteArrayOutputStream();
128    
129        @Override
130        public JsonWriter newJsonWriter() {
131          return JsonWriter.of(new OutputStreamWriter(output, Charsets.UTF_8));
132        }
133    
134        @Override
135        public XmlWriter newXmlWriter() {
136          return XmlWriter.of(new OutputStreamWriter(output, Charsets.UTF_8));
137        }
138    
139        @Override
140        public Stream stream() {
141          return new TestStream();
142        }
143    
144    
145        @Override
146        public Response noContent() {
147          IOUtils.closeQuietly(output);
148          return this;
149        }
150      }
151    
152    
153      public static class Result {
154        private final TestResponse response;
155    
156        private Result(TestResponse response) {
157          this.response = response;
158        }
159    
160        public Result assertNoContent() {
161          //FIXME
162          return this;
163        }
164    
165        public String outputAsString() {
166          return new String(response.output.toByteArray(), Charsets.UTF_8);
167        }
168    
169        public Result assertJson(String expectedJson) throws Exception {
170          String json = outputAsString();
171          JSONAssert.assertEquals(expectedJson, json, true);
172          return this;
173        }
174    
175        /**
176         * Compares JSON response with JSON file available in classpath. For example if class
177         * is org.foo.BarTest and filename is index.json, then file must be located
178         * at src/test/resources/org/foo/BarTest/index.json.
179         *
180         * @param clazz                the test class
181         * @param jsonResourceFilename name of the file containing the expected JSON
182         */
183        public Result assertJson(Class clazz, String expectedJsonFilename) throws Exception {
184          String path = clazz.getSimpleName() + "/" + expectedJsonFilename;
185          URL url = clazz.getResource(path);
186          if (url == null) {
187            throw new IllegalStateException("Cannot find " + path);
188          }
189          String json = outputAsString();
190          JSONAssert.assertEquals(IOUtils.toString(url), json, true);
191          return this;
192        }
193      }
194    
195      private final WebService.Context context = new WebService.Context();
196    
197      public WsTester(WebService... webServices) {
198        for (WebService webService : webServices) {
199          webService.define(context);
200        }
201      }
202    
203      public WebService.Context context() {
204        return context;
205      }
206    
207      @CheckForNull
208      public WebService.Controller controller(String path) {
209        return context.controller(path);
210      }
211    
212      public TestRequest newRequest(String actionKey) {
213        if (context.controllers().size() != 1) {
214          throw new IllegalStateException("The method newRequest(String) requires to define one, and only one, controller");
215        }
216        WebService.Controller controller = context.controllers().get(0);
217        WebService.Action action = controller.action(actionKey);
218        if (action == null) {
219          throw new IllegalArgumentException("Action not found: " + actionKey);
220        }
221        return new TestRequest(controller, action);
222      }
223    
224      public TestRequest newRequest(String controllerPath, String actionKey) {
225        WebService.Controller controller = context.controller(controllerPath);
226        WebService.Action action = controller.action(actionKey);
227        return new TestRequest(controller, action);
228      }
229    }