diff --git a/sth-backend/pom.xml b/sth-backend/pom.xml index 18c77c9ea9cf0c8259571608b1266915f5f3a305..539b22fe07c9d786494a8053c653dd92db1b1414 100644 --- a/sth-backend/pom.xml +++ b/sth-backend/pom.xml @@ -21,6 +21,11 @@ <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <version>5.8.0</version> + </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> @@ -61,8 +66,13 @@ <artifactId>spring-boot-starter-data-mongodb</artifactId> <version>3.2.0</version> </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> - </dependencies> + </dependencies> <build> <plugins> diff --git a/sth-backend/src/main/java/hdm/mi/sthbackend/controller/TournamentController.java b/sth-backend/src/main/java/hdm/mi/sthbackend/controller/TournamentController.java index 8a9061515631b474a37d4a4e71030425db6e6426..16288fb200ca4a661143d645b926f0c04658e343 100644 --- a/sth-backend/src/main/java/hdm/mi/sthbackend/controller/TournamentController.java +++ b/sth-backend/src/main/java/hdm/mi/sthbackend/controller/TournamentController.java @@ -7,6 +7,7 @@ import hdm.mi.sthbackend.dto.TeamDTO; import hdm.mi.sthbackend.dto.TournamentDTO; import hdm.mi.sthbackend.exeptions.*; import hdm.mi.sthbackend.model.Match; +import hdm.mi.sthbackend.model.Tournament; import hdm.mi.sthbackend.service.TournamentService; import hdm.mi.sthbackend.types.TeamMatchScore; import hdm.mi.sthbackend.types.TeamName; @@ -56,6 +57,10 @@ public class TournamentController { @PathVariable UUID teamId) throws TournamentIdNotFoundException, TeamIdNotFoundException { return service.deleteAndRemoveTeamFromTournament(tournamentId, teamId); } + @PatchMapping("tournaments/{tournamentId}/matches/{matchId}/teams/{teamId}/assignTeamToMatch") + public Tournament assignTeamToMatch(@PathVariable UUID tournamentId, @PathVariable UUID matchId, @PathVariable UUID teamId, @RequestBody int bracketRound, @RequestBody TeamMatchScore score) throws TournamentIdNotFoundException, MatchIdNotFoundException { + return service.assignTeamToMatch(tournamentId, bracketRound, matchId, teamId, score.getScore()); + } @PostMapping("/teams/{teamId}/addPlayer") public PlayerDTO addPlayerToTeam(@PathVariable UUID teamId, @@ -79,10 +84,6 @@ public class TournamentController { /** * Match Endpoints */ - @PatchMapping("matches/{matchId}/teams/{teamId}/assignTeamToMatch") - public MatchDTO assignTeamToMatch(@PathVariable UUID matchId, @PathVariable UUID teamId, @RequestBody TeamMatchScore score) throws MatchIdNotFoundException { - return service.assignTeamToMatch(matchId, teamId, score.getScore()); - } @PatchMapping("matches/{matchId}/teams/{teamId}/updateScore") public MatchDTO updateScore(@PathVariable UUID matchId, @PathVariable UUID teamId, @RequestBody TeamMatchScore newScore) throws MatchIdNotFoundException, TeamIdNotFoundException { @@ -99,17 +100,17 @@ public class TournamentController { * Tournament Endpoints */ @GetMapping("/tournaments/{tournamentId}") - public TournamentDTO findTournamentById(@PathVariable UUID tournamentId) throws TournamentIdNotFoundException { + public Tournament findTournamentById(@PathVariable UUID tournamentId) throws TournamentIdNotFoundException { return service.getTournament(tournamentId); } @PostMapping("/tournaments") - public TournamentDTO createTournament(@RequestBody TournamentName params) { - return service.createTournament(params.getName()); + public Tournament createTournament(@RequestBody Tournament tournament) { + return service.createTournament(tournament); } @DeleteMapping("/tournaments/{tournamentId}") - public TournamentDTO deleteTournament(@PathVariable UUID tournamentId) throws TournamentIdNotFoundException { + public Tournament deleteTournament(@PathVariable UUID tournamentId) throws TournamentIdNotFoundException { return service.deleteTournament(tournamentId); } diff --git a/sth-backend/src/main/java/hdm/mi/sthbackend/exeptions/BracketAlreadyInitializedException.java b/sth-backend/src/main/java/hdm/mi/sthbackend/exeptions/BracketAlreadyInitializedException.java new file mode 100644 index 0000000000000000000000000000000000000000..e5f0de9a9fc71d68bd722b2c178ffe8c3b79f9c3 --- /dev/null +++ b/sth-backend/src/main/java/hdm/mi/sthbackend/exeptions/BracketAlreadyInitializedException.java @@ -0,0 +1,15 @@ +package hdm.mi.sthbackend.exeptions; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.UUID; + +public class BracketAlreadyInitializedException extends Exception{ + private final Logger log = LogManager.getLogger("BracketAlreadyInitializedException"); + + public BracketAlreadyInitializedException(UUID tournamentId){ + super(String.format("Bracket of %s (tournamentId) already initialized", tournamentId.toString())); + } + +} diff --git a/sth-backend/src/main/java/hdm/mi/sthbackend/exeptions/InsufficientTeamsException.java b/sth-backend/src/main/java/hdm/mi/sthbackend/exeptions/InsufficientTeamsException.java new file mode 100644 index 0000000000000000000000000000000000000000..34c6cdc4e7a9702d6747dafd8227bc29c7f48bf2 --- /dev/null +++ b/sth-backend/src/main/java/hdm/mi/sthbackend/exeptions/InsufficientTeamsException.java @@ -0,0 +1,15 @@ +package hdm.mi.sthbackend.exeptions; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.UUID; + +public class InsufficientTeamsException extends Exception{ + private final Logger log = LogManager.getLogger("InsufficientTeamsException"); + + public InsufficientTeamsException(UUID tournamentId){ + super(String.format("Insufficient Teams in %s.", tournamentId.toString())); + } + +} diff --git a/sth-backend/src/main/java/hdm/mi/sthbackend/mapper/ModelToDTOMapper.java b/sth-backend/src/main/java/hdm/mi/sthbackend/mapper/ModelToDTOMapper.java index 897da408c9b9c5fe212796953adc7f53b8e762cf..b9418ca1d3d15337aefda1c141263998bc091a90 100644 --- a/sth-backend/src/main/java/hdm/mi/sthbackend/mapper/ModelToDTOMapper.java +++ b/sth-backend/src/main/java/hdm/mi/sthbackend/mapper/ModelToDTOMapper.java @@ -46,41 +46,42 @@ public class ModelToDTOMapper { this.playerRepository = playerRepository; } - public TournamentDTO mapToTournamentDTO(Tournament tournament) { - Map<UUID, Match> matches = new HashMap<>(); - tournament.getMatches() - .forEach(mId -> { - try { - matches.put(mId, matchRepository.findById(mId) - .orElseThrow(() -> new MatchIdNotFoundException(mId))); - } catch (MatchIdNotFoundException e) { - log.debug("Match with id " + mId + " not found"); - } - }); - Map<UUID, MatchDTO> mappedMatches = matches.entrySet() - .stream() - .collect(Collectors.toMap(Entry::getKey, e -> mapToMatchDTO(e.getValue()))); - - Map<UUID, Team> teams = new HashMap<>(); - tournament.getTeams() - .forEach(teamId -> { - try { - teams.put(teamId, teamRepository.findById(teamId) - .orElseThrow(() -> new TournamentIdNotFoundException(teamId))); - } catch (TournamentIdNotFoundException e) { - log.debug("Team with id " + teamId + " not found"); - } - }); - Map<UUID, TeamDTO> mappedTeams = teams.entrySet() - .stream() - .collect(Collectors.toMap(Entry::getKey, e -> mapToTeamDTO(e.getValue()))); - return new TournamentDTO( - tournament.getTournamentId(), - tournament.getTournamentName(), - mappedMatches, - mappedTeams - ); - } + //todo +// public TournamentDTO mapToTournamentDTO(Tournament tournament) { +// Map<UUID, Match> matches = new HashMap<>(); +// tournament.getMatches() +// .forEach(mId -> { +// try { +// matches.put(mId, matchRepository.findById(mId) +// .orElseThrow(() -> new MatchIdNotFoundException(mId))); +// } catch (MatchIdNotFoundException e) { +// log.debug("Match with id " + mId + " not found"); +// } +// }); +// Map<UUID, MatchDTO> mappedMatches = matches.entrySet() +// .stream() +// .collect(Collectors.toMap(Entry::getKey, e -> mapToMatchDTO(e.getValue()))); +// +// Map<UUID, Team> teams = new HashMap<>(); +// tournament.getTeams() +// .forEach(teamId -> { +// try { +// teams.put(teamId, teamRepository.findById(teamId) +// .orElseThrow(() -> new TournamentIdNotFoundException(teamId))); +// } catch (TournamentIdNotFoundException e) { +// log.debug("Team with id " + teamId + " not found"); +// } +// }); +// Map<UUID, TeamDTO> mappedTeams = teams.entrySet() +// .stream() +// .collect(Collectors.toMap(Entry::getKey, e -> mapToTeamDTO(e.getValue()))); +// return new TournamentDTO( +// tournament.getTournamentId(), +// tournament.getTournamentName(), +// mappedMatches, +// mappedTeams +// ); +// } public MatchDTO mapToMatchDTO(Match match) { return new MatchDTO( diff --git a/sth-backend/src/main/java/hdm/mi/sthbackend/model/BracketRound.java b/sth-backend/src/main/java/hdm/mi/sthbackend/model/BracketRound.java new file mode 100644 index 0000000000000000000000000000000000000000..1875694505bc1960d22d823ec74d0a69df459ea1 --- /dev/null +++ b/sth-backend/src/main/java/hdm/mi/sthbackend/model/BracketRound.java @@ -0,0 +1,19 @@ +package hdm.mi.sthbackend.model; + +import lombok.Getter; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.*; + +public class BracketRound { + private final Logger log = LogManager.getLogger("BracketRound"); + @Getter + Map<UUID, Match> matches; + int round; + + public BracketRound(int round){ + this.round = round; + matches = new HashMap<>(); + } +} diff --git a/sth-backend/src/main/java/hdm/mi/sthbackend/model/Match.java b/sth-backend/src/main/java/hdm/mi/sthbackend/model/Match.java index 8041fd19e362850a7ff37b73a8d0be30189d02ef..1eeecc5316d75ac8f9b7cfa69dc0179af90e90b1 100644 --- a/sth-backend/src/main/java/hdm/mi/sthbackend/model/Match.java +++ b/sth-backend/src/main/java/hdm/mi/sthbackend/model/Match.java @@ -8,18 +8,19 @@ import org.apache.logging.log4j.Logger; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; +import java.util.HashMap; import java.util.Map; import java.util.UUID; @Getter @Document("Match") -@AllArgsConstructor public class Match { private static final Logger log = LogManager.getLogger(Match.class); @Id private UUID matchId; + @Getter private Map<UUID, Integer> teamScores; @Setter @@ -30,4 +31,9 @@ public class Match { @Setter private UUID nextMatchId; + + public Match(UUID matchId){ + this.matchId = matchId; + this.teamScores = new HashMap<>(); + } } diff --git a/sth-backend/src/main/java/hdm/mi/sthbackend/model/Tournament.java b/sth-backend/src/main/java/hdm/mi/sthbackend/model/Tournament.java index 81d512624185d78c86cac1e65aeee280d8ed1582..18356b42fc89db0b33583facd9d2140c1105950c 100644 --- a/sth-backend/src/main/java/hdm/mi/sthbackend/model/Tournament.java +++ b/sth-backend/src/main/java/hdm/mi/sthbackend/model/Tournament.java @@ -6,6 +6,7 @@ import lombok.Setter; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.UUID; @@ -18,9 +19,14 @@ public class Tournament implements ITournament{ private UUID tournamentId; @Setter private String tournamentName; - - private List<UUID> matches; - + @Setter + private List<BracketRound> bracket; private List<UUID> teams; + public Tournament(String tournamentName, List<UUID> teams){ + this.tournamentId = UUID.randomUUID(); + this.tournamentName = tournamentName; + this.teams = teams; + this.bracket = new ArrayList<>(); + } } diff --git a/sth-backend/src/main/java/hdm/mi/sthbackend/model/User.java b/sth-backend/src/main/java/hdm/mi/sthbackend/model/User.java new file mode 100644 index 0000000000000000000000000000000000000000..238a06111a99a553f8d72be5068c77d9b970ed0e --- /dev/null +++ b/sth-backend/src/main/java/hdm/mi/sthbackend/model/User.java @@ -0,0 +1,16 @@ +package hdm.mi.sthbackend.model; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.Map; +import java.util.UUID; + +public class User { + private final Logger log = LogManager.getLogger("User"); + + private UUID userId; + private String name; + private Map<UUID, Tournament> tournaments; + +} diff --git a/sth-backend/src/main/java/hdm/mi/sthbackend/service/TournamentService.java b/sth-backend/src/main/java/hdm/mi/sthbackend/service/TournamentService.java index 857ff616f35fefbb2b3b904dc6217e11fd1c7343..fadff0303c71dcdc2ff2b86f30a00b7de6821e2c 100644 --- a/sth-backend/src/main/java/hdm/mi/sthbackend/service/TournamentService.java +++ b/sth-backend/src/main/java/hdm/mi/sthbackend/service/TournamentService.java @@ -6,10 +6,7 @@ import hdm.mi.sthbackend.dto.TeamDTO; import hdm.mi.sthbackend.dto.TournamentDTO; import hdm.mi.sthbackend.exeptions.*; import hdm.mi.sthbackend.mapper.ModelToDTOMapper; -import hdm.mi.sthbackend.model.Match; -import hdm.mi.sthbackend.model.Player; -import hdm.mi.sthbackend.model.Team; -import hdm.mi.sthbackend.model.Tournament; +import hdm.mi.sthbackend.model.*; import hdm.mi.sthbackend.repository.IMatchRepository; import hdm.mi.sthbackend.repository.IPlayerRepository; import hdm.mi.sthbackend.repository.ITeamRepository; @@ -145,11 +142,10 @@ public class TournamentService { return teamId; } - public TournamentDTO createTournament(String tournamentName) { - Tournament tournament = new Tournament(UUID.randomUUID(), tournamentName, new ArrayList<>(), new ArrayList<>()); + public Tournament createTournament(Tournament tournament) { tournamentRepository.insert(tournament); log.debug("created Tournament " + tournament.getTournamentId()); - return mapper.mapToTournamentDTO(tournament); + return tournament; } public UUID determineWinner (UUID matchId) throws MatchIdNotFoundException, WinnerNotDeterminedException { @@ -166,13 +162,25 @@ public class TournamentService { return winnerTeamId; } - public MatchDTO assignTeamToMatch(UUID matchId, UUID teamId, int score) throws MatchIdNotFoundException { - Match match = matchRepository.findById(matchId).orElseThrow(() -> new MatchIdNotFoundException(matchId)); + public Tournament assignTeamToMatch(UUID tournamentId, int bracketRound, UUID matchId, UUID teamId, int score) throws TournamentIdNotFoundException, MatchIdNotFoundException { + Tournament tournament = tournamentRepository.findById(tournamentId) + .orElseThrow(() -> new TournamentIdNotFoundException(tournamentId)); + try{ + tournament + .getBracket() + .get(bracketRound) + .getMatches() + .get(matchId) + .getTeamScores() + .put(teamId, score); + } + catch (Exception e){ + throw new MatchIdNotFoundException(matchId); + } - match.getTeamScores().put(teamId,score); - matchRepository.save(match); + tournamentRepository.save(tournament); log.debug("Team " + teamId + " assign to Match " + matchId ); - return mapper.mapToMatchDTO(match); + return tournament; } @@ -207,23 +215,79 @@ public class TournamentService { log.debug("Tournament Name for TournamentID " + tournamentId + " updated to " + newTournamentName); return tournamentId; } - public TournamentDTO getTournament(UUID tournamentId) throws TournamentIdNotFoundException { + public Tournament getTournament(UUID tournamentId) throws TournamentIdNotFoundException { Tournament tournament = tournamentRepository.findById(tournamentId) .orElseThrow(() -> new TournamentIdNotFoundException(tournamentId)); log.debug(tournament + "is found"); - return mapper.mapToTournamentDTO(tournament); + return tournament; } - public TournamentDTO deleteTournament(UUID tournamentId) throws TournamentIdNotFoundException { + public Tournament deleteTournament(UUID tournamentId) throws TournamentIdNotFoundException { Tournament tournamentToDelete = tournamentRepository.findById(tournamentId) .orElseThrow(() -> new TournamentIdNotFoundException(tournamentId)); tournamentRepository.delete(tournamentToDelete); log.debug("Tournament " + tournamentId + " has been deleted"); - return mapper.mapToTournamentDTO(tournamentToDelete); + return tournamentToDelete; + } + + public Tournament createBracket(UUID tournamentId) throws TournamentIdNotFoundException, InsufficientTeamsException, BracketAlreadyInitializedException{ + Tournament tournament = tournamentRepository.findById(tournamentId) + .orElseThrow(() -> new TournamentIdNotFoundException(tournamentId)); + List<UUID> teams = tournament.getTeams(); + + if(teams.size() < 2){ + throw new InsufficientTeamsException(tournamentId); + } + + // log zur Basis 2 von der Anzahl der Teams (aufgerundet) + int roundCount = (int) Math.ceil(Math.log(teams.size()) / Math.log(2)); + + if(tournament.getBracket().size() == 0){ + tournament.setBracket(new ArrayList<>()); + for(int i = 0; i < roundCount; i++){ + // richtige Anzahl an BracketRounds hinzufügen + tournament.getBracket().add(new BracketRound(i)); + + // anzahl der matches errechnen + int matchCount = (int) (Math.pow(2, roundCount)) / (int) Math.pow(2, i + 1); + + for(int j = 0; j < matchCount; j++){ + // matches hinzufügen + UUID matchId = UUID.randomUUID(); + tournament.getBracket().get(i).getMatches().put(matchId, new Match(matchId)); + } + } + } + else{ + throw new BracketAlreadyInitializedException(tournamentId); + } + tournamentRepository.save(tournament); + return tournament; } + public Tournament fillBracketRandom(UUID tournamentId) throws TournamentIdNotFoundException { + Tournament tournament = tournamentRepository.findById(tournamentId) + .orElseThrow(() -> new TournamentIdNotFoundException(tournamentId)); + List<UUID> teams = tournament.getTeams(); + + Random random = new Random(); + for(int i = 0; i < 2; i++){ + for(Match match: tournament.getBracket().get(0).getMatches().values()){ + if(teams.size() > 0){ + int teamIndex = random.nextInt(0, teams.size()); + match.getTeamScores().put(teams.get(teamIndex), 0); + teams.remove(teamIndex); + } + else{ + break; + } + } + } + tournamentRepository.save(tournament); + return tournament; + } /* Weitere Methoden: UpdateTeamScore Marius diff --git a/sth-backend/src/test/java/hdm/mi/sthbackend/dummyObjects/dummyTournaments.java b/sth-backend/src/test/java/hdm/mi/sthbackend/dummyObjects/dummyTournaments.java new file mode 100644 index 0000000000000000000000000000000000000000..2d5c9ee56ad0233c90934f963d0e7c71bedf0712 --- /dev/null +++ b/sth-backend/src/test/java/hdm/mi/sthbackend/dummyObjects/dummyTournaments.java @@ -0,0 +1,48 @@ +package hdm.mi.sthbackend.dummyObjects; + +import hdm.mi.sthbackend.model.Tournament; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.UUID; + +public class dummyTournaments { + private final Logger log = LogManager.getLogger("dummyTournaments"); + public static UUID dummyTournamentId = UUID.randomUUID(); + + public static Tournament t1 = new Tournament(dummyTournamentId, "TestTournament", new ArrayList<>(), + new ArrayList<>(Arrays.asList( + UUID.randomUUID(), UUID.randomUUID()))); + public static Tournament t2 = new Tournament(dummyTournamentId, "TestTournament", new ArrayList<>(), + new ArrayList<>(Arrays.asList( + UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID()))); + public static Tournament t3 = new Tournament(dummyTournamentId, "TestTournament", new ArrayList<>(), + new ArrayList<>(Arrays.asList( + UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), + UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID()))); + public static Tournament t4 = new Tournament(dummyTournamentId, "TestTournament", new ArrayList<>(), + new ArrayList<>(Arrays.asList( + UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), + UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), + UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), + UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID()))); + public static Tournament t11 = new Tournament(dummyTournamentId, "TestTournament", new ArrayList<>(), + new ArrayList<>(Arrays.asList( + UUID.randomUUID()))); + public static Tournament t12 = new Tournament(dummyTournamentId, "TestTournament", new ArrayList<>(), + new ArrayList<>(Arrays.asList( + UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID()))); + public static Tournament t13 = new Tournament(dummyTournamentId, "TestTournament", new ArrayList<>(), + new ArrayList<>(Arrays.asList( + UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), + UUID.randomUUID()))); + public static Tournament t14 = new Tournament(dummyTournamentId, "TestTournament", new ArrayList<>(), + new ArrayList<>(Arrays.asList( + UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), + UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), + UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), + UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID()))); + +} diff --git a/sth-backend/src/test/java/hdm/mi/sthbackend/serviceTests/CreacteBracketTest.java b/sth-backend/src/test/java/hdm/mi/sthbackend/serviceTests/CreacteBracketTest.java new file mode 100644 index 0000000000000000000000000000000000000000..03fc5eaea90fd0f086008b70917eebffd977e17b --- /dev/null +++ b/sth-backend/src/test/java/hdm/mi/sthbackend/serviceTests/CreacteBracketTest.java @@ -0,0 +1,108 @@ +package hdm.mi.sthbackend.serviceTests; + +import hdm.mi.sthbackend.exeptions.BracketAlreadyInitializedException; +import hdm.mi.sthbackend.exeptions.InsufficientTeamsException; +import hdm.mi.sthbackend.exeptions.TournamentIdNotFoundException; +import hdm.mi.sthbackend.model.Tournament; +import hdm.mi.sthbackend.repository.ITournamentRepository; +import hdm.mi.sthbackend.service.TournamentService; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import static hdm.mi.sthbackend.dummyObjects.dummyTournaments.*; +import static org.mockito.Mockito.*; + +import java.util.Optional; + +@ExtendWith(MockitoExtension.class) +public class CreacteBracketTest { + + @Mock + public ITournamentRepository tournamentRepository; + @InjectMocks + public TournamentService tournamentService; + + private final Logger log = LogManager.getLogger("CreateBracketTest"); + + @Test + public void create2TeamBracketTest() throws TournamentIdNotFoundException, InsufficientTeamsException, BracketAlreadyInitializedException { + when(tournamentRepository.findById(dummyTournamentId)).thenReturn(Optional.of(t1)); + + Tournament resultTournament = tournamentService.createBracket(dummyTournamentId); + Assertions.assertEquals(1, resultTournament.getBracket().size()); + Assertions.assertEquals(1, resultTournament.getBracket().get(0).getMatches().size()); + } + @Test + public void create4TeamBracketTest() throws TournamentIdNotFoundException, InsufficientTeamsException, BracketAlreadyInitializedException { + when(tournamentRepository.findById(dummyTournamentId)).thenReturn(Optional.of(t2)); + + Tournament resultTournament = tournamentService.createBracket(dummyTournamentId); + Assertions.assertEquals(2, resultTournament.getBracket().size()); + Assertions.assertEquals(2, resultTournament.getBracket().get(0).getMatches().size()); + Assertions.assertEquals(1, resultTournament.getBracket().get(1).getMatches().size()); + } + @Test + public void create8TeamBracketTest() throws TournamentIdNotFoundException, InsufficientTeamsException, BracketAlreadyInitializedException { + when(tournamentRepository.findById(dummyTournamentId)).thenReturn(Optional.of(t3)); + + Tournament resultTournament = tournamentService.createBracket(dummyTournamentId); + Assertions.assertEquals(3, resultTournament.getBracket().size()); + Assertions.assertEquals(4, resultTournament.getBracket().get(0).getMatches().size()); + Assertions.assertEquals(2, resultTournament.getBracket().get(1).getMatches().size()); + Assertions.assertEquals(1, resultTournament.getBracket().get(2).getMatches().size()); + } + @Test + public void create16TeamBracketTest() throws TournamentIdNotFoundException, InsufficientTeamsException, BracketAlreadyInitializedException { + when(tournamentRepository.findById(dummyTournamentId)).thenReturn(Optional.of(t4)); + + Tournament resultTournament = tournamentService.createBracket(dummyTournamentId); + Assertions.assertEquals(4, resultTournament.getBracket().size()); + Assertions.assertEquals(8, resultTournament.getBracket().get(0).getMatches().size()); + Assertions.assertEquals(4, resultTournament.getBracket().get(1).getMatches().size()); + Assertions.assertEquals(2, resultTournament.getBracket().get(2).getMatches().size()); + Assertions.assertEquals(1, resultTournament.getBracket().get(3).getMatches().size()); + } + @Test + public void create1TeamBracketTest(){ + when(tournamentRepository.findById(dummyTournamentId)).thenReturn(Optional.of(t11)); + + Assertions.assertThrows(InsufficientTeamsException.class, () -> tournamentService.createBracket(dummyTournamentId)); + } + @Test + public void create3TeamBracketTest() throws TournamentIdNotFoundException, InsufficientTeamsException, BracketAlreadyInitializedException { + when(tournamentRepository.findById(dummyTournamentId)).thenReturn(Optional.of(t12)); + + Tournament resultTournament = tournamentService.createBracket(dummyTournamentId); + Assertions.assertEquals(2, resultTournament.getBracket().size()); + Assertions.assertEquals(2, resultTournament.getBracket().get(0).getMatches().size()); + Assertions.assertEquals(1, resultTournament.getBracket().get(1).getMatches().size()); + } + @Test + public void create5TeamBracketTest() throws TournamentIdNotFoundException, InsufficientTeamsException, BracketAlreadyInitializedException { + when(tournamentRepository.findById(dummyTournamentId)).thenReturn(Optional.of(t13)); + + Tournament resultTournament = tournamentService.createBracket(dummyTournamentId); + Assertions.assertEquals(3, resultTournament.getBracket().size()); + Assertions.assertEquals(4, resultTournament.getBracket().get(0).getMatches().size()); + Assertions.assertEquals(2, resultTournament.getBracket().get(1).getMatches().size()); + Assertions.assertEquals(1, resultTournament.getBracket().get(2).getMatches().size()); + } + @Test + public void create15TeamBracketTest() throws TournamentIdNotFoundException, InsufficientTeamsException, BracketAlreadyInitializedException { + when(tournamentRepository.findById(dummyTournamentId)).thenReturn(Optional.of(t14)); + + Tournament resultTournament = tournamentService.createBracket(dummyTournamentId); + Assertions.assertEquals(4, resultTournament.getBracket().size(), 4); + Assertions.assertEquals(8, resultTournament.getBracket().get(0).getMatches().size()); + Assertions.assertEquals(4, resultTournament.getBracket().get(1).getMatches().size()); + Assertions.assertEquals(2, resultTournament.getBracket().get(2).getMatches().size()); + Assertions.assertEquals(1, resultTournament.getBracket().get(3).getMatches().size()); + } + +} diff --git a/sth-backend/src/test/java/hdm/mi/sthbackend/serviceTests/FillBracketRandomTest.java b/sth-backend/src/test/java/hdm/mi/sthbackend/serviceTests/FillBracketRandomTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d97782197bdbb681a0a3a596e05d1f41a0895805 --- /dev/null +++ b/sth-backend/src/test/java/hdm/mi/sthbackend/serviceTests/FillBracketRandomTest.java @@ -0,0 +1,52 @@ +package hdm.mi.sthbackend.serviceTests; + +import hdm.mi.sthbackend.exeptions.TournamentIdNotFoundException; +import hdm.mi.sthbackend.model.BracketRound; +import hdm.mi.sthbackend.model.Match; +import hdm.mi.sthbackend.model.Tournament; +import hdm.mi.sthbackend.repository.ITournamentRepository; +import hdm.mi.sthbackend.service.TournamentService; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.ArrayList; +import java.util.Optional; +import java.util.UUID; + +import static hdm.mi.sthbackend.dummyObjects.dummyTournaments.*; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class FillBracketRandomTest { + private final Logger log = LogManager.getLogger("FillBracketTest"); + + @Mock + ITournamentRepository tournamentRepository; + @InjectMocks + TournamentService tournamentService; + + + @Test + public void fill2TeamBracketRandomTest() throws TournamentIdNotFoundException { + Tournament t1withMatches = t1; + t1withMatches.setBracket(new ArrayList<>()); + t1withMatches.getBracket().add(new BracketRound(0)); + UUID matchId = UUID.randomUUID(); + t1withMatches.getBracket().get(0).getMatches().put(matchId, new Match(matchId)); + + when(tournamentRepository.findById(dummyTournamentId)).thenReturn(Optional.of(t1withMatches)); + Tournament t1Filled = tournamentService.fillBracketRandom(dummyTournamentId); + + int teamSum = t1Filled.getBracket() + .stream() + .flatMap(bracketRound -> bracketRound.getMatches().values().stream()) + .map(match -> match.getTeamScores().size()) + .reduce(0, Integer::sum); + } + +} diff --git a/sth-backend/src/test/java/hdm/mi/sthbackend/t1.java b/sth-backend/src/test/java/hdm/mi/sthbackend/t1.java new file mode 100644 index 0000000000000000000000000000000000000000..a1aaf59116e5c93362e97b763a86cf45bed5cf23 --- /dev/null +++ b/sth-backend/src/test/java/hdm/mi/sthbackend/t1.java @@ -0,0 +1,88 @@ +package hdm.mi.sthbackend; + +import hdm.mi.sthbackend.controller.TournamentController; +import hdm.mi.sthbackend.dto.MatchDTO; +import hdm.mi.sthbackend.exeptions.MatchIdNotFoundException; +import hdm.mi.sthbackend.mapper.ModelToDTOMapper; +import hdm.mi.sthbackend.model.Match; +import hdm.mi.sthbackend.repository.IMatchRepository; +import hdm.mi.sthbackend.repository.IPlayerRepository; +import hdm.mi.sthbackend.repository.ITeamRepository; +import hdm.mi.sthbackend.repository.ITournamentRepository; +import hdm.mi.sthbackend.service.TournamentService; +import hdm.mi.sthbackend.types.TeamMatchScore; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.beans.factory.annotation.Autowired; +import java.util.HashMap; +import java.util.Optional; +import java.util.UUID; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + + +import static org.hamcrest.Matchers.is; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@AutoConfigureMockMvc +@RunWith(MockitoJUnitRunner.class) +@SpringBootTest +public class t1 { + + @Mock + private IMatchRepository matchRepository; + + @Mock + private ITeamRepository teamRepository; + + @Mock + private IPlayerRepository playerRepository; + + @Mock + private ITournamentRepository tournamentRepository; + + @Mock + private ModelToDTOMapper mapper; + + @InjectMocks + private TournamentController tournamentController; + + @Autowired + private MockMvc mockMvc; + + @Test + public void assignTeamToMatch() throws Exception { + UUID matchId = UUID.randomUUID(); + + MatchDTO testMatch = new MatchDTO(); + testMatch.setTeamScores(new HashMap<>()); + testMatch.setComment("Sample comment"); + + // Erstelle ein gültiges Match-Objekt für die Mock-Antwort + Match mockMatch = new Match(matchId, new HashMap<>(), UUID.randomUUID(), "Some comment", UUID.randomUUID()); + + when(matchRepository.findById(matchId)).thenReturn(Optional.of(mockMatch)); + when(mapper.mapToMatchDTO(any())).thenReturn(testMatch); + + + verify(matchRepository, times(1)).findById(matchId); + verify(matchRepository, times(1)).save(any()); + verify(mapper, times(1)).mapToMatchDTO(any()); + } + } diff --git a/sth-frontend/src/features/landingpage/Landingpage.jsx b/sth-frontend/src/features/landingpage/Landingpage.jsx new file mode 100644 index 0000000000000000000000000000000000000000..ec6875aa5dc9087188f0499b3f7f3b6ce5679dcc --- /dev/null +++ b/sth-frontend/src/features/landingpage/Landingpage.jsx @@ -0,0 +1,17 @@ +import React from "react"; +import { Features, Footer, Header, Modes, Navbar, Partner } from './components' + +const Landingpage = () => { + return ( + <div className='bg-darkGray -z-50'> + <Navbar/> + <Header/> + <Modes/> + <Features/> + <Partner/> + <Footer/> + </div> + ) +} + +export default Landingpage \ No newline at end of file diff --git a/sth-frontend/src/features/landingpage/components/Features.jsx b/sth-frontend/src/features/landingpage/components/Features.jsx new file mode 100644 index 0000000000000000000000000000000000000000..22c7b72344ae334805ea87fcc6e5d7bbbfb32838 --- /dev/null +++ b/sth-frontend/src/features/landingpage/components/Features.jsx @@ -0,0 +1,30 @@ +import React from "react"; + +const Features = () => { + return ( + <div className='flex flex-col items-center gap-10 w-[1065px] h-[450px] ml-[15%] mt-10 bg-deepPurple'> + <div className='flex justify-center text-darkGray font-Outfit-ExtraBold font-bold text-7xl uppercase'> + How it works + </div> + <div className='flex gap-2'> + <div className='flex flex-col w-[345px] h-[250px] bg-lightGray rounded-2xl'> + <div className='text-darkGray text-center font-Outfit-Regular font-medium text-2xl'> + Bring People together and Game + </div> + </div> + <div className='flex flex-col w-[345px] h-[250px] bg-lightGray rounded-2xl'> + <div className='text-darkGray text-center font-Outfit-Regular font-medium text-2xl'> + Create your type of Tournament and let our Engine handle your Experience. Anything is possible + </div> + </div> + <div className='flex flex-col w-[345px] h-[250px] bg-lightGray rounded-2xl'> + <div className='text-darkGray text-center font-Outfit-Regular font-medium text-2xl'> + Bring on your Winner of the Tournament and give out MVP Medals for the best Players + </div> + </div> + </div> + </div> + ) +} + +export default Features \ No newline at end of file diff --git a/sth-frontend/src/features/landingpage/components/Footer.jsx b/sth-frontend/src/features/landingpage/components/Footer.jsx new file mode 100644 index 0000000000000000000000000000000000000000..20d5371274891905286fd9f39dcd8a5af809ec6a --- /dev/null +++ b/sth-frontend/src/features/landingpage/components/Footer.jsx @@ -0,0 +1,13 @@ +import React from "react"; + +const Footer = () => { + return ( + <div className='w-[1065px] h-[325px] bg-deepPurple ml-[15%]'> + <div className=''> + TJONE + </div> + </div> + ) +} + +export default Footer \ No newline at end of file diff --git a/sth-frontend/src/features/landingpage/components/Header.jsx b/sth-frontend/src/features/landingpage/components/Header.jsx new file mode 100644 index 0000000000000000000000000000000000000000..0b87efbe57e0c2db7e57a97fef932cbdf903e0a2 --- /dev/null +++ b/sth-frontend/src/features/landingpage/components/Header.jsx @@ -0,0 +1,44 @@ +import React from "react"; + +const Header = () => { + return ( + <div className='flex gap-5 pl-4 w-[1065px] h-[600px] ml-[15%]'> + <div> + <div className='flex justify-center py-2'> + <svg + className='w-1065 h-600 absolute left-1/2 transform -translate-x-1/2 top-1616 overflow-visible z-0' + width="1065" + height="600" + viewBox="0 0 1065 600" + fill="none" + xmlns="http://www.w3.org/2000/svg"> + <path d="M0 0H1065L975 471L0 600V0Z" fill="#916CFA"></path> + </svg> + <div className='flex flex-col gap-2.5 max-w-2xl'> + <div + className='z-10 relative text-darkGray text-left font-Outfit-ExtraBold font-sans text-6xl leading-tight font-extrabold uppercase'> + Launch your Tournament + </div> + <div + className='z-10 relative text-teal-300 text-left font-Outfit-Bold font-sans text-xl leading-tight font-bold uppercase'> + Let the Games begin + </div> + <div className='z-10 relative text-lightGray text-left font-Outfit-Regular font-sans text-2xl '> + Lorem ipsum Beschreibung oder so kann dahin aber muss ich noch überlegen ob + das Sinn macht aber hier als Placeholder + </div> + <button className='w-40 z-10 relative rounded-3xl border-2 px-12 py-2 text-lightGray'> + Create + </button> + <img src='' alt='Group'/> + </div> + </div> + <div className='flex flex-row gap-2.5 w-2xl'> + </div> + <div className='z-10 relative bg-deepPurple rounded-full w-64 h-64 filter blur-[250px] left-1/2'></div> + </div> + </div> + ) +} + +export default Header \ No newline at end of file diff --git a/sth-frontend/src/features/landingpage/components/Modes.jsx b/sth-frontend/src/features/landingpage/components/Modes.jsx new file mode 100644 index 0000000000000000000000000000000000000000..d715ee175527d448ed180ccce6b1d2f55287e4b9 --- /dev/null +++ b/sth-frontend/src/features/landingpage/components/Modes.jsx @@ -0,0 +1,38 @@ +import React from "react"; + +const Modes = () => { + return ( + <div> + <div className='flex gap-10 justify-around items-center w-[1065px] h-[540px] ml-[15%] mt-10'> + <div className='flex gap-5'> + <div className='bg-lightGray h-[370px] w-[75px]'> + Card 1 + </div> + <div className='bg-lightGray h-[370px] w-[75px]'> + Card 2 + </div> + <div className='bg-lightGray h-[370px] w-[75px]'> + Card 3 + </div> + <div className='bg-lightGray h-[370px] w-[75px]'> + Card 4 + </div> + </div> + <div className='flex flex-col w-[525px] h-[370px] gap-10'> + <div className='text-lightGray font-Outfit-ExtraBold font-bold text-7xl uppercase '> + Everything <br/> is possible + </div> + <div className='text-lightGray font-Outfit-Regular font-medium text-xl max-w-md'> + Create any Type of Tournament possible. With our new Tournament Engine + everything is in the Hand of you. Grab your Friends and lets GAME + </div> + </div> + </div> + {/*<div className='h-1'>*/} + {/* <div className='z-10 relative left-3/4 bg-deepPurple rounded-full w-[496px] h-[485px] filter blur-[250px]'></div>*/} + {/*</div>*/} + </div> + ) +} + +export default Modes \ No newline at end of file diff --git a/sth-frontend/src/features/landingpage/components/Navbar.jsx b/sth-frontend/src/features/landingpage/components/Navbar.jsx new file mode 100644 index 0000000000000000000000000000000000000000..be6f7082d1f5b02789d63c3efec5206c282629d3 --- /dev/null +++ b/sth-frontend/src/features/landingpage/components/Navbar.jsx @@ -0,0 +1,56 @@ +import React, {useState} from "react"; +import {Link} from "react-router-dom"; + +const Navbar = () => { + const [darkMode, setDarkMode] = useState(true) + + const toggleDarkMode = () => { + setDarkMode(!darkMode) + }; + + const backgroundColorClasses = darkMode ? 'bg-darkGray': 'bg-lightGray'; + const textClass = darkMode ? 'text-lightGray': 'text-darkGray'; + const moonOrSunIcon = darkMode ? '☾': '☼' ; + + + return ( + <div className={`${backgroundColorClasses} fixed top-0 z-50`}> + <nav className={`flex ${textClass} gap-3 items-center justify-center`}> + <div className='flex flex-row gap-5 items-center justify-center flex-shrink-0 w-screen relative'> + <div + className='flex flex-row flex-shrink-0 gap-0 items-center justify-start relative p-2.5 text-5xl'>TJ1 + </div> + <div className='flex flex-col items-start justify-start flex-shrink-0 relative gap-2.5 p-2.5'> + <ul className='flex flex-row items-start justify-start flex-shrink-0 w-1/3 relative px-4 gap-8 text-xl'> + <li> + <Link to="/landingpage">Home</Link> + </li> + <li> + <Link to="/landingpage">Tournaments</Link> + </li> + <li> + <Link to="/landingpage">Support</Link> + </li> + <li> + <Link to="/landingpage">Options</Link> + </li> + </ul> + </div> + <div onClick={toggleDarkMode} className='cursor-pointer text-3xl'> + {moonOrSunIcon} + </div> + <div className='flex flex-row items-center justify-start flex-shrink-0 relative gap-5'> + <button className='rounded-3xl border-2 px-12 py-2 bg-lightGray text-darkGray'> + Create + </button> + <button className='rounded-3xl border-2 px-12 py-2'> + Login + </button> + </div> + </div> + </nav> + </div> + ) +} + +export default Navbar \ No newline at end of file diff --git a/sth-frontend/src/features/landingpage/components/Partner.jsx b/sth-frontend/src/features/landingpage/components/Partner.jsx new file mode 100644 index 0000000000000000000000000000000000000000..1146e386549db9f7d512f6a0fb13ad4c382b3d82 --- /dev/null +++ b/sth-frontend/src/features/landingpage/components/Partner.jsx @@ -0,0 +1,21 @@ +import React from "react"; + +const Partner = () => { + return ( + <div className='flex flex-col items-center gap-10 w-[1065px] h-[285px] ml-[15%]'> + <div className='flex justify-center text-lightGray text-7xl text-Outfit-ExtraBold font-bold'> + Our Partner + </div> + <div className='flex gap-3.5 text-lightGray'> + <div> + League of Legends + </div> + <div> + Bierpong League + </div> + </div> + </div> + ) +} + +export default Partner \ No newline at end of file diff --git a/sth-frontend/src/features/landingpage/components/index.jsx b/sth-frontend/src/features/landingpage/components/index.jsx new file mode 100644 index 0000000000000000000000000000000000000000..ca5c7819b030d9bafd2715a737ad38b77bae007e --- /dev/null +++ b/sth-frontend/src/features/landingpage/components/index.jsx @@ -0,0 +1,6 @@ +export { default as Features } from "/src/features/landingpage/components/Features"; +export { default as Footer } from "/src/features/landingpage/components/Footer"; +export { default as Header } from "/src/features/landingpage/components/Header"; +export { default as Modes } from "/src/features/landingpage/components/Modes"; +export { default as Navbar } from "/src/features/landingpage/components/Navbar"; +export { default as Partner } from "/src/features/landingpage/components/Partner"; diff --git a/sth-frontend/src/utils/router.jsx b/sth-frontend/src/utils/router.jsx index e5eb6de3abc64ddc4656d0190ec3a01de4cc4331..665fcf1b4ea129a5b3bb3f2334be105969bfd18d 100644 --- a/sth-frontend/src/utils/router.jsx +++ b/sth-frontend/src/utils/router.jsx @@ -1,15 +1,20 @@ import {createBrowserRouter} from "react-router-dom"; import Tournament from "../features/tournament/components/Tournament"; import App from "../App"; +import Landingpage from "../features/landingpage/Landingpage"; const router = createBrowserRouter([ { path: "/", - element: <App />, + element: <App/>, children: [ {path: "", element: <Tournament/>}, ] }, + { + path:"/landingpage", + element: <Landingpage />, + } ]) diff --git a/sth-frontend/tailwind.config.js b/sth-frontend/tailwind.config.js index fe3b0216ac3a2766f08477df2df8f65d8e0b5cd5..bf5c54af2d9db76d6bfd00a1a9ed22673dc08fce 100644 --- a/sth-frontend/tailwind.config.js +++ b/sth-frontend/tailwind.config.js @@ -6,7 +6,11 @@ module.exports = { ], theme: { extend: { - + colors: { + 'darkGray': '#272525', + 'deepPurple': '#916CFA', + 'lightGray': '#D9D9D9', + } }, }, plugins: [],