refactored test arrange code

This commit is contained in:
pushrbx 2022-12-15 15:57:12 +00:00
parent 91da61a154
commit ca591b20a8
6 changed files with 282 additions and 252 deletions

View File

@ -7,7 +7,7 @@ use Jikan\Model\Common\DateProp;
/**
* Class representing a date range via Carbon objects.
*
*
* Mainly used for testing.
*/
class CarbonDateRange

View File

@ -3,6 +3,11 @@ namespace App\Testing\Concerns;
trait MakesHttpRequestsEx
{
protected function getBaseUri(): string
{
return "/";
}
/**
* Visit the given URI with a JSON GET request.
* @param string $uri
@ -13,4 +18,16 @@ trait MakesHttpRequestsEx
{
return $this->json('GET', $uri, [], $headers);
}
/**
* @param array $params
* @return array
*/
public function getJsonResponse(array $params): array
{
$parameters = http_build_query($params);
$uri = $this->getBaseUri() . "?" . $parameters;
$this->getJson($uri);
return $this->response->json();
}
}

View File

@ -115,7 +115,7 @@ trait JikanDataGenerator
private function createActiveDateRange($status, $activeStatus): array
{
$from = $this->createRandomDateTime("-15 years");
$to = $status != $activeStatus ? $from->addDays($this->faker->numberBetween(1, 368))->toAtomString() : null;
$to = $status != $activeStatus ? $from->addDays($this->faker->numberBetween(1, 368)) : null;
return [$from, $to];
}

View File

