get(route('woo.dashboard')) ->assertRedirect(route('sso.connect', ['redirect' => route('woo.dashboard')])); } public function test_authenticated_user_sees_dashboard(): void { $user = User::factory()->create(); $this->actingAs($user) ->get(route('woo.dashboard')) ->assertOk() ->assertSee('Woo Manager'); } public function test_store_activation_exchanges_install_token(): void { $user = User::factory()->create(); $store = WooStore::create([ 'user_id' => $user->id, 'site_url' => 'https://shop.example.com', 'site_name' => 'Example Shop', 'status' => WooStore::STATUS_PENDING, ]); $token = app(InstallTokenService::class)->issue($store)['token']; $response = $this->postJson('/api/v1/stores/activate', [ 'install_token' => $token, 'site_url' => 'https://shop.example.com', ]); $response->assertOk() ->assertJsonPath('store_id', $store->public_id) ->assertJsonStructure(['webhook_url', 'webhook_secret', 'plugin_token']); $this->assertSame(WooStore::STATUS_ACTIVE, $store->fresh()->status); } public function test_webhook_ingests_order(): void { $user = User::factory()->create(); $store = WooStore::create([ 'user_id' => $user->id, 'site_url' => 'https://shop.example.com', 'status' => WooStore::STATUS_ACTIVE, 'connected_at' => now(), 'plugin_token_hash' => hash('sha256', 'test-token'), ]); $payload = [ 'id' => 42, 'number' => '1042', 'status' => 'processing', 'currency' => 'GHS', 'total' => '120.50', 'billing' => [ 'first_name' => 'Ama', 'last_name' => 'Mensah', 'email' => 'ama@example.com', 'phone' => '0244123456', ], 'line_items' => [ ['name' => 'T-shirt', 'quantity' => 2, 'total' => '120.50', 'sku' => 'TS-1'], ], 'needs_payment' => false, 'date_modified_gmt' => now()->utc()->format('Y-m-d H:i:s'), ]; $body = json_encode($payload, JSON_THROW_ON_ERROR); $signature = base64_encode(hash_hmac('sha256', $body, (string) $store->webhook_secret, true)); $this->call( 'POST', route('woo.webhooks.woocommerce', $store->public_id), [], [], [], [ 'CONTENT_TYPE' => 'application/json', 'HTTP_X_WC_WEBHOOK_SIGNATURE' => $signature, 'HTTP_X_WC_WEBHOOK_TOPIC' => 'order.updated', ], $body, )->assertOk(); $order = WooOrder::query()->where('external_order_id', 42)->first(); $this->assertNotNull($order); $this->assertSame('ama@example.com', $order->customer_email); $this->assertSame(12050, $order->total_minor); } public function test_merchant_can_update_fulfillment_status(): void { $user = User::factory()->create(); $store = WooStore::create([ 'user_id' => $user->id, 'site_url' => 'https://shop.example.com', 'status' => WooStore::STATUS_ACTIVE, ]); $order = WooOrder::create([ 'woo_store_id' => $store->id, 'external_order_id' => 7, 'order_number' => '1007', 'line_items' => [['name' => 'Item', 'quantity' => 1, 'total_minor' => 1000]], 'currency' => 'GHS', 'total_minor' => 1000, ]); $this->actingAs($user) ->patch(route('woo.orders.update-status', $order), [ 'fulfillment_status' => WooOrder::FULFILLMENT_PREPARING, ]) ->assertRedirect(); $this->assertSame(WooOrder::FULFILLMENT_PREPARING, $order->fresh()->fulfillment_status); } public function test_product_webhook_ingests_product(): void { $user = User::factory()->create(); $store = WooStore::create([ 'user_id' => $user->id, 'site_url' => 'https://shop.example.com', 'status' => WooStore::STATUS_ACTIVE, 'connected_at' => now(), ]); $payload = [ 'id' => 101, 'name' => 'Hoodie', 'slug' => 'hoodie', 'sku' => 'HD-1', 'status' => 'publish', 'regular_price' => '150.00', 'categories' => [['id' => 12, 'name' => 'Apparel']], ]; $body = json_encode($payload, JSON_THROW_ON_ERROR); $signature = base64_encode(hash_hmac('sha256', $body, (string) $store->webhook_secret, true)); $this->call( 'POST', route('woo.webhooks.woocommerce', $store->public_id), [], [], [], [ 'CONTENT_TYPE' => 'application/json', 'HTTP_X_WC_WEBHOOK_SIGNATURE' => $signature, 'HTTP_X_WC_WEBHOOK_TOPIC' => 'product.updated', ], $body, )->assertOk(); $this->assertDatabaseHas('woo_products', [ 'woo_store_id' => $store->id, 'external_id' => 101, 'name' => 'Hoodie', 'sku' => 'HD-1', ]); } public function test_category_webhook_ingests_category(): void { $user = User::factory()->create(); $store = WooStore::create([ 'user_id' => $user->id, 'site_url' => 'https://shop.example.com', 'status' => WooStore::STATUS_ACTIVE, 'connected_at' => now(), ]); $payload = [ 'id' => 12, 'name' => 'Apparel', 'slug' => 'apparel', 'parent' => 0, 'count' => 4, ]; $body = json_encode($payload, JSON_THROW_ON_ERROR); $signature = base64_encode(hash_hmac('sha256', $body, (string) $store->webhook_secret, true)); $this->call( 'POST', route('woo.webhooks.woocommerce', $store->public_id), [], [], [], [ 'CONTENT_TYPE' => 'application/json', 'HTTP_X_WC_WEBHOOK_SIGNATURE' => $signature, 'HTTP_X_WC_WEBHOOK_TOPIC' => 'product_category.created', ], $body, )->assertOk(); $this->assertDatabaseHas('woo_categories', [ 'woo_store_id' => $store->id, 'external_id' => 12, 'name' => 'Apparel', ]); } public function test_authenticated_user_can_view_settings(): void { $user = User::factory()->create(); $this->actingAs($user) ->get(route('woo.settings')) ->assertOk() ->assertSee('Settings') ->assertSee('Manage connected stores'); } public function test_store_activation_dispatches_bootstrap_sync(): void { \Illuminate\Support\Facades\Bus::fake(); $user = User::factory()->create(); $store = WooStore::create([ 'user_id' => $user->id, 'site_url' => 'https://shop.example.com', 'status' => WooStore::STATUS_PENDING, ]); $token = app(InstallTokenService::class)->issue($store)['token']; $this->postJson('/api/v1/stores/activate', [ 'install_token' => $token, 'site_url' => 'https://shop.example.com', ])->assertOk(); \Illuminate\Support\Facades\Bus::assertDispatched(\App\Jobs\StoreBootstrapSync::class, fn ($job) => $job->storeId === $store->id); } public function test_webhook_accepts_post_without_csrf_token(): void { $user = User::factory()->create(); $store = WooStore::create([ 'user_id' => $user->id, 'site_url' => 'https://shop.example.com', 'status' => WooStore::STATUS_ACTIVE, 'connected_at' => now(), ]); $payload = ['id' => 99, 'number' => '1099', 'status' => 'processing', 'currency' => 'GHS', 'total' => '10.00', 'billing' => ['email' => 'a@b.com'], 'line_items' => [['name' => 'Item', 'quantity' => 1, 'total' => '10.00']], 'needs_payment' => false]; $body = json_encode($payload, JSON_THROW_ON_ERROR); $signature = base64_encode(hash_hmac('sha256', $body, (string) $store->webhook_secret, true)); $this->withMiddleware(\Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class) ->call( 'POST', route('woo.webhooks.woocommerce', $store->public_id), [], [], [], [ 'CONTENT_TYPE' => 'application/json', 'HTTP_X_WC_WEBHOOK_SIGNATURE' => $signature, 'HTTP_X_WC_WEBHOOK_TOPIC' => 'order.updated', ], $body, )->assertOk(); $this->assertDatabaseHas('woo_orders', ['external_order_id' => 99]); } public function test_merchant_can_view_products_page(): void { $user = User::factory()->create(); $store = WooStore::create([ 'user_id' => $user->id, 'site_url' => 'https://shop.example.com', 'status' => WooStore::STATUS_ACTIVE, ]); \App\Models\WooProduct::create([ 'woo_store_id' => $store->id, 'external_id' => 1, 'name' => 'Sample Tee', 'slug' => 'sample-tee', 'status' => 'publish', ]); $this->actingAs($user) ->get(route('woo.products.index')) ->assertOk() ->assertSee('Sample Tee'); } }