diff --git a/backend/pokerogue/src/main/java/com/pokerogue/helper/data/PokemonValidator.java b/backend/pokerogue/src/main/java/com/pokerogue/helper/data/PokemonValidator.java index 1da817eec..578bad176 100644 --- a/backend/pokerogue/src/main/java/com/pokerogue/helper/data/PokemonValidator.java +++ b/backend/pokerogue/src/main/java/com/pokerogue/helper/data/PokemonValidator.java @@ -31,8 +31,8 @@ class PokemonValidator { private static final String DELIMITER = "_"; private static final String EMPTY_ABILITY = "none"; private static final IntPredicate isExpectedIdLetter = character -> isLowerCase(character) - || isDigit(character) - || isDelimiter(character); + || isDigit(character) + || isDelimiter(character); private PokemonValidator() { } @@ -46,8 +46,8 @@ static void validatePokemonSize(int pokemonCount) { static void validatePokemonIdFormat(List pokemonIds) { Predicate isExpectedLetter = id -> id.codePoints().allMatch(isExpectedIdLetter); - Predicate isDelimiterSeparated = id -> id.contains(DELIMITER + DELIMITER); - Predicate isDelimiterInPlace = id -> id.startsWith(DELIMITER) || id.endsWith(DELIMITER); + Predicate isDelimiterSeparated = id -> !id.contains(DELIMITER + DELIMITER); + Predicate isDelimiterInPlace = id -> !id.startsWith(DELIMITER) || id.endsWith(DELIMITER); String message = "아이디: %s는 아이디 규칙에 맞지 않습니다."; @@ -147,22 +147,6 @@ static void validateTotalAbilityCount(List pokemons) { } } - static void validateTotalAbilityDuplication(List pokemons) { - Predicate isAbilityDisjoint = pokemon -> { - List totalAbilityIds = pokemon.getNormalAbilityIds(); - totalAbilityIds.add(pokemon.getHiddenAbilityId()); - totalAbilityIds.add(pokemon.getPassiveAbilityId()); - - Set uniqueIds = new HashSet<>(totalAbilityIds); - - return totalAbilityIds.size() == uniqueIds.size(); - }; - - for (Pokemon pokemon : pokemons) { - validate(isAbilityDisjoint, pokemon, ErrorMessage.POKEMON_ABILITY_DUPLICATION); - } - } - static void validateStatValueRange(List pokemons) { Predicate isStatsInRange = pokemon -> { List stats = List.of( @@ -271,5 +255,4 @@ private static boolean isInRange(int target, int min, int max) { private static boolean isDelimiter(int character) { return DELIMITER.charAt(0) == character; } - } diff --git a/backend/pokerogue/src/main/java/com/pokerogue/helper/move/data/MoveFlag.java b/backend/pokerogue/src/main/java/com/pokerogue/helper/move/data/MoveFlag.java index 8ab99da76..96c7910ac 100644 --- a/backend/pokerogue/src/main/java/com/pokerogue/helper/move/data/MoveFlag.java +++ b/backend/pokerogue/src/main/java/com/pokerogue/helper/move/data/MoveFlag.java @@ -12,6 +12,7 @@ public enum MoveFlag { MAKES_CONTACT("makes_contact"), IGNORE_PROTECT("ignore_protect"), IGNORE_VIRTUAL("ignore_virtual"), + IGNORE_SUBSTITUTE("ignore_substitute"), SOUND_BASED("sound_based"), HIDE_USER("hide_user"), HIDE_TARGET("hide_target"), @@ -27,7 +28,6 @@ public enum MoveFlag { TRIAGE_MOVE("triage_move"), IGNORE_ABILITIES("ignore_abilities"), CHECK_ALL_HITS("check_all_hits"), - IGNORE_SUBSTITUTE("ignore_substitute"), REDIRECT_COUNTER("redirect_counter"), ; diff --git a/backend/pokerogue/src/test/java/com/pokerogue/data/AbilityDataTest.java b/backend/pokerogue/src/test/java/com/pokerogue/data/AbilityDataTest.java new file mode 100644 index 000000000..2ea0077bd --- /dev/null +++ b/backend/pokerogue/src/test/java/com/pokerogue/data/AbilityDataTest.java @@ -0,0 +1,105 @@ +package com.pokerogue.data; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.pokerogue.data.pattern.DataPattern; +import com.pokerogue.environment.repository.MongoRepositoryTest; +import com.pokerogue.helper.ability.data.Ability; +import com.pokerogue.helper.ability.repository.AbilityRepository; +import com.pokerogue.helper.pokemon.data.Pokemon; +import com.pokerogue.helper.pokemon.repository.PokemonRepository; +import java.util.HashSet; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +public class AbilityDataTest extends MongoRepositoryTest { + + private final PokemonRepository pokemonRepository; + private final List abilities; + + @Autowired + public AbilityDataTest(AbilityRepository abilityRepository, PokemonRepository pokemonRepository) { + this.pokemonRepository = pokemonRepository; + this.abilities = abilityRepository.findAll(); + } + + @Test + @DisplayName("Ability의 아이디는 영어 소문자와 단일 _ 로만 이루어져 있다.") + void id_validateAbilityIds() { + List notMatchAbilityIds = abilities.stream() + .map(Ability::getId) + .filter(DataPattern.ID_PATTERN::isNotMatch) + .toList(); + + assertThat(notMatchAbilityIds).isEmpty(); + } + + @Test + @DisplayName("Ability 속 포켓몬 id들은 전부 존재한다.") + void pokemonId_validatePokemonIds() { + List pokemonIds = abilities.stream() + .map(Ability::getPokemonIds) + .flatMap(List::stream) + .toList(); + List pokemons = pokemonRepository.findAll(); + List ids = pokemons.stream() + .map(Pokemon::getId) + .toList(); + + List notMatchIds = pokemonIds.stream() + .filter(pokemonId -> !ids.contains(pokemonId)) + .toList(); + + assertThat(notMatchIds).isEmpty(); + } + + @Test + @DisplayName("모든 능력 설명이 한글이 포함되어 구성하고 있다.") + void description_compositionWithKorean() { + List notMatchDescription = abilities.stream() + .map(Ability::getDescription) + .filter(DataPattern.DESCRIPTION_PATTERN::isNotMatch) + .toList(); + + assertThat(notMatchDescription).isEmpty(); + } + + @Test + @DisplayName("모든 KoName이 적어도 한 자의 한글과 영어로 이루어져 있다.") + void koName_compositionWith_AtLeastOneKorean() { + List notMatchNames = abilities.stream() + .map(Ability::getKoName) + .filter(DataPattern.KO_NAME_PATTERN::isNotMatch) + .toList(); + + assertThat(notMatchNames).isEmpty(); + } + + @Test + @DisplayName("모든 Name이 영어로 이루어져 있다.") + void name_compositionWith_English() { + List notMatchNames = abilities.stream() + .map(Ability::getName) + .filter(DataPattern.NAME_PATTERN::isNotMatch) + .toList(); + + assertThat(notMatchNames).isEmpty(); + } + + @Test + @DisplayName("Ability의 pokemon Id들은 중복되지 않는다.") + void pokemonIds_NotDuplicated() { + List duplicatedPokemonAbilityIds = abilities.stream() + .filter(ability -> isDuplicated(ability.getPokemonIds())) + .map(Ability::getId) + .toList(); + + assertThat(duplicatedPokemonAbilityIds).isEmpty(); + } + + private boolean isDuplicated(List pokemonIds) { + return pokemonIds.size() != new HashSet<>(pokemonIds).size(); + } +} diff --git a/backend/pokerogue/src/test/java/com/pokerogue/data/BiomeDataTest.java b/backend/pokerogue/src/test/java/com/pokerogue/data/BiomeDataTest.java new file mode 100644 index 000000000..656af54d9 --- /dev/null +++ b/backend/pokerogue/src/test/java/com/pokerogue/data/BiomeDataTest.java @@ -0,0 +1,206 @@ +package com.pokerogue.data; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.pokerogue.data.pattern.DataPattern; +import com.pokerogue.environment.repository.MongoRepositoryTest; +import com.pokerogue.helper.biome.data.Biome; +import com.pokerogue.helper.biome.data.NativePokemon; +import com.pokerogue.helper.biome.data.Tier; +import com.pokerogue.helper.biome.data.Trainer; +import com.pokerogue.helper.biome.repository.BiomeRepository; +import com.pokerogue.helper.pokemon.data.Pokemon; +import com.pokerogue.helper.pokemon.repository.PokemonRepository; +import com.pokerogue.helper.type.data.Type; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +public class BiomeDataTest extends MongoRepositoryTest { + + private final List biomes; + private final List pokemonIds; + + @Autowired + public BiomeDataTest(BiomeRepository biomeRepository, PokemonRepository pokemonRepository) { + this.biomes = biomeRepository.findAll(); + + List pokemons = pokemonRepository.findAll(); + this.pokemonIds = pokemons.stream() + .map(Pokemon::getId) + .toList(); + } + + @Test + @DisplayName("Biome의 아이디는 영어 소문자와 단일 _ 로만 이루어져 있다. ") + void id_validateBiomeIds() { + List notMatchBiomeIds = biomes.stream() + .map(Biome::getId) + .filter(DataPattern.ID_PATTERN::isNotMatch) + .toList(); + + assertThat(notMatchBiomeIds).isEmpty(); + } + + @Test + @DisplayName("Biome의 name은 영어 문자로 이루어져 있다.") + void name_compositionWith_English() { + List notMatchNames = biomes.stream() + .map(Biome::getName) + .filter(DataPattern.BIOME_NAME_PATTERN::isNotMatch) + .filter(name -> !name.equals("???")) + .toList(); + + assertThat(notMatchNames).isEmpty(); + } + + @Test + @DisplayName("Biome의 koName은 한글이 포함되어 구성하고 있다.") + void koName_compositionWith_AtLeastOneKorean() { + List notMatchNames = biomes.stream() + .map(Biome::getKoName) + .filter(this::isNotMatchKoNamePattern) + .toList(); + + assertThat(notMatchNames).isEmpty(); + } + + @Test + @DisplayName("Biome의 타입들은 전부 Enum Type 안에 들어가있다.") + void type_isInEnumType() { + assertThat(biomes.stream() + .flatMap(biome -> biome.getTypes().stream())) + .allMatch(type -> type.getDeclaringClass() + .equals(Type.class)); + } + + @Test + @DisplayName("Biome의 Tier 들은 전부 Enum Tier 안에 들어가있다.") + void tier_isInEnumTier() { + assertThat(biomes.stream() + .flatMap(biome -> biome.getNativePokemons().stream() + .map(NativePokemon::getTier) + ) + ).allMatch(tier -> tier.getDeclaringClass().equals(Tier.class)); + } + + @Test + @DisplayName("Biome의 트레이너들의 타입들은 전부 Enum Type 안에 들어가있다.") + void trainerType_isInEnumType() { + List types = biomes.stream() + .flatMap(biome -> biome.getTrainers().stream() + .map(Trainer::getTypes)) + .flatMap(Collection::stream) + .toList(); + + assertThat(types) + .allMatch(type -> type.getDeclaringClass().equals(Type.class)); + } + + @Test + @DisplayName("Biome의 native Pokemon 속 pokemonId들이 전부 존재한다.") + void nativePokemonId_isInPokemonCollection() { + List nativePokemons = biomes.stream() + .map(Biome::getNativePokemons) + .flatMap(List::stream) + .toList(); + + List nativePokemonIds = nativePokemons.stream() + .map(NativePokemon::getPokemonIds) + .flatMap(List::stream) + .toList(); + + List notMatchIds = nativePokemonIds.stream() + .filter(nativeId -> !pokemonIds.contains(nativeId)) + .toList(); + + assertThat(notMatchIds).isEmpty(); + } + + @Test + @DisplayName("Biome Trainer 의 name 필드가 영어 소문자 혹은 연속되지 않는 _ 로 이루어져 있다.") + void name_isCompositionWithEnglish() { + List trainers = biomes.stream() + .map(Biome::getTrainers) + .flatMap(List::stream) + .toList(); + + List notMatchTrainerNames = trainers.stream() + .map(Trainer::getName) + .filter(DataPattern.NAME_PATTERN::isNotMatch) + .toList(); + + assertThat(notMatchTrainerNames).isEmpty(); + } + + @Test + @DisplayName("Biome Trainer 의 KoName 필드가 한국어로 이루어져있다.") + void koName_isCompositionWithKorean() { + List trainers = biomes.stream() + .map(Biome::getTrainers) + .flatMap(List::stream) + .toList(); + + List notMatchTrainerNames = trainers.stream() + .map(Trainer::getKoName) + .filter(this::isNotMatchKoNamePattern) + .toList(); + + assertThat(notMatchTrainerNames).isEmpty(); + } + + @Test + @DisplayName("Biome 의 nativePokemon Id들은 중복되지 않는다.") + void nativePokemonIds_NotDuplicate() { + List> nativePokemons = biomes.stream() + .map(Biome::getNativePokemons) + .toList(); + + List duplicatedNativePokemon = new ArrayList<>(); + + nativePokemons.forEach(natives -> + natives.forEach(nativePokemon -> { + if (isDuplicated(nativePokemon.getPokemonIds())) { + duplicatedNativePokemon.add(nativePokemon); + } + }) + ); + + assertThat(duplicatedNativePokemon).isEmpty(); + } + + @Test + @DisplayName("Biome의 trainer PokemonId 들은 중복되지 않는다.") + void trainerPokemonIds_NotDuplicated() { + List> trainers = biomes.stream() + .map(Biome::getTrainers) + .toList(); + + List duplicatedPokemonTrainer = new ArrayList<>(); + + trainers.forEach(trainerList -> + trainerList.forEach(trainer -> { + if (isDuplicated(trainer.getPokemonIds())) { + duplicatedPokemonTrainer.add(trainer); + } + }) + ); + + assertThat(duplicatedPokemonTrainer).isEmpty(); + } + + private boolean isNotMatchKoNamePattern(String koName) { + if ("???".equals(koName)) { + return false; + } + return DataPattern.KO_NAME_PATTERN.isNotMatch(koName); + } + + private boolean isDuplicated(List pokemonIds) { + return pokemonIds.size() != new HashSet<>(pokemonIds).size(); + } +} diff --git a/backend/pokerogue/src/test/java/com/pokerogue/data/MoveDataTest.java b/backend/pokerogue/src/test/java/com/pokerogue/data/MoveDataTest.java new file mode 100644 index 000000000..b36fe6320 --- /dev/null +++ b/backend/pokerogue/src/test/java/com/pokerogue/data/MoveDataTest.java @@ -0,0 +1,196 @@ +package com.pokerogue.data; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.pokerogue.data.pattern.DataPattern; +import com.pokerogue.environment.repository.MongoRepositoryTest; +import com.pokerogue.helper.move.data.Move; +import com.pokerogue.helper.move.data.MoveCategory; +import com.pokerogue.helper.move.data.MoveFlag; +import com.pokerogue.helper.move.data.MoveTarget; +import com.pokerogue.helper.move.repository.MoveRepository; +import com.pokerogue.helper.pokemon.data.Pokemon; +import com.pokerogue.helper.pokemon.repository.PokemonRepository; +import com.pokerogue.helper.type.data.Type; +import java.util.HashSet; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +public class MoveDataTest extends MongoRepositoryTest { + + private final List moves; + private final List pokemonIds; + + @Autowired + public MoveDataTest(MoveRepository moveRepository, PokemonRepository pokemonRepository) { + this.moves = moveRepository.findAll(); + + List pokemons = pokemonRepository.findAll(); + this.pokemonIds = pokemons.stream() + .map(Pokemon::getId) + .toList(); + } + + @Test + @DisplayName("Move의 아이디는 영어 소문자와 단일 _로만 이루어져 있다.") + void id_validateMoveIds() { + List notMatchMoveIds = moves.stream() + .map(Move::getId) + .filter(DataPattern.MOVE_ID_PATTERN::isNotMatch) + .toList(); + + assertThat(notMatchMoveIds).isEmpty(); + } + + @Test + @DisplayName("Move의 name은 영어와 특수문자 그리고 공백으로 이루어져있다.") + void name_validateMoveNames() { + List notMatchNames = moves.stream() + .map(Move::getName) + .filter(DataPattern.NAME_PATTERN::isNotMatch) + .toList(); + + assertThat(notMatchNames).isEmpty(); + } + + @Test + @DisplayName("Move의 koName은 적어도 하나의 한글이 포함되어 있다.") + void koName_validateMoveKoNames() { + List notMatchKoNames = moves.stream() + .map(Move::getKoName) + .filter(DataPattern.KO_NAME_PATTERN::isNotMatch) + .toList(); + + assertThat(notMatchKoNames).isEmpty(); + } + + @Test + @DisplayName("Move의 Type은 모두 Type Enum 이다.") + void type_validateIsTypeEnum() { + assertThat(moves.stream() + .allMatch(move -> move.getType().getDeclaringClass().equals(Type.class))) + .isTrue(); + } + + @Test + @DisplayName("Move의 MoveTarget은 모두 Move Target Enum 이다.") + void target_validateIsTargetEnum() { + assertThat(moves.stream() + .allMatch(move -> move.getMoveTarget() + .getDeclaringClass() + .equals(MoveTarget.class))) + .isTrue(); + } + + @Test + @DisplayName("Move의 MoveCategory는 모두 Move Category Enum 이다.") + void moveCategory_validateIsMoveCategoryEnum() { + assertThat(moves.stream() + .allMatch(move -> move.getMoveCategory() + .getDeclaringClass() + .equals(MoveCategory.class))) + .isTrue(); + } + + @Test + @DisplayName("Move의 MoveFlag는 모두 Move Flag Enum 이다") + void moveFlag_validateIsMoveFlagEnum() { + assertThat(moves.stream() + .flatMap(move -> move.getFlags().stream()) + ) + .allMatch(flag -> flag.getDeclaringClass() + .equals(MoveFlag.class)); + } + + @Test + @DisplayName("Move의 Power는 -1이거나 양수의 숫자이다.") + void power_validateIsMinusOne_OrPositiveDigit() { + List notMatchPowers = moves.stream() + .map(Move::getPower) + .filter(this::isNotAvailableDigit) + .toList(); + + assertThat(notMatchPowers).isEmpty(); + } + + @Test + @DisplayName("Move의 Accuracy는 -1이거나 양수의 숫자이다.") + void accuracy_validateIsMinusOne_OrPositiveDigit() { + List notMatchAccuracy = moves.stream() + .map(Move::getAccuracy) + .filter(this::isNotAvailableDigit) + .toList(); + + assertThat(notMatchAccuracy).isEmpty(); + } + + @Test + @DisplayName("Move의 PowerPoint는 -1이거나 양수의 숫자이다.") + void powerPoint_validateIsMinusOne_OrPositiveDigit() { + List notMatchPowerPoint = moves.stream() + .map(Move::getPowerPoint) + .filter(this::isNotAvailableDigit) + .toList(); + + assertThat(notMatchPowerPoint).isEmpty(); + } + + @Test + @DisplayName("Move의 effectChance는 -1이거나 양수의 숫자이다.") + void effectChance_validateIsMinusOne_OrPositiveDigit() { + List notMatchEffectChance = moves.stream() + .map(Move::getEffectChance) + .filter(this::isNotAvailableDigit) + .toList(); + + assertThat(notMatchEffectChance).isEmpty(); + } + + @Test + @DisplayName("Move의 effect는 최소 한자의 한글이거나 Dummy Data 라는 이름으로 이루어져 있다.") + void effect_validateIsKorean() { + List notMatchEffects = moves.stream() + .map(Move::getEffect) + .filter(DataPattern.KO_NAME_PATTERN::isNotMatch) + .filter(koName -> !koName.equals("Dummy Data")) + .toList(); + + assertThat(notMatchEffects).isEmpty(); + } + + @Test + @DisplayName("Move의 PokemonId 들은 Pokemon Collection 에 존재한다.") + void pokemonIds_validateInPokemonCollection() { + List movePokemonIds = moves.stream() + .map(Move::getPokemonIds) + .flatMap(List::stream) + .toList(); + + List notMatchIds = movePokemonIds.stream() + .filter(pokemonId -> !pokemonIds.contains(pokemonId)) + .toList(); + + assertThat(notMatchIds).isEmpty(); + } + + @Test + @DisplayName("Move의 PokemonId 들은 중복되지 않는다.") + void pokemonIds_NotDuplicated() { + List duplicatedPokemonMoveIds = moves.stream() + .filter(move -> isDuplicated(move.getPokemonIds())) + .map(Move::getId) + .toList(); + + assertThat(duplicatedPokemonMoveIds).isEmpty(); + } + + private boolean isDuplicated(List pokemonIds) { + return pokemonIds.size() != new HashSet<>(pokemonIds).size(); + } + + private boolean isNotAvailableDigit(int digit) { + return digit != -1 && digit <= 0; + } +} diff --git a/backend/pokerogue/src/test/java/com/pokerogue/data/PokemonDataTest.java b/backend/pokerogue/src/test/java/com/pokerogue/data/PokemonDataTest.java new file mode 100644 index 000000000..075731c0d --- /dev/null +++ b/backend/pokerogue/src/test/java/com/pokerogue/data/PokemonDataTest.java @@ -0,0 +1,373 @@ +package com.pokerogue.data; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import com.pokerogue.data.pattern.DataPattern; +import com.pokerogue.environment.repository.MongoRepositoryTest; +import com.pokerogue.helper.ability.data.Ability; +import com.pokerogue.helper.ability.repository.AbilityRepository; +import com.pokerogue.helper.biome.data.Biome; +import com.pokerogue.helper.biome.repository.BiomeRepository; +import com.pokerogue.helper.move.data.Move; +import com.pokerogue.helper.move.repository.MoveRepository; +import com.pokerogue.helper.pokemon.data.Evolution; +import com.pokerogue.helper.pokemon.data.FormChange; +import com.pokerogue.helper.pokemon.data.LevelMove; +import com.pokerogue.helper.pokemon.data.Pokemon; +import com.pokerogue.helper.pokemon.repository.PokemonRepository; +import com.pokerogue.helper.type.data.Type; +import java.util.HashSet; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +public class PokemonDataTest extends MongoRepositoryTest { + + private final List pokemons; + private final List pokemonIds; + private final List abilityIds; + private final List moveIds; + private final List biomeIds; + + @Autowired + public PokemonDataTest( + PokemonRepository pokemonRepository, + AbilityRepository abilityRepository, + MoveRepository moveRepository, + BiomeRepository biomeRepository + ) { + this.pokemons = pokemonRepository.findAll(); + + List pokemons = pokemonRepository.findAll(); + this.pokemonIds = pokemons.stream() + .map(Pokemon::getId) + .toList(); + + List abilities = abilityRepository.findAll(); + abilityIds = abilities.stream() + .map(Ability::getId) + .toList(); + + List moves = moveRepository.findAll(); + moveIds = moves.stream() + .map(Move::getId) + .toList(); + + List biomes = biomeRepository.findAll(); + biomeIds = biomes.stream() + .map(Biome::getId) + .toList(); + } + + @Test + @DisplayName("pokemon의 id가 숫자, 영어 소문자 혹은 _로 이루어져있다.") + void id_validatePokemonIds() { + List notMatchPokemonIds = pokemonIds.stream() + .filter(DataPattern.ID_PATTERN::isNotMatch) + .toList(); + + assertThat(notMatchPokemonIds).isEmpty(); + } + + @Test + @DisplayName("pokemon의 image id가 숫자, 영어 소문자 혹은 _로 이루어져있다.") + void imageId_validateImageIds() { + List notMatchingImageIds = pokemons.stream() + .map(Pokemon::getImageId) + .filter(DataPattern.ID_PATTERN::isNotMatch) + .toList(); + + assertThat(notMatchingImageIds).isEmpty(); + } + + @Test + @DisplayName("pokemon의 pokeDexNumber는 양수의 숫자여야 한다.") + void pokedexNumber_validateIsDigit() { + List notMatchingPokeDexNumbers = pokemons.stream() + .map(Pokemon::getPokedexNumber) + .filter(this::isNotPositiveDigit) + .toList(); + + assertThat(notMatchingPokeDexNumbers).isEmpty(); + } + + @Test + @DisplayName("pokemon의 name가 영어, 특수문자, 공백으로 이루어져있다.") + void name_validateNames() { + List notMatchingNames = pokemons.stream() + .map(Pokemon::getName) + .filter(DataPattern.NAME_PATTERN::isNotMatch) + .toList(); + + assertThat(notMatchingNames).isEmpty(); + } + + @Test + @DisplayName("pokemon의 koName이 최소 한 자의 한글로 이루어져 있다.") + void koName_validateKoName() { + List notMatchingKoNames = pokemons.stream() + .map(Pokemon::getKoName) + .filter(DataPattern.KO_NAME_PATTERN::isNotMatch) + .toList(); + + assertThat(notMatchingKoNames).isEmpty(); + } + + @Test + @DisplayName("pokemon의 speciesName이 영어, 특수문자, 공백으로 이루어져있다.") + void speciesName_validateSpeciesNames() { + List notMatchingSpeciesNames = pokemons.stream() + .map(Pokemon::getSpeciesName) + .filter(DataPattern.NAME_PATTERN::isNotMatch) + .toList(); + + assertThat(notMatchingSpeciesNames).isEmpty(); + } + + @Test + @DisplayName("pokemon의 formName이 영어, 특수문자, 숫자, 공백으로 이루어져있다.") + void formName_validateFormName() { + List notMatchingFormName = pokemons.stream() + .map(Pokemon::getFormName) + .filter(DataPattern.NAME_PATTERN::isNotMatch) + .filter(formName -> !formName.isEmpty()) + .toList(); + + assertThat(notMatchingFormName).isEmpty(); + } + + @Test + @DisplayName("pokemon의 baseExp는 양수로 이루어져 있다.") + void baseExp_validateIsPositiveNumber() { + List notMatchingBaseExps = pokemons.stream() + .map(Pokemon::getBaseExp) + .filter(this::isNotPositiveDigit) + .toList(); + + assertThat(notMatchingBaseExps).isEmpty(); + } + + @Test + @DisplayName("pokemon의 friendship은 0 이상의 숫자로 이루어져 있다.") + void friendShip_validateOverZero() { + List notMatchFriendship = pokemons.stream() + .map(Pokemon::getFriendship) + .filter(this::isNotZeroOrPositiveDigit) + .toList(); + + assertThat(notMatchFriendship).isEmpty(); + } + + @Test + @DisplayName("pokemon의 Type은 전부 Enum Type 이다.") + void type_validateEnumType() { + assertThat(pokemons.stream() + .flatMap(pokemon -> pokemon.getTypes().stream())) + .allMatch(type -> type.getDeclaringClass() + .equals(Type.class)); + } + + @Test + @DisplayName("pokemon의 normalAbilityId 들은 전부 Ability에 존재한다.") + void abilityId_validateAllInPokemon() { + List normalAbilityIds = pokemons.stream() + .map(Pokemon::getNormalAbilityIds) + .flatMap(List::stream) + .toList(); + + List notMatchIds = normalAbilityIds.stream() + .filter(abilityId -> !abilityIds.contains(abilityId)) + .toList(); + assertThat(notMatchIds).isEmpty(); + } + + @Test + @DisplayName("pokemon의 normalAbilityId 들은 중복되지 않는다") + void abilityId_validateIsNotDuplicate() { + List duplicatedAbilityPokemonIds = pokemons.stream() + .filter(pokemon -> isDuplicated(pokemon.getNormalAbilityIds())) + .map(Pokemon::getId) + .toList(); + + assertThat(duplicatedAbilityPokemonIds).isEmpty(); + } + + @Test + @DisplayName("pokemon의 hiddenAbilityId 들은 전부 Ability에 존재한다.") + void hiddenAbilityId_validateAllInPokemon() { + List hiddenAbilityIds = pokemons.stream() + .map(Pokemon::getHiddenAbilityId) + .toList(); + + List notMatchIds = hiddenAbilityIds.stream() + .filter(abilityId -> !abilityIds.contains(abilityId)) + .filter(abilityId -> !abilityId.isEmpty()) + .toList(); + assertThat(notMatchIds).isEmpty(); + } + + @Test + @DisplayName("pokemon의 passiveAbilityId 들은 전부 Ability에 존재한다.") + void passiveAbilityId_validateAllInPokemon() { + List passiveAbilityIds = pokemons.stream() + .map(Pokemon::getPassiveAbilityId) + .toList(); + + List notMatchIds = passiveAbilityIds.stream() + .filter(abilityId -> !abilityIds.contains(abilityId)) + .toList(); + assertThat(notMatchIds).isEmpty(); + } + + + @Test + @DisplayName("pokemon의 모든 Evolution의 from to 는 Pokemon에 존재한다 ") + void evolutionFromTo_validateAllInPokemonIds() { + List evolutions = pokemons.stream() + .map(Pokemon::getEvolutions) + .flatMap(List::stream) + .toList(); + + List notMatchEvolutionFromIds = evolutions.stream() + .map(Evolution::getFrom) + .filter(fromId -> !pokemonIds.contains(fromId)) + .toList(); + List notMatchEvolutionToIds = evolutions.stream() + .map(Evolution::getTo) + .filter(toId -> !pokemonIds.contains(toId)) + .toList(); + + assertAll( + () -> assertThat(notMatchEvolutionFromIds).isEmpty(), + () -> assertThat(notMatchEvolutionToIds).isEmpty() + ); + } + + @Test + @DisplayName("pokemon의 모든 FormChange는 Pokemon에 존재한다.") + void formChangeIds_validateAllInPokemonIds() { + List formChanges = pokemons.stream() + .map(Pokemon::getFormChanges) + .flatMap(List::stream) + .toList(); + + List notMatchFormChangePreviousIds = formChanges.stream() + .map(formChange -> makeFormChangeIdToPokemonId(formChange.getFrom(), formChange.getPreviousForm())) + .filter(formChangeId -> !pokemonIds.contains(formChangeId)) + .toList(); + + List notMatchFormChangeCurrentIds = formChanges.stream() + .map(formChange -> makeFormChangeIdToPokemonId(formChange.getFrom(), formChange.getCurrentForm())) + .filter(formChangeId -> !pokemonIds.contains(formChangeId)) + .toList(); + + assertAll( + () -> assertThat(notMatchFormChangeCurrentIds).isEmpty(), + () -> assertThat(notMatchFormChangePreviousIds).isEmpty() + ); + } + + @Test + @DisplayName("pokemon의 모든 EggMoves는 Move에 존재한다.") + void eggMoves_validateAllInMove() { + List eggMoveIds = pokemons.stream() + .map(Pokemon::getEggMoveIds) + .flatMap(List::stream) + .toList(); + + List notMatchMoveIds = eggMoveIds.stream() + .filter(moveId -> !moveIds.contains(moveId)) + .toList(); + + assertThat(notMatchMoveIds).isEmpty(); + } + + @Test + @DisplayName("pokemon의 각 EggMoves는 중복되지 않는다.") + void eggMoves_validateNotDuplicate() { + List duplicatedEggMovePokemonId = pokemons.stream() + .filter(pokemon -> isDuplicated(pokemon.getEggMoveIds())) + .map(Pokemon::getId) + .toList(); + + assertThat(duplicatedEggMovePokemonId).isEmpty(); + } + + @Test + @DisplayName("pokemon의 모든 levelMoves 는 Move에 존재한다.") + void levelMoves_validateAllInMove() { + List levelMoves = pokemons.stream() + .map(Pokemon::getLevelMoves) + .flatMap(List::stream) + .toList(); + + List notMatchLevelMoveIds = levelMoves.stream() + .map(LevelMove::getMoveId) + .filter(moveId -> !moveIds.contains(moveId)) + .toList(); + + assertThat(notMatchLevelMoveIds).isEmpty(); + } + + @Test + @DisplayName("pokemon의 모든 technicalMachineMoveIds는 Move에 존재한다.") + void technicalMachineMoveIds_validateAllInMove() { + List technicalMachineMoveIds = pokemons.stream() + .map(Pokemon::getTechnicalMachineMoveIds) + .flatMap(List::stream) + .toList(); + + List notMatchTechnicalMachineIds = technicalMachineMoveIds.stream() + .filter(technicalMachineMoveId -> !moveIds.contains(technicalMachineMoveId)) + .toList(); + + assertThat(notMatchTechnicalMachineIds).isEmpty(); + } + + @Test + @DisplayName("pokemon의 각 technicalMachineMoveIds는 중복되지 않는다.") + void technicalMachineMoveIds_validateNotDuplicate() { + List duplicateTechnicalMachineMoveIds = pokemons.stream() + .filter(pokemon -> isDuplicated(pokemon.getTechnicalMachineMoveIds())) + .map(Pokemon::getId) + .toList(); + + assertThat(duplicateTechnicalMachineMoveIds).isEmpty(); + } + + @Test + @DisplayName("pokemon의 모든 biomeIds는 Biome에 존재한다.") + void biomeIds_validateAllInBiome() { + List pokemonBiomeIds = pokemons.stream() + .map(Pokemon::getBiomeIds) + .flatMap(List::stream) + .toList(); + + List notMatchBiomeIds = pokemonBiomeIds.stream() + .filter(biomeId -> !biomeIds.contains(biomeId)) + .toList(); + + assertThat(notMatchBiomeIds).isEmpty(); + } + + private boolean isDuplicated(List pokemonIds) { + return pokemonIds.size() != new HashSet<>(pokemonIds).size(); + } + + private String makeFormChangeIdToPokemonId(String pokemonId, String formName) { + if (formName.isEmpty()) { + return pokemonId; + } + return pokemonId + "_" + formName; + } + + private boolean isNotPositiveDigit(int input) { + return input <= 0; + } + + private boolean isNotZeroOrPositiveDigit(int input) { + return input < 0; + } +} diff --git a/backend/pokerogue/src/test/java/com/pokerogue/data/pattern/DataPattern.java b/backend/pokerogue/src/test/java/com/pokerogue/data/pattern/DataPattern.java new file mode 100644 index 000000000..e7f2e135d --- /dev/null +++ b/backend/pokerogue/src/test/java/com/pokerogue/data/pattern/DataPattern.java @@ -0,0 +1,32 @@ +package com.pokerogue.data.pattern; + +import java.util.regex.Pattern; + +public enum DataPattern { + // ID + ID_PATTERN(Pattern.compile("^[a-z0-9]+(_[a-z0-9]+)*$")), // 영어 소문자와 숫자, 연속되지 않는 "_" + MOVE_ID_PATTERN(Pattern.compile("^[a-z0-9_]+$")), // 영어 소문자와 숫자, "_" + + // Name + NAME_PATTERN(Pattern.compile("^[a-z0-9A-Z_\\s\\W]*$")), // 영어와 특수문자 및 공백 및 숫자 + BIOME_NAME_PATTERN(Pattern.compile("^[a-zA-Z\\s]+$")), // 영어와 공백 + + // Ko Name + KO_NAME_PATTERN(Pattern.compile(".*[가-힣].*")), // 최소 한 자의 한글 + + // description + DESCRIPTION_PATTERN(Pattern.compile(".*[가-힣].*")); + + private final Pattern pattern; + + DataPattern(Pattern pattern) { + this.pattern = pattern; + } + + public boolean isNotMatch(String input) { + if (input.isEmpty()) { + return true; + } + return !pattern.matcher(input).matches(); + } +} diff --git a/backend/pokerogue/src/test/java/com/pokerogue/helper/data/PokemonDataTest.java b/backend/pokerogue/src/test/java/com/pokerogue/helper/data/PokemonDataTest.java index e8a79bb85..3a0b5f0a7 100644 --- a/backend/pokerogue/src/test/java/com/pokerogue/helper/data/PokemonDataTest.java +++ b/backend/pokerogue/src/test/java/com/pokerogue/helper/data/PokemonDataTest.java @@ -29,7 +29,6 @@ void pokemonCount() { Assertions.assertThatCode(validator).doesNotThrowAnyException(); } - @Disabled("파싱코드의 replace를 한 문자가 아닌 스트링의 전체를 replace하도록 바꿔야 함. 잘못된 id가 있어 disalbed") @DisplayName("포켓몬 데이터의 아이디 형식을 확인한다.") @Test void pokemonIdFormat() { @@ -45,7 +44,7 @@ void pokemonIdFormat() { ID / actualTotal / expectedTotal charizard_gigantamax / 634 / 644, kingler_gigantamax / 575 / 585 - + 두 건의 데이터에 대해 종족값이 일치하지 않는다. 추가적인 논의가 필요하여 disalbed""") @DisplayName("포켓몬 데이터의 종족값은 기본 능력치의 합이다.") @@ -100,20 +99,6 @@ void pokemonGeneration4() { Assertions.assertThatCode(validator).doesNotThrowAnyException(); } - @Disabled(""" - 기본 특성이 히든 특성과 같은 데이터가 있어서 disable - +) 이상해꽃 기간타맥스가 되면 기본 특성이 달라진다 - +) pokerouge dex와 데이터가 다른걸 보니 추가 확인이 필요""") - @DisplayName("abilitiy id는 서로 중복될 수 없다.") - @Test - void pokemonGeneration5() { - List actual = pokemonRepository.findAll(); - - ThrowingCallable validator = () -> PokemonValidator.validateTotalAbilityDuplication(actual); - - Assertions.assertThatCode(validator).doesNotThrowAnyException(); - } - @DisplayName("능력치가 정해진 범위의 수인지 확인한다.") @Test void pokemonGeneration6() { @@ -175,8 +160,6 @@ void pokemonGeneration11() { Assertions.assertThatCode(validator).doesNotThrowAnyException(); } - - @Disabled("데이터상 진화아이디와 포켓몬아이디가 불일치하여 disabled") @DisplayName("진화 아이디는 모두 포켓몬 아이디에 포함된다.") @Test void pokemonGeneration12() { diff --git a/backend/pokerogue/src/test/java/com/pokerogue/helper/data/PokeomonControllerTest.java b/backend/pokerogue/src/test/java/com/pokerogue/helper/data/PokeomonControllerTest.java deleted file mode 100644 index b74c03beb..000000000 --- a/backend/pokerogue/src/test/java/com/pokerogue/helper/data/PokeomonControllerTest.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.pokerogue.helper.data; - -import static io.restassured.RestAssured.given; - -import com.pokerogue.environment.repository.MongoRepositoryTest; -import com.pokerogue.helper.pokemon.data.Pokemon; -import com.pokerogue.helper.pokemon.repository.PokemonRepository; -import io.restassured.RestAssured; -import io.restassured.http.ContentType; -import java.util.List; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; - - -@Disabled("디버깅용 API 테스트") -public class PokeomonControllerTest extends MongoRepositoryTest { - - @Autowired - PokemonRepository pokemonRepository; - - @BeforeAll - public static void setup() { - RestAssured.baseURI = "http://localhost:8080/api/v1"; - } - - @Test - public void testApiError() { - given() - .contentType(ContentType.JSON) - .when() - .get("/pokemons2") - .then() - .statusCode(200); - } - - @Test - public void testApiError2() { - - List all = pokemonRepository.findAll(); - - for (Pokemon pokemon : all) { - System.out.println(pokemon); - given() - .contentType(ContentType.JSON) - .when() - .get("/pokemon2/" + pokemon.getId()) - .then() - .statusCode(200); - } - - } -} diff --git a/backend/pokerogue/src/test/java/com/pokerogue/helper/pokemon/data/PokemonTestFixture.java b/backend/pokerogue/src/test/java/com/pokerogue/helper/pokemon/data/PokemonTestFixture.java index 32ff0ec71..363e4bf8d 100644 --- a/backend/pokerogue/src/test/java/com/pokerogue/helper/pokemon/data/PokemonTestFixture.java +++ b/backend/pokerogue/src/test/java/com/pokerogue/helper/pokemon/data/PokemonTestFixture.java @@ -5,6 +5,7 @@ import java.util.List; public class PokemonTestFixture { + public static List BULBASAUR_EVOLUTIONS = Arrays.asList( new Evolution("bulbasaur", 16, "ivysaur", null, null), new Evolution("ivysaur", 32, "venusaur", null, null) @@ -66,7 +67,8 @@ public class PokemonTestFixture { "snore", "curse", "protect", "sludge_bomb", "mud_slap", "outrage", "giga_drain", "endure", "charm", "false_swipe", "swagger", "attract", "sleep_talk", "return", "frustration", "safeguard", "synthesis", "hidden_power", "sunny_day", "rock_smash", "facade", "nature_power", "helping_hand", - "knock_off", "weather_ball", "bullet_seed", "magical_leaf", "worry_seed", "seed_bomb", "energy_ball", + "knock_off", "weather_ball", "bullet_seed", "magical_leaf", "worry_seed", "seed_bomb", + "energy_ball", "leaf_storm", "power_whip", "grass_knot", "venoshock", "acid_spray", "round", "echoed_voice", "grass_pledge", "work_up", "grassy_terrain", "confide", "grassy_glide", "tera_blast", "trailblaze" ), // technicalMachineMoveIds @@ -126,14 +128,22 @@ public class PokemonTestFixture { List.of("dragon_dance", "bitter_blade", "earth_power", "oblivion_wing"), // eggMoveIds CHARMANDER_LEVEL_MOVES, // levelMoves List.of( - "mega_punch", "fire_punch", "thunder_punch", "swords_dance", "cut", "mega_kick", "body_slam", "take_down", "double_edge", - "roar", "flamethrower", "counter", "strength", "fire_spin", "dig", "toxic", "double_team", "reflect", "fire_blast", - "swift", "rest", "rock_slide", "substitute", "snore", "curse", "protect", "scary_face", "mud_slap", "outrage", - "endure", "false_swipe", "swagger", "attract", "sleep_talk", "return", "frustration", "iron_tail", "metal_claw", - "hidden_power", "sunny_day", "crunch", "rock_smash", "beat_up", "heat_wave", "will_o_wisp", "facade", "focus_punch", - "helping_hand", "brick_break", "weather_ball", "air_cutter", "overheat", "rock_tomb", "aerial_ace", "dragon_claw", - "dragon_dance", "fling", "flare_blitz", "dragon_pulse", "focus_blast", "shadow_claw", "fire_fang", "hone_claws", - "flame_charge", "round", "echoed_voice", "incinerate", "acrobatics", "fire_pledge", "dragon_tail", "work_up", + "mega_punch", "fire_punch", "thunder_punch", "swords_dance", "cut", "mega_kick", "body_slam", + "take_down", "double_edge", + "roar", "flamethrower", "counter", "strength", "fire_spin", "dig", "toxic", "double_team", + "reflect", "fire_blast", + "swift", "rest", "rock_slide", "substitute", "snore", "curse", "protect", "scary_face", "mud_slap", + "outrage", + "endure", "false_swipe", "swagger", "attract", "sleep_talk", "return", "frustration", "iron_tail", + "metal_claw", + "hidden_power", "sunny_day", "crunch", "rock_smash", "beat_up", "heat_wave", "will_o_wisp", + "facade", "focus_punch", + "helping_hand", "brick_break", "weather_ball", "air_cutter", "overheat", "rock_tomb", "aerial_ace", + "dragon_claw", + "dragon_dance", "fling", "flare_blitz", "dragon_pulse", "focus_blast", "shadow_claw", "fire_fang", + "hone_claws", + "flame_charge", "round", "echoed_voice", "incinerate", "acrobatics", "fire_pledge", "dragon_tail", + "work_up", "confide", "power_up_punch", "breaking_swipe", "tera_blast", "temper_flare" ), // technicalMachineMoveIds List.of("volcano") // biomeIds