Files
binarylane-dotnet/tests/BinaryLane.Api.Tests/ModelSerializationTests.cs
T
2026-08-18 00:13:44 +10:00

175 lines
6.4 KiB
C#

using System;
using System.Linq;
using System.Text.Json;
using BinaryLane.Api.V2.Http;
using BinaryLane.Api.V2.Models;
using Xunit;
namespace BinaryLane.Api.Tests;
public sealed class ModelSerializationTests
{
private static readonly JsonSerializerOptions Json =
new BinaryLaneJsonSerializerOptions().SerializerOptions;
[Fact]
public void PowerOnActionSerializesTheProviderDiscriminator()
{
string json = JsonSerializer.Serialize<ServerAction>(new PowerOnServerAction(), Json);
using JsonDocument document = JsonDocument.Parse(json);
Assert.Equal("power_on", document.RootElement.GetProperty("type").GetString());
Assert.Single(document.RootElement.EnumerateObject());
}
[Fact]
public void CreateServerRequestSerializesBinaryLaneWireNamesAndOmitsNulls()
{
CreateServerRequest request = new()
{
Name = "demo-server",
Image = "ubuntu-24.04",
Region = "melbourne",
Size = "std-min",
PortBlocking = false,
};
string json = JsonSerializer.Serialize(request, Json);
using JsonDocument document = JsonDocument.Parse(json);
Assert.Equal("demo-server", document.RootElement.GetProperty("name").GetString());
Assert.Equal("ubuntu-24.04", document.RootElement.GetProperty("image").GetString());
Assert.Equal("melbourne", document.RootElement.GetProperty("region").GetString());
Assert.Equal("std-min", document.RootElement.GetProperty("size").GetString());
Assert.False(document.RootElement.GetProperty("port_blocking").GetBoolean());
Assert.False(document.RootElement.TryGetProperty("password", out _));
Assert.False(document.RootElement.TryGetProperty("user_data", out _));
}
[Fact]
public void UnknownResponsePropertiesAreRetainedForForwardCompatibility()
{
const string json = """
{
"email": "alex@example.test",
"email_verified": true,
"two_factor_authentication_enabled": false,
"status": "active",
"tax_code": { "name": "GST", "type": "scalar" },
"configured_payment_methods": ["credit-card"],
"additional_ipv4_limit": 2,
"future_provider_field": "kept"
}
""";
Account? account = JsonSerializer.Deserialize<Account>(json, Json);
Assert.NotNull(account);
Assert.Equal("active", account.Status);
Assert.NotNull(account.AdditionalProperties);
Assert.Equal(
"kept",
account.AdditionalProperties!["future_provider_field"].GetString());
}
[Fact]
public void PreviewEnumValuesRemainRawStrings()
{
const string json = """
{
"id": 42,
"status": "future-status",
"type": "future-action",
"started_at": "2026-08-16T12:34:56Z",
"title": "Future action",
"reason": "A future provider action",
"progress": { "percent_complete": 0, "completed_steps": [] }
}
""";
BinaryLaneAction? action = JsonSerializer.Deserialize<BinaryLaneAction>(json, Json);
Assert.NotNull(action);
Assert.Equal("future-status", action.Status);
Assert.Equal("future-action", action.Type);
}
[Fact]
public void DocumentedValuesAreAvailableWithoutClosedEnums()
{
Assert.Equal("completed", BinaryLaneValues.ActionStatus.Completed);
Assert.Equal("power_on", BinaryLaneValues.ServerActionType.PowerOn);
Assert.Equal("AAAA", BinaryLaneValues.DomainRecordType.Aaaa);
}
[Fact]
public void ComplexServerActionUsesTheSharedEndpointDiscriminatorAndWireNames()
{
ServerAction action = new ChangeVpcIpv4ServerAction
{
CurrentIpv4Address = "10.0.0.10",
NewIpv4Address = "10.0.0.20",
};
string json = JsonSerializer.Serialize(action, Json);
using JsonDocument document = JsonDocument.Parse(json);
Assert.Equal("change_vpc_ipv4", document.RootElement.GetProperty("type").GetString());
Assert.Equal("10.0.0.10", document.RootElement.GetProperty("current_ipv4_address").GetString());
Assert.Equal("10.0.0.20", document.RootElement.GetProperty("new_ipv4_address").GetString());
}
[Fact]
public void ServerActionConverterDeserializesKnownAndUnknownProviderActions()
{
const string knownJson = """
{
"type": "change_vpc_ipv4",
"current_ipv4_address": "10.0.0.10",
"new_ipv4_address": "10.0.0.20"
}
""";
const string unknownJson = """
{
"type": "future_action",
"provider_option": "kept"
}
""";
ServerAction? known = JsonSerializer.Deserialize<ServerAction>(knownJson, Json);
ServerAction? unknown = JsonSerializer.Deserialize<ServerAction>(unknownJson, Json);
ChangeVpcIpv4ServerAction typed = Assert.IsType<ChangeVpcIpv4ServerAction>(known);
Assert.Equal("10.0.0.10", typed.CurrentIpv4Address);
Assert.Equal("10.0.0.20", typed.NewIpv4Address);
UnknownServerAction future = Assert.IsType<UnknownServerAction>(unknown);
Assert.Equal("future_action", future.Type);
Assert.NotNull(future.AdditionalProperties);
Assert.Equal("kept", future.AdditionalProperties!["provider_option"].GetString());
}
[Fact]
public void EveryConcreteServerActionRoundTripsThroughItsProviderDiscriminator()
{
var actionTypes = typeof(ServerAction).Assembly
.GetTypes()
.Where(type =>
type.IsSealed &&
type.IsSubclassOf(typeof(ServerAction)) &&
type.GetConstructor(Type.EmptyTypes) is not null)
.OrderBy(type => type.Name, StringComparer.Ordinal);
foreach (var actionType in actionTypes)
{
var action = (ServerAction)Activator.CreateInstance(actionType)!;
string json = JsonSerializer.Serialize<ServerAction>(action, Json);
ServerAction? deserialized = JsonSerializer.Deserialize<ServerAction>(json, Json);
Assert.NotNull(deserialized);
Assert.Equal(actionType, deserialized.GetType());
Assert.Equal(action.Type, deserialized.Type);
}
}
}