array( 'code' => 200 ), 'body' => '{}' ); $GLOBALS['status_gets'] = array(); $GLOBALS['status_ttl'] = null; class WP_Error { private $message; public function __construct( $code, $message = '' ) { $this->message = $message; } public function get_error_message() { return $this->message; } } function __( $text ) { return $text; } function get_option( $name, $default = false ) { return $GLOBALS['status_options'][ $name ] ?? $default; } function apply_filters( $hook, $value ) { return $value; } function untrailingslashit( $url ) { return rtrim( $url, '/' ); } function esc_url_raw( $url ) { return $url; } function sanitize_text_field( $value ) { return trim( strip_tags( (string) $value ) ); } function sanitize_title( $value ) { return strtolower( trim( preg_replace( '/[^a-z0-9]+/i', '-', $value ), '-' ) ); } function get_transient( $name ) { return $GLOBALS['status_transients'][ $name ] ?? false; } function set_transient( $name, $value, $ttl ) { $GLOBALS['status_transients'][ $name ] = $value; $GLOBALS['status_ttl'] = $ttl; } function delete_transient( $name ) { unset( $GLOBALS['status_transients'][ $name ] ); } function wp_remote_get( $url, $args ) { $GLOBALS['status_gets'][] = array( $url, $args ); return $GLOBALS['status_response']; } function is_wp_error( $value ) { return $value instanceof WP_Error; } function wp_remote_retrieve_response_code( $response ) { return $response['response']['code'] ?? 0; } function wp_remote_retrieve_body( $response ) { return $response['body'] ?? ''; } require_once __DIR__ . '/../includes/class-plugin.php'; require_once __DIR__ . '/../includes/class-status.php'; use Aisthetix\TryOn\Plugin; use Aisthetix\TryOn\Status; $passed = 0; $failed = 0; function status_check( $name, $condition ) { global $passed, $failed; if ( $condition ) { echo " ✓ {$name}\n"; ++$passed; return; } echo " ✗ {$name}\n"; ++$failed; } function status_connect( $connected = true ) { $GLOBALS['status_options'][ Plugin::OPTION_PUBLISHABLE_KEY ] = $connected ? 'pk' : ''; $GLOBALS['status_options'][ Plugin::OPTION_SECRET_KEY ] = $connected ? 'sk' : ''; } function status_response( $body, $code = 200 ) { $GLOBALS['status_response'] = array( 'response' => array( 'code' => $code ), 'body' => is_string( $body ) ? $body : json_encode( $body ) ); } echo "Status\n"; $status = new Status(); $status->init(); status_connect( false ); $empty = $status->get(); status_check( 'disconnected status is safely inactive', false === $empty['active'] && 'Not connected' === $empty['error'] && false === $empty['replay']['enabled'] ); status_connect(); $cached = array( 'active' => true, 'withinQuota' => true, 'error' => null, 'tryonTargeting' => array( 'mode' => 'all' ) ); $GLOBALS['status_transients'][ Status::TRANSIENT ] = $cached; status_check( 'uses a warm array cache without network', $cached === $status->get() && 0 === count( $GLOBALS['status_gets'] ) ); status_response( array( 'active' => true, 'withinQuota' => true, 'plan' => '', 'includedTryOns' => '300', 'remaining' => '27', 'resetsAt' => ' 2026-10-01 ', 'trial' => array( 'cap' => '50', 'used' => '4', 'remaining' => '46', 'endsAt' => '' ), 'replay' => array( 'enabled' => true, 'key' => '', 'host' => 'https://ingest.example', 'environment' => ' prod ' ), 'review' => array( 'completed' => true ), 'tryonTargeting' => array( 'mode' => 'include', 'categories' => array( 'Summer Wear' ), 'productIds' => array( 10 ) ), 'size' => array( 'hasSizeGuide' => true, 'guides' => array( 'bad', array( 'id' => '', 'categories' => array( 'categories' => array( 'Dresses' ) ), 'labels' => array( '' ) ) ), 'targeting' => array( 'mode' => 'exclude', 'categories' => array(), 'productIds' => array( 99 ) ), 'fitPreferenceEnabled' => true, 'defaultFitPreference' => 'loose', 'fitOverrides' => array( 'bad', array( 'categoryId' => 'wc:product_cat:9', 'categoryLabel' => '', 'preference' => 'tight', 'priority' => 20000 ), array( 'categoryId' => 'wc:product_cat:9', 'preference' => 'loose' ), array( 'categoryId' => 'unstable', 'preference' => 'regular' ), ), ), ) ); $fresh = $status->get( true ); status_check( 'normalizes successful counts, trial, and review', 27 === $fresh['remaining'] && 46 === $fresh['trial']['remaining'] && true === $fresh['review']['completed'] ); status_check( 'normalizes targeting and guide content', 'summer-wear' === $fresh['tryonTargeting']['categories'][0] && 'dresses' === $fresh['size']['guides'][0]['categories']['categories'][0] ); status_check( 'keeps one valid stable fit override and clamps priority', 1 === count( $fresh['size']['fitOverrides'] ) && 9999 === $fresh['size']['fitOverrides'][0]['priority'] ); status_check( 'successful status uses full cache TTL', Status::CACHE_TTL === $GLOBALS['status_ttl'] ); status_check( 'should_render combines quota and targeting', true === $status->should_render( array( 'categories' => array( 'summer-wear' ), 'productId' => '5' ) ) ); status_check( 'include targeting rejects unmatched products', false === $status->should_render( array( 'categories' => array( 'winter' ), 'productId' => '5' ) ) ); $GLOBALS['status_response'] = new WP_Error( 'offline', 'network down' ); $failed_status = $status->get( true ); status_check( 'transport failure returns a safe normalized status', 'network down' === $failed_status['error'] && false === $failed_status['size']['hasSizeGuide'] ); status_check( 'transport failure uses brief cache TTL', MINUTE_IN_SECONDS === $GLOBALS['status_ttl'] ); status_response( 'not-json', 200 ); status_check( 'invalid JSON reports the HTTP context safely', 'Brain returned HTTP 200' === $status->get( true )['error'] ); status_response( array( 'error' => 'bad' ), 503 ); status_check( 'non-200 response is safely inactive', 'Brain returned HTTP 503' === $status->get( true )['error'] ); $GLOBALS['status_transients'][ Status::TRANSIENT ] = array( 'stale' => true ); status_response( array( 'active' => false, 'withinQuota' => false ) ); $status->refresh(); status_check( 'refresh clears stale cache and fetches', ! isset( $GLOBALS['status_transients'][ Status::TRANSIENT ]['stale'] ) ); status_check( 'malformed and all-mode targeting fail open', true === Status::feature_enabled( 'bad', array() ) && true === Status::feature_enabled( array( 'mode' => 'all' ), array() ) ); status_check( 'exclude targeting rejects matches and accepts others', false === Status::feature_enabled( array( 'mode' => 'exclude', 'productIds' => array( '10' ) ), array( 'productId' => 10 ) ) && true === Status::feature_enabled( array( 'mode' => 'exclude' ), array() ) ); status_response( array( 'active' => true, 'size' => array( 'hasSizeGuide' => true, 'guides' => array( array( 'id' => 'product', 'categories' => array( 'categories' => array(), 'productIds' => array( '42', '43', '42' ) ), 'labels' => array( 'S', 'M' ) ), array( 'id' => 'broken', 'categories' => array( 'categories' => array(), 'productIds' => array( 42 ) ), 'labels' => array( 'S', 'M' ) ), ) ) ) ); $products_status = $status->get( true ); status_check( 'canonical product IDs survive status normalization', array( '42', '43' ) === $products_status['size']['guides'][0]['categories']['productIds'] ); status_check( 'malformed product IDs never turn into fallback guides', 1 === count( $products_status['size']['guides'] ) ); echo "\n{$passed} passed, {$failed} failed\n"; exit( $failed > 0 ? 1 : 0 );