@ -2,9 +2,12 @@
namespace Database\Factories;
use App\CarbonDateRange;
use App\GenreAnime;
use App\Anime;
use App\Testing\JikanDataGenerator;
use Illuminate\Support\Collection;
use MongoDB\BSON\UTCDateTime;
use Illuminate\Support\Carbon;
class AnimeFactory extends JikanModelFactory
@ -102,4 +105,238 @@ class AnimeFactory extends JikanModelFactory
"request_hash" => sprintf("request:%s:%s", "v4", $this->getItemTestUrl("anime", $mal_id))
];
}
/**
* Helper function for overriding fields of the model factory based on query string parameters.
*
* @param array $additionalParams
* @param bool $doOpposite
* @return self
*/
public function overrideFromQueryStringParameters(array $additionalParams, bool $doOpposite = false): self
{
$additionalParams = collect($additionalParams);
if ($doOpposite) {
$overrides = $this->getOppositeOverridesFromQueryStringParameters($additionalParams);
}
else {
$overrides = $this->getOverridesFromQueryStringParameters($additionalParams);
}
return $this->state($this->serializeStateDefinition($overrides));
}
private function getOppositeOverridesFromQueryStringParameters(Collection $additionalParams): array
{
$overrides = [];
if ($additionalParams->has("type")) {
$types = [
"ova" => "OVA",
"movie" => "Movie",
"tv" => "TV"
];
$overrides["type"] = $this->faker->randomElement(array_diff(array_keys($types), [$additionalParams["type"]]));
}
if ($additionalParams->has("min_score") && !$additionalParams->has("max_score")) {
$overrides["score"] = $this->faker->randomFloat(2, 1.00, floatval($additionalParams["min_score"]));
}
if (!$additionalParams->has("min_score") && $additionalParams->has("max_score")) {
$overrides["score"] = $this->faker->randomFloat(2, floatval($additionalParams["max_score"]), 9.99);
}
if ($additionalParams->has("min_score") && $additionalParams->has("max_score")) {
$overrides["score"] = $this->faker->randomElement([
$this->faker->randomFloat(2, 1.00, floatval($additionalParams["min_score"])),
$this->faker->randomFloat(2, floatval($additionalParams["max_score"]), 9.99)
]);
}
if ($additionalParams->has("status")) {
$statuses = [
"complete" => "Finished Airing",
"airing" => "Currently Airing",
"upcoming" => "Upcoming"
];
$overrides["status"] = $this->faker->randomElement(array_diff(array_keys($statuses), [$additionalParams["status"]]));
}
if (($additionalParams->has("genres") && $additionalParams->has("genres_exclude")) || (
!$additionalParams->has("genres") && $additionalParams->has("genres_exclude")
) ) {
$overrides["genres"] = [];
// use the "genres_exclude" values to add genres to the anime item
$genreIds = explode(",", $additionalParams["genres_exclude"]);
if (count($genreIds) > 1) {
$genreId = $this->faker->randomElement($genreIds);
} else {
$genreId = $genreIds[0];
}
$m = $this->ensureGenreExists($genreId);
$overrides["genres"][] = [
"mal_id" => $m->mal_id,
"type" => "anime",
"name" => $m->name,
"url" => $m->url
];
} else if ($additionalParams->has("genres")) {
$overrides["genres"] = [];
// add such genres which are not in the "genres" param
$genreIds = explode(",", $additionalParams["genres"]);
$numberOfGenresToAdd = $this->faker->numberBetween(0, 4);
for ($numberOfGenresAdded = 0; $numberOfGenresAdded <= $numberOfGenresToAdd; $numberOfGenresAdded++) {
$outboundsGenreId = $this->faker->numberBetween(0, 74);
while (in_array($outboundsGenreId, $genreIds)) {
$outboundsGenreId = $this->faker->numberBetween(0, 74);
}
$m = $this->ensureGenreExists($outboundsGenreId);
$overrides["genres"][] = [
"mal_id" => $m->mal_id,
"type" => "anime",
"name" => $m->name,
"url" => $m->url
];
}
}
if ($additionalParams->has("start_date") && !empty($additionalParams["start_date"])
&& !$additionalParams->has("end_date")) {
$startDate = $this->adaptDateString($additionalParams["start_date"]);
$dt = Carbon::parse($startDate)->subDays($this->faker->randomElement([30, 60, 90, 120, 180]));
$overrides["aired"] = new CarbonDateRange($dt, null);
}
if ($additionalParams->has("end_date") && !empty($additionalParams["end_date"])
&& !$additionalParams->has("start_date")) {
$endDate = $this->adaptDateString($additionalParams["end_date"]);
$to = Carbon::parse($endDate)->addDays($this->faker->randomElement([30, 60, 90, 120, 180]));
$from = $to->copy()->subDays($this->faker->randomElement([30, 60, 90, 120, 180]));
$overrides["aired"] = new CarbonDateRange($from, $to);
}
if ($additionalParams->has("start_date") && $additionalParams->has("end_date")
&& !empty($additionalParams["start_date"]) && !empty($additionalParams["end_date"])) {
$originalFrom = Carbon::parse($this->adaptDateString($additionalParams["start_date"]));
$originalTo = Carbon::parse($this->adaptDateString($additionalParams["end_date"]));
$interval = $originalTo->diff($originalFrom);
$afterOrBefore = $this->faker->randomElement(["after", "before"]);
$randomDayIntervalValue = $this->faker->numberBetween(8, 90) + $interval->days;
[$artificialFrom, $artificialTo] = match ($afterOrBefore) {
"after" => [$originalFrom->addDays($randomDayIntervalValue), $originalTo->addDays($randomDayIntervalValue)],
"before" => [$originalFrom->subDays($randomDayIntervalValue), $originalTo->subDays($randomDayIntervalValue)]
};
$overrides["aired"] = new CarbonDateRange($artificialFrom, $artificialTo);
}
return $overrides;
}
private function getOverridesFromQueryStringParameters(Collection $additionalParams): array
{
$overrides = [];
// let's make all database items the same type
if ($additionalParams->has("type")) {
$overrides["type"] = match ($additionalParams["type"]) {
"ova" => "OVA",
"movie" => "Movie",
default => "TV"
};
}
if ($additionalParams->has("min_score") && !$additionalParams->has("max_score")) {
$overrides["score"] = $this->faker->randomFloat(2, floatval($additionalParams["min_score"]), 9.99);
}
if (!$additionalParams->has("min_score") && $additionalParams->has("max_score")) {
$overrides["score"] = $this->faker->randomFloat(2, 1.00, floatval($additionalParams["max_score"]));
}
if ($additionalParams->has(["min_score", "max_score"])) {
$overrides["score"] = $this->faker->randomFloat(2, floatval($additionalParams["min_score"]), floatval($additionalParams["max_score"]));
}
if ($additionalParams->has("status")) {
$overrides["status"] = match ($additionalParams["status"]) {
"complete" => "Finished Airing",
"airing" => "Currently Airing",
"upcoming" => "Upcoming"
};
}
if ($additionalParams->has("genres")) {
$overrides["genres"] = [];
$genreIds = explode(",", $additionalParams["genres"]);
$genreIds = $this->faker->randomElements($genreIds, $this->faker->numberBetween(0, count($genreIds)));
foreach ($genreIds as $genreId) {
$m = $this->ensureGenreExists($genreId);
$overrides["genres"][] = [
"mal_id" => $m->mal_id,
"type" => "anime",
"name" => $m->name,
"url" => $m->url
];
}
}
if ($additionalParams->has("start_date") && !empty($additionalParams["start_date"])
&& !$additionalParams->has("end_date")) {
$startDate = $this->adaptDateString($additionalParams["start_date"]);
$dt = Carbon::parse($startDate)->addDays($this->faker->numberBetween(0, 25));
$overrides["aired"] = new CarbonDateRange($dt, null);
}
if ($additionalParams->has("end_date") && !empty($additionalParams["end_date"])
&& !$additionalParams->has("start_date")) {
$endDate = $this->adaptDateString($additionalParams["end_date"]);
$to = Carbon::parse($endDate);
$from = $to->copy()->subDays($this->faker->randomElement([30, 60, 90, 120, 180]));
$overrides["aired"] = new CarbonDateRange($from, $to->subDays($this->faker->numberBetween(0, 25)));
}
if ($additionalParams->has(["start_date", "end_date"])
&& !empty($additionalParams["start_date"]) && !empty($additionalParams["end_date"])) {
$startDate = $this->adaptDateString($additionalParams["start_date"]);
$from = Carbon::parse($startDate);
$endDate = $this->adaptDateString($additionalParams["end_date"]);
$to = Carbon::parse($endDate);
$overrides["aired"] = new CarbonDateRange($from, $to);
}
return $overrides;
}
private function ensureGenreExists(int $genreId): GenreAnime
{
$m = GenreAnime::query()->firstWhere("mal_id", $genreId);
if ($m == null) {
$f = GenreAnime::factory();
$m = $f->createOne([
"mal_id" => $genreId
]);
}
return $m;
}
private function adaptDateString($dateStr): string
{
$parts = explode("-", $dateStr);
if (count($parts) === 1) {
return $parts[0] . "-01-01";
}
return $dateStr;
}
}

