Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions src/Docker.DotNet/ConsoleSizeConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using Docker.DotNet.Models;

namespace Docker.DotNet;

/// <summary>
/// Serializes <see cref="ConsoleSize"/> as a JSON array [height, width] to match
/// the Docker Engine API's Go type <c>[2]uint</c>.
/// </summary>
internal class ConsoleSizeConverter : JsonConverter<ConsoleSize>
{
public override ConsoleSize Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.Null)
{
return null;
}

if (reader.TokenType != JsonTokenType.StartArray)
{
throw new JsonException("Expected a JSON array for ConsoleSize.");
}

reader.Read();
var height = reader.GetUInt64();

reader.Read();
var width = reader.GetUInt64();

reader.Read(); // EndArray

return new ConsoleSize
{
Height = height,
Width = width
};
}

public override void Write(Utf8JsonWriter writer, ConsoleSize value, JsonSerializerOptions options)
{
if (value == null)
{
writer.WriteNullValue();
return;
}

writer.WriteStartArray();
writer.WriteNumberValue(value.Height);
writer.WriteNumberValue(value.Width);
writer.WriteEndArray();
}
}
1 change: 1 addition & 0 deletions src/Docker.DotNet/Models/ConsoleSize.Generated.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
namespace Docker.DotNet.Models
{
[JsonConverter(typeof(ConsoleSizeConverter))]
public class ConsoleSize // (client.ConsoleSize)
{
[JsonPropertyName("Height")]
Expand Down
147 changes: 147 additions & 0 deletions test/Docker.DotNet.Tests/ConsoleSizeConverterTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
namespace Docker.DotNet.Tests;

public sealed class ConsoleSizeConverterTests
{
[Fact]
public void Serialize_ConsoleSize_ProducesJsonArray()
{
// Given
var consoleSize = new ConsoleSize { Height = 24, Width = 80 };

// When
var jsonString = JsonSerializer.Instance.Serialize(consoleSize);

// Then
Assert.Equal("[24,80]", jsonString);
}

[Fact]
public void Deserialize_JsonArray_ProducesConsoleSize()
{
// Given
var json = "[24,80]";

// When
var consoleSize = JsonSerializer.Instance.Deserialize<ConsoleSize>(Encoding.UTF8.GetBytes(json));

// Then
Assert.NotNull(consoleSize);
Assert.Equal(24UL, consoleSize.Height);
Assert.Equal(80UL, consoleSize.Width);
}

[Fact]
public void SerializeAndDeserialize_RoundTrip_Succeeds()
{
// Given
var original = new ConsoleSize { Height = 50, Width = 200 };

// When
var jsonString = JsonSerializer.Instance.Serialize(original);
var deserialized = JsonSerializer.Instance.Deserialize<ConsoleSize>(Encoding.UTF8.GetBytes(jsonString));

// Then
Assert.NotNull(deserialized);
Assert.Equal(original.Height, deserialized.Height);
Assert.Equal(original.Width, deserialized.Width);
}

[Fact]
public void Serialize_ContainerExecCreateParameters_WithConsoleSize_ProducesArrayFormat()
{
// Given
var parameters = new ContainerExecCreateParameters
{
ConsoleSize = new ConsoleSize { Height = 24, Width = 80 },
AttachStdin = true,
AttachStdout = true,
TTY = true,
Cmd = new List<string> { "/bin/bash" }
};

// When
var jsonString = JsonSerializer.Instance.Serialize(parameters);

// Then - ConsoleSize should be serialized as [24,80], not {"Height":24,"Width":80}
Assert.Contains("\"ConsoleSize\":[24,80]", jsonString);
Assert.DoesNotContain("\"Height\"", jsonString);
Assert.DoesNotContain("\"Width\"", jsonString);
}

[Fact]
public void Serialize_ContainerExecCreateParameters_WithoutConsoleSize_OmitsField()
{
// Given
var parameters = new ContainerExecCreateParameters
{
AttachStdin = true,
AttachStdout = true,
TTY = true,
Cmd = new List<string> { "/bin/bash" }
};

// When
var jsonString = JsonSerializer.Instance.Serialize(parameters);

// Then
Assert.DoesNotContain("ConsoleSize", jsonString);
}

[Fact]
public void Serialize_ContainerExecStartParameters_WithConsoleSize_ProducesArrayFormat()
{
// Given
var parameters = new ContainerExecStartParameters
{
ConsoleSize = new ConsoleSize { Height = 30, Width = 120 }
};

// When
var jsonString = JsonSerializer.Instance.Serialize(parameters);

// Then
Assert.Contains("\"ConsoleSize\":[30,120]", jsonString);
}

[Fact]
public void Deserialize_ContainerExecCreateParameters_WithArrayConsoleSize_Succeeds()
{
// Given - This is the format Docker API would return
var json = "{\"ConsoleSize\":[24,80],\"AttachStdin\":true,\"Tty\":true,\"Cmd\":[\"/bin/bash\"]}";

// When
var parameters = JsonSerializer.Instance.Deserialize<ContainerExecCreateParameters>(Encoding.UTF8.GetBytes(json));

// Then
Assert.NotNull(parameters);
Assert.NotNull(parameters.ConsoleSize);
Assert.Equal(24UL, parameters.ConsoleSize.Height);
Assert.Equal(80UL, parameters.ConsoleSize.Width);
}

[Fact]
public void Serialize_LargeConsoleSize_HandlesCorrectly()
{
// Given - Test with larger values that still fit in ulong
var consoleSize = new ConsoleSize { Height = 1000, Width = 2000 };

// When
var jsonString = JsonSerializer.Instance.Serialize(consoleSize);

// Then
Assert.Equal("[1000,2000]", jsonString);
}

[Fact]
public void Serialize_ZeroConsoleSize_HandlesCorrectly()
{
// Given
var consoleSize = new ConsoleSize { Height = 0, Width = 0 };

// When
var jsonString = JsonSerializer.Instance.Serialize(consoleSize);

// Then
Assert.Equal("[0,0]", jsonString);
}
}