Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Backend API] Implement ITableEntity to events and resources #329

Merged
merged 2 commits into from
Sep 20, 2024
Merged
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
21 changes: 20 additions & 1 deletion src/AzureOpenAIProxy.ApiApp/Models/AdminResourceDetails.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
using System.Runtime.Serialization;
using System.Text.Json.Serialization;

using Azure;
using Azure.Data.Tables;

using AzureOpenAIProxy.ApiApp.Converters;

namespace AzureOpenAIProxy.ApiApp.Models;

/// <summary>
/// This represent the entity for the resource details for admin.
/// </summary>
public class AdminResourceDetails
public class AdminResourceDetails : ITableEntity
{
/// <summary>
/// Gets or sets the event id.
Expand Down Expand Up @@ -57,6 +60,22 @@ public class AdminResourceDetails
/// </summary>
[JsonRequired]
public bool IsActive { get; set; }

/// <inheritdoc />
[JsonIgnore]
public string PartitionKey { get; set; } = string.Empty;

/// <inheritdoc />
[JsonIgnore]
public string RowKey { get; set; } = string.Empty;

/// <inheritdoc />
[JsonIgnore]
public DateTimeOffset? Timestamp { get; set; }

/// <inheritdoc />
[JsonIgnore]
public ETag ETag { get; set; }
}

/// <summary>
Expand Down
21 changes: 20 additions & 1 deletion src/AzureOpenAIProxy.ApiApp/Models/EventDetails.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
using System.Text.Json.Serialization;

using Azure;
using Azure.Data.Tables;

