Skip to content

Commit 6274caa

Browse files
cs-rajclaude
andcommitted
feat: add localized taxonomy and term delivery (CDA) support
Extends the .NET SDK with published-taxonomy fetch and term delivery endpoints to match the TypeScript feat/taxonomy-publishing parity (CD-11371). New: - Term.cs — Fetch(locale?), Locales<T>(), Ancestors<T>(), Descendants<T>() - TermQuery.cs — SetLocale()/IncludeFallback() chainable builder + Find<T>() Extended Taxonomy class: - Taxonomy(stack, uid) constructor - _Url switches to /taxonomies/{uid} when uid is set - Fetch<T>(locale?) — own HTTP path, parses $.taxonomy - Term(termUid) → Term, Terms() → TermQuery Extended ContentstackClient: - Taxonomies(uid) overload Tests: - TaxonomyUnitTests — 46 unit tests (all pass) - TaxonomyLocalisationTest — integration tests per CDA call - TestDataHelper — taxonomy-publish stack config properties Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 8267582 commit 6274caa

7 files changed

Lines changed: 874 additions & 5 deletions

File tree

Contentstack.Core.Tests/Helpers/TestDataHelper.cs

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -117,19 +117,49 @@ static TestDataHelper()
117117
#endregion
118118

119119
#region Taxonomy
120-
120+
121121
/// <summary>
122122
/// Gets the taxonomy term for USA state (e.g., "california")
123123
/// </summary>
124-
public static string TaxUsaState =>
124+
public static string TaxUsaState =>
125125
GetRequiredConfig("TAX_USA_STATE");
126-
126+
127127
/// <summary>
128128
/// Gets the taxonomy term for India state (e.g., "maharashtra")
129129
/// </summary>
130-
public static string TaxIndiaState =>
130+
public static string TaxIndiaState =>
131131
GetRequiredConfig("TAX_INDIA_STATE");
132-
132+
133+
/// <summary>
134+
/// API key for the taxonomy-publish test stack (gadgets).
135+
/// </summary>
136+
public static string TaxPublishApiKey =>
137+
GetRequiredConfig("TAX_PUBLISH_API_KEY");
138+
139+
/// <summary>
140+
/// Delivery token for the taxonomy-publish test stack.
141+
/// </summary>
142+
public static string TaxPublishDeliveryToken =>
143+
GetRequiredConfig("TAX_PUBLISH_DELIVERY_TOKEN");
144+
145+
/// <summary>
146+
/// Environment for the taxonomy-publish test stack.
147+
/// </summary>
148+
public static string TaxPublishEnvironment =>
149+
GetRequiredConfig("TAX_PUBLISH_ENVIRONMENT");
150+
151+
/// <summary>
152+
/// UID of the published taxonomy to use in localization tests (e.g. "gadgets").
153+
/// </summary>
154+
public static string TaxPublishTaxonomyUid =>
155+
GetRequiredConfig("TAX_PUBLISH_TAXONOMY_UID");
156+
157+
/// <summary>
158+
/// Locale code used for localized taxonomy/term delivery tests (e.g. "hi-in").
159+
/// </summary>
160+
public static string TaxPublishLocale =>
161+
GetRequiredConfig("TAX_PUBLISH_LOCALE");
162+
133163
#endregion
134164

