Files
projectsend/tests/Feature/Files/SlugRuleTest.php
ignacionelson 6e47d76ba6 ProjectSend 2.0.0
Client file sharing, rebuilt from the ground up: a private area per
client, resumable uploads, folders, groups and categories, sharing with
expiry dates and download limits, comments, file versions, an activity
log, a REST API, and sixteen languages.

This repository begins here. ProjectSend 2 was developed privately, and
that development history is not published — the previous generation
remains available, with its own history, at projectsend/legacy.

Free software under the GNU General Public License v2, or (at your
option) any later version.
2026-08-14 01:38:12 -03:00

76 lines
2.6 KiB
PHP

<?php
declare(strict_types=1);
use App\Models\User;
use App\Modules\Files\Models\File;
use App\Support\Rules;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;
beforeEach(function () {
Storage::fake('files');
$this->admin = User::factory()->create();
});
/** Run a candidate slug through the shared rule exactly as a controller would. */
function slugFails(string $slug, string $table = 'files', ?int $ignoreId = null, bool $public = true): bool
{
return Validator::make(
['slug' => $slug, 'public' => $public],
['slug' => Rules::slug($table, $ignoreId)],
)->fails();
}
// These land straight in a public URL path segment, so the shape is the
// point: lowercase alphanumerics in hyphen-separated runs, nothing else.
test('a well-formed slug passes', function () {
expect(slugFails('report'))->toBeFalse()
->and(slugFails('quarterly-report'))->toBeFalse()
->and(slugFails('report-2026-q1'))->toBeFalse()
->and(slugFails('a1'))->toBeFalse();
});
test('a malformed slug is rejected', function (string $slug) {
expect(slugFails($slug))->toBeTrue();
})->with([
'uppercase' => ['Report'],
'underscore' => ['my_report'],
'leading hyphen' => ['-report'],
'trailing hyphen' => ['report-'],
'doubled hyphen' => ['a--b'],
'space' => ['my report'],
'slash' => ['a/b'],
'dot' => ['a.b'],
'accented' => ['informe-anual-españa'],
'empty' => [''],
]);
test('a slug already taken by another row is rejected', function () {
File::factory()->create(['name' => 'Taken', 'slug' => 'taken']);
expect(slugFails('taken'))->toBeTrue();
});
test('ignoreId lets a row keep the slug it already holds', function () {
$file = File::factory()->create(['name' => 'Mine', 'slug' => 'mine']);
expect(slugFails('mine', ignoreId: $file->id))->toBeFalse()
->and(slugFails('mine'))->toBeTrue();
});
test('the slug is required only while public is true', function () {
// Not public: the name-derived slug stands in, so absence is fine.
expect(Validator::make(['public' => false], ['slug' => Rules::slug('files')])->fails())->toBeFalse()
->and(Validator::make(['public' => true], ['slug' => Rules::slug('files')])->fails())->toBeTrue();
});
test('uniqueness is scoped to the table it is asked about', function () {
File::factory()->create(['name' => 'Shared Name', 'slug' => 'shared-name']);
// The same slug is free in the folders and groups tables.
expect(slugFails('shared-name', 'files'))->toBeTrue()
->and(slugFails('shared-name', 'folders'))->toBeFalse()
->and(slugFails('shared-name', 'groups'))->toBeFalse();
});