diff --git a/includes/API.php b/includes/API.php index d6833f8..3d6e5e7 100644 --- a/includes/API.php +++ b/includes/API.php @@ -170,23 +170,28 @@ public function rest_authentication_errors( $errors ) { } /** - * @return false|string + * Extract the Authorization Bearer token from the request. + * + * @return string|false */ public function get_auth_header() { - // Get HTTP Authorization Header. - $header = isset( $_SERVER['HTTP_AUTHORIZATION'] ) ? sanitize_text_field( $_SERVER['HTTP_AUTHORIZATION'] ) : false; + // Check if HTTP_AUTHORIZATION is set in $_SERVER + if ( isset( $_SERVER['HTTP_AUTHORIZATION'] ) ) { + return sanitize_text_field( $_SERVER['HTTP_AUTHORIZATION'] ); + } - // Check for alternative header. - if ( ! $header && isset( $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ) ) { - $header = sanitize_text_field( $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ); + // Check for alternative header in $_SERVER + if ( isset( $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ) ) { + return sanitize_text_field( $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ); } - // Check for authorization param in URL - if ( ! $header && isset( $_GET['authorization'] ) ) { - $header = sanitize_text_field( $_GET['authorization'] ); + // Check for authorization param in URL ($_GET) + if ( isset( $_GET['authorization'] ) ) { + return sanitize_text_field( $_GET['authorization'] ); } - return $header; + // Return false if none of the variables are set + return false; } /** @@ -306,21 +311,31 @@ private function prevent_messages(): void { /** * @param false|int $user_id User ID if one has been determined, false otherwise. * - * @return int + * @return int|WP_Error */ private function authenticate( $user_id ) { - // extract Bearer token from Authorization Header - list($token) = sscanf( $this->get_auth_header(), 'Bearer %s' ); + // check if there is an auth header + $auth_header = $this->get_auth_header(); + if ( ! is_string( $auth_header ) ) { + return $user_id; + } + + // Extract Bearer token from Authorization Header + list($token) = sscanf( $auth_header, 'Bearer %s' ); if ( $token ) { $decoded_token = $this->auth_service->validate_token( $token ); - if ( empty( $decoded_token ) || is_wp_error( $decoded_token ) ) { - return $user_id; + // Check if validate_token returned WP_Error and user_id is null + if ( is_wp_error( $decoded_token ) && $user_id === null ) { + return $decoded_token; } - $user = ! empty( $decoded_token->data->user->id ) ? $decoded_token->data->user->id : $user_id; - return absint( $user ); + // If the token is valid, set the user_id + if ( ! is_wp_error( $decoded_token ) ) { + $user_id = $decoded_token->data->user->id; + return absint( $user_id ); + } } return $user_id; diff --git a/includes/API/Stores.php b/includes/API/Stores.php index 1d539c4..500f506 100644 --- a/includes/API/Stores.php +++ b/includes/API/Stores.php @@ -2,9 +2,31 @@ namespace WCPOS\WooCommercePOS\API; -use WC_Admin_Settings; +\defined( 'ABSPATH' ) || die; + +if ( ! class_exists( 'WP_REST_Controller' ) ) { + return; +} + +use WP_REST_Controller; +use WCPOS\WooCommercePOS\Services\Store; +use const WCPOS\WooCommercePOS\SHORT_NAME; + +class Stores extends WP_REST_Controller { + /** + * Endpoint namespace. + * + * @var string + */ + protected $namespace = SHORT_NAME . '/v1'; + + /** + * Route base. + * + * @var string + */ + protected $rest_base = 'stores'; -class Stores extends Abstracts\Controller { /** * Stores constructor. */ @@ -13,72 +35,64 @@ public function __construct() { public function register_routes(): void { - register_rest_route($this->namespace, '/stores', array( - 'methods' => 'GET', - 'callback' => array( $this, 'get_stores' ), - 'permission_callback' => '__return_true', - )); + register_rest_route( + $this->namespace, + '/' . $this->rest_base, + array( + 'methods' => 'GET', + 'callback' => array( $this, 'get_items' ), + 'permission_callback' => array( $this, 'check_permissions' ), + ) + ); } /** - * @TODO - * @return + * Retrieve store data. + * + * @param WP_REST_Request $request Full details about the request. + * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. */ - public function get_stores() { - $data = array( - $this->get_store(), - ); + public function get_items( $request ) { + try { + $store = new Store(); - /** - * - */ - return apply_filters( 'woocommerce_pos_stores', $data, $this ); - } + // Check if store data is available + if ( ! $store ) { + return new \WP_Error( + 'woocommerce_pos_store_not_found', + esc_html__( 'Store not found', 'woocommerce-pos' ), + array( 'status' => 404 ) + ); + } + $data = $store->get_data(); + $response = rest_ensure_response( array( $data ) ); + return $response; - public function get_store(): array { - return array_merge( - array( - 'id' => 0, - 'name' => get_option( 'blogname' ), - 'locale' => get_locale(), - ), - array( - /** - * Get POS Settings - */ - 'default_customer' => woocommerce_pos_get_settings( 'general', 'default_customer' ), - 'default_customer_is_cashier' => woocommerce_pos_get_settings( 'general', 'default_customer_is_cashier' ), - /** - * Get the General settings from WooCommerce - */ - 'store_address' => WC_Admin_Settings::get_option( 'woocommerce_store_address' ), - 'store_address_2' => WC_Admin_Settings::get_option( 'woocommerce_store_address_2' ), - 'store_city' => WC_Admin_Settings::get_option( 'woocommerce_store_city' ), - 'default_country' => WC_Admin_Settings::get_option( 'woocommerce_default_country' ), - 'store_postcode' => WC_Admin_Settings::get_option( 'woocommerce_store_postcode' ), - 'default_customer_address' => WC_Admin_Settings::get_option( 'woocommerce_default_customer_address' ), - 'calc_taxes' => WC_Admin_Settings::get_option( 'woocommerce_calc_taxes' ), - 'enable_coupons' => WC_Admin_Settings::get_option( 'woocommerce_enable_coupons' ), - 'calc_discounts_sequentially' => WC_Admin_Settings::get_option( 'woocommerce_calc_discounts_sequentially' ), - 'currency' => WC_Admin_Settings::get_option( 'woocommerce_currency' ), - 'currency_pos' => WC_Admin_Settings::get_option( 'woocommerce_currency_pos' ), - 'price_thousand_sep' => WC_Admin_Settings::get_option( 'woocommerce_price_thousand_sep' ), - 'price_decimal_sep' => WC_Admin_Settings::get_option( 'woocommerce_price_decimal_sep' ), - 'price_num_decimals' => WC_Admin_Settings::get_option( 'woocommerce_price_num_decimals' ), - /** - * Get the Tax settings from WooCommerce - */ - 'prices_include_tax' => WC_Admin_Settings::get_option( 'woocommerce_prices_include_tax' ), - 'tax_based_on' => 'base', // default should be base, perhaps have a setting for this? - 'shipping_tax_class' => WC_Admin_Settings::get_option( 'woocommerce_shipping_tax_class' ), - 'tax_round_at_subtotal' => WC_Admin_Settings::get_option( 'woocommerce_tax_round_at_subtotal' ), - 'tax_display_shop' => WC_Admin_Settings::get_option( 'woocommerce_tax_display_shop' ), - 'tax_display_cart' => WC_Admin_Settings::get_option( 'woocommerce_tax_display_cart' ), - 'price_display_suffix' => WC_Admin_Settings::get_option( 'woocommerce_price_display_suffix' ), - 'tax_total_display' => WC_Admin_Settings::get_option( 'woocommerce_tax_total_display' ), - ) - ); + } catch ( \Exception $e ) { + return new \WP_Error( + 'woocommerce_pos_store_retrieval_failed', + esc_html__( 'Failed to retrieve store data', 'woocommerce-pos' ), + array( 'status' => 500 ) + ); + } + } + + /** + * Check if the user is logged in. + * + * @return bool|WP_Error True if the user is logged in, WP_Error otherwise. + */ + public function check_permissions() { + if ( ! is_user_logged_in() ) { + return new \WP_Error( + 'woocommerce_pos_rest_forbidden', + esc_html__( 'You do not have permissions to view this data.', 'woocommerce-pos' ), + array( 'status' => rest_authorization_required_code() ) + ); + } + + return true; } } diff --git a/includes/Services/Auth.php b/includes/Services/Auth.php index 25049ad..2a64de5 100644 --- a/includes/Services/Auth.php +++ b/includes/Services/Auth.php @@ -6,21 +6,11 @@ use WCPOS\Vendor\Firebase\JWT\JWT; use WCPOS\Vendor\Firebase\JWT\Key; use Ramsey\Uuid\Uuid; -use WCPOS\WooCommercePOS\API\Stores; use WP_Error; use WP_User; use const DAY_IN_SECONDS; class Auth { - /** - * - */ - protected $stores_service; - - public function __construct() { - $this->stores_service = new Stores(); - } - /** * Generate a secret key if it doesn't exist, or return the existing one * @@ -177,7 +167,6 @@ public function get_user_data( WP_User $user ): array { 'nice_name' => $user->user_nicename, 'display_name' => $user->display_name, 'avatar_url' => get_avatar_url( $user->ID ), - 'stores' => $this->stores_service->get_stores(), ); return $data; diff --git a/includes/Services/Store.php b/includes/Services/Store.php index 0ed1968..6f974af 100644 --- a/includes/Services/Store.php +++ b/includes/Services/Store.php @@ -2,22 +2,371 @@ namespace WCPOS\WooCommercePOS\Services; -class Store { +use WC_Data; + +if ( ! class_exists( 'WC_Data' ) ) { + return; +} + +class Store extends WC_Data { + /** + * This is the name of this object type. + * + * @var string + */ + protected $object_type = 'product'; + + /** + * Stores product data. + * + * @var array + */ + protected $data = array( + 'name' => '', + 'locale' => '', + 'store_address' => '', + 'store_address_2' => '', + 'store_city' => '', + 'default_country' => '', + 'store_postcode' => '', + 'default_customer_address' => '', + 'calc_taxes' => '', + 'enable_coupons' => '', + 'calc_discounts_sequentially' => '', + 'currency' => '', + 'currency_pos' => '', + 'price_thousand_sep' => '', + 'price_decimal_sep' => '', + 'price_num_decimals' => '', + 'prices_include_tax' => '', + 'tax_based_on' => '', + 'shipping_tax_class' => '', + 'tax_round_at_subtotal' => '', + 'tax_display_shop' => '', + 'tax_display_cart' => '', + 'price_display_suffix' => '', + 'tax_total_display' => '', + 'default_customer' => 0, + 'default_customer_is_cashier' => false, + ); + + /** + * Construct the default POS store. + */ + public function __construct() { + $this->set_wordpress_settings(); + $this->set_woocommerce_general_settings(); + $this->set_woocommerce_tax_settings(); + $this->set_woocommerce_pos_settings(); + } + + /** + * + */ + public function set_wordpress_settings() { + $this->set_prop( 'name', \get_bloginfo( 'name' ) ); + $this->set_prop( 'locale', \get_locale() ); + } + + /** + * + */ + public function set_woocommerce_general_settings() { + $this->set_prop( 'store_address', \WC_Admin_Settings::get_option( 'woocommerce_store_address' ) ); + $this->set_prop( 'store_address_2', \WC_Admin_Settings::get_option( 'woocommerce_store_address_2' ) ); + $this->set_prop( 'store_city', \WC_Admin_Settings::get_option( 'woocommerce_store_city' ) ); + $this->set_prop( 'default_country', \WC_Admin_Settings::get_option( 'woocommerce_default_country' ) ); + $this->set_prop( 'store_postcode', \WC_Admin_Settings::get_option( 'woocommerce_store_postcode' ) ); + $this->set_prop( 'default_customer_address', \WC_Admin_Settings::get_option( 'woocommerce_default_customer_address' ) ); + $this->set_prop( 'calc_taxes', \WC_Admin_Settings::get_option( 'woocommerce_calc_taxes' ) ); + $this->set_prop( 'enable_coupons', \WC_Admin_Settings::get_option( 'woocommerce_enable_coupons' ) ); + $this->set_prop( 'calc_discounts_sequentially', \WC_Admin_Settings::get_option( 'woocommerce_calc_discounts_sequentially' ) ); + $this->set_prop( 'currency', \WC_Admin_Settings::get_option( 'woocommerce_currency' ) ); + $this->set_prop( 'currency_pos', \WC_Admin_Settings::get_option( 'woocommerce_currency_pos' ) ); + $this->set_prop( 'price_thousand_sep', \WC_Admin_Settings::get_option( 'woocommerce_price_thousand_sep' ) ); + $this->set_prop( 'price_decimal_sep', \WC_Admin_Settings::get_option( 'woocommerce_price_decimal_sep' ) ); + $this->set_prop( 'price_num_decimals', \WC_Admin_Settings::get_option( 'woocommerce_price_num_decimals' ) ); + } + + /** + * + */ + public function set_woocommerce_tax_settings() { + $this->set_prop( 'prices_include_tax', \WC_Admin_Settings::get_option( 'woocommerce_prices_include_tax' ) ); + $this->set_prop( 'tax_based_on', 'base' ); // default should be base, perhaps have a setting for this? + $this->set_prop( 'shipping_tax_class', \WC_Admin_Settings::get_option( 'woocommerce_shipping_tax_class' ) ); + $this->set_prop( 'tax_round_at_subtotal', \WC_Admin_Settings::get_option( 'woocommerce_tax_round_at_subtotal' ) ); + $this->set_prop( 'tax_display_shop', \WC_Admin_Settings::get_option( 'woocommerce_tax_display_shop' ) ); + $this->set_prop( 'tax_display_cart', \WC_Admin_Settings::get_option( 'woocommerce_tax_display_cart' ) ); + $this->set_prop( 'price_display_suffix', \WC_Admin_Settings::get_option( 'woocommerce_price_display_suffix' ) ); + $this->set_prop( 'tax_total_display', \WC_Admin_Settings::get_option( 'woocommerce_tax_total_display' ) ); + } + + /** + * + */ + public function set_woocommerce_pos_settings() { + $this->set_prop( 'default_customer', woocommerce_pos_get_settings( 'general', 'default_customer' ) ); + $this->set_prop( 'default_customer_is_cashier', woocommerce_pos_get_settings( 'general', 'default_customer_is_cashier' ) ); + } + + /** + * Get Store name. + * + * @param string $context What the value is for. Valid values are view and edit. + * @return string + */ + public function get_name( $context = 'view' ) { + return $this->get_prop( 'name', $context ); + } + + /** + * Get Store locale. + * + * @param string $context What the value is for. Valid values are view and edit. + * @return string + */ + public function get_locale( $context = 'view' ) { + return $this->get_prop( 'locale', $context ); + } + + /** + * Get Store address. + * + * @param string $context What the value is for. Valid values are view and edit. + * @return string + */ + public function get_store_address( $context = 'view' ) { + return $this->get_prop( 'store_address', $context ); + } + + /** + * Get Store address 2. + * + * @param string $context What the value is for. Valid values are view and edit. + * @return string + */ + public function get_store_address_2( $context = 'view' ) { + return $this->get_prop( 'store_address_2', $context ); + } + + /** + * Get Store city. + * + * @param string $context What the value is for. Valid values are view and edit. + * @return string + */ + public function get_store_city( $context = 'view' ) { + return $this->get_prop( 'store_city', $context ); + } + + /** + * Get Store postcode. + * + * @param string $context What the value is for. Valid values are view and edit. + * @return string + */ + public function get_store_postcode( $context = 'view' ) { + return $this->get_prop( 'store_postcode', $context ); + } + + /** + * Get Store country. + * + * @param string $context What the value is for. Valid values are view and edit. + * @return string + */ + public function get_default_country( $context = 'view' ) { + return $this->get_prop( 'default_country', $context ); + } + + /** + * Get Store customer address. + * + * @param string $context What the value is for. Valid values are view and edit. + * @return string + */ + public function get_default_customer_address( $context = 'view' ) { + return $this->get_prop( 'default_customer_address', $context ); + } + + /** + * Get Store calculate taxes. + * + * @param string $context What the value is for. Valid values are view and edit. + * @return string + */ + public function get_calc_taxes( $context = 'view' ) { + return $this->get_prop( 'calc_taxes', $context ); + } + + /** + * Get Store enable coupons. + * + * @param string $context What the value is for. Valid values are view and edit. + * @return bool + */ + public function get_enable_coupons( $context = 'view' ) { + return $this->get_prop( 'enable_coupons', $context ); + } + + /** + * Get Store calculate discounts sequentially. + * + * @param string $context What the value is for. Valid values are view and edit. + * @return bool + */ + public function get_calc_discounts_sequentially( $context = 'view' ) { + return $this->get_prop( 'calc_discounts_sequentially', $context ); + } + + /** + * Get Store currency. + * + * @param string $context What the value is for. Valid values are view and edit. + * @return string + */ + public function get_currency( $context = 'view' ) { + return $this->get_prop( 'currency', $context ); + } + + /** + * Get Store currency position. + * + * @param string $context What the value is for. Valid values are view and edit. + * @return string + */ + public function get_currency_position( $context = 'view' ) { + return $this->get_prop( 'currency_pos', $context ); + } + + /** + * Get Store price thousand separator. + * + * @param string $context What the value is for. Valid values are view and edit. + * @return string + */ + public function get_price_thousand_separator( $context = 'view' ) { + return $this->get_prop( 'price_thousand_sep', $context ); + } + + /** + * Get Store price decimal separator. + * + * @param string $context What the value is for. Valid values are view and edit. + * @return string + */ + public function get_price_decimal_separator( $context = 'view' ) { + return $this->get_prop( 'price_decimal_sep', $context ); + } + + /** + * Get Store price number of decimals. + * + * @param string $context What the value is for. Valid values are view and edit. + * @return int + */ + public function get_price_number_of_decimals( $context = 'view' ) { + return $this->get_prop( 'price_num_decimals', $context ); + } + + /** + * Get Store prices include tax. + * + * @param string $context What the value is for. Valid values are view and edit. + * @return bool + */ + public function get_prices_include_tax( $context = 'view' ) { + return $this->get_prop( 'prices_include_tax', $context ); + } + + /** + * Get Store tax based on. + * + * @param string $context What the value is for. Valid values are view and edit. + * @return string + */ + public function get_tax_based_on( $context = 'view' ) { + return $this->get_prop( 'tax_based_on', $context ); + } + + /** + * Get Store shipping tax class. + * + * @param string $context What the value is for. Valid values are view and edit. + * @return string + */ + public function get_shipping_tax_class( $context = 'view' ) { + return $this->get_prop( 'shipping_tax_class', $context ); + } + + /** + * Get Store tax round at subtotal. + * + * @param string $context What the value is for. Valid values are view and edit. + * @return bool + */ + public function get_tax_round_at_subtotal( $context = 'view' ) { + return $this->get_prop( 'tax_round_at_subtotal', $context ); + } + + /** + * Get Store tax display shop. + * + * @param string $context What the value is for. Valid values are view and edit. + * @return bool + */ + public function get_tax_display_shop( $context = 'view' ) { + return $this->get_prop( 'tax_display_shop', $context ); + } + /** - * Get Store Name. + * Get Store tax display cart. * - * @return string The Store Name + * @param string $context What the value is for. Valid values are view and edit. + * @return bool */ - public function get_name(): string { - return get_bloginfo('name'); + public function get_tax_display_cart( $context = 'view' ) { + return $this->get_prop( 'tax_display_cart', $context ); + } + + /** + * Get Store price display suffix. + * + * @param string $context What the value is for. Valid values are view and edit. + * @return string + */ + public function get_price_display_suffix( $context = 'view' ) { + return $this->get_prop( 'price_display_suffix', $context ); + } + + /** + * Get Store tax total display. + * + * @param string $context What the value is for. Valid values are view and edit. + * @return string + */ + public function get_tax_total_display( $context = 'view' ) { + return $this->get_prop( 'tax_total_display', $context ); + } + + /** + * Get Store default customer. + * + * @param string $context What the value is for. Valid values are view and edit. + * @return int + */ + public function get_default_customer( $context = 'view' ) { + return $this->get_prop( 'default_customer', $context ); } /** - * Get the store ID. + * Get Store default customer is cashier. * - * @return int The store ID. + * @param string $context What the value is for. Valid values are view and edit. + * @return bool */ - public function get_id(): int { - return 0; + public function get_default_customer_is_cashier( $context = 'view' ) { + return $this->get_prop( 'default_customer_is_cashier', $context ); } } diff --git a/includes/Services/Stores.php b/includes/Services/Stores.php deleted file mode 100644 index 66e6cd4..0000000 --- a/includes/Services/Stores.php +++ /dev/null @@ -1,7 +0,0 @@ -auth_service = new Auth(); - } - /** * @return void @@ -82,8 +74,8 @@ public function head(): void { public function footer(): void { $development = isset( $_ENV['DEVELOPMENT'] ) && $_ENV['DEVELOPMENT']; $user = wp_get_current_user(); - $store_settings = new Stores(); $github_url = 'https://wcpos.github.io/managed-expo/'; + $store = new Store(); $auth_service = new Auth(); $site_uuid = get_option( 'woocommerce_pos_uuid' ); @@ -118,7 +110,7 @@ public function footer(): void { 'use_jwt_as_param' => woocommerce_pos_get_settings( 'tools', 'use_jwt_as_param' ), ), 'wp_credentials' => $auth_service->get_user_data( $user ), - 'stores' => $store_settings->get_stores(), + 'stores' => array( $store->get_data() ), ); /** diff --git a/tests/includes/API/Test_Stores_API.php b/tests/includes/API/Test_Stores_API.php new file mode 100644 index 0000000..3c453a9 --- /dev/null +++ b/tests/includes/API/Test_Stores_API.php @@ -0,0 +1,94 @@ +endpoint = new Stores(); + } + + public function tearDown(): void { + parent::tearDown(); + } + + public function test_namespace_property(): void { + $namespace = $this->get_reflected_property_value( 'namespace' ); + + $this->assertEquals( 'wcpos/v1', $namespace ); + } + + public function test_rest_base(): void { + $rest_base = $this->get_reflected_property_value( 'rest_base' ); + + $this->assertEquals( 'stores', $rest_base ); + } + + /** + * Test route registration. + */ + public function test_register_routes(): void { + $routes = $this->server->get_routes(); + $this->assertArrayHasKey( '/wcpos/v1/stores', $routes ); + } + + /** + * Get all expected fields. + */ + public function get_expected_response_fields() { + return array( + 'id', + 'name', + 'locale', + 'default_customer', + 'default_customer_is_cashier', + 'store_address', + 'store_address_2', + 'store_city', + 'default_country', + 'store_postcode', + 'default_customer_address', + 'calc_taxes', + 'enable_coupons', + 'calc_discounts_sequentially', + 'currency', + 'currency_pos', + 'price_thousand_sep', + 'price_decimal_sep', + 'price_num_decimals', + 'prices_include_tax', + 'tax_based_on', + 'shipping_tax_class', + 'tax_round_at_subtotal', + 'tax_display_shop', + 'tax_display_cart', + 'price_display_suffix', + 'tax_total_display', + 'meta_data', + ); + } + + public function test_product_api_get_all_fields(): void { + $expected_response_fields = $this->get_expected_response_fields(); + + $request = $this->wp_rest_get_request( '/wcpos/v1/stores' ); + $response = $this->server->dispatch( $request ); + + $this->assertEquals( 200, $response->get_status() ); + $stores = $response->get_data(); + $this->assertIsArray( $stores ); + $this->assertEquals( 1, \count( $stores ) ); + $data = $stores[0]; + + $response_fields = array_keys( $data ); + $this->assertEmpty( array_diff( $expected_response_fields, $response_fields ), 'These fields were expected but not present in WCPOS API response: ' . print_r( array_diff( $expected_response_fields, $response_fields ), true ) ); + $this->assertEmpty( array_diff( $response_fields, $expected_response_fields ), 'These fields were not expected in the WCPOS API response: ' . print_r( array_diff( $response_fields, $expected_response_fields ), true ) ); + } +} diff --git a/tests/includes/Services/Test_Store_Service.php b/tests/includes/Services/Test_Store_Service.php new file mode 100644 index 0000000..f13d9cf --- /dev/null +++ b/tests/includes/Services/Test_Store_Service.php @@ -0,0 +1,243 @@ +store = new Store(); + } + + public function tearDown(): void { + parent::tearDown(); + unset( $this->store ); + } + + /** + * Get all expected fields. + */ + public function get_expected_data_props() { + return array( + 'id', + 'name', + 'locale', + 'default_customer', + 'default_customer_is_cashier', + 'store_address', + 'store_address_2', + 'store_city', + 'default_country', + 'store_postcode', + 'default_customer_address', + 'calc_taxes', + 'enable_coupons', + 'calc_discounts_sequentially', + 'currency', + 'currency_pos', + 'price_thousand_sep', + 'price_decimal_sep', + 'price_num_decimals', + 'prices_include_tax', + 'tax_based_on', + 'shipping_tax_class', + 'tax_round_at_subtotal', + 'tax_display_shop', + 'tax_display_cart', + 'price_display_suffix', + 'tax_total_display', + 'meta_data', + ); + } + + public function test_product_api_get_all_fields(): void { + $expected_data_props = $this->get_expected_data_props(); + $data = $this->store->get_data(); + $props = array_keys( $data ); + $this->assertEmpty( array_diff( $expected_data_props, $props ), 'These fields were expected but not present in Store: ' . print_r( array_diff( $expected_data_props, $props ), true ) ); + $this->assertEmpty( array_diff( $props, $expected_data_props ), 'These fields were not expected in Store: ' . print_r( array_diff( $props, $expected_data_props ), true ) ); + } + + public function test_get_store_id(): void { + $store_id = $this->store->get_id(); + $this->assertIsInt( $store_id ); + $this->assertEquals( 0, $store_id ); + } + + public function test_get_store_name(): void { + // @TODO - mocking functions doesn't work, I don't know why? + FunctionsMockerHack::add_function_mocks( + array( + 'get_bloginfo' => function ( $show = '' ) { + echo "Mocked get_bloginfo called with parameter: $show\n"; + return 'Mocked Store Name'; + }, + ) + ); + + $store_name = $this->store->get_name(); + $this->assertIsString( $store_name ); + $this->assertEquals( 'Test Blog', $store_name ); + } + + public function test_get_store_locale(): void { + $store_locale = $this->store->get_locale(); + $this->assertIsString( $store_locale ); + $this->assertEquals( 'en_US', $store_locale ); + } + + public function test_get_store_address(): void { + $store_address = $this->store->get_store_address(); + $this->assertIsString( $store_address ); + $this->assertEquals( '', $store_address ); // Assuming default is empty string + } + + public function test_get_store_address_2(): void { + $store_address_2 = $this->store->get_store_address_2(); + $this->assertIsString( $store_address_2 ); + $this->assertEquals( '', $store_address_2 ); // Assuming default is empty string + } + + public function test_get_store_city(): void { + $store_city = $this->store->get_store_city(); + $this->assertIsString( $store_city ); + $this->assertEquals( '', $store_city ); // Assuming default is empty string + } + + public function test_get_default_country(): void { + // Assuming you have a way to set these values or mock them + $store_country = $this->store->get_default_country(); + $this->assertIsString( $store_country ); + $this->assertEquals( 'US:CA', $store_country ); + } + + public function test_get_store_postcode(): void { + $store_postcode = $this->store->get_store_postcode(); + $this->assertIsString( $store_postcode ); + $this->assertEquals( '', $store_postcode ); // Assuming default is empty string + } + + public function test_get_default_customer_address(): void { + $store_customer_address = $this->store->get_default_customer_address(); + $this->assertIsString( $store_customer_address ); + $this->assertEquals( 'base', $store_customer_address ); // Assuming default is empty string + } + + public function test_get_calc_taxes(): void { + $calc_taxes = $this->store->get_calc_taxes(); + $this->assertIsString( $calc_taxes ); + $this->assertEquals( 'no', $calc_taxes ); // Default value + } + + public function test_get_enable_coupons(): void { + $enable_coupons = $this->store->get_enable_coupons(); + $this->assertIsString( $enable_coupons ); + $this->assertEquals( 'yes', $enable_coupons ); // Default value + } + + public function test_get_calc_discounts_sequentially(): void { + $calc_discounts_sequentially = $this->store->get_calc_discounts_sequentially(); + $this->assertIsString( $calc_discounts_sequentially ); + $this->assertEquals( 'no', $calc_discounts_sequentially ); // Default value + } + + public function test_get_currency(): void { + $currency = $this->store->get_currency(); + $this->assertIsString( $currency ); + $this->assertEquals( 'USD', $currency ); // Default value + } + + public function test_get_currency_position(): void { + $currency_pos = $this->store->get_currency_position(); + $this->assertIsString( $currency_pos ); + $this->assertEquals( 'left', $currency_pos ); // Default value + } + + public function test_get_price_thousand_separator(): void { + $separator = $this->store->get_price_thousand_separator(); + $this->assertIsString( $separator ); + $this->assertEquals( ',', $separator ); // Default value + } + + public function test_get_price_decimal_separator(): void { + $separator = $this->store->get_price_decimal_separator(); + $this->assertIsString( $separator ); + $this->assertEquals( '.', $separator ); // Default value + } + + public function test_get_price_number_of_decimals(): void { + $decimals = $this->store->get_price_number_of_decimals(); + $this->assertIsString( $decimals ); + $this->assertEquals( '2', $decimals ); // Default value + } + + public function test_get_prices_include_tax(): void { + $include_tax = $this->store->get_prices_include_tax(); + $this->assertIsString( $include_tax ); + $this->assertEquals( 'no', $include_tax ); // Default value + } + + public function test_get_tax_based_on(): void { + $tax_based_on = $this->store->get_tax_based_on(); + $this->assertIsString( $tax_based_on ); + $this->assertEquals( 'base', $tax_based_on ); // Default value + } + + public function test_get_shipping_tax_class(): void { + $tax_class = $this->store->get_shipping_tax_class(); + $this->assertIsString( $tax_class ); + $this->assertEquals( 'inherit', $tax_class ); // Default value + } + + public function test_get_tax_round_at_subtotal(): void { + $round_at_subtotal = $this->store->get_tax_round_at_subtotal(); + $this->assertIsString( $round_at_subtotal ); + $this->assertEquals( 'no', $round_at_subtotal ); // Default value + } + + public function test_get_tax_display_shop(): void { + $tax_display_shop = $this->store->get_tax_display_shop(); + $this->assertIsString( $tax_display_shop ); + $this->assertEquals( 'excl', $tax_display_shop ); // Default value + } + + public function test_get_tax_display_cart(): void { + $tax_display_cart = $this->store->get_tax_display_cart(); + $this->assertIsString( $tax_display_cart ); + $this->assertEquals( 'excl', $tax_display_cart ); // Default value + } + + public function test_get_price_display_suffix(): void { + $display_suffix = $this->store->get_price_display_suffix(); + $this->assertIsString( $display_suffix ); + $this->assertEquals( '', $display_suffix ); // Default value + } + + public function test_get_tax_total_display(): void { + $tax_total_display = $this->store->get_tax_total_display(); + $this->assertIsString( $tax_total_display ); + $this->assertEquals( 'itemized', $tax_total_display ); // Default value + } + + public function test_get_default_customer(): void { + $customer = $this->store->get_default_customer(); + $this->assertIsInt( $customer ); + $this->assertEquals( 0, $customer ); // Default value + } + + public function test_get_default_customer_is_cashier(): void { + $is_cashier = $this->store->get_default_customer_is_cashier(); + $this->assertIsBool( $is_cashier ); + $this->assertFalse( $is_cashier ); // Default value + } +}