Commit 180a5e867d5 for woocommerce

commit 180a5e867d54fff2e03f19a9b1c981fb0d98095b
Author: Vlad Olaru <vlad.olaru@automattic.com>
Date:   Fri Sep 18 21:14:36 2026 +0300

    [tests] Add payment gateway state and ordering coverage to the REST API tests (#68284)

    * test(rest-api): Fix system status route and add gateway state coverage

    The V3 system status tests dispatched against the wc/v2 route, so they never
    exercised the controller they are named for, and their tools tests keyed on
    regenerate_thumbnails, which the controller removes when Jetpack Photon is
    active. The payment gateway tests asserted a POST echo of enabled but never
    proved the stored gateway settings changed.

    Point the system status tests at wc/v3, assert the full set of top-level
    keys and the logging block, and key the tool tests on tools that are always
    registered. Add gateway coverage for BACS and COD setting types and for the
    enabled state surviving a rebuild of the gateway registry from options.
    Additive; the Playwright specs are untouched.

    Extracted from the reference branch behind #68046.

    Refs #68046

    * test(rest-api): Cover gateway state persistence and ordering

    Follow-up to review feedback on the gateway coverage added in this
    branch.

    Nothing in this test class asserted that a gateway update reaches
    storage. Every existing assertion reads the POST response, which the
    controller builds from the in-memory gateway the write just mutated,
    so a failure to persist goes unnoticed: dropping the update_option
    call from update_item leaves the whole class green.

    Replace the enabled-only test with one that posts top level fields and
    a settings value in separate requests, since update_item routes them
    differently, and reads both back. Add a second test for order, which
    lives in its own option and decides where a gateway sits in the
    collection. Both rebuild the registry so the read goes through gateway
    construction, as the next request would.

    Use assertArraySubset for the COD settings type check so both gateway
    assertions in test_get_payment_gateways read the same way.

    * test(rest-api): Clear the gateway registry before rebuilding it

    The gateway tests called WC_Payment_Gateways::init() to re-read gateways
    from storage, but init() only writes into slots keyed by each gateway's
    order. It never clears what is already there, so any gateway whose slot
    changed left its old instance behind at the previous key.

    The order test reordered cheque and bacs, which pushed cod and paypal
    into lower slots and stranded their first-init instances at the slots
    above. The registry grew from four entries to six, and because
    payment_gateways() re-keys by id with last-wins, cod and paypal resolved
    back to the stale objects the rebuild was meant to replace.

    Clear the registry before init(), the same way WC_REST_Unit_Test_Case
    does in setUp(), and move it behind a helper so all three call sites
    stay consistent.

    Also shorten the changelog entry, which described the earlier shape of
    these tests and no longer matched what they cover.

    Refs #68284

    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

    * test(rest-api): Move the system status tests to their own branch

    They change a separate file from the gateway tests and need a review
    from the people who own that endpoint, so keeping them here would hold
    the gateway work behind an unrelated review.

    Rename the changelog entry to match what is left.

    Refs #68284

    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

    ---------

    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

diff --git a/plugins/woocommerce/changelog/test-rest-api-payment-gateways b/plugins/woocommerce/changelog/test-rest-api-payment-gateways
new file mode 100644
index 00000000000..1f722ed9dcd
--- /dev/null
+++ b/plugins/woocommerce/changelog/test-rest-api-payment-gateways
@@ -0,0 +1,3 @@
+Significance: patch
+Type: dev
+Comment: Improve the payment gateway REST API tests.
diff --git a/plugins/woocommerce/tests/legacy/unit-tests/rest-api/Tests/Version3/payment-gateways.php b/plugins/woocommerce/tests/legacy/unit-tests/rest-api/Tests/Version3/payment-gateways.php
index 003de832293..e88aea63f9e 100644
--- a/plugins/woocommerce/tests/legacy/unit-tests/rest-api/Tests/Version3/payment-gateways.php
+++ b/plugins/woocommerce/tests/legacy/unit-tests/rest-api/Tests/Version3/payment-gateways.php
@@ -27,6 +27,18 @@ class Payment_Gateways extends WC_REST_Unit_Test_Case {
 		);
 	}

