diff --git a/src/inttest/java/com/faforever/api/clan/ClanControllerTest.java b/src/inttest/java/com/faforever/api/clan/ClanControllerTest.java index 59ae5511d..74e9998be 100644 --- a/src/inttest/java/com/faforever/api/clan/ClanControllerTest.java +++ b/src/inttest/java/com/faforever/api/clan/ClanControllerTest.java @@ -8,10 +8,6 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.test.context.support.WithUserDetails; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.annotation.DirtiesContext.MethodMode; import org.springframework.test.context.jdbc.Sql; import org.springframework.test.context.jdbc.Sql.ExecutionPhase; import org.springframework.test.web.servlet.MvcResult; @@ -20,16 +16,15 @@ import static org.hamcrest.Matchers.is; import static org.hamcrest.core.IsNull.nullValue; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -@DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) @Sql(executionPhase = ExecutionPhase.BEFORE_TEST_METHOD, scripts = "classpath:sql/truncateTables.sql") @Sql(executionPhase = ExecutionPhase.BEFORE_TEST_METHOD, scripts = "classpath:sql/prepDefaultData.sql") @Sql(executionPhase = ExecutionPhase.BEFORE_TEST_METHOD, scripts = "classpath:sql/prepClanData.sql") @@ -115,7 +110,6 @@ public void createClanWithoutAuth() throws Exception { } @Test - @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) public void createClanWithExistingName() throws Exception { Player player = playerRepository.getById(USERID_USER); @@ -138,7 +132,6 @@ public void createClanWithExistingName() throws Exception { } @Test - @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) public void createClanWithExistingTag() throws Exception { Player player = playerRepository.getById(USERID_USER); @@ -161,7 +154,6 @@ public void createClanWithExistingTag() throws Exception { } @Test - @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) public void createSecondClan() throws Exception { Player player = playerRepository.getById(USERID_CLAN_MEMBER); diff --git a/src/main/java/com/faforever/api/clan/ClanService.java b/src/main/java/com/faforever/api/clan/ClanService.java index dadfc9152..603940443 100644 --- a/src/main/java/com/faforever/api/clan/ClanService.java +++ b/src/main/java/com/faforever/api/clan/ClanService.java @@ -15,8 +15,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; -import org.springframework.security.jwt.Jwt; -import org.springframework.security.core.Authentication; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.Assert; @@ -69,15 +67,16 @@ public void preCreate(Clan clan) { @SneakyThrows @Transactional @Deprecated - // use POST via Elide instead - Clan create(String name, String tag, String description) { + // use POST via Elide instead + public Clan create(String name, String tag, String description) { Clan clan = new Clan(); clan.setName(name); clan.setTag(tag); clan.setDescription(description); clan.setRequiresInvitation(true); - clan.setFounder(playerService.getCurrentPlayer()); - clan.setLeader(playerService.getCurrentPlayer()); + Player currentPlayer = playerService.getCurrentPlayer(); + clan.setFounder(currentPlayer); + clan.setLeader(currentPlayer); // validation is done at preCreate() called by ClanListener clanRepository.save(clan); @@ -86,7 +85,7 @@ Clan create(String name, String tag, String description) { @SneakyThrows @Transactional - String generatePlayerInvitationToken(int newMemberId, int clanId) { + public String generatePlayerInvitationToken(int newMemberId, int clanId) { Player requester = playerService.getCurrentPlayer(); Clan clan = clanRepository.findById(clanId) @@ -102,16 +101,14 @@ String generatePlayerInvitationToken(int newMemberId, int clanId) { .plus(fafApiProperties.getClan().getInviteLinkExpireDurationMinutes(), ChronoUnit.MINUTES) .toEpochMilli(); - InvitationResult result = new InvitationResult(expire, - ClanResult.of(clan), - PlayerResult.of(newMember)); + InvitationResult result = new InvitationResult(expire, ClanResult.of(clan), PlayerResult.of(newMember)); return jwtService.sign(result); } @SneakyThrows void acceptPlayerInvitationToken(String stringToken) { - Jwt token = jwtService.decodeAndVerify(stringToken); - InvitationResult invitation = objectMapper.readValue(token.getClaims(), InvitationResult.class); + String decodedToken = jwtService.decodeAndVerify(stringToken); + InvitationResult invitation = objectMapper.readValue(decodedToken, InvitationResult.class); if (invitation.isExpired()) { throw ApiException.of(ErrorCode.CLAN_ACCEPT_TOKEN_EXPIRE); @@ -122,7 +119,7 @@ void acceptPlayerInvitationToken(String stringToken) { Clan clan = clanRepository.findById(clanId) .orElseThrow(() -> new ApiException(new Error(ErrorCode.CLAN_NOT_EXISTS, clanId))); - Player newMember = playerService.getById(invitation.newMember().getId()); + Player newMember = playerService.getById(invitation.newMember().id()); if (!Objects.equals(player.getId(), newMember.getId())) { throw ApiException.of(ErrorCode.CLAN_ACCEPT_WRONG_PLAYER); diff --git a/src/main/java/com/faforever/api/config/IgnoreOctetStreamToObjectHttpMessageConverter.java b/src/main/java/com/faforever/api/config/IgnoreOctetStreamToObjectHttpMessageConverter.java index 5842e719c..883b82095 100644 --- a/src/main/java/com/faforever/api/config/IgnoreOctetStreamToObjectHttpMessageConverter.java +++ b/src/main/java/com/faforever/api/config/IgnoreOctetStreamToObjectHttpMessageConverter.java @@ -6,7 +6,6 @@ import org.springframework.http.MediaType; import org.springframework.http.converter.AbstractHttpMessageConverter; import org.springframework.lang.Nullable; -import org.springframework.security.core.Authentication; import org.springframework.util.StreamUtils; import org.springframework.web.multipart.MultipartFile; @@ -16,7 +15,7 @@ * This class is used to support having both @RequestParam and @RequestPart with same multipart name in one request handler. * When multipart request contains simple request param octet-stream, this class is used to ignore parsing * of byte stream to {@link MapUploadMetadata}. - * See {@link com.faforever.api.map.MapsController#uploadMap(MultipartFile, String, MapUploadMetadata, Authentication)} + * See {@link com.faforever.api.map.MapsController#uploadMap(MultipartFile, String, MapUploadMetadata)} */ public class IgnoreOctetStreamToObjectHttpMessageConverter extends AbstractHttpMessageConverter { diff --git a/src/main/java/com/faforever/api/config/NoopMultipartFileToStringConverter.java b/src/main/java/com/faforever/api/config/NoopMultipartFileToStringConverter.java index 86bade358..a8b562586 100644 --- a/src/main/java/com/faforever/api/config/NoopMultipartFileToStringConverter.java +++ b/src/main/java/com/faforever/api/config/NoopMultipartFileToStringConverter.java @@ -2,14 +2,13 @@ import com.faforever.api.map.MapUploadMetadata; import org.springframework.core.convert.converter.Converter; -import org.springframework.security.core.Authentication; import org.springframework.web.multipart.MultipartFile; /** * This class is used to support having both @RequestParam and @RequestPart with same multipart name in one request handler. * When multipart request contains json multipart, this class is used to ignore conversion * of MultipartFile to String. - * See {@link com.faforever.api.map.MapsController#uploadMap(MultipartFile, String, MapUploadMetadata, Authentication)} + * See {@link com.faforever.api.map.MapsController#uploadMap(MultipartFile, String, MapUploadMetadata)} */ public class NoopMultipartFileToStringConverter implements Converter { diff --git a/src/main/java/com/faforever/api/map/MapsController.java b/src/main/java/com/faforever/api/map/MapsController.java index 373b2b915..351c3b0ad 100644 --- a/src/main/java/com/faforever/api/map/MapsController.java +++ b/src/main/java/com/faforever/api/map/MapsController.java @@ -1,6 +1,5 @@ package com.faforever.api.map; -import com.faforever.api.config.FafApiProperties; import com.faforever.api.data.domain.Player; import com.faforever.api.error.ApiException; import com.faforever.api.error.ErrorCode; @@ -14,9 +13,9 @@ import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.security.core.Authentication; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; @@ -34,12 +33,11 @@ @AllArgsConstructor public class MapsController { private final MapService mapService; - private final FafApiProperties fafApiProperties; private final ObjectMapper objectMapper; private final PlayerService playerService; - @RequestMapping(path = "/validate", method = RequestMethod.GET, produces = APPLICATION_JSON_UTF8_VALUE) + @GetMapping(path = "/validate", produces = APPLICATION_JSON_UTF8_VALUE) public ModelAndView showValidationForm(Map model) { return new ModelAndView("validate_map_metadata.html"); } @@ -49,11 +47,7 @@ public ModelAndView showValidationForm(Map model) { @ApiResponse(code = 200, message = "Information about derived names to be used in the scenario.lua"), @ApiResponse(code = 422, message = "A list of reasons why the name is not valid.") }) - @RequestMapping( - path = "/validateMapName", - method = RequestMethod.POST, - produces = APPLICATION_JSON_UTF8_VALUE - ) + @PostMapping(path = "/validateMapName", produces = APPLICATION_JSON_UTF8_VALUE) public MapNameValidationResponse validateMapName(@RequestParam("mapName") String mapName) { return mapService.requestMapNameValidation(mapName); } @@ -62,11 +56,7 @@ public MapNameValidationResponse validateMapName(@RequestParam("mapName") String @ApiResponses(value = { @ApiResponse(code = 200, message = "Valid without further information"), @ApiResponse(code = 422, message = "A list of errors in the scenario.lua")}) - @RequestMapping( - path = "/validateScenarioLua", - method = RequestMethod.POST, - produces = APPLICATION_JSON_UTF8_VALUE - ) + @PostMapping(path = "/validateScenarioLua", produces = APPLICATION_JSON_UTF8_VALUE) public void validateScenarioLua(@RequestParam(name = "scenarioLua") String scenarioLua) { mapService.validateScenarioLua(scenarioLua); } @@ -76,12 +66,13 @@ public void validateScenarioLua(@RequestParam(name = "scenarioLua") String scena @ApiResponse(code = 200, message = "Success"), @ApiResponse(code = 401, message = "Unauthorized"), @ApiResponse(code = 500, message = "Failure")}) - @RequestMapping(path = "/upload", method = RequestMethod.POST, produces = APPLICATION_JSON_UTF8_VALUE) + @PostMapping(path = "/upload", produces = APPLICATION_JSON_UTF8_VALUE) @PreAuthorize("hasScope('" + OAuthScope._UPLOAD_MAP + "')") - public void uploadMap(@RequestParam("file") MultipartFile file, - @Deprecated @RequestParam(value = "metadata", required = false) String metadataJsonString, - @RequestPart(value = "metadata", required = false) MapUploadMetadata metadata, - Authentication authentication) throws IOException { + public void uploadMap( + @RequestParam("file") MultipartFile file, + @Deprecated @RequestParam(value = "metadata", required = false) String metadataJsonString, + @RequestPart(value = "metadata", required = false) MapUploadMetadata metadata + ) throws IOException { if (metadataJsonString == null && metadata == null) { throw ApiException.of(ErrorCode.PARAMETER_MISSING, "metadata"); } @@ -102,7 +93,7 @@ public void uploadMap(@RequestParam("file") MultipartFile file, licenseId = metadata.licenseId(); } - Player player = playerService.getPlayer(authentication); + Player player = playerService.getCurrentPlayer(); mapService.uploadMap(file.getInputStream(), file.getOriginalFilename(), player, ranked, licenseId); } } diff --git a/src/main/java/com/faforever/api/mod/ModsController.java b/src/main/java/com/faforever/api/mod/ModsController.java index 0a327bb5d..e85e1f1a6 100644 --- a/src/main/java/com/faforever/api/mod/ModsController.java +++ b/src/main/java/com/faforever/api/mod/ModsController.java @@ -1,6 +1,5 @@ package com.faforever.api.mod; -import com.faforever.api.config.FafApiProperties; import com.faforever.api.player.PlayerService; import com.faforever.api.security.OAuthScope; import io.swagger.annotations.ApiOperation; @@ -8,9 +7,8 @@ import io.swagger.annotations.ApiResponses; import lombok.RequiredArgsConstructor; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.security.core.Authentication; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; @@ -28,19 +26,18 @@ public class ModsController { private final PlayerService playerService; private final ModService modService; - private final FafApiProperties fafApiProperties; @ApiOperation("Upload a mod") @ApiResponses(value = { @ApiResponse(code = 200, message = "Success"), @ApiResponse(code = 401, message = "Unauthorized"), @ApiResponse(code = 500, message = "Failure")}) - @RequestMapping(path = "/upload", method = RequestMethod.POST, produces = APPLICATION_JSON_UTF8_VALUE) + @PostMapping(path = "/upload", produces = APPLICATION_JSON_UTF8_VALUE) @PreAuthorize("hasScope('" + OAuthScope._UPLOAD_MOD + "')") public void uploadMod( @RequestParam("file") MultipartFile file, - @RequestPart(value = "metadata", required = false) ModUploadMetadata metadata, //TODO make required when implemented by client - Authentication authentication) throws IOException { + @RequestPart(value = "metadata", required = false) ModUploadMetadata metadata //TODO make required when implemented by client + ) throws IOException { Path tempFile = java.nio.file.Files.createTempFile("mod", ".tmp"); file.transferTo(tempFile.toFile()); @@ -48,7 +45,7 @@ public void uploadMod( modService.processUploadedMod( tempFile, file.getOriginalFilename(), - playerService.getPlayer(authentication), + playerService.getCurrentPlayer(), metadata != null ? metadata.licenseId() : null, metadata != null ? metadata.repositoryUrl() : null ); diff --git a/src/main/java/com/faforever/api/player/PlayerService.java b/src/main/java/com/faforever/api/player/PlayerService.java index 578222918..6c4bd1896 100644 --- a/src/main/java/com/faforever/api/player/PlayerService.java +++ b/src/main/java/com/faforever/api/player/PlayerService.java @@ -16,7 +16,7 @@ public class PlayerService { private final PlayerRepository playerRepository; - public Player getPlayer() { + public Player getCurrentPlayer() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication instanceof FafAuthenticationToken fafAuthenticationToken) { diff --git a/src/test/java/com/faforever/api/clan/ClanServiceTest.java b/src/test/java/com/faforever/api/clan/ClanServiceTest.java index 70f007ce8..38db0bc98 100644 --- a/src/test/java/com/faforever/api/clan/ClanServiceTest.java +++ b/src/test/java/com/faforever/api/clan/ClanServiceTest.java @@ -11,13 +11,13 @@ import com.faforever.api.player.PlayerService; import com.faforever.api.security.JwtService; import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.SneakyThrows; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import java.io.IOException; @@ -27,17 +27,15 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThrows; +import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.ArgumentMatchers.any; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Matchers.any; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) @@ -154,289 +152,223 @@ void success() { } - @Test - void createClanWhereLeaderIsAlreadyInAClan() { - String clanName = "My cool Clan"; - String tag = "123"; - String description = "A cool clan for testing"; - Player creator = new Player(); - creator.setId(1); - creator.setClanMembership(new ClanMembership()); - - when(playerService.getCurrentPlayer()).thenReturn(creator); - ApiException result = assertThrows(ApiException.class, () -> instance.create(clanName, tag, description, creator)); - assertThat(result, hasErrorCode(ErrorCode.CLAN_CREATE_FOUNDER_IS_IN_A_CLAN)); - - verify(clanRepository, Mockito.never()).save(any(Clan.class)); - } - - @Test - void createClanWithSameName() { - String clanName = "My cool Clan"; - String tag = "123"; - String description = "A cool clan for testing"; - - Player creator = new Player(); - creator.setId(1); - when(playerService.getCurrentPlayer()).thenReturn(creator); + @Nested + class TestGeneratePlayerInvitationToken { + @Mock + Player player; - when(clanRepository.findOneByName(clanName)).thenReturn(Optional.of(new Clan())); + @BeforeEach + void setUp() { + when(player.getId()).thenReturn(1); + when(playerService.getCurrentPlayer()).thenReturn(player); + } - ApiException result = assertThrows(ApiException.class, () -> instance.create(clanName, tag, description, creator)); - assertThat(result, hasErrorCode(ErrorCode.CLAN_NAME_EXISTS)); + @Test + void invalidClan() throws Exception { + reset(player); - ArgumentCaptor clanCaptor = ArgumentCaptor.forClass(Clan.class); - verify(clanRepository, Mockito.times(0)).save(clanCaptor.capture()); - } + ApiException e = assertThrows(ApiException.class, () -> instance.generatePlayerInvitationToken(45, 42)); - @Test - void createClanWithSameTag() { - String clanName = "My cool Clan"; - String tag = "123"; - String description = "A cool clan for testing"; + assertThat(e, hasErrorCode(ErrorCode.CLAN_NOT_EXISTS)); + verify(jwtService, never()).sign(any()); + } - Player creator = new Player(); - creator.setId(1); - when(playerService.getCurrentPlayer()).thenReturn(creator); + @Test + void generateTokenAsNonLeader() throws Exception { + Player newMember = new Player(); + newMember.setId(2); - when(clanRepository.findOneByName(clanName)).thenReturn(Optional.empty()); - when(clanRepository.findOneByTag(tag)).thenReturn(Optional.of(new Clan())); + Player leader = new Player(); + leader.setId(3); - ApiException result = assertThrows(ApiException.class, () -> instance.create(clanName, tag, description, creator)); - assertThat(result, hasErrorCode(ErrorCode.CLAN_TAG_EXISTS)); + Clan clan = ClanFactory.builder().id(42).leader(leader).build(); - ArgumentCaptor clanCaptor = ArgumentCaptor.forClass(Clan.class); - verify(clanRepository, Mockito.times(0)).save(clanCaptor.capture()); - } + when(clanRepository.findById(clan.getId())).thenReturn(Optional.of(clan)); - @Test - void createClanSuccessful() { - String clanName = "My cool Clan"; - String tag = "123"; - String description = "A cool clan for testing"; + ApiException e = assertThrows(ApiException.class, () -> instance.generatePlayerInvitationToken(45, 42)); - Player creator = new Player(); - creator.setId(1); - when(playerService.getCurrentPlayer()).thenReturn(creator); + assertThat(e, hasErrorCode(ErrorCode.CLAN_NOT_LEADER)); + verify(jwtService, never()).sign(any()); + } - when(clanRepository.findOneByName(clanName)).thenReturn(Optional.empty()); - when(clanRepository.findOneByTag(tag)).thenReturn(Optional.empty()); + @Test + void invalidPlayer() throws Exception { + Clan clan = ClanFactory.builder().id(42).leader(player).build(); + when(clanRepository.findById(clan.getId())).thenReturn(Optional.of(clan)); + when(playerService.getById(45)).thenThrow(ApiException.of(ErrorCode.PLAYER_NOT_FOUND)); - instance.create(clanName, tag, description); - ArgumentCaptor clanCaptor = ArgumentCaptor.forClass(Clan.class); - verify(clanRepository, Mockito.times(1)).save(clanCaptor.capture()); - assertEquals(clanName, clanCaptor.getValue().getName()); - assertEquals(tag, clanCaptor.getValue().getTag()); - assertEquals(description, clanCaptor.getValue().getDescription()); - assertEquals(creator, clanCaptor.getValue().getLeader()); - assertEquals(creator, clanCaptor.getValue().getFounder()); - assertEquals(1, clanCaptor.getValue().getMemberships().size()); - assertEquals(creator, clanCaptor.getValue().getMemberships().stream().findFirst().get().getPlayer()); - } + ApiException e = assertThrows(ApiException.class, () -> instance.generatePlayerInvitationToken(45, 42)); - @Test - void generatePlayerInvitationTokenWithInvalidClan() throws IOException { - Player requester = new Player(); - requester.setId(1); - when(playerService.getCurrentPlayer()).thenReturn(requester); + assertThat(e, hasErrorCode(ErrorCode.PLAYER_NOT_FOUND)); + verify(jwtService, never()).sign(any()); + } - ApiException result = assertThrows(ApiException.class, () -> instance.generatePlayerInvitationToken(requester, 45, 42)); - assertThat(result, hasErrorCode(ErrorCode.CLAN_NOT_EXISTS)); + @Test + void success() throws Exception { + Player newMember = new Player(); + newMember.setId(2); + + Clan clan = ClanFactory.builder().leader(player).build(); + + FafApiProperties props = new FafApiProperties(); + + when(clanRepository.findById(clan.getId())).thenReturn(Optional.of(clan)); + when(playerService.getById(newMember.getId())).thenReturn(newMember); + when(fafApiProperties.getClan()).thenReturn(props.getClan()); + + instance.generatePlayerInvitationToken(newMember.getId(), clan.getId()); + ArgumentCaptor captor = ArgumentCaptor.forClass(InvitationResult.class); + verify(jwtService).sign(captor.capture()); + assertThat("expire", captor.getValue().expire(), greaterThan(System.currentTimeMillis())); + assertEquals(newMember.getId(), captor.getValue().newMember().id()); + assertEquals(newMember.getLogin(), captor.getValue().newMember().login()); + assertEquals(clan.getId(), captor.getValue().clan().id()); + assertEquals(clan.getTag(), captor.getValue().clan().tag()); + assertEquals(clan.getName(), captor.getValue().clan().name()); + } - verify(jwtService, Mockito.never()).sign(any()); } - @Test - void generatePlayerInvitationTokenFromNonLeader() throws IOException { - Player requester = new Player(); - requester.setId(1); - when(playerService.getCurrentPlayer()).thenReturn(requester); - - Player newMember = new Player(); - newMember.setId(2); - - Player leader = new Player(); - leader.setId(3); + @Nested + class TestAcceptPlayerInvitationToken { - Clan clan = ClanFactory.builder().id(42).leader(leader).build(); + private final String STRING_TOKEN = "1234"; - when(clanRepository.findById(clan.getId())).thenReturn(Optional.of(clan)); + private long expire; - ApiException result = assertThrows(ApiException.class, () -> instance.generatePlayerInvitationToken(requester, newMember.getId(), clan.getId())); - assertThat(result, hasErrorCode(ErrorCode.CLAN_NOT_LEADER)); + private Clan clan; - verify(jwtService, Mockito.never()).sign(any()); - } + @BeforeEach + void setUp() { + expire = System.currentTimeMillis() + 1000 * 3; + clan = ClanFactory.builder().build(); + } - @Test - void generatePlayerInvitationTokenInvalidPlayer() throws IOException { - Player requester = new Player(); - requester.setId(1); - when(playerService.getCurrentPlayer()).thenReturn(requester); + @Test + void expired() throws IOException { + expire = System.currentTimeMillis(); - Clan clan = ClanFactory.builder().id(42).leader(requester).build(); + when(jwtService.decodeAndVerify(any())).thenReturn(String.format("{\"expire\":%s}", expire)); - when(clanRepository.findById(clan.getId())).thenReturn(Optional.of(clan)); - when(playerService.getById(45)).thenThrow(ApiException.of(ErrorCode.PLAYER_NOT_FOUND)); + ApiException e = assertThrows(ApiException.class, () -> instance.acceptPlayerInvitationToken(STRING_TOKEN)); - ApiException result = assertThrows(ApiException.class, () -> instance.generatePlayerInvitationToken(requester, 42, clan.getId())); - assertThat(result, hasErrorCode(ErrorCode.CLAN_GENERATE_LINK_PLAYER_NOT_FOUND)); + assertThat(e, hasErrorCode(ErrorCode.CLAN_ACCEPT_TOKEN_EXPIRE)); + verifyNoInteractions(clanMembershipRepository); + } - verify(jwtService, Mockito.never()).sign(any()); - } + @SneakyThrows + @Test + void invalidClan() { + when(jwtService.decodeAndVerify(any())).thenReturn(String.format("{\"expire\":%s,\"clan\":{\"id\":42}}", expire)); - @Test - void generatePlayerInvitationToken() throws IOException { - Player requester = new Player(); - requester.setId(1); - when(playerService.getCurrentPlayer()).thenReturn(requester); - - Player newMember = new Player(); - newMember.setId(2); - - Clan clan = ClanFactory.builder().leader(requester).build(); - - FafApiProperties props = new FafApiProperties(); - - when(clanRepository.findById(clan.getId())).thenReturn(Optional.of(clan)); - when(playerService.getById(newMember.getId())).thenReturn(newMember); - when(fafApiProperties.getClan()).thenReturn(props.getClan()); - - instance.generatePlayerInvitationToken(newMember.getId(), clan.getId()); - ArgumentCaptor captor = ArgumentCaptor.forClass(InvitationResult.class); - verify(jwtService, Mockito.times(1)).sign(captor.capture()); - assertThat("expire", - captor.getValue().expire(), - greaterThan(System.currentTimeMillis())); - assertEquals(newMember.getId(), captor.getValue().newMember().id()); - assertEquals(newMember.getLogin(), captor.getValue().newMember().login()); - assertEquals(clan.getId(), captor.getValue().clan().id()); - assertEquals(clan.getTag(), captor.getValue().clan().tag()); - assertEquals(clan.getName(), captor.getValue().clan().name()); - } + ApiException e = assertThrows(ApiException.class, () -> instance.acceptPlayerInvitationToken(STRING_TOKEN)); - @Test - void acceptPlayerInvitationTokenExpire() throws IOException { - String stringToken = "1234"; - long expire = System.currentTimeMillis(); + assertThat(e, hasErrorCode(ErrorCode.CLAN_NOT_EXISTS)); + verifyNoInteractions(clanMembershipRepository); + } - when(jwtService.decodeAndVerify(any())).thenReturn( - String.format("{\"expire\":%s}", expire)); + @Test + void invalidPlayer() throws IOException { + Player requester = new Player(); + requester.setId(1); + when(playerService.getCurrentPlayer()).thenReturn(requester); + String tokenResult = String.format("{\"expire\":%s,\"newMember\":{\"id\":2},\"clan\":{\"id\":%s}}", + expire, clan.getId()); + when(jwtService.decodeAndVerify(any())).thenReturn(tokenResult); + when(clanRepository.findById(clan.getId())).thenReturn(Optional.of(clan)); + when(playerService.getById(2)).thenThrow(ApiException.of(ErrorCode.PLAYER_NOT_FOUND)); - ApiException result = assertThrows(ApiException.class, () -> instance.acceptPlayerInvitationToken(stringToken, null)); - assertThat(result, hasErrorCode(ErrorCode.CLAN_ACCEPT_TOKEN_EXPIRE)); + ApiException e = assertThrows(ApiException.class, () -> instance.acceptPlayerInvitationToken(STRING_TOKEN)); - verify(clanMembershipRepository, Mockito.never()).save(any(ClanMembership.class)); - } + assertThat(e, hasErrorCode(ErrorCode.PLAYER_NOT_FOUND)); + verifyNoInteractions(clanMembershipRepository); + } - @Test - void acceptPlayerInvitationTokenInvalidClan() throws IOException { - String stringToken = "1234"; + @Test + void playerInTokenNotLoggedInPlayer() throws IOException { + Player newMember = new Player(); + newMember.setId(2); - long expire = System.currentTimeMillis() + 1000 * 3; - when(jwtService.decodeAndVerify(any())).thenReturn(String.format("{\"expire\":%s,\"clan\":{\"id\":42}}", expire)); + Player loggedInPlayer = new Player(); + loggedInPlayer.setId(3); - ApiException result = assertThrows(ApiException.class, () -> instance.acceptPlayerInvitationToken(stringToken, null)); - assertThat(result, hasErrorCode(ErrorCode.CLAN_NOT_EXISTS)); + String tokenResult = String.format("{\"expire\":%s,\"newMember\":{\"id\":%s},\"clan\":{\"id\":%s}}", + expire, newMember.getId(), clan.getId()); + when(jwtService.decodeAndVerify(any())).thenReturn(tokenResult); + when(clanRepository.findById(clan.getId())).thenReturn(Optional.of(clan)); + when(playerService.getById(newMember.getId())).thenReturn(newMember); + when(playerService.getCurrentPlayer()).thenReturn(loggedInPlayer); - verify(clanMembershipRepository, Mockito.never()).save(any(ClanMembership.class)); - } + ApiException e = assertThrows(ApiException.class, () -> instance.acceptPlayerInvitationToken(STRING_TOKEN)); - @Test - void acceptPlayerInvitationTokenInvalidPlayer() throws IOException { - Player requester = new Player(); - requester.setId(1); - when(playerService.getCurrentPlayer()).thenReturn(requester); + assertThat(e, hasErrorCode(ErrorCode.CLAN_ACCEPT_WRONG_PLAYER)); + verifyNoInteractions(clanMembershipRepository); + } - String stringToken = "1234"; - Clan clan = ClanFactory.builder().build(); + @Test + void playerIsAlreadyInClan() throws IOException { + Player newMember = new Player(); + newMember.setId(2); + newMember.setClanMembership(new ClanMembership().setClan(clan).setPlayer(newMember)); - long expire = System.currentTimeMillis() + 1000 * 3; - String tokenResult = String.format("{\"expire\":%s,\"newMember\":{\"id\":2},\"clan\":{\"id\":%s}}", - expire, clan.getId()); - when(jwtService.decodeAndVerify(any())).thenReturn(tokenResult); - when(clanRepository.findById(clan.getId())).thenReturn(Optional.of(clan)); - when(playerService.getById(2)).thenThrow(ApiException.of(ErrorCode.PLAYER_NOT_FOUND)); + String tokenResult = String.format("{\"expire\":%s,\"newMember\":{\"id\":%s},\"clan\":{\"id\":%s}}", + expire, newMember.getId(), clan.getId()); - ProgrammingError result = assertThrows(ProgrammingError.class, () -> instance.acceptPlayerInvitationToken(stringToken, null)); - assertThat(result.getMessage(), is("ClanMember does not exist: 2")); + when(jwtService.decodeAndVerify(any())).thenReturn(tokenResult); + when(clanRepository.findById(clan.getId())).thenReturn(Optional.of(clan)); + when(playerService.getById(newMember.getId())).thenReturn(newMember); + when(playerService.getCurrentPlayer()).thenReturn(newMember); - verify(clanMembershipRepository, Mockito.never()).save(any(ClanMembership.class)); - } + ApiException e = assertThrows(ApiException.class, () -> instance.acceptPlayerInvitationToken(STRING_TOKEN)); - @Test - void acceptPlayerInvitationTokenWrongPlayer() throws IOException { - String stringToken = "1234"; + assertThat(e, hasErrorCode(ErrorCode.CLAN_ACCEPT_PLAYER_IN_A_CLAN)); + verifyNoInteractions(clanMembershipRepository); + } - Player newMember = new Player(); - newMember.setId(2); + @Test + void success() throws IOException { + Player newMember = new Player(); + newMember.setId(2); - Clan clan = ClanFactory.builder().build(); + String tokenResult = String.format("{\"expire\":%s,\"newMember\":{\"id\":%s},\"clan\":{\"id\":%s}}", + expire, newMember.getId(), clan.getId()); - Player otherPlayer = new Player(); - otherPlayer.setId(3); + when(jwtService.decodeAndVerify(any())).thenReturn(tokenResult); + when(clanRepository.findById(clan.getId())).thenReturn(Optional.of(clan)); + when(playerService.getById(newMember.getId())).thenReturn(newMember); + when(playerService.getCurrentPlayer()).thenReturn(newMember); - long expire = System.currentTimeMillis() + 1000 * 3; - String tokenResult = String.format("{\"expire\":%s,\"newMember\":{\"id\":%s},\"clan\":{\"id\":%s}}", - expire, newMember.getId(), clan.getId()); - when(jwtService.decodeAndVerify(any())).thenReturn(tokenResult); - when(clanRepository.findById(clan.getId())).thenReturn(Optional.of(clan)); - when(playerService.getById(newMember.getId())).thenReturn(newMember); - when(playerService.getCurrentPlayer()).thenReturn(otherPlayer); + instance.acceptPlayerInvitationToken(STRING_TOKEN); - ApiException result = assertThrows(ApiException.class, () -> instance.acceptPlayerInvitationToken(stringToken, null)); - assertThat(result, hasErrorCode(ErrorCode.CLAN_ACCEPT_WRONG_PLAYER)); + ArgumentCaptor captor = ArgumentCaptor.forClass(ClanMembership.class); + verify(clanMembershipRepository).save(captor.capture()); - verify(clanMembershipRepository, Mockito.never()).save(any(ClanMembership.class)); + assertEquals(newMember.getId(), captor.getValue().getPlayer().getId()); + assertEquals(clan.getId(), captor.getValue().getClan().getId()); + } } @Test - void acceptPlayerInvitationTokenPlayerIAlreadyInAClan() throws IOException { - String stringToken = "1234"; - - Clan clan = ClanFactory.builder().build(); - - Player newMember = new Player(); - newMember.setId(2); - newMember.setClanMembership(new ClanMembership().setClan(clan).setPlayer(newMember)); - - long expire = System.currentTimeMillis() + 1000 * 3; - String tokenResult = String.format("{\"expire\":%s,\"newMember\":{\"id\":%s},\"clan\":{\"id\":%s}}", - expire, newMember.getId(), clan.getId()); + @Deprecated + void testCreate() { + Player player = mock(Player.class); + when(playerService.getCurrentPlayer()).thenReturn(player); - when(jwtService.decodeAndVerify(any())).thenReturn(tokenResult); - when(clanRepository.findById(clan.getId())).thenReturn(Optional.of(clan)); - when(playerService.getById(newMember.getId())).thenReturn(newMember); - when(playerService.getCurrentPlayer()).thenReturn(newMember); + String clanName = "My cool Clan"; + String tag = "123"; + String description = "A cool clan for testing"; - ApiException result = assertThrows(ApiException.class, () -> instance.acceptPlayerInvitationToken(stringToken, null)); - assertThat(result, hasErrorCode(ErrorCode.CLAN_ACCEPT_PLAYER_IN_A_CLAN)); + Clan clan = instance.create(clanName, tag, description); - verify(clanMembershipRepository, Mockito.never()).save(any(ClanMembership.class)); - } + assertAll( + () -> assertThat(clan.getName(), is(clanName)), + () -> assertThat(clan.getTag(), is(tag)), + () -> assertThat(clan.getDescription(), is(description)), + () -> assertThat(clan.getFounder(), is(player)) + ); - @Test - void acceptPlayerInvitationToken() throws IOException { - String stringToken = "1234"; - Clan clan = ClanFactory.builder().build(); - Player newMember = new Player(); - newMember.setId(2); - long expire = System.currentTimeMillis() + 1000 * 3; - String tokenResult = String.format("{\"expire\":%s,\"newMember\":{\"id\":%s},\"clan\":{\"id\":%s}}", - expire, newMember.getId(), clan.getId()); - - when(jwtService.decodeAndVerify(any())).thenReturn(tokenResult); - when(clanRepository.findById(clan.getId())).thenReturn(Optional.of(clan)); - when(playerService.getById(newMember.getId())).thenReturn(newMember); - when(playerService.getCurrentPlayer()).thenReturn(newMember); - - instance.acceptPlayerInvitationToken(stringToken); - - ArgumentCaptor captor = ArgumentCaptor.forClass(ClanMembership.class); - verify(clanMembershipRepository, Mockito.times(1)).save(captor.capture()); - assertEquals(newMember.getId(), captor.getValue().getPlayer().getId()); - assertEquals(clan.getId(), captor.getValue().getClan().getId()); + verify(clanRepository).save(clan); } }