001/*
002 * SonarQube, open source software quality management tool.
003 * Copyright (C) 2008-2013 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 */
020package org.sonar.api.server.ws;
021
022import org.apache.commons.io.Charsets;
023import org.apache.commons.io.IOUtils;
024import org.skyscreamer.jsonassert.JSONAssert;
025import org.sonar.api.utils.text.JsonWriter;
026import org.sonar.api.utils.text.XmlWriter;
027
028import javax.annotation.CheckForNull;
029
030import java.io.ByteArrayOutputStream;
031import java.io.OutputStream;
032import java.io.OutputStreamWriter;
033import java.net.URL;
034import java.util.HashMap;
035import java.util.Map;
036
037/**
038 * @since 4.2
039 */
040public 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}