test(api): cover [JsonProperty] path + fail loudly on rename collision (#198, review)
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 7m59s
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 14s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 10m31s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 7m59s
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 14s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 10m31s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Adversarial-review follow-ups:
- OpenApiSerializerContractTests: add a fifth case, a fully-populated
ChannelResponseModel — the only DTO with a [JsonProperty("ffmpegProfile")]
override, i.e. the attribute path of NewtonsoftSchemaNamingTransformer that
the existing four cases never exercised.
- NewtonsoftSchemaNamingTransformer: a rename colliding with an existing schema
key now throws InvalidOperationException (naming the type and keys) instead of
silently overwriting/dropping a property — the generator must never emit a
lossy spec.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ using System.Text.Json;
|
||||
using ErsatzTV.Application.Artworks;
|
||||
using ErsatzTV.Application.Channels;
|
||||
using ErsatzTV.Application.Watermarks;
|
||||
using ErsatzTV.Core.Api.Channels;
|
||||
using ErsatzTV.Core.Api.MediaItems;
|
||||
using ErsatzTV.Core.Api.Settings;
|
||||
using ErsatzTV.Serialization;
|
||||
@@ -41,6 +42,11 @@ public class OpenApiSerializerContractTests
|
||||
yield return new TestCaseData(FullyPopulatedWatermark(), "WatermarkViewModel").SetName("WatermarkViewModel");
|
||||
yield return new TestCaseData(FullyPopulatedMediaItemInfo(), "MediaItemInfoResponseModel")
|
||||
.SetName("MediaItemInfoResponseModel");
|
||||
|
||||
// The only DTO with a [JsonProperty] override (FFmpegProfile -> "ffmpegProfile") — covers
|
||||
// the attribute path of NewtonsoftSchemaNamingTransformer, which the cases above don't.
|
||||
yield return new TestCaseData(FullyPopulatedChannelSummary(), "ChannelResponseModel")
|
||||
.SetName("ChannelResponseModel");
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(Cases))]
|
||||
@@ -150,6 +156,19 @@ public class OpenApiSerializerContractTests
|
||||
[],
|
||||
[]);
|
||||
|
||||
private static ChannelResponseModel FullyPopulatedChannelSummary() => new(
|
||||
1,
|
||||
"1",
|
||||
1.0,
|
||||
"Name",
|
||||
"Group",
|
||||
"Categories",
|
||||
"1080p H.264",
|
||||
"en",
|
||||
"TransportStream",
|
||||
true,
|
||||
true);
|
||||
|
||||
private static string FindOpenApiDocument()
|
||||
{
|
||||
DirectoryInfo? directory = new(TestContext.CurrentContext.TestDirectory);
|
||||
|
||||
@@ -67,11 +67,18 @@ public static class NewtonsoftSchemaNamingTransformer
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
// Rebuild Properties preserving insertion order.
|
||||
// Rebuild Properties preserving insertion order. A rename colliding with an existing key
|
||||
// would silently drop a property from the spec — fail loudly instead (near-unreachable,
|
||||
// but the generator must never emit a lossy document).
|
||||
var renamedProperties = new Dictionary<string, IOpenApiSchema>(properties.Count, StringComparer.Ordinal);
|
||||
foreach ((string key, IOpenApiSchema value) in properties)
|
||||
{
|
||||
renamedProperties[renames.TryGetValue(key, out string newKey) ? newKey : key] = value;
|
||||
string finalKey = renames.TryGetValue(key, out string newKey) ? newKey : key;
|
||||
if (!renamedProperties.TryAdd(finalKey, value))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"OpenAPI schema property rename collision on '{clrType.FullName}': renaming '{key}' to '{finalKey}' would overwrite an existing property.");
|
||||
}
|
||||
}
|
||||
|
||||
schema.Properties = renamedProperties;
|
||||
|
||||
Reference in New Issue
Block a user