create(); Warehouse::create(['company_id' => $user->company_id, 'code' => 'MAIN', 'name' => 'Main Warehouse']); $this->actingAs($user) ->get(route('warehouses.index')) ->assertOk() ->assertSee('Warehouses', false) ->assertSee('Main Warehouse', false); } /** * Ensure unlicensed tenants are blocked from licensed warehouse screens. */ public function test_unlicensed_tenant_is_blocked_from_warehouse_dashboard(): void { $company = Company::create(['name' => 'Unlicensed Ltd', 'slug' => 'unlicensed-ltd']); $user = User::factory()->create(['company_id' => $company->id]); $this->actingAs($user) ->get(route('dashboard')) ->assertStatus(402); } /** * Ensure users without a company are forbidden by tenant assignment middleware. */ public function test_user_without_company_is_forbidden_from_warehouse_dashboard(): void { $user = User::factory()->create(['company_id' => null]); $this->actingAs($user) ->get(route('dashboard')) ->assertForbidden(); } /** * Ensure tenant users only see their own warehouses. */ public function test_warehouse_index_is_tenant_isolated(): void { $companyA = Company::create(['name' => 'Company A', 'slug' => 'company-a']); $companyB = Company::create(['name' => 'Company B', 'slug' => 'company-b']); LicenseKey::issueFor($companyA, 'A licence'); $user = User::factory()->create(['company_id' => $companyA->id]); Warehouse::create(['company_id' => $companyA->id, 'code' => 'A1', 'name' => 'A Warehouse']); Warehouse::create(['company_id' => $companyB->id, 'code' => 'B1', 'name' => 'B Warehouse']); $this->actingAs($user) ->get(route('warehouses.index')) ->assertOk() ->assertSee('A Warehouse', false) ->assertDontSee('B Warehouse', false); } /** * Ensure warehouse creation uses the authenticated tenant company and timezone. */ public function test_licensed_user_can_create_warehouse_for_their_company(): void { $user = User::factory()->create(); $this->actingAs($user)->withSession(['_token' => 'test-token'])->post(route('warehouses.store'), $this->csrfPayload([ 'code' => 'EAST', 'name' => 'East Midlands DC', 'address' => 'Main Road, Nottingham', ]))->assertRedirect(); $this->assertDatabaseHas('warehouses', [ 'company_id' => $user->company_id, 'code' => 'EAST', 'name' => 'East Midlands DC', 'status' => 'active', 'timezone' => 'Europe/London', ]); } /** * Ensure warehouse input validation protects required fields. */ public function test_create_warehouse_validates_input(): void { $user = User::factory()->create(); $this->actingAs($user) ->from(route('warehouses.index')) ->withSession(['_token' => 'test-token']) ->post(route('warehouses.store'), $this->csrfPayload([ 'code' => '', 'name' => '', ])) ->assertRedirect(route('warehouses.index')) ->assertSessionHasErrors(['code', 'name']); } /** * Ensure rules index only lists rules attached to the authenticated company. */ public function test_validation_rules_index_is_tenant_isolated(): void { $companyA = Company::create(['name' => 'Rules A', 'slug' => 'rules-a']); $companyB = Company::create(['name' => 'Rules B', 'slug' => 'rules-b']); LicenseKey::issueFor($companyA, 'Rules licence'); $user = User::factory()->create(['company_id' => $companyA->id]); $warehouseA = Warehouse::create(['company_id' => $companyA->id, 'code' => 'RA', 'name' => 'Rules A Warehouse']); $warehouseB = Warehouse::create(['company_id' => $companyB->id, 'code' => 'RB', 'name' => 'Rules B Warehouse']); WarehouseValidationRule::create(['warehouse_id' => $warehouseA->id, 'name' => 'A Rule', 'rule_key' => 'a-rule', 'severity' => 'warning']); WarehouseValidationRule::create(['warehouse_id' => $warehouseB->id, 'name' => 'B Rule', 'rule_key' => 'b-rule', 'severity' => 'blocker']); $this->actingAs($user) ->get(route('warehouse-rules.index')) ->assertOk() ->assertSee('A Rule', false) ->assertDontSee('B Rule', false); } /** * Ensure users can create validation rules for their own warehouses. */ public function test_user_can_create_validation_rule_for_own_warehouse(): void { $user = User::factory()->create(); $warehouse = Warehouse::create(['company_id' => $user->company_id, 'code' => 'MAIN', 'name' => 'Main Warehouse']); $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(); $this->assertDatabaseHas('warehouse_validation_rules', [ 'warehouse_id' => $warehouse->id, 'name' => 'Require batch number', 'rule_key' => 'batch-required', 'severity' => 'blocker', 'is_active' => true, ]); $this->assertSame(['field' => 'batch_no'], WarehouseValidationRule::firstOrFail()->configuration); } /** * Ensure users cannot create rules against another tenant warehouse. */ public function test_user_cannot_create_validation_rule_for_another_company_warehouse(): void { $user = User::factory()->create(); $otherCompany = Company::create(['name' => 'Other Company', 'slug' => 'other-company']); $otherWarehouse = Warehouse::create(['company_id' => $otherCompany->id, 'code' => 'OTHER', 'name' => 'Other Warehouse']); $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(); } /** * Ensure rule creation validates JSON and severity choices. */ public function test_create_validation_rule_validates_input(): void { $user = User::factory()->create(); $this->actingAs($user) ->from(route('warehouse-rules.index')) ->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. */ }