/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.google.wave.api;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.wave.api.JsonRpcConstant.ParamsProperty;
import com.google.wave.api.JsonRpcConstant.RequestProperty;
import com.google.wave.api.OperationRequest.Parameter;
import com.google.wave.api.WaveService.HttpFetcher;
import com.google.wave.api.WaveService.HttpResponse;
import com.google.wave.api.event.BlipContributorsChangedEvent;
import com.google.wave.api.event.BlipSubmittedEvent;
import com.google.wave.api.event.DocumentChangedEvent;
import com.google.wave.api.event.EventType;
import com.google.wave.api.event.WaveletTagsChangedEvent;
import com.google.wave.api.impl.EventMessageBundle;
import com.google.wave.api.impl.GsonFactory;
import com.google.wave.api.impl.WaveletData;
import junit.framework.TestCase;
import net.oauth.http.HttpMessage;
import org.mockito.Matchers;
import org.waveprotocol.wave.model.id.WaveId;
import org.waveprotocol.wave.model.id.WaveletId;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Test cases for {@link AbstractRobot}.
*/
public class AbstractRobotTest extends TestCase {
private static final WaveId WAVE_1 = WaveId.of("example.com", "wave1");
private static final WaveletId WAVELET_1 = WaveletId.of("example.com", "wavelet1");
private static final String PROFILE_PATH = "/basepath/_wave/robot/profile";
private static final String CAPABILITIES_XML_PATH = "/basepath/_wave/capabilities.xml";
private static final String JSONRPC_PATH = "/basepath/_wave/robot/jsonrpc";
private static final String VERIFY_TOKEN_PATH = "/basepath/_wave/verify_token";
private class MockRobot extends AbstractRobot {
public MockRobot() {
super();
}
// This method provided to enable mock on HttpFetcher
public List<JsonRpcResponse> submit(Wavelet wavelet, String rpcServerUrl, WaveService service)
throws IOException {
return service.submit(wavelet, rpcServerUrl);
}
@Override
protected String getRobotName() {
return "Foo";
}
@Override
protected String getRobotProfilePageUrl() {
return "http://foo.com";
}
@Override
protected String getRobotAvatarUrl() {
return "http://foo.com/foo.png";
}
@Capability(contexts = {Context.PARENT, Context.SELF, Context.CHILDREN}, filter=".*")
@Override
public void onBlipSubmitted(BlipSubmittedEvent e) {
calledEvents.add(e.getType());
}
@Override
public void onDocumentChanged(DocumentChangedEvent e) {
calledEvents.add(e.getType());
}
}
private static class MockWriter extends PrintWriter {
private String string;
public MockWriter() {
super(new StringWriter());
}
@Override
public void write(String string) {
this.string = string;
}
public String getString() {
return string;
}
}
private static <K, V> Map<K, V> anyMapOf(Class<K> keyClass, Class<V> valueClass) {
return Matchers.<Map<K, V>>any();
}
private final List<EventType> calledEvents = new ArrayList<EventType>();
public void testSubmit() throws Exception {
HttpFetcher fetcher = mock(HttpFetcher.class);
when(fetcher.execute(any(HttpMessage.class), anyMapOf(String.class, Object.class)))
.thenReturn(new HttpResponse("POST", new URL("http://foo.google.com"), 0,
new ByteArrayInputStream("[{\"id\":\"op1\",\"data\":{}}]".getBytes())));
MockRobot robot = new MockRobot();
robot.setupOAuth("consumerKey", "consumerSecret", "http://gmodules.com/api/rpc");
WaveService service = new WaveService(fetcher, robot.computeHash());
service.setupOAuth("consumerKey", "consumerSecret", "http://gmodules.com/api/rpc");
OperationQueue opQueue = new OperationQueue();
opQueue.appendOperation(OperationType.ROBOT_NOTIFY,
Parameter.of(ParamsProperty.CAPABILITIES_HASH, "123"));
Wavelet wavelet = mock(Wavelet.class);
when(wavelet.getOperationQueue()).thenReturn(opQueue);
assertEquals(1, opQueue.getPendingOperations().size());
robot.submit(wavelet, "http://gmodules.com/api/rpc", service);
assertEquals(0, opQueue.getPendingOperations().size());
verify(fetcher, times(1)).execute(any(HttpMessage.class), anyMapOf(String.class, Object.class));
}
public void testServiceCapabilitiesRequest() throws Exception {
AbstractRobot robot = new MockRobot() {
@Override
public void onBlipContributorsChanged(BlipContributorsChangedEvent e) {
calledEvents.add(e.getType());
}
};
MockWriter writer = new MockWriter();
robot.doGet(makeMockRequest(CAPABILITIES_XML_PATH), makeMockResponse(writer));
String capabilitiesXml = writer.getString();
String expectedCapabilityTag =
"<w:capability name=\"BLIP_SUBMITTED\" context=\"PARENT,SELF,CHILDREN\" filter=\".*\"/>\n";
assertTrue(capabilitiesXml.contains(expectedCapabilityTag));
expectedCapabilityTag =
"<w:capability name=\"DOCUMENT_CHANGED\"/>\n";
assertTrue(capabilitiesXml.contains(expectedCapabilityTag));
expectedCapabilityTag =
"<w:capability name=\"BLIP_CONTRIBUTORS_CHANGED\"/>\n";
assertTrue(capabilitiesXml.contains(expectedCapabilityTag));
expectedCapabilityTag =
"<w:capability name=\"WAVELET_SELF_ADDED\"/>\n";
assertFalse(capabilitiesXml.contains(expectedCapabilityTag));
expectedCapabilityTag =
"<w:capability name=\"WAVELET_SELF_ADDED\" context=\"ROOT,PARENT,CHILDREN\"/>\n";
assertFalse(capabilitiesXml.contains(expectedCapabilityTag));
}
public void testServiceProfileRequest() throws Exception {
AbstractRobot robot = new MockRobot();
MockWriter writer = new MockWriter();
robot.doGet(makeMockRequest(PROFILE_PATH), makeMockResponse(writer));
String profileJson = writer.getString();
String expectedProfileJson =
"{\"address\":\"\",\"name\":\"Foo\",\"imageUrl\":\"http://foo.com/foo.png\"," +
"\"profileUrl\":\"http://foo.com\"}";
assertEquals(expectedProfileJson, profileJson);
}
public void testServiceVerificationTokenRequest() throws Exception {
AbstractRobot robot = new MockRobot();
robot.setupVerificationToken("vertoken", "sectoken");
MockWriter writer = new MockWriter();
robot.doGet(makeMockRequest(VERIFY_TOKEN_PATH, "st", "sectoken"),
makeMockResponse(writer));
assertEquals("vertoken", writer.getString());
HttpServletResponse response = makeMockResponse(new MockWriter());
robot.doGet(makeMockRequest(VERIFY_TOKEN_PATH), response);
verify(response).setStatus(HttpURLConnection.HTTP_UNAUTHORIZED);
}
public void refactor_testServiceEventMessageBundleRequest() throws Exception {
final List<EventType> calledEvents = new ArrayList<EventType>();
AbstractRobot robot = new AbstractRobot() {
@Override
protected String getRobotName() {
return "Foo";
}
@Override
public String getRobotProfilePageUrl() {
return "http://code.google.com/apis/wave/";
}
@Override
public void onBlipSubmitted(BlipSubmittedEvent e) {
calledEvents.add(e.getType());
}
@Override
public void onDocumentChanged(DocumentChangedEvent e) {
calledEvents.add(e.getType());
}
@Override
public void onWaveletTagsChanged(WaveletTagsChangedEvent e) {
calledEvents.add(e.getType());
}
@Override
protected String computeHash() {
return "hash1";
}
};
WaveletData waveletData = new WaveletData("google.com!wave1", "google.com!conv+root", "blip1",
null);
waveletData.addParticipant("foo@google.com");
BlipSubmittedEvent event1 = new BlipSubmittedEvent(null, null, "foo@test.com", 1l, "blip1");
DocumentChangedEvent event2 = new DocumentChangedEvent(null, null, "foo@test.com", 1l, "blip1");
WaveletTagsChangedEvent event3 = new WaveletTagsChangedEvent(null, null, "foo@test.com", 1l,
"blip1");
EventMessageBundle bundle = new EventMessageBundle("Foo", "http://gmodules.com/api/rpc");
bundle.addEvent(event1);
bundle.addEvent(event2);
bundle.addEvent(event3);
bundle.setWaveletData(waveletData);
String json = new GsonFactory().create().toJson(bundle);
MockWriter mockWriter = new MockWriter();
robot.doPost(
makeMockRequest(JSONRPC_PATH, new BufferedReader(new StringReader(json))),
makeMockResponse(mockWriter));
assertEquals(3, calledEvents.size());
assertEquals(EventType.BLIP_SUBMITTED, calledEvents.get(0));
assertEquals(EventType.DOCUMENT_CHANGED, calledEvents.get(1));
assertEquals(EventType.WAVELET_TAGS_CHANGED, calledEvents.get(2));
// Assert that the outgoing operation bundle contains robot.notify() op.
JsonParser jsonParser = new JsonParser();
JsonArray ops = jsonParser.parse(mockWriter.getString()).getAsJsonArray();
assertEquals(1, ops.size());
JsonObject op = ops.get(0).getAsJsonObject();
assertEquals(OperationType.ROBOT_NOTIFY.method(),
op.get(RequestProperty.METHOD.key()).getAsString());
JsonObject params = op.get(RequestProperty.PARAMS.key()).getAsJsonObject();
assertEquals("0.22", params.get(ParamsProperty.PROTOCOL_VERSION.key()).getAsString());
assertEquals("hash1", params.get(ParamsProperty.CAPABILITIES_HASH.key()).getAsString());
}
public void testBlindWavelet() throws Exception {
AbstractRobot robot = new MockRobot();
Wavelet blindWavelet = robot.blindWavelet(WAVE_1, WAVELET_1);
assertEquals(0, blindWavelet.getOperationQueue().getPendingOperations().size());
blindWavelet.getParticipants().add("foo@test.com");
blindWavelet.reply("\n");
assertEquals(2, blindWavelet.getOperationQueue().getPendingOperations().size());
assertEquals(OperationType.WAVELET_ADD_PARTICIPANT_NEWSYNTAX.method(),
blindWavelet.getOperationQueue().getPendingOperations().get(0).getMethod());
assertEquals(OperationType.WAVELET_APPEND_BLIP.method(),
blindWavelet.getOperationQueue().getPendingOperations().get(1).getMethod());
}
public void testProxiedBlindWavelet() throws Exception {
AbstractRobot robot = new MockRobot();
Wavelet blindWavelet = robot.blindWavelet(WAVE_1, WAVELET_1, "proxyid");
assertEquals(0, blindWavelet.getOperationQueue().getPendingOperations().size());
blindWavelet.reply("\n");
List<OperationRequest> ops = blindWavelet.getOperationQueue().getPendingOperations();
assertEquals(1, ops.size());
assertEquals(OperationType.WAVELET_APPEND_BLIP.method(), ops.get(0).getMethod());
assertEquals("proxyid", ops.get(0).getParameter(ParamsProperty.PROXYING_FOR));
// Assert that proxy id should be valid.
try {
robot.blindWavelet(WAVE_1, WAVELET_1, "foo@bar.com");
fail("Should have failed since proxy id is not valid.");
} catch (IllegalArgumentException e) {
// Expected.
}
}
public void testInitRobot() throws Exception {
AbstractRobot robot = new MockRobot();
AbstractRobot spyRobot = spy(robot);
spyRobot.initRobot();
verify(spyRobot).computeCapabilityMap();
verify(spyRobot).computeHash();
}
private HttpServletRequest makeMockRequest(String path, BufferedReader reader)
throws IOException {
HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getRequestURI()).thenReturn(path);
when(request.getReader()).thenReturn(reader);
return request;
}
private HttpServletRequest makeMockRequest(String path, String parameterKey,
String parameterValue) {
HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getRequestURI()).thenReturn(path);
when(request.getParameter(parameterKey)).thenReturn(parameterValue);
return request;
}
private HttpServletRequest makeMockRequest(String path) {
HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getRequestURI()).thenReturn(path);
return request;
}
private HttpServletResponse makeMockResponse(MockWriter writer) throws IOException {
HttpServletResponse response = mock(HttpServletResponse.class);
when(response.getWriter()).thenReturn(writer);
return response;
}
}