Commit c42209f0ec for woocommerce
commit c42209f0ecb146af67f971f6f71564f2d32a24a4
Author: Radoslav Georgiev <rageorgiev@gmail.com>
Date: Mon Dec 22 15:34:50 2025 +0200
Product Feed: Decrease expiration period, validate feed validity (#62537)
* Decrease expiration to 20h, confirm that the field has not expired.
* Add unit test
* Add a positive test
diff --git a/plugins/woocommerce/src/Internal/ProductFeed/Integrations/POSCatalog/AsyncGenerator.php b/plugins/woocommerce/src/Internal/ProductFeed/Integrations/POSCatalog/AsyncGenerator.php
index 553342fa1e..0fb0f67e9e 100644
--- a/plugins/woocommerce/src/Internal/ProductFeed/Integrations/POSCatalog/AsyncGenerator.php
+++ b/plugins/woocommerce/src/Internal/ProductFeed/Integrations/POSCatalog/AsyncGenerator.php
@@ -44,7 +44,7 @@ class AsyncGenerator {
*
* @var int
*/
- const FEED_EXPIRY = 24 * HOUR_IN_SECONDS;
+ const FEED_EXPIRY = 20 * HOUR_IN_SECONDS;
/**
* Possible states of generation.
@@ -334,15 +334,26 @@ class AsyncGenerator {
* @return bool True if the status is valid, false otherwise.
*/
private function validate_status( array $status ): bool {
- // Validate the state.
/**
* For completed jobs, make sure the file still exists. Regenerate otherwise.
*
* The file should typically get deleted at the same time as the status is cleared.
* However, something else could cause the file to disappear in the meantime (ex. manual delete).
+ *
+ * Also, if the cleanup job failed, the feed might appear as complete, but be expired.
*/
- if ( self::STATE_COMPLETED === $status['state'] && ! file_exists( $status['path'] ) ) {
- return false;
+ if ( self::STATE_COMPLETED === $status['state'] ) {
+ if ( ! file_exists( $status['path'] ) ) {
+ return false;
+ }
+
+ if ( ! isset( $status['completed_at'] ) ) {
+ return false;
+ }
+
+ if ( $status['completed_at'] + self::FEED_EXPIRY < time() ) {
+ return false;
+ }
}
/**
diff --git a/plugins/woocommerce/tests/php/src/Internal/ProductFeed/Integrations/POSCatalog/AsyncGeneratorTest.php b/plugins/woocommerce/tests/php/src/Internal/ProductFeed/Integrations/POSCatalog/AsyncGeneratorTest.php
index cd7fd0de06..728fc65850 100644
--- a/plugins/woocommerce/tests/php/src/Internal/ProductFeed/Integrations/POSCatalog/AsyncGeneratorTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/ProductFeed/Integrations/POSCatalog/AsyncGeneratorTest.php
@@ -7,6 +7,7 @@ use PHPUnit\Framework\MockObject\MockObject;
use Automattic\WooCommerce\Internal\ProductFeed\Integrations\POSCatalog\AsyncGenerator;
use Automattic\WooCommerce\Internal\ProductFeed\Integrations\POSCatalog\POSIntegration;
use Automattic\WooCommerce\Internal\ProductFeed\Integrations\POSCatalog\ProductMapper;
+use ReflectionClass;
use WC_Helper_Product;
/**
@@ -103,4 +104,36 @@ class AsyncGeneratorTest extends \WC_Unit_Test_Case {
$updated_status = get_option( self::OPTION_KEY );
$this->assertEquals( AsyncGenerator::STATE_COMPLETED, $updated_status['state'] );
}
+
+ /**
+ * Test that validate_status returns false for expired feeds.
+ */
+ public function test_validate_status_returns_false_for_expired_feed() {
+ $status = array(
+ 'state' => AsyncGenerator::STATE_COMPLETED,
+ 'path' => __FILE__, // We just need a path that exists.
+ 'completed_at' => time() - AsyncGenerator::FEED_EXPIRY - 1,
+ );
+
+ $method = ( new ReflectionClass( $this->sut ) )->getMethod( 'validate_status' );
+ $method->setAccessible( true );
+
+ $this->assertFalse( $method->invoke( $this->sut, $status ) );
+ }
+
+ /**
+ * Test that validate_status returns true for non-expired feeds.
+ */
+ public function test_validate_status_returns_true_for_non_expired_feed() {
+ $status = array(
+ 'state' => AsyncGenerator::STATE_COMPLETED,
+ 'path' => __FILE__, // We just need a path that exists.
+ 'completed_at' => time() + AsyncGenerator::FEED_EXPIRY,
+ );
+
+ $method = ( new ReflectionClass( $this->sut ) )->getMethod( 'validate_status' );
+ $method->setAccessible( true );
+
+ $this->assertTrue( $method->invoke( $this->sut, $status ) );
+ }
}