| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337 |
- <?php
- declare(strict_types=1);
- namespace Tests\Feature;
- use App\Models\DocumentationDocument;
- use App\Models\DocumentationDocumentVersion;
- use App\Models\DocumentationFolder;
- use App\Models\DocumentationFolderPermission;
- use App\Models\Role;
- use App\Models\User;
- use Illuminate\Foundation\Testing\RefreshDatabase;
- use Illuminate\Http\UploadedFile;
- use Illuminate\Support\Facades\Storage;
- use Tests\TestCase;
- class DocumentationControllerTest extends TestCase
- {
- use RefreshDatabase;
- protected bool $seed = true;
- private User $admin;
- private User $manager;
- private User $brigadier;
- protected function setUp(): void
- {
- parent::setUp();
- Storage::fake('public');
- Storage::fake('local');
- $this->admin = User::factory()->create(['role' => Role::ADMIN]);
- $this->manager = User::factory()->create(['role' => Role::MANAGER]);
- $this->brigadier = User::factory()->create(['role' => Role::BRIGADIER]);
- }
- public function test_guest_cannot_open_documentation(): void
- {
- $this->get(route('documents.index'))->assertRedirect(route('login'));
- }
- public function test_authenticated_user_can_open_year_independent_documentation(): void
- {
- $folder = $this->createRootFolder();
- $this->actingAs($this->manager)
- ->withSession(['year' => 2020])
- ->get(route('documents.index', ['folder' => $folder->id]))
- ->assertOk()
- ->assertViewIs('documents.index')
- ->assertSeeText($folder->name);
- }
- public function test_user_without_documents_view_permission_cannot_enter_module(): void
- {
- $role = Role::query()->create([
- 'slug' => 'documentation_denied',
- 'name' => 'Без документации',
- 'is_active' => true,
- ]);
- $user = User::factory()->create([
- 'role' => $role->slug,
- 'role_id' => $role->id,
- ]);
- $this->actingAs($user)->get(route('documents.index'))->assertForbidden();
- }
- public function test_only_real_administrator_can_create_root_folder(): void
- {
- $assistant = User::factory()->create(['role' => Role::ASSISTANT_HEAD]);
- $this->actingAs($this->manager)
- ->post(route('documents.folders.store'), ['name' => 'Закрытая папка'])
- ->assertForbidden();
- $this->actingAs($assistant)
- ->post(route('documents.folders.store'), ['name' => 'Папка помощника'])
- ->assertForbidden();
- $this->actingAs($this->admin)
- ->post(route('documents.folders.store'), ['name' => 'Общие инструкции'])
- ->assertRedirect();
- $folder = DocumentationFolder::query()->where('name', 'Общие инструкции')->firstOrFail();
- $this->assertFalse($folder->inherits_permissions);
- $this->assertDatabaseHas('documentation_folder_permissions', [
- 'folder_id' => $folder->id,
- 'subject_type' => DocumentationFolderPermission::SUBJECT_ALL,
- 'can_read' => true,
- 'can_write' => false,
- ]);
- }
- public function test_folder_permissions_support_all_roles_and_users(): void
- {
- $folder = $this->createRootFolder();
- $managerRole = Role::query()->where('slug', Role::MANAGER)->firstOrFail();
- $this->actingAs($this->admin)
- ->put(route('documents.folders.permissions.update', $folder), [
- 'role_read_ids' => [$managerRole->id],
- 'user_write_ids' => [$this->brigadier->id],
- ])
- ->assertRedirect(route('documents.index', ['folder' => $folder->id]));
- $this->actingAs($this->manager)
- ->get(route('documents.index', ['folder' => $folder->id]))
- ->assertOk();
- $this->actingAs($this->manager)
- ->post(route('documents.folders.store'), ['parent_id' => $folder->id, 'name' => 'Нельзя'])
- ->assertForbidden();
- $this->actingAs($this->brigadier)
- ->post(route('documents.folders.store'), ['parent_id' => $folder->id, 'name' => 'Можно'])
- ->assertRedirect();
- $this->assertDatabaseHas('documentation_folder_permissions', [
- 'folder_id' => $folder->id,
- 'subject_type' => DocumentationFolderPermission::SUBJECT_ROLE,
- 'role_id' => $managerRole->id,
- 'can_read' => true,
- 'can_write' => false,
- ]);
- $this->assertDatabaseHas('documentation_folder_permissions', [
- 'folder_id' => $folder->id,
- 'subject_type' => DocumentationFolderPermission::SUBJECT_USER,
- 'user_id' => $this->brigadier->id,
- 'can_read' => true,
- 'can_write' => true,
- ]);
- }
- public function test_subfolder_inherits_parent_permissions(): void
- {
- $folder = $this->createRootFolder();
- $this->actingAs($this->admin)
- ->put(route('documents.folders.permissions.update', $folder), [
- 'user_write_ids' => [$this->manager->id],
- ])
- ->assertRedirect();
- $this->actingAs($this->manager)
- ->post(route('documents.folders.store'), [
- 'parent_id' => $folder->id,
- 'name' => 'Регламенты',
- ])
- ->assertRedirect();
- $child = DocumentationFolder::query()->where('name', 'Регламенты')->firstOrFail();
- $this->assertTrue($child->inherits_permissions);
- $this->assertDatabaseCount('documentation_folder_permissions', 1);
- $this->actingAs($this->manager)
- ->post(route('documents.store', $child), [
- 'name' => 'Регламент монтажа',
- 'file' => UploadedFile::fake()->create('rules.pdf', 12, 'application/pdf'),
- ])
- ->assertRedirect();
- $this->assertDatabaseHas('documentation_documents', [
- 'folder_id' => $child->id,
- 'name' => 'Регламент монтажа',
- 'last_version_number' => 1,
- ]);
- }
- public function test_document_versions_can_be_downloaded_and_current_version_falls_back_after_deletion(): void
- {
- $folder = $this->createRootFolder();
- $this->actingAs($this->admin)
- ->post(route('documents.store', $folder), [
- 'name' => 'Прайс',
- 'file' => UploadedFile::fake()->createWithContent('price.xlsx', 'version-one'),
- ])
- ->assertRedirect();
- $document = DocumentationDocument::query()->firstOrFail();
- $firstVersion = $document->versions()->firstOrFail();
- Storage::disk('local')->assertExists($firstVersion->file->path);
- Storage::disk('public')->assertMissing($firstVersion->file->path);
- $this->actingAs($this->admin)
- ->post(route('documents.versions.store', $document), [
- 'file' => UploadedFile::fake()->createWithContent('price.xlsx', 'version-two'),
- ])
- ->assertRedirect();
- $document->refresh();
- $secondVersion = $document->versions()->where('version', 2)->firstOrFail();
- $secondPath = $secondVersion->file->path;
- $this->assertSame(2, $document->currentVersion()->firstOrFail()->version);
- $this->actingAs($this->manager)
- ->get(route('documents.versions.download', $firstVersion))
- ->assertOk();
- $this->actingAs($this->admin)
- ->delete(route('documents.versions.destroy', $secondVersion))
- ->assertRedirect();
- Storage::disk('local')->assertMissing($secondPath);
- $this->assertSame(1, $document->refresh()->currentVersion()->firstOrFail()->version);
- }
- public function test_deleting_last_version_deletes_document_and_file(): void
- {
- $folder = $this->createRootFolder();
- $this->actingAs($this->admin)
- ->post(route('documents.store', $folder), [
- 'file' => UploadedFile::fake()->createWithContent('manual.txt', 'manual'),
- ])
- ->assertRedirect();
- $document = DocumentationDocument::query()->firstOrFail();
- $version = DocumentationDocumentVersion::query()->with('file')->firstOrFail();
- $fileId = $version->file_id;
- $path = $version->file->path;
- $this->actingAs($this->admin)
- ->delete(route('documents.versions.destroy', $version))
- ->assertRedirect();
- $this->assertDatabaseMissing('documentation_documents', ['id' => $document->id]);
- $this->assertDatabaseMissing('files', ['id' => $fileId]);
- Storage::disk('local')->assertMissing($path);
- }
- public function test_deleting_folder_removes_nested_documents_versions_and_physical_files(): void
- {
- $folder = $this->createRootFolder();
- $this->actingAs($this->admin)
- ->post(route('documents.folders.store'), ['parent_id' => $folder->id, 'name' => 'Архив'])
- ->assertRedirect();
- $child = DocumentationFolder::query()->where('parent_id', $folder->id)->firstOrFail();
- $this->actingAs($this->admin)
- ->post(route('documents.store', $child), [
- 'file' => UploadedFile::fake()->createWithContent('archive.txt', 'archive'),
- ])
- ->assertRedirect();
- $version = DocumentationDocumentVersion::query()->with('file')->firstOrFail();
- $path = $version->file->path;
- $fileId = $version->file_id;
- $this->actingAs($this->admin)
- ->delete(route('documents.folders.destroy', $folder))
- ->assertRedirect(route('documents.index'));
- $this->assertDatabaseMissing('documentation_folders', ['id' => $folder->id]);
- $this->assertDatabaseMissing('documentation_folders', ['id' => $child->id]);
- $this->assertDatabaseMissing('files', ['id' => $fileId]);
- Storage::disk('local')->assertMissing($path);
- }
- public function test_document_can_be_renamed_and_found_by_name(): void
- {
- $folder = $this->createRootFolder();
- $this->actingAs($this->admin)
- ->post(route('documents.store', $folder), [
- 'name' => 'Старое название',
- 'file' => UploadedFile::fake()->create('document.pdf', 5, 'application/pdf'),
- ])
- ->assertRedirect();
- $document = DocumentationDocument::query()->firstOrFail();
- $this->actingAs($this->admin)
- ->put(route('documents.update', $document), ['name' => 'Инструкция по монтажу'])
- ->assertRedirect();
- $this->actingAs($this->manager)
- ->get(route('documents.index', ['folder' => $folder->id, 'search' => 'монтажу']))
- ->assertOk()
- ->assertSeeText('Инструкция по монтажу');
- }
- public function test_file_icons_follow_mime_type_and_images_have_protected_preview(): void
- {
- $folder = $this->createRootFolder();
- $this->actingAs($this->admin)
- ->post(route('documents.store', $folder), [
- 'name' => 'Фотография',
- 'file' => UploadedFile::fake()->image('photo.png', 120, 80),
- ])
- ->assertRedirect();
- $this->actingAs($this->admin)
- ->post(route('documents.store', $folder), [
- 'name' => 'Инструкция PDF',
- 'file' => UploadedFile::fake()->create('manual.pdf', 5, 'application/pdf'),
- ])
- ->assertRedirect();
- $imageVersion = DocumentationDocument::query()
- ->where('name', 'Фотография')
- ->firstOrFail()
- ->currentVersion()
- ->with('file')
- ->firstOrFail();
- $pdfVersion = DocumentationDocument::query()
- ->where('name', 'Инструкция PDF')
- ->firstOrFail()
- ->currentVersion()
- ->with('file')
- ->firstOrFail();
- $this->actingAs($this->manager)
- ->get(route('documents.index', ['folder' => $folder->id]))
- ->assertOk()
- ->assertSee('bi-file-earmark-image', false)
- ->assertSee('bi-file-earmark-pdf', false)
- ->assertSee(route('documents.versions.preview', $imageVersion), false);
- $this->actingAs($this->manager)
- ->get(route('documents.versions.preview', $imageVersion))
- ->assertOk()
- ->assertHeader('Content-Type', 'image/png')
- ->assertHeader('X-Content-Type-Options', 'nosniff');
- $this->actingAs($this->manager)
- ->get(route('documents.versions.preview', $pdfVersion))
- ->assertNotFound();
- }
- private function createRootFolder(): DocumentationFolder
- {
- $this->actingAs($this->admin)
- ->post(route('documents.folders.store'), ['name' => 'Общая документация'])
- ->assertRedirect();
- return DocumentationFolder::query()->whereNull('parent_id')->latest('id')->firstOrFail();
- }
- }
|