Initial commit
This commit is contained in:
96
tests/Feature/Admin/UserControllerTest.php
Normal file
96
tests/Feature/Admin/UserControllerTest.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Admin;
|
||||
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Tests\TestCase;
|
||||
|
||||
class UserControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_admin_can_list_users(): void
|
||||
{
|
||||
$admin = $this->actingAsAdmin();
|
||||
$user = $this->createUser();
|
||||
|
||||
$response = $this->getJson('/api/users');
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $admin->id])
|
||||
->assertJsonFragment(['id' => $user->id]);
|
||||
}
|
||||
|
||||
public function test_non_admin_cannot_list_users(): void
|
||||
{
|
||||
$this->actingAsUser();
|
||||
|
||||
$this->getJson('/api/users')->assertStatus(403);
|
||||
}
|
||||
|
||||
public function test_admin_can_create_user(): void
|
||||
{
|
||||
$this->actingAsAdmin();
|
||||
|
||||
$response = $this->postJson('/api/users', [
|
||||
'name' => 'Test User',
|
||||
'email' => 'test-user@example.com',
|
||||
'password' => 'secretpass',
|
||||
'is_admin' => true,
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$response->assertStatus(201)
|
||||
->assertJsonFragment(['email' => 'test-user@example.com']);
|
||||
|
||||
$this->assertDatabaseHas('users', [
|
||||
'email' => 'test-user@example.com',
|
||||
'is_admin' => 1,
|
||||
'is_active' => 1,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_admin_can_update_user_and_password(): void
|
||||
{
|
||||
$this->actingAsAdmin();
|
||||
$user = $this->createUser(['password' => 'oldpass']);
|
||||
|
||||
$response = $this->putJson("/api/users/{$user->id}", [
|
||||
'name' => 'Updated Name',
|
||||
'email' => 'updated@example.com',
|
||||
'password' => 'newpass123',
|
||||
'is_admin' => false,
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['email' => 'updated@example.com']);
|
||||
|
||||
$user->refresh();
|
||||
$this->assertSame('Updated Name', $user->name);
|
||||
$this->assertTrue(Hash::check('newpass123', $user->password));
|
||||
}
|
||||
|
||||
public function test_admin_can_deactivate_user(): void
|
||||
{
|
||||
$this->actingAsAdmin();
|
||||
$user = $this->createUser(['is_active' => true]);
|
||||
|
||||
$response = $this->deleteJson("/api/users/{$user->id}");
|
||||
|
||||
$response->assertStatus(200);
|
||||
$this->assertDatabaseHas('users', [
|
||||
'id' => $user->id,
|
||||
'is_active' => 0,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_admin_cannot_deactivate_self(): void
|
||||
{
|
||||
$admin = $this->actingAsAdmin();
|
||||
|
||||
$this->deleteJson("/api/users/{$admin->id}")
|
||||
->assertStatus(422);
|
||||
}
|
||||
}
|
||||
43
tests/Feature/Auth/LoginControllerTest.php
Normal file
43
tests/Feature/Auth/LoginControllerTest.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Auth;
|
||||
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class LoginControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_login_success_returns_user(): void
|
||||
{
|
||||
$user = $this->createUser([
|
||||
'password' => 'demodemo',
|
||||
]);
|
||||
|
||||
$response = $this->withSession([])->postJson('/api/login', [
|
||||
'email' => $user->email,
|
||||
'password' => 'demodemo',
|
||||
]);
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment([
|
||||
'id' => $user->id,
|
||||
'email' => $user->email,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_login_inactive_user_fails(): void
|
||||
{
|
||||
$user = $this->createInactiveUser([
|
||||
'password' => 'demodemo',
|
||||
]);
|
||||
|
||||
$response = $this->withSession([])->postJson('/api/login', [
|
||||
'email' => $user->email,
|
||||
'password' => 'demodemo',
|
||||
]);
|
||||
|
||||
$response->assertStatus(422);
|
||||
}
|
||||
}
|
||||
74
tests/Feature/Catalog/BandControllerTest.php
Normal file
74
tests/Feature/Catalog/BandControllerTest.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Catalog;
|
||||
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class BandControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_index_returns_bands(): void
|
||||
{
|
||||
$band = $this->createBand();
|
||||
|
||||
$response = $this->getJson('/api/bands');
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $band->id]);
|
||||
}
|
||||
|
||||
public function test_show_returns_band(): void
|
||||
{
|
||||
$band = $this->createBand();
|
||||
|
||||
$response = $this->getJson("/api/bands/{$band->id}");
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $band->id]);
|
||||
}
|
||||
|
||||
public function test_admin_can_create_update_and_delete_band(): void
|
||||
{
|
||||
$this->actingAsAdmin();
|
||||
|
||||
$createResponse = $this->postJson('/api/bands', [
|
||||
'name' => '144 MHz',
|
||||
'order' => 1,
|
||||
'edi_band_begin' => 144000000,
|
||||
'edi_band_end' => 146000000,
|
||||
'has_power_category' => true,
|
||||
]);
|
||||
|
||||
$createResponse->assertStatus(201);
|
||||
$bandId = $createResponse->json('id');
|
||||
|
||||
$updateResponse = $this->putJson("/api/bands/{$bandId}", [
|
||||
'name' => '144 MHz (upd)',
|
||||
'order' => 2,
|
||||
'edi_band_begin' => 144000000,
|
||||
'edi_band_end' => 146000000,
|
||||
'has_power_category' => false,
|
||||
]);
|
||||
|
||||
$updateResponse->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $bandId]);
|
||||
|
||||
$this->deleteJson("/api/bands/{$bandId}")
|
||||
->assertStatus(204);
|
||||
}
|
||||
|
||||
public function test_non_admin_cannot_create_band(): void
|
||||
{
|
||||
$this->actingAsUser();
|
||||
|
||||
$this->postJson('/api/bands', [
|
||||
'name' => '144 MHz',
|
||||
'order' => 1,
|
||||
'edi_band_begin' => 144000000,
|
||||
'edi_band_end' => 146000000,
|
||||
'has_power_category' => true,
|
||||
])->assertStatus(403);
|
||||
}
|
||||
}
|
||||
65
tests/Feature/Catalog/CategoryControllerTest.php
Normal file
65
tests/Feature/Catalog/CategoryControllerTest.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Catalog;
|
||||
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class CategoryControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_index_returns_categories(): void
|
||||
{
|
||||
$category = $this->createCategory();
|
||||
|
||||
$response = $this->getJson('/api/categories');
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $category->id]);
|
||||
}
|
||||
|
||||
public function test_show_returns_category(): void
|
||||
{
|
||||
$category = $this->createCategory();
|
||||
|
||||
$response = $this->getJson("/api/categories/{$category->id}");
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $category->id]);
|
||||
}
|
||||
|
||||
public function test_admin_can_create_update_and_delete_category(): void
|
||||
{
|
||||
$this->actingAsAdmin();
|
||||
|
||||
$createResponse = $this->postJson('/api/categories', [
|
||||
'name' => 'CAT-A',
|
||||
'order' => 1,
|
||||
]);
|
||||
|
||||
$createResponse->assertStatus(201);
|
||||
$categoryId = $createResponse->json('id');
|
||||
|
||||
$updateResponse = $this->putJson("/api/categories/{$categoryId}", [
|
||||
'name' => 'CAT-B',
|
||||
'order' => 2,
|
||||
]);
|
||||
|
||||
$updateResponse->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $categoryId]);
|
||||
|
||||
$this->deleteJson("/api/categories/{$categoryId}")
|
||||
->assertStatus(204);
|
||||
}
|
||||
|
||||
public function test_non_admin_cannot_create_category(): void
|
||||
{
|
||||
$this->actingAsUser();
|
||||
|
||||
$this->postJson('/api/categories', [
|
||||
'name' => 'CAT-A',
|
||||
'order' => 1,
|
||||
])->assertStatus(403);
|
||||
}
|
||||
}
|
||||
100
tests/Feature/Catalog/ContestParameterControllerTest.php
Normal file
100
tests/Feature/Catalog/ContestParameterControllerTest.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Catalog;
|
||||
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class ContestParameterControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_index_returns_contest_parameters(): void
|
||||
{
|
||||
$item = $this->createContestParameter();
|
||||
|
||||
$response = $this->getJson('/api/contest-parameters');
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $item->id]);
|
||||
}
|
||||
|
||||
public function test_show_returns_contest_parameter(): void
|
||||
{
|
||||
$item = $this->createContestParameter();
|
||||
|
||||
$response = $this->getJson("/api/contest-parameters/{$item->id}");
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $item->id]);
|
||||
}
|
||||
|
||||
public function test_admin_can_create_update_and_delete_contest_parameter(): void
|
||||
{
|
||||
$this->actingAsAdmin();
|
||||
$contest = $this->createContest();
|
||||
|
||||
$createResponse = $this->postJson('/api/contest-parameters', [
|
||||
'contest_id' => $contest->id,
|
||||
'log_type' => 'STANDARD',
|
||||
'ignore_slash_part' => true,
|
||||
'ignore_third_part' => true,
|
||||
'letters_in_rst' => true,
|
||||
'discard_qso_rec_diff_call' => true,
|
||||
'discard_qso_sent_diff_call' => false,
|
||||
'discard_qso_rec_diff_rst' => true,
|
||||
'discard_qso_sent_diff_rst' => false,
|
||||
'discard_qso_rec_diff_code' => true,
|
||||
'discard_qso_sent_diff_code' => false,
|
||||
'unique_qso' => true,
|
||||
'time_tolerance' => 600,
|
||||
]);
|
||||
|
||||
$createResponse->assertStatus(201);
|
||||
$itemId = $createResponse->json('id');
|
||||
|
||||
$updateResponse = $this->putJson("/api/contest-parameters/{$itemId}", [
|
||||
'contest_id' => $contest->id,
|
||||
'log_type' => 'CHECK',
|
||||
'ignore_slash_part' => false,
|
||||
'ignore_third_part' => true,
|
||||
'letters_in_rst' => false,
|
||||
'discard_qso_rec_diff_call' => true,
|
||||
'discard_qso_sent_diff_call' => false,
|
||||
'discard_qso_rec_diff_rst' => true,
|
||||
'discard_qso_sent_diff_rst' => false,
|
||||
'discard_qso_rec_diff_code' => true,
|
||||
'discard_qso_sent_diff_code' => false,
|
||||
'unique_qso' => false,
|
||||
'time_tolerance' => 300,
|
||||
]);
|
||||
|
||||
$updateResponse->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $itemId]);
|
||||
|
||||
$this->deleteJson("/api/contest-parameters/{$itemId}")
|
||||
->assertStatus(204);
|
||||
}
|
||||
|
||||
public function test_non_admin_cannot_create_contest_parameter(): void
|
||||
{
|
||||
$this->actingAsUser();
|
||||
$contest = $this->createContest();
|
||||
|
||||
$this->postJson('/api/contest-parameters', [
|
||||
'contest_id' => $contest->id,
|
||||
'log_type' => 'STANDARD',
|
||||
'ignore_slash_part' => true,
|
||||
'ignore_third_part' => true,
|
||||
'letters_in_rst' => true,
|
||||
'discard_qso_rec_diff_call' => true,
|
||||
'discard_qso_sent_diff_call' => false,
|
||||
'discard_qso_rec_diff_rst' => true,
|
||||
'discard_qso_sent_diff_rst' => false,
|
||||
'discard_qso_rec_diff_code' => true,
|
||||
'discard_qso_sent_diff_code' => false,
|
||||
'unique_qso' => true,
|
||||
'time_tolerance' => 600,
|
||||
])->assertStatus(403);
|
||||
}
|
||||
}
|
||||
46
tests/Feature/Catalog/CountryWwlControllerTest.php
Normal file
46
tests/Feature/Catalog/CountryWwlControllerTest.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Catalog;
|
||||
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class CountryWwlControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_index_returns_country_wwl_records(): void
|
||||
{
|
||||
$item = $this->createCountryWwl();
|
||||
|
||||
$response = $this->getJson('/api/countries-wwl');
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment([
|
||||
'country_name' => $item->country_name,
|
||||
'wwl' => $item->wwl,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_admin_can_create_country_wwl(): void
|
||||
{
|
||||
$this->actingAsAdmin();
|
||||
|
||||
$createResponse = $this->postJson('/api/countries-wwl', [
|
||||
'country_name' => 'Test Country',
|
||||
'wwl' => 'AA00',
|
||||
]);
|
||||
|
||||
$createResponse->assertStatus(201);
|
||||
}
|
||||
|
||||
public function test_non_admin_cannot_create_country_wwl(): void
|
||||
{
|
||||
$this->actingAsUser();
|
||||
|
||||
$this->postJson('/api/countries-wwl', [
|
||||
'country_name' => 'Test Country',
|
||||
'wwl' => 'AA00',
|
||||
])->assertStatus(403);
|
||||
}
|
||||
}
|
||||
94
tests/Feature/Catalog/CtyControllerTest.php
Normal file
94
tests/Feature/Catalog/CtyControllerTest.php
Normal file
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Catalog;
|
||||
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class CtyControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_index_returns_cty_records(): void
|
||||
{
|
||||
$item = $this->createCty();
|
||||
|
||||
$response = $this->getJson('/api/cty');
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $item->id]);
|
||||
}
|
||||
|
||||
public function test_show_returns_cty_record(): void
|
||||
{
|
||||
$item = $this->createCty();
|
||||
|
||||
$response = $this->getJson("/api/cty/{$item->id}");
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $item->id]);
|
||||
}
|
||||
|
||||
public function test_admin_can_create_update_and_delete_cty_record(): void
|
||||
{
|
||||
$this->actingAsAdmin();
|
||||
|
||||
$createResponse = $this->postJson('/api/cty', [
|
||||
'country_name' => 'Test Country',
|
||||
'dxcc' => 999,
|
||||
'cq_zone' => 10,
|
||||
'itu_zone' => 20,
|
||||
'continent' => 'EU',
|
||||
'latitude' => 10.0,
|
||||
'longitude' => 20.0,
|
||||
'time_offset' => 1.0,
|
||||
'prefix' => 'TST',
|
||||
'prefix_norm' => 'TST',
|
||||
'precise' => true,
|
||||
'source' => 'test',
|
||||
]);
|
||||
|
||||
$createResponse->assertStatus(201);
|
||||
$itemId = $createResponse->json('id');
|
||||
|
||||
$updateResponse = $this->putJson("/api/cty/{$itemId}", [
|
||||
'country_name' => 'Test Country 2',
|
||||
'dxcc' => 999,
|
||||
'cq_zone' => 10,
|
||||
'itu_zone' => 20,
|
||||
'continent' => 'EU',
|
||||
'latitude' => 10.0,
|
||||
'longitude' => 20.0,
|
||||
'time_offset' => 1.0,
|
||||
'prefix' => 'TST2',
|
||||
'prefix_norm' => 'TST2',
|
||||
'precise' => false,
|
||||
'source' => 'test',
|
||||
]);
|
||||
|
||||
$updateResponse->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $itemId]);
|
||||
|
||||
$this->deleteJson("/api/cty/{$itemId}")
|
||||
->assertStatus(204);
|
||||
}
|
||||
|
||||
public function test_non_admin_cannot_create_cty_record(): void
|
||||
{
|
||||
$this->actingAsUser();
|
||||
|
||||
$this->postJson('/api/cty', [
|
||||
'country_name' => 'Test Country',
|
||||
'dxcc' => 999,
|
||||
'cq_zone' => 10,
|
||||
'itu_zone' => 20,
|
||||
'continent' => 'EU',
|
||||
'latitude' => 10.0,
|
||||
'longitude' => 20.0,
|
||||
'time_offset' => 1.0,
|
||||
'prefix' => 'TST',
|
||||
'precise' => true,
|
||||
'source' => 'test',
|
||||
])->assertStatus(403);
|
||||
}
|
||||
}
|
||||
62
tests/Feature/Catalog/EdiBandControllerTest.php
Normal file
62
tests/Feature/Catalog/EdiBandControllerTest.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Catalog;
|
||||
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class EdiBandControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_index_returns_edi_bands(): void
|
||||
{
|
||||
$item = $this->createEdiBand();
|
||||
|
||||
$response = $this->getJson('/api/edi-bands');
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $item->id]);
|
||||
}
|
||||
|
||||
public function test_show_returns_edi_band(): void
|
||||
{
|
||||
$item = $this->createEdiBand();
|
||||
|
||||
$response = $this->getJson("/api/edi-bands/{$item->id}");
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $item->id]);
|
||||
}
|
||||
|
||||
public function test_admin_can_create_update_and_delete_edi_band(): void
|
||||
{
|
||||
$this->actingAsAdmin();
|
||||
|
||||
$createResponse = $this->postJson('/api/edi-bands', [
|
||||
'value' => 'EDI-144',
|
||||
]);
|
||||
|
||||
$createResponse->assertStatus(201);
|
||||
$itemId = $createResponse->json('id');
|
||||
|
||||
$updateResponse = $this->putJson("/api/edi-bands/{$itemId}", [
|
||||
'value' => 'EDI-432',
|
||||
]);
|
||||
|
||||
$updateResponse->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $itemId]);
|
||||
|
||||
$this->deleteJson("/api/edi-bands/{$itemId}")
|
||||
->assertStatus(204);
|
||||
}
|
||||
|
||||
public function test_non_admin_cannot_create_edi_band(): void
|
||||
{
|
||||
$this->actingAsUser();
|
||||
|
||||
$this->postJson('/api/edi-bands', [
|
||||
'value' => 'EDI-144',
|
||||
])->assertStatus(403);
|
||||
}
|
||||
}
|
||||
62
tests/Feature/Catalog/EdiCategoryControllerTest.php
Normal file
62
tests/Feature/Catalog/EdiCategoryControllerTest.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Catalog;
|
||||
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class EdiCategoryControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_index_returns_edi_categories(): void
|
||||
{
|
||||
$item = $this->createEdiCategory();
|
||||
|
||||
$response = $this->getJson('/api/edi-categories');
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $item->id]);
|
||||
}
|
||||
|
||||
public function test_show_returns_edi_category(): void
|
||||
{
|
||||
$item = $this->createEdiCategory();
|
||||
|
||||
$response = $this->getJson("/api/edi-categories/{$item->id}");
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $item->id]);
|
||||
}
|
||||
|
||||
public function test_admin_can_create_update_and_delete_edi_category(): void
|
||||
{
|
||||
$this->actingAsAdmin();
|
||||
|
||||
$createResponse = $this->postJson('/api/edi-categories', [
|
||||
'value' => 'EDI-CAT-A',
|
||||
]);
|
||||
|
||||
$createResponse->assertStatus(201);
|
||||
$itemId = $createResponse->json('id');
|
||||
|
||||
$updateResponse = $this->putJson("/api/edi-categories/{$itemId}", [
|
||||
'value' => 'EDI-CAT-B',
|
||||
]);
|
||||
|
||||
$updateResponse->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $itemId]);
|
||||
|
||||
$this->deleteJson("/api/edi-categories/{$itemId}")
|
||||
->assertStatus(204);
|
||||
}
|
||||
|
||||
public function test_non_admin_cannot_create_edi_category(): void
|
||||
{
|
||||
$this->actingAsUser();
|
||||
|
||||
$this->postJson('/api/edi-categories', [
|
||||
'value' => 'EDI-CAT-A',
|
||||
])->assertStatus(403);
|
||||
}
|
||||
}
|
||||
76
tests/Feature/Catalog/EvaluationRuleSetControllerTest.php
Normal file
76
tests/Feature/Catalog/EvaluationRuleSetControllerTest.php
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Catalog;
|
||||
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class EvaluationRuleSetControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_index_returns_rule_sets(): void
|
||||
{
|
||||
$item = $this->createRuleSet();
|
||||
|
||||
$response = $this->getJson('/api/evaluation-rule-sets');
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $item->id]);
|
||||
}
|
||||
|
||||
public function test_show_returns_rule_set(): void
|
||||
{
|
||||
$item = $this->createRuleSet();
|
||||
|
||||
$response = $this->getJson("/api/evaluation-rule-sets/{$item->id}");
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $item->id]);
|
||||
}
|
||||
|
||||
public function test_admin_can_create_update_and_delete_rule_set(): void
|
||||
{
|
||||
$this->actingAsAdmin();
|
||||
|
||||
$createResponse = $this->postJson('/api/evaluation-rule-sets', [
|
||||
'name' => 'Test ruleset',
|
||||
'code' => 'TEST_RULES',
|
||||
'scoring_mode' => 'DISTANCE',
|
||||
'multiplier_type' => 'WWL',
|
||||
'dup_qso_policy' => 'ZERO_POINTS',
|
||||
'nil_qso_policy' => 'ZERO_POINTS',
|
||||
'busted_call_policy' => 'ZERO_POINTS',
|
||||
'busted_exchange_policy' => 'ZERO_POINTS',
|
||||
]);
|
||||
|
||||
$createResponse->assertStatus(201);
|
||||
$itemId = $createResponse->json('id');
|
||||
|
||||
$updateResponse = $this->putJson("/api/evaluation-rule-sets/{$itemId}", [
|
||||
'name' => 'Updated ruleset',
|
||||
]);
|
||||
|
||||
$updateResponse->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $itemId]);
|
||||
|
||||
$this->deleteJson("/api/evaluation-rule-sets/{$itemId}")
|
||||
->assertStatus(204);
|
||||
}
|
||||
|
||||
public function test_non_admin_cannot_create_rule_set(): void
|
||||
{
|
||||
$this->actingAsUser();
|
||||
|
||||
$this->postJson('/api/evaluation-rule-sets', [
|
||||
'name' => 'Test ruleset',
|
||||
'code' => 'TEST_RULES',
|
||||
'scoring_mode' => 'DISTANCE',
|
||||
'multiplier_type' => 'WWL',
|
||||
'dup_qso_policy' => 'ZERO_POINTS',
|
||||
'nil_qso_policy' => 'ZERO_POINTS',
|
||||
'busted_call_policy' => 'ZERO_POINTS',
|
||||
'busted_exchange_policy' => 'ZERO_POINTS',
|
||||
])->assertStatus(403);
|
||||
}
|
||||
}
|
||||
68
tests/Feature/Catalog/PowerCategoryControllerTest.php
Normal file
68
tests/Feature/Catalog/PowerCategoryControllerTest.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Catalog;
|
||||
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class PowerCategoryControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_index_returns_power_categories(): void
|
||||
{
|
||||
$item = $this->createPowerCategory();
|
||||
|
||||
$response = $this->getJson('/api/power-categories');
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $item->id]);
|
||||
}
|
||||
|
||||
public function test_show_returns_power_category(): void
|
||||
{
|
||||
$item = $this->createPowerCategory();
|
||||
|
||||
$response = $this->getJson("/api/power-categories/{$item->id}");
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $item->id]);
|
||||
}
|
||||
|
||||
public function test_admin_can_create_update_and_delete_power_category(): void
|
||||
{
|
||||
$this->actingAsAdmin();
|
||||
|
||||
$createResponse = $this->postJson('/api/power-categories', [
|
||||
'name' => 'PWR-A',
|
||||
'order' => 1,
|
||||
'power_level' => 100,
|
||||
]);
|
||||
|
||||
$createResponse->assertStatus(201);
|
||||
$itemId = $createResponse->json('id');
|
||||
|
||||
$updateResponse = $this->putJson("/api/power-categories/{$itemId}", [
|
||||
'name' => 'PWR-B',
|
||||
'order' => 2,
|
||||
'power_level' => 200,
|
||||
]);
|
||||
|
||||
$updateResponse->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $itemId]);
|
||||
|
||||
$this->deleteJson("/api/power-categories/{$itemId}")
|
||||
->assertStatus(204);
|
||||
}
|
||||
|
||||
public function test_non_admin_cannot_create_power_category(): void
|
||||
{
|
||||
$this->actingAsUser();
|
||||
|
||||
$this->postJson('/api/power-categories', [
|
||||
'name' => 'PWR-A',
|
||||
'order' => 1,
|
||||
'power_level' => 100,
|
||||
])->assertStatus(403);
|
||||
}
|
||||
}
|
||||
82
tests/Feature/Contests/ContestControllerTest.php
Normal file
82
tests/Feature/Contests/ContestControllerTest.php
Normal file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Contests;
|
||||
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class ContestControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_index_returns_contests(): void
|
||||
{
|
||||
$contest = $this->createContest();
|
||||
|
||||
$response = $this->getJson('/api/contests');
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $contest->id]);
|
||||
}
|
||||
|
||||
public function test_index_can_filter_only_active(): void
|
||||
{
|
||||
$active = $this->createContest(['is_active' => true]);
|
||||
$inactive = $this->createContest(['is_active' => false]);
|
||||
|
||||
$response = $this->getJson('/api/contests?only_active=1');
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $active->id]);
|
||||
|
||||
$ids = collect($response->json('data'))->pluck('id')->all();
|
||||
$this->assertFalse(in_array($inactive->id, $ids, true));
|
||||
}
|
||||
|
||||
public function test_show_returns_contest(): void
|
||||
{
|
||||
$contest = $this->createContest();
|
||||
|
||||
$response = $this->getJson("/api/contests/{$contest->id}");
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $contest->id]);
|
||||
}
|
||||
|
||||
public function test_admin_can_create_update_and_delete_contest(): void
|
||||
{
|
||||
$this->actingAsAdmin();
|
||||
$ruleSet = $this->createRuleSet();
|
||||
|
||||
$createResponse = $this->postJson('/api/contests', [
|
||||
'name' => ['cs' => 'Test soutěž', 'en' => 'Test contest'],
|
||||
'description' => ['cs' => 'Popis', 'en' => 'Description'],
|
||||
'rule_set_id' => $ruleSet->id,
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$createResponse->assertStatus(201);
|
||||
$contestId = $createResponse->json('id');
|
||||
|
||||
$updateResponse = $this->putJson("/api/contests/{$contestId}", [
|
||||
'name' => ['cs' => 'Upraveno', 'en' => 'Updated'],
|
||||
'description' => ['cs' => 'Popis', 'en' => 'Description'],
|
||||
'is_active' => false,
|
||||
]);
|
||||
|
||||
$updateResponse->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $contestId]);
|
||||
|
||||
$this->deleteJson("/api/contests/{$contestId}")
|
||||
->assertStatus(204);
|
||||
}
|
||||
|
||||
public function test_non_admin_cannot_create_contest(): void
|
||||
{
|
||||
$this->actingAsUser();
|
||||
|
||||
$this->postJson('/api/contests', [
|
||||
'name' => ['cs' => 'Test soutěž', 'en' => 'Test contest'],
|
||||
])->assertStatus(403);
|
||||
}
|
||||
}
|
||||
160
tests/Feature/Evaluation/AggregateLogResultsJobTest.php
Normal file
160
tests/Feature/Evaluation/AggregateLogResultsJobTest.php
Normal file
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Evaluation;
|
||||
|
||||
use App\Jobs\AggregateLogResultsJob;
|
||||
use App\Models\LogResult;
|
||||
use App\Models\QsoResult;
|
||||
use App\Models\WorkingQso;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class AggregateLogResultsJobTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
private function createQso(int $runId, int $logId, int $bandId, Carbon $ts, int $points): int
|
||||
{
|
||||
$logQso = $this->createLogQso(['log_id' => $logId]);
|
||||
|
||||
$this->createQsoResult([
|
||||
'evaluation_run_id' => $runId,
|
||||
'log_qso_id' => $logQso->id,
|
||||
'is_valid' => true,
|
||||
'points' => $points,
|
||||
]);
|
||||
|
||||
WorkingQso::create([
|
||||
'evaluation_run_id' => $runId,
|
||||
'log_qso_id' => $logQso->id,
|
||||
'log_id' => $logId,
|
||||
'ts_utc' => $ts,
|
||||
'band_id' => $bandId,
|
||||
'call_norm' => 'OK1AAA',
|
||||
'rcall_norm' => 'OK1BBB',
|
||||
'loc_norm' => 'JN00AA',
|
||||
'rloc_norm' => 'JN00AA',
|
||||
]);
|
||||
|
||||
return $logQso->id;
|
||||
}
|
||||
|
||||
public function test_aggregate_applies_operating_window_for_6h(): void
|
||||
{
|
||||
$ruleSet = $this->createRuleSet([
|
||||
'use_multipliers' => false,
|
||||
'multiplier_type' => 'NONE',
|
||||
'multiplier_scope' => 'OVERALL',
|
||||
'multiplier_source' => 'VALID_ONLY',
|
||||
'operating_window_mode' => 'BEST_CONTIGUOUS',
|
||||
'operating_window_hours' => 6,
|
||||
]);
|
||||
$round = $this->createRound();
|
||||
$log = $this->createLog(['round_id' => $round->id]);
|
||||
$run = $this->createEvaluationRun([
|
||||
'round_id' => $round->id,
|
||||
'rule_set_id' => $ruleSet->id,
|
||||
]);
|
||||
$band = $this->createBand();
|
||||
|
||||
$t0 = Carbon::create(2025, 1, 1, 0, 0, 0, 'UTC');
|
||||
$t1 = Carbon::create(2025, 1, 1, 1, 0, 0, 'UTC');
|
||||
$t2 = Carbon::create(2025, 1, 1, 7, 0, 0, 'UTC');
|
||||
$t3 = Carbon::create(2025, 1, 1, 8, 0, 0, 'UTC');
|
||||
|
||||
$firstA = $this->createQso($run->id, $log->id, $band->id, $t0, 10);
|
||||
$firstB = $this->createQso($run->id, $log->id, $band->id, $t1, 10);
|
||||
$secondA = $this->createQso($run->id, $log->id, $band->id, $t2, 10);
|
||||
$secondB = $this->createQso($run->id, $log->id, $band->id, $t3, 10);
|
||||
|
||||
$this->createLogResult([
|
||||
'evaluation_run_id' => $run->id,
|
||||
'log_id' => $log->id,
|
||||
'sixhr_category' => true,
|
||||
'status' => 'OK',
|
||||
]);
|
||||
|
||||
(new AggregateLogResultsJob($run->id, $log->id))->handle();
|
||||
|
||||
$result = LogResult::where('evaluation_run_id', $run->id)
|
||||
->where('log_id', $log->id)
|
||||
->first();
|
||||
|
||||
$this->assertNotNull($result);
|
||||
$this->assertSame(40, $result->official_score);
|
||||
$this->assertSame($t0->toDateTimeString(), $result->operating_window_start_utc?->toDateTimeString());
|
||||
$this->assertSame($t1->toDateTimeString(), $result->operating_window_end_utc?->toDateTimeString());
|
||||
$this->assertSame($t2->toDateTimeString(), $result->operating_window_2_start_utc?->toDateTimeString());
|
||||
$this->assertSame($t3->toDateTimeString(), $result->operating_window_2_end_utc?->toDateTimeString());
|
||||
$this->assertSame(6, $result->operating_window_hours);
|
||||
$this->assertSame(4, $result->operating_window_qso_count);
|
||||
|
||||
$included = QsoResult::where('evaluation_run_id', $run->id)
|
||||
->whereIn('log_qso_id', [$firstA, $firstB, $secondA, $secondB])
|
||||
->pluck('is_operating_window_excluded')
|
||||
->all();
|
||||
$this->assertSame([false, false, false, false], $included);
|
||||
|
||||
$excludedCount = QsoResult::where('evaluation_run_id', $run->id)
|
||||
->where('is_operating_window_excluded', true)
|
||||
->count();
|
||||
$this->assertSame(0, $excludedCount);
|
||||
}
|
||||
|
||||
public function test_aggregate_keeps_all_qso_for_non_6h(): void
|
||||
{
|
||||
$ruleSet = $this->createRuleSet([
|
||||
'use_multipliers' => false,
|
||||
'multiplier_type' => 'NONE',
|
||||
'multiplier_scope' => 'OVERALL',
|
||||
'multiplier_source' => 'VALID_ONLY',
|
||||
'operating_window_mode' => 'BEST_CONTIGUOUS',
|
||||
'operating_window_hours' => 6,
|
||||
]);
|
||||
$round = $this->createRound();
|
||||
$log = $this->createLog(['round_id' => $round->id]);
|
||||
$run = $this->createEvaluationRun([
|
||||
'round_id' => $round->id,
|
||||
'rule_set_id' => $ruleSet->id,
|
||||
]);
|
||||
$band = $this->createBand();
|
||||
|
||||
$t0 = Carbon::create(2025, 1, 1, 0, 0, 0, 'UTC');
|
||||
$t1 = Carbon::create(2025, 1, 1, 1, 0, 0, 'UTC');
|
||||
$t2 = Carbon::create(2025, 1, 1, 7, 0, 0, 'UTC');
|
||||
$t3 = Carbon::create(2025, 1, 1, 8, 0, 0, 'UTC');
|
||||
|
||||
$this->createQso($run->id, $log->id, $band->id, $t0, 10);
|
||||
$this->createQso($run->id, $log->id, $band->id, $t1, 10);
|
||||
$this->createQso($run->id, $log->id, $band->id, $t2, 10);
|
||||
$this->createQso($run->id, $log->id, $band->id, $t3, 10);
|
||||
|
||||
$this->createLogResult([
|
||||
'evaluation_run_id' => $run->id,
|
||||
'log_id' => $log->id,
|
||||
'sixhr_category' => false,
|
||||
'status' => 'OK',
|
||||
]);
|
||||
|
||||
(new AggregateLogResultsJob($run->id, $log->id))->handle();
|
||||
|
||||
$result = LogResult::where('evaluation_run_id', $run->id)
|
||||
->where('log_id', $log->id)
|
||||
->first();
|
||||
|
||||
$this->assertNotNull($result);
|
||||
$this->assertSame(40, $result->official_score);
|
||||
$this->assertNull($result->operating_window_start_utc);
|
||||
$this->assertNull($result->operating_window_end_utc);
|
||||
$this->assertNull($result->operating_window_2_start_utc);
|
||||
$this->assertNull($result->operating_window_2_end_utc);
|
||||
$this->assertNull($result->operating_window_hours);
|
||||
$this->assertNull($result->operating_window_qso_count);
|
||||
|
||||
$excludedCount = QsoResult::where('evaluation_run_id', $run->id)
|
||||
->where('is_operating_window_excluded', true)
|
||||
->count();
|
||||
$this->assertSame(0, $excludedCount);
|
||||
}
|
||||
}
|
||||
72
tests/Feature/Evaluation/EvaluationRunControllerTest.php
Normal file
72
tests/Feature/Evaluation/EvaluationRunControllerTest.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Evaluation;
|
||||
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class EvaluationRunControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_cancel_requires_authentication(): void
|
||||
{
|
||||
$run = $this->createEvaluationRun();
|
||||
|
||||
$this->postJson("/api/evaluation-runs/{$run->id}/cancel")
|
||||
->assertStatus(401);
|
||||
}
|
||||
|
||||
public function test_non_admin_cannot_cancel(): void
|
||||
{
|
||||
$this->actingAsUser();
|
||||
$run = $this->createEvaluationRun(['status' => 'RUNNING']);
|
||||
|
||||
$this->postJson("/api/evaluation-runs/{$run->id}/cancel")
|
||||
->assertStatus(403);
|
||||
}
|
||||
|
||||
public function test_admin_can_cancel_running_run(): void
|
||||
{
|
||||
$this->actingAsAdmin();
|
||||
$run = $this->createEvaluationRun(['status' => 'RUNNING']);
|
||||
|
||||
$response = $this->postJson("/api/evaluation-runs/{$run->id}/cancel");
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['status' => 'canceled']);
|
||||
|
||||
$this->assertDatabaseHas('evaluation_runs', [
|
||||
'id' => $run->id,
|
||||
'status' => 'CANCELED',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_cancel_rejects_finished_runs(): void
|
||||
{
|
||||
$this->actingAsAdmin();
|
||||
$run = $this->createEvaluationRun(['status' => 'SUCCEEDED']);
|
||||
|
||||
$this->postJson("/api/evaluation-runs/{$run->id}/cancel")
|
||||
->assertStatus(409);
|
||||
}
|
||||
|
||||
public function test_admin_can_set_result_type_and_update_round(): void
|
||||
{
|
||||
$this->actingAsAdmin();
|
||||
$run = $this->createEvaluationRun(['result_type' => null]);
|
||||
$round = $run->round;
|
||||
|
||||
$response = $this->postJson("/api/evaluation-runs/{$run->id}/result-type", [
|
||||
'result_type' => 'PRELIMINARY',
|
||||
]);
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['result_type' => 'PRELIMINARY']);
|
||||
|
||||
$round->refresh();
|
||||
$this->assertSame($run->id, $round->preliminary_evaluation_run_id);
|
||||
$this->assertNull($round->official_evaluation_run_id);
|
||||
$this->assertNull($round->test_evaluation_run_id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Evaluation;
|
||||
|
||||
use App\Jobs\RecalculateOfficialRanksJob;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class RecalculateOfficialRanksSixhrModeTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_iaru_sixhr_merges_single_and_multi(): void
|
||||
{
|
||||
$ruleSet = $this->createRuleSet(['sixhr_ranking_mode' => 'IARU']);
|
||||
$round = $this->createRound();
|
||||
$run = $this->createEvaluationRun(['round_id' => $round->id, 'rule_set_id' => $ruleSet->id]);
|
||||
|
||||
$band = $this->createBand();
|
||||
$single = $this->createCategory(['name' => 'SINGLE']);
|
||||
$multi = $this->createCategory(['name' => 'MULTI']);
|
||||
|
||||
$logSingle = $this->createLog(['round_id' => $round->id]);
|
||||
$logMulti = $this->createLog(['round_id' => $round->id]);
|
||||
|
||||
$resSingle = $this->createLogResult([
|
||||
'evaluation_run_id' => $run->id,
|
||||
'log_id' => $logSingle->id,
|
||||
'band_id' => $band->id,
|
||||
'category_id' => $single->id,
|
||||
'official_score' => 200,
|
||||
'valid_qso_count' => 10,
|
||||
'status' => 'OK',
|
||||
'sixhr_category' => true,
|
||||
]);
|
||||
$resMulti = $this->createLogResult([
|
||||
'evaluation_run_id' => $run->id,
|
||||
'log_id' => $logMulti->id,
|
||||
'band_id' => $band->id,
|
||||
'category_id' => $multi->id,
|
||||
'official_score' => 150,
|
||||
'valid_qso_count' => 8,
|
||||
'status' => 'OK',
|
||||
'sixhr_category' => true,
|
||||
]);
|
||||
|
||||
(new RecalculateOfficialRanksJob($run->id))->handle();
|
||||
|
||||
$resSingle->refresh();
|
||||
$resMulti->refresh();
|
||||
|
||||
$this->assertSame(1, $resSingle->rank_overall);
|
||||
$this->assertSame(2, $resMulti->rank_overall);
|
||||
$this->assertSame('ALL', $resSingle->sixhr_ranking_bucket);
|
||||
$this->assertSame('ALL', $resMulti->sixhr_ranking_bucket);
|
||||
}
|
||||
|
||||
public function test_crk_sixhr_keeps_single_and_multi_separate(): void
|
||||
{
|
||||
$ruleSet = $this->createRuleSet(['sixhr_ranking_mode' => 'CRK']);
|
||||
$round = $this->createRound();
|
||||
$run = $this->createEvaluationRun(['round_id' => $round->id, 'rule_set_id' => $ruleSet->id]);
|
||||
|
||||
$band = $this->createBand();
|
||||
$single = $this->createCategory(['name' => 'SINGLE']);
|
||||
$multi = $this->createCategory(['name' => 'MULTI']);
|
||||
|
||||
$logSingle = $this->createLog(['round_id' => $round->id]);
|
||||
$logMulti = $this->createLog(['round_id' => $round->id]);
|
||||
|
||||
$resSingle = $this->createLogResult([
|
||||
'evaluation_run_id' => $run->id,
|
||||
'log_id' => $logSingle->id,
|
||||
'band_id' => $band->id,
|
||||
'category_id' => $single->id,
|
||||
'official_score' => 200,
|
||||
'valid_qso_count' => 10,
|
||||
'status' => 'OK',
|
||||
'sixhr_category' => true,
|
||||
]);
|
||||
$resMulti = $this->createLogResult([
|
||||
'evaluation_run_id' => $run->id,
|
||||
'log_id' => $logMulti->id,
|
||||
'band_id' => $band->id,
|
||||
'category_id' => $multi->id,
|
||||
'official_score' => 150,
|
||||
'valid_qso_count' => 8,
|
||||
'status' => 'OK',
|
||||
'sixhr_category' => true,
|
||||
]);
|
||||
|
||||
(new RecalculateOfficialRanksJob($run->id))->handle();
|
||||
|
||||
$resSingle->refresh();
|
||||
$resMulti->refresh();
|
||||
|
||||
$this->assertSame(1, $resSingle->rank_overall);
|
||||
$this->assertSame(1, $resMulti->rank_overall);
|
||||
$this->assertSame('SINGLE', $resSingle->sixhr_ranking_bucket);
|
||||
$this->assertSame('MULTI', $resMulti->sixhr_ranking_bucket);
|
||||
}
|
||||
}
|
||||
7
tests/Feature/ExampleTest.php
Normal file
7
tests/Feature/ExampleTest.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
test('the application returns a successful response', function () {
|
||||
$response = $this->get('/');
|
||||
|
||||
$response->assertStatus(200);
|
||||
});
|
||||
86
tests/Feature/Files/FileControllerTest.php
Normal file
86
tests/Feature/Files/FileControllerTest.php
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Files;
|
||||
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\Queue;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Tests\TestCase;
|
||||
|
||||
class FileControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_index_returns_files(): void
|
||||
{
|
||||
$file = $this->createFile();
|
||||
|
||||
$response = $this->getJson('/api/files');
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $file->id]);
|
||||
}
|
||||
|
||||
public function test_show_returns_file_metadata(): void
|
||||
{
|
||||
$file = $this->createFile();
|
||||
|
||||
$response = $this->getJson("/api/files/{$file->id}");
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment([
|
||||
'id' => $file->id,
|
||||
'filename' => $file->filename,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_store_allows_guest_before_deadline(): void
|
||||
{
|
||||
Storage::fake();
|
||||
Queue::fake();
|
||||
$round = $this->createRound(['logs_deadline' => now()->addDay()]);
|
||||
|
||||
$response = $this->postJson('/api/files', [
|
||||
'round_id' => $round->id,
|
||||
'file' => UploadedFile::fake()->create('log.edi', 5, 'text/plain'),
|
||||
]);
|
||||
|
||||
$response->assertStatus(201);
|
||||
}
|
||||
|
||||
public function test_content_returns_stored_file(): void
|
||||
{
|
||||
Storage::fake();
|
||||
|
||||
$file = $this->createFile(['path' => 'uploads/test/file.edi', 'mimetype' => 'text/plain']);
|
||||
Storage::put($file->path, 'TEST');
|
||||
|
||||
$response = $this->get("/api/files/{$file->id}/content");
|
||||
|
||||
$response->assertStatus(200);
|
||||
$this->assertSame('TEST', $response->getContent());
|
||||
}
|
||||
|
||||
public function test_delete_requires_auth(): void
|
||||
{
|
||||
$file = $this->createFile();
|
||||
|
||||
$this->deleteJson("/api/files/{$file->id}")
|
||||
->assertStatus(401);
|
||||
}
|
||||
|
||||
public function test_admin_can_delete_file(): void
|
||||
{
|
||||
Storage::fake();
|
||||
$this->actingAsAdmin();
|
||||
|
||||
$file = $this->createFile(['path' => 'uploads/test/file.edi']);
|
||||
Storage::put($file->path, 'TEST');
|
||||
|
||||
$this->deleteJson("/api/files/{$file->id}")
|
||||
->assertStatus(204);
|
||||
|
||||
Storage::assertMissing($file->path);
|
||||
}
|
||||
}
|
||||
82
tests/Feature/Logs/LogControllerTest.php
Normal file
82
tests/Feature/Logs/LogControllerTest.php
Normal file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Logs;
|
||||
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class LogControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_index_returns_logs(): void
|
||||
{
|
||||
$log = $this->createLog();
|
||||
|
||||
$response = $this->getJson('/api/logs');
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $log->id]);
|
||||
}
|
||||
|
||||
public function test_index_can_filter_by_round(): void
|
||||
{
|
||||
$roundA = $this->createRound();
|
||||
$roundB = $this->createRound();
|
||||
$logA = $this->createLog(['round_id' => $roundA->id]);
|
||||
$this->createLog(['round_id' => $roundB->id]);
|
||||
|
||||
$response = $this->getJson("/api/logs?round_id={$roundA->id}");
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $logA->id]);
|
||||
|
||||
$ids = collect($response->json('data'))->pluck('id')->all();
|
||||
$this->assertCount(1, $ids);
|
||||
}
|
||||
|
||||
public function test_show_returns_log(): void
|
||||
{
|
||||
$log = $this->createLog();
|
||||
|
||||
$response = $this->getJson("/api/logs/{$log->id}");
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $log->id]);
|
||||
}
|
||||
|
||||
public function test_admin_can_create_update_and_delete_log(): void
|
||||
{
|
||||
$this->actingAsAdmin();
|
||||
$round = $this->createRound();
|
||||
|
||||
$createResponse = $this->postJson('/api/logs', [
|
||||
'round_id' => $round->id,
|
||||
'pcall' => 'OK1ABC',
|
||||
]);
|
||||
|
||||
$createResponse->assertStatus(201);
|
||||
$logId = $createResponse->json('id');
|
||||
|
||||
$updateResponse = $this->putJson("/api/logs/{$logId}", [
|
||||
'pcall' => 'OK1DEF',
|
||||
]);
|
||||
|
||||
$updateResponse->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $logId]);
|
||||
|
||||
$this->deleteJson("/api/logs/{$logId}")
|
||||
->assertStatus(204);
|
||||
}
|
||||
|
||||
public function test_non_admin_cannot_create_log(): void
|
||||
{
|
||||
$this->actingAsUser();
|
||||
$round = $this->createRound();
|
||||
|
||||
$this->postJson('/api/logs', [
|
||||
'round_id' => $round->id,
|
||||
'pcall' => 'OK1ABC',
|
||||
])->assertStatus(403);
|
||||
}
|
||||
}
|
||||
84
tests/Feature/Logs/LogQsoControllerTest.php
Normal file
84
tests/Feature/Logs/LogQsoControllerTest.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Logs;
|
||||
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class LogQsoControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_index_returns_log_qsos(): void
|
||||
{
|
||||
$qso = $this->createLogQso();
|
||||
|
||||
$response = $this->getJson('/api/log-qsos');
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $qso->id]);
|
||||
}
|
||||
|
||||
public function test_index_can_filter_by_log_id(): void
|
||||
{
|
||||
$logA = $this->createLog();
|
||||
$logB = $this->createLog();
|
||||
$qsoA = $this->createLogQso(['log_id' => $logA->id]);
|
||||
$this->createLogQso(['log_id' => $logB->id]);
|
||||
|
||||
$response = $this->getJson("/api/log-qsos?log_id={$logA->id}");
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $qsoA->id]);
|
||||
|
||||
$ids = collect($response->json('data'))->pluck('id')->all();
|
||||
$this->assertCount(1, $ids);
|
||||
}
|
||||
|
||||
public function test_show_returns_log_qso(): void
|
||||
{
|
||||
$qso = $this->createLogQso();
|
||||
|
||||
$response = $this->getJson("/api/log-qsos/{$qso->id}");
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $qso->id]);
|
||||
}
|
||||
|
||||
public function test_admin_can_create_update_and_delete_log_qso(): void
|
||||
{
|
||||
$this->actingAsAdmin();
|
||||
$log = $this->createLog();
|
||||
|
||||
$createResponse = $this->postJson('/api/log-qsos', [
|
||||
'log_id' => $log->id,
|
||||
'my_call' => 'OK1ABC',
|
||||
'dx_call' => 'OK2ABC',
|
||||
]);
|
||||
|
||||
$createResponse->assertStatus(201);
|
||||
$qsoId = $createResponse->json('id');
|
||||
|
||||
$updateResponse = $this->putJson("/api/log-qsos/{$qsoId}", [
|
||||
'dx_call' => 'OK9XYZ',
|
||||
]);
|
||||
|
||||
$updateResponse->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $qsoId]);
|
||||
|
||||
$this->deleteJson("/api/log-qsos/{$qsoId}")
|
||||
->assertStatus(204);
|
||||
}
|
||||
|
||||
public function test_non_admin_cannot_create_log_qso(): void
|
||||
{
|
||||
$this->actingAsUser();
|
||||
$log = $this->createLog();
|
||||
|
||||
$this->postJson('/api/log-qsos', [
|
||||
'log_id' => $log->id,
|
||||
'my_call' => 'OK1ABC',
|
||||
'dx_call' => 'OK2ABC',
|
||||
])->assertStatus(403);
|
||||
}
|
||||
}
|
||||
119
tests/Feature/Logs/LogQsoTableTest.php
Normal file
119
tests/Feature/Logs/LogQsoTableTest.php
Normal file
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Logs;
|
||||
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class LogQsoTableTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_qso_table_uses_latest_succeeded_non_claimed_run_by_default(): void
|
||||
{
|
||||
$round = $this->createRound();
|
||||
$log = $this->createLog(['round_id' => $round->id]);
|
||||
|
||||
$qsoA = $this->createLogQso([
|
||||
'log_id' => $log->id,
|
||||
'qso_index' => 1,
|
||||
'dx_call' => 'OK1AAA',
|
||||
]);
|
||||
$qsoB = $this->createLogQso([
|
||||
'log_id' => $log->id,
|
||||
'qso_index' => 2,
|
||||
'dx_call' => 'OK1BBB',
|
||||
]);
|
||||
|
||||
$claimedRun = $this->createEvaluationRun([
|
||||
'round_id' => $round->id,
|
||||
'rules_version' => 'CLAIMED',
|
||||
'status' => 'SUCCEEDED',
|
||||
]);
|
||||
$this->createQsoResult([
|
||||
'evaluation_run_id' => $claimedRun->id,
|
||||
'log_qso_id' => $qsoA->id,
|
||||
'error_code' => 'DUP',
|
||||
]);
|
||||
|
||||
$officialRun = $this->createEvaluationRun([
|
||||
'round_id' => $round->id,
|
||||
'rules_version' => 'OFFICIAL',
|
||||
'status' => 'SUCCEEDED',
|
||||
]);
|
||||
$this->createQsoResult([
|
||||
'evaluation_run_id' => $officialRun->id,
|
||||
'log_qso_id' => $qsoA->id,
|
||||
'error_code' => 'OK',
|
||||
'penalty_points' => 5,
|
||||
]);
|
||||
$this->createQsoOverride([
|
||||
'evaluation_run_id' => $officialRun->id,
|
||||
'log_qso_id' => $qsoB->id,
|
||||
'forced_status' => 'AUTO',
|
||||
'reason' => 'Manual override',
|
||||
]);
|
||||
|
||||
$response = $this->getJson("/api/logs/{$log->id}/qso-table");
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['evaluation_run_id' => $officialRun->id]);
|
||||
|
||||
$rows = collect($response->json('data'));
|
||||
$this->assertCount(2, $rows);
|
||||
|
||||
$rowA = $rows->firstWhere('id', $qsoA->id);
|
||||
$rowB = $rows->firstWhere('id', $qsoB->id);
|
||||
|
||||
$this->assertSame('OK1AAA', $rowA['dx_call']);
|
||||
$this->assertSame('OK', $rowA['result']['error_code']);
|
||||
$this->assertSame(5, $rowA['result']['penalty_points']);
|
||||
$this->assertNull($rowA['override']);
|
||||
|
||||
$this->assertSame('OK1BBB', $rowB['dx_call']);
|
||||
$this->assertNull($rowB['result']);
|
||||
$this->assertSame('Manual override', $rowB['override']['reason']);
|
||||
}
|
||||
|
||||
public function test_qso_table_respects_explicit_evaluation_run_id(): void
|
||||
{
|
||||
$round = $this->createRound();
|
||||
$log = $this->createLog(['round_id' => $round->id]);
|
||||
|
||||
$qso = $this->createLogQso([
|
||||
'log_id' => $log->id,
|
||||
'qso_index' => 1,
|
||||
]);
|
||||
|
||||
$claimedRun = $this->createEvaluationRun([
|
||||
'round_id' => $round->id,
|
||||
'rules_version' => 'CLAIMED',
|
||||
'status' => 'SUCCEEDED',
|
||||
]);
|
||||
$this->createQsoResult([
|
||||
'evaluation_run_id' => $claimedRun->id,
|
||||
'log_qso_id' => $qso->id,
|
||||
'error_code' => 'DUP',
|
||||
]);
|
||||
|
||||
$officialRun = $this->createEvaluationRun([
|
||||
'round_id' => $round->id,
|
||||
'rules_version' => 'OFFICIAL',
|
||||
'status' => 'SUCCEEDED',
|
||||
]);
|
||||
$this->createQsoResult([
|
||||
'evaluation_run_id' => $officialRun->id,
|
||||
'log_qso_id' => $qso->id,
|
||||
'error_code' => 'OK',
|
||||
]);
|
||||
|
||||
$response = $this->getJson("/api/logs/{$log->id}/qso-table?evaluation_run_id={$claimedRun->id}");
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['evaluation_run_id' => $claimedRun->id]);
|
||||
|
||||
$rows = collect($response->json('data'));
|
||||
$row = $rows->firstWhere('id', $qso->id);
|
||||
$this->assertSame('DUP', $row['result']['error_code']);
|
||||
}
|
||||
}
|
||||
73
tests/Feature/News/NewsPostControllerTest.php
Normal file
73
tests/Feature/News/NewsPostControllerTest.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\News;
|
||||
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class NewsPostControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_index_returns_only_published_by_default(): void
|
||||
{
|
||||
$published = $this->createNewsPost();
|
||||
$unpublished = $this->createNewsPost(['is_published' => false, 'published_at' => null]);
|
||||
|
||||
$response = $this->getJson('/api/news');
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $published->id]);
|
||||
|
||||
$ids = collect($response->json('data'))->pluck('id')->all();
|
||||
$this->assertFalse(in_array($unpublished->id, $ids, true));
|
||||
}
|
||||
|
||||
public function test_show_returns_404_for_unpublished(): void
|
||||
{
|
||||
$news = $this->createNewsPost(['is_published' => false, 'published_at' => null]);
|
||||
|
||||
$this->getJson("/api/news/{$news->slug}")
|
||||
->assertStatus(404);
|
||||
}
|
||||
|
||||
public function test_admin_can_create_update_and_delete_news(): void
|
||||
{
|
||||
$this->actingAsAdmin();
|
||||
|
||||
$createResponse = $this->postJson('/api/news', [
|
||||
'title' => ['cs' => 'Novinka', 'en' => 'News'],
|
||||
'content' => ['cs' => 'Obsah', 'en' => 'Content'],
|
||||
'excerpt' => ['cs' => 'Krátce', 'en' => 'Short'],
|
||||
'is_published' => true,
|
||||
]);
|
||||
|
||||
$createResponse->assertStatus(201);
|
||||
$slug = $createResponse->json('slug');
|
||||
|
||||
$updateResponse = $this->putJson("/api/news/{$slug}", [
|
||||
'title' => ['cs' => 'Novinka 2', 'en' => 'News 2'],
|
||||
'content' => ['cs' => 'Obsah', 'en' => 'Content'],
|
||||
'excerpt' => ['cs' => 'Krátce', 'en' => 'Short'],
|
||||
]);
|
||||
|
||||
$updateResponse->assertStatus(200)
|
||||
->assertJsonStructure(['slug']);
|
||||
|
||||
$updatedSlug = $updateResponse->json('slug');
|
||||
|
||||
$this->deleteJson("/api/news/{$updatedSlug}")
|
||||
->assertStatus(204);
|
||||
}
|
||||
|
||||
public function test_non_admin_cannot_create_news(): void
|
||||
{
|
||||
$this->actingAsUser();
|
||||
|
||||
$this->postJson('/api/news', [
|
||||
'title' => ['cs' => 'Novinka', 'en' => 'News'],
|
||||
'content' => ['cs' => 'Obsah', 'en' => 'Content'],
|
||||
'excerpt' => ['cs' => 'Krátce', 'en' => 'Short'],
|
||||
])->assertStatus(403);
|
||||
}
|
||||
}
|
||||
90
tests/Feature/Results/LogOverrideControllerTest.php
Normal file
90
tests/Feature/Results/LogOverrideControllerTest.php
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Results;
|
||||
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class LogOverrideControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_index_returns_log_overrides(): void
|
||||
{
|
||||
$override = $this->createLogOverride();
|
||||
|
||||
$response = $this->getJson('/api/log-overrides');
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $override->id]);
|
||||
}
|
||||
|
||||
public function test_index_can_filter_by_evaluation_run(): void
|
||||
{
|
||||
$runA = $this->createEvaluationRun();
|
||||
$runB = $this->createEvaluationRun();
|
||||
$overrideA = $this->createLogOverride(['evaluation_run_id' => $runA->id]);
|
||||
$this->createLogOverride(['evaluation_run_id' => $runB->id]);
|
||||
|
||||
$response = $this->getJson("/api/log-overrides?evaluation_run_id={$runA->id}");
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $overrideA->id]);
|
||||
|
||||
$ids = collect($response->json('data'))->pluck('id')->all();
|
||||
$this->assertCount(1, $ids);
|
||||
}
|
||||
|
||||
public function test_show_returns_log_override(): void
|
||||
{
|
||||
$override = $this->createLogOverride();
|
||||
|
||||
$response = $this->getJson("/api/log-overrides/{$override->id}");
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $override->id]);
|
||||
}
|
||||
|
||||
public function test_admin_can_create_update_and_delete_override(): void
|
||||
{
|
||||
$this->actingAsAdmin();
|
||||
$run = $this->createEvaluationRun();
|
||||
$log = $this->createLog(['round_id' => $run->round_id]);
|
||||
|
||||
$createResponse = $this->postJson('/api/log-overrides', [
|
||||
'evaluation_run_id' => $run->id,
|
||||
'log_id' => $log->id,
|
||||
'forced_log_status' => 'CHECK',
|
||||
'reason' => 'Test důvod',
|
||||
]);
|
||||
|
||||
$createResponse->assertStatus(201);
|
||||
$overrideId = $createResponse->json('id');
|
||||
|
||||
$updateResponse = $this->putJson("/api/log-overrides/{$overrideId}", [
|
||||
'evaluation_run_id' => $run->id,
|
||||
'log_id' => $log->id,
|
||||
'forced_log_status' => 'OK',
|
||||
'reason' => 'Aktualizace',
|
||||
]);
|
||||
|
||||
$updateResponse->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $overrideId]);
|
||||
|
||||
$this->deleteJson("/api/log-overrides/{$overrideId}")
|
||||
->assertStatus(204);
|
||||
}
|
||||
|
||||
public function test_non_admin_cannot_create_override(): void
|
||||
{
|
||||
$this->actingAsUser();
|
||||
$run = $this->createEvaluationRun();
|
||||
$log = $this->createLog(['round_id' => $run->round_id]);
|
||||
|
||||
$this->postJson('/api/log-overrides', [
|
||||
'evaluation_run_id' => $run->id,
|
||||
'log_id' => $log->id,
|
||||
'forced_log_status' => 'CHECK',
|
||||
])->assertStatus(403);
|
||||
}
|
||||
}
|
||||
141
tests/Feature/Results/LogResultControllerTest.php
Normal file
141
tests/Feature/Results/LogResultControllerTest.php
Normal file
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Results;
|
||||
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class LogResultControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_index_filters_by_evaluation_run(): void
|
||||
{
|
||||
$runA = $this->createEvaluationRun();
|
||||
$runB = $this->createEvaluationRun();
|
||||
$logA = $this->createLog(['round_id' => $runA->round_id]);
|
||||
$logB = $this->createLog(['round_id' => $runB->round_id]);
|
||||
$resultA = $this->createLogResult([
|
||||
'evaluation_run_id' => $runA->id,
|
||||
'log_id' => $logA->id,
|
||||
]);
|
||||
$this->createLogResult([
|
||||
'evaluation_run_id' => $runB->id,
|
||||
'log_id' => $logB->id,
|
||||
]);
|
||||
|
||||
$response = $this->getJson("/api/log-results?evaluation_run_id={$runA->id}");
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $resultA->id]);
|
||||
|
||||
$ids = collect($response->json('data'))->pluck('id')->all();
|
||||
$this->assertCount(1, $ids);
|
||||
}
|
||||
|
||||
public function test_index_resolves_claimed_run_for_round(): void
|
||||
{
|
||||
$round = $this->createRound();
|
||||
$run = $this->createEvaluationRun([
|
||||
'round_id' => $round->id,
|
||||
'rules_version' => 'CLAIMED',
|
||||
]);
|
||||
$log = $this->createLog(['round_id' => $round->id]);
|
||||
$result = $this->createLogResult([
|
||||
'evaluation_run_id' => $run->id,
|
||||
'log_id' => $log->id,
|
||||
]);
|
||||
|
||||
$response = $this->getJson("/api/log-results?round_id={$round->id}&status=CLAIMED");
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $result->id]);
|
||||
}
|
||||
|
||||
public function test_index_resolves_auto_result_type_from_round(): void
|
||||
{
|
||||
$round = $this->createRound();
|
||||
$run = $this->createEvaluationRun(['round_id' => $round->id]);
|
||||
$round->update(['official_evaluation_run_id' => $run->id]);
|
||||
$log = $this->createLog(['round_id' => $round->id]);
|
||||
$result = $this->createLogResult([
|
||||
'evaluation_run_id' => $run->id,
|
||||
'log_id' => $log->id,
|
||||
]);
|
||||
|
||||
$response = $this->getJson("/api/log-results?round_id={$round->id}&result_type=AUTO");
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $result->id]);
|
||||
}
|
||||
|
||||
public function test_index_only_ok_filters_by_callsign(): void
|
||||
{
|
||||
$round = $this->createRound();
|
||||
$run = $this->createEvaluationRun(['round_id' => $round->id]);
|
||||
|
||||
$logOk = $this->createLog([
|
||||
'round_id' => $round->id,
|
||||
'pcall' => 'OK1ABC',
|
||||
]);
|
||||
$logOther = $this->createLog([
|
||||
'round_id' => $round->id,
|
||||
'pcall' => 'DL1ABC',
|
||||
]);
|
||||
$resultOk = $this->createLogResult([
|
||||
'evaluation_run_id' => $run->id,
|
||||
'log_id' => $logOk->id,
|
||||
]);
|
||||
$this->createLogResult([
|
||||
'evaluation_run_id' => $run->id,
|
||||
'log_id' => $logOther->id,
|
||||
]);
|
||||
|
||||
$response = $this->getJson("/api/log-results?evaluation_run_id={$run->id}&round_id={$round->id}&only_ok=1");
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $resultOk->id]);
|
||||
|
||||
$ids = collect($response->json('data'))->pluck('id')->all();
|
||||
$this->assertCount(1, $ids);
|
||||
}
|
||||
|
||||
public function test_admin_can_create_update_and_delete_log_result(): void
|
||||
{
|
||||
$this->actingAsAdmin();
|
||||
$run = $this->createEvaluationRun();
|
||||
$log = $this->createLog(['round_id' => $run->round_id]);
|
||||
|
||||
$createResponse = $this->postJson('/api/log-results', [
|
||||
'evaluation_run_id' => $run->id,
|
||||
'log_id' => $log->id,
|
||||
'claimed_qso_count' => 10,
|
||||
'claimed_score' => 100,
|
||||
]);
|
||||
|
||||
$createResponse->assertStatus(201);
|
||||
$resultId = $createResponse->json('id');
|
||||
|
||||
$updateResponse = $this->putJson("/api/log-results/{$resultId}", [
|
||||
'claimed_score' => 200,
|
||||
]);
|
||||
|
||||
$updateResponse->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $resultId]);
|
||||
|
||||
$this->deleteJson("/api/log-results/{$resultId}")
|
||||
->assertStatus(204);
|
||||
}
|
||||
|
||||
public function test_non_admin_cannot_create_log_result(): void
|
||||
{
|
||||
$this->actingAsUser();
|
||||
$run = $this->createEvaluationRun();
|
||||
$log = $this->createLog(['round_id' => $run->round_id]);
|
||||
|
||||
$this->postJson('/api/log-results', [
|
||||
'evaluation_run_id' => $run->id,
|
||||
'log_id' => $log->id,
|
||||
])->assertStatus(403);
|
||||
}
|
||||
}
|
||||
90
tests/Feature/Results/QsoOverrideControllerTest.php
Normal file
90
tests/Feature/Results/QsoOverrideControllerTest.php
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Results;
|
||||
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class QsoOverrideControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_index_returns_qso_overrides(): void
|
||||
{
|
||||
$override = $this->createQsoOverride();
|
||||
|
||||
$response = $this->getJson('/api/qso-overrides');
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $override->id]);
|
||||
}
|
||||
|
||||
public function test_index_can_filter_by_evaluation_run(): void
|
||||
{
|
||||
$runA = $this->createEvaluationRun();
|
||||
$runB = $this->createEvaluationRun();
|
||||
$overrideA = $this->createQsoOverride(['evaluation_run_id' => $runA->id]);
|
||||
$this->createQsoOverride(['evaluation_run_id' => $runB->id]);
|
||||
|
||||
$response = $this->getJson("/api/qso-overrides?evaluation_run_id={$runA->id}");
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $overrideA->id]);
|
||||
|
||||
$ids = collect($response->json('data'))->pluck('id')->all();
|
||||
$this->assertCount(1, $ids);
|
||||
}
|
||||
|
||||
public function test_show_returns_qso_override(): void
|
||||
{
|
||||
$override = $this->createQsoOverride();
|
||||
|
||||
$response = $this->getJson("/api/qso-overrides/{$override->id}");
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $override->id]);
|
||||
}
|
||||
|
||||
public function test_admin_can_create_update_and_delete_override(): void
|
||||
{
|
||||
$this->actingAsAdmin();
|
||||
$run = $this->createEvaluationRun();
|
||||
$logQso = $this->createLogQso();
|
||||
|
||||
$createResponse = $this->postJson('/api/qso-overrides', [
|
||||
'evaluation_run_id' => $run->id,
|
||||
'log_qso_id' => $logQso->id,
|
||||
'forced_status' => 'VALID',
|
||||
'reason' => 'Test důvod',
|
||||
]);
|
||||
|
||||
$createResponse->assertStatus(201);
|
||||
$overrideId = $createResponse->json('id');
|
||||
|
||||
$updateResponse = $this->putJson("/api/qso-overrides/{$overrideId}", [
|
||||
'evaluation_run_id' => $run->id,
|
||||
'log_qso_id' => $logQso->id,
|
||||
'forced_status' => 'INVALID',
|
||||
'reason' => 'Aktualizace',
|
||||
]);
|
||||
|
||||
$updateResponse->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $overrideId]);
|
||||
|
||||
$this->deleteJson("/api/qso-overrides/{$overrideId}")
|
||||
->assertStatus(204);
|
||||
}
|
||||
|
||||
public function test_non_admin_cannot_create_override(): void
|
||||
{
|
||||
$this->actingAsUser();
|
||||
$run = $this->createEvaluationRun();
|
||||
$logQso = $this->createLogQso();
|
||||
|
||||
$this->postJson('/api/qso-overrides', [
|
||||
'evaluation_run_id' => $run->id,
|
||||
'log_qso_id' => $logQso->id,
|
||||
'forced_status' => 'VALID',
|
||||
])->assertStatus(403);
|
||||
}
|
||||
}
|
||||
85
tests/Feature/Results/QsoResultControllerTest.php
Normal file
85
tests/Feature/Results/QsoResultControllerTest.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Results;
|
||||
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class QsoResultControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_index_returns_qso_results(): void
|
||||
{
|
||||
$result = $this->createQsoResult();
|
||||
|
||||
$response = $this->getJson('/api/qso-results');
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $result->id]);
|
||||
}
|
||||
|
||||
public function test_index_can_filter_by_evaluation_run(): void
|
||||
{
|
||||
$runA = $this->createEvaluationRun();
|
||||
$runB = $this->createEvaluationRun();
|
||||
$resultA = $this->createQsoResult(['evaluation_run_id' => $runA->id]);
|
||||
$this->createQsoResult(['evaluation_run_id' => $runB->id]);
|
||||
|
||||
$response = $this->getJson("/api/qso-results?evaluation_run_id={$runA->id}");
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $resultA->id]);
|
||||
|
||||
$ids = collect($response->json('data'))->pluck('id')->all();
|
||||
$this->assertCount(1, $ids);
|
||||
}
|
||||
|
||||
public function test_show_returns_qso_result(): void
|
||||
{
|
||||
$result = $this->createQsoResult();
|
||||
|
||||
$response = $this->getJson("/api/qso-results/{$result->id}");
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $result->id]);
|
||||
}
|
||||
|
||||
public function test_admin_can_create_update_and_delete_qso_result(): void
|
||||
{
|
||||
$this->actingAsAdmin();
|
||||
$run = $this->createEvaluationRun();
|
||||
$logQso = $this->createLogQso();
|
||||
|
||||
$createResponse = $this->postJson('/api/qso-results', [
|
||||
'evaluation_run_id' => $run->id,
|
||||
'log_qso_id' => $logQso->id,
|
||||
'points' => 50,
|
||||
]);
|
||||
|
||||
$createResponse->assertStatus(201);
|
||||
$resultId = $createResponse->json('id');
|
||||
|
||||
$updateResponse = $this->putJson("/api/qso-results/{$resultId}", [
|
||||
'points' => 75,
|
||||
]);
|
||||
|
||||
$updateResponse->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $resultId]);
|
||||
|
||||
$this->deleteJson("/api/qso-results/{$resultId}")
|
||||
->assertStatus(204);
|
||||
}
|
||||
|
||||
public function test_non_admin_cannot_create_qso_result(): void
|
||||
{
|
||||
$this->actingAsUser();
|
||||
$run = $this->createEvaluationRun();
|
||||
$logQso = $this->createLogQso();
|
||||
|
||||
$this->postJson('/api/qso-results', [
|
||||
'evaluation_run_id' => $run->id,
|
||||
'log_qso_id' => $logQso->id,
|
||||
])->assertStatus(403);
|
||||
}
|
||||
}
|
||||
104
tests/Feature/Rounds/RoundControllerTest.php
Normal file
104
tests/Feature/Rounds/RoundControllerTest.php
Normal file
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Rounds;
|
||||
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class RoundControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_index_returns_rounds(): void
|
||||
{
|
||||
$round = $this->createRound();
|
||||
|
||||
$response = $this->getJson('/api/rounds');
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $round->id]);
|
||||
}
|
||||
|
||||
public function test_index_can_filter_by_contest(): void
|
||||
{
|
||||
$contestA = $this->createContest();
|
||||
$contestB = $this->createContest();
|
||||
$roundA = $this->createRound(['contest_id' => $contestA->id]);
|
||||
$this->createRound(['contest_id' => $contestB->id]);
|
||||
|
||||
$response = $this->getJson("/api/rounds?contest_id={$contestA->id}");
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $roundA->id]);
|
||||
|
||||
$ids = collect($response->json('data'))->pluck('id')->all();
|
||||
$this->assertCount(1, $ids);
|
||||
}
|
||||
|
||||
public function test_show_returns_round(): void
|
||||
{
|
||||
$round = $this->createRound();
|
||||
|
||||
$response = $this->getJson("/api/rounds/{$round->id}");
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $round->id]);
|
||||
}
|
||||
|
||||
public function test_admin_can_create_update_and_delete_round(): void
|
||||
{
|
||||
$this->actingAsAdmin();
|
||||
$contest = $this->createContest();
|
||||
$ruleSet = $this->createRuleSet();
|
||||
$start = now()->addDay();
|
||||
$end = (clone $start)->addHours(2);
|
||||
$deadline = (clone $end)->addDays(2);
|
||||
|
||||
$createResponse = $this->postJson('/api/rounds', [
|
||||
'contest_id' => $contest->id,
|
||||
'rule_set_id' => $ruleSet->id,
|
||||
'name' => ['cs' => 'Kolo 1', 'en' => 'Round 1'],
|
||||
'description' => ['cs' => 'Popis', 'en' => 'Description'],
|
||||
'start_time' => $start->toDateTimeString(),
|
||||
'end_time' => $end->toDateTimeString(),
|
||||
'logs_deadline' => $deadline->toDateTimeString(),
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$createResponse->assertStatus(201);
|
||||
$roundId = $createResponse->json('id');
|
||||
|
||||
$updateResponse = $this->putJson("/api/rounds/{$roundId}", [
|
||||
'contest_id' => $contest->id,
|
||||
'name' => ['cs' => 'Kolo 1B', 'en' => 'Round 1B'],
|
||||
'description' => ['cs' => 'Popis', 'en' => 'Description'],
|
||||
'start_time' => $start->toDateTimeString(),
|
||||
'end_time' => $end->toDateTimeString(),
|
||||
'logs_deadline' => $deadline->toDateTimeString(),
|
||||
'is_active' => false,
|
||||
]);
|
||||
|
||||
$updateResponse->assertStatus(200)
|
||||
->assertJsonFragment(['id' => $roundId]);
|
||||
|
||||
$this->deleteJson("/api/rounds/{$roundId}")
|
||||
->assertStatus(204);
|
||||
}
|
||||
|
||||
public function test_non_admin_cannot_create_round(): void
|
||||
{
|
||||
$this->actingAsUser();
|
||||
$contest = $this->createContest();
|
||||
$start = now()->addDay();
|
||||
$end = (clone $start)->addHours(2);
|
||||
$deadline = (clone $end)->addDays(2);
|
||||
|
||||
$this->postJson('/api/rounds', [
|
||||
'contest_id' => $contest->id,
|
||||
'name' => ['cs' => 'Kolo 1', 'en' => 'Round 1'],
|
||||
'start_time' => $start->toDateTimeString(),
|
||||
'end_time' => $end->toDateTimeString(),
|
||||
'logs_deadline' => $deadline->toDateTimeString(),
|
||||
])->assertStatus(403);
|
||||
}
|
||||
}
|
||||
47
tests/Pest.php
Normal file
47
tests/Pest.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Test Case
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The closure you provide to your test functions is always bound to a specific PHPUnit test
|
||||
| case class. By default, that class is "PHPUnit\Framework\TestCase". Of course, you may
|
||||
| need to change it using the "pest()" function to bind a different classes or traits.
|
||||
|
|
||||
*/
|
||||
|
||||
pest()->extend(Tests\TestCase::class)
|
||||
// ->use(Illuminate\Foundation\Testing\RefreshDatabase::class)
|
||||
->in('Feature');
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Expectations
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When you're writing tests, you often need to check that values meet certain conditions. The
|
||||
| "expect()" function gives you access to a set of "expectations" methods that you can use
|
||||
| to assert different things. Of course, you may extend the Expectation API at any time.
|
||||
|
|
||||
*/
|
||||
|
||||
expect()->extend('toBeOne', function () {
|
||||
return $this->toBe(1);
|
||||
});
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Functions
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| While Pest is very powerful out-of-the-box, you may have some testing code specific to your
|
||||
| project that you don't want to repeat in every file. Here you can also expose helpers as
|
||||
| global functions to help you to reduce the number of lines of code in your test files.
|
||||
|
|
||||
*/
|
||||
|
||||
function something()
|
||||
{
|
||||
// ..
|
||||
}
|
||||
38
tests/Support/ActsAsUser.php
Normal file
38
tests/Support/ActsAsUser.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Support;
|
||||
|
||||
use App\Models\User;
|
||||
use Laravel\Sanctum\Sanctum;
|
||||
|
||||
trait ActsAsUser
|
||||
{
|
||||
protected function createUser(array $overrides = []): User
|
||||
{
|
||||
return User::factory()->create($overrides);
|
||||
}
|
||||
|
||||
protected function createAdminUser(array $overrides = []): User
|
||||
{
|
||||
return User::factory()->admin()->create($overrides);
|
||||
}
|
||||
|
||||
protected function createInactiveUser(array $overrides = []): User
|
||||
{
|
||||
return User::factory()->inactive()->create($overrides);
|
||||
}
|
||||
|
||||
protected function actingAsUser(array $overrides = []): User
|
||||
{
|
||||
$user = $this->createUser($overrides);
|
||||
Sanctum::actingAs($user);
|
||||
return $user;
|
||||
}
|
||||
|
||||
protected function actingAsAdmin(array $overrides = []): User
|
||||
{
|
||||
$user = $this->createAdminUser($overrides);
|
||||
Sanctum::actingAs($user);
|
||||
return $user;
|
||||
}
|
||||
}
|
||||
144
tests/Support/CreatesDomainData.php
Normal file
144
tests/Support/CreatesDomainData.php
Normal file
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Support;
|
||||
|
||||
use App\Models\Band;
|
||||
use App\Models\Category;
|
||||
use App\Models\Contest;
|
||||
use App\Models\EvaluationRuleSet;
|
||||
use App\Models\EvaluationRun;
|
||||
use App\Models\EdiBand;
|
||||
use App\Models\EdiCategory;
|
||||
use App\Models\Cty;
|
||||
use App\Models\CountryWwl;
|
||||
use App\Models\ContestParameter;
|
||||
use App\Models\Log;
|
||||
use App\Models\LogQso;
|
||||
use App\Models\LogResult;
|
||||
use App\Models\LogOverride;
|
||||
use App\Models\QsoOverride;
|
||||
use App\Models\QsoResult;
|
||||
use App\Models\NewsPost;
|
||||
use App\Models\File;
|
||||
use App\Models\PowerCategory;
|
||||
use App\Models\Round;
|
||||
|
||||
trait CreatesDomainData
|
||||
{
|
||||
protected function createRuleSet(array $overrides = []): EvaluationRuleSet
|
||||
{
|
||||
return EvaluationRuleSet::factory()->create($overrides);
|
||||
}
|
||||
|
||||
protected function createContest(array $overrides = []): Contest
|
||||
{
|
||||
return Contest::factory()->create($overrides);
|
||||
}
|
||||
|
||||
protected function createRound(array $overrides = []): Round
|
||||
{
|
||||
return Round::factory()->create($overrides);
|
||||
}
|
||||
|
||||
protected function createLog(array $overrides = []): Log
|
||||
{
|
||||
return Log::factory()->create($overrides);
|
||||
}
|
||||
|
||||
protected function createBand(array $overrides = []): Band
|
||||
{
|
||||
return Band::factory()->create($overrides);
|
||||
}
|
||||
|
||||
protected function createCategory(array $overrides = []): Category
|
||||
{
|
||||
return Category::factory()->create($overrides);
|
||||
}
|
||||
|
||||
protected function createPowerCategory(array $overrides = []): PowerCategory
|
||||
{
|
||||
return PowerCategory::factory()->create($overrides);
|
||||
}
|
||||
|
||||
protected function createEdiBand(array $overrides = []): EdiBand
|
||||
{
|
||||
return EdiBand::factory()->create($overrides);
|
||||
}
|
||||
|
||||
protected function createEdiCategory(array $overrides = []): EdiCategory
|
||||
{
|
||||
return EdiCategory::factory()->create($overrides);
|
||||
}
|
||||
|
||||
protected function createCty(array $overrides = []): Cty
|
||||
{
|
||||
return Cty::factory()->create($overrides);
|
||||
}
|
||||
|
||||
protected function createCountryWwl(array $overrides = []): CountryWwl
|
||||
{
|
||||
return CountryWwl::factory()->create($overrides);
|
||||
}
|
||||
|
||||
protected function createContestParameter(array $overrides = []): ContestParameter
|
||||
{
|
||||
return ContestParameter::factory()->create($overrides);
|
||||
}
|
||||
|
||||
protected function createLogQso(array $overrides = []): LogQso
|
||||
{
|
||||
return LogQso::factory()->create($overrides);
|
||||
}
|
||||
|
||||
protected function createLogResult(array $overrides = []): LogResult
|
||||
{
|
||||
return LogResult::factory()->create($overrides);
|
||||
}
|
||||
|
||||
protected function createLogOverride(array $overrides = []): LogOverride
|
||||
{
|
||||
return LogOverride::factory()->create($overrides);
|
||||
}
|
||||
|
||||
protected function createQsoOverride(array $overrides = []): QsoOverride
|
||||
{
|
||||
return QsoOverride::factory()->create($overrides);
|
||||
}
|
||||
|
||||
protected function createQsoResult(array $overrides = []): QsoResult
|
||||
{
|
||||
return QsoResult::factory()->create($overrides);
|
||||
}
|
||||
|
||||
protected function createNewsPost(array $overrides = []): NewsPost
|
||||
{
|
||||
return NewsPost::factory()->create($overrides);
|
||||
}
|
||||
|
||||
protected function createFile(array $overrides = []): File
|
||||
{
|
||||
return File::factory()->create($overrides);
|
||||
}
|
||||
|
||||
protected function createEvaluationRun(array $overrides = []): EvaluationRun
|
||||
{
|
||||
return EvaluationRun::factory()->create($overrides);
|
||||
}
|
||||
|
||||
protected function attachRoundRelations(
|
||||
Round $round,
|
||||
array $bands = [],
|
||||
array $categories = [],
|
||||
array $powerCategories = []
|
||||
): void {
|
||||
if ($bands) {
|
||||
$round->bands()->sync($bands);
|
||||
}
|
||||
if ($categories) {
|
||||
$round->categories()->sync($categories);
|
||||
}
|
||||
if ($powerCategories) {
|
||||
$round->powerCategories()->sync($powerCategories);
|
||||
}
|
||||
}
|
||||
}
|
||||
13
tests/TestCase.php
Normal file
13
tests/TestCase.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Tests;
|
||||
|
||||
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
|
||||
use Tests\Support\ActsAsUser;
|
||||
use Tests\Support\CreatesDomainData;
|
||||
|
||||
abstract class TestCase extends BaseTestCase
|
||||
{
|
||||
use ActsAsUser;
|
||||
use CreatesDomainData;
|
||||
}
|
||||
5
tests/Unit/ExampleTest.php
Normal file
5
tests/Unit/ExampleTest.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
test('that true is true', function () {
|
||||
expect(true)->toBeTrue();
|
||||
});
|
||||
160
tests/Unit/OperatingWindowServiceTest.php
Normal file
160
tests/Unit/OperatingWindowServiceTest.php
Normal file
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit;
|
||||
|
||||
use App\Models\EvaluationRuleSet;
|
||||
use App\Models\WorkingQso;
|
||||
use App\Services\Evaluation\OperatingWindowService;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class OperatingWindowServiceTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
private function createQso(int $runId, int $logId, int $bandId, Carbon $ts, int $points, bool $valid = true): int
|
||||
{
|
||||
$logQso = $this->createLogQso(['log_id' => $logId]);
|
||||
|
||||
$this->createQsoResult([
|
||||
'evaluation_run_id' => $runId,
|
||||
'log_qso_id' => $logQso->id,
|
||||
'is_valid' => $valid,
|
||||
'points' => $points,
|
||||
]);
|
||||
|
||||
WorkingQso::create([
|
||||
'evaluation_run_id' => $runId,
|
||||
'log_qso_id' => $logQso->id,
|
||||
'log_id' => $logId,
|
||||
'ts_utc' => $ts,
|
||||
'band_id' => $bandId,
|
||||
'call_norm' => 'OK1AAA',
|
||||
'rcall_norm' => 'OK1BBB',
|
||||
'loc_norm' => 'JN00AA',
|
||||
'rloc_norm' => 'JN00AA',
|
||||
]);
|
||||
|
||||
return $logQso->id;
|
||||
}
|
||||
|
||||
public function test_pick_best_window_prefers_higher_score(): void
|
||||
{
|
||||
$ruleSet = $this->createRuleSet([
|
||||
'use_multipliers' => false,
|
||||
'multiplier_type' => 'NONE',
|
||||
'multiplier_scope' => 'OVERALL',
|
||||
'multiplier_source' => 'VALID_ONLY',
|
||||
]);
|
||||
$round = $this->createRound();
|
||||
$log = $this->createLog(['round_id' => $round->id]);
|
||||
$run = $this->createEvaluationRun(['rule_set_id' => $ruleSet->id, 'round_id' => $round->id]);
|
||||
$band = $this->createBand();
|
||||
|
||||
$t0 = Carbon::create(2025, 1, 1, 0, 0, 0, 'UTC');
|
||||
$t1 = Carbon::create(2025, 1, 1, 1, 0, 0, 'UTC');
|
||||
$t2 = Carbon::create(2025, 1, 1, 7, 0, 0, 'UTC');
|
||||
$t3 = Carbon::create(2025, 1, 1, 8, 0, 0, 'UTC');
|
||||
|
||||
$firstA = $this->createQso($run->id, $log->id, $band->id, $t0, 10);
|
||||
$firstB = $this->createQso($run->id, $log->id, $band->id, $t1, 10);
|
||||
$bestA = $this->createQso($run->id, $log->id, $band->id, $t2, 25);
|
||||
$bestB = $this->createQso($run->id, $log->id, $band->id, $t3, 25);
|
||||
|
||||
$service = new OperatingWindowService();
|
||||
$result = $service->pickBestOperatingWindow($run->id, $log->id, 6, $ruleSet);
|
||||
|
||||
$this->assertNotNull($result);
|
||||
$this->assertSame($t0->toDateTimeString(), $result['startUtc']->toDateTimeString());
|
||||
$this->assertSame($t1->toDateTimeString(), $result['endUtc']->toDateTimeString());
|
||||
$this->assertSame($t2->toDateTimeString(), $result['secondStartUtc']?->toDateTimeString());
|
||||
$this->assertSame($t3->toDateTimeString(), $result['secondEndUtc']?->toDateTimeString());
|
||||
$this->assertSame([$firstA, $firstB, $bestA, $bestB], $result['includedLogQsoIds']);
|
||||
$this->assertSame(4, $result['qsoCount']);
|
||||
}
|
||||
|
||||
public function test_tie_break_prefers_earlier_start(): void
|
||||
{
|
||||
$ruleSet = $this->createRuleSet([
|
||||
'use_multipliers' => false,
|
||||
'multiplier_type' => 'NONE',
|
||||
'multiplier_scope' => 'OVERALL',
|
||||
'multiplier_source' => 'VALID_ONLY',
|
||||
]);
|
||||
$round = $this->createRound();
|
||||
$log = $this->createLog(['round_id' => $round->id]);
|
||||
$run = $this->createEvaluationRun(['rule_set_id' => $ruleSet->id, 'round_id' => $round->id]);
|
||||
$band = $this->createBand();
|
||||
|
||||
$t0 = Carbon::create(2025, 1, 1, 0, 0, 0, 'UTC');
|
||||
$t1 = Carbon::create(2025, 1, 1, 1, 0, 0, 'UTC');
|
||||
$t2 = Carbon::create(2025, 1, 1, 15, 0, 0, 'UTC');
|
||||
$t3 = Carbon::create(2025, 1, 1, 21, 0, 0, 'UTC');
|
||||
|
||||
$firstA = $this->createQso($run->id, $log->id, $band->id, $t0, 10);
|
||||
$firstB = $this->createQso($run->id, $log->id, $band->id, $t1, 10);
|
||||
$this->createQso($run->id, $log->id, $band->id, $t2, 10);
|
||||
$this->createQso($run->id, $log->id, $band->id, $t3, 10);
|
||||
|
||||
$service = new OperatingWindowService();
|
||||
$result = $service->pickBestOperatingWindow($run->id, $log->id, 6, $ruleSet);
|
||||
|
||||
$this->assertNotNull($result);
|
||||
$this->assertSame($t0->toDateTimeString(), $result['startUtc']->toDateTimeString());
|
||||
$this->assertSame($t1->toDateTimeString(), $result['endUtc']->toDateTimeString());
|
||||
$this->assertNull($result['secondStartUtc']);
|
||||
$this->assertNull($result['secondEndUtc']);
|
||||
$this->assertSame([$firstA, $firstB], $result['includedLogQsoIds']);
|
||||
}
|
||||
|
||||
public function test_window_includes_boundary_at_six_hours(): void
|
||||
{
|
||||
$ruleSet = $this->createRuleSet([
|
||||
'use_multipliers' => false,
|
||||
'multiplier_type' => 'NONE',
|
||||
'multiplier_scope' => 'OVERALL',
|
||||
'multiplier_source' => 'VALID_ONLY',
|
||||
]);
|
||||
$round = $this->createRound();
|
||||
$log = $this->createLog(['round_id' => $round->id]);
|
||||
$run = $this->createEvaluationRun(['rule_set_id' => $ruleSet->id, 'round_id' => $round->id]);
|
||||
$band = $this->createBand();
|
||||
|
||||
$t0 = Carbon::create(2025, 1, 1, 0, 0, 0, 'UTC');
|
||||
$t1 = Carbon::create(2025, 1, 1, 6, 0, 0, 'UTC');
|
||||
|
||||
$this->createQso($run->id, $log->id, $band->id, $t0, 10);
|
||||
$this->createQso($run->id, $log->id, $band->id, $t1, 10);
|
||||
|
||||
$service = new OperatingWindowService();
|
||||
$result = $service->pickBestOperatingWindow($run->id, $log->id, 6, $ruleSet);
|
||||
|
||||
$this->assertNotNull($result);
|
||||
$this->assertSame($t0->toDateTimeString(), $result['startUtc']->toDateTimeString());
|
||||
$this->assertSame($t1->toDateTimeString(), $result['endUtc']->toDateTimeString());
|
||||
$this->assertSame(2, $result['qsoCount']);
|
||||
}
|
||||
|
||||
public function test_returns_null_when_no_valid_qso(): void
|
||||
{
|
||||
$ruleSet = $this->createRuleSet([
|
||||
'use_multipliers' => false,
|
||||
'multiplier_type' => 'NONE',
|
||||
'multiplier_scope' => 'OVERALL',
|
||||
'multiplier_source' => 'VALID_ONLY',
|
||||
]);
|
||||
$round = $this->createRound();
|
||||
$log = $this->createLog(['round_id' => $round->id]);
|
||||
$run = $this->createEvaluationRun(['rule_set_id' => $ruleSet->id, 'round_id' => $round->id]);
|
||||
$band = $this->createBand();
|
||||
|
||||
$t0 = Carbon::create(2025, 1, 1, 0, 0, 0, 'UTC');
|
||||
$this->createQso($run->id, $log->id, $band->id, $t0, 10, false);
|
||||
|
||||
$service = new OperatingWindowService();
|
||||
$result = $service->pickBestOperatingWindow($run->id, $log->id, 6, $ruleSet);
|
||||
|
||||
$this->assertNull($result);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user