Update & Complete tests

This commit is contained in:
Bubka 2025-06-13 16:46:59 +02:00
parent 6909be3318
commit 48cc7d76e4
29 changed files with 545 additions and 193 deletions

View File

@ -139,10 +139,9 @@ abstract class AbstractLogoLib implements LogoLibInterface
$url = $this->logoUrl($logoFilename);
try {
// $response = Http::withOptions([
// 'proxy' => config('2fauth.config.outgoingProxy'),
// ])->retry(3, 100)->get($url);
$response = Http::get($url);
$response = Http::withOptions([
'proxy' => config('2fauth.config.outgoingProxy'),
])->retry(3, 100)->get($url);
if ($response->successful()) {
$filename = $this->cachePrefix . $logoFilename;

View File

@ -6,13 +6,11 @@ use App\Api\v1\Controllers\IconController;
use App\Facades\IconStore;
use App\Models\TwoFAccount;
use App\Models\User;
use App\Services\LogoLib\TfaLogoLib;
use Illuminate\Http\Testing\FileFactory;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Storage;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProviderExternal;
use PHPUnit\Framework\Attributes\Test;
use Tests\Classes\LocalFile;
use Tests\Data\CommonDataProvider;
@ -39,12 +37,6 @@ class IconControllerTest extends FeatureTestCase
Storage::fake('logos');
Http::preventStrayRequests();
Http::fake([
'https://raw.githubusercontent.com/2factorauth/twofactorauth/master/img/*' => Http::response(HttpRequestTestData::SVG_LOGO_BODY, 200),
'https://cdn.jsdelivr.net/gh/selfhst/icons/*' => Http::response(HttpRequestTestData::SVG_LOGO_BODY, 200),
'https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/*' => Http::response(HttpRequestTestData::SVG_LOGO_BODY, 200),
TfalogoLib::TFA_URL => Http::response(HttpRequestTestData::TFA_JSON_BODY, 200),
]);
Http::fake([
OtpTestData::EXTERNAL_IMAGE_URL_DECODED => Http::response((new FileFactory)->image('file.png', 10, 10)->tempFile, 200),
]);
@ -105,13 +97,15 @@ class IconControllerTest extends FeatureTestCase
}
#[Test]
#[DataProviderExternal(CommonDataProvider::class, 'iconsCollectionProvider')]
public function test_fetch_logo_returns_filename($iconCollection)
public function test_fetch_logo_returns_filename()
{
Http::fake([
CommonDataProvider::SELFH_URL => Http::response(HttpRequestTestData::SVG_LOGO_BODY, 200),
]);
$response = $this->actingAs($this->user, 'api-guard')
->json('POST', '/api/v1/icons/default', [
'service' => 'service',
'iconCollection' => $iconCollection,
])
->assertStatus(201)
->assertJsonStructure([
@ -120,12 +114,50 @@ class IconControllerTest extends FeatureTestCase
}
#[Test]
public function test_fetch_logo_with_infected_svg_data_stores_sanitized_svg_content()
public function test_fetch_logo_using_specified_icon_collection_returns_filename()
{
Http::fake([
CommonDataProvider::SELFH_URL => Http::response(HttpRequestTestData::SVG_LOGO_BODY, 200),
CommonDataProvider::DASHBOARDICONS_URL => Http::response(OtpTestData::ICON_SVG_DATA, 200),
]);
$response = $this->actingAs($this->user, 'api-guard')
->json('POST', '/api/v1/icons/default', [
'service' => 'service',
'iconCollection' => 'dashboardicons',
])
->assertStatus(201)
->assertJsonStructure([
'filename',
]);
// Don't know why but 'getData()->filename' has some unwanted spaces in it so we trim them
$svgContent = trim(str_replace('> <', '><', IconStore::get($response->getData()->filename)));
$this->assertEquals(OtpTestData::ICON_SVG_DATA, $svgContent);
}
#[Test]
public function test_fetch_logo_return_validation_error()
{
$response = $this->actingAs($this->user, 'api-guard')
->json('POST', '/api/v1/icons/default', [
'service' => 'service',
'iconCollection' => 'not_a_valid_icon_collection',
])
->assertStatus(422);
}
#[Test]
public function test_fetch_logo_with_infected_svg_data_stores_sanitized_svg_content()
{
Http::fake([
CommonDataProvider::SELFH_URL => Http::response(OtpTestData::ICON_SVG_DATA_INFECTED, 200),
]);
$response = $this->actingAs($this->user, 'api-guard')
->json('POST', '/api/v1/icons/default', [
'service' => 'service',
'iconCollection' => 'tfa',
])
->assertStatus(201)
->assertJsonStructure([
@ -139,9 +171,13 @@ class IconControllerTest extends FeatureTestCase
#[Test]
public function test_fetch_unknown_logo_returns_nothing()
{
Http::fake([
CommonDataProvider::SELFH_URL => Http::response('not found', 404),
]);
$response = $this->actingAs($this->user, 'api-guard')
->json('POST', '/api/v1/icons/default', [
'service' => 'unknown_company',
'service' => 'NameOfAnUnknownServiceForSure',
])
->assertNoContent();
}

View File

@ -25,6 +25,7 @@ use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Test;
use Tests\Classes\LocalFile;
use Tests\Data\CommonDataProvider;
use Tests\Data\HttpRequestTestData;
use Tests\Data\MigrationTestData;
use Tests\Data\OtpTestData;
@ -242,8 +243,8 @@ class TwoFAccountControllerTest extends FeatureTestCase
Http::preventStrayRequests();
Http::fake([
TfaLogoLib::IMG_URL . '*' => Http::response(HttpRequestTestData::SVG_LOGO_BODY, 200),
TfaLogoLib::TFA_URL => Http::response(HttpRequestTestData::TFA_JSON_BODY, 200),
CommonDataProvider::TFA_URL => Http::response(HttpRequestTestData::SVG_LOGO_BODY, 200),
TfaLogoLib::TFA_JSON_URL => Http::response(HttpRequestTestData::TFA_JSON_BODY, 200),
OtpTestData::EXTERNAL_IMAGE_URL_DECODED => Http::response((new FileFactory)->image('file.png', 10, 10)->tempFile, 200),
OtpTestData::EXTERNAL_INFECTED_IMAGE_URL_DECODED => Http::response((new FileFactory)->createWithContent('infected.svg', OtpTestData::ICON_SVG_DATA_INFECTED)->tempFile, 200),
'example.com/*' => Http::response(null, 400),

View File

@ -35,7 +35,9 @@ class GroupAssignRequestTest extends TestCase
#[DataProvider('provideValidData')]
public function test_valid_data(array $data) : void
{
$request = new GroupAssignRequest;
$request = new GroupAssignRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertFalse($validator->fails());
@ -59,7 +61,9 @@ class GroupAssignRequestTest extends TestCase
#[DataProvider('provideInvalidData')]
public function test_invalid_data(array $data) : void
{
$request = new GroupAssignRequest;
$request = new GroupAssignRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertTrue($validator->fails());

View File

@ -35,7 +35,9 @@ class IconFetchRequestTest extends TestCase
#[DataProvider('provideValidData')]
public function test_valid_data(array $data) : void
{
$request = new IconFetchRequest;
$request = new IconFetchRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertFalse($validator->fails());
@ -47,15 +49,59 @@ class IconFetchRequestTest extends TestCase
public static function provideValidData() : array
{
return [
[[
'VALID_SERVICE_AS_STRING' => [[
'service' => 'validWord',
]],
[[
'service' => '0',
]],
[[
'VALID_SERVCE_WITH_SPECIAL_CHARS' => [[
'service' => '~string.with-sp3ci@l-ch4rs',
]],
'VALID_SELFH_ICON_COLLECTION' => [[
'service' => 'validWord',
'iconCollection' => 'selfh',
]],
'VALID_DASHBOARDICONS_ICON_COLLECTION' => [[
'service' => 'validWord',
'iconCollection' => 'dashboardicons',
]],
'VALID_TFA_ICON_COLLECTION' => [[
'service' => 'validWord',
'iconCollection' => 'tfa',
]],
'VALID_SELFH_ICON_COLLECTION_WITH_VALID_REGULAR_VARIANT' => [[
'service' => 'validWord',
'iconCollection' => 'selfh',
'variant' => 'regular',
]],
'VALID_SELFH_ICON_COLLECTION_WITH_VALID_LIGHT_VARIANT' => [[
'service' => 'validWord',
'iconCollection' => 'selfh',
'variant' => 'light',
]],
'VALID_SELFH_ICON_COLLECTION_WITH_VALID_DARK_VARIANT' => [[
'service' => 'validWord',
'iconCollection' => 'selfh',
'variant' => 'dark',
]],
'VALID_DASHBOARDICONS_ICON_COLLECTION_WITH_VALID_REGULAR_VARIANT' => [[
'service' => 'validWord',
'iconCollection' => 'dashboardicons',
'variant' => 'regular',
]],
'VALID_DASHBOARDICONS_ICON_COLLECTION_WITH_VALID_LIGHT_VARIANT' => [[
'service' => 'validWord',
'iconCollection' => 'dashboardicons',
'variant' => 'light',
]],
'VALID_DASHBOARDICONS_ICON_COLLECTION_WITH_VALID_DARK_VARIANT' => [[
'service' => 'validWord',
'iconCollection' => 'dashboardicons',
'variant' => 'dark',
]],
'VALID_TFA_ICON_COLLECTION_WITH_VALID_REGULAR_VARIANT' => [[
'service' => 'validWord',
'iconCollection' => 'tfa',
'variant' => 'regular',
]],
];
}
@ -63,7 +109,9 @@ class IconFetchRequestTest extends TestCase
#[DataProvider('provideInvalidData')]
public function test_invalid_data(array $data) : void
{
$request = new IconFetchRequest;
$request = new IconFetchRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertTrue($validator->fails());
@ -75,18 +123,55 @@ class IconFetchRequestTest extends TestCase
public static function provideInvalidData() : array
{
return [
[[
'NULL_SERVICE' => [[
'service' => null,
]],
[[
'NULL_ICON_COLLECTION' => [[
'service' => 'validWord',
'iconCollection' => null,
]],
'NULL_VARIANT' => [[
'service' => 'validWord',
'iconCollection' => 'tfa',
'variant' => null,
]],
'EMPTY_ICON_COLLECTION' => [[
'service' => 'validWord',
'iconCollection' => '',
]],
'EMPTY_VARIANT' => [[
'service' => 'validWord',
'iconCollection' => 'tfa',
'variant' => '',
]],
'SERVICE_AS_INT' => [[
'service' => 0,
]],
[[
'SERVICE_AS_BOOL' => [[
'service' => true,
]],
[[
'SERVICE_AS_ARRAY' => [[
'service' => [],
]],
'NOT_IN_ICON_COLLECTION_LIST' => [[
'service' => 'validWord',
'iconCollection' => 'string_not_in_icon_collection_list',
]],
'NOT_IN_SELFH_VARIANT_LIST' => [[
'service' => 'validWord',
'iconCollection' => 'selfh',
'variant' => 'string_not_in_selfh_variant_list',
]],
'NOT_IN_DASHBOARDICONS_VARIANT_LIST' => [[
'service' => 'validWord',
'iconCollection' => 'dashboardicons',
'variant' => 'string_not_in_dashboardicons_variant_list',
]],
'NOT_IN_TFA_VARIANT_LIST' => [[
'service' => 'validWord',
'iconCollection' => 'tfa',
'variant' => 'string_not_in_tfa_variant_list',
]],
];
}
}

View File

@ -36,7 +36,9 @@ class QrCodeDecodeRequestTest extends TestCase
#[DataProvider('provideValidData')]
public function test_valid_data(array $data) : void
{
$request = new QrCodeDecodeRequest;
$request = new QrCodeDecodeRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertFalse($validator->fails());
@ -60,7 +62,9 @@ class QrCodeDecodeRequestTest extends TestCase
#[DataProvider('provideInvalidData')]
public function test_invalid_data(array $data) : void
{
$request = new QrCodeDecodeRequest;
$request = new QrCodeDecodeRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertTrue($validator->fails());

View File

@ -38,7 +38,9 @@ class SettingStoreRequestTest extends FeatureTestCase
#[DataProvider('provideValidData')]
public function test_valid_data(array $data) : void
{
$request = new SettingStoreRequest;
$request = new SettingStoreRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertFalse($validator->fails());
@ -71,7 +73,9 @@ class SettingStoreRequestTest extends FeatureTestCase
{
Settings::set(self::UNIQUE_KEY, 'uniqueValue');
$request = new SettingStoreRequest;
$request = new SettingStoreRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertTrue($validator->fails());

View File

@ -35,7 +35,9 @@ class SettingUpdateRequestTest extends TestCase
#[DataProvider('provideValidData')]
public function test_valid_data(array $data) : void
{
$request = new SettingUpdateRequest;
$request = new SettingUpdateRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertFalse($validator->fails());
@ -63,7 +65,9 @@ class SettingUpdateRequestTest extends TestCase
#[DataProvider('provideInvalidData')]
public function test_invalid_data(array $data) : void
{
$request = new SettingUpdateRequest;
$request = new SettingUpdateRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertTrue($validator->fails());

View File

@ -36,7 +36,9 @@ class TwoFAccountBatchRequestTest extends TestCase
#[DataProviderExternal(TwoFAccountDataProvider::class, 'validIdsProvider')]
public function test_valid_data(array $data) : void
{
$request = new TwoFAccountBatchRequest;
$request = new TwoFAccountBatchRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertFalse($validator->fails());
@ -46,7 +48,9 @@ class TwoFAccountBatchRequestTest extends TestCase
#[DataProviderExternal(TwoFAccountDataProvider::class, 'invalidIdsProvider')]
public function test_invalid_data(array $data) : void
{
$request = new TwoFAccountBatchRequest;
$request = new TwoFAccountBatchRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertTrue($validator->fails());

View File

@ -35,7 +35,9 @@ class TwoFAccountExportRequestTest extends TestCase
#[DataProvider('provideValidData')]
public function test_valid_data(array $data) : void
{
$request = new TwoFAccountExportRequest;
$request = new TwoFAccountExportRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertFalse($validator->fails());
@ -81,7 +83,9 @@ class TwoFAccountExportRequestTest extends TestCase
#[DataProvider('provideInvalidData')]
public function test_invalid_data(array $data) : void
{
$request = new TwoFAccountExportRequest;
$request = new TwoFAccountExportRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertTrue($validator->fails());

View File

@ -35,7 +35,9 @@ class TwoFAccountImportRequestTest extends TestCase
#[DataProvider('provideValidData')]
public function test_valid_data(array $data) : void
{
$request = new TwoFAccountImportRequest;
$request = new TwoFAccountImportRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertFalse($validator->fails());
@ -57,7 +59,9 @@ class TwoFAccountImportRequestTest extends TestCase
#[DataProvider('provideInvalidData')]
public function test_invalid_data(array $data) : void
{
$request = new TwoFAccountImportRequest;
$request = new TwoFAccountImportRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertTrue($validator->fails());

View File

@ -36,7 +36,9 @@ class TwoFAccountIndexRequestTest extends TestCase
#[DataProviderExternal(TwoFAccountDataProvider::class, 'validIdsProvider')]
public function test_valid_data(array $data) : void
{
$request = new TwoFAccountIndexRequest;
$request = new TwoFAccountIndexRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertFalse($validator->fails());
@ -46,7 +48,9 @@ class TwoFAccountIndexRequestTest extends TestCase
#[DataProviderExternal(TwoFAccountDataProvider::class, 'invalidIdsProvider')]
public function test_invalid_data(array $data) : void
{
$request = new TwoFAccountIndexRequest;
$request = new TwoFAccountIndexRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertTrue($validator->fails());

View File

@ -35,7 +35,9 @@ class TwoFAccountReorderRequestTest extends TestCase
#[DataProvider('provideValidData')]
public function test_valid_data(array $data) : void
{
$request = new TwoFAccountReorderRequest;
$request = new TwoFAccountReorderRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertFalse($validator->fails());
@ -60,7 +62,9 @@ class TwoFAccountReorderRequestTest extends TestCase
#[DataProvider('provideInvalidData')]
public function test_invalid_data(array $data) : void
{
$request = new TwoFAccountReorderRequest;
$request = new TwoFAccountReorderRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertTrue($validator->fails());

View File

@ -37,7 +37,9 @@ class TwoFAccountStoreRequestTest extends TestCase
#[DataProvider('provideValidData')]
public function test_valid_data(array $data) : void
{
$request = new TwoFAccountStoreRequest;
$request = new TwoFAccountStoreRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertFalse($validator->fails());
@ -111,7 +113,9 @@ class TwoFAccountStoreRequestTest extends TestCase
#[DataProvider('provideInvalidData')]
public function test_invalid_data(array $data) : void
{
$request = new TwoFAccountStoreRequest;
$request = new TwoFAccountStoreRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertTrue($validator->fails());

View File

@ -37,7 +37,9 @@ class TwoFAccountUpdateRequestTest extends TestCase
#[DataProvider('provideValidData')]
public function test_valid_data(array $data) : void
{
$request = new TwoFAccountUpdateRequest;
$request = new TwoFAccountUpdateRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertFalse($validator->fails());
@ -87,7 +89,9 @@ class TwoFAccountUpdateRequestTest extends TestCase
#[DataProvider('provideInvalidData')]
public function test_invalid_data(array $data) : void
{
$request = new TwoFAccountUpdateRequest;
$request = new TwoFAccountUpdateRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertTrue($validator->fails());

View File

@ -35,7 +35,9 @@ class TwoFAccountUriRequestTest extends TestCase
#[DataProvider('provideValidData')]
public function test_valid_data(array $data) : void
{
$request = new TwoFAccountUriRequest;
$request = new TwoFAccountUriRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertFalse($validator->fails());
@ -64,7 +66,9 @@ class TwoFAccountUriRequestTest extends TestCase
#[DataProvider('provideInvalidData')]
public function test_invalid_data(array $data) : void
{
$request = new TwoFAccountUriRequest;
$request = new TwoFAccountUriRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertTrue($validator->fails());

View File

@ -35,7 +35,9 @@ class UserManagerPromoteRequestTest extends TestCase
#[DataProvider('provideValidData')]
public function test_valid_data(array $data) : void
{
$request = new UserManagerPromoteRequest;
$request = new UserManagerPromoteRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertFalse($validator->fails());
@ -66,7 +68,9 @@ class UserManagerPromoteRequestTest extends TestCase
#[DataProvider('provideInvalidData')]
public function test_invalid_data(array $data) : void
{
$request = new UserManagerPromoteRequest;
$request = new UserManagerPromoteRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertTrue($validator->fails());

View File

@ -41,7 +41,9 @@ class UserManagerStoreRequestTest extends FeatureTestCase
'email' => 'jane@example.com',
]);
$request = new UserManagerStoreRequest;
$request = new UserManagerStoreRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertFalse($validator->fails());
@ -79,7 +81,9 @@ class UserManagerStoreRequestTest extends FeatureTestCase
'email' => 'john@example.com',
]);
$request = new UserManagerStoreRequest;
$request = new UserManagerStoreRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertTrue($validator->fails());

View File

@ -4,20 +4,34 @@ declare(strict_types=1);
namespace Tests\Data;
use App\Services\LogoLib\DashboardiconsLogoLib;
use App\Services\LogoLib\SelfhLogoLib;
use App\Services\LogoLib\TfaLogoLib;
final class CommonDataProvider
{
const TFA_URL = 'https://raw.githubusercontent.com/2factorauth/twofactorauth/master/img/*';
const SELFH_URL_ROOT = 'https://cdn.jsdelivr.net/gh/selfhst/icons/';
const SELFH_URL = self::SELFH_URL_ROOT . '*';
const DASHBOARDICONS_URL_ROOT = 'https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/';
const DASHBOARDICONS_URL = self::DASHBOARDICONS_URL_ROOT . '*';
public static function iconsCollectionProvider() : array
{
return [
'TFA' => [
'tfa',
],
'SELFH' => [
'selfh',
],
'DASHBOARDICONS' => [
'dashboardicons',
],
'TFA' => [[
'name' => 'tfa',
'class' => TfaLogoLib::class,
]],
'SELFH' => [[
'name' => 'selfh',
'class' => SelfhLogoLib::class,
]],
'DASHBOARDICONS' => [[
'name' => 'dashboardicons',
'class' => DashboardiconsLogoLib::class,
]],
];
}

View File

@ -10,6 +10,12 @@ class HttpRequestTestData
const SVG_LOGO_BODY = '<svg xmlns="http://www.w3.org/2000/svg" class="r-k200y r-13gxpu9 r-4qtqp9 r-yyyyoo r-np7d94 r-dnmrzs r-bnwqim r-1plcrui r-lrvibr" width="22.706" height="22.706"><path d="M22.706 4.311c-.835.37-1.732.62-2.675.733a4.67 4.67 0 002.048-2.578 9.3 9.3 0 01-2.958 1.13 4.66 4.66 0 00-7.938 4.25 13.229 13.229 0 01-9.602-4.868c-.4.69-.63 1.49-.63 2.342a4.66 4.66 0 002.072 3.878 4.647 4.647 0 01-2.11-.583v.06a4.66 4.66 0 003.737 4.568 4.692 4.692 0 01-2.104.08 4.661 4.661 0 004.352 3.234 9.348 9.348 0 01-5.786 1.995A9.5 9.5 0 010 18.487a13.175 13.175 0 007.14 2.093c8.57 0 13.255-7.098 13.255-13.254 0-.2-.005-.402-.014-.602a9.47 9.47 0 002.323-2.41z" fill="#1da1f2"/></svg>';
const SVG_LOGO_BODY_VARIANT_REGULAR = '<svg xmlns="http://www.w3.org/2000/svg" width="400" height="400" viewBox="0 0 124 124" fill="none"><rect width="124" height="124"></rect></svg>';
const SVG_LOGO_BODY_VARIANT_LIGHT = '<svg xmlns="http://www.w3.org/2000/svg" width="400" height="400" viewBox="0 0 124 124" fill="none"><rect width="124" height="124" fill="white"></rect></svg>';
const SVG_LOGO_BODY_VARIANT_DARK = '<svg xmlns="http://www.w3.org/2000/svg" width="400" height="400" viewBox="0 0 124 124" fill="none"><rect width="124" height="124" fill="black"></rect></svg>';
const TFA_JSON_BODY = '
[
[

View File

@ -35,7 +35,9 @@ class LoginRequestTest extends FeatureTestCase
'email' => 'JOHN.DOE@example.com',
]);
$request = new LoginRequest;
$request = new LoginRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertFalse($validator->fails());
@ -66,7 +68,9 @@ class LoginRequestTest extends FeatureTestCase
'email' => 'JOHN.DOE@example.com',
]);
$request = new LoginRequest;
$request = new LoginRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertTrue($validator->fails());

View File

@ -35,7 +35,9 @@ class UserDeleteRequestTest extends FeatureTestCase
#[DataProvider('provideValidData')]
public function test_valid_data(array $data) : void
{
$request = new UserDeleteRequest;
$request = new UserDeleteRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertFalse($validator->fails());
@ -57,7 +59,9 @@ class UserDeleteRequestTest extends FeatureTestCase
#[DataProvider('provideInvalidData')]
public function test_invalid_data(array $data) : void
{
$request = new UserDeleteRequest;
$request = new UserDeleteRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertTrue($validator->fails());

View File

@ -35,7 +35,9 @@ class UserPatchPwdRequestTest extends TestCase
#[DataProvider('provideValidData')]
public function test_valid_data(array $data) : void
{
$request = new UserPatchPwdRequest;
$request = new UserPatchPwdRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertFalse($validator->fails());
@ -59,7 +61,9 @@ class UserPatchPwdRequestTest extends TestCase
#[DataProvider('provideInvalidData')]
public function test_invalid_data(array $data) : void
{
$request = new UserPatchPwdRequest;
$request = new UserPatchPwdRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertTrue($validator->fails());

View File

@ -36,7 +36,9 @@ class UserStoreRequestTest extends FeatureTestCase
'email' => 'jane@example.com',
]);
$request = new UserStoreRequest;
$request = new UserStoreRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertFalse($validator->fails());
@ -72,7 +74,9 @@ class UserStoreRequestTest extends FeatureTestCase
'email' => 'john@example.com',
]);
$request = new UserStoreRequest;
$request = new UserStoreRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertTrue($validator->fails());

View File

@ -22,7 +22,9 @@ class WebauthnAssertedRequestTest extends TestCase
#[DataProvider('provideValidData')]
public function test_valid_data(array $data) : void
{
$request = new WebauthnAssertedRequest;
$request = new WebauthnAssertedRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertFalse($validator->fails());
@ -53,7 +55,9 @@ class WebauthnAssertedRequestTest extends TestCase
#[DataProvider('provideInvalidData')]
public function test_invalid_data(array $data) : void
{
$request = new WebauthnAssertedRequest;
$request = new WebauthnAssertedRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertTrue($validator->fails());

View File

@ -35,7 +35,9 @@ class WebauthnRenameRequestTest extends TestCase
#[DataProvider('provideValidData')]
public function test_valid_data(array $data) : void
{
$request = new WebauthnRenameRequest;
$request = new WebauthnRenameRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertFalse($validator->fails());
@ -57,7 +59,9 @@ class WebauthnRenameRequestTest extends TestCase
#[DataProvider('provideInvalidData')]
public function test_invalid_data(array $data) : void
{
$request = new WebauthnRenameRequest;
$request = new WebauthnRenameRequest;
$request->merge($data);
$validator = Validator::make($data, $request->rules());
$this->assertTrue($validator->fails());

View File

@ -40,10 +40,10 @@ class IconServiceTest extends FeatureTestCase
Http::preventStrayRequests();
Http::fake([
'https://raw.githubusercontent.com/2factorauth/twofactorauth/master/img/*' => Http::response(HttpRequestTestData::SVG_LOGO_BODY, 200),
'https://cdn.jsdelivr.net/gh/selfhst/icons/*' => Http::response(HttpRequestTestData::SVG_LOGO_BODY, 200),
'https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/*' => Http::response(HttpRequestTestData::SVG_LOGO_BODY, 200),
TfaLogoLib::TFA_URL => Http::response(HttpRequestTestData::TFA_JSON_BODY, 200),
CommonDataProvider::TFA_URL => Http::response(HttpRequestTestData::SVG_LOGO_BODY, 200),
CommonDataProvider::SELFH_URL => Http::response(HttpRequestTestData::SVG_LOGO_BODY, 200),
CommonDataProvider::DASHBOARDICONS_URL => Http::response(HttpRequestTestData::SVG_LOGO_BODY, 200),
TfaLogoLib::TFA_JSON_URL => Http::response(HttpRequestTestData::TFA_JSON_BODY, 200),
]);
Http::fake([
OtpTestData::EXTERNAL_IMAGE_URL_DECODED => Http::response((new FileFactory)->image('file.png', 10, 10)->tempFile, 200),

View File

@ -1,109 +0,0 @@
<?php
namespace Tests\Feature\Services;
use App\Services\LogoLib\TfaLogoLib;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Storage;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use Tests\Data\HttpRequestTestData;
use Tests\FeatureTestCase;
/**
* TfalogoLibTest test class
*/
#[CoversClass(TfalogoLib::class)]
class TfaLogoLibTest extends FeatureTestCase
{
use WithoutMiddleware;
protected TfaLogoLib $tfaLogoLib;
public function setUp() : void
{
parent::setUp();
Storage::fake('icons');
Storage::fake('logos');
Storage::fake('imagesLink');
}
#[Test]
public function test_getIcon_returns_stored_icon_file_when_logo_exists()
{
Http::preventStrayRequests();
Http::fake([
TfalogoLib::IMG_URL . '*' => Http::response(HttpRequestTestData::SVG_LOGO_BODY, 200),
TfalogoLib::TFA_URL => Http::response(HttpRequestTestData::TFA_JSON_BODY, 200),
]);
$this->tfaLogoLib = $this->app->make(TfalogoLib::class);
$icon = $this->tfaLogoLib->getIcon('service');
$this->assertNotNull($icon);
Storage::disk('icons')->assertExists($icon);
}
#[Test]
public function test_getIcon_returns_null_when_github_request_fails()
{
Http::preventStrayRequests();
Http::fake([
TfalogoLib::IMG_URL . '*' => Http::response(HttpRequestTestData::SVG_LOGO_BODY, 200),
TfalogoLib::TFA_URL => Http::response('not found', 404),
]);
$this->tfaLogoLib = $this->app->make(TfalogoLib::class);
$icon = $this->tfaLogoLib->getIcon('service');
$this->assertEquals(null, $icon);
}
#[Test]
public function test_getIcon_returns_null_when_logo_fetching_fails()
{
Http::preventStrayRequests();
Http::fake([
TfalogoLib::IMG_URL . '*' => Http::response('not found', 404),
TfalogoLib::TFA_URL => Http::response(HttpRequestTestData::TFA_JSON_BODY, 200),
]);
$this->tfaLogoLib = $this->app->make(TfalogoLib::class);
$icon = $this->tfaLogoLib->getIcon('service');
$this->assertEquals(null, $icon);
}
#[Test]
public function test_getIcon_returns_null_when_no_logo_exists()
{
Http::preventStrayRequests();
Http::fake([
TfalogoLib::IMG_URL . '*' => Http::response(HttpRequestTestData::SVG_LOGO_BODY, 200),
TfalogoLib::TFA_URL => Http::response(HttpRequestTestData::TFA_JSON_BODY, 200),
]);
$this->tfaLogoLib = $this->app->make(TfalogoLib::class);
$icon = $this->tfaLogoLib->getIcon('no_logo_should_exists_with_this_name');
$this->assertEquals(null, $icon);
}
#[Test]
public function test_TfalogoLib_loads_empty_collection_when_tfajson_fetching_fails()
{
Http::preventStrayRequests();
Http::fake([
TfalogoLib::IMG_URL . '*' => Http::response(HttpRequestTestData::SVG_LOGO_BODY, 200),
TfalogoLib::TFA_URL => Http::response('not found', 404),
]);
$this->tfaLogoLib = $this->app->make(TfalogoLib::class);
$icon = $this->tfaLogoLib->getIcon('service');
$this->assertNull($icon);
Storage::disk('logos')->assertMissing(TfalogoLib::TFA_JSON);
}
}

View File

@ -0,0 +1,240 @@
<?php
namespace Tests\Feature\Services;
use App\Models\User;
use App\Services\LogoLib\AbstractLogoLib;
use App\Services\LogoLib\DashboardiconsLogoLib;
use App\Services\LogoLib\LogoLibInterface;
use App\Services\LogoLib\SelfhLogoLib;
use App\Services\LogoLib\TfaLogoLib;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Storage;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\DataProviderExternal;
use PHPUnit\Framework\Attributes\Test;
use Tests\Data\CommonDataProvider;
use Tests\Data\HttpRequestTestData;
use Tests\Data\OtpTestData;
use Tests\FeatureTestCase;
/**
* LogoLibsTest test class
*/
#[CoversClass(AbstractLogoLib::class)]
#[CoversClass(SelfhLogoLib::class)]
#[CoversClass(DashboardiconsLogoLib::class)]
#[CoversClass(TfalogoLib::class)]
class LogoLibsTest extends FeatureTestCase
{
use WithoutMiddleware;
protected LogoLibInterface $logoLib;
public function setUp() : void
{
parent::setUp();
Storage::fake('icons');
Storage::fake('logos');
Storage::fake('imagesLink');
}
#[Test]
#[DataProvider('provideLogoData')]
public function test_getIcon_returns_stored_icon_file_in_requested_variant_when_logo_exists(array $logo)
{
Http::preventStrayRequests();
Http::fake([
$logo['url'] => Http::response($logo['svg'], 200),
]);
$this->logoLib = $this->app->make($logo['class']);
$icon = $this->logoLib->getIcon('myservice', $logo['variant']);
$this->assertNotNull($icon);
Storage::disk('icons')->assertExists($icon);
// Don't know why but with a faked storage, when an svg file is put to the disk, the svg is prefixed with an xml tag
// so we prepend it manually for the assert to work as expected.
$this->assertEquals(trim(Storage::disk('icons')->get($icon)), '<?xml version="1.0" encoding="UTF-8"?> ' . $logo['svg']);
}
/**
* Provide Valid data for validation test
*/
public static function provideLogoData() : array
{
return [
'SELFH_REGULAR' => [[
'variant' => 'regular',
'url' => CommonDataProvider::SELFH_URL_ROOT . 'svg/myservice.svg',
'svg' => HttpRequestTestData::SVG_LOGO_BODY_VARIANT_REGULAR,
'class' => SelfhLogoLib::class,
]],
'SELFH_LIGHT' => [[
'variant' => 'light',
'url' => CommonDataProvider::SELFH_URL_ROOT . 'svg/myservice-light.svg',
'svg' => HttpRequestTestData::SVG_LOGO_BODY_VARIANT_LIGHT,
'class' => SelfhLogoLib::class,
]],
'SELFH_DARK' => [[
'variant' => 'dark',
'url' => CommonDataProvider::SELFH_URL_ROOT . 'svg/myservice-dark.svg',
'svg' => HttpRequestTestData::SVG_LOGO_BODY_VARIANT_DARK,
'class' => SelfhLogoLib::class,
]],
'DASHBOARDICONS_REGULAR' => [[
'variant' => 'regular',
'url' => CommonDataProvider::DASHBOARDICONS_URL_ROOT . 'svg/myservice.svg',
'svg' => HttpRequestTestData::SVG_LOGO_BODY_VARIANT_LIGHT,
'class' => DashboardiconsLogoLib::class,
]],
'DASHBOARDICONS_LIGHT' => [[
'variant' => 'light',
'url' => CommonDataProvider::DASHBOARDICONS_URL_ROOT . 'svg/myservice-light.svg',
'svg' => HttpRequestTestData::SVG_LOGO_BODY_VARIANT_LIGHT,
'class' => DashboardiconsLogoLib::class,
]],
'DASHBOARDICONS_DARK' => [[
'variant' => 'dark',
'url' => CommonDataProvider::DASHBOARDICONS_URL_ROOT . 'svg/myservice-dark.svg',
'svg' => HttpRequestTestData::SVG_LOGO_BODY_VARIANT_DARK,
'class' => DashboardiconsLogoLib::class,
]],
];
}
#[Test]
public function test_getIcon_fetches_png_when_svg_is_not_available()
{
Http::preventStrayRequests();
Http::fake([
CommonDataProvider::SELFH_URL_ROOT . 'svg/myservice.svg' => Http::response('not found', 404),
CommonDataProvider::SELFH_URL_ROOT . 'png/myservice.png' => Http::response(base64_decode(OtpTestData::ICON_PNG_DATA), 200),
]);
$this->logoLib = $this->app->make(SelfhLogoLib::class);
$icon = $this->logoLib->getIcon('myservice');
$this->assertStringEndsWith('.png', $icon);
}
#[Test]
public function test_getIcon_fallbacks_to_user_preferences_when_variant_is_omitted()
{
$user = User::factory()->create();
$user['preferences->iconVariant'] = 'dark';
$user->save();
Http::preventStrayRequests();
Http::fake([
CommonDataProvider::SELFH_URL_ROOT . 'svg/myservice.svg' => Http::response(HttpRequestTestData::SVG_LOGO_BODY_VARIANT_REGULAR, 200),
CommonDataProvider::SELFH_URL_ROOT . 'svg/myservice-dark.svg' => Http::response(HttpRequestTestData::SVG_LOGO_BODY_VARIANT_DARK, 200),
]);
$this->logoLib = $this->app->make(SelfhLogoLib::class);
$icon = $this->actingAs($user)->logoLib->getIcon('myservice');
$this->assertEquals(trim(Storage::disk('icons')->get($icon)), '<?xml version="1.0" encoding="UTF-8"?> ' . HttpRequestTestData::SVG_LOGO_BODY_VARIANT_DARK);
}
#[Test]
#[DataProvider('provideVariantInvalidData')]
public function test_getIcon_fallbacks_to_regular_when_variant_is_invalid_without_auth($variant)
{
Http::preventStrayRequests();
Http::fake([
CommonDataProvider::SELFH_URL_ROOT . 'svg/myservice.svg' => Http::response(HttpRequestTestData::SVG_LOGO_BODY_VARIANT_REGULAR, 200),
]);
$this->logoLib = $this->app->make(SelfhLogoLib::class);
$icon = $this->logoLib->getIcon('myservice', $variant);
$this->assertNotNull($icon);
}
/**
* Provide invalid variant data for validation test
*/
public static function provideVariantInvalidData() : array
{
return [
'INVALID_VARIANT' => [
'not_a_valid_variant'
],
'NULL_VARIANT' => [
null
],
'EMPTY_VARIANT' => [
''
],
];
}
#[Test]
public function test_getIcon_returns_null_when_tfa_github_request_fails()
{
Http::preventStrayRequests();
Http::fake([
CommonDataProvider::TFA_URL => Http::response(HttpRequestTestData::SVG_LOGO_BODY, 200),
TfalogoLib::TFA_JSON_URL => Http::response('not found', 404),
]);
$this->logoLib = $this->app->make(TfalogoLib::class);
$icon = $this->logoLib->getIcon('service');
$this->assertEquals(null, $icon);
}
#[Test]
#[DataProviderExternal(CommonDataProvider::class, 'iconsCollectionProvider')]
public function test_getIcon_returns_null_when_logo_fetching_fails(array $iconProvider)
{
Http::preventStrayRequests();
Http::fake([
CommonDataProvider::TFA_URL => Http::response('not found', 404),
CommonDataProvider::SELFH_URL => Http::response('not found', 404),
CommonDataProvider::DASHBOARDICONS_URL => Http::response('not found', 404),
TfalogoLib::TFA_JSON_URL => Http::response(HttpRequestTestData::TFA_JSON_BODY, 200),
]);
$this->logoLib = $this->app->make($iconProvider['class']);
$icon = $this->logoLib->getIcon('service');
$this->assertEquals(null, $icon);
}
#[Test]
public function test_getIcon_returns_null_when_no_logo_exists_in_the_tfa_collection()
{
Http::preventStrayRequests();
Http::fake([
CommonDataProvider::TFA_URL => Http::response(HttpRequestTestData::SVG_LOGO_BODY, 200),
TfalogoLib::TFA_JSON_URL => Http::response(HttpRequestTestData::TFA_JSON_BODY, 200),
]);
$this->logoLib = $this->app->make(TfalogoLib::class);
$icon = $this->logoLib->getIcon('no_logo_should_exists_with_this_name');
$this->assertEquals(null, $icon);
}
#[Test]
public function test_TfalogoLib_loads_empty_collection_when_tfajson_fetching_fails()
{
Http::preventStrayRequests();
Http::fake([
CommonDataProvider::TFA_URL => Http::response(HttpRequestTestData::SVG_LOGO_BODY, 200),
TfalogoLib::TFA_JSON_URL => Http::response('not found', 404),
]);
$this->logoLib = $this->app->make(TfalogoLib::class);
$icon = $this->logoLib->getIcon('service');
$this->assertNull($icon);
Storage::disk('logos')->assertMissing(TfalogoLib::TFA_JSON);
}
}