Browse Source

models tests

Alexander Musikhin 2 weeks ago
parent
commit
6b42172938

+ 101 - 0
tests/Unit/Helpers/ExcelHelperTest.php

@@ -0,0 +1,101 @@
+<?php
+
+namespace Tests\Unit\Helpers;
+
+use App\Helpers\ExcelHelper;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use PHPUnit\Framework\TestCase;
+
+class ExcelHelperTest extends TestCase
+{
+    public function test_copy_rows_copies_cell_values(): void
+    {
+        $spreadsheet = new Spreadsheet();
+        $sheet = $spreadsheet->getActiveSheet();
+
+        $sheet->setCellValue('A1', 'Hello');
+        $sheet->setCellValue('B1', 'World');
+        $sheet->setCellValue('A2', 'Foo');
+        $sheet->setCellValue('B2', 'Bar');
+
+        ExcelHelper::copyRows($sheet, 'A1:B2', 'C1');
+
+        $this->assertEquals('Hello', $sheet->getCell('C1')->getValue());
+        $this->assertEquals('World', $sheet->getCell('D1')->getValue());
+        $this->assertEquals('Foo', $sheet->getCell('C2')->getValue());
+        $this->assertEquals('Bar', $sheet->getCell('D2')->getValue());
+    }
+
+    public function test_copy_rows_invalid_src_range_does_nothing(): void
+    {
+        $spreadsheet = new Spreadsheet();
+        $sheet = $spreadsheet->getActiveSheet();
+
+        $sheet->setCellValue('A1', 'Test');
+
+        // Should not throw, just return silently
+        ExcelHelper::copyRows($sheet, 'INVALID_RANGE', 'C1');
+
+        $this->assertNull($sheet->getCell('C1')->getValue());
+    }
+
+    public function test_copy_rows_invalid_dst_cell_does_nothing(): void
+    {
+        $spreadsheet = new Spreadsheet();
+        $sheet = $spreadsheet->getActiveSheet();
+
+        $sheet->setCellValue('A1', 'Test');
+
+        // Should not throw, just return silently
+        ExcelHelper::copyRows($sheet, 'A1:B1', 'INVALID');
+
+        $this->assertNull($sheet->getCell('C1')->getValue());
+    }
+
+    public function test_copy_rows_to_different_sheet(): void
+    {
+        $spreadsheet = new Spreadsheet();
+        $srcSheet = $spreadsheet->getActiveSheet();
+        $spreadsheet->createSheet();
+        $dstSheet = $spreadsheet->getSheet(1);
+
+        $srcSheet->setCellValue('A1', 'Source');
+        $srcSheet->setCellValue('B1', 'Data');
+
+        ExcelHelper::copyRows($srcSheet, 'A1:B1', 'A1', $dstSheet);
+
+        $this->assertEquals('Source', $dstSheet->getCell('A1')->getValue());
+        $this->assertEquals('Data', $dstSheet->getCell('B1')->getValue());
+    }
+
+    public function test_copy_style_xf_collection_transfers_styles(): void
+    {
+        $src = new Spreadsheet();
+        $dst = new Spreadsheet();
+
+        $srcSheet = $src->getActiveSheet();
+        $srcSheet->getStyle('A1')->getFont()->setBold(true);
+
+        $srcCount = count($src->getCellXfCollection());
+
+        ExcelHelper::copyStyleXFCollection($src, $dst);
+
+        $dstCount = count($dst->getCellXfCollection());
+
+        $this->assertGreaterThanOrEqual($srcCount, $dstCount);
+    }
+
+    public function test_copy_rows_copies_single_row(): void
+    {
+        $spreadsheet = new Spreadsheet();
+        $sheet = $spreadsheet->getActiveSheet();
+
+        $sheet->setCellValue('A1', 100);
+        $sheet->setCellValue('B1', 200);
+
+        ExcelHelper::copyRows($sheet, 'A1:B1', 'D1');
+
+        $this->assertEquals(100, $sheet->getCell('D1')->getValue());
+        $this->assertEquals(200, $sheet->getCell('E1')->getValue());
+    }
+}

