/* * Copyright 2015 Google, Inc. All Rights Reserved. * * 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 com.examples.abelanav2.grpc; import com.examples.abelanav2.datastore.DbClient; import com.examples.abelanav2.storage.CloudStorage; import com.google.api.services.datastore.DatastoreV1; import com.google.api.services.datastore.client.DatastoreException; import com.google.common.collect.ImmutableList; import com.google.protobuf.ByteString; import io.grpc.stub.StreamObserver; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import java.util.ArrayList; import java.util.Date; import java.util.List; import static com.google.api.services.datastore.client.DatastoreHelper.makeKey; import static com.google.api.services.datastore.client.DatastoreHelper.makeProperty; import static com.google.api.services.datastore.client.DatastoreHelper.makeValue; import static org.junit.Assert.fail; import static org.mockito.Matchers.*; import static org.mockito.Mockito.spy; @RunWith(PowerMockRunner.class) @PrepareForTest({AuthUtils.class, CloudStorage.class}) @PowerMockIgnore("javax.crypto.*") public class GrpcCallsTest { @Mock DbClient dbClient; /** * We want to test the gRPC server implementation. */ private AbelanaGrpcImpl toTest = spy(new AbelanaGrpcImpl()); @Before public void setup(){ toTest.setDbClient(dbClient); PowerMockito.mockStatic(AuthUtils.class); PowerMockito.mockStatic(CloudStorage.class); } /** * Flag photo tests. */ @Test public void testFlagPhotoCallAllGood() { mockUserSignedIn(); try { Mockito.when(dbClient.getPhoto(12345)).thenReturn(mockPhotoEntity()); Mockito.when(dbClient.getPhotoFlags(any(DatastoreV1.Key.class))) .thenReturn(new ArrayList<DatastoreV1.Entity>()); Mockito.when(dbClient.insertFlag(any(DatastoreV1.Key.class), eq("user_id"))).thenReturn(true); Mockito.when(dbClient.setPhotoFlagged(any(DatastoreV1.Key.class)) ).thenReturn(true); } catch (DatastoreException e) { fail("Unexpected Datastore exception, code= " + e.getCode() + ", message= " + e.getMessage()); } toTest.flagPhoto(FlagRequest.newBuilder().setPhotoId(12345).build(), getStreamObserverStatusResponseCheckError(null)); } @Test public void testFlagPhotoCallNotAuthenticated() { toTest.flagPhoto(FlagRequest.newBuilder().setPhotoId(12345).build(), getStreamObserverStatusResponseCheckError("403")); } @Test public void testFlagPhotoCallPhotoDoesNotExist() { mockUserSignedIn(); try { Mockito.when(dbClient.getPhoto(123456)).thenReturn(null); } catch (DatastoreException e) { fail("Unexpected Datastore exception, code= " + e.getCode() + ", message= " + e.getMessage()); } toTest.flagPhoto(FlagRequest.newBuilder().setPhotoId(123456).build(), getStreamObserverStatusResponseCheckError("500")); } @Test public void testFlagPhotoCallUserAlreadyFlaggedPhoto() { mockUserSignedIn(); try { Mockito.when(dbClient.getPhoto(12345)).thenReturn(mockPhotoEntity()); List<DatastoreV1.Entity> flags = new ArrayList<>(); flags.add(mockFlagEntity("user_id")); Mockito.when(dbClient.getPhotoFlags(any(DatastoreV1.Key.class))) .thenReturn(flags); } catch (DatastoreException e) { fail("Unexpected Datastore exception, code= " + e.getCode() + ", message= " + e.getMessage()); } toTest.flagPhoto(FlagRequest.newBuilder().setPhotoId(12345).build(), getStreamObserverStatusResponseCheckError(null)); } @Test public void testFlagPhotoCallAnotherUserAlreadyFlaggedPhoto() { mockUserSignedIn(); try { Mockito.when(dbClient.getPhoto(12345)).thenReturn(mockPhotoEntity()); List<DatastoreV1.Entity> flags = new ArrayList<>(); flags.add(mockFlagEntity("user_id_2")); Mockito.when(dbClient.getPhotoFlags(any(DatastoreV1.Key.class))) .thenReturn(flags); Mockito.when(dbClient.insertFlag(any(DatastoreV1.Key.class), eq("user_id"))).thenReturn(true); Mockito.when(dbClient.setPhotoFlagged(any(DatastoreV1.Key.class)) ).thenReturn(true); } catch (DatastoreException e) { fail("Unexpected Datastore exception, code= " + e.getCode() + ", message= " + e.getMessage()); } toTest.flagPhoto(FlagRequest.newBuilder().setPhotoId(12345).build(), getStreamObserverStatusResponseCheckError(null)); } /** * Upload photo tests. */ @Test public void testUploadPhotoCallNotAuthenticated() { toTest.uploadPhoto(NewPhotoRequest.newBuilder().setDescription("Great description").build(), getStreamObserverUploadPhotoResponseCheckError("403")); } @Test public void testUploadPhotoCallAllGood() { mockUserSignedIn(); try { Mockito.when(dbClient.insertPhoto("Great description", "user_id")) .thenReturn(mockPhotoEntity()); Mockito.when(CloudStorage.getUploadUrl(anyString())).thenReturn("https://url"); } catch (DatastoreException e) { fail("Unexpected Datastore exception, code= " + e.getCode() + ", message= " + e.getMessage()); } toTest.uploadPhoto(NewPhotoRequest.newBuilder().setDescription("Great description").build(), getStreamObserverUploadPhotoResponseCheckError(null)); } @Test public void testUploadPhotoCallGcsUploadFails() { mockUserSignedIn(); try { Mockito.when(dbClient.insertPhoto("Great description", "user_id")) .thenReturn(mockPhotoEntity()); Mockito.when(CloudStorage.getUploadUrl(anyString())).thenReturn(null); } catch (DatastoreException e) { fail("Unexpected Datastore exception, code= " + e.getCode() + ", message= " + e.getMessage()); } toTest.uploadPhoto(NewPhotoRequest.newBuilder().setDescription("Great description").build(), getStreamObserverUploadPhotoResponseCheckError("500")); } @Test public void testUploadPhotoCallInsertPhotoFails() { mockUserSignedIn(); try { Mockito.when(dbClient.insertPhoto("Great description", "user_id")) .thenReturn(null); } catch (DatastoreException e) { fail("Unexpected Datastore exception, code= " + e.getCode() + ", message= " + e.getMessage()); } toTest.uploadPhoto(NewPhotoRequest.newBuilder().setDescription("Great description").build(), getStreamObserverUploadPhotoResponseCheckError("500")); } /** * Edit photo tests. */ @Test public void testEditPhotoCallNotAuthenticated() { toTest.editPhoto(EditPhotoRequest.newBuilder().setPhotoId(12345) .setDescription("Great description").build(), getStreamObserverStatusResponseCheckError("403")); } @Test public void testEditPhotoCallAllGood() { mockUserSignedIn(); try { Mockito.when(dbClient.getPhoto(12345)).thenReturn(mockPhotoEntity()); Mockito.when(dbClient.updatePhotoDescription(any(DatastoreV1.Key.class), eq("New description"))).thenReturn(true); } catch (DatastoreException e) { fail("Unexpected Datastore exception, code= " + e.getCode() + ", message= " + e.getMessage()); } toTest.editPhoto(EditPhotoRequest.newBuilder().setPhotoId(12345) .setDescription("New description").build(), getStreamObserverStatusResponseCheckError(null)); } @Test public void testEditPhotoCallPhotoDoesNotBelongToUser() { mockUserSignedIn(); try { Mockito.when(dbClient.getPhoto(12345)).thenReturn(mockPhotoEntity("user_id_2")); } catch (DatastoreException e) { fail("Unexpected Datastore exception, code= " + e.getCode() + ", message= " + e.getMessage()); } toTest.editPhoto(EditPhotoRequest.newBuilder().setPhotoId(12345) .setDescription("New description").build(), getStreamObserverStatusResponseCheckError("403")); } @Test public void testEditPhotoCallPhotoNotFound() { mockUserSignedIn(); try { Mockito.when(dbClient.getPhoto(12345)).thenReturn(null); } catch (DatastoreException e) { fail("Unexpected Datastore exception, code= " + e.getCode() + ", message= " + e.getMessage()); } toTest.editPhoto(EditPhotoRequest.newBuilder().setPhotoId(12345) .setDescription("New description").build(), getStreamObserverStatusResponseCheckError("500")); } @Test public void testEditPhotoCallUpdateFailed() { mockUserSignedIn(); try { Mockito.when(dbClient.getPhoto(12345)).thenReturn(mockPhotoEntity()); Mockito.when(dbClient.updatePhotoDescription(any(DatastoreV1.Key.class), eq("New description"))).thenReturn(false); } catch (DatastoreException e) { fail("Unexpected Datastore exception, code= " + e.getCode() + ", message= " + e.getMessage()); } toTest.editPhoto(EditPhotoRequest.newBuilder().setPhotoId(12345) .setDescription("New description").build(), getStreamObserverStatusResponseCheckError("500")); } /** * Delete photo tests. */ @Test public void testDeletePhotoCallNotAuthenticated() { toTest.deletePhoto(DeletePhotoRequest.newBuilder().setPhotoId(12345).build(), getStreamObserverStatusResponseCheckError("403")); } @Test public void testDeletePhotoCallAllGood() { mockUserSignedIn(); try { Mockito.when(dbClient.getPhoto(12345)).thenReturn(mockPhotoEntity()); Mockito.when(dbClient.deletePhotoAndChildren(any(DatastoreV1.Key.class))).thenReturn(true); } catch (DatastoreException e) { fail("Unexpected Datastore exception, code= " + e.getCode() + ", message= " + e.getMessage()); } toTest.deletePhoto(DeletePhotoRequest.newBuilder().setPhotoId(12345).build(), getStreamObserverStatusResponseCheckError(null)); } @Test public void testDeletePhotoCallPhotoDoesNotBelongToUser() { mockUserSignedIn(); try { Mockito.when(dbClient.getPhoto(12345)).thenReturn(mockPhotoEntity("user_id_2")); } catch (DatastoreException e) { fail("Unexpected Datastore exception, code= " + e.getCode() + ", message= " + e.getMessage()); } toTest.deletePhoto(DeletePhotoRequest.newBuilder().setPhotoId(12345).build(), getStreamObserverStatusResponseCheckError("403")); } @Test public void testDeletePhotoCallPhotoNotFound() { mockUserSignedIn(); try { Mockito.when(dbClient.getPhoto(12345)).thenReturn(null); } catch (DatastoreException e) { fail("Unexpected Datastore exception, code= " + e.getCode() + ", message= " + e.getMessage()); } toTest.deletePhoto(DeletePhotoRequest.newBuilder().setPhotoId(12345).build(), getStreamObserverStatusResponseCheckError("500")); } @Test public void testEditPhotoCallDeleteFailed() { mockUserSignedIn(); try { Mockito.when(dbClient.getPhoto(12345)).thenReturn(mockPhotoEntity()); Mockito.when(dbClient.deletePhotoAndChildren(any(DatastoreV1.Key.class))).thenReturn(false); } catch (DatastoreException e) { fail("Unexpected Datastore exception, code= " + e.getCode() + ", message= " + e.getMessage()); } toTest.deletePhoto(DeletePhotoRequest.newBuilder().setPhotoId(12345).build(), getStreamObserverStatusResponseCheckError("500")); } /** * Rate photo tests. */ @Test public void testRatePhotoCallNotAuthenticated() { toTest.ratePhoto(VoteRequest.newBuilder().setPhotoId(12345).setVote(VoteRequest .VoteType.THUMBS_UP).build(), getStreamObserverStatusResponseCheckError("403")); } @Test public void testRatePhotoCallAllGood() { mockUserSignedIn(); try { Mockito.when(dbClient.getPhoto(12345)).thenReturn(mockPhotoEntity()); Mockito.when(dbClient.voteForPhoto(eq(12345L), anyInt(), eq("user_id"))) .thenReturn(true); } catch (DatastoreException e) { fail("Unexpected Datastore exception, code= " + e.getCode() + ", message= " + e.getMessage()); } toTest.ratePhoto(VoteRequest.newBuilder().setPhotoId(12345).setVote(VoteRequest .VoteType.THUMBS_UP).build(), getStreamObserverStatusResponseCheckError(null)); } @Test public void testRatePhotoCallVoteFailed() { mockUserSignedIn(); try { Mockito.when(dbClient.getPhoto(12345)).thenReturn(mockPhotoEntity()); Mockito.when(dbClient.voteForPhoto(eq(12345L), anyInt(), eq("user_id"))) .thenReturn(false); } catch (DatastoreException e) { fail("Unexpected Datastore exception, code= " + e.getCode() + ", message= " + e.getMessage()); } toTest.ratePhoto(VoteRequest.newBuilder().setPhotoId(12345).setVote(VoteRequest .VoteType.THUMBS_UP).build(), getStreamObserverStatusResponseCheckError("500")); } @Test public void testRatePhotoCallPhotoNotFound() { mockUserSignedIn(); try { Mockito.when(dbClient.getPhoto(12345)).thenReturn(null); } catch (DatastoreException e) { fail("Unexpected Datastore exception, code= " + e.getCode() + ", message= " + e.getMessage()); } toTest.ratePhoto(VoteRequest.newBuilder().setPhotoId(12345).setVote(VoteRequest .VoteType.THUMBS_UP).build(), getStreamObserverStatusResponseCheckError("404")); } /** * Photo stream tests. */ @Test public void testPhotoStreamCallNotAuthenticated() { toTest.photoStream(PhotoListRequest.newBuilder().setPageNumber(0).build(), getStreamObserverPhotoListResponseCheckError("403")); } @Test public void testPhotoStreamCallAllGood() { mockUserSignedIn(); try { Mockito.when(dbClient.getPhotoList(eq("user_id"), any(DbClient.PhotoListType.class), any(ByteString.class))).thenReturn(mockPhotoList()); Mockito.when(dbClient.getVoteValueForPhoto(anyLong(), eq("user_id"))).thenReturn(-1L); } catch (DatastoreException e) { fail("Unexpected Datastore exception, code= " + e.getCode() + ", message= " + e.getMessage()); } toTest.photoStream(PhotoListRequest.newBuilder().setPageNumber(0).build(), getStreamObserverPhotoListResponseCheckError(null)); } @Test public void testPhotoStreamCallAllGoodInvalidCursor() { mockUserSignedIn(); try { Mockito.when(dbClient.getPhotoList(eq("user_id"), eq(DbClient.PhotoListType .PHOTO_LIST_STREAM), any(ByteString.class))).thenReturn(mockPhotoList()); Mockito.when(dbClient.getVoteValueForPhoto(anyLong(), eq("user_id"))).thenReturn(-1L); Mockito.when(dbClient.getAndDeleteCursor(1)).thenReturn(null); } catch (DatastoreException e) { fail("Unexpected Datastore exception, code= " + e.getCode() + ", message= " + e.getMessage()); } toTest.photoStream(PhotoListRequest.newBuilder().setPageNumber(1).build(), getStreamObserverPhotoListResponseCheckError("400-200")); } /** * My pictures tests. */ @Test public void testMyPicturesCallNotAuthenticated() { toTest.listMyPhotos(PhotoListRequest.newBuilder().setPageNumber(0).build(), getStreamObserverPhotoListResponseCheckError("403")); } @Test public void testMyPicturesCallAllGood() { mockUserSignedIn(); try { Mockito.when(dbClient.getPhotoList(eq("user_id"), eq(DbClient.PhotoListType.PHOTO_LIST_MINE), any(ByteString.class))).thenReturn(mockPhotoList()); } catch (DatastoreException e) { fail("Unexpected Datastore exception, code= " + e.getCode() + ", message= " + e.getMessage()); } toTest.listMyPhotos(PhotoListRequest.newBuilder().setPageNumber(0).build(), getStreamObserverPhotoListResponseCheckError(null)); } @Test public void testMyPicturesCallAllGoodInvalidCursor() { mockUserSignedIn(); try { Mockito.when(dbClient.getPhotoList(eq("user_id"), eq(DbClient.PhotoListType .PHOTO_LIST_MINE), any(ByteString.class))).thenReturn(mockPhotoList()); Mockito.when(dbClient.getVoteValueForPhoto(anyLong(), eq("user_id"))).thenReturn(-1L); Mockito.when(dbClient.getAndDeleteCursor(1)).thenReturn(null); } catch (DatastoreException e) { fail("Unexpected Datastore exception, code= " + e.getCode() + ", message= " + e.getMessage()); } toTest.listMyPhotos(PhotoListRequest.newBuilder().setPageNumber(1).build(), getStreamObserverPhotoListResponseCheckError("400-200")); } /** * My likes tests. */ @Test public void testMyLikesCallNotAuthenticated() { toTest.listMyLikes(PhotoListRequest.newBuilder().setPageNumber(0).build(), getStreamObserverPhotoListResponseCheckError("403")); } @Test public void testMyLikesCallAllGood() { mockUserSignedIn(); try { Mockito.when(dbClient.getPhotoList(eq("user_id"), eq(DbClient.PhotoListType.PHOTO_LIST_LIKES), any(ByteString.class))).thenReturn(mockPhotoList()); } catch (DatastoreException e) { fail("Unexpected Datastore exception, code= " + e.getCode() + ", message= " + e.getMessage()); } toTest.listMyLikes(PhotoListRequest.newBuilder().setPageNumber(0).build(), getStreamObserverPhotoListResponseCheckError(null)); } @Test public void testMyLikesCallAllGoodInvalidCursor() { mockUserSignedIn(); try { Mockito.when(dbClient.getPhotoList(eq("user_id"), eq(DbClient.PhotoListType .PHOTO_LIST_LIKES), any(ByteString.class))).thenReturn(mockPhotoList()); Mockito.when(dbClient.getVoteValueForPhoto(anyLong(), eq("user_id"))).thenReturn(-1L); Mockito.when(dbClient.getAndDeleteCursor(1)).thenReturn(null); } catch (DatastoreException e) { fail("Unexpected Datastore exception, code= " + e.getCode() + ", message= " + e.getMessage()); } toTest.listMyLikes(PhotoListRequest.newBuilder().setPageNumber(1).build(), getStreamObserverPhotoListResponseCheckError("400-200")); } private StreamObserver<StatusResponse> getStreamObserverStatusResponseCheckError( final String errorCode ) { return new StreamObserver<StatusResponse>() { @Override public void onValue(StatusResponse response) { if(errorCode != null) { assert(response.hasError() && response.getError().getCode().equals(errorCode)); } else { assert(!response.hasError()); } } @Override public void onError(Throwable throwable) { } @Override public void onCompleted() { } }; } private StreamObserver<UploadPhotoResponse> getStreamObserverUploadPhotoResponseCheckError( final String errorCode ) { return new StreamObserver<UploadPhotoResponse>() { @Override public void onValue(UploadPhotoResponse response) { if(errorCode != null) { assert(response.hasError() && response.getError().getCode().equals(errorCode)); } else { assert(!response.hasError()); } } @Override public void onError(Throwable throwable) { } @Override public void onCompleted() { } }; } private StreamObserver<PhotoListResponse> getStreamObserverPhotoListResponseCheckError( final String errorCode ) { return new StreamObserver<PhotoListResponse>() { @Override public void onValue(PhotoListResponse response) { if(errorCode != null) { assert(response.hasError() && response.getError().getCode().equals(errorCode)); } else { assert(!response.hasError()); } } @Override public void onError(Throwable throwable) { } @Override public void onCompleted() { } }; } /** * Returns a dummy Photo Entity with the default user id in those tests: user_id. * @return a Photo Entity. */ public DatastoreV1.Entity mockPhotoEntity() { return mockPhotoEntity("user_id"); } /** * Returns a dummy Photo Entity with the userId passed. * @param userId the user ID to use. * @return a Photo Entity. */ public DatastoreV1.Entity mockPhotoEntity(String userId) { List<DatastoreV1.Property> properties = ImmutableList.of( makeProperty("description", makeValue("Great description")).build(), makeProperty("userId", makeValue(userId)).build(), makeProperty("date", makeValue(new Date())).build(), makeProperty("flagged", makeValue(false)).build(), makeProperty("available", makeValue(false)).build(), makeProperty("numberVotes", makeValue(0)).build(), makeProperty("numberPositiveVotes", makeValue(0)).build(), makeProperty("lowerTruePopularity", makeValue(0)).build(), makeProperty("upperTruePopularity", makeValue(0)).build() ); return DatastoreV1.Entity.newBuilder().setKey(makeKey("Photo", 12345)) .addAllProperty(properties).build(); } /** * Returns a dummy Flag Entity with the userId passed. * @param userId the user ID to use. * @return a Flag Entity. */ public DatastoreV1.Entity mockFlagEntity(String userId) { List<DatastoreV1.Property> properties = ImmutableList.of( makeProperty("userId", makeValue(userId)).build() ); return DatastoreV1.Entity.newBuilder().setKey(makeKey(makeKey("Photo", 12345).build(), "Flag", 67890)).addAllProperty(properties).build(); } /** * Returns a dummy Photo list. * @return a EntityListAndCursorResult with photos. */ public DbClient.EntityListAndCursorResult mockPhotoList() { List<DatastoreV1.Entity> photoList = new ArrayList<>(); photoList.add(mockPhotoEntity("user_id_2")); photoList.add(mockPhotoEntity("user_id_3")); return new DbClient.EntityListAndCursorResult(photoList, null); } /** * Sets the user as signed in the application. */ public void mockUserSignedIn() { Mockito.when(AuthUtils.isSignedIn()).thenReturn(true); Mockito.when(AuthUtils.getUserId()).thenReturn("user_id"); } }