From 5feb1cef9e109aeeb8db1a3f039f031345d72941 Mon Sep 17 00:00:00 2001 From: Theaux Masquelier <43664045+Theauxm@users.noreply.github.com> Date: Tue, 5 May 2026 15:07:03 -0600 Subject: [PATCH] =?UTF-8?q?test:=20push=20Cli=20to=2095%=20=E2=80=94=20exc?= =?UTF-8?q?lude=20Program=20entry=20point=20and=20cover=20OpenAPI=20numeri?= =?UTF-8?q?c-collision=20/=20string-enum-component=20/=20cached-ref=20path?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Trax.Cli/Program.Coverage.cs | 8 +++ .../Fixtures/Schemas/openapi-more-edges.json | 62 +++++++++++++++++++ .../UnitTests/OpenApiMoreEdgesTests.cs | 52 ++++++++++++++++ 3 files changed, 122 insertions(+) create mode 100644 src/Trax.Cli/Program.Coverage.cs create mode 100644 tests/Trax.Cli.Tests/Fixtures/Schemas/openapi-more-edges.json create mode 100644 tests/Trax.Cli.Tests/UnitTests/OpenApiMoreEdgesTests.cs diff --git a/src/Trax.Cli/Program.Coverage.cs b/src/Trax.Cli/Program.Coverage.cs new file mode 100644 index 0000000..3b59399 --- /dev/null +++ b/src/Trax.Cli/Program.Coverage.cs @@ -0,0 +1,8 @@ +using System.Diagnostics.CodeAnalysis; + +// The CLI entry point in Program.cs is a top-level program that's never invoked +// during unit tests (tests target GenerateCommand.Handle directly). The synthesized +// Program class gets a partial declaration here so we can mark the auto-generated +// Main method as excluded from coverage rather than carrying a permanent 0%. +[ExcludeFromCodeCoverage] +internal partial class Program; diff --git a/tests/Trax.Cli.Tests/Fixtures/Schemas/openapi-more-edges.json b/tests/Trax.Cli.Tests/Fixtures/Schemas/openapi-more-edges.json new file mode 100644 index 0000000..6f275d1 --- /dev/null +++ b/tests/Trax.Cli.Tests/Fixtures/Schemas/openapi-more-edges.json @@ -0,0 +1,62 @@ +{ + "openapi": "3.0.0", + "info": { "title": "More Edges", "version": "1.0" }, + "paths": { + "/a/list": { + "get": { + "operationId": "listThings", + "responses": { "200": { "description": "ok" } } + } + }, + "/b/list": { + "get": { + "operationId": "listThings", + "responses": { "200": { "description": "ok" } } + } + }, + "/c/list": { + "get": { + "operationId": "listThings", + "responses": { "200": { "description": "ok" } } + } + }, + "/with-status": { + "get": { + "operationId": "getStatus", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Status" } + } + } + } + } + } + }, + "/with-status-again": { + "get": { + "operationId": "getStatus2", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Status" } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Status": { + "type": "string", + "enum": ["Active", "Inactive", "Pending"] + } + } + } +} diff --git a/tests/Trax.Cli.Tests/UnitTests/OpenApiMoreEdgesTests.cs b/tests/Trax.Cli.Tests/UnitTests/OpenApiMoreEdgesTests.cs new file mode 100644 index 0000000..3452011 --- /dev/null +++ b/tests/Trax.Cli.Tests/UnitTests/OpenApiMoreEdgesTests.cs @@ -0,0 +1,52 @@ +using FluentAssertions; +using Trax.Cli.Schema.OpenApi; + +namespace Trax.Cli.Tests.UnitTests; + +[TestFixture] +public class OpenApiMoreEdgesTests +{ + private static string FixturePath(string name) => + Path.Combine(TestContext.CurrentContext.TestDirectory, "Fixtures", "Schemas", name); + + [Test] + public void Parse_OperationIdCollision_NoPathParams_FallsBackToNumericSuffix() + { + var parser = new OpenApiSchemaParser(); + var schema = parser.Parse(FixturePath("openapi-more-edges.json")); + + // Three GET ops with operationId "listThings" and no path params force the + // numeric fallback in EnsureUniqueOperationName. + var listOps = schema + .Operations.Where(o => o.Name.StartsWith("ListThings", StringComparison.Ordinal)) + .Select(o => o.Name) + .ToList(); + + listOps.Should().HaveCount(3); + listOps.Should().Contain("ListThings"); + listOps.Should().Contain(n => n.EndsWith("2") || n.EndsWith("3")); + } + + [Test] + public void Parse_StringEnumComponent_BuildsApiEnum() + { + var parser = new OpenApiSchemaParser(); + var schema = parser.Parse(FixturePath("openapi-more-edges.json")); + + var statusEnum = schema.Enums.SingleOrDefault(e => e.Name == "Status"); + statusEnum.Should().NotBeNull(); + statusEnum!.Values.Should().BeEquivalentTo("Active", "Inactive", "Pending"); + } + + [Test] + public void Parse_RepeatedRef_ReusesCachedType() + { + // Two operations both reference Status — second resolution hits the + // _resolvedTypes/_resolvedEnums cache rather than building a fresh entry. + var parser = new OpenApiSchemaParser(); + var schema = parser.Parse(FixturePath("openapi-more-edges.json")); + + // One enum entry total even though referenced twice. + schema.Enums.Where(e => e.Name == "Status").Should().HaveCount(1); + } +}