ifnex/04_Laravel/tests/Feature/Services/PriceCalculatorServiceTest.php
Kazem Alghasi b9ede16613 refactor(core): harden production environment and fix discount logic
Implement critical security hardening and resolve a P0 bug in the
discount code consumption flow. This update transitions the system
from a development-heavy configuration to a production-ready state.

Key changes:
- Fix P0 bug where `consumeDiscountCode()` was missing, breaking order
  registration; replaced with a robust `DiscountCodeService` using
  `discount_code_usages` for audit trails and idempotency.
- Implement strict production guards for the Zarinpal payment gateway
  to prevent accidental mock processing in live environments.
- Refactor WordPress bridge asset management to use `filemtime` based
  versioning instead of `time()` to prevent unnecessary cache busting.
- Remove debug-only hacks (`opcache_reset`, `header` cache control)
  from the WordPress plugin.
- Enhance file upload security by enforcing strict `mimetypes` validation.
- Update `.gitignore` to exclude sensitive WordPress configuration
  files and local PHPUnit caches.
- Add comprehensive documentation for production deployment, including
  security checklists and key rotation procedures.
- Update test suite to reflect new architecture and ensure 100%
  coverage of the discount service.
2026-10-31 19:18:58 +03:30

216 lines
7.1 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace Tests\Feature\Services;
use App\Models\Country;
use App\Models\CountryZone;
use App\Models\RateEntry;
use App\Models\SystemSetting;
use App\Services\PriceCalculatorService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;
/**
* تست محاسبه‌گر قیمت — بر اساس معماری ۴-رُبعی (Export/Import × Doc/Parcel)
* و ذخیره‌سازی نرخ‌ها به ریال (IRR) — فاز ۳.۷ و ۳.۸.۱
*/
class PriceCalculatorServiceTest extends TestCase
{
use RefreshDatabase;
#[Test]
public function it_calculates_price_correctly_for_standard_package()
{
// ۱. تنظیم SystemSetting ها برای تست
SystemSetting::create(['key' => 'profit_margin', 'value' => 1.0]); // بدون سود
SystemSetting::create(['key' => 'vat_rate', 'value' => 0.0]); // بدون مالیات
// ۲. ایجاد کشور
Country::factory()->create([
'iso_code' => 'US',
'name' => 'United States',
'is_active' => true,
]);
// ۳. نگاشت زون — جدول country_zones (سیستم ۴-رُبعی)
CountryZone::create([
'country_name' => 'United States',
'country_iso2' => 'US',
'document_zone' => 1,
'parcel_zone' => 1,
]);
// ۴. نرخ حمل — جدول rate_entries، مستقیماً به ریال (IRR)
RateEntry::create([
'direction' => 'export',
'service_type' => 'document',
'weight' => 3.0,
'zone' => 1,
'rate' => 40000.0, // ۴۰,۰۰۰ ریال
'currency' => 'IRR',
'effective_at' => now()->subDay(),
]);
// ۵. اجرای سرویس
$service = app(PriceCalculatorService::class);
$data = [
'direction' => 'Outbound', // ← در سرویس به 'export' تبدیل می‌شود
'type' => 'DOC_NORMAL',
'country_iso' => 'US',
'weight' => 2.5,
'volumetric_weight' => 3.0,
'extra_service' => 10000, // ۱۰,۰۰۰ ریال
'packing_cost' => 0,
'domestic_pickup' => 0,
'domestic_delivery' => 0,
'warehousing_cost' => 0,
];
$result = $service->calculate($data);
// ۶. بررسی ساختار خروجی
$this->assertIsArray($result);
$this->assertArrayHasKey('total_fee', $result);
$this->assertArrayHasKey('base_price', $result);
// ۷. بررسی مقادیر
$this->assertEquals(1, $result['zone']);
$this->assertEquals(3.0, $result['chargeable_weight']);
$this->assertEquals(40000.0, $result['base_price']);
$this->assertFalse($result['discount_applied']);
// محاسبهٔ دستی (همه به ریال):
// base_price = 40000
// net_rial = 40000 × 1.0 (سود) + 10000 (خدمات) = 50000
// vat = 0
// تخفیف = 0
// total_fee = گردش به نزدیک‌ترین ۱۰۰۰ = 50000
$this->assertEquals(50000, $result['total_fee']);
$this->assertEquals(50000, $result['net_rial']);
}
#[Test]
public function it_applies_discount_from_code_in_pricing()
{
SystemSetting::create(['key' => 'profit_margin', 'value' => 1.0]);
SystemSetting::create(['key' => 'vat_rate', 'value' => 0.0]);
Country::factory()->create([
'iso_code' => 'DE',
'name' => 'Germany',
'is_active' => true,
]);
CountryZone::create([
'country_name' => 'Germany',
'country_iso2' => 'DE',
'document_zone' => 2,
'parcel_zone' => 2,
]);
RateEntry::create([
'direction' => 'export',
'service_type' => 'document',
'weight' => 1.0,
'zone' => 2,
'rate' => 100000.0, // ۱۰۰,۰۰۰ ریال
'currency' => 'IRR',
'effective_at' => now()->subDay(),
]);
// کد تخفیف ۱۰٪ فعال با ظرفیت
\App\Models\DiscountCode::create([
'code' => 'PCT10',
'type' => 'percentage',
'value' => 10,
'usage_limit' => 10,
'used_count' => 0,
'is_active' => true,
]);
$service = app(PriceCalculatorService::class);
$result = $service->calculate([
'direction' => 'export',
'type' => 'DOC_NORMAL',
'country_iso' => 'DE',
'weight' => 1.0,
'volumetric_weight' => 0,
'extra_service' => 0,
'packing_cost' => 0,
'domestic_pickup' => 0,
'domestic_delivery' => 0,
'warehousing_cost' => 0,
'discount_code' => 'PCT10',
]);
// base = 100000، سود = 1.0، vat = 0 → subtotal = 100000
// تخفیف = ۱۰٪ = 10000 → total = 90000 (گردش به ۱۰۰۰)
$this->assertTrue($result['discount_applied']);
$this->assertEquals(10000, $result['discount_amount']);
$this->assertEquals(90000, $result['total_fee']);
}
#[Test]
public function it_ignores_discount_code_whose_usage_limit_is_reached()
{
SystemSetting::create(['key' => 'profit_margin', 'value' => 1.0]);
SystemSetting::create(['key' => 'vat_rate', 'value' => 0.0]);
Country::factory()->create([
'iso_code' => 'AE',
'name' => 'United Arab Emirates',
'is_active' => true,
]);
CountryZone::create([
'country_name' => 'United Arab Emirates',
'country_iso2' => 'AE',
'document_zone' => 1,
'parcel_zone' => 1,
]);
RateEntry::create([
'direction' => 'export',
'service_type' => 'document',
'weight' => 1.0,
'zone' => 1,
'rate' => 100000.0,
'currency' => 'IRR',
'effective_at' => now()->subDay(),
]);
// کد فعال ولی ظرفیتش پر شده — نباید در قیمت اعمال شود (سخت‌سازی)
\App\Models\DiscountCode::create([
'code' => 'FULL',
'type' => 'percentage',
'value' => 50,
'usage_limit' => 3,
'used_count' => 3,
'is_active' => true,
]);
$service = app(PriceCalculatorService::class);
$result = $service->calculate([
'direction' => 'export',
'type' => 'DOC_NORMAL',
'country_iso' => 'AE',
'weight' => 1.0,
'volumetric_weight' => 0,
'extra_service' => 0,
'packing_cost' => 0,
'domestic_pickup' => 0,
'domestic_delivery' => 0,
'warehousing_cost' => 0,
'discount_code' => 'FULL',
]);
$this->assertFalse($result['discount_applied']);
$this->assertEquals(0, $result['discount_amount']);
$this->assertEquals(100000, $result['total_fee']);
}
}