mirror of
https://github.com/NNTmux/newznab-tmux.git
synced 2026-08-29 09:18:54 +00:00
652 lines
21 KiB
PHP
652 lines
21 KiB
PHP
<?php
|
|
|
|
namespace Tests\Unit;
|
|
|
|
use App\Services\TmdbClient;
|
|
use Illuminate\Http\Client\ConnectionException;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Tests\TestCase;
|
|
|
|
class TmdbClientTest extends TestCase
|
|
{
|
|
protected TmdbClient $client;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
config(['tmdb.api_key' => 'test_api_key']);
|
|
config(['tmdb.timeout' => 30]);
|
|
config(['tmdb.retry_times' => 3]);
|
|
config(['tmdb.retry_delay' => 100]);
|
|
config(['cache.default' => 'array']);
|
|
$this->client = new TmdbClient;
|
|
}
|
|
|
|
// =========================================================================
|
|
// CONFIGURATION TESTS
|
|
// =========================================================================
|
|
|
|
public function test_is_configured_returns_true_when_api_key_set(): void
|
|
{
|
|
$this->assertTrue($this->client->isConfigured());
|
|
}
|
|
|
|
public function test_is_configured_returns_false_when_api_key_empty(): void
|
|
{
|
|
config(['tmdb.api_key' => '']);
|
|
$client = new TmdbClient;
|
|
$this->assertFalse($client->isConfigured());
|
|
}
|
|
|
|
public function test_is_configured_returns_false_when_api_key_null(): void
|
|
{
|
|
// When config returns null, the (string) cast converts it to empty string
|
|
config(['tmdb.api_key' => null]);
|
|
$client = new TmdbClient;
|
|
// (string) null === '', so isConfigured returns false
|
|
$this->assertFalse($client->isConfigured());
|
|
}
|
|
|
|
// =========================================================================
|
|
// IMAGE URL TESTS
|
|
// =========================================================================
|
|
|
|
public function test_get_image_url_returns_full_url(): void
|
|
{
|
|
$url = $this->client->getImageUrl('/abc123.jpg', 'w500');
|
|
$this->assertSame('https://image.tmdb.org/t/p/w500/abc123.jpg', $url);
|
|
}
|
|
|
|
public function test_get_image_url_returns_empty_for_null_path(): void
|
|
{
|
|
$url = $this->client->getImageUrl(null);
|
|
$this->assertSame('', $url);
|
|
}
|
|
|
|
public function test_get_image_url_returns_empty_for_empty_path(): void
|
|
{
|
|
$url = $this->client->getImageUrl('');
|
|
$this->assertSame('', $url);
|
|
}
|
|
|
|
public function test_get_image_url_uses_default_size(): void
|
|
{
|
|
$url = $this->client->getImageUrl('/test.jpg');
|
|
$this->assertSame('https://image.tmdb.org/t/p/w500/test.jpg', $url);
|
|
}
|
|
|
|
public function test_get_image_url_with_original_size(): void
|
|
{
|
|
$url = $this->client->getImageUrl('/test.jpg', 'original');
|
|
$this->assertSame('https://image.tmdb.org/t/p/original/test.jpg', $url);
|
|
}
|
|
|
|
// =========================================================================
|
|
// MOVIE SEARCH TESTS
|
|
// =========================================================================
|
|
|
|
public function test_search_movies_makes_correct_api_call(): void
|
|
{
|
|
Http::fake([
|
|
'api.themoviedb.org/3/search/movie*' => Http::response([
|
|
'page' => 1,
|
|
'results' => [
|
|
['id' => 123, 'title' => 'Test Movie', 'release_date' => '2024-01-15'],
|
|
],
|
|
'total_results' => 1,
|
|
'total_pages' => 1,
|
|
]),
|
|
]);
|
|
|
|
$result = $this->client->searchMovies('Test Movie');
|
|
|
|
$this->assertNotNull($result);
|
|
$this->assertSame(1, $result['total_results']);
|
|
$this->assertCount(1, $result['results']);
|
|
$this->assertSame('Test Movie', $result['results'][0]['title']);
|
|
|
|
Http::assertSent(function ($request) {
|
|
return str_contains($request->url(), 'api.themoviedb.org/3/search/movie')
|
|
&& $request['query'] === 'Test Movie'
|
|
&& $request['api_key'] === 'test_api_key';
|
|
});
|
|
}
|
|
|
|
public function test_search_movies_with_year_filter(): void
|
|
{
|
|
Http::fake([
|
|
'api.themoviedb.org/3/search/movie*' => Http::response([
|
|
'page' => 1,
|
|
'results' => [],
|
|
'total_results' => 0,
|
|
]),
|
|
]);
|
|
|
|
$this->client->searchMovies('Test', 1, '2024');
|
|
|
|
Http::assertSent(function ($request) {
|
|
return $request['year'] === '2024';
|
|
});
|
|
}
|
|
|
|
public function test_search_movies_returns_null_on_api_failure(): void
|
|
{
|
|
Http::fake([
|
|
'api.themoviedb.org/3/search/movie*' => Http::response([], 500),
|
|
]);
|
|
|
|
$result = $this->client->searchMovies('Test');
|
|
$this->assertNull($result);
|
|
}
|
|
|
|
public function test_search_movies_returns_null_when_not_configured(): void
|
|
{
|
|
config(['tmdb.api_key' => '']);
|
|
$client = new TmdbClient;
|
|
|
|
$result = $client->searchMovies('Test');
|
|
$this->assertNull($result);
|
|
}
|
|
|
|
// =========================================================================
|
|
// GET MOVIE TESTS
|
|
// =========================================================================
|
|
|
|
public function test_get_movie_returns_movie_data(): void
|
|
{
|
|
Http::fake([
|
|
'api.themoviedb.org/3/movie/550*' => Http::response([
|
|
'id' => 550,
|
|
'title' => 'Fight Club',
|
|
'overview' => 'A ticking-Loss time bomb of a movie...',
|
|
'release_date' => '1999-10-15',
|
|
'vote_average' => 8.4,
|
|
'poster_path' => '/poster.jpg',
|
|
'backdrop_path' => '/backdrop.jpg',
|
|
'imdb_id' => 'tt0137523',
|
|
]),
|
|
]);
|
|
|
|
$result = $this->client->getMovie(550);
|
|
|
|
$this->assertNotNull($result);
|
|
$this->assertSame(550, $result['id']);
|
|
$this->assertSame('Fight Club', $result['title']);
|
|
$this->assertSame('tt0137523', $result['imdb_id']);
|
|
}
|
|
|
|
public function test_get_movie_with_append_to_response(): void
|
|
{
|
|
Http::fake([
|
|
'api.themoviedb.org/3/movie/550*' => Http::response([
|
|
'id' => 550,
|
|
'title' => 'Fight Club',
|
|
'credits' => [
|
|
'cast' => [['name' => 'Brad Pitt']],
|
|
'crew' => [['name' => 'David Fincher', 'job' => 'Director']],
|
|
],
|
|
]),
|
|
]);
|
|
|
|
$result = $this->client->getMovie(550, ['credits']);
|
|
|
|
Http::assertSent(function ($request) {
|
|
return str_contains($request->url(), 'append_to_response=credits');
|
|
});
|
|
|
|
$this->assertNotNull($result);
|
|
$this->assertArrayHasKey('credits', $result);
|
|
}
|
|
|
|
public function test_get_movie_with_imdb_id(): void
|
|
{
|
|
Http::fake([
|
|
'api.themoviedb.org/3/find/tt0137523*' => Http::response([
|
|
'movie_results' => [
|
|
['id' => 550],
|
|
],
|
|
]),
|
|
'api.themoviedb.org/3/movie/550*' => Http::response([
|
|
'id' => 550,
|
|
'title' => 'Fight Club',
|
|
'imdb_id' => 'tt0137523',
|
|
]),
|
|
]);
|
|
|
|
$result = $this->client->getMovie('tt0137523');
|
|
|
|
Http::assertSent(function ($request) {
|
|
return str_contains($request->url(), '/find/tt0137523')
|
|
&& str_contains($request->url(), 'external_source=imdb_id');
|
|
});
|
|
|
|
Http::assertSent(function ($request) {
|
|
return str_contains($request->url(), '/movie/550');
|
|
});
|
|
|
|
$this->assertNotNull($result);
|
|
$this->assertSame(550, $result['id']);
|
|
$this->assertSame('tt0137523', $result['imdb_id']);
|
|
}
|
|
|
|
public function test_get_movie_returns_null_on_404(): void
|
|
{
|
|
Http::fake([
|
|
'api.themoviedb.org/3/movie/*' => Http::response([], 404),
|
|
]);
|
|
|
|
$result = $this->client->getMovie(999999999);
|
|
$this->assertNull($result);
|
|
}
|
|
|
|
// =========================================================================
|
|
// TV SEARCH TESTS
|
|
// =========================================================================
|
|
|
|
public function test_search_tv_returns_results(): void
|
|
{
|
|
Http::fake([
|
|
'api.themoviedb.org/3/search/tv*' => Http::response([
|
|
'page' => 1,
|
|
'results' => [
|
|
['id' => 1396, 'name' => 'Breaking Bad', 'first_air_date' => '2008-01-20'],
|
|
],
|
|
'total_results' => 1,
|
|
]),
|
|
]);
|
|
|
|
$result = $this->client->searchTv('Breaking Bad');
|
|
|
|
$this->assertNotNull($result);
|
|
$this->assertCount(1, $result['results']);
|
|
$this->assertSame('Breaking Bad', $result['results'][0]['name']);
|
|
}
|
|
|
|
public function test_search_tv_with_first_air_date_year(): void
|
|
{
|
|
Http::fake([
|
|
'api.themoviedb.org/3/search/tv*' => Http::response([
|
|
'results' => [],
|
|
'total_results' => 0,
|
|
]),
|
|
]);
|
|
|
|
$this->client->searchTv('Test Show', 1, 2020);
|
|
|
|
Http::assertSent(function ($request) {
|
|
return $request['first_air_date_year'] === 2020;
|
|
});
|
|
}
|
|
|
|
// =========================================================================
|
|
// TV SHOW DETAILS TESTS
|
|
// =========================================================================
|
|
|
|
public function test_get_tv_show_returns_show_data(): void
|
|
{
|
|
Http::fake([
|
|
'api.themoviedb.org/3/tv/1396*' => Http::response([
|
|
'id' => 1396,
|
|
'name' => 'Breaking Bad',
|
|
'overview' => 'A high school chemistry teacher...',
|
|
'first_air_date' => '2008-01-20',
|
|
'seasons' => [
|
|
['season_number' => 1, 'episode_count' => 7],
|
|
],
|
|
]),
|
|
]);
|
|
|
|
$result = $this->client->getTvShow(1396);
|
|
|
|
$this->assertNotNull($result);
|
|
$this->assertSame(1396, $result['id']);
|
|
$this->assertSame('Breaking Bad', $result['name']);
|
|
}
|
|
|
|
public function test_get_tv_external_ids(): void
|
|
{
|
|
Http::fake([
|
|
'api.themoviedb.org/3/tv/1396/external_ids*' => Http::response([
|
|
'imdb_id' => 'tt0903747',
|
|
'tvdb_id' => 81189,
|
|
'tvrage_id' => 18164,
|
|
]),
|
|
]);
|
|
|
|
$result = $this->client->getTvExternalIds(1396);
|
|
|
|
$this->assertNotNull($result);
|
|
$this->assertSame('tt0903747', $result['imdb_id']);
|
|
$this->assertSame(81189, $result['tvdb_id']);
|
|
}
|
|
|
|
public function test_get_tv_alternative_titles(): void
|
|
{
|
|
Http::fake([
|
|
'api.themoviedb.org/3/tv/1396/alternative_titles*' => Http::response([
|
|
'id' => 1396,
|
|
'results' => [
|
|
['title' => 'Breaking Bad - Reazioni collaterali', 'iso_3166_1' => 'IT'],
|
|
],
|
|
]),
|
|
]);
|
|
|
|
$result = $this->client->getTvAlternativeTitles(1396);
|
|
|
|
$this->assertNotNull($result);
|
|
$this->assertCount(1, $result['results']);
|
|
}
|
|
|
|
// =========================================================================
|
|
// TV SEASON AND EPISODE TESTS
|
|
// =========================================================================
|
|
|
|
public function test_get_tv_season_returns_season_data(): void
|
|
{
|
|
Http::fake([
|
|
'api.themoviedb.org/3/tv/1396/season/1*' => Http::response([
|
|
'id' => 3572,
|
|
'season_number' => 1,
|
|
'episodes' => [
|
|
[
|
|
'id' => 62085,
|
|
'name' => 'Pilot',
|
|
'episode_number' => 1,
|
|
'season_number' => 1,
|
|
'air_date' => '2008-01-20',
|
|
'overview' => 'When an unassuming high school chemistry teacher...',
|
|
],
|
|
],
|
|
]),
|
|
]);
|
|
|
|
$result = $this->client->getTvSeason(1396, 1);
|
|
|
|
$this->assertNotNull($result);
|
|
$this->assertSame(1, $result['season_number']);
|
|
$this->assertCount(1, $result['episodes']);
|
|
}
|
|
|
|
public function test_get_tv_episode_returns_episode_data(): void
|
|
{
|
|
Http::fake([
|
|
'api.themoviedb.org/3/tv/1396/season/1/episode/1*' => Http::response([
|
|
'id' => 62085,
|
|
'name' => 'Pilot',
|
|
'episode_number' => 1,
|
|
'season_number' => 1,
|
|
'air_date' => '2008-01-20',
|
|
'overview' => 'When an unassuming high school chemistry teacher...',
|
|
]),
|
|
]);
|
|
|
|
$result = $this->client->getTvEpisode(1396, 1, 1);
|
|
|
|
$this->assertNotNull($result);
|
|
$this->assertSame('Pilot', $result['name']);
|
|
$this->assertSame(1, $result['episode_number']);
|
|
$this->assertSame(1, $result['season_number']);
|
|
}
|
|
|
|
// =========================================================================
|
|
// HELPER METHOD TESTS
|
|
// =========================================================================
|
|
|
|
public function test_get_string_returns_string_value(): void
|
|
{
|
|
$data = ['title' => 'Test Movie', 'count' => 5];
|
|
$this->assertSame('Test Movie', TmdbClient::getString($data, 'title'));
|
|
}
|
|
|
|
public function test_get_string_returns_default_for_missing_key(): void
|
|
{
|
|
$data = ['title' => 'Test'];
|
|
$this->assertSame('', TmdbClient::getString($data, 'missing'));
|
|
$this->assertSame('default', TmdbClient::getString($data, 'missing', 'default'));
|
|
}
|
|
|
|
public function test_get_string_returns_default_for_non_string_value(): void
|
|
{
|
|
$data = ['count' => 123, 'list' => ['a', 'b']];
|
|
$this->assertSame('', TmdbClient::getString($data, 'count'));
|
|
$this->assertSame('', TmdbClient::getString($data, 'list'));
|
|
}
|
|
|
|
public function test_get_int_returns_integer_value(): void
|
|
{
|
|
$data = ['id' => 550, 'score' => '85', 'rating' => 7.5];
|
|
$this->assertSame(550, TmdbClient::getInt($data, 'id'));
|
|
$this->assertSame(85, TmdbClient::getInt($data, 'score'));
|
|
$this->assertSame(7, TmdbClient::getInt($data, 'rating'));
|
|
}
|
|
|
|
public function test_get_int_returns_default_for_missing_key(): void
|
|
{
|
|
$data = ['id' => 550];
|
|
$this->assertSame(0, TmdbClient::getInt($data, 'missing'));
|
|
$this->assertSame(99, TmdbClient::getInt($data, 'missing', 99));
|
|
}
|
|
|
|
public function test_get_int_returns_default_for_non_numeric_value(): void
|
|
{
|
|
$data = ['title' => 'Test', 'list' => [1, 2]];
|
|
$this->assertSame(0, TmdbClient::getInt($data, 'title'));
|
|
$this->assertSame(0, TmdbClient::getInt($data, 'list'));
|
|
}
|
|
|
|
public function test_get_float_returns_float_value(): void
|
|
{
|
|
$data = ['rating' => 7.5, 'score' => '8.3', 'count' => 100];
|
|
$this->assertSame(7.5, TmdbClient::getFloat($data, 'rating'));
|
|
$this->assertSame(8.3, TmdbClient::getFloat($data, 'score'));
|
|
$this->assertSame(100.0, TmdbClient::getFloat($data, 'count'));
|
|
}
|
|
|
|
public function test_get_float_returns_default_for_missing_key(): void
|
|
{
|
|
$data = ['rating' => 7.5];
|
|
$this->assertSame(0.0, TmdbClient::getFloat($data, 'missing'));
|
|
$this->assertSame(5.5, TmdbClient::getFloat($data, 'missing', 5.5));
|
|
}
|
|
|
|
public function test_get_array_returns_array_value(): void
|
|
{
|
|
$data = ['genres' => [['name' => 'Action'], ['name' => 'Drama']]];
|
|
$result = TmdbClient::getArray($data, 'genres');
|
|
$this->assertIsArray($result);
|
|
$this->assertCount(2, $result);
|
|
}
|
|
|
|
public function test_get_array_returns_default_for_missing_key(): void
|
|
{
|
|
$data = ['title' => 'Test'];
|
|
$this->assertSame([], TmdbClient::getArray($data, 'genres'));
|
|
$this->assertSame(['default'], TmdbClient::getArray($data, 'genres', ['default']));
|
|
}
|
|
|
|
public function test_get_array_returns_default_for_non_array_value(): void
|
|
{
|
|
$data = ['title' => 'Test', 'count' => 5];
|
|
$this->assertSame([], TmdbClient::getArray($data, 'title'));
|
|
$this->assertSame([], TmdbClient::getArray($data, 'count'));
|
|
}
|
|
|
|
public function test_get_nested_returns_nested_value(): void
|
|
{
|
|
$data = [
|
|
'credits' => [
|
|
'cast' => [
|
|
['name' => 'Actor 1'],
|
|
['name' => 'Actor 2'],
|
|
],
|
|
],
|
|
];
|
|
|
|
$cast = TmdbClient::getNested($data, 'credits.cast');
|
|
$this->assertIsArray($cast);
|
|
$this->assertCount(2, $cast);
|
|
}
|
|
|
|
public function test_get_nested_returns_default_for_missing_path(): void
|
|
{
|
|
$data = ['title' => 'Test'];
|
|
$this->assertNull(TmdbClient::getNested($data, 'credits.cast'));
|
|
$this->assertSame([], TmdbClient::getNested($data, 'credits.cast', []));
|
|
}
|
|
|
|
public function test_get_nested_handles_deep_paths(): void
|
|
{
|
|
$data = [
|
|
'level1' => [
|
|
'level2' => [
|
|
'level3' => [
|
|
'value' => 'deep',
|
|
],
|
|
],
|
|
],
|
|
];
|
|
|
|
$this->assertSame('deep', TmdbClient::getNested($data, 'level1.level2.level3.value'));
|
|
}
|
|
|
|
public function test_get_nested_returns_default_when_path_breaks(): void
|
|
{
|
|
$data = [
|
|
'level1' => [
|
|
'level2' => 'not an array',
|
|
],
|
|
];
|
|
|
|
$this->assertSame('default', TmdbClient::getNested($data, 'level1.level2.level3', 'default'));
|
|
}
|
|
|
|
// =========================================================================
|
|
// ERROR HANDLING TESTS
|
|
// =========================================================================
|
|
|
|
public function test_handles_connection_timeout(): void
|
|
{
|
|
Http::fake([
|
|
'api.themoviedb.org/*' => function () {
|
|
throw new ConnectionException('Connection timed out');
|
|
},
|
|
]);
|
|
|
|
$result = $this->client->searchMovies('Test');
|
|
$this->assertNull($result);
|
|
}
|
|
|
|
public function test_handles_invalid_json_response(): void
|
|
{
|
|
Http::fake([
|
|
'api.themoviedb.org/*' => Http::response('invalid json', 200),
|
|
]);
|
|
|
|
$result = $this->client->searchMovies('Test');
|
|
// Laravel's Http client returns null for invalid JSON
|
|
$this->assertNull($result);
|
|
}
|
|
|
|
public function test_handles_rate_limiting_response(): void
|
|
{
|
|
Http::fake([
|
|
'api.themoviedb.org/*' => Http::response([
|
|
'status_code' => 25,
|
|
'status_message' => 'Your request count is over the allowed limit.',
|
|
], 429),
|
|
]);
|
|
|
|
$result = $this->client->searchMovies('Test');
|
|
$this->assertNull($result);
|
|
}
|
|
|
|
// =========================================================================
|
|
// PARAMETER VALIDATION TESTS
|
|
// =========================================================================
|
|
|
|
public function test_get_tv_season_returns_null_for_invalid_tv_id(): void
|
|
{
|
|
// Should not make any API call
|
|
Http::fake();
|
|
|
|
$this->assertNull($this->client->getTvSeason(0, 1));
|
|
$this->assertNull($this->client->getTvSeason(-1, 1));
|
|
|
|
Http::assertNothingSent();
|
|
}
|
|
|
|
public function test_get_tv_season_returns_null_for_negative_season(): void
|
|
{
|
|
// Should not make any API call
|
|
Http::fake();
|
|
|
|
$this->assertNull($this->client->getTvSeason(1396, -1));
|
|
|
|
Http::assertNothingSent();
|
|
}
|
|
|
|
public function test_get_tv_episode_returns_null_for_invalid_tv_id(): void
|
|
{
|
|
// Should not make any API call
|
|
Http::fake();
|
|
|
|
$this->assertNull($this->client->getTvEpisode(0, 1, 1));
|
|
$this->assertNull($this->client->getTvEpisode(-1, 1, 1));
|
|
|
|
Http::assertNothingSent();
|
|
}
|
|
|
|
public function test_get_tv_episode_returns_null_for_negative_season(): void
|
|
{
|
|
// Should not make any API call
|
|
Http::fake();
|
|
|
|
$this->assertNull($this->client->getTvEpisode(1396, -1, 1));
|
|
|
|
Http::assertNothingSent();
|
|
}
|
|
|
|
public function test_get_tv_episode_returns_null_for_negative_episode(): void
|
|
{
|
|
// Should not make any API call
|
|
Http::fake();
|
|
|
|
$this->assertNull($this->client->getTvEpisode(1396, 1, -1));
|
|
|
|
Http::assertNothingSent();
|
|
}
|
|
|
|
public function test_get_tv_season_allows_season_zero_for_specials(): void
|
|
{
|
|
Http::fake([
|
|
'api.themoviedb.org/3/tv/1396/season/0*' => Http::response([
|
|
'id' => 3577,
|
|
'season_number' => 0,
|
|
'name' => 'Specials',
|
|
]),
|
|
]);
|
|
|
|
$result = $this->client->getTvSeason(1396, 0);
|
|
|
|
$this->assertNotNull($result);
|
|
$this->assertSame(0, $result['season_number']);
|
|
}
|
|
|
|
public function test_get_tv_episode_allows_season_and_episode_zero(): void
|
|
{
|
|
Http::fake([
|
|
'api.themoviedb.org/3/tv/1396/season/0/episode/0*' => Http::response([
|
|
'id' => 12345,
|
|
'name' => 'Special Episode',
|
|
'episode_number' => 0,
|
|
'season_number' => 0,
|
|
]),
|
|
]);
|
|
|
|
$result = $this->client->getTvEpisode(1396, 0, 0);
|
|
|
|
$this->assertNotNull($result);
|
|
$this->assertSame(0, $result['episode_number']);
|
|
}
|
|
}
|