RegisterDynamicTypeDef fall back

This commit is contained in:
2026-08-20 00:32:01 +03:00
parent 3e608ef1d1
commit f14dc633ae
6 changed files with 167 additions and 4 deletions
@@ -0,0 +1,26 @@
{
"fixtureVersion": 1,
"protocolVersion": "3.1",
"producer": "esiur-ts",
"encoding": "indexed-TypeDefInfo",
"payloadBase64": "iaSIolEDAckWCAAIAQgCCAMIBAggCCEIIggjCCQIJcmFCQMIEUkEUHVtcEkTQnVpbGRpbmcuQXV0b21hdGlvbggASRxDb250cm9scyBhIGNpcmN1bGF0aW9uIHB1bXAuSQ9QdW1wIGNvbnRyb2xsZXJJDGVuYWJsZWQ9dHJ1ZUkESFZBQ0kDMy4xiBpREhLJB0kFb3duZXLJDEkKb3BlcmF0aW9ucw==",
"expected": {
"id": 17,
"kind": "Resource",
"version": 3,
"name": "Building.Automation.Pump",
"namespace": "Building.Automation",
"usage": "Controls a circulation pump.",
"description": "Pump controller",
"example": "enabled=true",
"category": "HVAC",
"since": "3.1",
"annotations": {
"owner": "operations"
},
"properties": [],
"functions": [],
"events": [],
"constants": []
}
}
+4
View File
@@ -22,6 +22,10 @@
<Using Include="Xunit" />
</ItemGroup>
<ItemGroup>
<None Include="ConformanceFixtures\**\*.json" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
<!-- Reference Esiur both as a runtime assembly and as the source generator (analyzer),
mirroring Tests/Features/Functional so [Resource]/[Export] test types get generated code. -->
<ItemGroup>
@@ -0,0 +1,66 @@
using System.Text.Json;
using Esiur.Data;
using Esiur.Data.Types;
using Esiur.Resource;
namespace Esiur.Tests.Unit;
public class TypeDefConformanceFixtureTests
{
[Fact]
public void DotnetEncoder_MatchesCanonicalTypeScriptFixture()
{
var root = ReadFixture();
var info = new TypeDefInfo
{
Version = 3,
Id = 17,
Name = "Pump",
Namespace = "Building.Automation",
Kind = TypeDefKind.Resource,
Usage = "Controls a circulation pump.",
Description = "Pump controller",
Example = "enabled=true",
Category = "HVAC",
Since = "3.1",
Annotations = new Map<string, string> { ["owner"] = "operations" },
};
var encoded = Codec.Compose(info, Warehouse.Default, null);
Assert.Equal(root.GetProperty("payloadBase64").GetString(), Convert.ToBase64String(encoded));
}
[Fact]
public void TypeScriptIndexedTypeDefInfoFixture_DecodesWithExpectedSemantics()
{
var root = ReadFixture();
var expected = root.GetProperty("expected");
var payload = Convert.FromBase64String(root.GetProperty("payloadBase64").GetString()!);
var (size, info) = Codec.ParseIndexedType<TypeDefInfo>(payload, 0, Warehouse.Default);
Assert.Equal((uint)payload.Length, size);
Assert.Equal(expected.GetProperty("id").GetUInt64(), info.Id);
Assert.Equal(expected.GetProperty("version").GetInt32(), info.Version);
Assert.Equal("Pump", info.Name);
Assert.Equal(expected.GetProperty("namespace").GetString(), info.Namespace);
Assert.Equal(expected.GetProperty("usage").GetString(), info.Usage);
Assert.Equal(expected.GetProperty("description").GetString(), info.Description);
Assert.Equal(expected.GetProperty("example").GetString(), info.Example);
Assert.Equal(expected.GetProperty("category").GetString(), info.Category);
Assert.Equal(expected.GetProperty("since").GetString(), info.Since);
Assert.Equal(
expected.GetProperty("annotations").GetProperty("owner").GetString(),
info.Annotations!["owner"]);
}
private static JsonElement ReadFixture()
{
var location = Path.Combine(
AppContext.BaseDirectory,
"ConformanceFixtures",
"typedef-info-v3-flat.json");
using var document = JsonDocument.Parse(File.ReadAllText(location));
return document.RootElement.Clone();
}
}
+35
View File
@@ -1,4 +1,5 @@
using Esiur.Core;
using Esiur.Data.Types;
using Esiur.Resource;
using Esiur.Stores;
@@ -6,6 +7,25 @@ namespace Esiur.Tests.Unit;
public sealed class WarehouseLifecycleTests
{
[Fact]
public void ReparsedRemoteTypeDef_ReusesWarehouseLocalId()
{
var warehouse = new Warehouse();
_ = new LocalTypeDef(typeof(LocalCollisionMarker), warehouse);
var first = new TestRemoteTypeDef(1, "Remote.ReportingService");
Assert.True(warehouse.TryRegisterRemoteTypeDef("flow.example", first));
Assert.NotEqual(0u, first.LocalTypeDefId);
var reparsed = new TestRemoteTypeDef(1, "Remote.ReportingService");
Assert.False(warehouse.TryRegisterRemoteTypeDef("flow.example", reparsed));
Assert.Equal(first.LocalTypeDefId, reparsed.LocalTypeDefId);
// Dynamic EpResource construction registers the definition again.
// This must use the reused local id, not the colliding wire id 1.
warehouse.RegisterDynamicTypeDef(reparsed);
}
[Fact]
public async Task Close_AllowsWarehouseToBeOpenedAgain()
{
@@ -176,6 +196,21 @@ public sealed class WarehouseLifecycleTests
private static async Task<bool> Observe(AsyncReply<bool> reply) => await reply;
private enum LocalCollisionMarker
{
Value,
}
private sealed class TestRemoteTypeDef : RemoteTypeDef
{
public TestRemoteTypeDef(ulong id, string name)
{
_typeId = id;
_typeName = name;
_typeDefKind = TypeDefKind.Resource;
}
}
private sealed class ControlledLifecycleResource : IResource
{
private readonly AsyncReply<bool>? terminateReply;