diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 09a6afe..f58c9f7 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -116,4 +116,5 @@ dependencies { implementation("de.hdodenhof:circleimageview:3.1.0") testImplementation("org.mockito:mockito-core:3.12.4") + testImplementation("org.mockito:mockito-inline:3.12.4") } \ No newline at end of file diff --git a/app/src/main/java/com/example/scanpal/Controllers/EventController.java b/app/src/main/java/com/example/scanpal/Controllers/EventController.java index 2a438d5..67131a1 100644 --- a/app/src/main/java/com/example/scanpal/Controllers/EventController.java +++ b/app/src/main/java/com/example/scanpal/Controllers/EventController.java @@ -38,6 +38,7 @@ public class EventController { private final FirebaseFirestore database; protected ImageController imageController; protected AttendeeController attendeeController; + protected QrCodeController qrCodeController; /** * Constructs an EventController with a reference to a Firestore database. @@ -46,18 +47,21 @@ public EventController() { database = FirebaseFirestore.getInstance(); imageController = new ImageController(); attendeeController = new AttendeeController(database); + qrCodeController = new QrCodeController(); } /** * Constructs an EventController with a reference to a Firestore database and Firebase storage. * @param database The Firestore database instance. - * @param storage The Firebase storage instance. * @param imageController The image controller instance. + * @param attendeeController The attendee controller instance. + * @param qrCodeController The QR code controller instance. */ - public EventController(FirebaseFirestore database, FirebaseStorage storage, ImageController imageController) { + public EventController(FirebaseFirestore database, ImageController imageController, AttendeeController attendeeController, QrCodeController qrCodeController) { this.database = database; - this.storage = storage; this.imageController = imageController; + this.attendeeController = attendeeController; + this.qrCodeController = qrCodeController; } /** @@ -107,7 +111,6 @@ public void addEvent(Event event, String ID) { eventMap.put("participants", participantRefs); // Generate Qr Code or get custom code - QrCodeController qrCodeController = new QrCodeController(); qrCodeController.generateAndStoreQrCode(event, eventMap); // auto generate a qr code DocumentReference eventRef = database.collection("Events").document(event.getId()); diff --git a/app/src/main/java/com/example/scanpal/Controllers/ImageController.java b/app/src/main/java/com/example/scanpal/Controllers/ImageController.java index b6a09dc..c0a90a8 100644 --- a/app/src/main/java/com/example/scanpal/Controllers/ImageController.java +++ b/app/src/main/java/com/example/scanpal/Controllers/ImageController.java @@ -17,7 +17,15 @@ * Controller class for managing image uploads and retrievals with Firebase Storage. */ public class ImageController { - protected FirebaseStorage storage = FirebaseStorage.getInstance(); + protected FirebaseStorage storage; + + public ImageController() { + storage = FirebaseStorage.getInstance(); + } + + public ImageController(FirebaseStorage storage) { + this.storage = storage; + } /** * Uploads an image file to Firebase Storage within a specified folder. diff --git a/app/src/test/java/com/example/scanpal/EventControllerTest.java b/app/src/test/java/com/example/scanpal/EventControllerTest.java index e921aa7..2de2687 100644 --- a/app/src/test/java/com/example/scanpal/EventControllerTest.java +++ b/app/src/test/java/com/example/scanpal/EventControllerTest.java @@ -3,26 +3,47 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; +import org.mockito.MockedStatic; import org.mockito.MockitoAnnotations; import org.mockito.junit.MockitoJUnitRunner; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.*; import static org.mockito.MockitoAnnotations.openMocks; +import android.net.Uri; +import android.util.Log; + +import com.example.scanpal.Callbacks.DeleteAllAttendeesCallback; import com.example.scanpal.Callbacks.EventDeleteCallback; +import com.example.scanpal.Callbacks.EventIDsFetchCallback; +import com.example.scanpal.Callbacks.UserFetchCallback; +import com.example.scanpal.Controllers.AttendeeController; import com.example.scanpal.Controllers.EventController; import com.example.scanpal.Controllers.ImageController; +import com.example.scanpal.Controllers.QrCodeController; +import com.example.scanpal.Models.Event; +import com.example.scanpal.Models.User; +import com.google.android.gms.tasks.OnCompleteListener; import com.google.android.gms.tasks.OnFailureListener; import com.google.android.gms.tasks.OnSuccessListener; import com.google.android.gms.tasks.Task; import com.google.firebase.firestore.CollectionReference; import com.google.firebase.firestore.DocumentReference; +import com.google.firebase.firestore.DocumentSnapshot; import com.google.firebase.firestore.FirebaseFirestore; +import com.google.firebase.firestore.QueryDocumentSnapshot; +import com.google.firebase.firestore.QuerySnapshot; import com.google.firebase.storage.FirebaseStorage; import com.google.firebase.storage.TaskState; +import java.util.Arrays; import java.util.Collection; +import java.util.List; +import java.util.Map; @RunWith(MockitoJUnitRunner.class) public class EventControllerTest { @@ -38,7 +59,11 @@ public class EventControllerTest { private EventDeleteCallback eventDeleteCallback; private EventController eventController; @Mock - private Task mockDeleteTask; + private CollectionReference collectionReference; + @Mock + private AttendeeController attendeeController; + @Mock + private QrCodeController qrCodeController; @Before @@ -46,14 +71,10 @@ public void setUp() { database = mock(FirebaseFirestore.class); documentReference = mock(DocumentReference.class); eventDeleteCallback = mock(EventDeleteCallback.class); - CollectionReference collectionReference = mock(CollectionReference.class); - mockDeleteTask = mock(Task.class); - - when(database.collection(any())).thenReturn(collectionReference); - when(collectionReference.document(any())).thenReturn(documentReference); - when(documentReference.delete()).thenReturn(mockDeleteTask); - when(mockDeleteTask.addOnSuccessListener(any())).thenReturn(mockDeleteTask); - when(mockDeleteTask.addOnFailureListener(any())).thenReturn(mockDeleteTask); + collectionReference = mock(CollectionReference.class); + attendeeController = mock(AttendeeController.class); + qrCodeController = mock(QrCodeController.class); + eventController = new EventController(database, imageController, attendeeController, qrCodeController); } /** @@ -61,19 +82,30 @@ public void setUp() { */ @Test public void testDeleteEvent() { + Task mockDeleteTask = mock(Task.class); + DeleteAllAttendeesCallback deleteAllAttendeesCallback = mock(DeleteAllAttendeesCallback.class); + + when(database.collection(any())).thenReturn(collectionReference); + when(collectionReference.document(any())).thenReturn(documentReference); + when(documentReference.delete()).thenReturn(mockDeleteTask); + + doAnswer(invocation -> { + DeleteAllAttendeesCallback callback = invocation.getArgument(1); + callback.onSuccess(); + return null; + }).when(attendeeController).deleteAllAttendeesForEvent(any(), any()); + String eventID = "123"; - eventController = new EventController(database, storage, imageController); - // Mock the delete operation to return a successful task doAnswer(invocation -> { OnSuccessListener onSuccess = invocation.getArgument(0); onSuccess.onSuccess(null); return mockDeleteTask; - }).when(mockDeleteTask).addOnSuccessListener(any()); + }).when(mockDeleteTask).addOnSuccessListener(any(OnSuccessListener.class)); eventController.deleteEvent(eventID, eventDeleteCallback); - verify(imageController, times(1)).deleteImage("events", "event_" + eventID + ".jpg"); + verify(imageController, times(1)).deleteImage("events", eventID + ".jpg"); verify(imageController, times(1)).deleteImage("qr-codes", eventID + "-check-in.png"); verify(imageController, times(1)).deleteImage("qr-codes", eventID + "-event.png"); verify(eventDeleteCallback, times(1)).onSuccess(); @@ -85,23 +117,146 @@ public void testDeleteEvent() { */ @Test public void testDeleteEventFail() { - String eventID = "123"; - eventController = new EventController(database, storage, imageController); + Task mockDeleteTask = mock(Task.class); + when(database.collection(any())).thenReturn(collectionReference); + when(collectionReference.document(any())).thenReturn(documentReference); - // Mock the delete operation to return a failed task doAnswer(invocation -> { - OnFailureListener onFailureListener = invocation.getArgument(0); - onFailureListener.onFailure(null); - return mockDeleteTask; - }).when(mockDeleteTask).addOnFailureListener(any()); + DeleteAllAttendeesCallback callback = invocation.getArgument(1); + callback.onError(mock(Exception.class)); + return null; + }).when(attendeeController).deleteAllAttendeesForEvent(any(), any()); + + String eventID = "123"; eventController.deleteEvent(eventID, eventDeleteCallback); - verify(imageController, times(1)).deleteImage("events", "event_" + eventID + ".jpg"); - verify(imageController, times(1)).deleteImage("qr-codes", eventID + "-check-in.png"); - verify(imageController, times(1)).deleteImage("qr-codes", eventID + "-event.png"); + verify(imageController, times(0)).deleteImage("events", eventID + ".jpg"); + verify(imageController, times(0)).deleteImage("qr-codes", eventID + "-check-in.png"); + verify(imageController, times(0)).deleteImage("qr-codes", eventID + "-event.png"); verify(eventDeleteCallback, never()).onSuccess(); verify(eventDeleteCallback, times(1)).onError(any()); } + /** + * Test the getAllEventIds method in the EventController class. + */ + @Test + public void testGetAllEventIds() { + Task task = mock(Task.class); + QuerySnapshot querySnapshot = mock(QuerySnapshot.class); + EventIDsFetchCallback callback = mock(EventIDsFetchCallback.class); + QueryDocumentSnapshot documentSnapshot1 = mock(QueryDocumentSnapshot.class); + QueryDocumentSnapshot documentSnapshot2 = mock(QueryDocumentSnapshot.class); + List documentSnapshots = Arrays.asList(documentSnapshot1, documentSnapshot2); + + when(database.collection("Events")).thenReturn(collectionReference); + when(collectionReference.get()).thenReturn(task); + when(task.addOnCompleteListener(any())).thenAnswer(invocation -> { + OnCompleteListener listener = invocation.getArgument(0); + // simulate a successful task completion + when(task.isSuccessful()).thenReturn(true); + when(task.getResult()).thenReturn(querySnapshot); + // manually trigger the listener with the mocked task + listener.onComplete(task); + return null; + }); + when(task.isSuccessful()).thenReturn(true); + when(task.getResult()).thenReturn(querySnapshot); + when(querySnapshot.iterator()).thenReturn(documentSnapshots.iterator()); + when(documentSnapshot1.getId()).thenReturn("id1"); + when(documentSnapshot2.getId()).thenReturn("id2"); + + eventController.getAllEventIds(callback); + + // Verify that the callback is called with the correct event IDs + ArgumentCaptor> argumentCaptor = ArgumentCaptor.forClass(List.class); + verify(callback).onSuccess(argumentCaptor.capture()); + List eventIDs = argumentCaptor.getValue(); + + assertEquals(2, eventIDs.size()); + assertTrue(eventIDs.contains("id1")); + assertTrue(eventIDs.contains("id2")); + } + + /** + * Test the getAllEventIds method in the EventController class when the documentReference is null. + */ + @Test + public void testFetchEventOrganizerByRef_DocumentReferenceIsNull() { + UserFetchCallback callback = mock(UserFetchCallback.class); + try (MockedStatic mockedLog = mockStatic(Log.class)) { + mockedLog.when(() -> Log.e(anyString(), anyString())).thenReturn(0); + eventController.fetchEventOrganizerByRef(null, callback); + } + verify(callback).onError(any(NullPointerException.class)); + } + + /** + * Test the fetchEventOrganizerByRef method in the EventController class. + */ + @Test + public void testFetchEventOrganizerByRef() { + DocumentSnapshot documentSnapshot = mock(DocumentSnapshot.class); + when(documentSnapshot.getString("firstName")).thenReturn("John"); + when(documentSnapshot.getString("lastName")).thenReturn("Doe"); + when(documentSnapshot.getString("homepage")).thenReturn("https://example.com"); + when(documentSnapshot.getString("photo")).thenReturn("uri_photo"); + when(documentSnapshot.getString("deviceToken")).thenReturn("token"); + when(documentSnapshot.getBoolean("administrator")).thenReturn(true); + when(documentSnapshot.exists()).thenReturn(true); + + DocumentReference eventRef = mock(DocumentReference.class); + Task task = mock(Task.class); + when(eventRef.get()).thenReturn(task); + + when(task.addOnCompleteListener(any())).thenAnswer(invocation -> { + OnCompleteListener listener = invocation.getArgument(0); + when(task.isSuccessful()).thenReturn(true); + when(task.getResult()).thenReturn(documentSnapshot); + listener.onComplete(task); + return null; + }); + + UserFetchCallback callback = mock(UserFetchCallback.class); + eventController.fetchEventOrganizerByRef(eventRef, callback); + + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(User.class); + verify(callback).onSuccess(argumentCaptor.capture()); + User user = argumentCaptor.getValue(); + + assertEquals("John", user.getFirstName()); + assertEquals("Doe", user.getLastName()); + assertEquals("https://example.com", user.getHomepage()); + assertEquals("uri_photo", user.getPhoto()); + assertEquals("token", user.getDeviceToken()); + assertTrue(user.isAdministrator()); + } + + /** + * Test addEvent method + */ + @Test + public void testAddEvent() { + Event mockEvent = mock(Event.class); + User mockUser = mock(User.class); + String id = "123"; + + when(database.collection(any())).thenReturn(collectionReference); + when(collectionReference.document(any())).thenReturn(documentReference); + + when(mockEvent.getOrganizer()).thenReturn(mockUser); + when(mockUser.getUsername()).thenReturn(id); + + doAnswer(invocation -> { + OnSuccessListener onSuccess = invocation.getArgument(3); + onSuccess.onSuccess(mock(Uri.class)); + return null; + }).when(imageController).uploadImage(any(), any(), any(), any(), any()); + + eventController.addEvent(mockEvent, id); + verify(qrCodeController, times(1)).generateAndStoreQrCode(any(Event.class), any(Map.class)); + verify(documentReference, times(1)).update(anyString(), any()); + } + } diff --git a/app/src/test/java/com/example/scanpal/ExampleUnitTest.java b/app/src/test/java/com/example/scanpal/ExampleUnitTest.java deleted file mode 100644 index e8be019..0000000 --- a/app/src/test/java/com/example/scanpal/ExampleUnitTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.example.scanpal; - -import static org.junit.Assert.assertEquals; - -import org.junit.Test; - -/** - * Example local unit test, which will execute on the development machine (host). - * - * @see Testing documentation - */ -public class ExampleUnitTest { - @Test - public void addition_isCorrect() { - assertEquals(4, 2 + 2); - } -} \ No newline at end of file diff --git a/app/src/test/java/com/example/scanpal/ImageControllerTest.java b/app/src/test/java/com/example/scanpal/ImageControllerTest.java new file mode 100644 index 0000000..55e9cba --- /dev/null +++ b/app/src/test/java/com/example/scanpal/ImageControllerTest.java @@ -0,0 +1,93 @@ +package com.example.scanpal; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.net.Uri; + +import com.example.scanpal.Callbacks.ImagesFetchCallback; +import com.example.scanpal.Controllers.ImageController; +import com.google.android.gms.tasks.OnFailureListener; +import com.google.android.gms.tasks.OnSuccessListener; +import com.google.android.gms.tasks.Task; +import com.google.firebase.storage.FirebaseStorage; +import com.google.firebase.storage.StorageReference; +import com.google.firebase.storage.StorageTask; +import com.google.firebase.storage.UploadTask; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class ImageControllerTest { + @Mock + private FirebaseStorage storage; + + private ImageController imageController; + + @Mock + private StorageReference storageReference; + + @Mock + private UploadTask uploadTask; + @Before + public void setUp() { + storage = mock(FirebaseStorage.class); + imageController = new ImageController(storage); + storageReference = mock(StorageReference.class); + uploadTask = mock(UploadTask.class); + + Task mockDownloadUrlTask = mock(Task.class); + + when(storage.getReference()).thenReturn(storageReference); + when(storageReference.child(anyString())).thenReturn(storageReference); + when(storageReference.getDownloadUrl()).thenReturn(mockDownloadUrlTask); + + when(storageReference.putFile(any(Uri.class))).thenReturn(uploadTask); + + doAnswer(invocation -> { + OnSuccessListener onSuccessListener = invocation.getArgument(0); + onSuccessListener.onSuccess(mock(Uri.class)); + return mock(Task.class); + }).when(mockDownloadUrlTask).addOnSuccessListener(any(OnSuccessListener.class)); + + doAnswer(invocation -> { + OnSuccessListener onSuccessListener = invocation.getArgument(0); + onSuccessListener.onSuccess(mock(UploadTask.TaskSnapshot.class)); + return mock(StorageTask.class); + }).when(uploadTask).addOnSuccessListener(any(OnSuccessListener.class)); + + } + + @Test + public void testUploadImage() { + + Uri mockUri = mock(Uri.class); + imageController.uploadImage(mockUri, "folderPath", "fileName", mock(OnSuccessListener.class), mock(OnFailureListener.class)); + + verify(storageReference, times(1)).putFile(mockUri); + verify(uploadTask, times(1)).addOnSuccessListener(any(OnSuccessListener.class)); + } + + @Test + public void testFetchImage() { + imageController.fetchImage("folderPath", "fileName", mock(OnSuccessListener.class), mock(OnFailureListener.class)); + + verify(storageReference, times(1)).getDownloadUrl(); + } + + @Test + public void testDeleteImage() { + imageController.deleteImage("folderPath", "fileName"); + + verify(storageReference, times(1)).delete(); + } +}