+	/**
+	 * Rebuild the gateway registry so a read goes through gateway construction, as the next request would.
+	 *
+	 * Clearing first matters: init() writes each gateway into a slot keyed by its order, so on its own it
+	 * leaves stale gateways behind in the slots the new order no longer uses.
+	 */
+	private function rebuild_payment_gateways() {
+		$gateways                   = WC_Payment_Gateways::instance();
+		$gateways->payment_gateways = array();
+		$gateways->init();
+	}
+
 	/**
 	 * Test route registration.
 	 *
@@ -51,6 +63,21 @@ class Payment_Gateways extends WC_REST_Unit_Test_Case {

 		$this->assertEquals( 200, $response->get_status() );

+		// COD covers several distinct form field types, so use it to check how each one is surfaced.
+		$gateways_by_id = array_column( $gateways, null, 'id' );
+		$this->assertArrayHasKey( WC_Gateway_COD::ID, $gateways_by_id );
+		$this->assertArraySubset(
+			array(
+				'settings' => array(
+					'title'              => array( 'type' => 'safe_text' ),
+					'instructions'       => array( 'type' => 'textarea' ),
+					'enable_for_methods' => array( 'type' => 'multiselect' ),
+					'enable_for_virtual' => array( 'type' => 'checkbox' ),
+				),
+			),
+			$gateways_by_id[ WC_Gateway_COD::ID ]
+		);
+
 		$matching_gateway_data = current(
 			array_filter(
 				$gateways,
@@ -272,6 +299,77 @@ class Payment_Gateways extends WC_REST_Unit_Test_Case {
 		$this->assertEquals( 'authorization', $paypal['settings']['paymentaction']['value'] );
 	}

+	/**
+	 * @testdox A gateway update reaches storage, so a later request reads the new state.
+	 */
+	public function test_update_payment_gateway_persists_to_storage() {
+		wp_set_current_user( $this->user );
+
+		// No settings payload here, so the write has to happen for the top level fields on their own.
+		$request = new WP_REST_Request( 'POST', '/wc/v3/payment_gateways/cheque' );
+		$request->set_body_params(
+			array(
+				'enabled' => true,
+				'title'   => 'Pay by check',
+			)
+		);
+		$this->assertSame( 200, $this->server->dispatch( $request )->get_status() );
+
+		$this->rebuild_payment_gateways();
+
+		$data = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v3/payment_gateways/cheque' ) )->get_data();
+		$this->assertTrue( $data['enabled'] );
+		$this->assertSame( 'Pay by check', $data['title'] );
+
+		// Settings values take a different route through update_item, so post one on its own too.
+		$request = new WP_REST_Request( 'POST', '/wc/v3/payment_gateways/cheque' );
+		$request->set_body_params(
+			array(
+				'settings' => array(
+					'instructions' => 'Post the check to our office.',
+				),
+			)
+		);
+		$this->assertSame( 200, $this->server->dispatch( $request )->get_status() );
+
+		$this->rebuild_payment_gateways();
+
+		$data = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v3/payment_gateways/cheque' ) )->get_data();
+		$this->assertSame( 'Post the check to our office.', $data['settings']['instructions']['value'] );
+		$this->assertTrue( $data['enabled'] );
+	}
+
+	/**
+	 * @testdox Gateway order persists and decides where a gateway sits in the collection.
+	 */
+	public function test_update_payment_gateway_order_persists_to_storage() {
+		wp_set_current_user( $this->user );
+
+		// BACS loads before cheque, so putting cheque first means the registry has to reorder them.
+		foreach ( array(
+			WC_Gateway_Cheque::ID => 0,
+			WC_Gateway_BACS::ID   => 1,
+		) as $gateway_id => $order ) {
+			$request = new WP_REST_Request( 'POST', '/wc/v3/payment_gateways/' . $gateway_id );
+			$request->set_body_params( array( 'order' => $order ) );
+			$this->assertSame( 200, $this->server->dispatch( $request )->get_status() );
+		}
+
+		// Order lives in its own option, and the registry reads it to sort gateways.
+		$this->rebuild_payment_gateways();
+
+		$gateways = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v3/payment_gateways' ) )->get_data();
+		$by_id    = array_column( $gateways, null, 'id' );
+		$this->assertSame( 0, $by_id[ WC_Gateway_Cheque::ID ]['order'] );
+		$this->assertSame( 1, $by_id[ WC_Gateway_BACS::ID ]['order'] );
+
+		$ids = array_column( $gateways, 'id' );
+		$this->assertLessThan(
+			array_search( WC_Gateway_BACS::ID, $ids, true ),
+			array_search( WC_Gateway_Cheque::ID, $ids, true )
+		);
+	}
+
 	/**
 	 * Test updating a payment gateway without valid permissions.
 	 *