| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290 |
- <?php
- namespace Webkul\BagistoApi\Tests;
- use Illuminate\Support\Facades\DB;
- use Illuminate\Foundation\Testing\DatabaseTransactions;
- use Illuminate\Testing\TestResponse;
- use Webkul\Attribute\Models\Attribute;
- use Webkul\Attribute\Models\AttributeOption;
- use Webkul\BagistoApi\Tests\BagistoApiTest;
- use Webkul\Core\Models\Channel;
- use Webkul\Customer\Models\Customer;
- use Webkul\Customer\Models\CustomerGroup;
- use Webkul\Product\Models\Product;
- use Webkul\Product\Models\ProductAttributeValue;
- /**
- * Base test case for all BagistoApi tests.
- *
- * Provides shared storefront key handling, customer authentication,
- * database seeding, and foreign key constraint management.
- */
- abstract class BagistoApiTestCase extends BagistoApiTest
- {
- use DatabaseTransactions;
- /** Default storefront API key for tests */
- protected string $storefrontKey = 'pk_storefront_WaZh0x0FlbKF1suYmDD37YTfkRKm6BJ1';
- /** Disable API logging middleware for tests */
- protected $withoutMiddleware = [
- \Webkul\BagistoApi\Http\Middleware\LogApiRequests::class,
- ];
- public function setUp(): void
- {
- parent::setUp();
- DB::statement('SET FOREIGN_KEY_CHECKS=0');
- }
- public function tearDown(): void
- {
- DB::statement('SET FOREIGN_KEY_CHECKS=1');
- parent::tearDown();
- }
- /**
- * Get storefront key header (public API access)
- */
- protected function storefrontHeaders(): array
- {
- return [
- 'X-STOREFRONT-KEY' => $this->storefrontKey,
- ];
- }
- /**
- * Get headers with storefront key + customer auth token
- */
- protected function authHeaders(Customer $customer): array
- {
- $token = $customer->createToken('test-token')->plainTextToken;
- return [
- 'Authorization' => "Bearer {$token}",
- 'X-STOREFRONT-KEY' => $this->storefrontKey,
- ];
- }
- /**
- * Seed required database records (channel, customer group, category)
- */
- protected function seedRequiredData(): void
- {
- try {
- if (! \Webkul\Category\Models\Category::exists()) {
- \Webkul\Category\Models\Category::factory()->create([
- 'parent_id' => null,
- ]);
- }
- if (! Channel::exists()) {
- Channel::factory()->create();
- }
- if (! CustomerGroup::where('code', 'general')->exists()) {
- CustomerGroup::create([
- 'code' => 'general',
- 'name' => 'General',
- 'is_user_defined' => 0,
- ]);
- }
- } catch (\Exception $e) {
- $this->markTestSkipped('Test database not properly configured: '.$e->getMessage());
- }
- }
- /**
- * Create a customer and return it
- */
- protected function createCustomer(array $attributes = []): Customer
- {
- $this->seedRequiredData();
- return Customer::factory()->create($attributes);
- }
- /**
- * Create a test customer with a valid token for Bearer authentication
- * Returns an array with customer and token keys
- */
- protected function createTestCustomer(): array
- {
- // Create customer with a token field (same way it's created during registration)
- $customer = $this->createCustomer([
- 'token' => md5(uniqid(rand(), true)),
- ]);
- return [
- 'customer' => $customer,
- 'token' => $customer->token,
- ];
- }
- protected function getAttributeIdByCode(string $code): int
- {
- $attributeId = Attribute::query()->where('code', $code)->value('id');
- if (! $attributeId) {
- $this->markTestSkipped(sprintf('Required attribute "%s" not found. Run Bagisto seeders for attributes.', $code));
- }
- return (int) $attributeId;
- }
- protected function upsertProductAttributeValue(
- int $productId,
- string $attributeCode,
- mixed $value,
- ?string $locale = 'en',
- ?string $channel = 'default'
- ): void {
- $attribute = Attribute::query()->where('code', $attributeCode)->first();
- if (! $attribute) {
- $this->markTestSkipped(sprintf('Required attribute "%s" not found. Run Bagisto seeders for attributes.', $attributeCode));
- }
- $type = (string) ($attribute->type ?? 'text');
- $field = ProductAttributeValue::$attributeTypeFields[$type] ?? 'text_value';
- $payload = [
- 'product_id' => $productId,
- 'attribute_id' => (int) $attribute->id,
- 'locale' => $locale,
- 'channel' => $channel,
- 'text_value' => null,
- 'boolean_value'=> null,
- 'integer_value'=> null,
- 'float_value' => null,
- 'datetime_value'=> null,
- 'date_value' => null,
- 'json_value' => null,
- ];
- $normalized = $value;
- if ($field === 'boolean_value') {
- $normalized = (bool) $value;
- } elseif ($field === 'integer_value') {
- $normalized = (int) $value;
- } elseif ($field === 'float_value') {
- $normalized = (float) $value;
- } elseif ($field === 'json_value') {
- $normalized = is_string($value) ? $value : json_encode($value);
- } else {
- $normalized = is_string($value) ? $value : (string) $value;
- }
- $payload[$field] = $normalized;
- ProductAttributeValue::query()->updateOrCreate(
- [
- 'product_id' => $productId,
- 'attribute_id' => (int) $attribute->id,
- 'locale' => $locale,
- 'channel' => $channel,
- ],
- $payload
- );
- }
- protected function ensureProductIsSaleable(Product $product, ?float $price = 10.0): void
- {
- $this->upsertProductAttributeValue($product->id, 'name', 'Test '.$product->sku, 'en', 'default');
- $this->upsertProductAttributeValue($product->id, 'url_key', strtolower($product->sku), 'en', 'default');
- $this->upsertProductAttributeValue($product->id, 'status', 1, null, 'default');
- if ($price !== null) {
- $this->upsertProductAttributeValue($product->id, 'price', $price, null, 'default');
- }
- }
- protected function ensureInventory(Product $product, int $qty = 50): void
- {
- $inventorySourceId = (int) (DB::table('inventory_sources')->value('id') ?? 0);
- if (! $inventorySourceId) {
- $this->markTestSkipped('No inventory_sources found. Run Bagisto seeders for inventory sources.');
- }
- DB::table('product_inventories')->updateOrInsert(
- [
- 'product_id' => $product->id,
- 'inventory_source_id' => $inventorySourceId,
- 'vendor_id' => 0,
- ],
- [
- 'qty' => $qty,
- ]
- );
- }
- protected function createBaseProduct(string $type, array $overrides = []): Product
- {
- $this->seedRequiredData();
- $attributeFamilyId = (int) (DB::table('attribute_families')->value('id') ?? 1);
- $product = Product::factory()->create([
- 'type' => $type,
- 'attribute_family_id' => $attributeFamilyId,
- ...$overrides,
- ]);
- $this->ensureProductIsSaleable($product, 10.0);
- return $product;
- }
- protected function createAttributeOption(int $attributeId, string $label, string $locale = 'en'): int
- {
- /** @var AttributeOption $option */
- $option = AttributeOption::query()->create([
- 'attribute_id' => $attributeId,
- 'admin_name' => $label,
- 'sort_order' => 1,
- ]);
- DB::table('attribute_option_translations')->insert([
- 'attribute_option_id' => $option->id,
- 'locale' => $locale,
- 'label' => $label,
- ]);
- return (int) $option->id;
- }
- /**
- * Get an existing product with inventory from the database for testing
- * Returns an array with product and inventory_source_id keys
- */
- protected function createTestProduct(): array
- {
- // Find an existing product that has inventory in the database
- $productWithInventory = DB::table('product_inventories')
- ->select('product_id')
- ->groupBy('product_id')
- ->havingRaw('SUM(qty) > 0')
- ->first();
- if (!$productWithInventory) {
- throw new \Exception('No products with inventory found in database');
- }
- $productId = $productWithInventory->product_id;
-
- // Get the product model
- $product = Product::find($productId);
-
- if (!$product) {
- throw new \Exception('Product not found with ID: ' . $productId);
- }
- return [
- 'product' => $product,
- 'inventory_source_id' => 1,
- ];
- }
- }
|