diff --git a/kell_creations_apps/packages/feature_wordpress/lib/src/data/wordpress_product_publishing_repository.dart b/kell_creations_apps/packages/feature_wordpress/lib/src/data/wordpress_product_publishing_repository.dart index 80e47ff..139996e 100644 --- a/kell_creations_apps/packages/feature_wordpress/lib/src/data/wordpress_product_publishing_repository.dart +++ b/kell_creations_apps/packages/feature_wordpress/lib/src/data/wordpress_product_publishing_repository.dart @@ -6,10 +6,8 @@ import 'wordpress_product_mapper.dart'; /// Real [ProductPublishingRepository] backed by the WooCommerce REST API. /// -/// Supports read-only product retrieval and status updates via -/// `PUT /wp-json/wc/v3/products/{id}`. The [publishDraft] method throws -/// [UnimplementedError] — full publishing support will be added in a future -/// iteration. +/// Supports product retrieval, publishing, and status updates via +/// `PUT /wp-json/wc/v3/products/{id}`. class WordPressProductPublishingRepository implements ProductPublishingRepository { final WooCommerceApiClient _apiClient; final WordPressProductMapper _mapper; @@ -27,14 +25,9 @@ class WordPressProductPublishingRepository implements ProductPublishingRepositor } @override - Future publishDraft(String id) { - // Publishing is not yet implemented for the real backend. - // This will be added in a future iteration alongside media upload - // and order sync. - throw UnimplementedError( - 'WordPressProductPublishingRepository.publishDraft is not yet implemented. ' - 'Use FakeProductPublishingRepository for development.', - ); + Future publishDraft(String id) async { + final json = await _apiClient.updateProduct(id, {'status': 'publish'}); + return _mapper.fromJson(json); } @override diff --git a/kell_creations_apps/packages/feature_wordpress/test/wordpress_product_publishing_repository_test.dart b/kell_creations_apps/packages/feature_wordpress/test/wordpress_product_publishing_repository_test.dart index 6698eeb..f675302 100644 --- a/kell_creations_apps/packages/feature_wordpress/test/wordpress_product_publishing_repository_test.dart +++ b/kell_creations_apps/packages/feature_wordpress/test/wordpress_product_publishing_repository_test.dart @@ -120,21 +120,106 @@ void main() { expect(drafts[1].name, 'Product 2'); }); - test('publishDraft throws UnimplementedError', () { - final mockClient = MockClient((request) async { - return http.Response('[]', 200); + group('publishDraft', () { + /// Builds a single WooCommerce product JSON response with the given + /// [id] and status set to `'publish'`. + Map buildPublishedProductJson(int id) { + return { + 'id': id, + 'name': 'Product $id', + 'description': '

Description

', + 'price': '10.99', + 'sku': 'SKU-$id', + 'status': 'publish', + 'date_modified': '2026-04-01T10:00:00', + 'date_created': '2026-03-01T08:00:00', + 'categories': [ + {'id': 1, 'name': 'Test Category', 'slug': 'test-category'}, + ], + 'images': [ + {'id': 1, 'src': 'https://example.com/img$id.jpg'}, + ], + }; + } + + test('sends PUT with status publish and returns mapped ProductDraft', () async { + Map? capturedBody; + Uri? capturedUri; + String? capturedMethod; + + final mockClient = MockClient((request) async { + capturedMethod = request.method; + capturedUri = request.url; + capturedBody = jsonDecode(request.body) as Map; + + return http.Response(jsonEncode(buildPublishedProductJson(42)), 200); + }); + + final apiClient = WooCommerceApiClient( + siteUrl: 'https://store.example.com', + consumerKey: 'ck_test', + consumerSecret: 'cs_test', + httpClient: mockClient, + ); + + final repository = WordPressProductPublishingRepository(apiClient: apiClient); + final result = await repository.publishDraft('42'); + + // Verify the HTTP request shape. + expect(capturedMethod, 'PUT'); + expect(capturedUri!.path, '/wp-json/wc/v3/products/42'); + expect(capturedBody, {'status': 'publish'}); + + // Verify the returned domain object is cleanly mapped. + expect(result, isA()); + expect(result.id, '42'); + expect(result.name, 'Product 42'); + expect(result.status, PublishStatus.published); }); - final apiClient = WooCommerceApiClient( - siteUrl: 'https://store.example.com', - consumerKey: 'ck_test', - consumerSecret: 'cs_test', - httpClient: mockClient, - ); + test('propagates WooCommerceApiException on non-200 response', () async { + final mockClient = MockClient((request) async { + return http.Response('{"code":"rest_cannot_edit"}', 403); + }); - final repository = WordPressProductPublishingRepository(apiClient: apiClient); + final apiClient = WooCommerceApiClient( + siteUrl: 'https://store.example.com', + consumerKey: 'ck_test', + consumerSecret: 'cs_test', + httpClient: mockClient, + ); - expect(() => repository.publishDraft('1'), throwsA(isA())); + final repository = WordPressProductPublishingRepository(apiClient: apiClient); + + expect(() => repository.publishDraft('42'), throwsA(isA())); + }); + + test('returns ProductDraft, not raw WooCommerce JSON (package boundary)', () async { + final mockClient = MockClient((request) async { + final json = buildPublishedProductJson(9); + json['permalink'] = 'https://store.example.com/product/9'; + json['meta_data'] = [ + {'id': 1, 'key': '_internal', 'value': 'secret'}, + ]; + return http.Response(jsonEncode(json), 200); + }); + + final apiClient = WooCommerceApiClient( + siteUrl: 'https://store.example.com', + consumerKey: 'ck_test', + consumerSecret: 'cs_test', + httpClient: mockClient, + ); + + final repository = WordPressProductPublishingRepository(apiClient: apiClient); + final result = await repository.publishDraft('9'); + + // The result is a domain ProductDraft — no WooCommerce-specific + // fields like permalink or meta_data are exposed. + expect(result, isA()); + expect(result.id, '9'); + expect(result.status, PublishStatus.published); + }); }); test('getProductDrafts propagates API errors', () async {