+ 105 - 0
tests/Unit/Helpers/RolesHelperTest.php

@@ -0,0 +1,105 @@
+<?php
+
+namespace Tests\Unit\Helpers;
+
+use App\Models\Role;
+use Illuminate\Foundation\Testing\RefreshDatabase;
+use Tests\TestCase;
+
+class RolesHelperTest extends TestCase
+{
+    use RefreshDatabase;
+
+    protected $seed = true;
+
+    public function test_get_roles_returns_all_roles(): void
+    {
+        $roles = getRoles();
+
+        $this->assertIsArray($roles);
+        $this->assertArrayHasKey(Role::ADMIN, $roles);
+        $this->assertArrayHasKey(Role::MANAGER, $roles);
+        $this->assertArrayHasKey(Role::BRIGADIER, $roles);
+    }
+
+    public function test_get_roles_returns_name_by_key(): void
+    {
+        $this->assertEquals('Админ', getRoles(Role::ADMIN));
+        $this->assertEquals('Менеджер', getRoles(Role::MANAGER));
+        $this->assertEquals('Бригадир', getRoles(Role::BRIGADIER));
+    }
+
+    public function test_get_roles_returns_all_when_key_not_found(): void
+    {
+        $result = getRoles('nonexistent');
+
+        $this->assertIsArray($result);
+        $this->assertCount(3, $result);
+    }
+
+    public function test_has_role_returns_true_for_correct_role(): void
+    {
+        $user = new \App\Models\User();
+        $user->role = Role::ADMIN;
+
+        $this->assertTrue(hasRole(Role::ADMIN, $user));
+    }
+
+    public function test_has_role_returns_false_for_wrong_role(): void
+    {
+        $user = new \App\Models\User();
+        $user->role = Role::MANAGER;
+
+        $this->assertFalse(hasRole(Role::ADMIN, $user));
+    }
+
+    public function test_has_role_supports_multiple_roles(): void
+    {
+        $user = new \App\Models\User();
+        $user->role = Role::BRIGADIER;
+
+        $this->assertTrue(hasRole(Role::MANAGER . ',' . Role::BRIGADIER, $user));
+    }
+
+    public function test_has_role_returns_false_when_user_null(): void
+    {
+        $this->assertFalse(hasRole(Role::ADMIN, null));
+    }
+
+    public function test_role_name_returns_correct_name(): void
+    {
+        $this->assertEquals('Админ', roleName(Role::ADMIN));
+        $this->assertEquals('Менеджер', roleName(Role::MANAGER));
+        $this->assertEquals('Бригадир', roleName(Role::BRIGADIER));
+    }
+
+    public function test_file_name_replaces_special_chars(): void
+    {
+        $result = fileName('file*name:test\\path/to?file<more>');
+
+        $this->assertStringNotContainsString('*', $result);
+        $this->assertStringNotContainsString(':', $result);
+        $this->assertStringNotContainsString('\\', $result);
+        $this->assertStringNotContainsString('/', $result);
+        $this->assertStringNotContainsString('?', $result);
+        $this->assertStringNotContainsString('<', $result);
+        $this->assertStringNotContainsString('>', $result);
+    }
+
+    public function test_file_name_keeps_safe_chars(): void
+    {
+        $result = fileName('normal-file_name.xlsx');
+
+        $this->assertEquals('normal-file_name.xlsx', $result);
+    }
+
+    public function test_year_returns_integer(): void
+    {
+        $this->assertIsInt(year());
+    }
+
+    public function test_year_returns_current_year_by_default(): void
+    {
+        $this->assertEquals((int) date('Y'), year());
+    }
+}

+ 43 - 0
tests/Unit/Models/ContractTest.php

