From 0d26015267ee64c3d8776af1bad783ee4c22bea8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Deniz=20=C4=B0rgin?= Date: Sat, 17 Oct 2026 13:33:25 +0300 Subject: [PATCH] test: the materialization matrix compiles three source sets instead of eight TST-E3 first half, PRs 4 and 5 together. ModelMaterializationMatrixTests binds its object/DTO scenarios (group B) and its container/promotion scenarios (group C) as one combined document each, so each group's tests request one identical plan and the compiler cache shares one compilation: 8 distinct compilations become 3. Two synthetic operations get collision-free identities (widget.containerList; widget.nullable at /api/widget/nullable); every asserted facet, test name and assertion is unchanged. The tests clone ratchet keeps a minimum headroom: the ceiling is the measurement plus 0.25 points, rounded up to the next 0.5%; at 5.48% it stays 6%. --- docs/engineering/quality-gates.md | 5 +- .../ModelMaterializationMatrixTests.cs | 240 +++++++----------- tools/jscpd/tests.baseline.json | 4 - 3 files changed, 98 insertions(+), 151 deletions(-) diff --git a/docs/engineering/quality-gates.md b/docs/engineering/quality-gates.md index 13075cea3..ec100c9f3 100644 --- a/docs/engineering/quality-gates.md +++ b/docs/engineering/quality-gates.md @@ -238,8 +238,9 @@ The lint job runs `dotnet run --file tools/opencode-tool.cs -- verify-clones`: t `tools/jscpd/`, installed with the pinned Bun from its own lockfile, at a 50-token minimum over four scopes. Hand-written `src/`, `tools/` and `tests/` are gated against the committed baselines in `tools/jscpd/`: a clone a baseline does not hold fails the job, `tools/` also fails above 3% -duplicated lines, and `tests/` above 6%. The tests threshold only falls: a change that removes -test clones lowers it to its new measurement, rounded up to the next 0.5%. Generated output is +duplicated lines, and `tests/` above 6%. The tests ceiling is the measurement plus 0.25 points, +rounded up to the next 0.5%, and it only ever falls: a change that removes test clones lowers it +to that value when it is lower than the current ceiling. Generated output is reported, not gated. The generator's manifest decides which `src/` files are generated, so the split needs no path convention. A failure names each new clone with its two `file:line` ranges and its size in tokens, and the job keeps the jscpd reports as an artifact. Locally the same command runs from the diff --git a/tests/OpenCode.Sdk.Tools.Tests/Generator/Emission/ModelMaterializationMatrixTests.cs b/tests/OpenCode.Sdk.Tools.Tests/Generator/Emission/ModelMaterializationMatrixTests.cs index 612032fcc..57e83b59a 100644 --- a/tests/OpenCode.Sdk.Tools.Tests/Generator/Emission/ModelMaterializationMatrixTests.cs +++ b/tests/OpenCode.Sdk.Tools.Tests/Generator/Emission/ModelMaterializationMatrixTests.cs @@ -501,16 +501,7 @@ private static async Task AssertRootUnionNullFailsAsync(Assembly assembly, EmitP [ParallelLimiter] public async Task Bind_Should_Compile_And_Roundtrip_An_Open_Models_Extension_Data() { - var document = await BindingTestHost.IngestAsync(SpecScenario.Define(spec => spec - .WithObject("OpenSettings", schema => schema - .Property("timeout", property => property.Type("number")) - .AllOf(rest => rest.Type("object").AdditionalProperties(value => value.Unrestricted()))) - .WithTaggedError("WidgetError", "WidgetError") - .WithOperation("settings.get", path: "/api/settings", configure: operation => operation - .Response(200, "application/json", schema => schema.Ref("OpenSettings")) - .Response(400, "application/json", schema => schema.Ref("WidgetError"))))); - var plan = new BindingTestHost().Bind(document, Selection("settings.get"), - Curation(Groups("settings", RootGroup()), redactedOpenMembers: [OpenMembers("OpenSettings", redact: false)])); + var plan = await CreateObjectDtoPlanAsync(); var assembly = await GeneratedSourceCompiler.CompileAndLoadWithSdkCoreAsync(SourceEmitter.Emit(plan)); var typeInfo = ResolveTypeInfo(assembly, "OpenCode.Sdk.Models.OpenSettings"); @@ -559,7 +550,8 @@ public async Task Bind_Should_Compile_And_Roundtrip_An_Open_Models_Extension_Dat [ParallelLimiter] public async Task Bind_Should_Compile_And_Roundtrip_Container_Envelope_Payloads() { - var (plan, operations) = await CreateContainerEnvelopePlanAsync(); + var plan = await CreateContainerPromotionPlanAsync(); + var operations = GetContainerOperations(plan); await AssertContainerPayloadShapesAsync(operations); var assembly = await GeneratedSourceCompiler.CompileAndLoadWithSdkCoreAsync(SourceEmitter.Emit(plan)); @@ -582,7 +574,9 @@ public async Task Bind_Should_Compile_And_Roundtrip_Container_Envelope_Payloads( [ParallelLimiter] public async Task Bind_Should_Compile_And_Roundtrip_Promoted_Inline_Envelope_Payloads() { - var (plan, stats, handoff) = await CreatePromotedInlinePlanAsync(); + var plan = await CreateContainerPromotionPlanAsync(); + var stats = GetOperation(plan, "widget.stats"); + var handoff = GetOperation(plan, "widget.handoff"); await Assert.That(((NamedTypeReferencePlan)stats.Envelope!.PayloadType!).Name).IsEqualTo("WidgetStatsData"); await Assert.That(((NamedTypeReferencePlan)handoff.Envelope!.PayloadType!).Name).IsEqualTo("WidgetHandoffData"); @@ -608,7 +602,9 @@ public async Task Bind_Should_Compile_And_Roundtrip_Promoted_Inline_Envelope_Pay [ParallelLimiter] public async Task Bind_Should_Materialize_A_Single_Key_Envelope_Under_Its_Own_Wire_Key() { - var (plan, operation) = await CreateSingleKeyEnvelopePlanAsync(); + var plan = await CreateObjectDtoPlanAsync(); + var operation = plan.Clients.SelectMany(static client => client.Operations) + .Single(static candidate => candidate.OperationId == "widget.handoff"); await Assert.That(operation.Envelope!.PayloadName).IsEqualTo("Handoff"); var assembly = await GeneratedSourceCompiler.CompileAndLoadWithSdkCoreAsync(SourceEmitter.Emit(plan)); @@ -633,7 +629,8 @@ public async Task Bind_Should_Materialize_A_Single_Key_Envelope_Under_Its_Own_Wi [ParallelLimiter] public async Task Bind_Should_Materialize_A_Nullable_Data_Envelope_Payload_By_Response_State() { - var (plan, operation) = await CreateNullableDataEnvelopePlanAsync(); + var plan = await CreateContainerPromotionPlanAsync(); + var operation = GetOperation(plan, "widget.nullable"); await Assert.That(operation.Envelope!.PayloadType!.IsNullable).IsTrue(); var assembly = await GeneratedSourceCompiler.CompileAndLoadWithSdkCoreAsync(SourceEmitter.Emit(plan)); @@ -675,17 +672,32 @@ public async Task Bind_Should_Materialize_A_Nullable_Data_Envelope_Payload_By_Re [ParallelLimiter] public async Task Bind_Should_Still_Fail_To_Materialize_A_Wire_Null_For_A_Non_Nullable_Data_Envelope_Payload() { - var (plan, operations) = await CreateContainerEnvelopePlanAsync(); + var plan = await CreateContainerPromotionPlanAsync(); + var operations = GetContainerOperations(plan); var assembly = await GeneratedSourceCompiler.CompileAndLoadWithSdkCoreAsync(SourceEmitter.Emit(plan)); await AssertMissingDataFailsAsync(assembly, operations.DataList.Envelope!, """{"data":null}"""); } - private static async Task<(EmitPlan Plan, OperationPlan Operation)> CreateSingleKeyEnvelopePlanAsync() + private static async Task CreateObjectDtoPlanAsync() { var document = await BindingTestHost.IngestAsync(SpecScenario.Define(spec => spec + .WithObject("OpenSettings", schema => schema + .Property("timeout", property => property.Type("number")) + .AllOf(rest => rest.Type("object").AdditionalProperties(value => value.Unrestricted()))) .WithObject("WidgetInfo", SchemaPresets.RequiredId) + .WithSchema("PlaceInfo", DefinePlaceInfo) + .WithSchema("WidgetNameList", schema => schema + .Type("array") + .Items(static item => item.Type("string"))) + .WithObject("WidgetEnvelope", schema => schema + .AdditionalPropertiesFalse() + .Property("location", property => property.Ref("PlaceInfo"), required: true) + .Property("data", property => property.Ref("WidgetNameList"), required: true)) .WithTaggedError("WidgetError", "WidgetError") + .WithOperation("settings.get", path: "/api/settings", configure: operation => operation + .Response(200, "application/json", schema => schema.Ref("OpenSettings")) + .Response(400, "application/json", schema => schema.Ref("WidgetError"))) .WithOperation("widget.handoff", path: "/api/widget/handoff", configure: operation => operation .Response(200, "application/json", schema => schema .Type("object") @@ -693,38 +705,19 @@ public async Task Bind_Should_Still_Fail_To_Materialize_A_Wire_Null_For_A_Non_Nu .Property("handoff", property => property.AnyOf( static branch => branch.Ref("WidgetInfo"), static branch => branch.Type("null")), required: true)) - .Response(400, "application/json", schema => schema.Ref("WidgetError"))))); - - var plan = new BindingTestHost().Bind( - document, - Selection("widget.handoff"), - Curation(Groups("widget", RootGroup()))); - - var operation = plan.Clients.SelectMany(static client => client.Operations).Single(); - return (plan, operation); - } - - private static async Task<(EmitPlan Plan, OperationPlan Operation)> CreateNullableDataEnvelopePlanAsync() - { - var document = await BindingTestHost.IngestAsync(SpecScenario.Define(spec => spec - .WithObject("WidgetInfo", SchemaPresets.RequiredId) - .WithTaggedError("WidgetError", "WidgetError") - .WithObject("WidgetResponse", schema => schema - .AdditionalPropertiesFalse() - .Property("data", property => property.AnyOf( - static branch => branch.Ref("WidgetInfo"), - static branch => branch.Type("null")), required: true)) + .Response(400, "application/json", schema => schema.Ref("WidgetError"))) .WithOperation("widget.list", path: "/api/widget", configure: operation => operation - .Response(200, "application/json", schema => schema.Ref("WidgetResponse")) + .Response(200, "application/json", schema => schema.Ref("WidgetEnvelope")) .Response(400, "application/json", schema => schema.Ref("WidgetError"))))); - var plan = new BindingTestHost().Bind( + var groups = Groups("settings", RootGroup()); + groups.Add("widget", RootGroup()); + return new BindingTestHost().Bind( document, - Selection("widget.list"), - Curation(Groups("widget", RootGroup()))); - - var operation = plan.Clients.SelectMany(static client => client.Operations).Single(); - return (plan, operation); + Selection("settings.get", "widget.handoff", "widget.list"), + Curation( + groups, + redactedOpenMembers: [OpenMembers("OpenSettings", redact: false)])); } /// @@ -735,7 +728,8 @@ public async Task Bind_Should_Still_Fail_To_Materialize_A_Wire_Null_For_A_Non_Nu [ParallelLimiter] public async Task Bind_Should_Compile_And_Roundtrip_A_Promoted_Data_Location_List_Item() { - var (plan, operation) = await CreatePromotedDataLocationListPlanAsync(); + var plan = await CreateContainerPromotionPlanAsync(); + var operation = GetOperation(plan, "widget.list"); await Assert.That(operation.Envelope!.Kind).IsEqualTo(EnvelopeKind.DataLocationList); var elementType = (NamedTypeReferencePlan)((ListTypeReferencePlan)operation.Envelope.PayloadType!).ElementType; await Assert.That(elementType.Name).IsEqualTo("WidgetListData"); @@ -754,32 +748,6 @@ public async Task Bind_Should_Compile_And_Roundtrip_A_Promoted_Data_Location_Lis private static void DefinePlaceInfo(SchemaBuilder schema) => _ = schema.Type("object") .Property("directory", property => property.Type("string"), required: true); - private static async Task<(EmitPlan Plan, OperationPlan Operation)> CreatePromotedDataLocationListPlanAsync() - { - var document = await BindingTestHost.IngestAsync(SpecScenario.Define(spec => spec - .WithSchema("PlaceInfo", DefinePlaceInfo) - .WithTaggedError("WidgetError", "WidgetError") - .WithObject("WidgetEnvelope", schema => schema - .AdditionalPropertiesFalse() - .Property("location", property => property.Ref("PlaceInfo"), required: true) - .Property("data", property => property - .Type("array") - .Items(static item => item - .Type("object") - .Property("id", static inner => inner.Type("string"), required: true)), required: true)) - .WithOperation("widget.list", path: "/api/widget", configure: operation => operation - .Response(200, "application/json", schema => schema.Ref("WidgetEnvelope")) - .Response(400, "application/json", schema => schema.Ref("WidgetError"))))); - - var plan = new BindingTestHost().Bind( - document, - Selection("widget.list"), - Curation(Groups("widget", RootGroup()))); - - var operation = plan.Clients.SelectMany(static client => client.Operations).Single(); - return (plan, operation); - } - /// /// The mechanism extension: a location envelope's 'data' member is a RefNode naming an /// ARRAY component directly (vcs.branches' exact shape, Vcs.BranchList = @@ -792,7 +760,9 @@ private static void DefinePlaceInfo(SchemaBuilder schema) => _ = schema.Type("ob [ParallelLimiter] public async Task Bind_Should_Compile_And_Roundtrip_A_Data_Location_List_Ref_To_A_Named_Array_Component() { - var (plan, operation) = await CreateRefToNamedArrayDataLocationListPlanAsync(); + var plan = await CreateObjectDtoPlanAsync(); + var operation = plan.Clients.SelectMany(static client => client.Operations) + .Single(static candidate => candidate.OperationId == "widget.list"); await Assert.That(operation.Envelope!.Kind).IsEqualTo(EnvelopeKind.DataLocationList); var elementType = (NamedTypeReferencePlan)((ListTypeReferencePlan)operation.Envelope.PayloadType!).ElementType; await Assert.That(elementType.Name).IsEqualTo("string"); @@ -810,36 +780,39 @@ public async Task Bind_Should_Compile_And_Roundtrip_A_Data_Location_List_Ref_To_ await Assert.That(GetProperty(location, "Directory")).IsEqualTo("widget-place"); } - private static async Task<(EmitPlan Plan, OperationPlan Operation)> CreateRefToNamedArrayDataLocationListPlanAsync() + private static async Task CreateContainerPromotionPlanAsync() { var document = await BindingTestHost.IngestAsync(SpecScenario.Define(spec => spec - .WithSchema("PlaceInfo", DefinePlaceInfo) + .WithObject("WidgetInfo", SchemaPresets.RequiredId) .WithTaggedError("WidgetError", "WidgetError") - .WithSchema("WidgetNameList", schema => schema - .Type("array") - .Items(static item => item.Type("string"))) + .WithSchema("PlaceInfo", DefinePlaceInfo) + .WithObject("WidgetListEnvelope", schema => schema + .AdditionalPropertiesFalse() + .Property("data", property => property.Type("array").Items(item => item.Ref("WidgetInfo")), required: true)) + .WithObject("WidgetMapEnvelope", schema => schema + .AdditionalPropertiesFalse() + .Property("data", property => property.Type("object").AdditionalProperties(value => value.Ref("WidgetInfo")), + required: true)) + .WithObject("WidgetResponse", schema => schema + .AdditionalPropertiesFalse() + .Property("data", property => property.AnyOf( + static branch => branch.Ref("WidgetInfo"), + static branch => branch.Type("null")), required: true)) .WithObject("WidgetEnvelope", schema => schema .AdditionalPropertiesFalse() .Property("location", property => property.Ref("PlaceInfo"), required: true) - .Property("data", property => property.Ref("WidgetNameList"), required: true)) - .WithOperation("widget.list", path: "/api/widget", configure: operation => operation - .Response(200, "application/json", schema => schema.Ref("WidgetEnvelope")) - .Response(400, "application/json", schema => schema.Ref("WidgetError"))))); - - var plan = new BindingTestHost().Bind( - document, - Selection("widget.list"), - Curation(Groups("widget", RootGroup()))); - - var operation = plan.Clients.SelectMany(static client => client.Operations).Single(); - return (plan, operation); - } - - private static async Task<(EmitPlan Plan, OperationPlan Stats, OperationPlan Handoff)> CreatePromotedInlinePlanAsync() - { - var document = await BindingTestHost.IngestAsync(SpecScenario.Define(spec => spec - .WithObject("WidgetInfo", SchemaPresets.RequiredId) - .WithTaggedError("WidgetError", "WidgetError") + .Property("data", property => property + .Type("array") + .Items(static item => item + .Type("object") + .Property("id", static inner => inner.Type("string"), required: true)), required: true)) + .WithOperation("widget.containerList", path: "/api/widget/list", + configure: operation => DefineWidgetResponse(operation, "WidgetListEnvelope")) + .WithJsonOperation("widget.map", "/api/widget/map", schema => schema.Ref("WidgetMapEnvelope")) + .WithJsonOperation("widget.bareList", "/api/widget/bare-list", + schema => schema.Type("array").Items(item => item.Ref("WidgetInfo"))) + .WithJsonOperation("widget.bareMap", "/api/widget/bare-map", + schema => schema.Type("object").AdditionalProperties(value => value.Ref("WidgetInfo"))) // Both bodies carry a second, optional member: a one-member inline body is the // single-key facet's, and this plan is about promoted bare payloads. .WithOperation("widget.stats", path: "/api/widget/stats", configure: operation => operation @@ -849,68 +822,45 @@ public async Task Bind_Should_Compile_And_Roundtrip_A_Data_Location_List_Ref_To_ .Property("count", property => property.Type("integer"), required: true) .Property("label", property => property.Type("string"))) .Response(400, "application/json", schema => schema.Ref("WidgetError"))) - .WithOperation("widget.handoff", path: "/api/widget/handoff", configure: operation => operation - .Response(200, "application/json", schema => schema - .Type("object") - .AdditionalPropertiesFalse() - .Property("handoff", property => property.Ref("WidgetInfo"), required: true) - .Property("note", property => property.Type("string")))))); - - var plan = new BindingTestHost().Bind( - document, - Selection("widget.stats", "widget.handoff"), - Curation(Groups("widget", RootGroup()))); - - var bound = plan.Clients.SelectMany(static client => client.Operations).ToArray(); - return ( - plan, - bound.Single(static operation => operation.MethodName == "GetStatsAsync"), - bound.Single(static operation => operation.MethodName == "GetHandoffAsync")); - } - - private static async Task<(EmitPlan Plan, ContainerOperations Operations)> CreateContainerEnvelopePlanAsync() - { - var document = await BindingTestHost.IngestAsync(SpecScenario.Define(spec => spec - .WithObject("WidgetInfo", SchemaPresets.RequiredId) - .WithObject("WidgetListEnvelope", schema => schema + .WithJsonOperation("widget.handoff", "/api/widget/handoff", schema => schema + .Type("object") .AdditionalPropertiesFalse() - .Property("data", property => property.Type("array").Items(item => item.Ref("WidgetInfo")), required: true)) - .WithObject("WidgetMapEnvelope", schema => schema - .AdditionalPropertiesFalse() - .Property("data", property => property.Type("object").AdditionalProperties(value => value.Ref("WidgetInfo")), - required: true)) - .WithTaggedError("WidgetError", "WidgetError") - .WithOperation("widget.list", path: "/api/widget/list", configure: operation => operation - .Response(200, "application/json", schema => schema.Ref("WidgetListEnvelope")) - .Response(400, "application/json", schema => schema.Ref("WidgetError"))) - .WithJsonOperation("widget.map", "/api/widget/map", schema => schema.Ref("WidgetMapEnvelope")) - .WithJsonOperation("widget.bareList", "/api/widget/bare-list", - schema => schema.Type("array").Items(item => item.Ref("WidgetInfo"))) - .WithJsonOperation("widget.bareMap", "/api/widget/bare-map", - schema => schema.Type("object").AdditionalProperties(value => value.Ref("WidgetInfo"))))); + .Property("handoff", property => property.Ref("WidgetInfo"), required: true) + .Property("note", property => property.Type("string"))) + .WithOperation("widget.nullable", path: "/api/widget/nullable", + configure: operation => DefineWidgetResponse(operation, "WidgetResponse")) + .WithOperation("widget.list", path: "/api/widget", + configure: operation => DefineWidgetResponse(operation, "WidgetEnvelope")))); - var plan = new BindingTestHost().Bind( + return new BindingTestHost().Bind( document, - Selection("widget.list", "widget.map", "widget.bareList", "widget.bareMap"), + Selection("widget.containerList", "widget.map", "widget.bareList", "widget.bareMap", + "widget.stats", "widget.handoff", "widget.nullable", "widget.list"), Curation( Groups("widget", RootGroup()), operationNames: [ - OperationName("widget.list", "GetWidgetListAsync"), + OperationName("widget.containerList", "GetWidgetListAsync"), OperationName("widget.map", "GetWidgetMapAsync"), OperationName("widget.bareList", "GetWidgetBareListAsync"), OperationName("widget.bareMap", "GetWidgetBareMapAsync"), ])); - - var bound = plan.Clients.SelectMany(static client => client.Operations).ToArray(); - var operations = new ContainerOperations( - DataList: bound.Single(static operation => operation.MethodName == "GetWidgetListAsync"), - DataMap: bound.Single(static operation => operation.MethodName == "GetWidgetMapAsync"), - BareList: bound.Single(static operation => operation.MethodName == "GetWidgetBareListAsync"), - BareMap: bound.Single(static operation => operation.MethodName == "GetWidgetBareMapAsync")); - return (plan, operations); } + private static void DefineWidgetResponse(OperationBuilder operation, string schemaName) => _ = operation + .Response(200, "application/json", schema => schema.Ref(schemaName)) + .Response(400, "application/json", schema => schema.Ref("WidgetError")); + + private static OperationPlan GetOperation(EmitPlan plan, string operationId) => + plan.Clients.SelectMany(static client => client.Operations) + .Single(operation => operation.OperationId == operationId); + + private static ContainerOperations GetContainerOperations(EmitPlan plan) => new( + DataList: GetOperation(plan, "widget.containerList"), + DataMap: GetOperation(plan, "widget.map"), + BareList: GetOperation(plan, "widget.bareList"), + BareMap: GetOperation(plan, "widget.bareMap")); + private static async Task AssertContainerPayloadShapesAsync(ContainerOperations operations) { await Assert.That(operations.DataList.Envelope!.Kind).IsEqualTo(EnvelopeKind.Data); diff --git a/tools/jscpd/tests.baseline.json b/tools/jscpd/tests.baseline.json index a4c3283e8..2511d1ae0 100644 --- a/tools/jscpd/tests.baseline.json +++ b/tools/jscpd/tests.baseline.json @@ -155,7 +155,6 @@ "34812987b8a4a722": 1, "34ff66db689cfea4": 1, "350995582d58fa70": 1, - "3585ff3c763db8ec": 1, "365bc439e50e1c16": 1, "366c2d108bd284de": 1, "36c9a7fe23bd443f": 1, @@ -245,7 +244,6 @@ "58cf3aedc0f4f1c5": 1, "58df2dce6a57ddc3": 1, "5977e81f73804251": 1, - "59be108b44da0966": 1, "5ae402699aab9b4f": 1, "5b5f89cf29e631cb": 1, "5bb29ca2159fbdc5": 1, @@ -347,7 +345,6 @@ "7c4d439b46ebc8bf": 1, "7cb3773f240a7d3b": 1, "7da92939d4cdca00": 1, - "7df00680786e1792": 1, "7e09a30a918c9d2f": 1, "7ea14f75d7412427": 1, "7eda9e729d5869ca": 1, @@ -535,7 +532,6 @@ "ca23d20b55a208df": 1, "ca52433f79ca9adf": 1, "cb72f4a5d460e4a8": 1, - "cc7e06ec26f04567": 1, "ccc29e43c6a46325": 1, "cd14fbe400cc8d59": 1, "cd3551439395039c": 1,