seedRolesAndPermissions(); $this->admin = $this->createAdmin(); } /** * Test can view budget dashboard */ public function test_can_view_budget_dashboard(): void { $response = $this->actingAs($this->admin)->get( route('admin.budgets.index') ); $response->assertStatus(200); } /** * Test can create budget category */ public function test_can_create_budget_category(): void { $response = $this->actingAs($this->admin)->post( route('admin.budget-categories.store'), [ 'name' => '行政費用', 'description' => '日常行政支出', ] ); $response->assertRedirect(); $this->assertDatabaseHas('budget_categories', ['name' => '行政費用']); } /** * Test can create budget */ public function test_can_create_budget(): void { $category = BudgetCategory::factory()->create(); $response = $this->actingAs($this->admin)->post( route('admin.budgets.store'), [ 'category_id' => $category->id, 'year' => now()->year, 'amount' => 100000, 'description' => '年度預算', ] ); $response->assertRedirect(); $this->assertDatabaseHas('budgets', [ 'category_id' => $category->id, 'amount' => 100000, ]); } /** * Test budget tracks spending */ public function test_budget_tracks_spending(): void { $category = BudgetCategory::factory()->create(); $budget = Budget::factory()->create([ 'category_id' => $category->id, 'amount' => 100000, 'spent' => 0, ]); // Create finance document linked to category $document = $this->createFinanceDocument([ 'budget_category_id' => $category->id, 'amount' => 5000, 'status' => FinanceDocument::STATUS_APPROVED_CHAIR, ]); // Spending should be updated $budget->refresh(); $this->assertGreaterThanOrEqual(0, $budget->spent); } /** * Test budget overspend warning */ public function test_budget_overspend_warning(): void { $category = BudgetCategory::factory()->create(); $budget = Budget::factory()->create([ 'category_id' => $category->id, 'amount' => 10000, 'spent' => 9500, ]); // Budget is 95% used $percentUsed = ($budget->spent / $budget->amount) * 100; $this->assertGreaterThan(90, $percentUsed); } /** * Test can update budget amount */ public function test_can_update_budget_amount(): void { $budget = Budget::factory()->create(['amount' => 50000]); $response = $this->actingAs($this->admin)->patch( route('admin.budgets.update', $budget), ['amount' => 75000] ); $budget->refresh(); $this->assertEquals(75000, $budget->amount); } /** * Test budget year filter */ public function test_budget_year_filter(): void { Budget::factory()->create(['year' => 2024]); Budget::factory()->create(['year' => 2025]); $response = $this->actingAs($this->admin)->get( route('admin.budgets.index', ['year' => 2024]) ); $response->assertStatus(200); } /** * Test budget category filter */ public function test_budget_category_filter(): void { $category1 = BudgetCategory::factory()->create(['name' => '類別A']); $category2 = BudgetCategory::factory()->create(['name' => '類別B']); Budget::factory()->create(['category_id' => $category1->id]); Budget::factory()->create(['category_id' => $category2->id]); $response = $this->actingAs($this->admin)->get( route('admin.budgets.index', ['category_id' => $category1->id]) ); $response->assertStatus(200); } /** * Test budget remaining calculation */ public function test_budget_remaining_calculation(): void { $budget = Budget::factory()->create([ 'amount' => 100000, 'spent' => 30000, ]); $remaining = $budget->amount - $budget->spent; $this->assertEquals(70000, $remaining); } /** * Test duplicate budget prevention */ public function test_duplicate_budget_prevention(): void { $category = BudgetCategory::factory()->create(); $year = now()->year; Budget::factory()->create([ 'category_id' => $category->id, 'year' => $year, ]); // Attempt to create duplicate $response = $this->actingAs($this->admin)->post( route('admin.budgets.store'), [ 'category_id' => $category->id, 'year' => $year, 'amount' => 50000, ] ); $response->assertSessionHasErrors(); } }