@@ -0,0 +1,43 @@
+<?php
+
+namespace Tests\Unit\Models;
+
+use App\Models\Contract;
+use Illuminate\Foundation\Testing\RefreshDatabase;
+use Tests\TestCase;
+
+class ContractTest extends TestCase
+{
+    use RefreshDatabase;
+
+    protected $seed = true;
+
+    public function test_can_create_contract(): void
+    {
+        $contract = Contract::factory()->create();
+
+        $this->assertNotNull($contract->id);
+        $this->assertDatabaseHas('contracts', ['id' => $contract->id]);
+    }
+
+    public function test_default_sort_by_constant(): void
+    {
+        $this->assertEquals('contract_date', Contract::DEFAULT_SORT_BY);
+    }
+
+    public function test_fillable_fields(): void
+    {
+        $contract = new Contract();
+
+        $this->assertContains('year', $contract->getFillable());
+        $this->assertContains('contract_number', $contract->getFillable());
+        $this->assertContains('contract_date', $contract->getFillable());
+    }
+
+    public function test_for_year_factory_state(): void
+    {
+        $contract = Contract::factory()->forYear(2025)->create();
+
+        $this->assertEquals(2025, $contract->year);
+    }
+}

+ 57 - 0
tests/Unit/Models/Dictionary/AreaTest.php

@@ -0,0 +1,57 @@
+<?php
+
+namespace Tests\Unit\Models\Dictionary;
+
+use App\Models\Dictionary\Area;
+use App\Models\Dictionary\District;
+use App\Models\Responsible;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+use Illuminate\Foundation\Testing\RefreshDatabase;
+use Tests\TestCase;
+
+class AreaTest extends TestCase
+{
+    use RefreshDatabase;
+
+    protected $seed = true;
+
+    public function test_can_create_area(): void
+    {
+        $area = Area::factory()->create();
+
+        $this->assertNotNull($area->id);
+        $this->assertDatabaseHas('areas', ['id' => $area->id]);
+    }
+
+    public function test_belongs_to_district(): void
+    {
+        $area = Area::factory()->create();
+
+        $this->assertInstanceOf(District::class, $area->district);
+    }
+
+    public function test_soft_deletes(): void
+    {
+        $area = Area::factory()->create();
+
+        $area->delete();
+
+        $this->assertTrue($area->trashed());
+    }
+
+    public function test_fillable_fields(): void
+    {
+        $area = new Area();
+
+        $this->assertContains('name', $area->getFillable());
+        $this->assertContains('district_id', $area->getFillable());
+    }
+
+    public function test_relations_exist(): void
+    {
+        $area = Area::factory()->create();
+
+        $this->assertInstanceOf(BelongsTo::class, $area->district());
+        $this->assertInstanceOf(BelongsTo::class, $area->responsible());
+    }
+}

+ 57 - 0
tests/Unit/Models/Dictionary/DistrictTest.php

@@ -0,0 +1,57 @@
+<?php
+
+namespace Tests\Unit\Models\Dictionary;
+
+use App\Models\Dictionary\Area;
+use App\Models\Dictionary\District;
+use Illuminate\Database\Eloquent\Relations\HasMany;
+use Illuminate\Foundation\Testing\RefreshDatabase;
+use Tests\TestCase;
+
+class DistrictTest extends TestCase
+{
+    use RefreshDatabase;
+
+    protected $seed = true;
+
+    public function test_can_create_district(): void
+    {
+        $district = District::factory()->create();
+
+        $this->assertNotNull($district->id);
+        $this->assertDatabaseHas('districts', ['id' => $district->id]);
+    }
+
+    public function test_has_many_areas(): void
+    {
+        $district = District::factory()->create();
+
+        Area::factory()->for($district)->count(3)->create();
+
+        $this->assertEquals(3, $district->areas()->count());
+    }
+
+    public function test_soft_deletes(): void
+    {
+        $district = District::factory()->create();
+
+        $district->delete();
+
+        $this->assertTrue($district->trashed());
+    }
+
+    public function test_fillable_fields(): void
+    {
+        $district = new District();
+
+        $this->assertContains('name', $district->getFillable());
+        $this->assertContains('shortname', $district->getFillable());
+    }
+
+    public function test_relations_exist(): void
+    {
+        $district = District::factory()->create();
+
+        $this->assertInstanceOf(HasMany::class, $district->areas());
+    }
+}

+ 66 - 0
tests/Unit/Models/MafOrderTest.php

