Commit d3f2e2cae48 for woocommerce

commit d3f2e2cae48a61550ab8e42bf698799e87440052
Author: Deepak Lalwani <deepak.lalwani81@gmail.com>
Date:   Thu Mar 19 17:09:23 2026 +0530

    Update slug sanitize callback to use sanitize_slug (#63342)

    * Update sanitize callback to use sanitize_slug
    * Add changefile(s) from automation for the following project(s): woocommerce
    * Add unit tests
    * Fix linting

    ---------

    Co-authored-by: woocommercebot <woocommercebot@users.noreply.github.com>
    Co-authored-by: Vladimir Reznichenko <kalessil@gmail.com>

diff --git a/plugins/woocommerce/changelog/63342-fix-63276-update-slug-sanitize-callback b/plugins/woocommerce/changelog/63342-fix-63276-update-slug-sanitize-callback
new file mode 100644
index 00000000000..670717ac26d
--- /dev/null
+++ b/plugins/woocommerce/changelog/63342-fix-63276-update-slug-sanitize-callback
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Update slug sanitize callback to use sanitize_slug
\ No newline at end of file
diff --git a/plugins/woocommerce/includes/rest-api/Controllers/Version1/class-wc-rest-product-attribute-terms-v1-controller.php b/plugins/woocommerce/includes/rest-api/Controllers/Version1/class-wc-rest-product-attribute-terms-v1-controller.php
index 244530bfb85..564ba016667 100644
--- a/plugins/woocommerce/includes/rest-api/Controllers/Version1/class-wc-rest-product-attribute-terms-v1-controller.php
+++ b/plugins/woocommerce/includes/rest-api/Controllers/Version1/class-wc-rest-product-attribute-terms-v1-controller.php
@@ -211,7 +211,7 @@ class WC_REST_Product_Attribute_Terms_V1_Controller extends WC_REST_Terms_Contro
 					'type'        => 'string',
 					'context'     => array( 'view', 'edit' ),
 					'arg_options' => array(
-						'sanitize_callback' => 'sanitize_title',
+						'sanitize_callback' => array( $this, 'sanitize_slug' ),
 					),
 				),
 				'description' => array(
diff --git a/plugins/woocommerce/includes/rest-api/Controllers/Version1/class-wc-rest-product-attributes-v1-controller.php b/plugins/woocommerce/includes/rest-api/Controllers/Version1/class-wc-rest-product-attributes-v1-controller.php
index 0f947594e40..8f4804dd857 100644
--- a/plugins/woocommerce/includes/rest-api/Controllers/Version1/class-wc-rest-product-attributes-v1-controller.php
+++ b/plugins/woocommerce/includes/rest-api/Controllers/Version1/class-wc-rest-product-attributes-v1-controller.php
@@ -502,7 +502,7 @@ class WC_REST_Product_Attributes_V1_Controller extends WC_REST_Controller {
 					'type'        => 'string',
 					'context'     => array( 'view', 'edit' ),
 					'arg_options' => array(
-						'sanitize_callback' => 'sanitize_title',
+						'sanitize_callback' => array( $this, 'sanitize_slug' ),
 					),
 				),
 				'type'         => array(
diff --git a/plugins/woocommerce/includes/rest-api/Controllers/Version1/class-wc-rest-product-categories-v1-controller.php b/plugins/woocommerce/includes/rest-api/Controllers/Version1/class-wc-rest-product-categories-v1-controller.php
index 4d19661a01c..3c88b5e41e4 100644
--- a/plugins/woocommerce/includes/rest-api/Controllers/Version1/class-wc-rest-product-categories-v1-controller.php
+++ b/plugins/woocommerce/includes/rest-api/Controllers/Version1/class-wc-rest-product-categories-v1-controller.php
@@ -189,7 +189,7 @@ class WC_REST_Product_Categories_V1_Controller extends WC_REST_Terms_Controller
 					'type'        => 'string',
 					'context'     => array( 'view', 'edit' ),
 					'arg_options' => array(
-						'sanitize_callback' => 'sanitize_title',
+						'sanitize_callback' => array( $this, 'sanitize_slug' ),
 					),
 				),
 				'parent' => array(
diff --git a/plugins/woocommerce/includes/rest-api/Controllers/Version1/class-wc-rest-product-shipping-classes-v1-controller.php b/plugins/woocommerce/includes/rest-api/Controllers/Version1/class-wc-rest-product-shipping-classes-v1-controller.php
index 5de129c1333..d328980234b 100644
--- a/plugins/woocommerce/includes/rest-api/Controllers/Version1/class-wc-rest-product-shipping-classes-v1-controller.php
+++ b/plugins/woocommerce/includes/rest-api/Controllers/Version1/class-wc-rest-product-shipping-classes-v1-controller.php
@@ -109,7 +109,7 @@ class WC_REST_Product_Shipping_Classes_V1_Controller extends WC_REST_Terms_Contr
 					'type'        => 'string',
 					'context'     => array( 'view', 'edit' ),
 					'arg_options' => array(
-						'sanitize_callback' => 'sanitize_title',
+						'sanitize_callback' => array( $this, 'sanitize_slug' ),
 					),
 				),
 				'description' => array(
diff --git a/plugins/woocommerce/includes/rest-api/Controllers/Version1/class-wc-rest-product-tags-v1-controller.php b/plugins/woocommerce/includes/rest-api/Controllers/Version1/class-wc-rest-product-tags-v1-controller.php
index 121f101ee3d..667b6baf6f6 100644
--- a/plugins/woocommerce/includes/rest-api/Controllers/Version1/class-wc-rest-product-tags-v1-controller.php
+++ b/plugins/woocommerce/includes/rest-api/Controllers/Version1/class-wc-rest-product-tags-v1-controller.php
@@ -109,7 +109,7 @@ class WC_REST_Product_Tags_V1_Controller extends WC_REST_Terms_Controller {
 					'type'        => 'string',
 					'context'     => array( 'view', 'edit' ),
 					'arg_options' => array(
-						'sanitize_callback' => 'sanitize_title',
+						'sanitize_callback' => array( $this, 'sanitize_slug' ),
 					),
 				),
 				'description' => array(
diff --git a/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-product-categories-v2-controller.php b/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-product-categories-v2-controller.php
index a7f58f70e30..52daf698e4e 100644
--- a/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-product-categories-v2-controller.php
+++ b/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-product-categories-v2-controller.php
@@ -118,7 +118,7 @@ class WC_REST_Product_Categories_V2_Controller extends WC_REST_Product_Categorie
 					'type'        => 'string',
 					'context'     => array( 'view', 'edit' ),
 					'arg_options' => array(
-						'sanitize_callback' => 'sanitize_title',
+						'sanitize_callback' => array( $this, 'sanitize_slug' ),
 					),
 				),
 				'parent'      => array(
diff --git a/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-product-categories-controller.php b/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-product-categories-controller.php
index 4f9db596307..b81c13b85fa 100644
--- a/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-product-categories-controller.php
+++ b/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-product-categories-controller.php
@@ -118,7 +118,7 @@ class WC_REST_Product_Categories_Controller extends WC_REST_Product_Categories_V
 					'type'        => 'string',
 					'context'     => array( 'view', 'edit' ),
 					'arg_options' => array(
-						'sanitize_callback' => 'sanitize_title',
+						'sanitize_callback' => array( $this, 'sanitize_slug' ),
 					),
 				),
 				'parent'      => array(
diff --git a/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version1/class-wc-rest-product-attribute-terms-v1-controller-tests.php b/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version1/class-wc-rest-product-attribute-terms-v1-controller-tests.php
new file mode 100644
index 00000000000..8dddd3f8008
--- /dev/null
+++ b/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version1/class-wc-rest-product-attribute-terms-v1-controller-tests.php
@@ -0,0 +1,64 @@
+<?php
+declare( strict_types = 1 );
+
+use Automattic\WooCommerce\Tests\Blocks\Helpers\FixtureData;
+
+/**
+ * Product attribute terms controller tests for V1 REST API.
+ */
+class WC_REST_Product_Attribute_Terms_V1_Controller_Tests extends WC_REST_Unit_Test_Case {
+	/**
+	 * @var int Admin user id.
+	 */
+	private $admin_id;
+
+	/**
+	 * Test setup.
+	 */
+	public function setUp(): void {
+		parent::setUp();
+		$this->admin_id = $this->factory->user->create( array( 'role' => 'administrator' ) );
+	}
+
+	/**
+	 * @testdox Product attribute terms item schema contains expected properties.
+	 */
+	public function test_get_item_schema() {
+		wp_set_current_user( $this->admin_id );
+
+		$request    = new WP_REST_Request( 'OPTIONS', '/wc/v1/products/attributes/1/terms' );
+		$response   = $this->server->dispatch( $request );
+		$data       = $response->get_data();
+		$properties = $data['schema']['properties'];
+
+		$this->assertEquals( 200, $response->get_status() );
+
+		$this->assertArrayHasKey( 'id', $properties );
+		$this->assertArrayHasKey( 'name', $properties );
+		$this->assertArrayHasKey( 'slug', $properties );
+		$this->assertArrayHasKey( 'description', $properties );
+		$this->assertArrayHasKey( 'menu_order', $properties );
+		$this->assertArrayHasKey( 'count', $properties );
+	}
+
+	/**
+	 * @testdox Creating a product attribute term with an empty slug succeeds.
+	 */
+	public function test_create_with_empty_slug() {
+		wp_set_current_user( $this->admin_id );
+
+		$attribute = FixtureData::get_product_attribute( 'color', array( 'red', 'blue' ) );
+
+		$request = new WP_REST_Request( 'POST', '/wc/v1/products/attributes/' . $attribute['attribute_id'] . '/terms' );
+		$request->set_body_params(
+			array(
+				'name' => 'Test term',
+				'slug' => '',
+			)
+		);
+
+		$response = $this->server->dispatch( $request );
+
+		$this->assertEquals( 201, $response->get_status() );
+	}
+}
diff --git a/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version1/class-wc-rest-product-attributes-v1-controller-tests.php b/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version1/class-wc-rest-product-attributes-v1-controller-tests.php
index 9007ef44bce..5cfce90080e 100644
--- a/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version1/class-wc-rest-product-attributes-v1-controller-tests.php
+++ b/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version1/class-wc-rest-product-attributes-v1-controller-tests.php
@@ -4,12 +4,19 @@
  * class WC_REST_Product_Attributes_V1_Controller_Tests.
  * Product Attributes Controller tests for V1 REST API.
  */
-class WC_REST_Product_Attributes_V1_Controller_Tests extends WC_Unit_Test_Case {
+class WC_REST_Product_Attributes_V1_Controller_Tests extends WC_REST_Unit_Test_Case {
+	/**
+	 * @var int Admin user id.
+	 */
+	private $admin_id;

 	/**
 	 * Runs before any test.
 	 */
 	public function setUp(): void {
+		parent::setUp();
+		$this->admin_id = $this->factory->user->create( array( 'role' => 'administrator' ) );
+
 		// phpcs:disable Generic.CodeAnalysis, Squiz.Commenting
 		$this->sut = new class() extends WC_REST_Product_Attributes_V1_Controller {
 			public function get_taxonomy( $request ) {
@@ -40,5 +47,45 @@ class WC_REST_Product_Attributes_V1_Controller_Tests extends WC_Unit_Test_Case {
 		$this->assertEquals( 'taxonomy_1', $value1 );
 		$this->assertEquals( 'taxonomy_2', $value2 );
 	}
+
+	/**
+	 * @testdox Product attributes item schema contains expected properties.
+	 */
+	public function test_get_item_schema() {
+		wp_set_current_user( $this->admin_id );
+
+		$request    = new WP_REST_Request( 'OPTIONS', '/wc/v1/products/attributes' );
+		$response   = $this->server->dispatch( $request );
+		$data       = $response->get_data();
+		$properties = $data['schema']['properties'];
+
+		$this->assertEquals( 200, $response->get_status() );
+
+		$this->assertArrayHasKey( 'id', $properties );
+		$this->assertArrayHasKey( 'name', $properties );
+		$this->assertArrayHasKey( 'slug', $properties );
+		$this->assertArrayHasKey( 'type', $properties );
+		$this->assertArrayHasKey( 'order_by', $properties );
+		$this->assertArrayHasKey( 'has_archives', $properties );
+	}
+
+	/**
+	 * @testdox Creating a product attribute with an empty slug succeeds.
+	 */
+	public function test_create_with_empty_slug() {
+		wp_set_current_user( $this->admin_id );
+
+		$request = new WP_REST_Request( 'POST', '/wc/v1/products/attributes' );
+		$request->set_body_params(
+			array(
+				'name' => 'Test attribute',
+				'slug' => '',
+			)
+		);
+
+		$response = $this->server->dispatch( $request );
+
+		$this->assertEquals( 201, $response->get_status() );
+	}
 }

diff --git a/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version1/class-wc-rest-product-categories-v1-controller-tests.php b/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version1/class-wc-rest-product-categories-v1-controller-tests.php
new file mode 100644
index 00000000000..746f4533e53
--- /dev/null
+++ b/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version1/class-wc-rest-product-categories-v1-controller-tests.php
@@ -0,0 +1,63 @@
+<?php
+declare( strict_types = 1 );
+
+/**
+ * Product categories controller tests for V1 REST API.
+ */
+class WC_REST_Product_Categories_V1_Controller_Tests extends WC_REST_Unit_Test_Case {
+	/**
+	 * @var int Admin user id.
+	 */
+	private $admin_id;
+
+	/**
+	 * Test setup.
+	 */
+	public function setUp(): void {
+		parent::setUp();
+		$this->admin_id = $this->factory->user->create( array( 'role' => 'administrator' ) );
+	}
+
+	/**
+	 * @testdox Product categories V1 item schema contains expected properties.
+	 */
+	public function test_get_item_schema() {
+		wp_set_current_user( $this->admin_id );
+
+		$request    = new WP_REST_Request( 'OPTIONS', '/wc/v1/products/categories' );
+		$response   = $this->server->dispatch( $request );
+		$data       = $response->get_data();
+		$properties = $data['schema']['properties'];
+
+		$this->assertEquals( 200, $response->get_status() );
+
+		$this->assertArrayHasKey( 'id', $properties );
+		$this->assertArrayHasKey( 'name', $properties );
+		$this->assertArrayHasKey( 'slug', $properties );
+		$this->assertArrayHasKey( 'parent', $properties );
+		$this->assertArrayHasKey( 'description', $properties );
+		$this->assertArrayHasKey( 'display', $properties );
+		$this->assertArrayHasKey( 'image', $properties );
+		$this->assertArrayHasKey( 'menu_order', $properties );
+		$this->assertArrayHasKey( 'count', $properties );
+	}
+
+	/**
+	 * @testdox Creating a product category in v1 with an empty slug succeeds.
+	 */
+	public function test_create_with_empty_slug() {
+		wp_set_current_user( $this->admin_id );
+
+		$request = new WP_REST_Request( 'POST', '/wc/v1/products/categories' );
+		$request->set_body_params(
+			array(
+				'name' => 'Test category',
+				'slug' => '',
+			)
+		);
+
+		$response = $this->server->dispatch( $request );
+
+		$this->assertEquals( 201, $response->get_status() );
+	}
+}
diff --git a/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version1/class-wc-rest-product-shipping-classes-v1-controller-tests.php b/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version1/class-wc-rest-product-shipping-classes-v1-controller-tests.php
new file mode 100644
index 00000000000..a648df63847
--- /dev/null
+++ b/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version1/class-wc-rest-product-shipping-classes-v1-controller-tests.php
@@ -0,0 +1,59 @@
+<?php
+declare( strict_types = 1 );
+
+/**
+ * Product shipping classes controller tests for V1 REST API.
+ */
+class WC_REST_Product_Shipping_Classes_V1_Controller_Tests extends WC_REST_Unit_Test_Case {
+	/**
+	 * @var int Admin user id.
+	 */
+	private $admin_id;
+
+	/**
+	 * Test setup.
+	 */
+	public function setUp(): void {
+		parent::setUp();
+		$this->admin_id = $this->factory->user->create( array( 'role' => 'administrator' ) );
+	}
+
+	/**
+	 * @testdox Product shipping classes item schema contains expected properties.
+	 */
+	public function test_get_item_schema() {
+		wp_set_current_user( $this->admin_id );
+
+		$request    = new WP_REST_Request( 'OPTIONS', '/wc/v1/products/shipping_classes' );
+		$response   = $this->server->dispatch( $request );
+		$data       = $response->get_data();
+		$properties = $data['schema']['properties'];
+
+		$this->assertEquals( 200, $response->get_status() );
+
+		$this->assertArrayHasKey( 'id', $properties );
+		$this->assertArrayHasKey( 'name', $properties );
+		$this->assertArrayHasKey( 'slug', $properties );
+		$this->assertArrayHasKey( 'description', $properties );
+		$this->assertArrayHasKey( 'count', $properties );
+	}
+
+	/**
+	 * @testdox Creating a product shipping class with an empty slug succeeds.
+	 */
+	public function test_create_with_empty_slug() {
+		wp_set_current_user( $this->admin_id );
+
+		$request = new WP_REST_Request( 'POST', '/wc/v1/products/shipping_classes' );
+		$request->set_body_params(
+			array(
+				'name' => 'Test shipping class',
+				'slug' => '',
+			)
+		);
+
+		$response = $this->server->dispatch( $request );
+
+		$this->assertEquals( 201, $response->get_status() );
+	}
+}
diff --git a/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version1/class-wc-rest-product-tags-v1-controller-tests.php b/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version1/class-wc-rest-product-tags-v1-controller-tests.php
new file mode 100644
index 00000000000..d697e5b5497
--- /dev/null
+++ b/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version1/class-wc-rest-product-tags-v1-controller-tests.php
@@ -0,0 +1,59 @@
+<?php
+declare( strict_types = 1 );
+
+/**
+ * Product tags controller tests for V1 REST API.
+ */
+class WC_REST_Product_Tags_V1_Controller_Tests extends WC_REST_Unit_Test_Case {
+	/**
+	 * @var int Admin user id.
+	 */
+	private $admin_id;
+
+	/**
+	 * Test setup.
+	 */
+	public function setUp(): void {
+		parent::setUp();
+		$this->admin_id = $this->factory->user->create( array( 'role' => 'administrator' ) );
+	}
+
+	/**
+	 * @testdox Product tags item schema contains expected properties.
+	 */
+	public function test_get_item_schema() {
+		wp_set_current_user( $this->admin_id );
+
+		$request    = new WP_REST_Request( 'OPTIONS', '/wc/v1/products/tags' );
+		$response   = $this->server->dispatch( $request );
+		$data       = $response->get_data();
+		$properties = $data['schema']['properties'];
+
+		$this->assertEquals( 200, $response->get_status() );
+
+		$this->assertArrayHasKey( 'id', $properties );
+		$this->assertArrayHasKey( 'name', $properties );
+		$this->assertArrayHasKey( 'slug', $properties );
+		$this->assertArrayHasKey( 'description', $properties );
+		$this->assertArrayHasKey( 'count', $properties );
+	}
+
+	/**
+	 * @testdox Creating a product tag with an empty slug succeeds.
+	 */
+	public function test_create_with_empty_slug() {
+		wp_set_current_user( $this->admin_id );
+
+		$request = new WP_REST_Request( 'POST', '/wc/v1/products/tags' );
+		$request->set_body_params(
+			array(
+				'name' => 'Test tag',
+				'slug' => '',
+			)
+		);
+
+		$response = $this->server->dispatch( $request );
+
+		$this->assertEquals( 201, $response->get_status() );
+	}
+}
diff --git a/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version2/class-wc-rest-product-categories-v2-controller-tests.php b/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version2/class-wc-rest-product-categories-v2-controller-tests.php
new file mode 100644
index 00000000000..be11ab25cef
--- /dev/null
+++ b/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version2/class-wc-rest-product-categories-v2-controller-tests.php
@@ -0,0 +1,63 @@
+<?php
+declare( strict_types = 1 );
+
+/**
+ * Product categories controller tests for V2 REST API.
+ */
+class WC_REST_Product_Categories_V2_Controller_Tests extends WC_REST_Unit_Test_Case {
+	/**
+	 * @var int Admin user id.
+	 */
+	private $admin_id;
+
+	/**
+	 * Test setup.
+	 */
+	public function setUp(): void {
+		parent::setUp();
+		$this->admin_id = $this->factory->user->create( array( 'role' => 'administrator' ) );
+	}
+
+	/**
+	 * @testdox Product categories V2 item schema contains expected properties.
+	 */
+	public function test_get_item_schema() {
+		wp_set_current_user( $this->admin_id );
+
+		$request    = new WP_REST_Request( 'OPTIONS', '/wc/v2/products/categories' );
+		$response   = $this->server->dispatch( $request );
+		$data       = $response->get_data();
+		$properties = $data['schema']['properties'];
+
+		$this->assertEquals( 200, $response->get_status() );
+
+		$this->assertArrayHasKey( 'id', $properties );
+		$this->assertArrayHasKey( 'name', $properties );
+		$this->assertArrayHasKey( 'slug', $properties );
+		$this->assertArrayHasKey( 'parent', $properties );
+		$this->assertArrayHasKey( 'description', $properties );
+		$this->assertArrayHasKey( 'display', $properties );
+		$this->assertArrayHasKey( 'image', $properties );
+		$this->assertArrayHasKey( 'menu_order', $properties );
+		$this->assertArrayHasKey( 'count', $properties );
+	}
+
+	/**
+	 * @testdox Creating a product category in v2 with an empty slug succeeds.
+	 */
+	public function test_create_with_empty_slug() {
+		wp_set_current_user( $this->admin_id );
+
+		$request = new WP_REST_Request( 'POST', '/wc/v2/products/categories' );
+		$request->set_body_params(
+			array(
+				'name' => 'Test category',
+				'slug' => '',
+			)
+		);
+
+		$response = $this->server->dispatch( $request );
+
+		$this->assertEquals( 201, $response->get_status() );
+	}
+}
diff --git a/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version3/class-wc-rest-product-categories-controller-test.php b/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version3/class-wc-rest-product-categories-controller-test.php
index dcffcae92c9..41ae8a30ed7 100644
--- a/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version3/class-wc-rest-product-categories-controller-test.php
+++ b/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version3/class-wc-rest-product-categories-controller-test.php
@@ -284,4 +284,47 @@ class WC_REST_Product_Categories_Controller_Test extends WC_REST_Unit_Test_Case
 		// Clean up.
 		wp_delete_term( $category['term_id'], 'product_cat' );
 	}
+
+	/**
+	 * @testdox Product categories v3 item schema contains expected properties.
+	 */
+	public function test_get_item_schema() {
+		wp_set_current_user( $this->user );
+
+		$request    = new WP_REST_Request( 'OPTIONS', '/wc/v3/products/categories' );
+		$response   = $this->server->dispatch( $request );
+		$data       = $response->get_data();
+		$properties = $data['schema']['properties'];
+
+		$this->assertEquals( 200, $response->get_status() );
+
+		$this->assertArrayHasKey( 'id', $properties );
+		$this->assertArrayHasKey( 'name', $properties );
+		$this->assertArrayHasKey( 'slug', $properties );
+		$this->assertArrayHasKey( 'parent', $properties );
+		$this->assertArrayHasKey( 'description', $properties );
+		$this->assertArrayHasKey( 'display', $properties );
+		$this->assertArrayHasKey( 'image', $properties );
+		$this->assertArrayHasKey( 'menu_order', $properties );
+		$this->assertArrayHasKey( 'count', $properties );
+	}
+
+	/**
+	 * @testdox Creating a product category in v3 with an empty slug succeeds.
+	 */
+	public function test_create_with_empty_slug() {
+		wp_set_current_user( $this->user );
+
+		$request = new WP_REST_Request( 'POST', '/wc/v3/products/categories' );
+		$request->set_body_params(
+			array(
+				'name' => 'Test category',
+				'slug' => '',
+			)
+		);
+
+		$response = $this->server->dispatch( $request );
+
+		$this->assertEquals( 201, $response->get_status() );
+	}
 }