From 28b7bf6a897cf3b2e2aad7df767b0f4428bec7a4 Mon Sep 17 00:00:00 2001 From: Stevan Freeborn <65925598+StevanFreeborn@users.noreply.github.com> Date: Thu, 2 Jan 2025 21:45:37 -0600 Subject: [PATCH] feat: add classes to model response to get model list endpoint --- src/AnthropicClient/Models/AnthropicModel.cs | 31 +++++ src/AnthropicClient/Models/Page.cs | 38 ++++++ .../Unit/Models/AnthropicModelTests.cs | 57 +++++++++ .../Unit/Models/PageTests.cs | 111 ++++++++++++++++++ 4 files changed, 237 insertions(+) create mode 100644 src/AnthropicClient/Models/AnthropicModel.cs create mode 100644 src/AnthropicClient/Models/Page.cs create mode 100644 tests/AnthropicClient.Tests/Unit/Models/AnthropicModelTests.cs create mode 100644 tests/AnthropicClient.Tests/Unit/Models/PageTests.cs diff --git a/src/AnthropicClient/Models/AnthropicModel.cs b/src/AnthropicClient/Models/AnthropicModel.cs new file mode 100644 index 0000000..50be8c1 --- /dev/null +++ b/src/AnthropicClient/Models/AnthropicModel.cs @@ -0,0 +1,31 @@ +using System.Text.Json.Serialization; + +namespace AnthropicClient.Models; + +/// +/// Represents an Anthropic model. +/// +public class AnthropicModel +{ + /// + /// The type of the model. + /// + public string Type { get; init; } = string.Empty; + + /// + /// The id of the model. + /// + public string Id { get; init; } = string.Empty; + + /// + /// The display name of the model. + /// + [JsonPropertyName("display_name")] + public string DisplayName { get; init; } = string.Empty; + + /// + /// The created date of the model. + /// + [JsonPropertyName("created_at")] + public DateTimeOffset CreatedAt { get; init; } +} diff --git a/src/AnthropicClient/Models/Page.cs b/src/AnthropicClient/Models/Page.cs new file mode 100644 index 0000000..e0ec5a8 --- /dev/null +++ b/src/AnthropicClient/Models/Page.cs @@ -0,0 +1,38 @@ +using System.Text.Json.Serialization; + +namespace AnthropicClient.Models; + +/// +/// Represents a page. +/// +public class Page +{ + /// + /// The id of the first item in the page. + /// + [JsonPropertyName("first_id")] + public string FirstId { get; init; } = string.Empty; + + /// + /// The id of the last item in the page. + /// + [JsonPropertyName("last_id")] + public string LastId { get; init; } = string.Empty; + + /// + /// Indicates whether there is more data to be retrieved. + /// + [JsonPropertyName("has_more")] + public bool HasMore { get; init; } +} + +/// +/// Represents a page with data. +/// +public class Page : Page +{ + /// + /// The data in the page. + /// + public T[] Data { get; init; } = []; +} \ No newline at end of file diff --git a/tests/AnthropicClient.Tests/Unit/Models/AnthropicModelTests.cs b/tests/AnthropicClient.Tests/Unit/Models/AnthropicModelTests.cs new file mode 100644 index 0000000..b7d1662 --- /dev/null +++ b/tests/AnthropicClient.Tests/Unit/Models/AnthropicModelTests.cs @@ -0,0 +1,57 @@ +namespace AnthropicClient.Tests.Unit.Models; + +public class AnthropicModelTests : SerializationTest +{ + private const string SampleJson = @"{ + ""type"": ""model"", + ""id"": ""claude-3-opus-20240229"", + ""display_name"": ""Claude 3 Opus"", + ""created_at"": ""2024-02-29T00:00:00Z"" + }"; + + [Fact] + public void Constructor_WhenCalled_ItShouldReturnAnInstanceWithPropertiesSet() + { + var model = new AnthropicModel(); + + model.Type.Should().BeEmpty(); + model.Id.Should().BeEmpty(); + model.DisplayName.Should().BeEmpty(); + model.CreatedAt.Should().Be(default); + } + + [Fact] + public void JsonDeserialization_WhenDeserialized_ItShouldHaveExpectedValues() + { + var result = Deserialize(SampleJson); + + result.Should().NotBeNull(); + result!.Type.Should().Be("model"); + result.Id.Should().Be("claude-3-opus-20240229"); + result.DisplayName.Should().Be("Claude 3 Opus"); + result.CreatedAt.Should().Be(new DateTimeOffset(2024, 2, 29, 0, 0, 0, TimeSpan.Zero)); + } + + [Fact] + public void JsonSerialization_WhenSerialized_ItShouldReturnExpectedShape() + { + var model = new AnthropicModel + { + Type = "model", + Id = "claude-3-opus-20240229", + DisplayName = "Claude 3 Opus", + CreatedAt = new DateTimeOffset(2024, 2, 29, 0, 0, 0, TimeSpan.Zero) + }; + + var result = Serialize(model); + + var expectedJson = @"{ + ""type"": ""model"", + ""id"": ""claude-3-opus-20240229"", + ""display_name"": ""Claude 3 Opus"", + ""created_at"": ""2024-02-29T00:00:00+00:00"" + }"; + + JsonAssert.Equal(expectedJson, result); + } +} \ No newline at end of file diff --git a/tests/AnthropicClient.Tests/Unit/Models/PageTests.cs b/tests/AnthropicClient.Tests/Unit/Models/PageTests.cs new file mode 100644 index 0000000..61a2780 --- /dev/null +++ b/tests/AnthropicClient.Tests/Unit/Models/PageTests.cs @@ -0,0 +1,111 @@ +namespace AnthropicClient.Tests.Unit.Models; + +public class PageTests : SerializationTest +{ + private const string BasePageJson = @"{ + ""first_id"": ""msg_123"", + ""last_id"": ""msg_456"", + ""has_more"": true + }"; + + private const string GenericPageJson = @"{ + ""first_id"": ""msg_123"", + ""last_id"": ""msg_456"", + ""has_more"": true, + ""data"": [""item1"", ""item2""] + }"; + + private const string EmptyPageJson = @"{ + ""first_id"": """", + ""last_id"": """", + ""has_more"": false, + ""data"": [] + }"; + + [Fact] + public void Constructor_WhenCalled_ItShouldReturnAnInstanceWithPropertiesSet() + { + var page = new Page(); + + page.FirstId.Should().BeEmpty(); + page.LastId.Should().BeEmpty(); + page.HasMore.Should().BeFalse(); + } + + [Fact] + public void Constructor_WhenCalledWithGeneric_ItShouldReturnAnInstanceWithPropertiesSet() + { + var page = new Page(); + + page.FirstId.Should().BeEmpty(); + page.LastId.Should().BeEmpty(); + page.HasMore.Should().BeFalse(); + page.Data.Should().BeEmpty(); + } + + [Fact] + public void JsonDeserialization_WhenCalled_ItShouldHaveCorrectValues() + { + var result = Deserialize(BasePageJson); + + result.Should().NotBeNull(); + result!.FirstId.Should().Be("msg_123"); + result.LastId.Should().Be("msg_456"); + result.HasMore.Should().BeTrue(); + } + + [Fact] + public void JsonSerialization_WhenCalled_ItShouldHaveExpectedShape() + { + var page = new Page + { + FirstId = "msg_123", + LastId = "msg_456", + HasMore = true + }; + + var result = Serialize(page); + + JsonAssert.Equal(BasePageJson, result); + } + + [Fact] + public void JsonDeserialization_WhenCalledWithData_ItShouldHaveCorrectValues() + { + var result = Deserialize>(GenericPageJson); + + result.Should().NotBeNull(); + result!.FirstId.Should().Be("msg_123"); + result.LastId.Should().Be("msg_456"); + result.HasMore.Should().BeTrue(); + result.Data.Should().BeEquivalentTo(["item1", "item2"]); + } + + [Fact] + public void JsonSerialization_WhenCalledWithData_ItShouldHaveExpectedShape() + { + var page = new Page + { + FirstId = "msg_123", + LastId = "msg_456", + HasMore = true, + Data = ["item1", "item2"] + }; + + var result = Serialize(page); + + JsonAssert.Equal(GenericPageJson, result); + } + + [Fact] + public void JsonDeserialization_WhenCalledWithEmptyData_ItShouldHaveCorrectValues() + { + var result = Deserialize>(EmptyPageJson); + + result.Should().NotBeNull(); + result!.FirstId.Should().BeEmpty(); + result.LastId.Should().BeEmpty(); + result.HasMore.Should().BeFalse(); + result.Data.Should().BeEmpty(); + } +} \ No newline at end of file