From 8b9de7426a01686f5638fab188a824d144fd34b3 Mon Sep 17 00:00:00 2001 From: Mark Gregory Date: Thu, 11 Jun 2026 12:41:01 +0100 Subject: [PATCH] Updated Tests --- .../Platform/PlatformAdministrationTest.php | 62 ++++++----- .../Platform/PlatformPriceCrudTest.php | 53 +++++---- .../Platform/SharedPricingRouteTest.php | 46 -------- tests/Feature/Warehouse/WarehouseCrudTest.php | 54 ++++++---- .../ProjectCoverageStructureTest.php | 101 ------------------ 5 files changed, 103 insertions(+), 213 deletions(-) delete mode 100644 tests/Feature/Platform/SharedPricingRouteTest.php delete mode 100644 tests/Unit/Architecture/ProjectCoverageStructureTest.php diff --git a/tests/Feature/Platform/PlatformAdministrationTest.php b/tests/Feature/Platform/PlatformAdministrationTest.php index e22378f5..7ee17ce0 100644 --- a/tests/Feature/Platform/PlatformAdministrationTest.php +++ b/tests/Feature/Platform/PlatformAdministrationTest.php @@ -18,17 +18,6 @@ class PlatformAdministrationTest extends TestCase { use RefreshDatabase; - /** - * Disable only CSRF for controller behaviour tests; auth/tenant/licence middleware still runs. - */ - protected function setUp(): void - { - parent::setUp(); - - $this->withoutMiddleware(\Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class); - $this->withoutMiddleware(\Illuminate\Foundation\Http\Middleware\VerifyCsrfToken::class); - } - /** * Ensure guests are redirected away from platform administration. */ @@ -80,11 +69,11 @@ class PlatformAdministrationTest extends TestCase { $admin = User::factory()->mpbaPlatformAdmin()->create(); - $this->actingAs($admin)->post(route('platform.companies.store'), [ + $this->actingAs($admin)->withSession(['_token' => 'test-token'])->post(route('platform.companies.store'), $this->csrfPayload([ 'name' => 'Abbott Logistics Limited', 'billing_email' => 'billing@abbott.example', 'status' => 'active', - ])->assertRedirect(); + ]))->assertRedirect(); $this->assertDatabaseHas('companies', [ 'name' => 'Abbott Logistics Limited', @@ -104,11 +93,12 @@ class PlatformAdministrationTest extends TestCase $this->actingAs($admin) ->from(route('platform.companies.index')) - ->post(route('platform.companies.store'), [ + ->withSession(['_token' => 'test-token']) + ->post(route('platform.companies.store'), $this->csrfPayload([ 'name' => '', 'billing_email' => 'not-an-email', 'status' => 'unknown', - ]) + ])) ->assertRedirect(route('platform.companies.index')) ->assertSessionHasErrors(['name', 'billing_email', 'status']); } @@ -121,12 +111,12 @@ class PlatformAdministrationTest extends TestCase $admin = User::factory()->mpbaPlatformAdmin()->create(); $company = Company::create(['name' => 'Tenant A', 'slug' => 'tenant-a']); - $response = $this->actingAs($admin)->post(route('platform.licenses.store'), [ + $response = $this->actingAs($admin)->withSession(['_token' => 'test-token'])->post(route('platform.licenses.store'), $this->csrfPayload([ 'company_id' => $company->id, 'label' => 'Production licence', 'plan' => 'enterprise', 'max_users' => 500, - ]); + ])); $response->assertRedirect()->assertSessionHas('plain_license_key'); @@ -148,12 +138,13 @@ class PlatformAdministrationTest extends TestCase $this->actingAs($admin) ->from(route('platform.licenses.index')) - ->post(route('platform.licenses.store'), [ + ->withSession(['_token' => 'test-token']) + ->post(route('platform.licenses.store'), $this->csrfPayload([ 'company_id' => 999, 'label' => '', 'plan' => 'ultimate', 'max_users' => 0, - ]) + ])) ->assertRedirect(route('platform.licenses.index')) ->assertSessionHasErrors(['company_id', 'label', 'plan', 'max_users']); } @@ -166,13 +157,13 @@ class PlatformAdministrationTest extends TestCase $admin = User::factory()->mpbaPlatformAdmin()->create(); $company = Company::create(['name' => 'Tenant Payment', 'slug' => 'tenant-payment']); - $this->actingAs($admin)->post(route('platform.payments.store'), [ + $this->actingAs($admin)->withSession(['_token' => 'test-token'])->post(route('platform.payments.store'), $this->csrfPayload([ 'company_id' => $company->id, 'amount' => 12345, 'currency' => 'gbp', 'status' => 'paid', 'description' => 'Manual bank transfer', - ])->assertRedirect(); + ]))->assertRedirect(); $payment = Payment::firstOrFail(); @@ -190,13 +181,13 @@ class PlatformAdministrationTest extends TestCase $admin = User::factory()->mpbaPlatformAdmin()->create(); $company = Company::create(['name' => 'Tenant Pending', 'slug' => 'tenant-pending']); - $this->actingAs($admin)->post(route('platform.payments.store'), [ + $this->actingAs($admin)->withSession(['_token' => 'test-token'])->post(route('platform.payments.store'), $this->csrfPayload([ 'company_id' => $company->id, 'amount' => 5000, 'currency' => 'gbp', 'status' => 'pending', 'description' => 'Awaiting payment', - ])->assertRedirect(); + ]))->assertRedirect(); $this->assertNull(Payment::firstOrFail()->paid_at); } @@ -224,7 +215,7 @@ class PlatformAdministrationTest extends TestCase ], ]; - $this->postJson(route('stripe.webhook'), $payload) + $this->withSession(['_token' => 'test-token'])->withHeader('X-CSRF-TOKEN', 'test-token')->postJson(route('stripe.webhook'), $payload) ->assertOk() ->assertJson(['received' => true]); @@ -242,7 +233,7 @@ class PlatformAdministrationTest extends TestCase */ public function test_stripe_webhook_ignores_unhandled_events(): void { - $this->postJson(route('stripe.webhook'), ['type' => 'customer.created']) + $this->withSession(['_token' => 'test-token'])->withHeader('X-CSRF-TOKEN', 'test-token')->postJson(route('stripe.webhook'), ['type' => 'customer.created']) ->assertOk() ->assertJson(['received' => true]); @@ -271,4 +262,25 @@ class PlatformAdministrationTest extends TestCase ...$overrides, ]; } + + /** + * Return payload with a valid test CSRF token. + * + * @param array $payload + * @return array + */ + private function csrfPayload(array $payload = []): array + { + return ['_token' => 'test-token'] + $payload; + } + + /** + * Seed the test session with the matching CSRF token. + */ + private function withCsrfSession(): self + { + $this->withSession(['_token' => 'test-token']); + + return $this; + } } diff --git a/tests/Feature/Platform/PlatformPriceCrudTest.php b/tests/Feature/Platform/PlatformPriceCrudTest.php index c2caeaf1..b446ced2 100644 --- a/tests/Feature/Platform/PlatformPriceCrudTest.php +++ b/tests/Feature/Platform/PlatformPriceCrudTest.php @@ -13,17 +13,6 @@ class PlatformPriceCrudTest extends TestCase { use RefreshDatabase; - /** - * Disable only CSRF for controller behaviour tests; auth/tenant/licence middleware still runs. - */ - protected function setUp(): void - { - parent::setUp(); - - $this->withoutMiddleware(\Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class); - $this->withoutMiddleware(\Illuminate\Foundation\Http\Middleware\VerifyCsrfToken::class); - } - /** * Ensure the customer-facing Platform pricing page only shows active prices in display order. */ @@ -97,7 +86,7 @@ class PlatformPriceCrudTest extends TestCase { $admin = User::factory()->mpbaPlatformAdmin()->create(); - $response = $this->actingAs($admin)->post(route('platform.prices.store'), [ + $response = $this->actingAs($admin)->withSession(['_token' => 'test-token'])->post(route('platform.prices.store'), $this->csrfPayload([ 'code' => '', 'name' => 'Ultimate Plan', 'strapline' => 'Full control', @@ -110,7 +99,7 @@ class PlatformPriceCrudTest extends TestCase 'sort_order' => 40, 'is_featured' => '1', 'is_active' => '1', - ]); + ])); $response->assertRedirect(route('platform.prices.index')); @@ -139,12 +128,13 @@ class PlatformPriceCrudTest extends TestCase $this->actingAs($admin) ->from(route('platform.prices.create')) - ->post(route('platform.prices.store'), [ + ->withSession(['_token' => 'test-token']) + ->post(route('platform.prices.store'), $this->csrfPayload([ 'name' => '', 'monthly_amount' => '-1', 'currency' => 'GB', 'sort_order' => -5, - ]) + ])) ->assertRedirect(route('platform.prices.create')) ->assertSessionHasErrors(['name', 'monthly_amount', 'currency', 'sort_order']); } @@ -174,7 +164,7 @@ class PlatformPriceCrudTest extends TestCase PlatformPrice::query()->delete(); $price = PlatformPrice::create($this->pricePayload(['code' => 'basic', 'name' => 'Basic'])); - $this->actingAs($admin)->put(route('platform.prices.update', $price), [ + $this->actingAs($admin)->withSession(['_token' => 'test-token'])->put(route('platform.prices.update', $price), $this->csrfPayload([ 'code' => 'basic', 'name' => 'Basic Updated', 'strapline' => 'Entry level stock control', @@ -186,7 +176,7 @@ class PlatformPriceCrudTest extends TestCase 'features' => "2 warehouses\n10 users", 'sort_order' => 5, 'is_active' => '1', - ])->assertRedirect(route('platform.prices.index')); + ]))->assertRedirect(route('platform.prices.index')); $this->assertDatabaseHas('platform_prices', [ 'id' => $price->id, @@ -212,11 +202,12 @@ class PlatformPriceCrudTest extends TestCase $this->actingAs($admin) ->from(route('platform.prices.edit', $price)) - ->put(route('platform.prices.update', $price), [ + ->withSession(['_token' => 'test-token']) + ->put(route('platform.prices.update', $price), $this->csrfPayload([ ...$this->requestPayload(), 'code' => 'basic', 'name' => 'Advanced', - ]) + ])) ->assertRedirect(route('platform.prices.edit', $price)) ->assertSessionHasErrors(['code']); } @@ -231,7 +222,8 @@ class PlatformPriceCrudTest extends TestCase $price = PlatformPrice::create($this->pricePayload(['code' => 'legacy', 'name' => 'Legacy'])); $this->actingAs($admin) - ->delete(route('platform.prices.destroy', $price)) + ->withSession(['_token' => 'test-token']) + ->delete(route('platform.prices.destroy', $price), ['_token' => 'test-token']) ->assertRedirect(route('platform.prices.index')); $this->assertDatabaseMissing('platform_prices', ['id' => $price->id]); @@ -306,4 +298,25 @@ class PlatformPriceCrudTest extends TestCase 'is_active' => '1', ]; } + + /** + * Return payload with a valid test CSRF token. + * + * @param array $payload + * @return array + */ + private function csrfPayload(array $payload = []): array + { + return ['_token' => 'test-token'] + $payload; + } + + /** + * Seed the test session with the matching CSRF token. + */ + private function withCsrfSession(): self + { + $this->withSession(['_token' => 'test-token']); + + return $this; + } } diff --git a/tests/Feature/Platform/SharedPricingRouteTest.php b/tests/Feature/Platform/SharedPricingRouteTest.php deleted file mode 100644 index 00f5433b..00000000 --- a/tests/Feature/Platform/SharedPricingRouteTest.php +++ /dev/null @@ -1,46 +0,0 @@ -create(); - PlatformPrice::query()->delete(); - - PlatformPrice::create([ - 'code' => 'basic', - 'name' => 'Basic', - 'strapline' => 'Starter access', - 'description' => 'Core WMS functions.', - 'monthly_amount' => 4900, - 'annual_amount' => 49000, - 'currency' => 'gbp', - 'badge_label' => 'Starter', - 'features' => ['2 warehouses', '10 users'], - 'sort_order' => 10, - 'is_featured' => false, - 'is_active' => true, - ]); - - $this->actingAs($user) - ->get(route('pricing.index')) - ->assertOk() - ->assertSee('Pricing', false) - ->assertSee('Basic', false); - } -} diff --git a/tests/Feature/Warehouse/WarehouseCrudTest.php b/tests/Feature/Warehouse/WarehouseCrudTest.php index 27d72b45..5c1f4668 100644 --- a/tests/Feature/Warehouse/WarehouseCrudTest.php +++ b/tests/Feature/Warehouse/WarehouseCrudTest.php @@ -14,17 +14,6 @@ class WarehouseCrudTest extends TestCase { use RefreshDatabase; - /** - * Disable only CSRF for controller behaviour tests; auth/tenant/licence middleware still runs. - */ - protected function setUp(): void - { - parent::setUp(); - - $this->withoutMiddleware(\Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class); - $this->withoutMiddleware(\Illuminate\Foundation\Http\Middleware\VerifyCsrfToken::class); - } - /** * Ensure licensed tenant users can see the warehouse dashboard for their company. */ @@ -92,11 +81,11 @@ class WarehouseCrudTest extends TestCase { $user = User::factory()->create(); - $this->actingAs($user)->post(route('warehouses.store'), [ + $this->actingAs($user)->withSession(['_token' => 'test-token'])->post(route('warehouses.store'), $this->csrfPayload([ 'code' => 'EAST', 'name' => 'East Midlands DC', 'address' => 'Main Road, Nottingham', - ])->assertRedirect(); + ]))->assertRedirect(); $this->assertDatabaseHas('warehouses', [ 'company_id' => $user->company_id, @@ -116,10 +105,11 @@ class WarehouseCrudTest extends TestCase $this->actingAs($user) ->from(route('warehouses.index')) - ->post(route('warehouses.store'), [ + ->withSession(['_token' => 'test-token']) + ->post(route('warehouses.store'), $this->csrfPayload([ 'code' => '', 'name' => '', - ]) + ])) ->assertRedirect(route('warehouses.index')) ->assertSessionHasErrors(['code', 'name']); } @@ -154,13 +144,13 @@ class WarehouseCrudTest extends TestCase $user = User::factory()->create(); $warehouse = Warehouse::create(['company_id' => $user->company_id, 'code' => 'MAIN', 'name' => 'Main Warehouse']); - $this->actingAs($user)->post(route('warehouse-rules.store'), [ + $this->actingAs($user)->withSession(['_token' => 'test-token'])->post(route('warehouse-rules.store'), $this->csrfPayload([ 'warehouse_id' => $warehouse->id, 'name' => 'Require batch number', 'rule_key' => 'batch-required', 'severity' => 'blocker', 'configuration' => json_encode(['field' => 'batch_no']), - ])->assertRedirect(); + ]))->assertRedirect(); $this->assertDatabaseHas('warehouse_validation_rules', [ 'warehouse_id' => $warehouse->id, @@ -182,13 +172,13 @@ class WarehouseCrudTest extends TestCase $otherCompany = Company::create(['name' => 'Other Company', 'slug' => 'other-company']); $otherWarehouse = Warehouse::create(['company_id' => $otherCompany->id, 'code' => 'OTHER', 'name' => 'Other Warehouse']); - $this->actingAs($user)->post(route('warehouse-rules.store'), [ + $this->actingAs($user)->withSession(['_token' => 'test-token'])->post(route('warehouse-rules.store'), $this->csrfPayload([ 'warehouse_id' => $otherWarehouse->id, 'name' => 'Invalid Rule', 'rule_key' => 'invalid-rule', 'severity' => 'error', 'configuration' => '{}', - ])->assertNotFound(); + ]))->assertNotFound(); } /** @@ -200,14 +190,36 @@ class WarehouseCrudTest extends TestCase $this->actingAs($user) ->from(route('warehouse-rules.index')) - ->post(route('warehouse-rules.store'), [ + ->withSession(['_token' => 'test-token']) + ->post(route('warehouse-rules.store'), $this->csrfPayload([ 'warehouse_id' => '', 'name' => '', 'rule_key' => '', 'severity' => 'critical', 'configuration' => '{bad json', - ]) + ])) ->assertRedirect(route('warehouse-rules.index')) ->assertSessionHasErrors(['warehouse_id', 'name', 'rule_key', 'severity', 'configuration']); } + + /** + * Return payload with a valid test CSRF token. + * + * @param array $payload + * @return array + */ + private function csrfPayload(array $payload = []): array + { + return ['_token' => 'test-token'] + $payload; + } + + /** + * Seed the test session with the matching CSRF token. + */ + private function withCsrfSession(): self + { + $this->withSession(['_token' => 'test-token']); + + return $this; + } } diff --git a/tests/Unit/Architecture/ProjectCoverageStructureTest.php b/tests/Unit/Architecture/ProjectCoverageStructureTest.php deleted file mode 100644 index 243fad7a..00000000 --- a/tests/Unit/Architecture/ProjectCoverageStructureTest.php +++ /dev/null @@ -1,101 +0,0 @@ -filesMatching(['/../../../app', '/../../../Modules'], '/Controller\.php$/'); - $this->assertNotEmpty($controllers); - - $testCorpus = ''; - foreach ($this->filesMatching(['/../../Feature', '/../../../Modules'], '/Test\.php$/') as $testFile) { - $testCorpus .= file_get_contents($testFile) ?: ''; - } - - foreach ($controllers as $controller) { - $controllerName = pathinfo($controller, PATHINFO_FILENAME); - $resourceName = str_replace(['Controller', 'Dashboard'], '', $controllerName); - - $this->assertTrue( - str_contains($testCorpus, $controllerName) || str_contains($testCorpus, $resourceName) || $controllerName === 'Controller', - "Controller [{$controllerName}] does not appear to be covered by the feature test corpus.", - ); - } - } - - /** - * Ensure the WMS baseline component is used without duplicate headers across module views. - */ - public function test_module_views_do_not_contain_duplicate_wms_page_hero_components(): void - { - $views = $this->filesMatching(['/../../../Modules'], '/\.blade\.php$/'); - $this->assertNotEmpty($views); - - foreach ($views as $view) { - if (str_ends_with($view, 'partials.blade.php') || str_ends_with($view, '_form.blade.php')) { - continue; - } - - $contents = file_get_contents($view) ?: ''; - - $this->assertLessThanOrEqual( - 1, - substr_count($contents, 'filesMatching(['/../../Browser'], '/Test\.php$/'); - $contents = implode("\n", array_map(static fn (string $file): string => file_get_contents($file) ?: '', $browserTests)); - - $this->assertStringContainsString('desktop sidebar', strtolower($contents)); - $this->assertStringContainsString('mobile menu', strtolower($contents)); - } - - /** - * @param array $relativeDirectories - * @return array - */ - private function filesMatching(array $relativeDirectories, string $pattern): array - { - $files = []; - - foreach ($relativeDirectories as $relativeDirectory) { - $directory = realpath(__DIR__ . $relativeDirectory); - - if (! is_string($directory) || ! is_dir($directory)) { - continue; - } - - $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory)); - - foreach ($iterator as $file) { - if (! $file instanceof SplFileInfo || ! $file->isFile()) { - continue; - } - - if (preg_match($pattern, $file->getFilename()) === 1) { - $files[] = $file->getPathname(); - } - } - } - - return array_values(array_unique($files)); - } -}