/** * Copyright 2010 Google Inc. * * Licensed 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 org.waveprotocol.box.server.rpc; import static org.mockito.Matchers.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import com.google.gson.JsonElement; import com.google.gson.JsonParser; import com.google.protobuf.Message; import junit.framework.TestCase; import org.waveprotocol.box.common.comms.WaveClientRpc.WaveletSnapshot; import org.waveprotocol.box.server.account.HumanAccountDataImpl; import org.waveprotocol.box.server.authentication.SessionManager; import org.waveprotocol.box.server.authentication.SessionManagerImpl; import org.waveprotocol.box.server.common.SnapshotSerializer; import org.waveprotocol.box.server.persistence.AccountStore; import org.waveprotocol.box.server.persistence.memory.MemoryStore; import org.waveprotocol.box.server.util.TestDataUtil; import org.waveprotocol.wave.model.id.WaveId; import org.waveprotocol.wave.model.id.WaveletId; import org.waveprotocol.wave.model.wave.ParticipantId; import org.waveprotocol.wave.model.wave.data.BlipData; import org.waveprotocol.wave.model.wave.data.WaveletData; import org.waveprotocol.wave.model.waveref.WaveRef; import org.waveprotocol.wave.util.escapers.jvm.JavaWaverefEncoder; import java.io.PrintWriter; import java.io.StringWriter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * Tests for the FetchServlet. The fetch servlet provides wavelet snapshots * from a waveletProvider. * * These tests make sure reasonable errors are generated for invalid URLs and * that the fetch results match what was sent. * * @author josephg@gmail.com (Joseph Gentle) */ public class FetchServletTest extends TestCase { private static final ProtoSerializer protoSerializer = new ProtoSerializer(); private WaveletProviderStub waveletProvider; private FetchServlet servlet; @Override protected void setUp() throws Exception { waveletProvider = new WaveletProviderStub(); AccountStore accountStore = new MemoryStore(); accountStore.putAccount(new HumanAccountDataImpl(ParticipantId.ofUnsafe("fred@example.com"))); org.eclipse.jetty.server.SessionManager jettySessionManager = mock(org.eclipse.jetty.server.SessionManager.class); SessionManager sessionManager = new SessionManagerImpl(accountStore, jettySessionManager); servlet = new FetchServlet(waveletProvider, protoSerializer, sessionManager); } public void testGetInvalidWaverefReturnsNotFound() throws Exception { HttpServletRequest request = mock(HttpServletRequest.class); HttpServletResponse response = mock(HttpServletResponse.class); when(request.getPathInfo()).thenReturn("/invalidwaveref"); servlet.doGet(request, response); verify(response, times(1)).sendError(HttpServletResponse.SC_NOT_FOUND); } public void testDisallowedUserReturnsForbidden() throws Exception { waveletProvider.setAllowsAccess(false); WaveletData wavelet = waveletProvider.getHostedWavelet(); WaveRef waveref = WaveRef.of(wavelet.getWaveId(), wavelet.getWaveletId()); verifyServletReturnsForbiddenForWaveref(waveref); } public void testGetMissingDataReturnsForbidden() throws Exception { WaveletData wavelet = waveletProvider.getHostedWavelet(); WaveId waveId = wavelet.getWaveId(); WaveletId waveletId = wavelet.getWaveletId(); WaveRef unknownWave = WaveRef.of(WaveId.of(waveId.getDomain(), waveId.getId() + "junk")); verifyServletReturnsForbiddenForWaveref(unknownWave); WaveRef unknownWavelet = WaveRef.of(waveId, WaveletId.of(waveletId.getDomain(), waveletId.getId() + "junk")); verifyServletReturnsForbiddenForWaveref(unknownWavelet); WaveRef unknownDocument = WaveRef.of(waveId, waveletId, "madeupdocid"); verifyServletReturnsForbiddenForWaveref(unknownDocument); } /** * Round-trip a wavelet and make sure all the fields match. * We only check the fields that WaveletSnapshot serializes. * @throws Exception */ public void testGetWavelet() throws Exception { WaveletData wavelet = waveletProvider.getHostedWavelet(); WaveRef waveref = WaveRef.of(wavelet.getWaveId(), wavelet.getWaveletId()); WaveletSnapshot snapshot = fetchWaverRefAndParse(waveref, WaveletSnapshot.class); WaveletData roundtripped = SnapshotSerializer.deserializeWavelet(snapshot, waveref.getWaveId()); // We have just round-tripped wavelet through the servlet. wavelet and // roundtripped should be identical in all the fields that get serialized. TestDataUtil.checkSerializedWavelet(wavelet, roundtripped); // TODO(josephg): Enable this test when the persistence store is in place. // assertEquals(snapshot.getVersion(), waveletProvider.getCommittedVersion()); } /** * The fetch servlet also exposes document snapshots through a longer url * (/fetch/domain/waveid/domain/waveletid/docid). * * @throws Exception */ public void testGetDocument() throws Exception { WaveletData wavelet = waveletProvider.getHostedWavelet(); for (String docId : wavelet.getDocumentIds()) { // We currently have no way to deserialize a document. Instead, we'll // serialize the expected document and compare with what we get from the // fetch servlet. StringWriter writer = new StringWriter(); BlipData expectedDoc = wavelet.getDocument(docId); writer.append("" + protoSerializer.toJson(SnapshotSerializer.serializeDocument(expectedDoc))); String expectedResult = writer.toString(); WaveRef waveref = WaveRef.of(wavelet.getWaveId(), wavelet.getWaveletId(), docId); String actualResult = fetchWaveRef(waveref); assertEquals(expectedResult, actualResult); } } // ** Helper methods /** * Fetch the given waveref from the servlet. */ private void requestWaveRef(WaveRef waveref, HttpServletResponse response) throws Exception { HttpServletRequest request = mock(HttpServletRequest.class); when(request.getPathInfo()).thenReturn("/" + JavaWaverefEncoder.encodeToUriPathSegment(waveref)); servlet.doGet(request, response); } private void verifyServletReturnsForbiddenForWaveref(WaveRef waveref) throws Exception { HttpServletResponse response = mock(HttpServletResponse.class); requestWaveRef(waveref, response); verify(response).sendError(HttpServletResponse.SC_FORBIDDEN); } private String fetchWaveRef(WaveRef waveref) throws Exception { HttpServletResponse response = mock(HttpServletResponse.class); StringWriter writer = new StringWriter(); when(response.getWriter()).thenReturn(new PrintWriter(writer)); requestWaveRef(waveref, response); verify(response).getWriter(); verify(response, never()).sendError(anyInt()); return writer.toString(); } private <T extends Message> T fetchWaverRefAndParse(WaveRef waveref, Class<T> klass) throws Exception { String message = fetchWaveRef(waveref); JsonElement json = new JsonParser().parse(message); return protoSerializer.fromJson(json, klass); } }