Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
### Version: 2.28.0
#### Date: May-15-2026

##### Feat:
- Entry Variants Branch Support
- Added support for passing an optional `branch` parameter to the `.Variant()` method in both `Entry` and `Query` classes.
- If the branch parameter is null or empty, it automatically falls back to the Stack's configured branch or "main".
- Added comprehensive unit and integration tests for Entry and Query variant branch logic.

### Version: 2.27.0
#### Date: Apr-23-2026

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,96 @@ public EntryVariantsComprehensiveTest(ITestOutputHelper output) : base(output)

#region Basic Variant Operations

[Fact(DisplayName = "Entry Operations - Variant With Invalid Branch Throws Exception")]
public async Task Variant_WithInvalidBranch_ThrowsException()
{
// Arrange
LogArrange("Creating client and setting up test data");
LogContext("ContentType", TestDataHelper.ComplexContentTypeUid);
LogContext("EntryUid", TestDataHelper.ComplexEntryUid);
LogContext("VariantUid", TestDataHelper.VariantUid);
LogContext("Branch", "invalid_branch_name_123");

var client = CreateClient();

// Act & Assert
LogAct("Fetching entry with variant and INVALID branch using .Variant() method");

var exception = await Assert.ThrowsAsync<Exception>(async () =>
{
await client
.ContentType(TestDataHelper.ComplexContentTypeUid)
.Entry(TestDataHelper.ComplexEntryUid)
.Variant(TestDataHelper.VariantUid, "invalid_branch_name_123")
.Fetch<Entry>();
});

LogAssert("Verifying exception was thrown");
TestAssert.NotNull(exception);
}

[Fact(DisplayName = "Entry Operations - Variant With Valid Branch Returns Results")]
public async Task Variant_WithValidBranch_ReturnsResults()
{
// Arrange
LogArrange("Creating client and setting up test data");
LogContext("ContentType", TestDataHelper.ComplexContentTypeUid);
LogContext("EntryUid", TestDataHelper.ComplexEntryUid);
LogContext("VariantUid", TestDataHelper.VariantUid);
LogContext("Branch", "development");

var client = CreateClient();

// Act
LogAct("Fetching entry with variant and valid branch using .Variant() method");

var entry = await client
.ContentType(TestDataHelper.ComplexContentTypeUid)
.Entry(TestDataHelper.ComplexEntryUid)
.Variant(TestDataHelper.VariantUid, "development")
.Fetch<Entry>();

// Assert
LogAssert("Verifying entry properties");
TestAssert.NotNull(entry);
TestAssert.NotNull(entry.Uid);
}

[Fact(DisplayName = "Entry Operations - Variant With Null Or Empty Branch Falls Back To Stack Branch Or Main")]
public async Task Variant_WithNullOrEmptyBranch_FallsBackToStackBranchOrMain()
{
// Arrange
LogArrange("Creating client and setting up test data");
LogContext("ContentType", TestDataHelper.ComplexContentTypeUid);
LogContext("EntryUid", TestDataHelper.ComplexEntryUid);
LogContext("VariantUid", TestDataHelper.VariantUid);

var client = CreateClient();

// Act
LogAct("Fetching entry with variant and null/empty branch using .Variant() method");

var entryWithNullBranch = await client
.ContentType(TestDataHelper.ComplexContentTypeUid)
.Entry(TestDataHelper.ComplexEntryUid)
.Variant(TestDataHelper.VariantUid, null)
.Fetch<Entry>();

var entryWithEmptyBranch = await client
.ContentType(TestDataHelper.ComplexContentTypeUid)
.Entry(TestDataHelper.ComplexEntryUid)
.Variant(TestDataHelper.VariantUid, " ")
.Fetch<Entry>();

// Assert
LogAssert("Verifying entry properties");
TestAssert.NotNull(entryWithNullBranch);
TestAssert.NotNull(entryWithNullBranch.Uid);

TestAssert.NotNull(entryWithEmptyBranch);
TestAssert.NotNull(entryWithEmptyBranch.Uid);
}