@@ -0,0 +1,66 @@
+<?php
+
+namespace Tests\Unit\Models;
+
+use App\Models\MafOrder;
+use App\Models\Product;
+use App\Models\ProductSKU;
+use App\Models\User;
+use Illuminate\Foundation\Testing\RefreshDatabase;
+use Tests\TestCase;
+
+class MafOrderTest extends TestCase
+{
+    use RefreshDatabase;
+
+    protected $seed = true;
+
+    public function test_belongs_to_user(): void
+    {
+        $mafOrder = MafOrder::factory()->create();
+
+        $this->assertInstanceOf(User::class, $mafOrder->user);
+    }
+
+    public function test_belongs_to_product(): void
+    {
+        $mafOrder = MafOrder::factory()->create();
+
+        $this->assertInstanceOf(Product::class, $mafOrder->product);
+    }
+
+    public function test_has_many_products_sku(): void
+    {
+        $mafOrder = MafOrder::factory()->create();
+
+        ProductSKU::factory()
+            ->withMafOrder($mafOrder)
+            ->count(3)
+            ->create();
+
+        $this->assertEquals(3, $mafOrder->products_sku()->withoutGlobalScopes()->count());
+    }
+
+    public function test_soft_deletes(): void
+    {
+        $mafOrder = MafOrder::factory()->create();
+
+        $mafOrder->delete();
+
+        $this->assertTrue($mafOrder->trashed());
+    }
+
+    public function test_year_set_on_create(): void
+    {
+        $mafOrder = MafOrder::factory()->create();
+
+        $this->assertEquals((int) date('Y'), $mafOrder->year);
+    }
+
+    public function test_fully_in_stock_state(): void
+    {
+        $mafOrder = MafOrder::factory()->fullyInStock()->create();
+
+        $this->assertEquals($mafOrder->quantity, $mafOrder->in_stock);
+    }
+}

+ 70 - 0
tests/Unit/Models/ProductSKUTest.php

@@ -0,0 +1,70 @@
+<?php
+
+namespace Tests\Unit\Models;
+
+use App\Models\File;
+use App\Models\MafOrder;
+use App\Models\Order;
+use App\Models\Product;
+use App\Models\ProductSKU;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+use Illuminate\Foundation\Testing\RefreshDatabase;
+use Tests\TestCase;
+
+class ProductSKUTest extends TestCase
+{
+    use RefreshDatabase;
+
+    protected $seed = true;
+
+    public function test_belongs_to_product(): void
+    {
+        $sku = ProductSKU::factory()->create();
+
+        $this->assertInstanceOf(Product::class, $sku->product);
+    }
+
+    public function test_belongs_to_order(): void
+    {
+        $order = Order::factory()->create();
+        $sku = ProductSKU::factory()->forOrder($order)->create();
+
+        $this->assertNotNull($sku->order_id);
+        $this->assertInstanceOf(Order::class, $sku->order);
+    }
+
+    public function test_belongs_to_maf_order(): void
+    {
+        $mafOrder = MafOrder::factory()->create();
+        $sku = ProductSKU::factory()->withMafOrder($mafOrder)->create();
+
+        $this->assertNotNull($sku->maf_order_id);
+        $this->assertInstanceOf(MafOrder::class, $sku->maf_order);
+    }
+
+    public function test_soft_deletes(): void
+    {
+        $sku = ProductSKU::factory()->create();
+
+        $sku->delete();
+
+        $this->assertTrue($sku->trashed());
+    }
+
+    public function test_year_set_on_create(): void
+    {
+        $sku = ProductSKU::factory()->create();
+
+        $this->assertEquals((int) date('Y'), $sku->year);
+    }
+
+    public function test_relations_exist(): void
+    {
+        $sku = ProductSKU::factory()->create();
+
+        $this->assertInstanceOf(BelongsTo::class, $sku->product());
+        $this->assertInstanceOf(BelongsTo::class, $sku->order());
+        $this->assertInstanceOf(BelongsTo::class, $sku->passport());
+        $this->assertInstanceOf(BelongsTo::class, $sku->maf_order());
+    }
+}

+ 97 - 0
tests/Unit/Models/ProductTest.php

