From b5a3d77b9e2890fd3213ac5a8a9fe774da03b5bd Mon Sep 17 00:00:00 2001 From: OMpawar-21 Date: Wed, 25 Feb 2026 14:20:19 +0530 Subject: [PATCH 1/5] Add AddQueryResource alternatives for bulk unpublish query params in BulkUnpublishService.cs. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Description In BulkUnpublishService.cs, two comment-only lines were added to document an alternative way of sending the bulk unpublish options: skip_workflow_stage_check – A commented call AddQueryResource("skip_workflow_stage_check", "true") was added next to the existing Headers["skip_workflow_stage_check"] = "true" assignment. approvals – A commented call AddQueryResource("approvals", "true") was added next to the existing Headers["approvals"] = "true" assignment. --- .../Contentstack.Management.Core.Tests.csproj | 5 ----- .../Services/Stack/BulkOperation/BulkUnpublishService.cs | 2 ++ 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/Contentstack.Management.Core.Tests/Contentstack.Management.Core.Tests.csproj b/Contentstack.Management.Core.Tests/Contentstack.Management.Core.Tests.csproj index f8be953..076a59c 100644 --- a/Contentstack.Management.Core.Tests/Contentstack.Management.Core.Tests.csproj +++ b/Contentstack.Management.Core.Tests/Contentstack.Management.Core.Tests.csproj @@ -35,11 +35,6 @@ - - - PreserveNewest - - diff --git a/Contentstack.Management.Core/Services/Stack/BulkOperation/BulkUnpublishService.cs b/Contentstack.Management.Core/Services/Stack/BulkOperation/BulkUnpublishService.cs index 8d9689d..faddc5b 100644 --- a/Contentstack.Management.Core/Services/Stack/BulkOperation/BulkUnpublishService.cs +++ b/Contentstack.Management.Core/Services/Stack/BulkOperation/BulkUnpublishService.cs @@ -39,11 +39,13 @@ public BulkUnpublishService(JsonSerializer serializer, Contentstack.Management.C if (_skipWorkflowStage) { Headers["skip_workflow_stage_check"] = "true"; + // AddQueryResource("skip_workflow_stage_check", "true"); } if (_approvals) { Headers["approvals"] = "true"; + // AddQueryResource("approvals", "true"); } if (_isNested) From a55088a2f528ad9a7aba081ec5b530a86eeeb70c Mon Sep 17 00:00:00 2001 From: OMpawar-21 Date: Wed, 25 Feb 2026 14:24:54 +0530 Subject: [PATCH 2/5] =?UTF-8?q?Add=20AddQueryResource=20alternatives=20for?= =?UTF-8?q?=20bulk=20unpublish=20query=20params=20in=20Bul=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/Stack/BulkOperation/BulkUnpublishService.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Contentstack.Management.Core/Services/Stack/BulkOperation/BulkUnpublishService.cs b/Contentstack.Management.Core/Services/Stack/BulkOperation/BulkUnpublishService.cs index faddc5b..0993409 100644 --- a/Contentstack.Management.Core/Services/Stack/BulkOperation/BulkUnpublishService.cs +++ b/Contentstack.Management.Core/Services/Stack/BulkOperation/BulkUnpublishService.cs @@ -38,14 +38,12 @@ public BulkUnpublishService(JsonSerializer serializer, Contentstack.Management.C // Set headers based on parameters if (_skipWorkflowStage) { - Headers["skip_workflow_stage_check"] = "true"; - // AddQueryResource("skip_workflow_stage_check", "true"); + AddQueryResource("skip_workflow_stage_check", "true"); } if (_approvals) { - Headers["approvals"] = "true"; - // AddQueryResource("approvals", "true"); + AddQueryResource("approvals", "true"); } if (_isNested) From 68132b9174dc00074e7689f1899f2a55eae9e130 Mon Sep 17 00:00:00 2001 From: OMpawar-21 Date: Mon, 2 Mar 2026 11:40:20 +0530 Subject: [PATCH 3/5] feat(DX-3233): send bulk publish/unpublish flags as query params and add integration tests - Bulk publish/unpublish: send skip_workflow_stage_check and approvals as query params via AddQueryResource instead of headers (BulkPublishService; BulkUnpublishService already used query params). - Unit tests: in BulkPublishServiceTest, BulkUnpublishServiceTest, and BulkOperationServicesTest, assert on QueryResources instead of Headers for these two flags. - Integration tests: add EnsureBulkTestContentTypeAndEntriesAsync() so bulk_test_content_type and at least one entry exist; add Test003a (bulk publish with skipWorkflowStage and approvals) and Test004a (bulk unpublish with same flags). --- CHANGELOG.md | 7 + .../Contentstack.Management.Core.Tests.csproj | 3 +- .../Contentstack015_BulkOperationTest.cs | 142 ++++++++++++++++++ .../Services/Stack/BulkPublishServiceTest.cs | 12 +- .../Stack/BulkUnpublishServiceTest.cs | 12 +- .../Services/BulkOperationServicesTest.cs | 24 +-- .../Stack/BulkOperation/BulkPublishService.cs | 6 +- 7 files changed, 178 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee2230a..62ca689 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [v0.7.0](https://github.com/contentstack/contentstack-management-dotnet/tree/v0.7.0) + - Feat + - **Bulk publish/unpublish: query parameters (DX-3233)** + - `skip_workflow_stage_check` and `approvals` are now sent as query parameters instead of headers for bulk publish and bulk unpublish + - Unit tests updated to assert on `QueryResources` for these flags (BulkPublishServiceTest, BulkUnpublishServiceTest, BulkOperationServicesTest) + - Integration tests: bulk publish with skipWorkflowStage and approvals (Test003a), bulk unpublish with skipWorkflowStage and approvals (Test004a), and helper `EnsureBulkTestContentTypeAndEntriesAsync()` so bulk tests can run in any order + ## [v0.6.1](https://github.com/contentstack/contentstack-management-dotnet/tree/v0.6.1) (2026-02-02) - Fix - Release DELETE request no longer includes Content-Type header to comply with API requirements diff --git a/Contentstack.Management.Core.Tests/Contentstack.Management.Core.Tests.csproj b/Contentstack.Management.Core.Tests/Contentstack.Management.Core.Tests.csproj index 076a59c..4ee1a12 100644 --- a/Contentstack.Management.Core.Tests/Contentstack.Management.Core.Tests.csproj +++ b/Contentstack.Management.Core.Tests/Contentstack.Management.Core.Tests.csproj @@ -4,7 +4,7 @@ net7.0 false - $(Version) + 0.1.3 true ../CSManagementSDK.snk @@ -24,6 +24,7 @@ + diff --git a/Contentstack.Management.Core.Tests/IntegrationTest/Contentstack015_BulkOperationTest.cs b/Contentstack.Management.Core.Tests/IntegrationTest/Contentstack015_BulkOperationTest.cs index 9cbc4f0..52352a0 100644 --- a/Contentstack.Management.Core.Tests/IntegrationTest/Contentstack015_BulkOperationTest.cs +++ b/Contentstack.Management.Core.Tests/IntegrationTest/Contentstack015_BulkOperationTest.cs @@ -196,6 +196,82 @@ public async Task Test004_Should_Perform_Bulk_Unpublish_Operation() } } + [TestMethod] + [DoNotParallelize] + public async Task Test003a_Should_Perform_Bulk_Publish_With_SkipWorkflowStage_And_Approvals() + { + try + { + await EnsureBulkTestContentTypeAndEntriesAsync(); + + List availableEntries = await FetchExistingEntries(); + Assert.IsTrue(availableEntries.Count > 0, "No entries available for bulk operation"); + + List availableEnvironments = await GetAvailableEnvironments(); + + var publishDetails = new BulkPublishDetails + { + Entries = availableEntries.Select(e => new BulkPublishEntry + { + Uid = e.Uid, + ContentType = _contentTypeUid, + Version = e.Version, + Locale = "en-us" + }).ToList(), + Locales = new List { "en-us" }, + Environments = availableEnvironments + }; + + ContentstackResponse response = _stack.BulkOperation().Publish(publishDetails, skipWorkflowStage: true, approvals: true); + var responseJson = response.OpenJObjectResponse(); + + Assert.IsNotNull(response); + Assert.IsTrue(response.IsSuccessStatusCode); + } + catch (Exception e) + { + Assert.Fail($"Failed to perform bulk publish with skipWorkflowStage and approvals: {e.Message}"); + } + } + + [TestMethod] + [DoNotParallelize] + public async Task Test004a_Should_Perform_Bulk_Unpublish_With_SkipWorkflowStage_And_Approvals() + { + try + { + await EnsureBulkTestContentTypeAndEntriesAsync(); + + List availableEntries = await FetchExistingEntries(); + Assert.IsTrue(availableEntries.Count > 0, "No entries available for bulk operation"); + + List availableEnvironments = await GetAvailableEnvironments(); + + var unpublishDetails = new BulkPublishDetails + { + Entries = availableEntries.Select(e => new BulkPublishEntry + { + Uid = e.Uid, + ContentType = _contentTypeUid, + Version = e.Version, + Locale = "en-us" + }).ToList(), + Locales = new List { "en-us" }, + Environments = availableEnvironments + }; + + ContentstackResponse response = _stack.BulkOperation().Unpublish(unpublishDetails, skipWorkflowStage: true, approvals: true); + var responseJson = response.OpenJObjectResponse(); + + Assert.IsNotNull(response); + Assert.IsTrue(response.IsSuccessStatusCode); + } + catch (Exception e) + { + Assert.Fail($"Failed to perform bulk unpublish with skipWorkflowStage and approvals: {e.Message}"); + } + } + [TestMethod] [DoNotParallelize] public async Task Test005_Should_Perform_Bulk_Release_Operations() @@ -570,6 +646,72 @@ private async Task> GetAvailableEnvironments() } } + /// + /// Ensures bulk_test_content_type exists and has at least one entry so bulk tests can run in any order. + /// + private async Task EnsureBulkTestContentTypeAndEntriesAsync() + { + try + { + bool contentTypeExists = false; + try + { + ContentstackResponse ctResponse = _stack.ContentType(_contentTypeUid).Fetch(); + contentTypeExists = ctResponse.IsSuccessStatusCode; + } + catch + { + // Content type not found + } + + if (!contentTypeExists) + { + await CreateTestEnvironment(); + await CreateTestRelease(); + var contentModelling = new ContentModelling + { + Title = "bulk_test_content_type", + Uid = _contentTypeUid, + Schema = new List + { + new TextboxField + { + DisplayName = "Title", + Uid = "title", + DataType = "text", + Mandatory = true, + Unique = false, + Multiple = false + } + } + }; + _stack.ContentType().Create(contentModelling); + } + + // Ensure at least one entry exists + List existing = await FetchExistingEntries(); + if (existing == null || existing.Count == 0) + { + var entry = new SimpleEntry { Title = "Bulk test entry" }; + ContentstackResponse createResponse = _stack.ContentType(_contentTypeUid).Entry().Create(entry); + var responseJson = createResponse.OpenJObjectResponse(); + if (createResponse.IsSuccessStatusCode && responseJson["entry"] != null && responseJson["entry"]["uid"] != null) + { + _createdEntries.Add(new EntryInfo + { + Uid = responseJson["entry"]["uid"].ToString(), + Title = responseJson["entry"]["title"]?.ToString() ?? "Bulk test entry", + Version = responseJson["entry"]["_version"] != null ? (int)responseJson["entry"]["_version"] : 1 + }); + } + } + } + catch (Exception) + { + // Caller will handle if entries are still missing + } + } + private async Task> FetchExistingEntries() { try diff --git a/Contentstack.Management.Core.Unit.Tests/Core/Services/Stack/BulkPublishServiceTest.cs b/Contentstack.Management.Core.Unit.Tests/Core/Services/Stack/BulkPublishServiceTest.cs index 19d2073..73284a2 100644 --- a/Contentstack.Management.Core.Unit.Tests/Core/Services/Stack/BulkPublishServiceTest.cs +++ b/Contentstack.Management.Core.Unit.Tests/Core/Services/Stack/BulkPublishServiceTest.cs @@ -48,25 +48,25 @@ public void Should_Create_Service_With_Valid_Parameters() } [TestMethod] - public void Should_Set_Skip_Workflow_Stage_Header_When_True() + public void Should_Set_Skip_Workflow_Stage_Query_Parameter_When_True() { var details = new BulkPublishDetails(); var service = new BulkPublishService(serializer, new Management.Core.Models.Stack(null), details, skipWorkflowStage: true); Assert.IsNotNull(service); - Assert.IsTrue(service.Headers.ContainsKey("skip_workflow_stage_check")); - Assert.AreEqual("true", service.Headers["skip_workflow_stage_check"]); + Assert.IsTrue(service.QueryResources.ContainsKey("skip_workflow_stage_check")); + Assert.AreEqual("true", service.QueryResources["skip_workflow_stage_check"]); } [TestMethod] - public void Should_Set_Approvals_Header_When_True() + public void Should_Set_Approvals_Query_Parameter_When_True() { var details = new BulkPublishDetails(); var service = new BulkPublishService(serializer, new Management.Core.Models.Stack(null), details, approvals: true); Assert.IsNotNull(service); - Assert.IsTrue(service.Headers.ContainsKey("approvals")); - Assert.AreEqual("true", service.Headers["approvals"]); + Assert.IsTrue(service.QueryResources.ContainsKey("approvals")); + Assert.AreEqual("true", service.QueryResources["approvals"]); } [TestMethod] diff --git a/Contentstack.Management.Core.Unit.Tests/Core/Services/Stack/BulkUnpublishServiceTest.cs b/Contentstack.Management.Core.Unit.Tests/Core/Services/Stack/BulkUnpublishServiceTest.cs index d6e0a65..ff9b709 100644 --- a/Contentstack.Management.Core.Unit.Tests/Core/Services/Stack/BulkUnpublishServiceTest.cs +++ b/Contentstack.Management.Core.Unit.Tests/Core/Services/Stack/BulkUnpublishServiceTest.cs @@ -48,25 +48,25 @@ public void Should_Create_Service_With_Valid_Parameters() } [TestMethod] - public void Should_Set_Skip_Workflow_Stage_Header_When_True() + public void Should_Set_Skip_Workflow_Stage_Query_Parameter_When_True() { var details = new BulkPublishDetails(); var service = new BulkUnpublishService(serializer, new Management.Core.Models.Stack(null), details, skipWorkflowStage: true); Assert.IsNotNull(service); - Assert.IsTrue(service.Headers.ContainsKey("skip_workflow_stage_check")); - Assert.AreEqual("true", service.Headers["skip_workflow_stage_check"]); + Assert.IsTrue(service.QueryResources.ContainsKey("skip_workflow_stage_check")); + Assert.AreEqual("true", service.QueryResources["skip_workflow_stage_check"]); } [TestMethod] - public void Should_Set_Approvals_Header_When_True() + public void Should_Set_Approvals_Query_Parameter_When_True() { var details = new BulkPublishDetails(); var service = new BulkUnpublishService(serializer, new Management.Core.Models.Stack(null), details, approvals: true); Assert.IsNotNull(service); - Assert.IsTrue(service.Headers.ContainsKey("approvals")); - Assert.AreEqual("true", service.Headers["approvals"]); + Assert.IsTrue(service.QueryResources.ContainsKey("approvals")); + Assert.AreEqual("true", service.QueryResources["approvals"]); } [TestMethod] diff --git a/Contentstack.Management.Core.Unit.Tests/Services/BulkOperationServicesTest.cs b/Contentstack.Management.Core.Unit.Tests/Services/BulkOperationServicesTest.cs index 01c6b51..f2ccf92 100644 --- a/Contentstack.Management.Core.Unit.Tests/Services/BulkOperationServicesTest.cs +++ b/Contentstack.Management.Core.Unit.Tests/Services/BulkOperationServicesTest.cs @@ -142,10 +142,10 @@ public void Test004_BulkPublishService_Initialization() Assert.IsNotNull(service); Assert.AreEqual("/bulk/publish", service.ResourcePath); Assert.AreEqual("POST", service.HttpMethod); - Assert.IsTrue(service.Headers.ContainsKey("skip_workflow_stage_check")); - Assert.IsTrue(service.Headers.ContainsKey("approvals")); - Assert.AreEqual("true", service.Headers["skip_workflow_stage_check"]); - Assert.AreEqual("true", service.Headers["approvals"]); + Assert.IsTrue(service.QueryResources.ContainsKey("skip_workflow_stage_check")); + Assert.IsTrue(service.QueryResources.ContainsKey("approvals")); + Assert.AreEqual("true", service.QueryResources["skip_workflow_stage_check"]); + Assert.AreEqual("true", service.QueryResources["approvals"]); } [TestMethod] @@ -197,10 +197,10 @@ public void Test006_BulkPublishService_With_All_Flags() var service = new BulkPublishService(_serializer, _stack, publishDetails, true, true, true); Assert.IsNotNull(service); - Assert.IsTrue(service.Headers.ContainsKey("skip_workflow_stage_check")); - Assert.IsTrue(service.Headers.ContainsKey("approvals")); - Assert.AreEqual("true", service.Headers["skip_workflow_stage_check"]); - Assert.AreEqual("true", service.Headers["approvals"]); + Assert.IsTrue(service.QueryResources.ContainsKey("skip_workflow_stage_check")); + Assert.IsTrue(service.QueryResources.ContainsKey("approvals")); + Assert.AreEqual("true", service.QueryResources["skip_workflow_stage_check"]); + Assert.AreEqual("true", service.QueryResources["approvals"]); } [TestMethod] @@ -218,8 +218,8 @@ public void Test007_BulkPublishService_Without_Flags() var service = new BulkPublishService(_serializer, _stack, publishDetails, false, false, false); Assert.IsNotNull(service); - Assert.IsFalse(service.Headers.ContainsKey("skip_workflow_stage_check")); - Assert.IsFalse(service.Headers.ContainsKey("approvals")); + Assert.IsFalse(service.QueryResources.ContainsKey("skip_workflow_stage_check")); + Assert.IsFalse(service.QueryResources.ContainsKey("approvals")); } [TestMethod] @@ -248,8 +248,8 @@ public void Test008_BulkUnpublishService_Initialization() Assert.IsNotNull(service); Assert.AreEqual("/bulk/unpublish", service.ResourcePath); Assert.AreEqual("POST", service.HttpMethod); - Assert.IsTrue(service.Headers.ContainsKey("skip_workflow_stage_check")); - Assert.IsTrue(service.Headers.ContainsKey("approvals")); + Assert.IsTrue(service.QueryResources.ContainsKey("skip_workflow_stage_check")); + Assert.IsTrue(service.QueryResources.ContainsKey("approvals")); } [TestMethod] diff --git a/Contentstack.Management.Core/Services/Stack/BulkOperation/BulkPublishService.cs b/Contentstack.Management.Core/Services/Stack/BulkOperation/BulkPublishService.cs index 1aa6c87..5bf5a19 100644 --- a/Contentstack.Management.Core/Services/Stack/BulkOperation/BulkPublishService.cs +++ b/Contentstack.Management.Core/Services/Stack/BulkOperation/BulkPublishService.cs @@ -35,15 +35,15 @@ public BulkPublishService(JsonSerializer serializer, Contentstack.Management.Cor ResourcePath = "/bulk/publish"; HttpMethod = "POST"; - // Set headers based on parameters + // Set query parameters based on options if (_skipWorkflowStage) { - Headers["skip_workflow_stage_check"] = "true"; + AddQueryResource("skip_workflow_stage_check", "true"); } if (_approvals) { - Headers["approvals"] = "true"; + AddQueryResource("approvals", "true"); } if (_isNested) From 48807128eb7cb89abd0f336e2413dabbf7350b84 Mon Sep 17 00:00:00 2001 From: OMpawar-21 Date: Mon, 2 Mar 2026 12:05:41 +0530 Subject: [PATCH 4/5] Update Directory.Build.props --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 735f780..d79e191 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,5 +1,5 @@ - 0.6.1 + 0.7.0 From 896c82177996527456993d56d995d48f0a9761e0 Mon Sep 17 00:00:00 2001 From: OMpawar-21 Date: Mon, 2 Mar 2026 15:35:00 +0530 Subject: [PATCH 5/5] =?UTF-8?q?feat:=20bulk=20ops=20=E2=80=93=20add=20api?= =?UTF-8?q?=5Fversion=203.2=20tests=20and=20robust=20status/error=20handli?= =?UTF-8?q?ng?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Integration tests (Contentstack015_BulkOperationTest): - API version 3.2: - Test003b: bulk publish with skipWorkflowStage, approvals, and apiVersion "3.2" (api_version header). - Test004b: bulk unpublish with skipWorkflowStage, approvals, and apiVersion "3.2" (api_version header). - Error handling and assertions: - Add FailWithError(operation, ex) to report HTTP status, ErrorCode, and API message on ContentstackErrorException. - In Test003a, Test004a, Test003b, Test004b: assert response.StatusCode == HttpStatusCode.OK and use FailWithError in catch. - Add Test004c: negative test for bulk unpublish with invalid data (empty entries, non-existent env); expect ContentstackErrorException and assert non-success status and presence of error message. - Usings: System.Net (HttpStatusCode), Contentstack.Management.Core.Exceptions (ContentstackErrorException). --- .../Contentstack015_BulkOperationTest.cs | 142 +++++++++++++++++- 1 file changed, 135 insertions(+), 7 deletions(-) diff --git a/Contentstack.Management.Core.Tests/IntegrationTest/Contentstack015_BulkOperationTest.cs b/Contentstack.Management.Core.Tests/IntegrationTest/Contentstack015_BulkOperationTest.cs index 52352a0..6304cac 100644 --- a/Contentstack.Management.Core.Tests/IntegrationTest/Contentstack015_BulkOperationTest.cs +++ b/Contentstack.Management.Core.Tests/IntegrationTest/Contentstack015_BulkOperationTest.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net; using System.Threading.Tasks; +using Contentstack.Management.Core.Exceptions; using Contentstack.Management.Core.Models; using Contentstack.Management.Core.Models.Fields; using Contentstack.Management.Core.Tests.Model; @@ -21,6 +23,17 @@ public class Contentstack015_BulkOperationTest private string _testReleaseUid = "bulk_test_release"; private List _createdEntries = new List(); + /// + /// Fails the test with a clear message from ContentstackErrorException or generic exception. + /// + private static void FailWithError(string operation, Exception ex) + { + if (ex is ContentstackErrorException cex) + Assert.Fail($"{operation} failed. HTTP {(int)cex.StatusCode} ({cex.StatusCode}). ErrorCode: {cex.ErrorCode}. Message: {cex.ErrorMessage ?? cex.Message}"); + else + Assert.Fail($"{operation} failed: {ex.Message}"); + } + [TestInitialize] public async Task Initialize() { @@ -223,14 +236,17 @@ public async Task Test003a_Should_Perform_Bulk_Publish_With_SkipWorkflowStage_An }; ContentstackResponse response = _stack.BulkOperation().Publish(publishDetails, skipWorkflowStage: true, approvals: true); - var responseJson = response.OpenJObjectResponse(); Assert.IsNotNull(response); - Assert.IsTrue(response.IsSuccessStatusCode); + Assert.IsTrue(response.IsSuccessStatusCode, $"Bulk publish failed with status {(int)response.StatusCode} ({response.StatusCode})."); + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode, $"Expected 200 OK, got {(int)response.StatusCode}."); + + var responseJson = response.OpenJObjectResponse(); + Assert.IsNotNull(responseJson); } - catch (Exception e) + catch (Exception ex) { - Assert.Fail($"Failed to perform bulk publish with skipWorkflowStage and approvals: {e.Message}"); + FailWithError("Bulk publish with skipWorkflowStage and approvals", ex); } } @@ -261,14 +277,126 @@ public async Task Test004a_Should_Perform_Bulk_Unpublish_With_SkipWorkflowStage_ }; ContentstackResponse response = _stack.BulkOperation().Unpublish(unpublishDetails, skipWorkflowStage: true, approvals: true); + + Assert.IsNotNull(response); + Assert.IsTrue(response.IsSuccessStatusCode, $"Bulk unpublish failed with status {(int)response.StatusCode} ({response.StatusCode})."); + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode, $"Expected 200 OK, got {(int)response.StatusCode}."); + var responseJson = response.OpenJObjectResponse(); + Assert.IsNotNull(responseJson); + } + catch (Exception ex) + { + FailWithError("Bulk unpublish with skipWorkflowStage and approvals", ex); + } + } + + [TestMethod] + [DoNotParallelize] + public async Task Test003b_Should_Perform_Bulk_Publish_With_ApiVersion_3_2() + { + try + { + await EnsureBulkTestContentTypeAndEntriesAsync(); + + List availableEntries = await FetchExistingEntries(); + Assert.IsTrue(availableEntries.Count > 0, "No entries available for bulk operation"); + + List availableEnvironments = await GetAvailableEnvironments(); + + var publishDetails = new BulkPublishDetails + { + Entries = availableEntries.Select(e => new BulkPublishEntry + { + Uid = e.Uid, + ContentType = _contentTypeUid, + Version = e.Version, + Locale = "en-us" + }).ToList(), + Locales = new List { "en-us" }, + Environments = availableEnvironments + }; + + ContentstackResponse response = _stack.BulkOperation().Publish(publishDetails, skipWorkflowStage: true, approvals: true, apiVersion: "3.2"); Assert.IsNotNull(response); - Assert.IsTrue(response.IsSuccessStatusCode); + Assert.IsTrue(response.IsSuccessStatusCode, $"Bulk publish with api_version 3.2 failed with status {(int)response.StatusCode} ({response.StatusCode})."); + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode, $"Expected 200 OK, got {(int)response.StatusCode}."); + + var responseJson = response.OpenJObjectResponse(); + Assert.IsNotNull(responseJson); } - catch (Exception e) + catch (Exception ex) + { + FailWithError("Bulk publish with api_version 3.2", ex); + } + } + + [TestMethod] + [DoNotParallelize] + public async Task Test004b_Should_Perform_Bulk_Unpublish_With_ApiVersion_3_2() + { + try + { + await EnsureBulkTestContentTypeAndEntriesAsync(); + + List availableEntries = await FetchExistingEntries(); + Assert.IsTrue(availableEntries.Count > 0, "No entries available for bulk operation"); + + List availableEnvironments = await GetAvailableEnvironments(); + + var unpublishDetails = new BulkPublishDetails + { + Entries = availableEntries.Select(e => new BulkPublishEntry + { + Uid = e.Uid, + ContentType = _contentTypeUid, + Version = e.Version, + Locale = "en-us" + }).ToList(), + Locales = new List { "en-us" }, + Environments = availableEnvironments + }; + + ContentstackResponse response = _stack.BulkOperation().Unpublish(unpublishDetails, skipWorkflowStage: true, approvals: true, apiVersion: "3.2"); + + Assert.IsNotNull(response); + Assert.IsTrue(response.IsSuccessStatusCode, $"Bulk unpublish with api_version 3.2 failed with status {(int)response.StatusCode} ({response.StatusCode})."); + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode, $"Expected 200 OK, got {(int)response.StatusCode}."); + + var responseJson = response.OpenJObjectResponse(); + Assert.IsNotNull(responseJson); + } + catch (Exception ex) + { + FailWithError("Bulk unpublish with api_version 3.2", ex); + } + } + + [TestMethod] + [DoNotParallelize] + public void Test004c_Should_Return_Error_When_Bulk_Unpublish_With_Invalid_Data() + { + var invalidDetails = new BulkPublishDetails + { + Entries = new List(), + Locales = new List { "en-us" }, + Environments = new List { "non_existent_environment_uid" } + }; + + try + { + _stack.BulkOperation().Unpublish(invalidDetails); + Assert.Fail("Expected ContentstackErrorException was not thrown."); + } + catch (ContentstackErrorException ex) + { + Assert.IsFalse(ex.StatusCode >= HttpStatusCode.OK && (int)ex.StatusCode < 300, "Expected non-success status code."); + Assert.IsNotNull(ex.ErrorMessage ?? ex.Message, "Error message should be present."); + } + catch (Exception ex) { - Assert.Fail($"Failed to perform bulk unpublish with skipWorkflowStage and approvals: {e.Message}"); + FailWithError("Bulk unpublish with invalid data (negative test)", ex); } }