seed(CurrencySeeder::class); $this->seed(CountriesTableSeeder::class); $this->seed(RoleAndPermissionSeeder::class); $this->seed(SystemSettingSeeder::class); // نرخ‌های غیر صفر برای ارزها (شبیه‌سازی import-fx-rates اجرا شده) Currency::where('code', 'USD')->update(['rate_to_rial' => 60000]); Currency::where('code', 'AED')->update(['rate_to_rial' => 16200]); Currency::where('code', 'EUR')->update(['rate_to_rial' => 65000]); Currency::where('code', 'CNY')->update(['rate_to_rial' => 8000]); // یک کاربر ادمین $admin = User::factory()->create(['email' => 'admin@ifnex.ir']); $admin->assignRole('super_admin'); } /** * HTTP fake برای endpointهای API که smoke-test صدا می‌زند. * URL پایه به‌صورت localhost در نظر گرفته می‌شود (APP_URL پیش‌فرض تست). */ private function fakeApiEndpoints(string $baseUrl = 'http://localhost'): void { Http::fake([ "{$baseUrl}/up" => Http::response('OK', 200), "{$baseUrl}/api/v1/countries" => Http::response(['data' => []], 200), "{$baseUrl}/api/v1/track/INVALID-AWB-TEST" => Http::response([], 404), "{$baseUrl}/api/v1/customer/profile" => Http::response(['message' => 'Unauthenticated'], 401), ]); } // ═══════════════════ PASS scenarios ═══════════════════ #[Test] public function it_passes_when_everything_is_healthy() { $this->seedHealthyData(); $this->fakeApiEndpoints(); $this->artisan('ifnex:smoke-test') ->assertSuccessful(); } // ═══════════════════ FAIL scenarios ═══════════════════ #[Test] public function it_fails_when_currencies_table_is_empty() { // ارزها ساخته نمی‌شوند — جدول خالی $this->seed(CountriesTableSeeder::class); $this->seed(RoleAndPermissionSeeder::class); $this->seed(SystemSettingSeeder::class); $admin = User::factory()->create(); $admin->assignRole('super_admin'); $this->fakeApiEndpoints(); $this->artisan('ifnex:smoke-test') ->assertFailed(); } #[Test] public function it_fails_when_currency_rate_is_zero() { $this->seedHealthyData(); // شبیه‌سازی نرخ صفر — import-fx-rates هنوز اجرا نشده Currency::where('code', 'USD')->update(['rate_to_rial' => 0]); $this->fakeApiEndpoints(); $this->artisan('ifnex:smoke-test') ->assertFailed(); } #[Test] public function it_fails_when_no_admin_user_exists() { $this->seedHealthyData(); // حذف همهٔ کاربران ادمین User::query()->delete(); $this->fakeApiEndpoints(); $this->artisan('ifnex:smoke-test') ->assertFailed(); } #[Test] public function it_fails_when_countries_not_seeded() { $this->seed(CurrencySeeder::class); $this->seed(RoleAndPermissionSeeder::class); $this->seed(SystemSettingSeeder::class); // کشورها ساخته نمی‌شوند $admin = User::factory()->create(); $admin->assignRole('super_admin'); $this->fakeApiEndpoints(); $this->artisan('ifnex:smoke-test') ->assertFailed(); } #[Test] public function it_fails_when_roles_not_seeded() { // بدون RoleAndPermissionSeeder — نقش‌ها موجود نیستند $this->seed(CurrencySeeder::class); $this->seed(CountriesTableSeeder::class); $this->seed(SystemSettingSeeder::class); // کاربر بدون assignRole (چون نقش وجود ندارد) User::factory()->create(); $this->fakeApiEndpoints(); $this->artisan('ifnex:smoke-test') ->assertFailed(); } #[Test] public function it_fails_when_health_endpoint_returns_500() { $this->seedHealthyData(); // شبیه‌سازی شکست /up Http::fake([ 'http://localhost/up' => Http::response('Server Error', 500), 'http://localhost/api/v1/countries' => Http::response(['data' => []], 200), 'http://localhost/api/v1/track/INVALID-AWB-TEST' => Http::response([], 404), 'http://localhost/api/v1/customer/profile' => Http::response(['message' => 'Unauthenticated'], 401), ]); $this->artisan('ifnex:smoke-test') ->assertFailed(); } #[Test] public function it_fails_when_protected_endpoint_allows_unauthenticated_access() { $this->seedHealthyData(); // شبیه‌سازی باگ امنیتی: endpoint محافظت‌شده، 200 برمی‌گرداند (به‌جای 401) Http::fake([ 'http://localhost/up' => Http::response('OK', 200), 'http://localhost/api/v1/countries' => Http::response(['data' => []], 200), 'http://localhost/api/v1/track/INVALID-AWB-TEST' => Http::response([], 404), 'http://localhost/api/v1/customer/profile' => Http::response(['data' => ['id' => 1]], 200), // ← باید 401 باشد! ]); $this->artisan('ifnex:smoke-test') ->assertFailed(); } #[Test] public function it_accepts_custom_base_url() { $this->seedHealthyData(); $customUrl = 'https://system.ifnex.ir'; Http::fake([ "{$customUrl}/up" => Http::response('OK', 200), "{$customUrl}/api/v1/countries" => Http::response(['data' => []], 200), "{$customUrl}/api/v1/track/INVALID-AWB-TEST" => Http::response([], 404), "{$customUrl}/api/v1/customer/profile" => Http::response(['message' => 'Unauthenticated'], 401), ]); $this->artisan('ifnex:smoke-test', ['--url' => $customUrl]) ->assertSuccessful(); } #[Test] public function it_accepts_422_for_tracking_endpoint() { $this->seedHealthyData(); // ممکن است controller برای AWB نامعتبر، 422 برگرداند به‌جای 404 — این هم قابل‌قبول است Http::fake([ 'http://localhost/up' => Http::response('OK', 200), 'http://localhost/api/v1/countries' => Http::response(['data' => []], 200), 'http://localhost/api/v1/track/INVALID-AWB-TEST' => Http::response(['errors' => []], 422), 'http://localhost/api/v1/customer/profile' => Http::response(['message' => 'Unauthenticated'], 401), ]); $this->artisan('ifnex:smoke-test') ->assertSuccessful(); } #[Test] public function it_fails_when_tracking_endpoint_returns_500() { $this->seedHealthyData(); // 500 یعنی باگ در controller — نباید رخ دهد Http::fake([ 'http://localhost/up' => Http::response('OK', 200), 'http://localhost/api/v1/countries' => Http::response(['data' => []], 200), 'http://localhost/api/v1/track/INVALID-AWB-TEST' => Http::response('Fatal', 500), 'http://localhost/api/v1/customer/profile' => Http::response(['message' => 'Unauthenticated'], 401), ]); $this->artisan('ifnex:smoke-test') ->assertFailed(); } }