@@ -86,7 +86,6 @@ public function mockLoaderAndActiveShippingMethods() {
8686 public function test_class_exists () {
8787
8888 $ this ->assertTrue ( class_exists ( 'WC_Connect_Loader ' ) );
89-
9089 }
9190
9291 /**
@@ -104,7 +103,6 @@ public function test_init_hook_attached_in_constructor() {
104103
105104 $ attached = has_action ( 'before_woocommerce_init ' , array ( $ loader , 'pre_wc_init ' ) );
106105 $ this ->assertNotFalse ( $ attached , 'WC_Connect_Loader::pre_wc_init() not attached to `before_woocommerce_init`. ' );
107-
108106 }
109107
110108 /**
@@ -121,7 +119,6 @@ public function test_logger_getter_setter() {
121119 $ loader ->set_logger ( $ logger );
122120
123121 $ this ->assertEquals ( $ logger , $ loader ->get_logger () );
124-
125122 }
126123
127124 /**
@@ -138,7 +135,6 @@ public function test_api_client_getter_setter() {
138135 $ loader ->set_api_client ( $ client );
139136
140137 $ this ->assertEquals ( $ client , $ loader ->get_api_client () );
141-
142138 }
143139
144140 /**
@@ -156,7 +152,6 @@ public function test_services_store_getter_setter() {
156152 $ loader ->set_service_schemas_store ( $ store );
157153
158154 $ this ->assertEquals ( $ store , $ loader ->get_service_schemas_store () );
159-
160155 }
161156
162157 /**
@@ -173,7 +168,6 @@ public function test_services_validator_getter_setter() {
173168 $ loader ->set_service_schemas_validator ( $ validator );
174169
175170 $ this ->assertEquals ( $ validator , $ loader ->get_service_schemas_validator () );
176-
177171 }
178172
179173 /**
@@ -218,7 +212,6 @@ public function test_init_service() {
218212 $ this ->assertEquals ( $ loader ->get_logger (), $ method ->get_logger () );
219213 $ this ->assertEquals ( $ loader ->get_api_client (), $ method ->get_api_client () );
220214 $ this ->assertEquals ( $ service_data , $ method ->get_service_schema () );
221-
222215 }
223216
224217 /**
@@ -229,7 +222,6 @@ public function test_is_wc_connect_shipping_service() {
229222
230223 $ this ->assertTrue ( $ loader ->is_wc_connect_shipping_service ( 'test_method_that_is_from_wc_connect ' ) );
231224 $ this ->assertFalse ( $ loader ->is_wc_connect_shipping_service ( 'test_method_that_is_not_from_wc_connect ' ) );
232-
233225 }
234226
235227 /**
@@ -247,4 +239,113 @@ public function test_shipping_zone_method_added() {
247239 $ this ->assertEquals ( 2 , did_action ( 'wc_connect_shipping_zone_method_added ' ) );
248240 }
249241
242+ /**
243+ * When the StoreApi class is present, extend_store_api() registers the
244+ * plugin's Store API extensions.
245+ *
246+ * @covers WC_Connect_Loader::extend_store_api
247+ */
248+ public function test_extend_store_api_registers_when_store_api_available () {
249+ $ sut = $ this ->getMockBuilder ( 'WC_Connect_Loader ' )
250+ ->disableOriginalConstructor ()
251+ ->setMethods ( array ( 'is_store_api_available ' , 'register_store_api_extensions ' ) )
252+ ->getMock ();
253+
254+ $ sut ->method ( 'is_store_api_available ' )->willReturn ( true );
255+ $ sut ->expects ( $ this ->once () )->method ( 'register_store_api_extensions ' );
256+
257+ $ sut ->extend_store_api ();
258+ }
259+
260+ /**
261+ * On WooCommerce versions without the StoreApi class, extend_store_api()
262+ * skips registration instead of fataling on the missing class. This is the
263+ * guard that prevents the `Class "…\StoreApi" not found` fatal (WOOTAX-298).
264+ *
265+ * @covers WC_Connect_Loader::extend_store_api
266+ */
267+ public function test_extend_store_api_skips_registration_when_store_api_unavailable () {
268+ $ sut = $ this ->getMockBuilder ( 'WC_Connect_Loader ' )
269+ ->disableOriginalConstructor ()
270+ ->setMethods ( array ( 'is_store_api_available ' , 'register_store_api_extensions ' ) )
271+ ->getMock ();
272+
273+ $ sut ->method ( 'is_store_api_available ' )->willReturn ( false );
274+ $ sut ->expects ( $ this ->never () )->method ( 'register_store_api_extensions ' );
275+
276+ // Must not throw when the StoreApi class is absent.
277+ $ this ->assertNull ( $ sut ->extend_store_api () );
278+ }
279+
280+ /**
281+ * is_store_api_available() returns true when the StoreApi class the plugin
282+ * depends on is present. The PHPUnit harness loads WooCommerce, so the class
283+ * exists here - pinning to true means a typo or rename in the guarded class
284+ * string would fail this test rather than silently tracking the change.
285+ *
286+ * @covers WC_Connect_Loader::is_store_api_available
287+ */
288+ public function test_is_store_api_available_returns_true_when_store_api_class_present () {
289+ // Precondition: the harness must actually load the StoreApi class for the
290+ // assertion below to be meaningful.
291+ $ this ->assertTrue (
292+ class_exists ( '\Automattic\WooCommerce\StoreApi\StoreApi ' ),
293+ 'Test precondition: the StoreApi class must be loaded in the test harness. '
294+ );
295+
296+ $ sut = $ this ->getMockBuilder ( 'WC_Connect_Loader ' )
297+ ->disableOriginalConstructor ()
298+ ->setMethods ( null )
299+ ->getMock ();
300+
301+ $ method = new ReflectionMethod ( 'WC_Connect_Loader ' , 'is_store_api_available ' );
302+ $ method ->setAccessible ( true );
303+
304+ $ this ->assertTrue (
305+ $ method ->invoke ( $ sut ),
306+ 'is_store_api_available() should return true when the StoreApi class is present. '
307+ );
308+ }
309+
310+ /**
311+ * On the unavailable path, extend_store_api() logs a notice - but at most
312+ * once per day, because it runs on `woocommerce_blocks_loaded` (nearly every
313+ * request). The throttle transient must suppress the second same-day call.
314+ *
315+ * @covers WC_Connect_Loader::extend_store_api
316+ * @covers WC_Connect_Loader::log_store_api_unavailable
317+ */
318+ public function test_extend_store_api_logs_unavailable_notice_once_per_day () {
319+ delete_transient ( 'wcservices_store_api_unavailable_logged ' );
320+
321+ // Spy logger injected via the woocommerce_logging_class filter. Returning
322+ // an object bypasses wc_get_logger()'s static cache.
323+ $ logger = $ this ->getMockBuilder ( 'WC_Logger_Interface ' )->getMock ();
324+ $ logger ->expects ( $ this ->once () )
325+ ->method ( 'notice ' )
326+ ->with (
327+ 'StoreApi class not found. Store API extensions will not be registered. ' ,
328+ array ( 'source ' => 'woocommerce-services ' )
329+ );
330+
331+ $ inject_logger = function () use ( $ logger ) {
332+ return $ logger ;
333+ };
334+ add_filter ( 'woocommerce_logging_class ' , $ inject_logger );
335+
336+ $ sut = $ this ->getMockBuilder ( 'WC_Connect_Loader ' )
337+ ->disableOriginalConstructor ()
338+ ->setMethods ( array ( 'is_store_api_available ' , 'register_store_api_extensions ' ) )
339+ ->getMock ();
340+
341+ $ sut ->method ( 'is_store_api_available ' )->willReturn ( false );
342+ $ sut ->expects ( $ this ->never () )->method ( 'register_store_api_extensions ' );
343+
344+ // First skip logs the notice; the second same-day skip is throttled.
345+ $ sut ->extend_store_api ();
346+ $ sut ->extend_store_api ();
347+
348+ remove_filter ( 'woocommerce_logging_class ' , $ inject_logger );
349+ delete_transient ( 'wcservices_store_api_unavailable_logged ' );
350+ }
250351}
0 commit comments