View File

@ -12,12 +12,17 @@ abstract class JikanModelFactory extends Factory
* @inheritDoc
*/
public function definition(): array
{
return $this->serializeStateDefinition($this->definitionInternal());
}
protected function serializeStateDefinition($stateDefinition): array
{
/**
* @var Serializer $serializer
*/
$serializer = app("SerializerV4");
return $serializer->toArray($this->definitionInternal());
return $serializer->toArray($stateDefinition);
}
protected abstract function definitionInternal(): array;

View File

@ -1,7 +1,6 @@
<?php /** @noinspection PhpIllegalPsrClassPathInspection */
namespace Tests\Integration;
use App\Anime;
use App\GenreAnime;
use App\Testing\ScoutFlush;
use App\Testing\SyntheticMongoDbTransaction;
use Illuminate\Support\Carbon;
@ -18,254 +17,24 @@ class AnimeSearchEndpointTest extends TestCase
$this->searchIndexModelCleanupList = ["App\\Anime"];
}
private function ensureGenreExists(int $genreId): GenreAnime
protected function getBaseUri(): string
{
$m = GenreAnime::query()->firstWhere("mal_id", $genreId);
if ($m == null) {
$f = GenreAnime::factory();
$m = $f->createOne([
"mal_id" => $genreId
]);
}
return $m;
}
private function adaptDateString($dateStr): string
{
$parts = explode("-", $dateStr);
if (count($parts) === 1) {
return $parts[0] . "-01-01";
}
return $dateStr;
}
/**
* Helper function for overriding fields of model factories based on parameters.
*
* @param array $additionalParams
* @param bool $negate
* @return array
*/
private function getFactoryFieldOverrides(array $additionalParams, bool $negate = false): array
{
$overrides = [];
if (!$negate) {
// let's make all database items the same type
if (array_key_exists("type", $additionalParams)) {
$overrides["type"] = match ($additionalParams["type"]) {
"ova" => "OVA",
"movie" => "Movie",
default => "TV"
};
}
if (array_key_exists("min_score", $additionalParams) && !array_key_exists("max_score", $additionalParams)) {
$overrides["score"] = $this->faker->randomFloat(2, floatval($additionalParams["min_score"]), 9.99);
}
if (!array_key_exists("min_score", $additionalParams) && array_key_exists("max_score", $additionalParams)) {
$overrides["score"] = $this->faker->randomFloat(2, 1.00, floatval($additionalParams["max_score"]));
}
if (array_key_exists("min_score", $additionalParams) && array_key_exists("max_score", $additionalParams)) {
$overrides["score"] = $this->faker->randomFloat(2, floatval($additionalParams["min_score"]), floatval($additionalParams["max_score"]));
}
if (array_key_exists("status", $additionalParams)) {
$overrides["status"] = match ($additionalParams["status"]) {
"complete" => "Finished Airing",
"airing" => "Currently Airing",
"upcoming" => "Upcoming"
};
}
if (array_key_exists("genres", $additionalParams)) {
$overrides["genres"] = [];
$genreIds = explode(",", $additionalParams["genres"]);
$genreIds = $this->faker->randomElements($genreIds, $this->faker->numberBetween(0, count($genreIds)));
foreach ($genreIds as $genreId) {
$m = $this->ensureGenreExists($genreId);
$overrides["genres"][] = [
"mal_id" => $m->mal_id,
"type" => "anime",
"name" => $m->name,
"url" => $m->url
];
}
}
if (array_key_exists("start_date", $additionalParams) && !empty($additionalParams["start_date"])
&& !array_key_exists("end_date", $additionalParams)) {
$startDate = $this->adaptDateString($additionalParams["start_date"]);
$dt = Carbon::parse($startDate)->addDays($this->faker->numberBetween(0, 25));
$overrides["aired"]["from"] = $dt->toAtomString();
// $overrides["aired"]["to"] = $dt->addDays($this->faker->randomElement([30, 60, 90, 120, 180]))->toAtomString();
$overrides["aired"]["to"] = null;
}
if (array_key_exists("end_date", $additionalParams) && !empty($additionalParams["end_date"])
&& !array_key_exists("start_date", $additionalParams)) {
$endDate = $this->adaptDateString($additionalParams["end_date"]);
$to = Carbon::parse($endDate);
$from = $to->copy()->subDays($this->faker->randomElement([30, 60, 90, 120, 180]));
$overrides["aired"]["from"] = $from->toAtomString();
$overrides["aired"]["to"] = $to->subDays($this->faker->numberBetween(0, 25))->toAtomString();
}
if (array_key_exists("start_date", $additionalParams) && array_key_exists("end_date", $additionalParams)
&& !empty($additionalParams["start_date"]) && !empty($additionalParams["end_date"])) {
$startDate = $additionalParams["start_date"];
$from = Carbon::parse($startDate);
$endDate = $additionalParams["end_date"];
$to = Carbon::parse($endDate);
$overrides["aired"]["from"] = $from->toAtomString();
$overrides["aired"]["to"] = $to->toAtomString();
}
} else {
// the opposites
if (array_key_exists("type", $additionalParams)) {
$types = [
"ova" => "OVA",
"movie" => "Movie",
"tv" => "TV"
];
$overrides["type"] = $this->faker->randomElement(array_diff(array_keys($types), [$additionalParams["type"]]));
}
if (array_key_exists("min_score", $additionalParams) && !array_key_exists("max_score", $additionalParams)) {
$overrides["score"] = $this->faker->randomFloat(2, 1.00, floatval($additionalParams["min_score"]));
}
if (!array_key_exists("min_score", $additionalParams) && array_key_exists("max_score", $additionalParams)) {
$overrides["score"] = $this->faker->randomFloat(2, floatval($additionalParams["max_score"]), 9.99);
}
if (array_key_exists("min_score", $additionalParams) && array_key_exists("max_score", $additionalParams)) {
$overrides["score"] = $this->faker->randomElement([
$this->faker->randomFloat(2, 1.00, floatval($additionalParams["min_score"])),
$this->faker->randomFloat(2, floatval($additionalParams["max_score"]), 9.99)
]);
}
if (array_key_exists("status", $additionalParams)) {
$statuses = [
"complete" => "Finished Airing",
"airing" => "Currently Airing",
"upcoming" => "Upcoming"
];
$overrides["status"] = $this->faker->randomElement(array_diff(array_keys($statuses), [$additionalParams["status"]]));
}
if ((array_key_exists("genres", $additionalParams) && array_key_exists("genres_exclude", $additionalParams)) || (
!array_key_exists("genres", $additionalParams) && array_key_exists("genres_exclude", $additionalParams)
) ) {
$overrides["genres"] = [];
// use the "genres_exclude" values to add genres to the anime item
$genreIds = explode(",", $additionalParams["genres_exclude"]);
if (count($genreIds) > 1) {
$genreId = $this->faker->randomElement($genreIds);
} else {
$genreId = $genreIds[0];
}
$m = $this->ensureGenreExists($genreId);
$overrides["genres"][] = [
"mal_id" => $m->mal_id,
"type" => "anime",
"name" => $m->name,
"url" => $m->url
];
} else if (array_key_exists("genres", $additionalParams)) {
$overrides["genres"] = [];
// add such genres which are not in the "genres" param
$genreIds = explode(",", $additionalParams["genres"]);
$numberOfGenresToAdd = $this->faker->numberBetween(0, 4);
for ($numberOfGenresAdded = 0; $numberOfGenresAdded <= $numberOfGenresToAdd; $numberOfGenresAdded++) {
$outboundsGenreId = $this->faker->numberBetween(0, 74);
while (in_array($outboundsGenreId, $genreIds)) {
$outboundsGenreId = $this->faker->numberBetween(0, 74);
}
$m = $this->ensureGenreExists($outboundsGenreId);
$overrides["genres"][] = [
"mal_id" => $m->mal_id,
"type" => "anime",
"name" => $m->name,
"url" => $m->url
];
}
}
if (array_key_exists("start_date", $additionalParams) && !empty($additionalParams["start_date"])
&& !array_key_exists("end_date", $additionalParams)) {
$startDate = $this->adaptDateString($additionalParams["start_date"]);
$dt = Carbon::parse($startDate)->subDays($this->faker->randomElement([30, 60, 90, 120, 180]));
$overrides["aired"]["from"] = $dt->toAtomString();
// $overrides["aired"]["to"] = $dt->addDays($this->faker->randomElement([30, 60, 90, 120, 180]))->toAtomString();
$overrides["aired"]["to"] = null;
}
if (array_key_exists("end_date", $additionalParams) && !empty($additionalParams["end_date"])
&& !array_key_exists("start_date", $additionalParams)) {
$endDate = $this->adaptDateString($additionalParams["end_date"]);
$to = Carbon::parse($endDate)->addDays($this->faker->randomElement([30, 60, 90, 120, 180]));
$from = $to->copy()->subDays($this->faker->randomElement([30, 60, 90, 120, 180]));
$overrides["aired"]["from"] = $from->toAtomString();
$overrides["aired"]["to"] = $to->toAtomString();
}
if (array_key_exists("start_date", $additionalParams) && array_key_exists("end_date", $additionalParams)
&& !empty($additionalParams["start_date"]) && !empty($additionalParams["end_date"])) {
$originalFrom = Carbon::parse($this->adaptDateString($additionalParams["start_date"]));
$originalTo = Carbon::parse($this->adaptDateString($additionalParams["end_date"]));
$interval = $originalTo->diff($originalFrom);
$afterOrBefore = $this->faker->randomElement(["after", "before"]);
$randomDayIntervalValue = $this->faker->numberBetween(8, 90) + $interval->days;
[$artificialFrom, $artificialTo] = match ($afterOrBefore) {
"after" => [$originalFrom->addDays($randomDayIntervalValue), $originalTo->addDays($randomDayIntervalValue)],
"before" => [$originalFrom->subDays($randomDayIntervalValue), $originalTo->subDays($randomDayIntervalValue)]
};
$overrides["aired"]["from"] = $artificialFrom->toAtomString();
$overrides["aired"]["to"] = $artificialTo->toAtomString();
}
}
return $overrides;
}
/**
* @param array $params
* @return array
*/
private function getJsonResponse(array $params): array
{
$parameters = http_build_query($params);
$uri = "/v4/anime?" . $parameters;
$this->getJson($uri);
return $this->response->json();
return "/v4/anime";
}
private function generateFiveSpecificAndTenRandomElementsInDb(array $params): array
{
// 10 random elements
$f = Anime::factory()->count(10);
$f->create($this->getFactoryFieldOverrides($params, true));
Anime::factory(10)
->overrideFromQueryStringParameters($params, true)
->create();
// 5 specific elements
$f = Anime::factory()->count(5);
$overrides = $this->getFactoryFieldOverrides($params);
$f->create($overrides);
$f = Anime::factory(5)
->overrideFromQueryStringParameters($params);
return $overrides;
$f->create();
return $f->raw()[0];
}
public function limitParameterCombinationsProvider(): array
@ -342,8 +111,10 @@ class AnimeSearchEndpointTest extends TestCase
*/
public function testLimitParameter(int $limitCount, array $additionalParams)
{
$f = Anime::factory()->count(25);
$f->create($this->getFactoryFieldOverrides($additionalParams));
Anime::factory( 25)
->overrideFromQueryStringParameters($additionalParams)
->create();
$content = $this->getJsonResponse([
"limit" => $limitCount,
...$additionalParams
@ -363,6 +134,7 @@ class AnimeSearchEndpointTest extends TestCase
public function testSearchByEmptyDatesShouldDoNothing($params)
{
$this->generateFiveSpecificAndTenRandomElementsInDb($params);
$content = $this->getJsonResponse($params);
$this->seeStatusCode(200);
@ -378,6 +150,7 @@ class AnimeSearchEndpointTest extends TestCase
public function testSearchByStartDate($params)
{
$overrides = $this->generateFiveSpecificAndTenRandomElementsInDb($params);
$content = $this->getJsonResponse($params);
$actualStartDate = Carbon::parse(data_get($content, "data.0.aired.from"));
@ -398,6 +171,7 @@ class AnimeSearchEndpointTest extends TestCase
public function testSearchByEndDate($params)
{
$overrides = $this->generateFiveSpecificAndTenRandomElementsInDb($params);
$content = $this->getJsonResponse($params);
$actualEndDate = Carbon::parse(data_get($content, "data.0.aired.to"));
@ -418,6 +192,7 @@ class AnimeSearchEndpointTest extends TestCase
public function testSearchByStartAndEndDate($params)
{
$overrides = $this->generateFiveSpecificAndTenRandomElementsInDb($params);
$content = $this->getJsonResponse($params);
$actualStartDate = Carbon::parse(data_get($content, "data.0.aired.from"));
@ -440,12 +215,8 @@ class AnimeSearchEndpointTest extends TestCase
*/
public function testSearchByGenres($params)
{
// 10 random elements
$f = Anime::factory()->count(10);
$f->create($this->getFactoryFieldOverrides($params, true));
// 5 specific elements
$f = Anime::factory()->count(5);
$f->create($this->getFactoryFieldOverrides($params));
$this->generateFiveSpecificAndTenRandomElementsInDb($params);
$content = $this->getJsonResponse($params);
$this->seeStatusCode(200);