ldc-sitemap-generator/tests/Unit/Config/SitemapConfigTest.php
Jérémy Munsch 58c9050ecc
feat(config): add SitemapStorage for managing sitemap files
- Introduced SitemapStorage class for reading, writing, and checking existence of sitemap files.
- Updated GenerateSitemapCommand to utilize SitemapStorage for file operations.
- Refactored tests to accommodate changes in sitemap file handling.
- Enhanced configuration to support output paths and storage disks.
2026-10-15 14:42:20 +02:00

401 lines
15 KiB
PHP

<?php
declare(strict_types=1);
namespace Uwil\SitemapGenerator\Tests\Unit\Config;
use Cron\CronExpression;
use Illuminate\Config\Repository as ConfigRepository;
use Uwil\SitemapGenerator\Config\SitemapConfig;
use Uwil\SitemapGenerator\Config\SitemapStorage;
use Uwil\SitemapGenerator\Exceptions\SitemapException;
use Uwil\SitemapGenerator\Localizers\DefaultRouteLocalizer;
use Uwil\SitemapGenerator\Localizers\McamaraRouteLocalizer;
covers(SitemapConfig::class);
describe('SitemapConfig', function () {
it('can be instantiated with valid configuration', function () {
/** @var \Uwil\SitemapGenerator\Tests\TestCase $this */
// GIVEN
$configRepository = new ConfigRepository([
SitemapConfig::CONFIG_KEY => [
'single_pages' => ['contact'],
'supported_locales' => ['fr', 'en'],
'cruds' => [],
'schedule' => false,
'cron' => '45 15 * * *',
'localizer' => 'default',
'output' => [
'storage_disk' => $this->outputDisk,
'path' => $this->outputPath,
],
],
]);
// WHEN
$sitemapConfig = new SitemapConfig($configRepository);
// THEN
expect($sitemapConfig)->toBeInstanceOf(SitemapConfig::class)
->and($sitemapConfig->singlePages)->toBe(['contact'])
->and($sitemapConfig->supportedLocales)->toBe(['fr', 'en'])
->and($sitemapConfig->cruds)->toBe([])
->and($sitemapConfig->schedule)->toBeFalse()
->and($sitemapConfig->localizer)->toBe('default')
->and($sitemapConfig->cron)->toBeInstanceOf(CronExpression::class)
->and($sitemapConfig->sitemapStorage)->toBeInstanceOf(SitemapStorage::class)
->and((string) $sitemapConfig->sitemapStorage)->toBe(
"{$this->outputDisk}:" .
ltrim($this->outputPath, '/')
);
});
it('can be configured with a relative output path inside disk', function () {
// GIVEN
$configRepository = new ConfigRepository([
SitemapConfig::CONFIG_KEY => [
'single_pages' => ['contact'],
'supported_locales' => ['fr'],
'cruds' => [],
'schedule' => false,
'cron' => '* * * * *',
'localizer' => 'default',
'output' => [
'storage_disk' => 'public',
'path' => 'sitemaps/sitemap.xml',
],
],
]);
// WHEN
$sitemapConfig = new SitemapConfig($configRepository);
// THEN
expect((string) $sitemapConfig->sitemapStorage)->toBe('public:sitemaps/sitemap.xml');
});
it('throws exception when legacy singlePages config key is used', function () {
// GIVEN
$configRepository = new ConfigRepository([
SitemapConfig::CONFIG_KEY => [
'singlePages' => ['contact'],
'single_pages' => ['contact'],
'supported_locales' => ['fr'],
'cruds' => [],
'schedule' => false,
'cron' => '* * * * *',
'localizer' => 'default',
'output' => [
'storage_disk' => 'local',
'path' => 'sitemap.xml',
],
],
]);
// WHEN
$action = fn () => new SitemapConfig($configRepository);
// THEN
expect($action)->toThrow(
SitemapException::class,
"You may migrate your config to use the 'single_pages' key instead of 'singlePages'"
);
});
it('throws exception when legacy output_path config key is used', function () {
// GIVEN
$configRepository = new ConfigRepository([
SitemapConfig::CONFIG_KEY => [
'output_path' => '/tmp/sitemap.xml',
'single_pages' => ['contact'],
'supported_locales' => ['fr'],
'cruds' => [],
'schedule' => false,
'cron' => '* * * * *',
'localizer' => 'default',
'output' => [
'storage_disk' => 'local',
'path' => 'sitemap.xml',
],
],
]);
// WHEN
$action = fn () => new SitemapConfig($configRepository);
// THEN
expect($action)->toThrow(
SitemapException::class,
"You may migrate your config to use the 'output.storage_disk' and 'output.path' keys instead of 'output_path'."
);
});
it('throws exception when legacy relationFilters key is used in crud config', function () {
// GIVEN
$configRepository = new ConfigRepository([
SitemapConfig::CONFIG_KEY => [
'single_pages' => [],
'supported_locales' => ['fr'],
'cruds' => [
[
'model' => ModelClass::class,
'relationFilters' => ['posts' => fn () => true],
],
],
'schedule' => false,
'cron' => '* * * * *',
'localizer' => 'default',
'output' => [
'storage_disk' => 'local',
'path' => 'sitemap.xml',
],
],
]);
// WHEN
$action = fn () => new SitemapConfig($configRepository);
// THEN
expect($action)->toThrow(
SitemapException::class,
"CRUD config must use the 'relation_filters' key instead of 'relationFilters'."
);
});
it('throws exception when legacy modelFilters key is used in crud config', function () {
// GIVEN
$configRepository = new ConfigRepository([
SitemapConfig::CONFIG_KEY => [
'single_pages' => [],
'supported_locales' => ['fr'],
'cruds' => [
[
'model' => ModelClass::class,
'modelFilters' => [['is_active', '=', true]],
],
],
'schedule' => false,
'cron' => '* * * * *',
'localizer' => 'default',
'output' => [
'storage_disk' => 'local',
'path' => 'sitemap.xml',
],
],
]);
// WHEN
$action = fn () => new SitemapConfig($configRepository);
// THEN
expect($action)->toThrow(
SitemapException::class,
"CRUD config must use the 'model_filters' key instead of 'modelFilters'."
);
});
it('throws exception when legacy allowNullRelation key is used in crud config', function () {
// GIVEN
$configRepository = new ConfigRepository([
SitemapConfig::CONFIG_KEY => [
'single_pages' => [],
'supported_locales' => ['fr'],
'cruds' => [
[
'model' => ModelClass::class,
'allowNullRelation' => ['category'],
],
],
'schedule' => false,
'cron' => '* * * * *',
'localizer' => 'default',
'output' => [
'storage_disk' => 'local',
'path' => 'sitemap.xml',
],
],
]);
// WHEN
$action = fn () => new SitemapConfig($configRepository);
// THEN
expect($action)->toThrow(
SitemapException::class,
"CRUD config must use the 'allow_null_relation' key instead of 'allowNullRelation'."
);
});
it('supports associative array for supported_locales and filters empty values', function () {
// GIVEN
$configRepository = new ConfigRepository([
SitemapConfig::CONFIG_KEY => [
'single_pages' => ['contact', '', ' ', 123, null],
'supported_locales' => [
'fr' => 'Français',
'en' => 'English',
'' => 'Vide',
' ' => 'Espaces',
],
'cruds' => [],
'schedule' => false,
'cron' => '* * * * *',
'localizer' => 'default',
'output' => [
'storage_disk' => 'local',
'path' => 'sitemap.xml',
],
],
]);
// WHEN
$sitemapConfig = new SitemapConfig($configRepository);
// THEN
expect($sitemapConfig->supportedLocales)->toBe(['fr', 'en'])
->and($sitemapConfig->singlePages)->toBe(['contact']);
});
it('throws exception when cron expression is invalid', function () {
// GIVEN
$configRepository = new ConfigRepository([
SitemapConfig::CONFIG_KEY => [
'single_pages' => [],
'supported_locales' => ['fr'],
'cruds' => [],
'schedule' => false,
'cron' => 'invalid-cron',
'localizer' => 'default',
'output' => [
'storage_disk' => 'local',
'path' => 'sitemap.xml',
],
],
]);
// WHEN
$action = fn () => new SitemapConfig($configRepository);
// THEN
expect($action)
->toThrow(SitemapException::class, "Cron config is not a valid expression 'invalid-cron'");
});
it('throws exception when output path does not end with .xml', function () {
// GIVEN
$configRepository = new ConfigRepository([
SitemapConfig::CONFIG_KEY => [
'single_pages' => [],
'supported_locales' => ['fr'],
'cruds' => [],
'schedule' => false,
'cron' => '* * * * *',
'localizer' => 'default',
'output' => [
'storage_disk' => 'local',
'path' => '/tmp/sitemap.txt',
],
],
]);
// WHEN
$action = fn () => new SitemapConfig($configRepository);
// THEN
expect($action)
->toThrow(SitemapException::class, 'The path must indicate a valid XML file location.');
});
it('can parse cruds configuration properly', function () {
// GIVEN
$configRepository = new ConfigRepository([
SitemapConfig::CONFIG_KEY => [
'single_pages' => [],
'supported_locales' => ['fr'],
'cruds' => [
[
'model' => ModelClass::class,
'relation_filters' => ['posts' => fn () => true],
'model_filters' => [['is_active', '=', true]],
'allow_null_relation' => ['category'],
],
],
'schedule' => false,
'cron' => '* * * * *',
'localizer' => 'default',
'output' => [
'storage_disk' => 'local',
'path' => 'sitemap.xml',
],
],
]);
// WHEN
$sitemapConfig = new SitemapConfig($configRepository);
// THEN
expect($sitemapConfig->cruds)->toHaveCount(1)
->and($sitemapConfig->cruds[0]->modelClass)->toBe(ModelClass::class)
->and($sitemapConfig->cruds[0]->allowNullRelations)->toBe(['category']);
});
it('returns the configured localizer instance', function (string $strategy, string $expectedClass) {
/** @var class-string $expectedClass */
// GIVEN
$configRepository = new ConfigRepository([
SitemapConfig::CONFIG_KEY => [
'single_pages' => [],
'supported_locales' => ['fr'],
'cruds' => [],
'schedule' => false,
'cron' => '* * * * *',
'localizer' => $strategy,
'output' => [
'storage_disk' => 'local',
'path' => 'sitemap.xml',
],
],
]);
// WHEN
$sitemapConfig = new SitemapConfig($configRepository);
$localizer = $sitemapConfig->getConfiguredLocalizerFor(app());
// THEN
expect($localizer)->toBeInstanceOf($expectedClass);
})->with([
'default alias' => ['default', DefaultRouteLocalizer::class],
'default class string' => [DefaultRouteLocalizer::class, DefaultRouteLocalizer::class],
'mcamara alias' => ['mcamara', McamaraRouteLocalizer::class],
'mcamara class string' => [McamaraRouteLocalizer::class, McamaraRouteLocalizer::class],
]);
it('throws exception when localizer is unsupported', function () {
// GIVEN
$configRepository = new ConfigRepository([
SitemapConfig::CONFIG_KEY => [
'single_pages' => [],
'supported_locales' => ['fr'],
'cruds' => [],
'schedule' => false,
'cron' => '* * * * *',
'localizer' => 'unsupported-localizer',
'output' => [
'storage_disk' => 'local',
'path' => 'sitemap.xml',
],
],
]);
// WHEN
$sitemapConfig = new SitemapConfig($configRepository);
$action = fn () => $sitemapConfig->getConfiguredLocalizerFor(app());
// THEN
expect($action)->toThrow(
SitemapException::class,
"Unsupported localizer 'unsupported-localizer'"
);
});
});