ldc-sitemap-generator/tests/Unit/Config/SitemapStorageTest.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

327 lines
9.2 KiB
PHP

<?php
declare(strict_types=1);
namespace Uwil\SitemapGenerator\Tests\Unit\Config;
use Illuminate\Contracts\Filesystem\Filesystem;
use Illuminate\Support\Facades\Storage;
use League\Flysystem\WhitespacePathNormalizer;
use Mockery;
use Uwil\SitemapGenerator\Config\SitemapStorage;
use Uwil\SitemapGenerator\Exceptions\SitemapException;
covers(SitemapStorage::class);
describe('SitemapStorage', function () {
beforeEach(function () {
Storage::fake('test-disk');
});
it('can be instantiated and normalizes relative path', function () {
// GIVEN
$disk = Storage::disk('test-disk');
$normalizer = new WhitespacePathNormalizer();
// WHEN
$storage = new SitemapStorage(
'test-disk',
$disk,
'sitemaps/sitemap.xml',
$normalizer
);
// THEN
expect((string) $storage)->toBe('test-disk:sitemaps/sitemap.xml');
});
it('normalizes path by trimming whitespace and leading slashes', function () {
// GIVEN
$disk = Storage::disk('test-disk');
$normalizer = new WhitespacePathNormalizer();
// WHEN
$storage = new SitemapStorage(
'test-disk',
$disk,
' /nested/folder/sitemap.xml ',
$normalizer
);
// THEN
expect((string) $storage)->toBe('test-disk:nested/folder/sitemap.xml');
});
it('accepts uppercase XML extension', function () {
// GIVEN
$disk = Storage::disk('test-disk');
$normalizer = new WhitespacePathNormalizer();
// WHEN
$storage = new SitemapStorage(
'test-disk',
$disk,
'sitemap.XML',
$normalizer
);
// THEN
expect((string) $storage)->toBe('test-disk:sitemap.XML');
});
it('throws exception when path does not end with xml extension', function (string $invalidPath) {
// GIVEN
$disk = Storage::disk('test-disk');
$normalizer = new WhitespacePathNormalizer();
// WHEN
$action = fn () => new SitemapStorage(
'test-disk',
$disk,
$invalidPath,
$normalizer
);
// THEN
expect($action)->toThrow(
SitemapException::class,
'The path must indicate a valid XML file location.'
);
})->with([
'txt extension' => 'sitemap.txt',
'json extension' => 'sitemap.json',
'no extension' => 'sitemap',
]);
it('throws exception when path has empty filename', function (string $invalidPath) {
// GIVEN
$disk = Storage::disk('test-disk');
$normalizer = new WhitespacePathNormalizer();
// WHEN
$action = fn () => new SitemapStorage(
'test-disk',
$disk,
$invalidPath,
$normalizer
);
// THEN
expect($action)->toThrow(
SitemapException::class,
'The path must indicate a valid XML file location.'
);
})->with([
'only extension' => '.xml',
'empty string' => '',
'whitespace string' => ' ',
]);
it('throws exception when path traversal is detected', function (string $traversalPath) {
// GIVEN
$disk = Storage::disk('test-disk');
$normalizer = new WhitespacePathNormalizer();
// WHEN
$action = fn () => new SitemapStorage(
'test-disk',
$disk,
$traversalPath,
$normalizer
);
// THEN
expect($action)->toThrow(SitemapException::class);
})->with([
'parent directory' => '../sitemap.xml',
'nested parent traversal' => 'foo/../../sitemap.xml',
]);
it('checks if sitemap exists on disk', function () {
// GIVEN
$disk = Storage::disk('test-disk');
$storage = new SitemapStorage(
'test-disk',
$disk,
'sitemap.xml',
new WhitespacePathNormalizer()
);
// WHEN / THEN
expect($storage->sitemapExists())->toBeFalse();
$storage->writeInto('<urlset></urlset>');
expect($storage->sitemapExists())->toBeTrue();
});
it('writes content into disk successfully', function () {
// GIVEN
$disk = Storage::disk('test-disk');
$storage = new SitemapStorage(
'test-disk',
$disk,
'sitemap.xml',
new WhitespacePathNormalizer()
);
// WHEN
$storage->writeInto('<urlset><url><loc>https://example.com</loc></url></urlset>');
// THEN
expect($disk->exists('sitemap.xml'))->toBeTrue()
->and($disk->get('sitemap.xml'))->toBe('<urlset><url><loc>https://example.com</loc></url></urlset>');
});
it('throws exception when write fails', function () {
// GIVEN
/** @var Filesystem&Mockery\MockInterface $diskMock */
$diskMock = Mockery::mock(Filesystem::class);
$diskMock->expects('put')->with('sitemap.xml', '<content>')->andReturnFalse();
$storage = new SitemapStorage(
'mock-disk',
$diskMock,
'sitemap.xml',
new WhitespacePathNormalizer()
);
// WHEN
$action = fn () => $storage->writeInto('<content>');
// THEN
expect($action)->toThrow(
SitemapException::class,
'Failed to write sitemap content to disk.'
);
});
it('reads content from disk successfully', function () {
// GIVEN
$disk = Storage::disk('test-disk');
$storage = new SitemapStorage(
'test-disk',
$disk,
'sitemap.xml',
new WhitespacePathNormalizer()
);
$content = '<urlset><url><loc>https://example.com</loc></url></urlset>';
$storage->writeInto($content);
// WHEN
$readContent = $storage->readFromDisk();
// THEN
expect($readContent)->toBe($content);
});
it('throws exception when reading non-existing file', function () {
// GIVEN
$disk = Storage::disk('test-disk');
$storage = new SitemapStorage(
'test-disk',
$disk,
'sitemap.xml',
new WhitespacePathNormalizer()
);
// WHEN
$action = fn () => $storage->readFromDisk();
// THEN
expect($action)->toThrow(
SitemapException::class,
'Sitemap file does not exist on disk.'
);
});
it('throws exception when reading content returns null', function () {
// GIVEN
/** @var Filesystem&Mockery\MockInterface $diskMock */
$diskMock = Mockery::mock(Filesystem::class);
$diskMock->expects('exists')->with('sitemap.xml')->andReturnTrue();
$diskMock->expects('get')->with('sitemap.xml')->andReturnNull();
$storage = new SitemapStorage(
'mock-disk',
$diskMock,
'sitemap.xml',
new WhitespacePathNormalizer()
);
// WHEN
$action = fn () => $storage->readFromDisk();
// THEN
expect($action)->toThrow(
SitemapException::class,
'Failed to read sitemap content from disk.'
);
});
it('streams content from disk successfully', function () {
// GIVEN
$disk = Storage::disk('test-disk');
$storage = new SitemapStorage(
'test-disk',
$disk,
'sitemap.xml',
new WhitespacePathNormalizer()
);
$content = '<urlset><url><loc>https://example.com</loc></url></urlset>';
$storage->writeInto($content);
// WHEN
$stream = $storage->streamFromDisk();
// THEN
expect(is_resource($stream))->toBeTrue();
$streamedContent = stream_get_contents($stream);
fclose($stream);
expect($streamedContent)->toBe($content);
});
it('throws exception when streaming non-existing file', function () {
// GIVEN
$disk = Storage::disk('test-disk');
$storage = new SitemapStorage(
'test-disk',
$disk,
'sitemap.xml',
new WhitespacePathNormalizer()
);
// WHEN
$action = fn () => $storage->streamFromDisk();
// THEN
expect($action)->toThrow(
SitemapException::class,
'Sitemap file does not exist on disk.'
);
});
it('throws exception when streaming fails to return a resource', function () {
// GIVEN
/** @var Filesystem&Mockery\MockInterface $diskMock */
$diskMock = Mockery::mock(Filesystem::class);
$diskMock->expects('exists')->with('sitemap.xml')->andReturnTrue();
$diskMock->expects('readStream')->with('sitemap.xml')->andReturnNull();
$storage = new SitemapStorage(
'mock-disk',
$diskMock,
'sitemap.xml',
new WhitespacePathNormalizer()
);
// WHEN
$action = fn () => $storage->streamFromDisk();
// THEN
expect($action)->toThrow(
SitemapException::class,
'Failed to read sitemap stream from disk.'
);
});
});