diff --git a/src/Events/OrderPlaced.php b/src/Events/OrderPlaced.php index b265409..8deefd1 100644 --- a/src/Events/OrderPlaced.php +++ b/src/Events/OrderPlaced.php @@ -26,7 +26,8 @@ function hellotext_order_placed(\WC_Order $order): void { ? sanitize_text_field($_COOKIE[Constants::SESSION_COOKIE_NAME]) : null; $encrypted_session = Session::encrypt($session); - add_post_meta($order->get_id(), Constants::META_SESSION, $encrypted_session); + $order->update_meta_data(Constants::META_SESSION, $encrypted_session); + $order->save(); $event->track(Constants::EVENT_ORDER_PLACED, [ 'object_parameters' => $parsedOrder, diff --git a/src/Events/OrderStatus.php b/src/Events/OrderStatus.php index c40fad1..c88ad10 100644 --- a/src/Events/OrderStatus.php +++ b/src/Events/OrderStatus.php @@ -17,7 +17,7 @@ * @return void */ function track_order_status(int $order_id, string $old_status, string $new_status, \WC_Order $order): void { - $encrypted_session = get_post_meta($order_id, Constants::META_SESSION, true); + $encrypted_session = $order->get_meta(Constants::META_SESSION, true); $session = Session::decrypt($encrypted_session); $orderAdapter = new OrderAdapter($order); diff --git a/src/Events/RefundReceived.php b/src/Events/RefundReceived.php index 666e3ed..bcdc3f8 100644 --- a/src/Events/RefundReceived.php +++ b/src/Events/RefundReceived.php @@ -20,7 +20,7 @@ function hellotext_refund_created(int $order_id, int $refund_id): void { do_action('hellotext_create_profile', $order->get_user_id()); - $encrypted_session = get_post_meta($order_id, Constants::META_SESSION, true); + $encrypted_session = $order->get_meta(Constants::META_SESSION, true); $session = Session::decrypt($encrypted_session); (new Event($session))->track(Constants::EVENT_REFUND_RECEIVED, [ diff --git a/tests/Mocks.php b/tests/Mocks.php index f045b0d..2967101 100644 --- a/tests/Mocks.php +++ b/tests/Mocks.php @@ -1,17 +1,350 @@ $callback, + 'accepted_args' => $accepted_args, + ]; + + return true; +} + +function do_action($hook_name, ...$args) +{ + foreach ($GLOBALS['test_actions'][$hook_name] ?? [] as $action) { + call_user_func_array($action['callback'], array_slice($args, 0, $action['accepted_args'])); + } +} + +function register_activation_hook($file, $callback) +{ + $GLOBALS['test_activation_hook'] = $callback; +} + +function register_deactivation_hook($file, $callback) +{ + $GLOBALS['test_deactivation_hook'] = $callback; +} + +function register_uninstall_hook($file, $callback) +{ + $GLOBALS['test_uninstall_hook'] = $callback; +} + +function sanitize_text_field($value) +{ + return is_scalar($value) ? trim((string) $value) : ''; +} + +function site_url($path = '') +{ + return 'https://example.test' . $path; +} + +function home_url($path = '') +{ + return 'https://example.test/' . ltrim($path, '/'); +} + +function add_query_arg($args = [], $url = '') +{ + return $url; +} + +function wp_get_post_terms($post_id, $taxonomy, $args = []) +{ + return []; +} + +function wp_get_attachment_url($attachment_id) +{ + return 'https://example.test/image.jpg'; +} + +function get_permalink($post_id) +{ + return 'https://example.test/product/' . $post_id; +} + +function load_plugin_textdomain($domain, $deprecated = false, $plugin_rel_path = false) +{ + return true; +} + +function is_admin() +{ + return false; +} + +function get_transient($transient) +{ + return $GLOBALS['test_transients'][$transient] ?? false; +} + +function set_transient($transient, $value, $expiration = 0) +{ + $GLOBALS['test_transients'][$transient] = $value; + + return true; +} + +function delete_transient($transient) +{ + unset($GLOBALS['test_transients'][$transient]); + + return true; +} + +function wp_kses_post($data) +{ + return $data; +} + +function wp_kses($data, $allowed_html = []) +{ + return $data; +} + +function __($text, $domain = 'default') +{ + return $text; +} + +function _e($text, $domain = 'default') +{ + echo $text; +} + +function esc_attr($text) +{ + return htmlspecialchars((string) $text, ENT_QUOTES); +} + +function esc_html($text) +{ + return htmlspecialchars((string) $text, ENT_QUOTES); +} + +function selected($selected, $current = true, $echo = true) +{ + $result = $selected == $current ? ' selected="selected"' : ''; + if ($echo) { + echo $result; + } + + return $result; +} + +function add_settings_section() +{ + return true; +} + +function add_settings_field() +{ + return true; +} + +function register_setting() +{ + return true; +} + +function add_submenu_page() +{ + return true; +} + +function settings_fields() +{ + return true; +} + +function do_settings_sections() +{ + return true; +} + +function submit_button() +{ + return true; +} + +function get_current_user_id() +{ + return 1; +} + +function get_bloginfo($show = '') +{ + return match ($show) { + 'name' => 'Test Store', + 'url' => 'https://example.test', + 'admin_email' => 'admin@example.test', + default => 'Test Store', + }; +} + +function wc_rand_hash() +{ + return 'testhash'; +} + +function wc_api_hash($data) +{ + return 'hashed_' . $data; +} + +function get_user_by($field, $value) +{ + $users = $GLOBALS['test_users'] ?? []; + + if ('id' === $field) { + return $users[$value] ?? null; + } + + if ('email' === $field) { + foreach ($users as $user) { + if (($user->user_email ?? null) === $value) { + return $user; + } + } + } + + return null; +} + +function wp_create_user($name, $password, $email) +{ + $user = new WP_User(count($GLOBALS['test_users'] ?? []) + 1); + $user->nickname = $name; + $user->last_name = ''; + $user->user_email = $email; + $GLOBALS['test_users'][$user->ID] = $user; + + return $user; +} + +function wp_get_current_user() +{ + return new WP_User(1); +} + +function is_user_logged_in() +{ + return $GLOBALS['test_is_user_logged_in'] ?? true; +} + +function get_user_meta($user_id, $key = '', $single = false) +{ + if ('' === $key) { + return $GLOBALS['test_user_meta'][$user_id] ?? []; + } + + return $GLOBALS['test_user_meta'][$user_id][$key] ?? ''; +} + +function add_user_meta($user_id, $meta_key, $meta_value, $unique = false) +{ + $GLOBALS['test_user_meta'][$user_id][$meta_key] = $meta_value; + + return true; +} + +function update_user_meta($user_id, $meta_key, $meta_value) +{ + $GLOBALS['test_user_meta'][$user_id][$meta_key] = $meta_value; + + return true; +} + +function plugin_basename($file) +{ + return basename($file); +} + +function delete_option($option) +{ + unset($GLOBALS['test_options'][$option]); + + return true; +} + +function add_post_meta($post_id, $meta_key, $meta_value, $unique = false) +{ + $GLOBALS['test_post_meta'][$post_id][$meta_key] = $meta_value; + + return true; +} + +function update_post_meta($post_id, $meta_key, $meta_value) +{ + $GLOBALS['test_post_meta'][$post_id][$meta_key] = $meta_value; + + return true; +} + +function get_post_meta($post_id, $key = '', $single = false) +{ + if ('' === $key) { + return $GLOBALS['test_post_meta'][$post_id] ?? []; + } + + return $GLOBALS['test_post_meta'][$post_id][$key] ?? ''; +} + +function wc_load_cart() +{ + return true; +} + +function WC() +{ + if (!isset($GLOBALS['test_woocommerce'])) { + $GLOBALS['test_woocommerce'] = (object) [ + 'cart' => new WC_Cart(), + ]; + } + + return $GLOBALS['test_woocommerce']; +} + // Mock WooCommerce wc_create_order function -function wc_create_order ($args = array()) { +function wc_create_order($args = array()) +{ return new WC_Order(); } // Mock WooCommerce wc_create_refund function -function wc_create_refund ($args = array()) { +function wc_create_refund($args = array()) +{ return new WC_Order_Refund($args); } // Mock WooCommerce wc_get_product function -function wc_get_product ($id = 0) { +function wc_get_product($id = 0) +{ // If already a WC_Product, return it if ($id instanceof WC_Product) { return $id; @@ -25,7 +358,8 @@ function wc_get_product ($id = 0) { return new WC_Product(); } -function wc_get_order ($id = 0) { +function wc_get_order($id = 0) +{ // If already a WC_Order, return it if ($id instanceof WC_Order) { return $id; @@ -39,88 +373,152 @@ function wc_get_order ($id = 0) { return new WC_Order(); } -function get_woocommerce_currency () { +function get_woocommerce_currency() +{ return 'USD'; } -class WC_Order { - public function get_id () { +class WC_Order +{ + public $data = [ + 'billing' => [ + 'first_name' => 'Jane', + 'last_name' => 'Doe', + 'email' => 'jane@example.test', + 'phone' => '+15555550100', + ], + ]; + + private array $meta_data = []; + + public function get_id() + { + return 1; + } + + public function get_user_id() + { return 1; } - public function get_total () { + public function get_total() + { return 50; } - public function get_currency () { + public function get_currency() + { return 'USD'; } - public function add_product ($product, $quantity) { + public function add_product($product, $quantity) + { $item = new WC_Order_Item_Product(); $item->set_quantity($quantity); return $item; } - public function get_items () { + public function get_items() + { return [ new WC_Order_Item_Product() ]; } - public function apply_changes () { + public function apply_changes() + { return true; } - public function save () { + public function save() + { + $GLOBALS['test_saved_orders'][$this->get_id()] = true; + return true; } + + public function add_meta_data($key, $value, $unique = false) + { + $this->meta_data[$key] = $value; + $GLOBALS['test_post_meta'][$this->get_id()][$key] = $value; + } + + public function update_meta_data($key, $value) + { + $this->add_meta_data($key, $value); + } + + public function get_meta($key = '', $single = true) + { + if (isset($this->meta_data[$key])) { + return $this->meta_data[$key]; + } + + return $GLOBALS['test_post_meta'][$this->get_id()][$key] ?? ''; + } } -class WC_Order_Refund { +class WC_Order_Refund +{ public $amount; public $order_id; - public function __construct ($args) { - $this->amount = $args['amount']; - $this->order_id = $args['order_id']; + public function __construct($args = []) + { + if (is_array($args)) { + $this->amount = $args['amount'] ?? 25; + $this->order_id = $args['order_id'] ?? 1; + return; + } + + $this->amount = 25; + $this->order_id = 1; } - public function get_id () { + public function get_id() + { return 1; } - public function get_amount () { + public function get_amount() + { return $this->amount; } - public function get_currency () { + public function get_currency() + { return 'USD'; } } -class WC_Order_Item_Product { +class WC_Order_Item_Product +{ public $quantity; - public function get_product_id () { + public function get_product_id() + { return 1; } - public function set_quantity ($quantity) { + public function set_quantity($quantity) + { $this->quantity = $quantity; } - public function get_quantity () { + public function get_quantity() + { return $this->quantity; } - public function get_product () { + public function get_product() + { return new WC_Product(); } } -class WC_Product { +class WC_Product +{ public $reference; public $type; public $name; @@ -132,49 +530,118 @@ class WC_Product { public $fields; public $image_url; - public function save () { + public function save() + { } - public function get_id () { + public function get_id() + { return 1; } // getters - public function get_name () { + public function get_name() + { return 'simple'; } - public function get_price () { + public function get_price() + { return 50; } - public function get_image_id () { + public function get_image_id() + { return 1; } - public function set_props ($props) { + public function set_props($props) + { foreach ($props as $key => $value) { $this->$key = $value; } } } -class User { +class WC_Coupon +{ + public function __construct(public string $code) + { + } + + public function get_id() + { + return 10; + } + + public function get_description() + { + return 'Test coupon'; + } +} + +class WC_Discounts +{ + public function is_coupon_valid($coupon) + { + return $GLOBALS['test_coupon_valid'] ?? true; + } +} + +class WC_Cart +{ + public array $items = []; + + public function get_cart() + { + return $this->items; + } + + public function get_cart_contents_total() + { + return 50; + } +} + +class User +{ public $ID; + public string $nickname = ''; + public string $last_name = ''; + public string $user_email = ''; - public function __construct ($id) { + public function __construct($id) + { $this->ID = $id; } - public function get_id () { + public function get_id() + { return $this->ID; } } +class WP_User extends User +{ +} + +class WP_Error +{ + public function __construct(private string $message = 'Test error') + { + } + + public function get_error_message() + { + return $this->message; + } +} + // Mock WordPress HTTP API functions if (!function_exists('wp_remote_request')) { - function wp_remote_request ($url, $args = array()) { + function wp_remote_request($url, $args = array()) + { // Allow per-test override if (isset($GLOBALS['test_wp_remote_request'])) { return $GLOBALS['test_wp_remote_request']($url, $args); @@ -188,37 +655,43 @@ function wp_remote_request ($url, $args = array()) { } if (!function_exists('wp_remote_post')) { - function wp_remote_post ($url, $args = array()) { + function wp_remote_post($url, $args = array()) + { return wp_remote_request($url, $args); } } if (!function_exists('wp_remote_get')) { - function wp_remote_get ($url, $args = array()) { + function wp_remote_get($url, $args = array()) + { return wp_remote_request($url, $args); } } if (!function_exists('is_wp_error')) { - function is_wp_error ($thing) { + function is_wp_error($thing) + { return $thing instanceof WP_Error; } } if (!function_exists('wp_remote_retrieve_response_code')) { - function wp_remote_retrieve_response_code ($response) { + function wp_remote_retrieve_response_code($response) + { return $response['response']['code'] ?? 200; } } if (!function_exists('wp_remote_retrieve_body')) { - function wp_remote_retrieve_body ($response) { + function wp_remote_retrieve_body($response) + { return $response['body'] ?? ''; } } if (!function_exists('get_option')) { - function get_option ($option, $default = false) { + function get_option($option, $default = false) + { // Allow per-test override if (isset($GLOBALS['test_options'][$option])) { return $GLOBALS['test_options'][$option]; @@ -229,12 +702,17 @@ function get_option ($option, $default = false) { return 'test_token_123'; } + if ($option === 'hellotext_business_id') { + return 'test_business_123'; + } + return $default; } } if (!function_exists('get_plugin_data')) { - function get_plugin_data ($plugin_file, $markup = true, $translate = true) { + function get_plugin_data($plugin_file, $markup = true, $translate = true) + { // Allow per-test override if (isset($GLOBALS['test_plugin_data'])) { return $GLOBALS['test_plugin_data']; @@ -242,7 +720,7 @@ function get_plugin_data ($plugin_file, $markup = true, $translate = true) { return [ 'Name' => 'Hellotext', - 'Version' => '1.3.0', + 'Version' => '1.3.1', 'Author' => 'Hellotext', ]; } diff --git a/tests/Pest.php b/tests/Pest.php index 644af5b..bfba31e 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -3,7 +3,6 @@ $ds = DIRECTORY_SEPARATOR; // require_once dirname( __FILE__, 2 ) . $ds . '../../../wp-load.php'; require_once dirname( __FILE__, 2 ) . $ds . 'vendor' . $ds . 'autoload.php'; -require_once dirname( __FILE__, 2 ) . $ds . 'vendor' . $ds . 'php-stubs' . $ds . 'wordpress-stubs' . $ds . 'wordpress-stubs.php'; require_once dirname( __FILE__, 2 ) . $ds . 'tests'. $ds . 'Mocks.php'; require_once dirname( __FILE__, 2 ) . $ds . 'hellotext.php'; diff --git a/tests/Unit/Adapters/RefundAdapterTest.php b/tests/Unit/Adapters/RefundAdapterTest.php index 5cf6a31..26ef1fa 100644 --- a/tests/Unit/Adapters/RefundAdapterTest.php +++ b/tests/Unit/Adapters/RefundAdapterTest.php @@ -1,8 +1,7 @@ $user->ID ]); - add_post_meta($this->order->get_id(), 'hellotext_session', Session::encrypt('123')); + $this->order->update_meta_data(Constants::META_SESSION, Session::encrypt('123')); + $this->order->save(); $this->refund = wc_create_refund([ 'amount' => $this->order->get_total() / 2, diff --git a/tests/Unit/Events/EventTrackingTest.php b/tests/Unit/Events/EventTrackingTest.php new file mode 100644 index 0000000..989ece0 --- /dev/null +++ b/tests/Unit/Events/EventTrackingTest.php @@ -0,0 +1,243 @@ + 'cart']; + $_COOKIE = []; + $_SESSION = []; + + $GLOBALS['test_wp_remote_request'] = function ($url, $args = []) { + $GLOBALS['test_http_requests'][] = [ + 'url' => $url, + 'args' => $args, + 'body' => json_decode($args['body'] ?? '{}', true), + ]; + + return [ + 'response' => ['code' => 200], + 'body' => json_encode(['success' => true, 'id' => 'profile_123']), + ]; + }; +}); + +function tracked_request(int $index = -1): array +{ + $requests = $GLOBALS['test_http_requests']; + + return $requests[$index < 0 ? count($requests) + $index : $index]; +} + +function tracked_event(int $index = -1): array +{ + $requests = $GLOBALS['test_http_requests']; + + return $requests[$index < 0 ? count($requests) + $index : $index]['body']; +} + +test('tracks product viewed events', function () { + global $product; + + $_COOKIE[Constants::SESSION_COOKIE_NAME] = 'browser-session'; + $product = new WC_Product(); + + do_action('woocommerce_after_single_product'); + + $event = tracked_event(); + $request = tracked_request(); + + expect($event['action'])->toBe(Constants::EVENT_PRODUCT_VIEWED) + ->and($event['session'])->toBe('browser-session') + ->and($event['object_parameters']['reference'])->toBe(1) + ->and($event['object_parameters']['source'])->toBe('woo') + ->and($request['url'])->toEndWith(Constants::API_ENDPOINT_TRACK) + ->and($request['args']['headers']['Content-Type'])->toBe('application/json') + ->and($request['args']['headers']['Authorization'])->toBe('Bearer test_business_123'); +}); + +test('tracks valid coupon redemption only', function () { + do_action('woocommerce_applied_coupon', 'SAVE10'); + + $event = tracked_event(); + + expect($event['action'])->toBe(Constants::EVENT_COUPON_REDEEMED) + ->and($event['object_parameters']['type'])->toBe('coupon') + ->and($event['object_parameters']['code'])->toBe('SAVE10') + ->and($event['object_parameters']['destination_url'])->toBe('https://example.test/cart'); + + $GLOBALS['test_http_requests'] = []; + $GLOBALS['test_coupon_valid'] = false; + + do_action('woocommerce_applied_coupon', 'EXPIRED'); + + expect($GLOBALS['test_http_requests'])->toBe([]); +}); + +test('tracks cart added and removed events from cart hooks', function () { + $_COOKIE[Constants::SESSION_COOKIE_NAME] = 'cart-session'; + $GLOBALS['test_is_user_logged_in'] = false; + $_SESSION[Constants::SESSION_CART_ITEMS] = json_encode([]); + WC()->cart->items = [ + 'abc' => [ + 'data' => new WC_Product(), + 'quantity' => 2, + ], + ]; + + do_action('woocommerce_add_to_cart'); + + $added = tracked_event(); + expect($added['action'])->toBe(Constants::EVENT_CART_ADDED) + ->and($added['session'])->toBe('cart-session') + ->and($added['amount'])->toBe(50) + ->and($added['currency'])->toBe('USD') + ->and($added['url'])->toBe('https://example.test/cart') + ->and($added['object_parameters']['items'][0]['quantity'])->toBe(2) + ->and($added['object_parameters']['items'][0]['product']['reference'])->toBe(1); + + $GLOBALS['test_http_requests'] = []; + WC()->cart->items = []; + + do_action('woocommerce_cart_item_removed'); + + $removed = tracked_event(); + expect($removed['action'])->toBe(Constants::EVENT_CART_REMOVED) + ->and($removed['session'])->toBe('cart-session') + ->and($removed['object_parameters']['items'][0]['quantity'])->toBe(2) + ->and($removed['object_parameters']['items'][0]['product']['reference'])->toBe(1); +}); + +test('tracks cart quantity increases and ignores unchanged carts', function () { + $_COOKIE[Constants::SESSION_COOKIE_NAME] = 'cart-session'; + $GLOBALS['test_is_user_logged_in'] = false; + $_SESSION[Constants::SESSION_CART_ITEMS] = json_encode([ + [ + 'product' => [ + 'reference' => 1, + 'source' => 'woo', + ], + 'quantity' => 1, + ], + ]); + WC()->cart->items = [ + 'abc' => [ + 'data' => new WC_Product(), + 'quantity' => 3, + ], + ]; + + do_action('woocommerce_after_cart_item_quantity_update'); + + expect(tracked_event()['action'])->toBe(Constants::EVENT_CART_ADDED); + + $GLOBALS['test_http_requests'] = []; + + hellotext_cart_updated(); + + expect($GLOBALS['test_http_requests'])->toBe([]); +}); + +test('tracks order placed and stores encrypted session with order CRUD', function () { + $_COOKIE[Constants::SESSION_COOKIE_NAME] = 'checkout-session'; + $order = new WC_Order(); + + hellotext_order_placed($order); + + $event = tracked_event(); + $encrypted_session = $order->get_meta(Constants::META_SESSION, true); + + expect($event['action'])->toBe(Constants::EVENT_ORDER_PLACED) + ->and($event['session'])->toBe('checkout-session') + ->and($event['object_parameters']['reference'])->toBe(1) + ->and(Session::decrypt($encrypted_session))->toBe('checkout-session') + ->and($GLOBALS['test_saved_orders'][1])->toBeTrue(); +}); + +test('tracks supported order status transitions with stored session', function (string $status, string $action) { + $order = new WC_Order(); + $order->update_meta_data(Constants::META_SESSION, Session::encrypt('stored-session')); + + do_action('woocommerce_order_status_changed', $order->get_id(), 'pending', $status, $order); + + $event = tracked_event(); + + expect($event['action'])->toBe($action) + ->and($event['session'])->toBe('stored-session') + ->and($event['object_parameters']['reference'])->toBe(1); +})->with([ + 'processing' => ['processing', Constants::EVENT_ORDER_CONFIRMED], + 'cancelled' => ['cancelled', Constants::EVENT_ORDER_CANCELLED], + 'completed' => ['completed', Constants::EVENT_ORDER_DELIVERED], + ]); + +test('does not track unsupported order status transitions', function () { + $order = new WC_Order(); + $order->update_meta_data(Constants::META_SESSION, Session::encrypt('stored-session')); + + do_action('woocommerce_order_status_changed', $order->get_id(), 'pending', 'on-hold', $order); + + expect($GLOBALS['test_http_requests'])->toBe([]); +}); + +test('tracks refunds with the order session', function () { + $order = new WC_Order(); + $order->update_meta_data(Constants::META_SESSION, Session::encrypt('refund-session')); + + do_action('woocommerce_order_refunded', $order->get_id(), 1); + + $event = tracked_event(); + + expect($event['action'])->toBe(Constants::EVENT_REFUND_RECEIVED) + ->and($event['session'])->toBe('refund-session') + ->and($event['object_parameters']['reference'])->toBe(1) + ->and($event['object_parameters']['source'])->toBe('woo'); +}); + +test('creates a profile when a user registration hook fires', function () { + $_COOKIE[Constants::SESSION_COOKIE_NAME] = 'registration-session'; + $user = new WP_User(42); + $user->nickname = 'Jane'; + $user->last_name = 'Doe'; + $user->user_email = 'jane@example.test'; + $GLOBALS['test_users'][$user->ID] = $user; + $GLOBALS['test_user_meta'][$user->ID]['billing_phone'] = '+15555550100'; + + do_action('user_register', $user->ID); + + $profile_request = tracked_request(0); + $session_request = tracked_request(1); + + expect($profile_request['url'])->toEndWith('/profiles') + ->and($profile_request['args']['method'])->toBe('POST') + ->and($profile_request['args']['headers']['Authorization'])->toBe('Bearer test_token_123') + ->and($profile_request['args']['headers']['Content-Type'])->toBe('application/json') + ->and($profile_request['args']['headers']['X-Plugin-Version'])->toBe('1.3.1') + ->and($profile_request['body']['session'])->toBe('registration-session') + ->and($profile_request['body']['reference'])->toBe(42) + ->and($profile_request['body']['email'])->toBe('jane@example.test') + ->and($GLOBALS['test_user_meta'][$user->ID][Constants::META_PROFILE_ID])->toBe('profile_123') + ->and($session_request['url'])->toEndWith('/sessions/registration-session') + ->and($session_request['args']['method'])->toBe('PATCH') + ->and($session_request['body']['profile'])->toBe('profile_123'); +}); + +test('deactivation removes the Hellotext WooCommerce integration', function () { + hellotext_deactivate('business_456'); + + $request = tracked_request(); + + expect($request['url'])->toEndWith('/integrations/woo') + ->and($request['args']['method'])->toBe('DELETE') + ->and($request['args']['headers']['Authorization'])->toBe('Bearer test_token_123') + ->and($request['args']['headers']['Content-Type'])->toBe('application/json') + ->and($request['args']['headers']['X-Plugin-Version'])->toBe('1.3.1') + ->and($request['body']['shop']['business_id'])->toBe('business_456'); +});