@@ -0,0 +1,97 @@
+<?php
+
+namespace Tests\Unit\Models;
+
+use App\Models\File;
+use App\Models\MafOrder;
+use App\Models\Product;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+use Illuminate\Database\Eloquent\Relations\BelongsToMany;
+use Illuminate\Database\Eloquent\Relations\HasMany;
+use Illuminate\Foundation\Testing\RefreshDatabase;
+use Tests\TestCase;
+
+class ProductTest extends TestCase
+{
+    use RefreshDatabase;
+
+    protected $seed = true;
+
+    public function test_product_price_stored_as_kopeks_returned_as_rubles(): void
+    {
+        $product = Product::factory()->create([
+            'product_price' => 150.50,
+        ]);
+
+        $product->refresh();
+
+        $this->assertEquals(150.50, $product->product_price);
+    }
+
+    public function test_installation_price_accessor(): void
+    {
+        $product = Product::factory()->create([
+            'installation_price' => 200.00,
+        ]);
+
+        $product->refresh();
+
+        $this->assertEquals(200.00, $product->installation_price);
+    }
+
+    public function test_total_price_accessor(): void
+    {
+        $product = Product::factory()->create([
+            'total_price' => 300.00,
+        ]);
+
+        $product->refresh();
+
+        $this->assertEquals(300.00, $product->total_price);
+    }
+
+    public function test_price_txt_contains_ruble_sign(): void
+    {
+        $product = Product::factory()->create([
+            'product_price' => 500.00,
+        ]);
+
+        $product->refresh();
+
+        $this->assertStringContainsString('₽', $product->product_price_txt);
+    }
+
+    public function test_soft_deletes(): void
+    {
+        $product = Product::factory()->create();
+
+        $product->delete();
+
+        $this->assertTrue($product->trashed());
+    }
+
+    public function test_has_relations_false_when_no_relations(): void
+    {
+        $product = Product::factory()->create();
+
+        $this->assertFalse($product->hasRelations());
+    }
+
+    public function test_has_many_maf_orders(): void
+    {
+        $product = Product::factory()->create();
+
+        MafOrder::factory()->forProduct($product)->create();
+
+        $this->assertGreaterThan(0, $product->maf_orders()->withoutGlobalScopes()->count());
+    }
+
+    public function test_relations_exist(): void
+    {
+        $product = Product::factory()->create();
+
+        $this->assertInstanceOf(BelongsToMany::class, $product->orders());
+        $this->assertInstanceOf(BelongsTo::class, $product->certificate());
+        $this->assertInstanceOf(HasMany::class, $product->maf_orders());
+    }
+}

+ 97 - 0
tests/Unit/Models/ReservationTest.php

