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('');
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('https://example.com');
// THEN
expect($disk->exists('sitemap.xml'))->toBeTrue()
->and($disk->get('sitemap.xml'))->toBe('https://example.com');
});
it('throws exception when write fails', function () {
// GIVEN
/** @var Filesystem&Mockery\MockInterface $diskMock */
$diskMock = Mockery::mock(Filesystem::class);
$diskMock->expects('put')->with('sitemap.xml', '')->andReturnFalse();
$storage = new SitemapStorage(
'mock-disk',
$diskMock,
'sitemap.xml',
new WhitespacePathNormalizer()
);
// WHEN
$action = fn () => $storage->writeInto('');
// 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 = 'https://example.com';
$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 = 'https://example.com';
$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.'
);
});
});