- 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.
327 lines
9.2 KiB
PHP
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.'
|
|
);
|
|
});
|
|
});
|