product_id = $product_id; } public function get_product_id() { return $this->product_id; } } class WC_Order { private $data; public function __construct( $data ) { $this->data = $data; } public function get_items() { return $this->data['items']; } public function get_customer_id() { return $this->data['customer_id']; } public function get_id() { return $this->data['id']; } public function get_total() { return $this->data['total']; } public function get_currency() { return $this->data['currency']; } } function add_action( $hook, $callback, $priority = 10, $args = 1 ) { $GLOBALS['attr_actions'][] = array( $hook, $callback, $priority, $args ); } function get_option( $name, $default = false ) { return $GLOBALS['attr_options'][ $name ] ?? $default; } function apply_filters( $hook, $value ) { return $value; } function untrailingslashit( $url ) { return rtrim( $url, '/' ); } function esc_url_raw( $url ) { return $url; } function wc_get_order( $id ) { return $GLOBALS['attr_orders'][ $id ] ?? null; } function wp_unslash( $value ) { return $value; } function wp_json_encode( $value ) { return json_encode( $value ); } function wp_remote_post( $url, $args ) { $GLOBALS['attr_posts'][] = array( $url, $args ); return array(); } require_once __DIR__ . '/../includes/class-plugin.php'; require_once __DIR__ . '/../includes/class-storefront.php'; require_once __DIR__ . '/../includes/class-attribution.php'; use Aisthetix\TryOn\Attribution; use Aisthetix\TryOn\Plugin; use Aisthetix\TryOn\Storefront; $passed = 0; $failed = 0; function attr_check( $name, $condition ) { global $passed, $failed; if ( $condition ) { echo " ✓ {$name}\n"; ++$passed; return; } echo " ✗ {$name}\n"; ++$failed; } function attr_configure( $connected, $analytics ) { $GLOBALS['attr_options'][ Plugin::OPTION_PUBLISHABLE_KEY ] = $connected ? 'pk' : ''; $GLOBALS['attr_options'][ Plugin::OPTION_SECRET_KEY ] = $connected ? 'sk' : ''; $GLOBALS['attr_options'][ Plugin::OPTION_SETTINGS ] = array( 'enable_analytics' => $analytics ); } echo "Attribution\n"; $attribution = new Attribution(); $attribution->init(); attr_check( 'registers the paid-order hook at the intended priority', array( 'woocommerce_payment_complete', array( $attribution, 'on_payment_complete' ), 20, 1 ) === $GLOBALS['attr_actions'][0] ); attr_configure( false, true ); $attribution->on_payment_complete( 7 ); attr_configure( true, false ); $attribution->on_payment_complete( 7 ); attr_configure( true, true ); $attribution->on_payment_complete( 0 ); $attribution->on_payment_complete( 999 ); attr_check( 'disconnect, opt-out, invalid id, and missing order all suppress delivery', 0 === count( $GLOBALS['attr_posts'] ) ); $order = new WC_Order( array( 'id' => 77, 'customer_id' => 42, 'total' => '123.45', 'currency' => 'EUR', 'items' => array( new WC_Order_Item_Product( 10 ), new WC_Order_Item_Product( 10 ), new WC_Order_Item_Product( 11 ), new WC_Order_Item_Product( 0 ), new stdClass() ), ) ); $GLOBALS['attr_orders'][77] = $order; $_COOKIE[ Storefront::VISITOR_COOKIE ] = str_repeat( 'a', 32 ); $attribution->on_payment_complete( 77 ); $post = $GLOBALS['attr_posts'][0]; $payload = json_decode( $post[1]['body'], true ); attr_check( 'posts non-blocking to the authenticated order endpoint', false === $post[1]['blocking'] && 'sk' === $post[1]['headers']['X-Aisthetix-Secret'] && false !== strpos( $post[0], '/api/storefront/order' ) ); attr_check( 'uses gross total and stable order/customer/visitor ids', '77' === $payload['orderId'] && '42' === $payload['customerId'] && str_repeat( 'a', 32 ) === $payload['visitorKey'] && 123.45 === $payload['value'] ); attr_check( 'deduplicates positive canonical product ids', array( '10', '11' ) === $payload['productIds'] ); $guest = new WC_Order( array( 'id' => 78, 'customer_id' => 0, 'total' => 0, 'currency' => 'USD', 'items' => array() ) ); $GLOBALS['attr_orders'][78] = $guest; $_COOKIE[ Storefront::VISITOR_COOKIE ] = 'invalid'; $attribution->on_payment_complete( 78 ); $guest_payload = json_decode( $GLOBALS['attr_posts'][1][1]['body'], true ); attr_check( 'guest and malformed-cookie paths use honest null keys', null === $guest_payload['customerId'] && null === $guest_payload['visitorKey'] ); unset( $_COOKIE[ Storefront::VISITOR_COOKIE ] ); $attribution->on_payment_complete( 78 ); $absent_payload = json_decode( $GLOBALS['attr_posts'][2][1]['body'], true ); attr_check( 'absent visitor cookie also remains null', null === $absent_payload['visitorKey'] ); echo "\n{$passed} passed, {$failed} failed\n"; exit( $failed > 0 ? 1 : 0 );