/// <summary>
/// This represent the entity for the event details for users.
/// </summary>
public class EventDetails
public class EventDetails : ITableEntity
{
/// <summary>
/// Gets or sets the event id.
Expand Down Expand Up @@ -34,4 +37,20 @@ public class EventDetails
/// </summary>
[JsonRequired]
public int DailyRequestCap { get; set; }

/// <inheritdoc />
[JsonIgnore]
public string PartitionKey { get; set; } = string.Empty;

/// <inheritdoc />
[JsonIgnore]
public string RowKey { get; set; } = string.Empty;

/// <inheritdoc />
[JsonIgnore]
public DateTimeOffset? Timestamp { get; set; }

/// <inheritdoc />
[JsonIgnore]
public ETag ETag { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using System.Text.Json;

using AzureOpenAIProxy.ApiApp.Models;

using FluentAssertions;

namespace AzureOpenAIProxy.ApiApp.Tests.Models;

public class AdminEventDetailsTests
{
private static readonly AdminEventDetails examplePayload = new()
{
EventId = Guid.Parse("67f410a3-c5e4-4326-a3ad-5812b9adfc06"),
Title = "Test Title",
Summary = "Test Summary",
Description = "Test Description",
DateStart = DateTimeOffset.Parse("2024-12-01T12:34:56+00:00"),
DateEnd = DateTimeOffset.Parse("2024-12-02T12:34:56+00:00"),
TimeZone = "Asia/Seoul",
IsActive = true,
OrganizerName = "Test Organizer",
OrganizerEmail = "[email protected]",
CoorganizerName = "Test Coorganizer",
CoorganizerEmail = "[email protected]",
MaxTokenCap = 1000,
DailyRequestCap = 4000,
};

private static readonly string exampleJson = """
{
"eventId": "67f410a3-c5e4-4326-a3ad-5812b9adfc06",
"title": "Test Title",
"summary": "Test Summary",
"description": "Test Description",
"dateStart": "2024-12-01T12:34:56+00:00",
"dateEnd": "2024-12-02T12:34:56+00:00",
"timeZone": "Asia/Seoul",
"isActive": true,
"organizerName": "Test Organizer",
"organizerEmail": "[email protected]",
"coorganizerName": "Test Coorganizer",
"coorganizerEmail": "[email protected]",
"maxTokenCap": 1000,
"dailyRequestCap": 4000
}
""";

private static readonly JsonSerializerOptions options = new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true
};

[Fact]
public void Given_ExamplePayload_When_Serialized_Then_It_Should_Match_Json()
{
// Act
var serialised = JsonSerializer.Serialize(examplePayload, options);

// Assert
serialised.Should().ContainAll(
"\"eventId\":", "\"67f410a3-c5e4-4326-a3ad-5812b9adfc06\"",
"\"title\":", "\"Test Title\"",
"\"summary\":", "\"Test Summary\"",
"\"description\":", "\"Test Description\"",
"\"dateStart\":", "\"2024-12-01T12:34:56+00:00\"",
"\"dateEnd\":", "\"2024-12-02T12:34:56+00:00\"",
"\"timeZone\":", "\"Asia/Seoul\"",
"\"isActive\":", "true",
"\"organizerName\":", "\"Test Organizer\"",
"\"organizerEmail\":", "\"[email protected]\"",
"\"coorganizerName\":", "\"Test Coorganizer\"",
"\"coorganizerEmail\":", "\"[email protected]\"",
"\"maxTokenCap\":", "1000",
"\"dailyRequestCap\":", "4000");
}

[Fact]
public void Given_ExampleJson_When_Deserialized_Then_It_Should_Match_Object()
{
// Arrange & Act
var deserialised = JsonSerializer.Deserialize<AdminEventDetails>(exampleJson, options);

// Assert
deserialised.Should().BeEquivalentTo(examplePayload);
}
}
58 changes: 58 additions & 0 deletions test/AzureOpenAIProxy.ApiApp.Tests/Models/EventDetailsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System.Text.Json;

using FluentAssertions;

namespace AzureOpenAIProxy.ApiApp.Tests.Models;

public class EventDetailsTests
{
private static readonly EventDetails examplePayload = new()
{
EventId = Guid.Parse("67f410a3-c5e4-4326-a3ad-5812b9adfc06"),
Title = "Test Title",
Summary = "Test Summary",
MaxTokenCap = 1000,
DailyRequestCap = 4000,
};

private static readonly string exampleJson = """
{
"eventId": "67f410a3-c5e4-4326-a3ad-5812b9adfc06",
"title": "Test Title",
"summary": "Test Summary",
"maxTokenCap": 1000,
"dailyRequestCap": 4000
}
""";

private static readonly JsonSerializerOptions options = new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true
};

[Fact]
public void Given_ExamplePayload_When_Serialized_Then_It_Should_Match_Json()
{
// Act
var serialised = JsonSerializer.Serialize(examplePayload, options);

// Assert
serialised.Should().ContainAll(
"\"eventId\":", "\"67f410a3-c5e4-4326-a3ad-5812b9adfc06\"",
"\"title\":", "\"Test Title\"",
"\"summary\":", "\"Test Summary\"",
"\"maxTokenCap\":", "1000",
"\"dailyRequestCap\":", "4000");
}

[Fact]
public void Given_ExampleJson_When_Deserialized_Then_It_Should_Match_Object()
{
// Arrange & Act
var deserialised = JsonSerializer.Deserialize<EventDetails>(exampleJson, options);

// Assert
deserialised.Should().BeEquivalentTo(examplePayload);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

using FluentAssertions;

using IdentityModel.Client;

namespace AzureOpenAIProxy.AppHost.Tests.ApiApp.Endpoints;

public class AdminCreateEventsOpenApiTests(AspireAppHostFixture host) : IClassFixture<AspireAppHostFixture>
Expand Down Expand Up @@ -165,4 +167,106 @@ public async Task Given_Resource_When_Invoked_Endpoint_Then_It_Should_Return_Mod
.TryGetProperty("AdminEventDetails", out var property) ? property : default;
result.ValueKind.Should().Be(JsonValueKind.Object);
}

[Theory]
[InlineData("eventId", true)]
[InlineData("title", true)]
[InlineData("summary", true)]
[InlineData("description", false)]
[InlineData("dateStart", true)]
[InlineData("dateEnd", true)]
[InlineData("timeZone", true)]
[InlineData("isActive", true)]
[InlineData("organizerName", true)]
[InlineData("organizerEmail", true)]
[InlineData("coorganizerName", false)]
[InlineData("coorganizerEmail", false)]
[InlineData("maxTokenCap", true)]
[InlineData("dailyRequestCap", true)]
public async Task Given_Resource_When_Invoked_Endpoint_Then_It_Should_Return_Required(string attribute, bool isRequired)
{
// Arrange
using var httpClient = host.App!.CreateHttpClient("apiapp");
await host.ResourceNotificationService.WaitForResourceAsync("apiapp", KnownResourceStates.Running).WaitAsync(TimeSpan.FromSeconds(30));

// Act
var json = await httpClient.GetStringAsync("/swagger/v1.0.0/swagger.json");
var openapi = JsonSerializer.Deserialize<JsonDocument>(json);

// Assert
var result = openapi!.RootElement.GetProperty("components")
.GetProperty("schemas")
.GetProperty("AdminEventDetails")
.TryGetStringArray("required")
.ToList();
result.Contains(attribute).Should().Be(isRequired);
}

[Theory]
[InlineData("eventId")]
[InlineData("title")]
[InlineData("summary")]
[InlineData("description")]
[InlineData("dateStart")]
[InlineData("dateEnd")]
[InlineData("timeZone")]
[InlineData("isActive")]
[InlineData("organizerName")]
[InlineData("organizerEmail")]
[InlineData("coorganizerName")]
[InlineData("coorganizerEmail")]
[InlineData("maxTokenCap")]
[InlineData("dailyRequestCap")]
public async Task Given_Resource_When_Invoked_Endpoint_Then_It_Should_Return_Property(string attribute)
{
// Arrange
using var httpClient = host.App!.CreateHttpClient("apiapp");
await host.ResourceNotificationService.WaitForResourceAsync("apiapp", KnownResourceStates.Running).WaitAsync(TimeSpan.FromSeconds(30));

// Act
var json = await httpClient.GetStringAsync("/swagger/v1.0.0/swagger.json");
var openapi = JsonSerializer.Deserialize<JsonDocument>(json);

// Assert
var result = openapi!.RootElement.GetProperty("components")
.GetProperty("schemas")
.GetProperty("AdminEventDetails")
.GetProperty("properties")
.TryGetProperty(attribute, out var property) ? property : default;
result.ValueKind.Should().Be(JsonValueKind.Object);
}

[Theory]
[InlineData("eventId", "string")]
[InlineData("title", "string")]
[InlineData("summary", "string")]
[InlineData("description", "string")]
[InlineData("dateStart", "string")]
[InlineData("dateEnd", "string")]
[InlineData("timeZone", "string")]
[InlineData("isActive", "boolean")]
[InlineData("organizerName", "string")]
[InlineData("organizerEmail", "string")]
[InlineData("coorganizerName", "string")]
[InlineData("coorganizerEmail", "string")]
[InlineData("maxTokenCap", "integer")]
[InlineData("dailyRequestCap", "integer")]
public async Task Given_Resource_When_Invoked_Endpoint_Then_It_Should_Return_Type(string attribute, string type)
{
// Arrange
using var httpClient = host.App!.CreateHttpClient("apiapp");
await host.ResourceNotificationService.WaitForResourceAsync("apiapp", KnownResourceStates.Running).WaitAsync(TimeSpan.FromSeconds(30));

// Act
var json = await httpClient.GetStringAsync("/swagger/v1.0.0/swagger.json");
var openapi = JsonSerializer.Deserialize<JsonDocument>(json);

// Assert
var result = openapi!.RootElement.GetProperty("components")
.GetProperty("schemas")
.GetProperty("AdminEventDetails")
.GetProperty("properties")
.GetProperty(attribute);
result.TryGetString("type").Should().Be(type);
}
}
Loading
Loading