@@ -286,12 +286,21 @@ public function test_extend_store_api_skips_registration_when_store_api_unavaila
286286 }
287287
288288 /**
289- * is_store_api_available() reflects whether the StoreApi class the plugin
290- * depends on actually exists, so the guard tracks the real class.
289+ * is_store_api_available() returns true when the StoreApi class the plugin
290+ * depends on is present. The PHPUnit harness loads WooCommerce, so the class
291+ * exists here - pinning to true means a typo or rename in the guarded class
292+ * string would fail this test rather than silently tracking the change.
291293 *
292294 * @covers WC_Connect_Loader::is_store_api_available
293295 */
294- public function test_is_store_api_available_tracks_store_api_class () {
296+ public function test_is_store_api_available_returns_true_when_store_api_class_present () {
297+ // Precondition: the harness must actually load the StoreApi class for the
298+ // assertion below to be meaningful.
299+ $ this ->assertTrue (
300+ class_exists ( '\Automattic\WooCommerce\StoreApi\StoreApi ' ),
301+ 'Test precondition: the StoreApi class must be loaded in the test harness. '
302+ );
303+
295304 $ sut = $ this ->getMockBuilder ( 'WC_Connect_Loader ' )
296305 ->disableOriginalConstructor ()
297306 ->setMethods ( null )
@@ -300,11 +309,52 @@ public function test_is_store_api_available_tracks_store_api_class() {
300309 $ method = new ReflectionMethod ( 'WC_Connect_Loader ' , 'is_store_api_available ' );
301310 $ method ->setAccessible ( true );
302311
303- $ this ->assertSame (
304- class_exists ( '\Automattic\WooCommerce\StoreApi\StoreApi ' ),
312+ $ this ->assertTrue (
305313 $ method ->invoke ( $ sut ),
306- 'is_store_api_available() should mirror the existence of the StoreApi class. '
314+ 'is_store_api_available() should return true when the StoreApi class is present . '
307315 );
308316 }
309317
318+ /**
319+ * On the unavailable path, extend_store_api() logs a notice - but at most
320+ * once per day, because it runs on `woocommerce_blocks_loaded` (nearly every
321+ * request). The throttle transient must suppress the second same-day call.
322+ *
323+ * @covers WC_Connect_Loader::extend_store_api
324+ * @covers WC_Connect_Loader::log_store_api_unavailable
325+ */
326+ public function test_extend_store_api_logs_unavailable_notice_once_per_day () {
327+ delete_transient ( 'wcservices_store_api_unavailable_logged ' );
328+
329+ // Spy logger injected via the woocommerce_logging_class filter. Returning
330+ // an object bypasses wc_get_logger()'s static cache.
331+ $ logger = $ this ->getMockBuilder ( 'WC_Logger_Interface ' )->getMock ();
332+ $ logger ->expects ( $ this ->once () )
333+ ->method ( 'notice ' )
334+ ->with (
335+ 'StoreApi class not found. Store API extensions will not be registered. ' ,
336+ array ( 'source ' => 'woocommerce-services ' )
337+ );
338+
339+ $ inject_logger = function () use ( $ logger ) {
340+ return $ logger ;
341+ };
342+ add_filter ( 'woocommerce_logging_class ' , $ inject_logger );
343+
344+ $ sut = $ this ->getMockBuilder ( 'WC_Connect_Loader ' )
345+ ->disableOriginalConstructor ()
346+ ->setMethods ( array ( 'is_store_api_available ' , 'register_store_api_extensions ' ) )
347+ ->getMock ();
348+
349+ $ sut ->method ( 'is_store_api_available ' )->willReturn ( false );
350+ $ sut ->expects ( $ this ->never () )->method ( 'register_store_api_extensions ' );
351+
352+ // First skip logs the notice; the second same-day skip is throttled.
353+ $ sut ->extend_store_api ();
354+ $ sut ->extend_store_api ();
355+
356+ remove_filter ( 'woocommerce_logging_class ' , $ inject_logger );
357+ delete_transient ( 'wcservices_store_api_unavailable_logged ' );
358+ }
359+
310360}
0 commit comments