diff --git a/.cursor/rules/README.md b/.cursor/rules/README.md new file mode 100644 index 0000000..e069005 --- /dev/null +++ b/.cursor/rules/README.md @@ -0,0 +1,5 @@ +# Cursor (optional) + +**Cursor** users: start at **[`AGENTS.md`](../../AGENTS.md)** at the repository root. All conventions live in **`skills/*/SKILL.md`**. + +This folder only points contributors to **`AGENTS.md`** so editor-specific configuration does not duplicate the canonical documentation. diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..c1a49d7 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,56 @@ +# Contentstack Utils .NET – Agent guide + +**Universal entry point** for contributors and AI agents. Detailed conventions live in **`skills/*/SKILL.md`**. + +## What this repo is + +| Field | Detail | +|-------|--------| +| **Name:** | [contentstack-utils-dotnet](https://github.com/contentstack/contentstack-utils-dotnet) | +| **Purpose:** | NuGet library **`contentstack.utils`**: JSON RTE → HTML rendering, embedded entry/asset rendering, GQL-oriented helpers, variant metadata (`GetVariantMetadataTags` / `GetVariantAliases`), Live Preview–style editable tags (`addEditableTags`). This is **not** a full Contentstack Delivery or Management HTTP SDK. | +| **Out of scope (if any):** | No built-in HTTP client to Contentstack APIs; consumers use the Contentstack .NET Delivery SDK or other clients. This package focuses on parsing, rendering, and JSON helpers. | + +## Tech stack (at a glance) + +| Area | Details | +|------|---------| +| **Language** | C#; library multi-targets **netstandard2.0**, **net47**, **net472** ([`Contentstack.Utils/Contentstack.Utils.csproj`](Contentstack.Utils/Contentstack.Utils.csproj)); test project **net7.0** ([`Contentstack.Utils.Tests/Contentstack.Utils.Tests.csproj`](Contentstack.Utils.Tests/Contentstack.Utils.Tests.csproj)). | +| **Build** | **dotnet** + solution [`Contentstack.Utils.sln`](Contentstack.Utils.sln); shared package version in [`Directory.Build.props`](Directory.Build.props) (e.g. `Version` **1.2.0**). | +| **Tests** | **xUnit**, **Microsoft.NET.Test.Sdk**, **coverlet.collector**; JSON fixtures under [`Contentstack.Utils.Tests/Resources/`](Contentstack.Utils.Tests/Resources/). | +| **Lint / coverage** | No repo-level `.editorconfig` or format workflow; tests use **Coverlet** (`XPlat code coverage`) via the shell scripts. | +| **Key dependencies** | **HtmlAgilityPack**, **Newtonsoft.Json** in the library; tests reference compatible Newtonsoft.Json. | + +## Commands (quick reference) + +| Command type | Command | +|--------------|---------| +| **Build** | `dotnet build Contentstack.Utils.sln` (add `-c Release` for release configuration). | +| **Test (quick, local)** | `dotnet test Contentstack.Utils.sln` — from repo root; no TRX/coverage (fastest feedback). | +| **Test (CI parity)** | `sh ./Scripts/run-unit-test-case.sh` — clears `Contentstack.Utils.Tests/TestResults`, runs `dotnet test` on the test project with TRX logging and coverage. | +| **Test + HTML coverage report** | `bash ./Scripts/run-test-case.sh` — tests the **solution**, then runs `python3 Scripts/generate_test_report.py` to emit HTML under `Contentstack.Utils.Tests/TestResults/Coverage-.../index.html`. | +| **Pack (release)** | `dotnet pack -c Release -o out` (see [`.github/workflows/nuget-publish.yml`](.github/workflows/nuget-publish.yml)). | +| **SCA (local parity with CI)** | `dotnet restore ./Contentstack.Utils.sln`, then from `Contentstack.Utils`: `snyk test` (requires Snyk CLI and `SNYK_TOKEN`; see [`sca-scan.yml`](.github/workflows/sca-scan.yml)). | + +**CI:** [`.github/workflows/unit-test.yml`](.github/workflows/unit-test.yml) runs `run-unit-test-case.sh` on Windows for `pull_request` and `push`. + +## CI and automation (workflows) + +| Workflow | Trigger | Role | +|----------|---------|------| +| [`unit-test.yml`](.github/workflows/unit-test.yml) | `pull_request`, `push` | Windows: unit tests via `Scripts/run-unit-test-case.sh`. | +| [`check-branch.yml`](.github/workflows/check-branch.yml) | `pull_request` | Merges into **`master`** are only allowed from **`staging`** (otherwise fails with a PR comment). | +| [`nuget-publish.yml`](.github/workflows/nuget-publish.yml) | `release` (created) | `dotnet pack -c Release -o out`; push package to NuGet.org and GitHub Packages. | +| [`sca-scan.yml`](.github/workflows/sca-scan.yml) | PR (opened, synchronize, reopened) | Ubuntu: `dotnet restore`, **Snyk** `snyk test` under `Contentstack.Utils`. | +| [`policy-scan.yml`](.github/workflows/policy-scan.yml) | PR (public repos) | Requires `SECURITY.md` and a license file containing the current year. | +| [`codeql-analysis.yml`](.github/workflows/codeql-analysis.yml) | `pull_request` (branches `*`) | CodeQL analysis for **csharp** (autobuild). | +| [`issues-jira.yml`](.github/workflows/issues-jira.yml) | `issues` (opened) | Creates Jira tickets from GitHub issues (secrets). | + +## Where the documentation lives: skills + +| Skill | Path | What it covers | +|-------|------|----------------| +| Dev workflow | [`skills/dev-workflow/SKILL.md`](skills/dev-workflow/SKILL.md) | Branches, build/test/pack, CI, security workflows, CODEOWNERS, Talisman. | +| Testing | [`skills/testing/SKILL.md`](skills/testing/SKILL.md) | xUnit layout, fixtures, mocks, coverage scripts, alignment with CI. | +| Code review | [`skills/code-review/SKILL.md`](skills/code-review/SKILL.md) | PR expectations, checklist, security notes. | +| Contentstack Utils (API) | [`skills/contentstack-utils/SKILL.md`](skills/contentstack-utils/SKILL.md) | Public API, package boundaries, `Utils` / GQL / variants, dependencies. | +| C# style (this repo) | [`skills/csharp-style/SKILL.md`](skills/csharp-style/SKILL.md) | Folder layout, namespaces, naming consistency, TFMs. | diff --git a/skills/README.md b/skills/README.md new file mode 100644 index 0000000..79e9d8f --- /dev/null +++ b/skills/README.md @@ -0,0 +1,5 @@ +# Skills – Contentstack Utils .NET + +Source of truth for detailed guidance is **`skills/*/SKILL.md`**. Read **[`AGENTS.md`](../AGENTS.md)** first, then open the skill that matches your task. + +Each folder contains **`SKILL.md`** with YAML frontmatter (`name`, `description`). diff --git a/skills/code-review/SKILL.md b/skills/code-review/SKILL.md new file mode 100644 index 0000000..1002ee7 --- /dev/null +++ b/skills/code-review/SKILL.md @@ -0,0 +1,31 @@ +--- +name: code-review +description: Use for PR expectations, review checklist, docs/changelog, and security considerations in contentstack-utils-dotnet. +--- + +# Code review – Contentstack Utils .NET + +## When to use + +- Opening or reviewing a pull request. +- Deciding whether README/CHANGELOG updates or extra tests are required. +- Assessing impact of new dependencies or public API changes. + +## Instructions + +### Branch and ownership + +- Merges into **`master`** are expected from **`staging`** only (see [`check-branch.yml`](../../.github/workflows/check-branch.yml)). Align your PR base/head with team process. +- [`CODEOWNERS`](../../CODEOWNERS) may request reviews from **`@contentstack/devex-pr-reviewers`** and security admins for workflow or `.snyk` changes. + +### Checklist + +- **Behavior**: New or changed logic in [`Contentstack.Utils`](../../Contentstack.Utils/) should have **xUnit** coverage in [`Contentstack.Utils.Tests`](../../Contentstack.Utils.Tests/) unless truly non-functional (e.g. comment-only). +- **Public API**: User-visible changes should update [`README.md`](../../README.md) and [`CHANGELOG.md`](../../CHANGELOG.md) as appropriate. +- **Multi-targeting**: The library builds **netstandard2.0**, **net47**, **net472**. After API or dependency changes, verify `dotnet build` succeeds for all target frameworks (or rely on CI). +- **Dependencies**: New or upgraded NuGet packages affect **Snyk** ([`sca-scan.yml`](../../.github/workflows/sca-scan.yml)) and **CodeQL** ([`codeql-analysis.yml`](../../.github/workflows/codeql-analysis.yml)). Ensure licenses and advisories are acceptable. +- **Secrets**: Never commit API keys, tokens, or connection strings. CI uses GitHub secrets only. + +### Severity labels (optional) + +Teams may use **Blocker** (must fix before merge), **Major** (should fix or track), **Minor** (nit / follow-up)—keep comments actionable. diff --git a/skills/contentstack-utils/SKILL.md b/skills/contentstack-utils/SKILL.md new file mode 100644 index 0000000..ff548d9 --- /dev/null +++ b/skills/contentstack-utils/SKILL.md @@ -0,0 +1,125 @@ +--- +name: contentstack-utils +description: Use for the contentstack.utils NuGet API—Utils, GQL, Options, variants, converters, code flows, and integration boundaries. +--- + +# Contentstack Utils (API) – Contentstack Utils .NET + +## When to use + +- Implementing or extending JSON RTE → HTML rendering or embedded content behavior. +- Adding node types, converters, or variant/metadata helpers. +- Choosing whether logic belongs in this package vs the Contentstack .NET Delivery SDK. + +## Instructions + +### Package and namespaces + +- **NuGet package id:** `contentstack.utils` ([`Contentstack.Utils.csproj`](../../Contentstack.Utils/Contentstack.Utils.csproj)). +- **Root namespace:** `Contentstack.Utils`. Sub-namespaces follow folders: **`Contentstack.Utils.Models`**, **`Contentstack.Utils.Interfaces`**, **`Contentstack.Utils.Enums`**, **`Contentstack.Utils.Extensions`**, **`Contentstack.Utils.Converters`**. + +### Models (library) + +Source files under [`Contentstack.Utils/Models/`](../../Contentstack.Utils/Models/): + +- [`JsonRTENode.cs`](../../Contentstack.Utils/Models/JsonRTENode.cs), [`JsonRTENodes.cs`](../../Contentstack.Utils/Models/JsonRTENodes.cs) — GQL-shaped RTE wrappers. +- [`Metadata.cs`](../../Contentstack.Utils/Models/Metadata.cs) — embed metadata; implicit conversions from **HtmlAgilityPack** `HtmlNode` and RTE `Node`. +- [`Node.cs`](../../Contentstack.Utils/Models/Node.cs), [`TextNode.cs`](../../Contentstack.Utils/Models/TextNode.cs) — RTE tree nodes. +- [`Options.cs`](../../Contentstack.Utils/Models/Options.cs) — default rendering; subclass for custom HTML. + +### Interfaces (embedding and rendering) + +- **`IEntryEmbedable`** — [`IEntryEmbedable.cs`](../../Contentstack.Utils/Interfaces/IEntryEmbedable.cs): `embeddedItems` map for resolving references. +- **`IEmbeddedObject`**, **`IEmbeddedEntry`**, **`EditableEntry`**, **`IEmbeddedAsset`** — [`IEmbeddedObject.cs`](../../Contentstack.Utils/Interfaces/IEmbeddedObject.cs). +- **`IRenderable`**, **`NodeChildrenCallBack`** — [`IOptions.cs`](../../Contentstack.Utils/Interfaces/IOptions.cs). +- **`IEdges`** — [`IEdges.cs`](../../Contentstack.Utils/Interfaces/IEdges.cs) (GQL edge list). Spelling **`IEntryEmbedable`** matches the existing public API. + +### Core type: `Contentstack.Utils.Utils` + +[`Utils.cs`](../../Contentstack.Utils/Utils.cs) exposes static helpers including: + +- **`JsonToHtml`**: Overloads for `Node`, `List` — see [Code flows](#code-flows). +- **`RenderContent`**: Overloads for `string` and `List` HTML — see [Code flows](#code-flows). +- **`Utils.GQL`**: `JsonToHtml` for `JsonRTENode` / `JsonRTENodes` where `T : IEmbeddedObject` — see [Code flows](#code-flows). +- **`addEditableTags`**: Adds Live Preview–style **`data-cslp`** (or object-shaped) metadata on **`EditableEntry`** graphs for a given content type and locale (see [`Utils.addEditableTags`](../../Contentstack.Utils/Utils.cs)). +- **Variants / metadata:** + - **`GetVariantAliases`** — reads each entry’s `publish_details` → `variants` object and collects non-empty **`alias`** values per variant entry (implementation: **`ExtractVariantAliasesFromEntry`** in [`Utils.cs`](../../Contentstack.Utils/Utils.cs), approx. lines 401–426). + - **`GetVariantMetadataTags`** — wraps alias data into a **`JObject`** with key **`data-csvariants`** (compact JSON string). + - **`GetDataCsvariantsAttribute`** — **Obsolete**; use **`GetVariantMetadataTags`** instead (same behavior; see XML comments on [`Utils`](../../Contentstack.Utils/Utils.cs)). + +### Rendering hooks (`Options` / `IRenderable`) + +Subclass or configure **[`Options.cs`](../../Contentstack.Utils/Models/Options.cs)** (`Options : IRenderable`) to override: + +- **`RenderOption(IEmbeddedObject, Metadata)`** +- **`RenderNode(string nodeType, Node, NodeChildrenCallBack)`** +- **`RenderMark(MarkType, string text, string className, string Id)`** + +### JSON serialization + +- [`NodeJsonConverter.cs`](../../Contentstack.Utils/Converters/NodeJsonConverter.cs) and [`RTEJsonConverter.cs`](../../Contentstack.Utils/Converters/RTEJsonConverter.cs) integrate with **Newtonsoft.Json** for RTE node graphs. New node shapes should follow the same converter and model patterns. + +### Dependencies + +- **HtmlAgilityPack**: HTML load, query, and embedded-object replacement for `RenderContent`. +- **Newtonsoft.Json**: JSON models (`JObject`, `JArray`, converters). Upgrades should stay compatible with consumers and pass Snyk/CI. + +### Out of scope + +- This library **does not** ship an HTTP client for Contentstack Delivery or Management APIs. Apps typically use **Contentstack .NET SDK** (or REST) to fetch entries, then use **Utils** to render RTE HTML or process JSON. Keep HTTP, auth, and caching in application or SDK layers. + +### Authoritative source + +- **Signatures and full behavior** live in C# under [`Contentstack.Utils/`](../../Contentstack.Utils/). This skill summarizes; when in doubt, read the implementation (especially [`Utils.cs`](../../Contentstack.Utils/Utils.cs)). + +## Code flows + +Stepwise behavior with pointers into [`Utils.cs`](../../Contentstack.Utils/Utils.cs). Line numbers are approximate; verify against the file after large edits. + +### RTE JSON → HTML (`JsonToHtml`) + +1. **`JsonToHtml(Node, Options)`** (approx. lines 97–113) builds a **`referenceToHtml`** delegate: for **`reference`** nodes it calls **`findEmbeddedObject`** using **`options.entry.embeddedItems`**, then **`options.RenderOption`** when a match exists. +2. **`nodeChildrenToHtml`** (lines 115–118) concatenates HTML for each child via **`nodeToHtml`**. +3. **`nodeToHtml`** (lines 121–131): **`type == "text"`** → **`textToHtml`** (lines 133+), which applies marks in order via **`RenderMark`**; **`type == "reference"`** → **`referenceToHtml`**; **otherwise** → **`options.RenderNode`** with a callback that recurses into **`nodeChildrenToHtml`**. + +### GQL RTE (`Utils.GQL.JsonToHtml`) + +- Reuses **`nodeChildrenToHtml`** / **`nodeToHtml`**. **`referenceToHtml`** is built by **`GQL.refernceToHtml`** (lines 32–50): find **`IEdges`** where **`edge.Node`** matches **`Metadata.ItemUid`** and **`ContentTypeUid`**, then **`options.RenderOption(edge.Node, metadata)`**. Entry points: **`GQL.JsonToHtml(JsonRTENode, ...)`** (lines 17–20) and list overload (lines 22–30). + +### HTML string with embeds (`RenderContent`) + +1. **`RenderContent(string, Options)`** (lines 64–82): **`HtmlDocument.LoadHtml`**, then **`FindEmbeddedObject`** extension on [`HtmlDocumentExtension.cs`](../../Contentstack.Utils/Extensions/HtmlDocumentExtension.cs) (selects nodes with class **`embedded-asset`** or **`embedded-entry`**). +2. Each **HtmlNode** is passed to the callback; **`Metadata`** is produced via **implicit conversion** from **`HtmlNode`** ([`Metadata.cs`](../../Contentstack.Utils/Models/Metadata.cs), lines 46–68). +3. **`findEmbeddedObject`** resolves **`IEmbeddedObject`** from **`options.entry.embeddedItems`**; result string replaces **`metadata.OuterHTML`** in the accumulated HTML. + +### Variant metadata (`GetVariantAliases` / `GetVariantMetadataTags`) + +- **`ExtractVariantAliasesFromEntry`**: requires **`entry["publish_details"]`** as **`JObject`**, then **`publish_details["variants"]`** as **`JObject`**; for each property, reads **`alias`** from the nested object when present (lines 401–426). +- **`GetVariantMetadataTags`** builds **`{ "data-csvariants": }`** from **`GetVariantAliases`** results (lines 365–380). + +### Diagrams (high level) + +```mermaid +flowchart TD + rteEntry[JsonToHtml_Node_or_List] + rteEntry --> nodeChildren[nodeChildrenToHtml] + nodeChildren --> nodeTo[nodeToHtml] + nodeTo -->|text| textTo[textToHtml_RenderMark_chain] + nodeTo -->|reference| refDel[referenceToHtml_findEmbeddedObject_RenderOption] + nodeTo -->|other_types| renderNode[RenderNode_recursive_callback] +``` + +```mermaid +flowchart TD + htmlEntry[RenderContent_string] + htmlEntry --> load[HtmlDocument_LoadHtml] + load --> find[FindEmbeddedObject_extension] + find --> meta[HtmlNode_to_Metadata_implicit] + meta --> resolve[findEmbeddedObject_embeddedItems] + resolve --> replace[Replace_OuterHTML_with_RenderOption] +``` + +## References + +- [Product README](../../README.md) — install and usage examples. +- [Contentstack .NET Utils on NuGet](https://www.nuget.org/packages/contentstack.utils) — package listing (verify version). diff --git a/skills/csharp-style/SKILL.md b/skills/csharp-style/SKILL.md new file mode 100644 index 0000000..338e01b --- /dev/null +++ b/skills/csharp-style/SKILL.md @@ -0,0 +1,40 @@ +--- +name: csharp-style +description: Use for folder placement, namespaces, naming consistency, and multi-target framework rules in Contentstack.Utils. +--- + +# C# style (this repo) – Contentstack Utils .NET + +## When to use + +- Adding new types to the library or tests. +- Choosing namespaces and file locations to match existing structure. +- Ensuring changes remain compatible with **netstandard2.0** and desktop TFMs. + +## Instructions + +### Library folder taxonomy + +Under [`Contentstack.Utils/`](../../Contentstack.Utils/), place new code according to its role: + +| Folder | Contents | +|--------|----------| +| `Interfaces/` | [`IEntryEmbedable.cs`](../../Contentstack.Utils/Interfaces/IEntryEmbedable.cs) (`IEntryEmbedable`); [`IEmbeddedObject.cs`](../../Contentstack.Utils/Interfaces/IEmbeddedObject.cs) (`IEmbeddedObject`, `IEmbeddedEntry`, `EditableEntry`, `IEmbeddedAsset`); [`IOptions.cs`](../../Contentstack.Utils/Interfaces/IOptions.cs) (`IRenderable`, `NodeChildrenCallBack`); [`IEdges.cs`](../../Contentstack.Utils/Interfaces/IEdges.cs) (`IEdges`). | +| `Models/` | DTOs and [`Options.cs`](../../Contentstack.Utils/Models/Options.cs) (`Options` base class). | +| `Enums/` | [`MarkType`](../../Contentstack.Utils/Enums/MarkType.cs), [`StyleType`](../../Contentstack.Utils/Enums/StyleType.cs), [`EmbedItemType`](../../Contentstack.Utils/Enums/EmbedItemType.cs). | +| `Extensions/` | Extension methods — [`HtmlDocumentExtension.cs`](../../Contentstack.Utils/Extensions/HtmlDocumentExtension.cs) (`FindEmbeddedObject` on **HtmlAgilityPack** `HtmlDocument`). | +| `Converters/` | Newtonsoft.Json converters for RTE/node JSON ([`NodeJsonConverter.cs`](../../Contentstack.Utils/Converters/NodeJsonConverter.cs), [`RTEJsonConverter.cs`](../../Contentstack.Utils/Converters/RTEJsonConverter.cs)). | +| `Constants/` | Shared strings — [`ErrorMessages.cs`](../../Contentstack.Utils/Constants/ErrorMessages.cs). | + +### Namespaces + +- Mirror the folder structure: `Contentstack.Utils.Interfaces`, `Contentstack.Utils.Models`, and so on. Keep **`Contentstack.Utils`** as the root for public surface consistency. + +### Naming consistency + +- The codebase mixes typical .NET **PascalCase** for most public members with some **camelCase** public static names (notably **`addEditableTags`** on [`Utils`](../../Contentstack.Utils/Utils.cs)). For new code, **prefer consistency with the nearest existing API** in the same class or feature area rather than blindly applying IDE defaults, so consumers see uniform style per subsystem. + +### Target frameworks + +- **Library** ([`Contentstack.Utils.csproj`](../../Contentstack.Utils/Contentstack.Utils.csproj)): **netstandard2.0**, **net47**, **net472**. Public APIs must be implementable on all targets; avoid APIs that only compile on .NET 5+ unless you intentionally raise baselines with a major version policy. +- **Tests** ([`Contentstack.Utils.Tests.csproj`](../../Contentstack.Utils.Tests/Contentstack.Utils.Tests.csproj)): **net7.0** only—fine for test-only APIs; do not assume test-only APIs exist in the library. diff --git a/skills/dev-workflow/SKILL.md b/skills/dev-workflow/SKILL.md new file mode 100644 index 0000000..1e7340e --- /dev/null +++ b/skills/dev-workflow/SKILL.md @@ -0,0 +1,61 @@ +--- +name: dev-workflow +description: Use for branches, build/pack, test scripts, CI workflows, versioning, CODEOWNERS, and security tooling in contentstack-utils-dotnet. +--- + +# Dev workflow – Contentstack Utils .NET + +## When to use + +- Setting up the repo locally, cutting releases, or debugging CI. +- Changing GitHub Actions, branch protection expectations, or dependency scanning. +- Bumping package version or understanding how NuGet publish is triggered. + +## Instructions + +### Branching and merges + +- [`.github/workflows/check-branch.yml`](../../.github/workflows/check-branch.yml) runs on **pull requests**. If the base branch is **`master`** and the head branch is **not** **`staging`**, the job fails and [thollander/actions-comment-pull-request](https://github.com/thollander/actions-comment-pull-request) posts an explanatory comment. To merge into `master`, open a PR **from `staging`** (per current org policy). + +### Versioning + +- Package version is centralized in [`Directory.Build.props`](../../Directory.Build.props) (`Version` property). [`Contentstack.Utils.csproj`](../../Contentstack.Utils/Contentstack.Utils.csproj) uses `PackageVersion` / `ReleaseVersion` tied to `$(Version)` where applicable—bump version in one place for releases. + +### Build + +- From repo root: `dotnet build Contentstack.Utils.sln` (use `-c Release` for release builds). + +### Test scripts + +- **`Scripts/run-unit-test-case.sh`**: Deletes `Contentstack.Utils.Tests/TestResults`, runs `dotnet test Contentstack.Utils.Tests/Contentstack.Utils.Tests.csproj` with TRX logger `Report-Contentstack-DotNet-Test-Case.trx` and `XPlat code coverage`. This is what [`.github/workflows/unit-test.yml`](../../.github/workflows/unit-test.yml) runs on **Windows**. +- **`Scripts/run-test-case.sh`**: Runs `dotnet test` on the **solution** with a date-stamped TRX name, collects coverage, finds `coverage.cobertura.xml`, then runs **`python3 Scripts/generate_test_report.py`** to produce HTML under `Contentstack.Utils.Tests/TestResults/Coverage-.../index.html`. Use this for a local combined test + coverage report (Python 3 stdlib only). + +### Pack and publish + +- Local pack: `dotnet pack -c Release -o out` (same as [`.github/workflows/nuget-publish.yml`](../../.github/workflows/nuget-publish.yml)). +- Publishing: triggered on **GitHub release created**. Jobs build on Windows, pack, and push `contentstack.utils.*.nupkg` to NuGet.org and GitHub Packages (requires GitHub secrets, including **`NUGET_API_KEY`** for NuGet.org and GitHub Packages auth where applicable; do not commit secrets). + +### CI and security jobs + +| Workflow | Purpose | +|----------|---------| +| `unit-test.yml` | Windows unit tests via `run-unit-test-case.sh`. | +| `check-branch.yml` | Enforce `staging` → `master` for PRs. | +| `nuget-publish.yml` | Pack and push on release. | +| `sca-scan.yml` | `dotnet restore` + **Snyk** `snyk test` in `Contentstack.Utils` (needs `SNYK_TOKEN`). | +| `policy-scan.yml` | For **public** repos: `SECURITY.md` and license file with current calendar year. | +| `codeql-analysis.yml` | CodeQL **csharp** with autobuild. | +| `issues-jira.yml` | Mirror new issues to Jira (Atlassian actions + secrets). | + +### CODEOWNERS + +- [`CODEOWNERS`](../../CODEOWNERS): default review **`@contentstack/devex-pr-reviewers`**; **`@contentstack/security-admin`** for security workflows, `.snyk`, and related paths. +- **Note:** `CODEOWNERS` references `.github/workflows/codeql-anaylsis.yml` (typo). The actual file is **`codeql-analysis.yml`**. Fix the typo in `CODEOWNERS` in a dedicated PR if you want CodeQL ownership to apply. + +### Talisman + +- [`.talismanrc`](../../.talismanrc) pins checksums for specific files. Do not add ignores or weaken checks without security team agreement. + +## References + +- [GitHub Actions documentation](https://docs.github.com/actions) — workflow syntax and secrets. diff --git a/skills/testing/SKILL.md b/skills/testing/SKILL.md new file mode 100644 index 0000000..e0230d9 --- /dev/null +++ b/skills/testing/SKILL.md @@ -0,0 +1,59 @@ +--- +name: testing +description: Use for xUnit tests, JSON fixtures, mocks, Coverlet coverage, and scripts in Contentstack.Utils.Tests. +--- + +# Testing – Contentstack Utils .NET + +## When to use + +- Adding or changing behavior in `Contentstack.Utils` and needing unit tests. +- Debugging CI test failures on Windows (unit-test workflow). +- Generating local HTML coverage reports. + +## Instructions + +### Test project + +- [`Contentstack.Utils.Tests.csproj`](../../Contentstack.Utils.Tests/Contentstack.Utils.Tests.csproj): targets **net7.0**, **IsPackable** false, references the main library project. +- Packages: **xunit**, **xunit.runner.visualstudio**, **Microsoft.NET.Test.Sdk**, **coverlet.collector**, **Newtonsoft.Json** (aligned with test needs). + +### Test classes (inventory) + +Use these as a map of coverage areas when adding related behavior: + +| Class | Focus | +|-------|--------| +| `JsonToHtmlTest` | JSON RTE → HTML | +| `GQLTest` | `Utils.GQL` / GraphQL-shaped RTE | +| `UtilsTest` | Core `Utils` APIs | +| `UtilsArrayStringTest` | Array/string rendering paths | +| `UtilsCustomRenderTest` | Custom `Options` / rendering | +| `DefaultRenderTest` | Default render behavior | +| `HtmlDocumentExtensionTest` | HTML document extensions | +| `MetadataTest` | Embedded metadata | +| `VariantAliasesTest` | Variant aliases / metadata tags | + +### Fixtures and content + +- JSON files live under [`Contentstack.Utils.Tests/Resources/`](../../Contentstack.Utils.Tests/Resources/). The csproj uses `Content Include="Resources\**\*.json" CopyToOutputDirectory="PreserveNewest"` so files are available at test runtime. +- Checked-in fixtures: [`variantsSingleEntry.json`](../../Contentstack.Utils.Tests/Resources/variantsSingleEntry.json), [`variantsEntries.json`](../../Contentstack.Utils.Tests/Resources/variantsEntries.json). + +### Constants, helpers, mocks + +- Shared expected strings/constants: [`Constants/`](../../Contentstack.Utils.Tests/Constants/). +- Parsing helpers: [`Helpers/NodeParser.cs`](../../Contentstack.Utils.Tests/Helpers/NodeParser.cs). +- Test doubles in [`Mocks/`](../../Contentstack.Utils.Tests/Mocks/): [`CustomRenderOptionMock.cs`](../../Contentstack.Utils.Tests/Mocks/CustomRenderOptionMock.cs), [`DefaultRenderMock.cs`](../../Contentstack.Utils.Tests/Mocks/DefaultRenderMock.cs), [`EmbeddedModelMock.cs`](../../Contentstack.Utils.Tests/Mocks/EmbeddedModelMock.cs), [`GQLModel.cs`](../../Contentstack.Utils.Tests/Mocks/GQLModel.cs). Follow existing patterns for new scenarios. + +### Quick local test + +- From repo root: `dotnet test Contentstack.Utils.sln` — runs all tests without the shell scripts (no TRX or coverage collection). + +### Coverage + +- Scripts use Coverlet’s **XPlat code coverage** and emit Cobertura XML (e.g. `coverage.cobertura.xml` under `TestResults`). +- For HTML: run [`Scripts/run-test-case.sh`](../../Scripts/run-test-case.sh), which invokes [`Scripts/generate_test_report.py`](../../Scripts/generate_test_report.py) (Python 3, standard library only). + +### CI alignment + +- GitHub Actions runs [`Scripts/run-unit-test-case.sh`](../../Scripts/run-unit-test-case.sh) on **Windows**. When reproducing CI failures, use that script (or the same `dotnet test` arguments) from repo root.