license: built full stack, test and documentation
This commit is contained in:
177
backend/tests/Feature/LicenseControllerTest.php
Normal file
177
backend/tests/Feature/LicenseControllerTest.php
Normal file
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\Lists\License;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class LicenseControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
// --- Lettura (pubblica) ---------------------------------------------------
|
||||
|
||||
public function test_index_is_public_and_ordered_by_sort_order(): void
|
||||
{
|
||||
License::factory()->create(['acronym' => 'LAST', 'sort_order' => 90]);
|
||||
License::factory()->create(['acronym' => 'FIRST', 'sort_order' => 10]);
|
||||
|
||||
$this->getJson('/api/licenses')
|
||||
->assertOk()
|
||||
->assertJsonCount(2, 'data')
|
||||
->assertJsonPath('data.0.acronym', 'FIRST')
|
||||
->assertJsonPath('data.1.acronym', 'LAST');
|
||||
}
|
||||
|
||||
public function test_index_filters_by_record_scope(): void
|
||||
{
|
||||
License::factory()->count(2)->create();
|
||||
$open = License::factory()->forRecord()->create();
|
||||
|
||||
$this->getJson('/api/licenses?scope=record')
|
||||
->assertOk()
|
||||
->assertJsonCount(1, 'data')
|
||||
->assertJsonPath('data.0.id', $open->id);
|
||||
}
|
||||
|
||||
public function test_index_filters_by_media_scope(): void
|
||||
{
|
||||
License::factory()->create(['for_media' => false, 'for_record' => true]);
|
||||
$media = License::factory()->create();
|
||||
|
||||
$this->getJson('/api/licenses?scope=media')
|
||||
->assertOk()
|
||||
->assertJsonCount(1, 'data')
|
||||
->assertJsonPath('data.0.id', $media->id);
|
||||
}
|
||||
|
||||
public function test_index_filters_by_active(): void
|
||||
{
|
||||
License::factory()->inactive()->create();
|
||||
$active = License::factory()->create();
|
||||
|
||||
$this->getJson('/api/licenses?active=1')
|
||||
->assertOk()
|
||||
->assertJsonCount(1, 'data')
|
||||
->assertJsonPath('data.0.id', $active->id);
|
||||
}
|
||||
|
||||
public function test_index_rejects_an_unknown_scope(): void
|
||||
{
|
||||
$this->getJson('/api/licenses?scope=banana')
|
||||
->assertStatus(422)
|
||||
->assertJsonValidationErrors('scope');
|
||||
}
|
||||
|
||||
public function test_show_is_public(): void
|
||||
{
|
||||
$license = License::factory()->create();
|
||||
|
||||
$this->getJson("/api/licenses/{$license->id}")
|
||||
->assertOk()
|
||||
->assertJsonPath('data.id', $license->id)
|
||||
->assertJsonStructure(['data' => [
|
||||
'id', 'name', 'acronym', 'spdx_id', 'kind', 'description',
|
||||
'uri', 'file', 'for_record', 'for_media', 'sort_order', 'active',
|
||||
]]);
|
||||
}
|
||||
|
||||
// --- Stato d'uso (tier.app) ------------------------------------------------
|
||||
|
||||
public function test_usage_requires_authentication(): void
|
||||
{
|
||||
$license = License::factory()->create();
|
||||
|
||||
$this->getJson("/api/licenses/{$license->id}/usage")->assertUnauthorized();
|
||||
}
|
||||
|
||||
public function test_usage_reports_an_unreferenced_license_as_free(): void
|
||||
{
|
||||
$this->actingAs($this->operationalUser(), 'sanctum');
|
||||
$license = License::factory()->create();
|
||||
|
||||
$this->getJson("/api/licenses/{$license->id}/usage")
|
||||
->assertOk()
|
||||
->assertJsonPath('in_use', false);
|
||||
}
|
||||
|
||||
// --- Scrittura (tier.admin) -------------------------------------------------
|
||||
|
||||
public function test_admin_can_create_a_license(): void
|
||||
{
|
||||
$this->actingAs($this->adminUser(), 'sanctum');
|
||||
|
||||
$this->postJson('/api/licenses', [
|
||||
'name' => 'In Copyright - Non-Commercial Use Permitted',
|
||||
'acronym' => 'InC-NC',
|
||||
'kind' => 'rights_statement',
|
||||
'description' => 'Copyrighted, but non-commercial use is permitted.',
|
||||
'uri' => 'http://rightsstatements.org/vocab/InC-NC/1.0/',
|
||||
])
|
||||
->assertCreated()
|
||||
->assertJsonPath('data.acronym', 'InC-NC');
|
||||
|
||||
$this->assertDatabaseHas('licenses', ['acronym' => 'InC-NC', 'kind' => 'rights_statement']);
|
||||
}
|
||||
|
||||
public function test_create_rejects_a_duplicate_acronym(): void
|
||||
{
|
||||
$this->actingAs($this->adminUser(), 'sanctum');
|
||||
License::factory()->create(['acronym' => 'CC BY 4.0']);
|
||||
|
||||
$this->postJson('/api/licenses', [
|
||||
'name' => 'Attribution 4.0 International',
|
||||
'acronym' => 'CC BY 4.0',
|
||||
'kind' => 'license',
|
||||
])
|
||||
->assertStatus(422)
|
||||
->assertJsonValidationErrors('acronym');
|
||||
}
|
||||
|
||||
public function test_create_rejects_an_unknown_kind(): void
|
||||
{
|
||||
$this->actingAs($this->adminUser(), 'sanctum');
|
||||
|
||||
$this->postJson('/api/licenses', [
|
||||
'name' => 'Some License',
|
||||
'acronym' => 'SL',
|
||||
'kind' => 'banana',
|
||||
])
|
||||
->assertStatus(422)
|
||||
->assertJsonValidationErrors('kind');
|
||||
}
|
||||
|
||||
public function test_admin_can_retire_a_license_with_a_partial_update(): void
|
||||
{
|
||||
$this->actingAs($this->adminUser(), 'sanctum');
|
||||
$license = License::factory()->create();
|
||||
|
||||
$this->patchJson("/api/licenses/{$license->id}", ['active' => false])
|
||||
->assertOk()
|
||||
->assertJsonPath('data.active', false);
|
||||
|
||||
$this->assertSame($license->acronym, $license->fresh()->acronym);
|
||||
}
|
||||
|
||||
public function test_admin_can_delete_an_unused_license(): void
|
||||
{
|
||||
$this->actingAs($this->adminUser(), 'sanctum');
|
||||
$license = License::factory()->create();
|
||||
|
||||
$this->deleteJson("/api/licenses/{$license->id}")->assertOk();
|
||||
|
||||
$this->assertDatabaseMissing('licenses', ['id' => $license->id]);
|
||||
}
|
||||
|
||||
public function test_a_non_admin_cannot_write(): void
|
||||
{
|
||||
$this->actingAs($this->operationalUser(), 'sanctum');
|
||||
$license = License::factory()->create();
|
||||
|
||||
$this->postJson('/api/licenses', ['name' => 'X', 'acronym' => 'X', 'kind' => 'license'])
|
||||
->assertForbidden();
|
||||
$this->patchJson("/api/licenses/{$license->id}", ['active' => false])->assertForbidden();
|
||||
$this->deleteJson("/api/licenses/{$license->id}")->assertForbidden();
|
||||
}
|
||||
}
|
||||
107
backend/tests/Feature/LicenseSeederTest.php
Normal file
107
backend/tests/Feature/LicenseSeederTest.php
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Enums\LicenseKind;
|
||||
use App\Models\Lists\License;
|
||||
use Database\Seeders\DatabaseSeeder;
|
||||
use Database\Seeders\LicenseSeeder;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class LicenseSeederTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_it_seeds_the_initial_license_set(): void
|
||||
{
|
||||
$this->seed(LicenseSeeder::class);
|
||||
|
||||
$this->assertSame(11, License::count());
|
||||
}
|
||||
|
||||
public function test_legacy_ids_are_fixed_for_the_etl(): void
|
||||
{
|
||||
$this->seed(LicenseSeeder::class);
|
||||
|
||||
// v1: 1=Public Domain, 2=CC0, 3=CC BY — l'ETL mappa le FK 1:1.
|
||||
$this->assertSame('PDM 1.0', License::find(1)?->acronym);
|
||||
$this->assertSame('CC0 1.0', License::find(2)?->acronym);
|
||||
$this->assertSame('CC BY 4.0', License::find(3)?->acronym);
|
||||
}
|
||||
|
||||
public function test_only_open_licenses_are_allowed_for_records(): void
|
||||
{
|
||||
$this->seed(LicenseSeeder::class);
|
||||
|
||||
$this->assertEqualsCanonicalizing(
|
||||
['CC0-1.0', 'CC-BY-4.0'],
|
||||
License::forRecord()->pluck('spdx_id')->all()
|
||||
);
|
||||
}
|
||||
|
||||
public function test_every_license_is_available_for_media(): void
|
||||
{
|
||||
$this->seed(LicenseSeeder::class);
|
||||
|
||||
$this->assertSame(11, License::forMedia()->count());
|
||||
$this->assertSame(11, License::active()->count());
|
||||
}
|
||||
|
||||
public function test_marks_and_rights_statements_have_no_spdx_id_and_no_file(): void
|
||||
{
|
||||
$this->seed(LicenseSeeder::class);
|
||||
|
||||
License::where('kind', '!=', LicenseKind::License)->get()->each(function (License $license): void {
|
||||
$this->assertNull($license->spdx_id, "{$license->acronym} must have no SPDX id");
|
||||
$this->assertNull($license->file, "{$license->acronym} must have no legal text file");
|
||||
});
|
||||
}
|
||||
|
||||
public function test_proper_licenses_have_spdx_id_file_and_uri(): void
|
||||
{
|
||||
$this->seed(LicenseSeeder::class);
|
||||
|
||||
$proper = License::where('kind', LicenseKind::License)->get();
|
||||
|
||||
$this->assertCount(7, $proper);
|
||||
$proper->each(function (License $license): void {
|
||||
$this->assertNotNull($license->spdx_id);
|
||||
$this->assertSame("{$license->spdx_id}.txt", $license->file);
|
||||
$this->assertNotNull($license->uri);
|
||||
});
|
||||
}
|
||||
|
||||
public function test_legal_text_files_ship_with_the_application(): void
|
||||
{
|
||||
$this->seed(LicenseSeeder::class);
|
||||
|
||||
License::whereNotNull('file')->get()->each(function (License $license): void {
|
||||
$this->assertFileExists(storage_path("app/public/licenses/{$license->file}"));
|
||||
});
|
||||
}
|
||||
|
||||
public function test_every_entry_has_an_admin_facing_description(): void
|
||||
{
|
||||
$this->seed(LicenseSeeder::class);
|
||||
|
||||
License::all()->each(function (License $license): void {
|
||||
$this->assertNotEmpty($license->description, "{$license->acronym} must have a description");
|
||||
});
|
||||
}
|
||||
|
||||
public function test_it_is_idempotent(): void
|
||||
{
|
||||
$this->seed(LicenseSeeder::class);
|
||||
$this->seed(LicenseSeeder::class);
|
||||
|
||||
$this->assertSame(11, License::count());
|
||||
}
|
||||
|
||||
public function test_database_seeder_includes_the_licenses(): void
|
||||
{
|
||||
$this->seed(DatabaseSeeder::class);
|
||||
|
||||
$this->assertSame(11, License::count());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user