@@ -0,0 +1,97 @@
+<?php
+
+namespace Tests\Unit\Models;
+
+use App\Models\Reclamation;
+use App\Models\Reservation;
+use App\Models\SparePart;
+use App\Models\SparePartOrder;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+use Illuminate\Foundation\Testing\RefreshDatabase;
+use Tests\TestCase;
+
+class ReservationTest extends TestCase
+{
+    use RefreshDatabase;
+
+    protected $seed = true;
+
+    public function test_status_constants_defined(): void
+    {
+        $this->assertEquals('active', Reservation::STATUS_ACTIVE);
+        $this->assertEquals('issued', Reservation::STATUS_ISSUED);
+        $this->assertEquals('cancelled', Reservation::STATUS_CANCELLED);
+    }
+
+    public function test_is_active_returns_true_for_active(): void
+    {
+        $reservation = Reservation::factory()->active()->create();
+
+        $this->assertTrue($reservation->isActive());
+    }
+
+    public function test_is_issued_returns_true_for_issued(): void
+    {
+        $reservation = Reservation::factory()->issued()->create();
+
+        $this->assertTrue($reservation->isIssued());
+    }
+
+    public function test_is_cancelled_returns_true_for_cancelled(): void
+    {
+        $reservation = Reservation::factory()->cancelled()->create();
+
+        $this->assertTrue($reservation->isCancelled());
+    }
+
+    public function test_scope_active_filters_correctly(): void
+    {
+        $sparePart = SparePart::factory()->create();
+        $order = SparePartOrder::factory()->inStock()->forSparePart($sparePart)->create();
+
+        Reservation::factory()->active()->forSparePart($sparePart)->fromOrder($order)->create();
+        Reservation::factory()->cancelled()->forSparePart($sparePart)->fromOrder($order)->create();
+
+        $activeCount = Reservation::active()->forSparePart($sparePart->id)->count();
+
+        $this->assertEquals(1, $activeCount);
+    }
+
+    public function test_scope_for_spare_part_filters_correctly(): void
+    {
+        $sparePart1 = SparePart::factory()->create();
+        $sparePart2 = SparePart::factory()->create();
+
+        $order1 = SparePartOrder::factory()->inStock()->forSparePart($sparePart1)->create();
+        $order2 = SparePartOrder::factory()->inStock()->forSparePart($sparePart2)->create();
+
+        Reservation::factory()->forSparePart($sparePart1)->fromOrder($order1)->count(2)->create();
+        Reservation::factory()->forSparePart($sparePart2)->fromOrder($order2)->create();
+
+        $count = Reservation::forSparePart($sparePart1->id)->count();
+
+        $this->assertEquals(2, $count);
+    }
+
+    public function test_scope_for_reclamation_filters_correctly(): void
+    {
+        $reclamation1 = Reclamation::factory()->create();
+        $reclamation2 = Reclamation::factory()->create();
+
+        Reservation::factory()->forReclamation($reclamation1)->count(3)->create();
+        Reservation::factory()->forReclamation($reclamation2)->create();
+
+        $count = Reservation::forReclamation($reclamation1->id)->count();
+
+        $this->assertEquals(3, $count);
+    }
+
+    public function test_relations_exist(): void
+    {
+        $reservation = Reservation::factory()->create();
+
+        $this->assertInstanceOf(BelongsTo::class, $reservation->sparePart());
+        $this->assertInstanceOf(BelongsTo::class, $reservation->sparePartOrder());
+        $this->assertInstanceOf(BelongsTo::class, $reservation->reclamation());
+    }
+}

+ 64 - 0
tests/Unit/Models/ScheduleTest.php

@@ -0,0 +1,64 @@
+<?php
+
+namespace Tests\Unit\Models;
+
+use App\Models\Dictionary\Area;
+use App\Models\Dictionary\District;
+use App\Models\Schedule;
+use App\Models\User;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+use Illuminate\Foundation\Testing\RefreshDatabase;
+use Tests\TestCase;
+
+class ScheduleTest extends TestCase
+{
+    use RefreshDatabase;
+
+    protected $seed = true;
+
+    private function makeSchedule(array $attrs = []): Schedule
+    {
+        $brigadier = User::factory()->create();
+        return Schedule::factory()->create(array_merge(['brigadier_id' => $brigadier->id], $attrs));
+    }
+
+    public function test_belongs_to_district(): void
+    {
+        $schedule = $this->makeSchedule();
+
+        $this->assertInstanceOf(District::class, $schedule->district);
+    }
+
+    public function test_belongs_to_area(): void
+    {
+        $schedule = $this->makeSchedule();
+
+        $this->assertInstanceOf(Area::class, $schedule->area);
+    }
+
+    public function test_brigadier_belongs_to_user(): void
+    {
+        $brigadier = User::factory()->create();
+        $schedule = Schedule::factory()->create(['brigadier_id' => $brigadier->id]);
+
+        $this->assertInstanceOf(User::class, $schedule->brigadier);
+        $this->assertEquals($brigadier->id, $schedule->brigadier->id);
+    }
+
+    public function test_relations_exist(): void
+    {
+        $schedule = $this->makeSchedule();
+
+        $this->assertInstanceOf(BelongsTo::class, $schedule->district());
+        $this->assertInstanceOf(BelongsTo::class, $schedule->area());
+        $this->assertInstanceOf(BelongsTo::class, $schedule->brigadier());
+    }
+
+    public function test_can_create_schedule(): void
+    {
+        $schedule = $this->makeSchedule();
+
+        $this->assertNotNull($schedule->id);
+        $this->assertDatabaseHas('schedules', ['id' => $schedule->id]);
+    }
+}

