From 766712070ac3a86a1bd855c42783a56c6371ce2d Mon Sep 17 00:00:00 2001 From: perakasem Date: Tue, 2 Dec 2025 03:48:13 -0500 Subject: [PATCH] Room Entity + interactor Tests --- src/test/java/entity/RoomTest.java | 109 +++++++++++ .../room/create/CreateRoomInteractorTest.java | 164 ++++++++++++++++ .../room/join/JoinRoomInteractorTest.java | 184 ++++++++++++++++++ 3 files changed, 457 insertions(+) create mode 100644 src/test/java/entity/RoomTest.java create mode 100644 src/test/java/use_case/room/create/CreateRoomInteractorTest.java create mode 100644 src/test/java/use_case/room/join/JoinRoomInteractorTest.java diff --git a/src/test/java/entity/RoomTest.java b/src/test/java/entity/RoomTest.java new file mode 100644 index 0000000..c2f5c6b --- /dev/null +++ b/src/test/java/entity/RoomTest.java @@ -0,0 +1,109 @@ +package entity; + +import org.junit.jupiter.api.Test; + +import java.time.LocalDateTime; + +import static org.junit.jupiter.api.Assertions.*; + +class RoomTest { + + // ---------- 1. Successful constructor ---------- + @Test + void constructor_setsFieldsCorrectly() { + final String id = "room-1"; + final String name = "My Room"; + final String description = "A nice place"; + final String ownerId = "owner-1"; + final String inviteCode = "123456"; + + final Room room = new Room(id, name, description, ownerId, inviteCode); + + assertEquals(id, room.getId()); + assertEquals(name, room.getName()); + assertEquals(description, room.getDescription()); + assertEquals(ownerId, room.getOwnerId()); + assertEquals(inviteCode, room.getInviteCode()); + + assertNotNull(room.getCreatedAt()); + assertNotNull(room.getUpdatedAt()); + assertTrue(room.getCreatedAt().isBefore(LocalDateTime.now().plusSeconds(1))); + } + + @Test + void constructor_allowsNullDescription() { + final String id = "room-2"; + final String name = "Another Room"; + final String ownerId = "owner-2"; + final String inviteCode = "654321"; + + final Room room = new Room(id, name, null, ownerId, inviteCode); + + assertNull(room.getDescription()); + assertEquals(name, room.getName()); + assertEquals(ownerId, room.getOwnerId()); + } + + // ---------- 2. Validation errors (branch tests) ---------- + + @Test + void constructor_throwsForNullId() { + assertThrows(IllegalArgumentException.class, + () -> new Room(null, "name", "desc", "owner", "123")); + } + + @Test + void constructor_throwsForEmptyId() { + assertThrows(IllegalArgumentException.class, + () -> new Room(" ", "name", "desc", "owner", "123")); + } + + @Test + void constructor_throwsForNullName() { + assertThrows(IllegalArgumentException.class, + () -> new Room("rid", null, "desc", "owner", "123")); + } + + @Test + void constructor_throwsForEmptyName() { + assertThrows(IllegalArgumentException.class, + () -> new Room("rid", " ", "desc", "owner", "123")); + } + + @Test + void constructor_throwsForNullOwnerId() { + assertThrows(IllegalArgumentException.class, + () -> new Room("rid", "name", "desc", null, "123")); + } + + @Test + void constructor_throwsForEmptyOwnerId() { + assertThrows(IllegalArgumentException.class, + () -> new Room("rid", "name", "desc", " ", "123")); + } + + @Test + void constructor_throwsForNullInviteCode() { + assertThrows(IllegalArgumentException.class, + () -> new Room("rid", "name", "desc", "owner", null)); + } + + @Test + void constructor_throwsForEmptyInviteCode() { + assertThrows(IllegalArgumentException.class, + () -> new Room("rid", "name", "desc", "owner", " ")); + } + + // ---------- 3. Mutators (setters) ---------- + @Test + void setters_updateNameAndDescription() { + final Room room = new Room("room-3", "Initial", "first desc", "owner-3", "000111"); + + room.setName("Updated Name"); + room.setDescription("Updated description"); + + assertEquals("Updated Name", room.getName()); + assertEquals("Updated description", room.getDescription()); + } +} + diff --git a/src/test/java/use_case/room/create/CreateRoomInteractorTest.java b/src/test/java/use_case/room/create/CreateRoomInteractorTest.java new file mode 100644 index 0000000..25213ab --- /dev/null +++ b/src/test/java/use_case/room/create/CreateRoomInteractorTest.java @@ -0,0 +1,164 @@ +package use_case.room.create; + +import entity.Room; +import entity.User; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import use_case.exception.CreateRoomFailedException; +import use_case.logged_in.UserService; +import use_case.session.SessionDataAccessInterface; +import use_case.room.RoomDataAccessInterface; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class CreateRoomInteractorTest { + + @Mock + private RoomDataAccessInterface roomDataAccess; + + @Mock + private SessionDataAccessInterface sessionDataAccess; + + @Mock + private CreateRoomOutputBoundary createRoomPresenter; + + @Mock + private UserService userService; + + // ---------- 1. Success path ---------- + @Test + void execute_success_savesRoomAndPresentsSuccess() { + // Arrange + final String roomName = "My Room"; + final String description = "A place"; + final CreateRoomInputData input = new CreateRoomInputData(roomName, description); + + final User user = mock(User.class); + when(userService.getUser()).thenReturn(user); + when(user.getId()).thenReturn("user-1"); + + // Ensure uniqueness: getRoomByInviteCode returns null for any candidate + when(roomDataAccess.getRoomByInviteCode(anyString())).thenReturn(null); + + CreateRoomInteractor interactor = new CreateRoomInteractor( + roomDataAccess, sessionDataAccess, createRoomPresenter, userService + ); + + // Act + interactor.execute(input); + + // Assert: room saved and user added + ArgumentCaptor roomCaptor = ArgumentCaptor.forClass(Room.class); + verify(roomDataAccess, times(1)).saveRoom(roomCaptor.capture()); + Room saved = roomCaptor.getValue(); + assertEquals(roomName, saved.getName()); + assertEquals(description, saved.getDescription()); + assertEquals("user-1", saved.getOwnerId()); + assertNotNull(saved.getInviteCode()); + + verify(roomDataAccess, times(1)).addUserToRoom(saved.getId(), "user-1"); + + // Assert presenter called with success + ArgumentCaptor outCaptor = ArgumentCaptor.forClass(CreateRoomOutputData.class); + verify(createRoomPresenter, times(1)).presentSuccess(outCaptor.capture()); + CreateRoomOutputData out = outCaptor.getValue(); + assertEquals(roomName, out.getRoomName()); + assertEquals(description, out.getRoomDescription()); + assertEquals(saved.getInviteCode(), out.getInviteCode()); + + verify(createRoomPresenter, never()).presentFailure(anyString()); + } + + // ---------- 2. user null ---------- + @Test + void execute_userNull_presentsNotLoggedIn() { + when(userService.getUser()).thenReturn(null); + + CreateRoomInteractor interactor = new CreateRoomInteractor( + roomDataAccess, sessionDataAccess, createRoomPresenter, userService + ); + + interactor.execute(new CreateRoomInputData("n", "d")); + + verify(createRoomPresenter, times(1)).presentFailure("User not logged in"); + verify(roomDataAccess, never()).saveRoom(any()); + } + + // ---------- 3. user already in a room ---------- + @Test + void execute_userAlreadyInRoom_presentsAlreadyInRoom() { + final User user = mock(User.class); + when(userService.getUser()).thenReturn(user); + when(user.getId()).thenReturn("user-2"); + + when(roomDataAccess.getUserRoomId("user-2")).thenReturn("existing-room"); + + CreateRoomInteractor interactor = new CreateRoomInteractor( + roomDataAccess, sessionDataAccess, createRoomPresenter, userService + ); + + interactor.execute(new CreateRoomInputData("rname", "desc")); + + verify(createRoomPresenter, times(1)).presentFailure("You are already in a room. Leave your current room first."); + verify(roomDataAccess, never()).saveRoom(any()); + } + + // ---------- 4. empty room name ---------- + @Test + void execute_emptyName_presentsValidation() { + final User user = mock(User.class); + when(userService.getUser()).thenReturn(user); + when(user.getId()).thenReturn("user-3"); + + CreateRoomInteractor interactor = new CreateRoomInteractor( + roomDataAccess, sessionDataAccess, createRoomPresenter, userService + ); + + interactor.execute(new CreateRoomInputData(" ", "desc")); + + verify(createRoomPresenter, times(1)).presentFailure("Room name cannot be empty"); + verify(roomDataAccess, never()).saveRoom(any()); + } + + // ---------- 5. empty owner id ---------- + @Test + void execute_emptyOwnerId_presentsValidation() { + final User user = mock(User.class); + when(userService.getUser()).thenReturn(user); + when(user.getId()).thenReturn(" "); + + CreateRoomInteractor interactor = new CreateRoomInteractor( + roomDataAccess, sessionDataAccess, createRoomPresenter, userService + ); + + interactor.execute(new CreateRoomInputData("name", "desc")); + + verify(createRoomPresenter, times(1)).presentFailure("Owner ID cannot be empty"); + verify(roomDataAccess, never()).saveRoom(any()); + } + + // ---------- 6. DAO failure path ---------- + @Test + void execute_daoThrows_presentsFailure() { + final User user = mock(User.class); + when(userService.getUser()).thenReturn(user); + when(user.getId()).thenReturn("user-4"); + + when(roomDataAccess.getRoomByInviteCode(anyString())).thenReturn(null); + doThrow(new CreateRoomFailedException("db down")).when(roomDataAccess).saveRoom(any()); + + CreateRoomInteractor interactor = new CreateRoomInteractor( + roomDataAccess, sessionDataAccess, createRoomPresenter, userService + ); + + interactor.execute(new CreateRoomInputData("name", "desc")); + + verify(createRoomPresenter, times(1)).presentFailure("Failed to create room: db down"); + verify(roomDataAccess, never()).addUserToRoom(anyString(), anyString()); + } +} diff --git a/src/test/java/use_case/room/join/JoinRoomInteractorTest.java b/src/test/java/use_case/room/join/JoinRoomInteractorTest.java new file mode 100644 index 0000000..4edaf7c --- /dev/null +++ b/src/test/java/use_case/room/join/JoinRoomInteractorTest.java @@ -0,0 +1,184 @@ +package use_case.room.join; + +import entity.Room; +import entity.User; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import use_case.exception.JoinRoomFailedException; +import use_case.logged_in.UserService; +import use_case.session.SessionDataAccessInterface; +import use_case.room.RoomDataAccessInterface; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class JoinRoomInteractorTest { + + @Mock + private RoomDataAccessInterface roomDataAccess; + + @Mock + private SessionDataAccessInterface sessionDataAccess; + + @Mock + private JoinRoomOutputBoundary joinRoomPresenter; + + @Mock + private UserService userService; + + // ---------- 1. Success path ---------- + @Test + void execute_success_addsUserAndPresentsSuccess() { + final User user = mock(User.class); + when(userService.getUser()).thenReturn(user); + when(user.getId()).thenReturn("user-1"); + + final Room room = new Room("r1", "RoomName", "desc", "owner-1", "111111"); + when(roomDataAccess.getRoomByInviteCode("111111")).thenReturn(room); + when(roomDataAccess.getUserRoomId("user-1")).thenReturn(null); + when(roomDataAccess.isUserInRoom("r1", "user-1")).thenReturn(false); + + JoinRoomInteractor interactor = new JoinRoomInteractor( + roomDataAccess, sessionDataAccess, joinRoomPresenter, userService + ); + + interactor.execute(new JoinRoomInputData("111111")); + + verify(roomDataAccess, times(1)).addUserToRoom("r1", "user-1"); + + ArgumentCaptor outCaptor = ArgumentCaptor.forClass(JoinRoomOutputData.class); + verify(joinRoomPresenter, times(1)).presentSuccess(outCaptor.capture()); + JoinRoomOutputData out = outCaptor.getValue(); + assertEquals("RoomName", out.getRoomName()); + assertTrue(out.isSuccess()); + } + + // ---------- 2. user null ---------- + @Test + void execute_userNull_presentsNotLoggedIn() { + when(userService.getUser()).thenReturn(null); + + JoinRoomInteractor interactor = new JoinRoomInteractor( + roomDataAccess, sessionDataAccess, joinRoomPresenter, userService + ); + + interactor.execute(new JoinRoomInputData("code")); + + verify(joinRoomPresenter, times(1)).presentFailure("User not logged in"); + } + + // ---------- 3. empty invite code ---------- + @Test + void execute_emptyInviteCode_presentsValidation() { + final User user = mock(User.class); + when(userService.getUser()).thenReturn(user); + + JoinRoomInteractor interactor = new JoinRoomInteractor( + roomDataAccess, sessionDataAccess, joinRoomPresenter, userService + ); + + interactor.execute(new JoinRoomInputData(" ")); + + verify(joinRoomPresenter, times(1)).presentFailure("Invite code cannot be empty"); + } + + // ---------- 4. user already in a room ---------- + @Test + void execute_userAlreadyInRoom_presentsAlreadyInRoom() { + final User user = mock(User.class); + when(userService.getUser()).thenReturn(user); + when(user.getId()).thenReturn("u3"); + + when(roomDataAccess.getUserRoomId("u3")).thenReturn("some-room"); + + JoinRoomInteractor interactor = new JoinRoomInteractor( + roomDataAccess, sessionDataAccess, joinRoomPresenter, userService + ); + + interactor.execute(new JoinRoomInputData("111111")); + + verify(joinRoomPresenter, times(1)).presentFailure("You are already in a room. Leave your current room first."); + } + + // ---------- 5. empty user id ---------- + @Test + void execute_emptyUserId_presentsValidation() { + final User user = mock(User.class); + when(userService.getUser()).thenReturn(user); + when(user.getId()).thenReturn(" "); + + JoinRoomInteractor interactor = new JoinRoomInteractor( + roomDataAccess, sessionDataAccess, joinRoomPresenter, userService + ); + + interactor.execute(new JoinRoomInputData("111111")); + + verify(joinRoomPresenter, times(1)).presentFailure("User ID cannot be empty"); + } + + // ---------- 6. invalid invite code ---------- + @Test + void execute_invalidInviteCode_presentsInvalid() { + final User user = mock(User.class); + when(userService.getUser()).thenReturn(user); + when(user.getId()).thenReturn("u4"); + + when(roomDataAccess.getRoomByInviteCode("999999")).thenReturn(null); + + JoinRoomInteractor interactor = new JoinRoomInteractor( + roomDataAccess, sessionDataAccess, joinRoomPresenter, userService + ); + + interactor.execute(new JoinRoomInputData("999999")); + + verify(joinRoomPresenter, times(1)).presentFailure("Invalid invite code"); + } + + // ---------- 7. already member ---------- + @Test + void execute_alreadyMember_presentsAlreadyMember() { + final User user = mock(User.class); + when(userService.getUser()).thenReturn(user); + when(user.getId()).thenReturn("u5"); + + final Room room = new Room("r5", "Name", "d", "owner", "222222"); + when(roomDataAccess.getRoomByInviteCode("222222")).thenReturn(room); + when(roomDataAccess.getUserRoomId("u5")).thenReturn(null); + when(roomDataAccess.isUserInRoom("r5", "u5")).thenReturn(true); + + JoinRoomInteractor interactor = new JoinRoomInteractor( + roomDataAccess, sessionDataAccess, joinRoomPresenter, userService + ); + + interactor.execute(new JoinRoomInputData("222222")); + + verify(joinRoomPresenter, times(1)).presentFailure("You are already a member of this room"); + } + + // ---------- 8. DAO failure path ---------- + @Test + void execute_daoThrows_presentsFailure() { + final User user = mock(User.class); + when(userService.getUser()).thenReturn(user); + when(user.getId()).thenReturn("u6"); + + final Room room = new Room("r6", "Name6", "d", "owner6", "333333"); + when(roomDataAccess.getRoomByInviteCode("333333")).thenReturn(room); + when(roomDataAccess.getUserRoomId("u6")).thenReturn(null); + when(roomDataAccess.isUserInRoom("r6", "u6")).thenReturn(false); + + doThrow(new JoinRoomFailedException("db down")).when(roomDataAccess).addUserToRoom("r6", "u6"); + + JoinRoomInteractor interactor = new JoinRoomInteractor( + roomDataAccess, sessionDataAccess, joinRoomPresenter, userService + ); + + interactor.execute(new JoinRoomInputData("333333")); + + verify(joinRoomPresenter, times(1)).presentFailure("Failed to join create room: db down"); + } +}