[REFACTOR] 統一訂單同步 API 錯誤回應與修正 Linter 警告
This commit is contained in:
194
tests/Feature/Integration/ProductSearchApiTest.php
Normal file
194
tests/Feature/Integration/ProductSearchApiTest.php
Normal file
@@ -0,0 +1,194 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Integration;
|
||||
|
||||
use Tests\TestCase;
|
||||
use App\Modules\Core\Models\Tenant;
|
||||
use App\Modules\Core\Models\User;
|
||||
use App\Modules\Inventory\Models\Product;
|
||||
use App\Modules\Inventory\Models\Category;
|
||||
use App\Modules\Inventory\Models\Unit;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Stancl\Tenancy\Facades\Tenancy;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class ProductSearchApiTest extends TestCase
|
||||
{
|
||||
protected $tenant;
|
||||
protected $user;
|
||||
protected $domain;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
Artisan::call('migrate:fresh');
|
||||
|
||||
$this->domain = 'search-test-' . Str::random(8) . '.erp.local';
|
||||
$tenantId = 'test_tenant_s_' . Str::random(8);
|
||||
|
||||
tenancy()->central(function () use ($tenantId) {
|
||||
$this->tenant = Tenant::create([
|
||||
'id' => $tenantId,
|
||||
'name' => 'Search Test Tenant',
|
||||
]);
|
||||
$this->tenant->domains()->create(['domain' => $this->domain]);
|
||||
});
|
||||
|
||||
tenancy()->initialize($this->tenant);
|
||||
Artisan::call('tenants:migrate');
|
||||
|
||||
$this->user = User::factory()->create(['name' => 'Search Admin']);
|
||||
|
||||
// 建立測試資料
|
||||
$cat1 = Category::firstOrCreate(['name' => '飲品'], ['code' => 'CAT-DRINK']);
|
||||
$cat2 = Category::firstOrCreate(['name' => '食品'], ['code' => 'CAT-FOOD']);
|
||||
$unit = Unit::firstOrCreate(['name' => '瓶'], ['code' => 'BO']);
|
||||
|
||||
Product::create([
|
||||
'name' => '可口可樂',
|
||||
'code' => 'COKE-001',
|
||||
'barcode' => '4710001',
|
||||
'price' => 25,
|
||||
'category_id' => $cat1->id,
|
||||
'base_unit_id' => $unit->id,
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$pepsi = Product::create([
|
||||
'name' => '百事可樂',
|
||||
'code' => 'PEPSI-001',
|
||||
'barcode' => '4710002',
|
||||
'price' => 23,
|
||||
'category_id' => $cat1->id,
|
||||
'base_unit_id' => $unit->id,
|
||||
'is_active' => true,
|
||||
]);
|
||||
DB::table('products')->where('id', $pepsi->id)->update(['updated_at' => now()->subDay()]);
|
||||
|
||||
Product::create([
|
||||
'name' => '漢堡',
|
||||
'code' => 'BURGER-001',
|
||||
'barcode' => '4710003',
|
||||
'price' => 50,
|
||||
'category_id' => $cat2->id,
|
||||
'base_unit_id' => $unit->id,
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
Product::create([
|
||||
'name' => '停用商品',
|
||||
'code' => 'INACTIVE-001',
|
||||
'is_active' => false,
|
||||
'category_id' => $cat1->id,
|
||||
'base_unit_id' => $unit->id,
|
||||
]);
|
||||
|
||||
tenancy()->end();
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
if ($this->tenant) {
|
||||
$this->tenant->delete();
|
||||
}
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function test_can_search_all_active_products()
|
||||
{
|
||||
\Laravel\Sanctum\Sanctum::actingAs($this->user, ['*']);
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-Tenant-Domain' => $this->domain,
|
||||
'Accept' => 'application/json',
|
||||
])->getJson('/api/v1/integration/products');
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonPath('status', 'success')
|
||||
->assertJsonCount(3, 'data'); // 3 active products
|
||||
}
|
||||
|
||||
public function test_can_filter_by_category()
|
||||
{
|
||||
\Laravel\Sanctum\Sanctum::actingAs($this->user, ['*']);
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-Tenant-Domain' => $this->domain,
|
||||
'Accept' => 'application/json',
|
||||
])->getJson('/api/v1/integration/products?category=食品');
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonCount(1, 'data')
|
||||
->assertJsonPath('data.0.name', '漢堡');
|
||||
}
|
||||
|
||||
public function test_can_filter_by_updated_after()
|
||||
{
|
||||
\Laravel\Sanctum\Sanctum::actingAs($this->user, ['*']);
|
||||
|
||||
// 搜尋今天更新的商品 (百事可樂是昨天更新的,應該被過濾掉)
|
||||
$timeStr = now()->startOfDay()->format('Y-m-d H:i:s');
|
||||
$response = $this->withHeaders([
|
||||
'X-Tenant-Domain' => $this->domain,
|
||||
'Accept' => 'application/json',
|
||||
])->getJson("/api/v1/integration/products?updated_after={$timeStr}");
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonCount(2, 'data'); // 可口可樂, 漢堡 (百事可樂是昨天)
|
||||
}
|
||||
|
||||
public function test_pagination_works()
|
||||
{
|
||||
\Laravel\Sanctum\Sanctum::actingAs($this->user, ['*']);
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-Tenant-Domain' => $this->domain,
|
||||
'Accept' => 'application/json',
|
||||
])->getJson('/api/v1/integration/products?per_page=2');
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonCount(2, 'data')
|
||||
->assertJsonPath('meta.per_page', 2)
|
||||
->assertJsonPath('meta.total', 3);
|
||||
}
|
||||
|
||||
public function test_can_filter_by_precision_params()
|
||||
{
|
||||
\Laravel\Sanctum\Sanctum::actingAs($this->user, ['*']);
|
||||
|
||||
// 1. By Barcode
|
||||
$response = $this->withHeaders([
|
||||
'X-Tenant-Domain' => $this->domain,
|
||||
'Accept' => 'application/json',
|
||||
])->getJson('/api/v1/integration/products?barcode=4710001');
|
||||
$response->assertStatus(200)->assertJsonCount(1, 'data')->assertJsonPath('data.0.name', '可口可樂');
|
||||
|
||||
// 2. By Code
|
||||
$response = $this->withHeaders([
|
||||
'X-Tenant-Domain' => $this->domain,
|
||||
'Accept' => 'application/json',
|
||||
])->getJson('/api/v1/integration/products?code=PEPSI-001');
|
||||
$response->assertStatus(200)->assertJsonCount(1, 'data')->assertJsonPath('data.0.name', '百事可樂');
|
||||
|
||||
// 3. By product_id (ERP ID)
|
||||
$productId = Product::where('code', 'BURGER-001')->value('id');
|
||||
$response = $this->withHeaders([
|
||||
'X-Tenant-Domain' => $this->domain,
|
||||
'Accept' => 'application/json',
|
||||
])->getJson("/api/v1/integration/products?product_id={$productId}");
|
||||
$response->assertStatus(200)->assertJsonCount(1, 'data')->assertJsonPath('data.0.name', '漢堡');
|
||||
}
|
||||
|
||||
public function test_is_protected_by_auth()
|
||||
{
|
||||
// 不帶 Token
|
||||
$response = $this->withHeaders([
|
||||
'X-Tenant-Domain' => $this->domain,
|
||||
'Accept' => 'application/json',
|
||||
])->getJson('/api/v1/integration/products');
|
||||
|
||||
$response->assertStatus(401);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user