135165
#region Live Preview
Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Threading.Tasks;
5+
using Xunit;
6+
using Xunit.Abstractions;
7+
using Contentstack.Core.Configuration;
8+
using Contentstack.Core.Models;
9+
using Contentstack.Core.Internals;
10+
using Contentstack.Core.Tests.Helpers;
11+
12+
namespace Contentstack.Core.Tests.Integration.Taxonomy
13+
{
14+
/// <summary>
15+
/// Integration tests for localized taxonomy and term delivery (CDA).
16+
/// Covers: Taxonomy.Fetch(locale), TermQuery.SetLocale/IncludeFallback/Find,
17+
/// Term.Fetch(locale), Term.Locales, Term.Ancestors, Term.Descendants.
18+
/// Stack: gadgets taxonomy, locales en-us / hi-in.
19+
/// </summary>
20+
[Trait("Category", "TaxonomyLocalisation")]
21+
public class TaxonomyLocalisationTest : IntegrationTestBase
22+
{
23+
public TaxonomyLocalisationTest(ITestOutputHelper output) : base(output) { }
24+
25+
/// <summary>
26+
/// Creates a client scoped to the gadgets taxonomy-publish test stack.
27+
/// Uses the default CDN host (no custom host override needed).
28+
/// </summary>
29+
private ContentstackClient CreateGadgetsClient()
30+
{
31+
var options = new ContentstackOptions
32+
{
33+
ApiKey = TestDataHelper.TaxPublishApiKey,
34+
DeliveryToken = TestDataHelper.TaxPublishDeliveryToken,
35+
Environment = TestDataHelper.TaxPublishEnvironment
36+
};
37+
var client = new ContentstackClient(options);
38+
client.Plugins.Add(new RequestLoggingPlugin(TestOutput));
39+
return client;
40+
}
41+
42+
// ── 1. Fetch localized taxonomy ──────────────────────────────────────
43+
44+
[Fact(DisplayName = "TaxPublish - Fetch taxonomy returns object with uid and name")]
45+
public async Task Fetch_Taxonomy_MasterLocale_ReturnsValidObject()
46+
{
47+
LogArrange("Fetching taxonomy without locale (master)");
48+
LogContext("TaxonomyUid", TestDataHelper.TaxPublishTaxonomyUid);
49+
50+
var client = CreateGadgetsClient();
51+
52+
LogAct("Calling Taxonomies(uid).Fetch<JObject>()");
53+
var result = await client
54+
.Taxonomies(TestDataHelper.TaxPublishTaxonomyUid)
55+
.Fetch<Newtonsoft.Json.Linq.JObject>();
56+
57+
LogAssert("Verifying response");
58+
Assert.NotNull(result);
59+
Assert.NotNull(result["uid"]?.ToString());
60+
Assert.Equal(TestDataHelper.TaxPublishTaxonomyUid, result["uid"]?.ToString());
61+
}
62+
63+
[Fact(DisplayName = "TaxPublish - Fetch taxonomy with locale returns localized name")]
64+
public async Task Fetch_Taxonomy_WithLocale_ReturnsLocalizedName()
65+
{
66+
LogArrange("Fetching taxonomy with locale");
67+
LogContext("TaxonomyUid", TestDataHelper.TaxPublishTaxonomyUid);
68+
LogContext("Locale", TestDataHelper.TaxPublishLocale);
69+
70+
var client = CreateGadgetsClient();
71+
72+
LogAct("Calling Taxonomies(uid).Fetch<JObject>(locale)");
73+
var result = await client
74+
.Taxonomies(TestDataHelper.TaxPublishTaxonomyUid)
75+
.Fetch<Newtonsoft.Json.Linq.JObject>(TestDataHelper.TaxPublishLocale);
76+
77+
LogAssert("Verifying response");
78+
Assert.NotNull(result);
79+
Assert.NotNull(result["uid"]?.ToString());
80+
// The name should differ from the master locale (en-us) value
81+
Assert.NotNull(result["name"]?.ToString());
82+
}
83+
84+
// ── 2. Find all taxonomies ────────────────────────────────────────────
85+
86+
[Fact(DisplayName = "TaxPublish - Find all taxonomies returns non-empty collection")]
87+
public async Task Find_AllTaxonomies_ReturnsCollection()
88+
{
89+
LogArrange("Fetching all published taxonomies");
90+
91+
var client = CreateGadgetsClient();
92+
93+
LogAct("Calling Taxonomies().Find<Entry>()");
94+
var result = await client.Taxonomies().Find<Entry>();
95+
96+
LogAssert("Verifying response");
97+
Assert.NotNull(result);
98+
Assert.NotNull(result.Items);
99+
}
100+
101+
// ── 3. Find terms with locale ─────────────────────────────────────────
102+
103+
[Fact(DisplayName = "TaxPublish - Find terms with locale returns localized terms")]
104+
public async Task Find_Terms_WithLocale_ReturnsLocalizedTerms()
105+
{
106+
LogArrange("Finding terms with locale");
107+
LogContext("TaxonomyUid", TestDataHelper.TaxPublishTaxonomyUid);
108+
LogContext("Locale", TestDataHelper.TaxPublishLocale);
109+
110+
var client = CreateGadgetsClient();
111+
112+
LogAct("Calling Terms().SetLocale(locale).Find<JObject>()");
113+
var result = await client
114+
.Taxonomies(TestDataHelper.TaxPublishTaxonomyUid)
115+
.Terms()
116+
.SetLocale(TestDataHelper.TaxPublishLocale)
117+
.Find<Newtonsoft.Json.Linq.JObject>();
118+
119+
LogAssert("Verifying response");
120+
Assert.NotNull(result);
121+
Assert.NotNull(result.Items);
122+
}
123+
124+
[Fact(DisplayName = "TaxPublish - Find terms with locale and fallback returns terms")]
125+
public async Task Find_Terms_WithLocaleAndFallback_ReturnsTerms()
126+
{
127+
LogArrange("Finding terms with locale and include_fallback");
128+
LogContext("TaxonomyUid", TestDataHelper.TaxPublishTaxonomyUid);
129+
LogContext("Locale", TestDataHelper.TaxPublishLocale);
130+
131+
var client = CreateGadgetsClient();
132+
133+
LogAct("Calling Terms().SetLocale(locale).IncludeFallback().Find<JObject>()");
134+
var result = await client
135+
.Taxonomies(TestDataHelper.TaxPublishTaxonomyUid)
136+
.Terms()
137+
.SetLocale(TestDataHelper.TaxPublishLocale)
138+
.IncludeFallback()
139+
.Find<Newtonsoft.Json.Linq.JObject>();
140+
141+
LogAssert("Verifying response");
142+
Assert.NotNull(result);
143+
Assert.NotNull(result.Items);
144+
// Fallback means we should get at least as many terms as without fallback
145+
Assert.True(result.Items.Any());
146+
}
147+
148+
// ── 4–7. Single term methods ──────────────────────────────────────────
149+
150+
/// <summary>
151+
/// Fetches the first available term UID from the gadgets taxonomy to use in subsequent tests.
152+
/// </summary>
153+
private async Task<string> GetFirstTermUidAsync(ContentstackClient client)
154+
{
155+
var terms = await client
156+
.Taxonomies(TestDataHelper.TaxPublishTaxonomyUid)
157+
.Terms()
158+
.SetLocale(TestDataHelper.TaxPublishLocale)
159+
.IncludeFallback()
160+
.Find<Newtonsoft.Json.Linq.JObject>();
161+
162+
return terms?.Items?.FirstOrDefault()?["uid"]?.ToString();
163+
}
164+
165+
[Fact(DisplayName = "TaxPublish - Fetch single term with locale returns localized term")]
166+
public async Task Fetch_SingleTerm_WithLocale_ReturnsLocalizedTerm()
167+
{
168+
var client = CreateGadgetsClient();
169+
var termUid = await GetFirstTermUidAsync(client);
170+
171+
if (string.IsNullOrEmpty(termUid))
172+
{
173+
Output.WriteLine("No term UID found — skipping test.");
174+
return;
175+
}
176+
177+
LogArrange("Fetching single term with locale");
178+
LogContext("TaxonomyUid", TestDataHelper.TaxPublishTaxonomyUid);
179+
LogContext("TermUid", termUid);
180+
LogContext("Locale", TestDataHelper.TaxPublishLocale);
181+
182+
LogAct("Calling Term(termUid).Fetch<JObject>(locale)");
183+
var result = await client
184+
.Taxonomies(TestDataHelper.TaxPublishTaxonomyUid)
185+
.Term(termUid)
186+
.Fetch<Newtonsoft.Json.Linq.JObject>(TestDataHelper.TaxPublishLocale);
187+
188+
LogAssert("Verifying response");
189+
Assert.NotNull(result);
190+
Assert.NotNull(result["uid"]?.ToString());
191+
Assert.Equal(termUid, result["uid"]?.ToString());
192+
}
193+
194+
[Fact(DisplayName = "TaxPublish - Term.Locales returns locales collection")]
195+
public async Task Term_Locales_ReturnsLocalesCollection()
196+
{
197+
var client = CreateGadgetsClient();
198+
var termUid = await GetFirstTermUidAsync(client);
199+
200+
if (string.IsNullOrEmpty(termUid))
201+
{
202+
Output.WriteLine("No term UID found — skipping test.");
203+
return;
204+
}
205+
206+
LogArrange("Fetching all locales for a term");
207+
LogContext("TaxonomyUid", TestDataHelper.TaxPublishTaxonomyUid);
208+
LogContext("TermUid", termUid);
209+
210+
LogAct("Calling Term(termUid).Locales<JArray>()");
211+
var result = await client
212+
.Taxonomies(TestDataHelper.TaxPublishTaxonomyUid)
213+
.Term(termUid)
214+
.Locales<Newtonsoft.Json.Linq.JToken>();
215+
216+
LogAssert("Verifying response");
217+
Assert.NotNull(result);
218+
}
219+
220+
[Fact(DisplayName = "TaxPublish - Term.Ancestors returns ancestors collection")]
221+
public async Task Term_Ancestors_ReturnsAncestorsCollection()
222+
{
223+
var client = CreateGadgetsClient();
224+
var termUid = await GetFirstTermUidAsync(client);
225+
226+
if (string.IsNullOrEmpty(termUid))
227+
{
228+
Output.WriteLine("No term UID found — skipping test.");
229+
return;
230+
}
231+
232+
LogArrange("Fetching ancestors for a term");
233+
LogContext("TaxonomyUid", TestDataHelper.TaxPublishTaxonomyUid);
234+
LogContext("TermUid", termUid);
235+
236+
LogAct("Calling Term(termUid).Ancestors<JToken>()");
237+
var result = await client
238+
.Taxonomies(TestDataHelper.TaxPublishTaxonomyUid)
239+
.Term(termUid)
240+
.Ancestors<Newtonsoft.Json.Linq.JToken>();
241+
242+
LogAssert("Verifying response");
243+
Assert.NotNull(result);
244+
}
245+
246+
[Fact(DisplayName = "TaxPublish - Term.Descendants returns descendants collection")]
247+
public async Task Term_Descendants_ReturnsDescendantsCollection()
248+
{
249+
var client = CreateGadgetsClient();
250+
var termUid = await GetFirstTermUidAsync(client);
251+
252+
if (string.IsNullOrEmpty(termUid))
253+
{
254+
Output.WriteLine("No term UID found — skipping test.");
255+
return;
256+
}
257+
258+
LogArrange("Fetching descendants for a term");
259+
LogContext("TaxonomyUid", TestDataHelper.TaxPublishTaxonomyUid);
260+
LogContext("TermUid", termUid);
261+
262+
LogAct("Calling Term(termUid).Descendants<JToken>()");
263+
var result = await client
264+
.Taxonomies(TestDataHelper.TaxPublishTaxonomyUid)
265+
.Term(termUid)
266+
.Descendants<Newtonsoft.Json.Linq.JToken>();
267+
268+
LogAssert("Verifying response");
269+
Assert.NotNull(result);
270+
}
271+
}
272+
}

0 commit comments

Comments
 (0)