+ 113 - 0
tests/Unit/Models/SparePartOrderShipmentTest.php

@@ -0,0 +1,113 @@
+<?php
+
+namespace Tests\Unit\Models;
+
+use App\Models\Reclamation;
+use App\Models\SparePartOrder;
+use App\Models\SparePartOrderShipment;
+use App\Models\User;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+use Illuminate\Foundation\Testing\RefreshDatabase;
+use Illuminate\Support\Facades\DB;
+use Tests\TestCase;
+
+class SparePartOrderShipmentTest extends TestCase
+{
+    use RefreshDatabase;
+
+    protected $seed = true;
+
+    public function test_can_create_shipment(): void
+    {
+        $sparePartOrder = SparePartOrder::factory()->inStock()->create();
+
+        $id = DB::table('spare_part_order_shipments')->insertGetId([
+            'spare_part_order_id' => $sparePartOrder->id,
+            'quantity' => 5,
+            'note' => 'Test shipment',
+            'reclamation_id' => null,
+            'user_id' => null,
+            'is_automatic' => false,
+            'created_at' => now(),
+            'updated_at' => now(),
+        ]);
+
+        $shipment = SparePartOrderShipment::find($id);
+
+        $this->assertNotNull($shipment);
+        $this->assertEquals($sparePartOrder->id, $shipment->spare_part_order_id);
+    }
+
+    public function test_casts_is_automatic_to_boolean(): void
+    {
+        $sparePartOrder = SparePartOrder::factory()->inStock()->create();
+
+        $id = DB::table('spare_part_order_shipments')->insertGetId([
+            'spare_part_order_id' => $sparePartOrder->id,
+            'quantity' => 3,
+            'is_automatic' => true,
+            'created_at' => now(),
+            'updated_at' => now(),
+        ]);
+
+        $shipment = SparePartOrderShipment::find($id);
+
+        $this->assertIsBool($shipment->is_automatic);
+        $this->assertTrue($shipment->is_automatic);
+    }
+
+    public function test_casts_quantity_to_integer(): void
+    {
+        $sparePartOrder = SparePartOrder::factory()->inStock()->create();
+
+        $id = DB::table('spare_part_order_shipments')->insertGetId([
+            'spare_part_order_id' => $sparePartOrder->id,
+            'quantity' => 7,
+            'is_automatic' => false,
+            'created_at' => now(),
+            'updated_at' => now(),
+        ]);
+
+        $shipment = SparePartOrderShipment::find($id);
+
+        $this->assertIsInt($shipment->quantity);
+        $this->assertEquals(7, $shipment->quantity);
+    }
+
+    public function test_relations_exist(): void
+    {
+        $sparePartOrder = SparePartOrder::factory()->inStock()->create();
+
+        $id = DB::table('spare_part_order_shipments')->insertGetId([
+            'spare_part_order_id' => $sparePartOrder->id,
+            'quantity' => 1,
+            'is_automatic' => false,
+            'created_at' => now(),
+            'updated_at' => now(),
+        ]);
+
+        $shipment = SparePartOrderShipment::find($id);
+
+        $this->assertInstanceOf(BelongsTo::class, $shipment->sparePartOrder());
+        $this->assertInstanceOf(BelongsTo::class, $shipment->reclamation());
+        $this->assertInstanceOf(BelongsTo::class, $shipment->user());
+    }
+
+    public function test_spare_part_order_relation_returns_correct_model(): void
+    {
+        $sparePartOrder = SparePartOrder::factory()->inStock()->create();
+
+        $id = DB::table('spare_part_order_shipments')->insertGetId([
+            'spare_part_order_id' => $sparePartOrder->id,
+            'quantity' => 2,
+            'is_automatic' => false,
+            'created_at' => now(),
+            'updated_at' => now(),
+        ]);
+
+        $shipment = SparePartOrderShipment::find($id);
+
+        $this->assertInstanceOf(SparePartOrder::class, $shipment->sparePartOrder);
+        $this->assertEquals($sparePartOrder->id, $shipment->sparePartOrder->id);
+    }
+}