diff --git a/app/Http/Controllers/V4DB/ScheduleController.php b/app/Http/Controllers/V4DB/ScheduleController.php index 8ad4b18..143deb0 100644 --- a/app/Http/Controllers/V4DB/ScheduleController.php +++ b/app/Http/Controllers/V4DB/ScheduleController.php @@ -13,8 +13,6 @@ class ScheduleController extends Controller * operationId="getSchedules", * tags={"schedules"}, * - * @OA\Parameter(ref="#/components/parameters/page"), - * * @OA\Parameter( * name="filter", * in="query", diff --git a/app/Repositories/DefaultAnimeRepository.php b/app/Repositories/DefaultAnimeRepository.php index e337182..4a29a48 100644 --- a/app/Repositories/DefaultAnimeRepository.php +++ b/app/Repositories/DefaultAnimeRepository.php @@ -5,15 +5,12 @@ namespace App\Repositories; use App\Anime; use App\Contracts\AnimeRepository; use App\Contracts\Repository; -use App\Enums\AnimeRatingEnum; use App\Enums\AnimeScheduleFilterEnum; -use App\Enums\AnimeSeasonEnum; use App\Enums\AnimeStatusEnum; use App\Enums\AnimeTypeEnum; use Illuminate\Contracts\Database\Query\Builder as EloquentBuilder; use Illuminate\Database\Eloquent\Collection; use Illuminate\Support\Carbon; -use Jikan\Helper\Constants; use Laravel\Scout\Builder as ScoutBuilder; /** @@ -120,44 +117,49 @@ final class DefaultAnimeRepository extends DatabaseRepository implements AnimeRe ?string $premiered = null ): EloquentBuilder { - /** @noinspection PhpParamsInspection */ $queryable = $this->queryable(true); - $airedFilter = ["aired.from" => [ + $airedFilter = ['aired.from' => [ '$gte' => $from->toAtomString(), - '$lte' => $to->modify("last day of this month")->toAtomString() + '$lte' => $to->modify('last day of this month')->toAtomString() ]]; $finalFilter = []; // if the premiered parameter for the filter is not null, look for those items which have a premiered attribute set, // and equals to the parameter value, OR look for those items which doesn't have premired attribute set, - // they don't have a garbled aired string and their aired.from date is within the from-to parameters range + // they don't have a garbled aired string and their aired.from date is within the from-to parameters range. + // Additionally, we want to include all those items which are carry overs from previous seasons. if ($premiered !== null) { $finalFilter['$or'] = [ - ["premiered" => $premiered], + ['premiered' => $premiered], [ - "premiered" => null, - "aired.string" => [ - '$not' => ['$regex' => "{$from->year} to ?"] + 'premiered' => null, + 'aired.string' => [ + '$nin' => ["{$from->year} to ?"] ], ...$airedFilter + ], + // this condition will include "continuing" items from previous seasons + [ + 'aired.from' => ['$lte' => $from->toAtomString()], + 'airing' => true ] ]; } else { $finalFilter = array_merge($finalFilter, $airedFilter); - $finalFilter["aired.string"] = [ - '$not' => ['$regex' => "{$from->year} to ?"] + $finalFilter['aired.string'] = [ + '$nin' => ["{$from->year} to ?"] ]; } if (!is_null($type)) { - $finalFilter["type"] = $type->label; + $finalFilter['type'] = $type->label; } $queryable = $queryable->whereRaw($finalFilter); - return $queryable->orderBy("members", "desc"); + return $queryable->orderBy('members', 'desc'); } public function getUpcomingSeasonItems( diff --git a/app/Support/CachedData.php b/app/Support/CachedData.php index 898009c..33c0db7 100644 --- a/app/Support/CachedData.php +++ b/app/Support/CachedData.php @@ -65,7 +65,7 @@ final class CachedData implements ArrayAccess $expiry = $this->expiry(); - return time() > $expiry; + return Carbon::now()->unix() > $expiry; } public function toArray(): array diff --git a/app/Support/RepositoryQueryBase.php b/app/Support/RepositoryQueryBase.php index 0463586..17fcd19 100644 --- a/app/Support/RepositoryQueryBase.php +++ b/app/Support/RepositoryQueryBase.php @@ -3,6 +3,7 @@ namespace App\Support; use Laravel\Scout\Builder as ScoutBuilder; use Illuminate\Contracts\Database\Query\Builder; +use Jenssegers\Mongodb\Eloquent\Builder as MongoDbBuilder; class RepositoryQueryBase { @@ -15,7 +16,7 @@ class RepositoryQueryBase { } - protected function queryable(bool $createNew = false): Builder + protected function queryable(bool $createNew = false): Builder|MongoDbBuilder { if ($createNew) { $callback = $this->getQueryable; diff --git a/storage/api-docs/api-docs.json b/storage/api-docs/api-docs.json index 4a295e4..c32616f 100644 --- a/storage/api-docs/api-docs.json +++ b/storage/api-docs/api-docs.json @@ -2283,9 +2283,6 @@ ], "operationId": "getSchedules", "parameters": [ - { - "$ref": "#/components/parameters/page" - }, { "name": "filter", "in": "query", @@ -9104,4 +9101,4 @@ "description": "About", "url": "https://jikan.moe" } -} \ No newline at end of file +} diff --git a/tests/Integration/SeasonControllerTest.php b/tests/Integration/SeasonControllerTest.php index 2879209..44a71db 100644 --- a/tests/Integration/SeasonControllerTest.php +++ b/tests/Integration/SeasonControllerTest.php @@ -25,7 +25,7 @@ class SeasonControllerTest extends TestCase $state = $f->serializeStateDefinition([ "aired" => new CarbonDateRange($carbonStartDate, null) ]); - $state["aired"]["string"] = "Jan 1, 2024 to ?"; + $state["aired"]["string"] = "2024 to ?"; $state["premiered"] = null; $state["status"] = "Not yet aired"; $state["airing"] = false; @@ -49,4 +49,91 @@ class SeasonControllerTest extends TestCase $this->assertIsArray($content["data"]); $this->assertCount(1, $content["data"]); } + + public function testShouldNotFilterOutFutureAiringDates() + { + Carbon::setTestNow(Carbon::parse("2024-01-11")); + // an item in the future airing + $f = Anime::factory(1); + $startDate = "2024-02-24"; + $carbonStartDate = Carbon::parse($startDate); + $state = $f->serializeStateDefinition([ + "aired" => new CarbonDateRange($carbonStartDate, null) + ]); + $state["aired"]["string"] = "Feb 24, 2024 to ?"; + $state["premiered"] = null; + $state["status"] = "Not yet aired"; + $state["airing"] = false; + $f->create($state); + + // the "garbled" wrong item + $f = Anime::factory(1); + $startDate = "2024-01-01"; + $carbonStartDate = Carbon::parse($startDate); + $state = $f->serializeStateDefinition([ + "aired" => new CarbonDateRange($carbonStartDate, null) + ]); + $state["aired"]["string"] = "2024 to ?"; + $state["premiered"] = null; + $state["status"] = "Not yet aired"; + $state["airing"] = false; + $f->create($state); + + // the absolutely correct item + $f = Anime::factory(1); + $state = $f->serializeStateDefinition([ + "aired" => new CarbonDateRange(Carbon::parse("2024-01-10"), Carbon::parse("2024-02-15")) + ]); + $state["premiered"] = "Winter 2024"; + $state["status"] = "Currently Airing"; + $state["airing"] = true; + $f->create($state); + + $content = $this->getJsonResponse([], "/v4/seasons/2024/winter"); + $this->seeStatusCode(200); + $this->assertIsArray($content["data"]); + $this->assertCount(2, $content["data"]); + } + + public function testShouldNotFilterOutContinuingItemsFromPreviousSeasons() + { + Carbon::setTestNow(Carbon::parse("2024-01-11")); + // an item in the future airing + $f = Anime::factory(1); + $startDate = "2024-02-24"; + $carbonStartDate = Carbon::parse($startDate); + $state = $f->serializeStateDefinition([ + "aired" => new CarbonDateRange($carbonStartDate, null) + ]); + $state["aired"]["string"] = "Feb 24, 2024 to ?"; + $state["premiered"] = null; + $state["status"] = "Not yet aired"; + $state["airing"] = false; + $f->create($state); + + // the absolutely correct item + $f = Anime::factory(1); + $state = $f->serializeStateDefinition([ + "aired" => new CarbonDateRange(Carbon::parse("2024-01-10"), Carbon::parse("2024-02-15")) + ]); + $state["premiered"] = "Winter 2024"; + $state["status"] = "Currently Airing"; + $state["airing"] = true; + $f->create($state); + + // the continuing item + $f = Anime::factory(1); + $state = $f->serializeStateDefinition([ + "aired" => new CarbonDateRange(Carbon::parse("2023-10-10"), null) + ]); + $state["premiered"] = "Fall 2023"; + $state["status"] = "Currently Airing"; + $state["airing"] = true; + $f->create($state); + + $content = $this->getJsonResponse([], "/v4/seasons/2024/winter"); + $this->seeStatusCode(200); + $this->assertIsArray($content["data"]); + $this->assertCount(3, $content["data"]); + } } diff --git a/tests/Unit/DefaultCachedScraperServiceTest.php b/tests/Unit/DefaultCachedScraperServiceTest.php index 46f5e5e..6d8ca5e 100644 --- a/tests/Unit/DefaultCachedScraperServiceTest.php +++ b/tests/Unit/DefaultCachedScraperServiceTest.php @@ -51,7 +51,7 @@ final class DefaultCachedScraperServiceTest extends TestCase public function testIfFindListReturnsNotExpiredItems() { $testRequestHash = $this->requestHash(); - $now = Carbon::now(); + $now = Carbon::createFromDate(2022, 1, 11, "UTC")->addHours(8)->addMinutes(12); Carbon::setTestNow($now); // the cached data in the database // this should be an array of arrays as builder->get() returns multiple items @@ -76,14 +76,14 @@ final class DefaultCachedScraperServiceTest extends TestCase public function testIfFindListUpdatesCacheIfItemsExpired() { $testRequestHash = $this->requestHash(); - $now = Carbon::now(); + $now = Carbon::createFromDate(2022, 1, 11, "UTC")->addHours(8)->addMinutes(12); Carbon::setTestNow($now); // the cached data in the database // this should be an array of arrays as builder->get() returns multiple items $dummyResults = collect([[ "request_hash" => $testRequestHash, - "modifiedAt" => new UTCDateTime($now->sub("2 days")->getPreciseTimestamp(3)), + "modifiedAt" => new UTCDateTime($now->copy()->subDays(2)->getPreciseTimestamp(3)), "results" => [ ["dummy" => "dummy1"], ["dummy" => "dummy2"] @@ -128,7 +128,7 @@ final class DefaultCachedScraperServiceTest extends TestCase public function testIfFindListUpdatesCacheIfCacheIsEmpty() { $testRequestHash = $this->requestHash(); - $now = Carbon::now(); + $now = Carbon::createFromDate(2022, 1, 11, "UTC")->addHours(8)->addMinutes(12); Carbon::setTestNow($now); // the data returned by the scraper @@ -175,7 +175,7 @@ final class DefaultCachedScraperServiceTest extends TestCase { $malId = 1; $testRequestHash = $this->requestHash(); - $now = Carbon::now(); + $now = Carbon::createFromDate(2022, 1, 11, "UTC")->addHours(8)->addMinutes(12); Carbon::setTestNow($now); $mockModel = Anime::factory()->makeOne([ "mal_id" => $malId, @@ -197,7 +197,7 @@ final class DefaultCachedScraperServiceTest extends TestCase { $malId = 1; $testRequestHash = $this->requestHash(); - $now = Carbon::now(); + $now = Carbon::createFromDate(2022, 1, 11, "UTC")->addHours(8)->addMinutes(12); Carbon::setTestNow($now); $mockModel = Anime::factory()->makeOne([ "mal_id" => $malId, @@ -224,11 +224,11 @@ final class DefaultCachedScraperServiceTest extends TestCase { $malId = 1; $testRequestHash = $this->requestHash(); - $now = Carbon::now(); + $now = Carbon::createFromDate(2022, 1, 11, "UTC")->addHours(8)->addMinutes(12); $mockModel = Anime::factory()->makeOne([ "mal_id" => $malId, - "modifiedAt" => new UTCDateTime($now->sub("3 days")->getPreciseTimestamp(3)), - "createdAt" => new UTCDateTime($now->sub("3 days")->getPreciseTimestamp(3)) + "modifiedAt" => new UTCDateTime($now->copy()->sub("3 days")->getPreciseTimestamp(3)), + "createdAt" => new UTCDateTime($now->copy()->sub("3 days")->getPreciseTimestamp(3)) ]); $now = Carbon::now(); Carbon::setTestNow($now); @@ -264,7 +264,7 @@ final class DefaultCachedScraperServiceTest extends TestCase { $username = "kompot"; $testRequestHash = $this->requestHash(); - $now = Carbon::now(); + $now = Carbon::createFromDate(2022, 1, 11, "UTC")->addHours(8)->addMinutes(12); Carbon::setTestNow($now); $mockModel = Profile::factory()->makeOne([ "username" => $username, @@ -287,7 +287,7 @@ final class DefaultCachedScraperServiceTest extends TestCase { $username = "kompot"; $testRequestHash = $this->requestHash(); - $now = Carbon::now(); + $now = Carbon::createFromDate(2022, 1, 11, "UTC")->addHours(8)->addMinutes(12); Carbon::setTestNow($now); $mockModel = Profile::factory()->makeOne([ "username" => $username, @@ -319,12 +319,12 @@ final class DefaultCachedScraperServiceTest extends TestCase $malId = 1; $username = "kompot"; $testRequestHash = $this->requestHash(); - $now = Carbon::now(); + $now = Carbon::createFromDate(2022, 1, 11, "UTC")->addHours(8)->addMinutes(12); $mockModel = Profile::factory()->makeOne([ "mal_id" => $malId, "username" => $username, - "modifiedAt" => new UTCDateTime($now->sub("3 days")->getPreciseTimestamp(3)), - "createdAt" => new UTCDateTime($now->sub("3 days")->getPreciseTimestamp(3)) + "modifiedAt" => new UTCDateTime($now->copy()->sub("3 days")->getPreciseTimestamp(3)), + "createdAt" => new UTCDateTime($now->copy()->sub("3 days")->getPreciseTimestamp(3)) ]); $now = Carbon::now(); Carbon::setTestNow($now);