[Fact(DisplayName = "Entry Operations - Variant Fetch With Variant Method Returns Variant Content")]
public async Task Variant_FetchWithVariantMethod_ReturnsVariantContent()
{
Expand Down Expand Up @@ -221,6 +311,92 @@ public async Task Variant_MultipleVariants_UsingList()

#region Query with Variants

[Fact(DisplayName = "Entry Operations - Variant Query With Invalid Branch Throws Exception")]
public async Task Variant_Query_WithInvalidBranch_ThrowsException()
{
// Arrange
LogArrange("Setting up query operation with invalid branch");
LogContext("ContentType", TestDataHelper.ComplexContentTypeUid);
LogContext("VariantUid", TestDataHelper.VariantUid);
LogContext("Branch", "invalid_branch_name_123");

var client = CreateClient();
var query = client.ContentType(TestDataHelper.ComplexContentTypeUid).Query();

// Act & Assert
LogAct("Executing query with variant and INVALID branch");

query.Variant(TestDataHelper.VariantUid, "invalid_branch_name_123");
query.Limit(5);

var exception = await Assert.ThrowsAsync<Exception>(async () =>
{
await query.Find<Entry>();
});

LogAssert("Verifying exception was thrown");
TestAssert.NotNull(exception);
}

[Fact(DisplayName = "Entry Operations - Variant Query With Valid Branch Returns Results")]
public async Task Variant_Query_WithValidBranch_ReturnsResults()
{
// Arrange
LogArrange("Setting up query operation with valid branch");
LogContext("ContentType", TestDataHelper.ComplexContentTypeUid);
LogContext("VariantUid", TestDataHelper.VariantUid);
LogContext("Branch", "development");

var client = CreateClient();
var query = client.ContentType(TestDataHelper.ComplexContentTypeUid).Query();

// Act
LogAct("Executing query with variant and valid branch");

query.Variant(TestDataHelper.VariantUid, "development");
query.Limit(5);
var result = await query.Find<Entry>();

// Assert
LogAssert("Verifying response");

TestAssert.NotNull(result);
TestAssert.NotNull(result.Items);
}

[Fact(DisplayName = "Entry Operations - Variant Query With Null Or Empty Branch Falls Back To Stack Branch Or Main")]
public async Task Variant_Query_WithNullOrEmptyBranch_FallsBackToStackBranchOrMain()
{
// Arrange
LogArrange("Setting up query operation with null/empty branch");
LogContext("ContentType", TestDataHelper.ComplexContentTypeUid);
LogContext("VariantUid", TestDataHelper.VariantUid);

var client = CreateClient();

// Act
LogAct("Executing queries with variant and null/empty branch");

var queryWithNullBranch = client.ContentType(TestDataHelper.ComplexContentTypeUid).Query();
queryWithNullBranch.Variant(TestDataHelper.VariantUid, null);
queryWithNullBranch.Limit(1);
var resultWithNullBranch = await queryWithNullBranch.Find<Entry>();

var queryWithEmptyBranch = client.ContentType(TestDataHelper.ComplexContentTypeUid).Query();
queryWithEmptyBranch.Variant(TestDataHelper.VariantUid, " ");
queryWithEmptyBranch.Limit(1);
var resultWithEmptyBranch = await queryWithEmptyBranch.Find<Entry>();

// Assert
LogAssert("Verifying response");

TestAssert.NotNull(resultWithNullBranch);
TestAssert.NotNull(resultWithNullBranch.Items);

TestAssert.NotNull(resultWithEmptyBranch);
TestAssert.NotNull(resultWithEmptyBranch.Items);
}

[Fact(DisplayName = "Entry Operations - Variant Query With Variant Method")]
public async Task Variant_Query_WithVariantMethod()
{
Expand Down
89 changes: 89 additions & 0 deletions Contentstack.Core.Unit.Tests/EntryUnitTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2745,6 +2745,95 @@ public void Entry_Fetch_WithCachePolicyNotSet_Setup_VerifiesDefault()
}

#endregion

#region Variant Tests

private ContentstackClient GetMockClient(string stackBranch = null)
{
var options = new ContentstackOptions
{
ApiKey = "dummy_api_key",
DeliveryToken = "dummy_delivery_token",
Environment = "dummy_environment",
Branch = stackBranch
};
return new ContentstackClient(options);
}

[Fact(DisplayName = "Entry Operations - Variant With Branch Sets Branch Header")]
public void Entry_Variant_WithBranch_SetsBranchHeader()
{
// Arrange
var client = GetMockClient("main");
var entry = client.ContentType("dummy_content_type").Entry("dummy_entry_uid");

// Act
entry.Variant("variant_1", "development");

// Assert
Assert.True(entry._Headers.ContainsKey("x-cs-variant-uid"));
Assert.Equal("variant_1", entry._Headers["x-cs-variant-uid"]);

Assert.True(entry._Headers.ContainsKey("branch"));
Assert.Equal("development", entry._Headers["branch"]);
}

[Fact(DisplayName = "Entry Operations - Variant With Null Branch Falls Back To Stack Branch")]
public void Entry_Variant_WithNullBranch_FallsBackToStackBranch()
{
// Arrange
var client = GetMockClient("stack_branch");
var entry = client.ContentType("dummy_content_type").Entry("dummy_entry_uid");

// Act
entry.Variant("variant_1", null);

// Assert
Assert.True(entry._Headers.ContainsKey("x-cs-variant-uid"));
Assert.Equal("variant_1", entry._Headers["x-cs-variant-uid"]);

Assert.True(entry._Headers.ContainsKey("branch"));
Assert.Equal("stack_branch", entry._Headers["branch"]);
}

[Fact(DisplayName = "Entry Operations - Variant With Empty Branch Falls Back To Main If Stack Branch Is Null")]
public void Entry_Variant_WithEmptyBranch_FallsBackToMainIfStackBranchIsNull()
{
// Arrange
var client = GetMockClient(null);
var entry = client.ContentType("dummy_content_type").Entry("dummy_entry_uid");

// Act
entry.Variant("variant_1", " ");

// Assert
Assert.True(entry._Headers.ContainsKey("x-cs-variant-uid"));
Assert.Equal("variant_1", entry._Headers["x-cs-variant-uid"]);

Assert.True(entry._Headers.ContainsKey("branch"));
Assert.Equal("main", entry._Headers["branch"]);
}

[Fact(DisplayName = "Entry Operations - Variant With Multiple Variants And Branch Sets Headers")]
public void Entry_Variant_WithMultipleVariantsAndBranch_SetsHeaders()
{
// Arrange
var client = GetMockClient("main");
var entry = client.ContentType("dummy_content_type").Entry("dummy_entry_uid");
var variants = new List<string> { "variant_1", "variant_2" };

// Act
entry.Variant(variants, "feature_branch");

// Assert
Assert.True(entry._Headers.ContainsKey("x-cs-variant-uid"));
Assert.Equal("variant_1,variant_2", entry._Headers["x-cs-variant-uid"]);

Assert.True(entry._Headers.ContainsKey("branch"));
Assert.Equal("feature_branch", entry._Headers["branch"]);
}

#endregion
}
}

Loading
Loading