diff --git a/.github/workflows/azure-dev-build-only.yml b/.github/workflows/azure-dev-build-only.yml
index 15926842..58979745 100644
--- a/.github/workflows/azure-dev-build-only.yml
+++ b/.github/workflows/azure-dev-build-only.yml
@@ -25,8 +25,8 @@ jobs:
- name: Install local certs
shell: pwsh
run: |
- dotnet dev-certs https --clean
- dotnet dev-certs https --trust
+ dotnet tool update -g linux-dev-certs
+ dotnet linux-dev-certs install
- name: Install Aspire workload
shell: pwsh
diff --git a/.github/workflows/azure-dev.yml b/.github/workflows/azure-dev.yml
index 1bbdc574..0f6924aa 100644
--- a/.github/workflows/azure-dev.yml
+++ b/.github/workflows/azure-dev.yml
@@ -34,8 +34,8 @@ jobs:
- name: Install local certs
shell: pwsh
run: |
- dotnet dev-certs https --clean
- dotnet dev-certs https --trust
+ dotnet tool update -g linux-dev-certs
+ dotnet linux-dev-certs install
- name: Install Aspire workload
shell: pwsh
diff --git a/AzureOpenAIProxy.sln b/AzureOpenAIProxy.sln
index ea1ee86f..bd88d76b 100644
--- a/AzureOpenAIProxy.sln
+++ b/AzureOpenAIProxy.sln
@@ -17,6 +17,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{A69C5782-F
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AzureOpenAIProxy.AppHost.Tests", "test\AzureOpenAIProxy.AppHost.Tests\AzureOpenAIProxy.AppHost.Tests.csproj", "{E8994388-24FA-4221-8E46-4CD1DA96C585}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AzureOpenAIProxy.PlaygroundApp.Tests", "test\AzureOpenAIProxy.PlaygroundApp.Tests\AzureOpenAIProxy.PlaygroundApp.Tests.csproj", "{0AE9860A-0F8D-49EF-B375-BF95374A1EDA}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AzureOpenAIProxy.ApiApp.Tests", "test\AzureOpenAIProxy.ApiApp.Tests\AzureOpenAIProxy.ApiApp.Tests.csproj", "{0E772300-9263-49CA-8E26-D0B9CC584202}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -43,6 +47,14 @@ Global
{E8994388-24FA-4221-8E46-4CD1DA96C585}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E8994388-24FA-4221-8E46-4CD1DA96C585}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E8994388-24FA-4221-8E46-4CD1DA96C585}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0AE9860A-0F8D-49EF-B375-BF95374A1EDA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0AE9860A-0F8D-49EF-B375-BF95374A1EDA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0AE9860A-0F8D-49EF-B375-BF95374A1EDA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0AE9860A-0F8D-49EF-B375-BF95374A1EDA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0E772300-9263-49CA-8E26-D0B9CC584202}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0E772300-9263-49CA-8E26-D0B9CC584202}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0E772300-9263-49CA-8E26-D0B9CC584202}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0E772300-9263-49CA-8E26-D0B9CC584202}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -53,6 +65,8 @@ Global
{A8BBA7EA-5615-470C-8D2E-77CCCAE3F51E} = {004CE987-A209-4933-884A-2F59506CDC9E}
{1E3B6609-56EC-4FEC-A689-34F9E5DCCF22} = {004CE987-A209-4933-884A-2F59506CDC9E}
{E8994388-24FA-4221-8E46-4CD1DA96C585} = {A69C5782-F0B3-46B9-8089-3E7216AAAB25}
+ {0AE9860A-0F8D-49EF-B375-BF95374A1EDA} = {A69C5782-F0B3-46B9-8089-3E7216AAAB25}
+ {0E772300-9263-49CA-8E26-D0B9CC584202} = {A69C5782-F0B3-46B9-8089-3E7216AAAB25}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1D2D444D-0384-4C78-B968-156C7C848DE8}
diff --git a/Directory.Build.props b/Directory.Build.props
index 664fe504..7236a166 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -17,7 +17,10 @@
6.*
6.*
+ 6.*
17.*
+ 5.*
+ 8.*
2.*
diff --git a/src/AzureOpenAIProxy.ApiApp/Builders/OpenAIServiceRequestBuilder.cs b/src/AzureOpenAIProxy.ApiApp/Builders/OpenAIServiceRequestBuilder.cs
index b46b451e..50283df2 100644
--- a/src/AzureOpenAIProxy.ApiApp/Builders/OpenAIServiceRequestBuilder.cs
+++ b/src/AzureOpenAIProxy.ApiApp/Builders/OpenAIServiceRequestBuilder.cs
@@ -64,10 +64,10 @@ public IOpenAIServiceRequestBuilder SetOpenAIInstance(OpenAISettings openaiSetti
var instances = (openaiSettings ?? throw new ArgumentNullException(nameof(openaiSettings)))
.Instances
.Where(p => p.DeploymentNames!.Contains(deploymentName) == true);
- var openai = instances.Skip(openaiSettings.Random.Next(instances.Count())).First();
+ var openai = instances.Skip(openaiSettings.Random.Next(instances.Count())).FirstOrDefault();
- this._endpoint = openai.Endpoint;
- this._apiKey = openai.ApiKey;
+ this._endpoint = openai?.Endpoint;
+ this._apiKey = openai?.ApiKey;
return this;
}
diff --git a/test/AzureOpenAIProxy.ApiApp.Tests/AzureOpenAIProxy.ApiApp.Tests.csproj b/test/AzureOpenAIProxy.ApiApp.Tests/AzureOpenAIProxy.ApiApp.Tests.csproj
new file mode 100644
index 00000000..e5441763
--- /dev/null
+++ b/test/AzureOpenAIProxy.ApiApp.Tests/AzureOpenAIProxy.ApiApp.Tests.csproj
@@ -0,0 +1,34 @@
+
+
+
+ false
+ true
+
+ AzureOpenAIProxy.ApiApp.Tests
+ AzureOpenAIProxy.ApiApp.Tests
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/AzureOpenAIProxy.ApiApp.Tests/Builders/OpenAIServiceRequestBuilderTests.cs b/test/AzureOpenAIProxy.ApiApp.Tests/Builders/OpenAIServiceRequestBuilderTests.cs
new file mode 100644
index 00000000..fa737b5c
--- /dev/null
+++ b/test/AzureOpenAIProxy.ApiApp.Tests/Builders/OpenAIServiceRequestBuilderTests.cs
@@ -0,0 +1,67 @@
+using System.Reflection;
+
+using AzureOpenAIProxy.ApiApp.Builders;
+using AzureOpenAIProxy.ApiApp.Configurations;
+
+using FluentAssertions;
+
+namespace AzureOpenAIProxy.ApiApp.Tests.Builders;
+
+public class OpenAIServiceRequestBuilderTests
+{
+ [Theory]
+ [InlineData("https://localhost", "my-api-key", "deployment-name-1")]
+ public void Given_OpenAISettings_When_Invoked_SetOpenAIInstance_Then_It_Should_Store_Value(string endpoint, string apiKey, string deploymentName)
+ {
+ // Arrange
+ var instance = new OpenAIInstanceSettings
+ {
+ Endpoint = endpoint,
+ ApiKey = apiKey,
+ DeploymentNames = new List() { deploymentName },
+ };
+ var settings = new OpenAISettings()
+ {
+ Instances = { instance }
+ };
+
+ // Act
+ var builder = new OpenAIServiceRequestBuilder();
+ builder.SetOpenAIInstance(settings, deploymentName);
+
+ // Assert
+ var _endpoint = builder.GetType().GetField("_endpoint", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(builder) as string;
+ var _apiKey = builder.GetType().GetField("_apiKey", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(builder) as string;
+
+ _endpoint.Should().Be(endpoint);
+ _apiKey.Should().Be(apiKey);
+ }
+
+ [Theory]
+ [InlineData("https://localhost", "my-api-key", "deployment-name-1", "deployment-name-2")]
+ public void Given_OpenAISettings_When_Invoked_SetOpenAIInstance_Then_It_Should_Return_Null(string endpoint, string apiKey, string deploymentName, string nonDeploymentName)
+ {
+ // Arrange
+ var instance = new OpenAIInstanceSettings
+ {
+ Endpoint = endpoint,
+ ApiKey = apiKey,
+ DeploymentNames = new List() { deploymentName },
+ };
+ var settings = new OpenAISettings()
+ {
+ Instances = { instance }
+ };
+
+ // Act
+ var builder = new OpenAIServiceRequestBuilder();
+ builder.SetOpenAIInstance(settings, nonDeploymentName);
+
+ // Assert
+ var _endpoint = builder.GetType().GetField("_endpoint", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(builder) as string;
+ var _apiKey = builder.GetType().GetField("_apiKey", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(builder) as string;
+
+ _endpoint.Should().BeNull();
+ _apiKey.Should().BeNull();
+ }
+}
\ No newline at end of file
diff --git a/test/AzureOpenAIProxy.ApiApp.Tests/UnitTest1.cs b/test/AzureOpenAIProxy.ApiApp.Tests/UnitTest1.cs
new file mode 100644
index 00000000..9dec26e7
--- /dev/null
+++ b/test/AzureOpenAIProxy.ApiApp.Tests/UnitTest1.cs
@@ -0,0 +1,9 @@
+namespace AzureOpenAIProxy.ApiApp.Tests;
+
+public class UnitTest1
+{
+ [Fact]
+ public void Test1()
+ {
+ }
+}
\ No newline at end of file
diff --git a/test/AzureOpenAIProxy.AppHost.Tests/AppHostProgramTests.cs b/test/AzureOpenAIProxy.AppHost.Tests/AppHostProgramTests.cs
new file mode 100644
index 00000000..880bf4d7
--- /dev/null
+++ b/test/AzureOpenAIProxy.AppHost.Tests/AppHostProgramTests.cs
@@ -0,0 +1,26 @@
+using System.Net;
+
+using FluentAssertions;
+
+namespace AzureOpenAIProxy.Tests;
+
+public class AppHostProgramTests
+{
+ [Theory]
+ [InlineData("apiapp", "/health", HttpStatusCode.OK)]
+ [InlineData("playgroundapp", "/health", HttpStatusCode.OK)]
+ public async Task Given_Resource_When_Invoked_Endpoint_Then_It_Should_Return_Healthy(string resourceName, string endpoint, HttpStatusCode statusCode)
+ {
+ // Arrange
+ var appHost = await DistributedApplicationTestingBuilder.CreateAsync();
+ await using var app = await appHost.BuildAsync();
+ await app.StartAsync();
+
+ // Act
+ var httpClient = app.CreateHttpClient(resourceName);
+ var response = await httpClient.GetAsync(endpoint);
+
+ // Assert
+ response.StatusCode.Should().Be(statusCode);
+ }
+}
diff --git a/test/AzureOpenAIProxy.AppHost.Tests/AzureOpenAIProxy.AppHost.Tests.csproj b/test/AzureOpenAIProxy.AppHost.Tests/AzureOpenAIProxy.AppHost.Tests.csproj
index 2002218a..f7cc56ea 100644
--- a/test/AzureOpenAIProxy.AppHost.Tests/AzureOpenAIProxy.AppHost.Tests.csproj
+++ b/test/AzureOpenAIProxy.AppHost.Tests/AzureOpenAIProxy.AppHost.Tests.csproj
@@ -14,7 +14,9 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
all
diff --git a/test/AzureOpenAIProxy.AppHost.Tests/WebTests.cs b/test/AzureOpenAIProxy.AppHost.Tests/WebTests.cs
deleted file mode 100644
index 6c83ad0d..00000000
--- a/test/AzureOpenAIProxy.AppHost.Tests/WebTests.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-// using System.Net;
-
-// namespace AzureOpenAIProxy.Tests;
-
-// public class WebTests
-// {
-// [Fact]
-// public async Task GetWebResourceRootReturnsOkStatusCode()
-// {
-// // Arrange
-// var appHost = await DistributedApplicationTestingBuilder.CreateAsync();
-// await using var app = await appHost.BuildAsync();
-// await app.StartAsync();
-
-// // Act
-// var httpClient = app.CreateHttpClient("playgroundapp");
-// var response = await httpClient.GetAsync("/");
-
-// // Assert
-// Assert.Equal(HttpStatusCode.OK, response.StatusCode);
-// }
-// }
diff --git a/test/AzureOpenAIProxy.PlaygroundApp.Tests/AzureOpenAIProxy.PlaygroundApp.Tests.csproj b/test/AzureOpenAIProxy.PlaygroundApp.Tests/AzureOpenAIProxy.PlaygroundApp.Tests.csproj
new file mode 100644
index 00000000..1a9f53fe
--- /dev/null
+++ b/test/AzureOpenAIProxy.PlaygroundApp.Tests/AzureOpenAIProxy.PlaygroundApp.Tests.csproj
@@ -0,0 +1,33 @@
+
+
+
+ false
+ true
+
+ AzureOpenAIProxy.PlaygroundApp.Tests
+ AzureOpenAIProxy.PlaygroundApp.Tests
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/AzureOpenAIProxy.PlaygroundApp.Tests/UnitTest1.cs b/test/AzureOpenAIProxy.PlaygroundApp.Tests/UnitTest1.cs
new file mode 100644
index 00000000..e41ada39
--- /dev/null
+++ b/test/AzureOpenAIProxy.PlaygroundApp.Tests/UnitTest1.cs
@@ -0,0 +1,9 @@
+namespace AzureOpenAIProxy.PlaygroundApp.Tests;
+
+public class UnitTest1
+{
+ [Fact]
+ public void Test1()
+ {
+ }
+}
\ No newline at end of file