seed(RolePermissionSeeder::class); } protected function actingWithRole(string $role): void { $user = User::factory()->create(); $user->syncRoles([$role]); $this->withHeaders($this->apiHeadersFor($user)); } #[Test] public function it_creates_a_technology_with_its_prose_in_several_languages(): void { $this->actingWithRole('editor'); $this->postJson('/api/v1/technologies?lang=es&with=translations', [ 'name' => 'Kotlin Multiplatform', 'slug' => 'kotlin-multiplatform', 'category' => 'framework', 'radar_ring' => 'trial', 'color' => '#7F52FF', 'website' => 'https://kotlinlang.org/docs/multiplatform.html', 'description' => ['es' => 'Código Kotlin compartido.', 'en' => 'Shared Kotlin code.'], ]) ->assertCreated() ->assertJsonPath('data.name', 'Kotlin Multiplatform') ->assertJsonPath('data.radar_ring', 'trial') ->assertJsonPath('data.description', 'Código Kotlin compartido.') ->assertJsonPath('data.translations.description.en', 'Shared Kotlin code.'); $this->assertSame(RadarRing::Trial, Technology::query()->sole()->radar_ring); } #[Test] public function the_logo_is_a_path_from_the_upload_endpoint(): void { Storage::fake((string) config('media.disk')); $this->actingWithRole('editor'); $path = $this->post('/api/v1/media', [ 'file' => UploadedFile::fake()->createWithContent( 'angular.svg', '', ), 'for' => 'technologies', ], ['Accept' => 'application/json'])->json('data.path'); $this->postJson('/api/v1/technologies', [ 'name' => 'Angular', 'slug' => 'angular', 'logo' => $path, ]) ->assertCreated() ->assertJsonPath('data.logo', $path) ->assertJsonPath('data.logo_media.url', fn (string $url) => str_ends_with($url, $path)) ->assertJsonPath('data.logo_media.alt', 'Angular'); } #[Test] public function a_partial_update_leaves_the_rest_alone(): void { $this->actingWithRole('editor'); $technology = Technology::factory()->create([ 'name' => 'Laravel', 'radar_ring' => RadarRing::Assess, 'description' => ['es' => 'Framework PHP.', 'en' => 'PHP framework.'], ]); $this->patchJson("/api/v1/technologies/{$technology->id}", [ 'radar_ring' => 'adopt', 'description' => ['ca' => 'Framework PHP.'], ]) ->assertOk() ->assertJsonPath('data.name', 'Laravel') ->assertJsonPath('data.radar_ring', 'adopt'); $this->assertSame('PHP framework.', $technology->fresh()->getTranslation('description', 'en')); } #[Test] public function a_ring_can_be_cleared_to_take_it_off_the_radar(): void { $this->actingWithRole('editor'); $technology = Technology::factory()->create(['radar_ring' => RadarRing::Hold]); $this->patchJson("/api/v1/technologies/{$technology->id}", ['radar_ring' => null]) ->assertOk() ->assertJsonPath('data.radar_ring', null); $this->getJson('/api/v1/technologies?radar_ring=none') ->assertOk() ->assertJsonCount(1, 'data'); } #[Test] public function slugs_are_unique_and_url_safe(): void { $this->actingWithRole('editor'); Technology::factory()->create(['slug' => 'react']); $this->postJson('/api/v1/technologies', ['name' => 'React', 'slug' => 'react']) ->assertUnprocessable() ->assertJsonValidationErrors('slug'); $this->postJson('/api/v1/technologies', ['name' => 'Spring Boot', 'slug' => 'spring boot']) ->assertUnprocessable() ->assertJsonValidationErrors('slug'); } #[Test] public function one_still_in_use_cannot_be_deleted(): void { $this->actingWithRole('editor'); $technology = Technology::factory()->create(); Project::factory()->create()->technologies()->attach($technology); $this->deleteJson("/api/v1/technologies/{$technology->id}")->assertForbidden(); $this->assertModelExists($technology); } #[Test] public function a_reader_can_list_but_not_write(): void { $this->actingWithRole('viewer'); Technology::factory()->create(); $this->getJson('/api/v1/technologies')->assertOk()->assertJsonCount(1, 'data'); $this->postJson('/api/v1/technologies', ['name' => 'Flutter', 'slug' => 'flutter']) ->assertForbidden(); } }