diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md
index dec2120..13ba6d5 100644
--- a/.github/copilot-instructions.md
+++ b/.github/copilot-instructions.md
@@ -1,10 +1,10 @@
- Always use curly braces `{}` for all control structures (if, else, for, foreach, while, etc.), even wrapping single-line statements.
- Insert a single blank line (CRLF) between method and function definitions. Adhere to StyleCop and commonly accepted C# formatting best practices throughout your code.
-- Adhere to SOLID design principles and design patterns where applicable.
+- Adhere to SOLID design principles and design patterns where applicable. Try to keep code elegant, clean, and succinct.
- Create interfaces for public classes, especially any that might be used for dependency injection. Add XML documentation comments to the interfaces with on classes implementing the interface.
- Add XML documentation comments to all public interfaces (and classes, methods, and properties not covered by . Ensure comments are meaningful and up to date.
- Prefix private fields in a class with an underscore (`_`) and use camelCase.
-- Prefer asynchronous methods over synchronous/blocking methods. Avoid blocking calls such as `.Result` or `.Wait()` on tasks.
+- Prefer asynchronous methods over synchronous/blocking methods. Avoid blocking calls such as `.Result`, `.Wait()`, `.GetAwaiter()`, etc. on tasks.
- Always use `.ConfigureAwait(false)` on awaited tasks unless it explicitly requires the synchronization context.
- Write unit tests using the MSTest framework. For mocking dependencies, use MOQ. If mocking requires dependency injection and a suitable mock isn’t available, use Autofac.Extras.Moq’s `AutoMock.GetLoose`.
- Use the `var` keyword for local variable declarations when the type is clear from the right-hand side of the assignment; otherwise, use the explicit type.
@@ -12,4 +12,5 @@
- Strive for small, single-responsibility methods. Refactor larger methods (>50 lines) into smaller pieces when practical.
- If a class contains many private methods, consider refactoring related functionality into separate classes to improve maintainability and cohesion.
- Use `IEnumerable` or arrays for method parameters and return types when the collection is not intended to be modified. Use `List` only when modification is required.
-- Apply consistent exception handling practices across the codebase. Ensure that exceptions are meaningful, logged appropriately, and do not expose sensitive details.
\ No newline at end of file
+- Apply consistent exception handling practices across the codebase. Ensure that exceptions are meaningful, logged appropriately, and do not expose sensitive details.
+- Use Windows CRLF line endings for all code files. Ensure that your development environment is configured to use CRLF line endings to maintain consistency across the codebase.
\ No newline at end of file
diff --git a/ClippyWeb.Tests/ClippyWeb.Tests.csproj b/ClippyWeb.Tests/ClippyWeb.Tests.csproj
index 2b430a0..4d0c975 100644
--- a/ClippyWeb.Tests/ClippyWeb.Tests.csproj
+++ b/ClippyWeb.Tests/ClippyWeb.Tests.csproj
@@ -9,11 +9,15 @@
-
+
+
+
+
+
-
-
-
+
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
diff --git a/ClippyWeb.Tests/SemanticKernelClientTests.cs b/ClippyWeb.Tests/SemanticKernelClientTests.cs
index 966b554..cecb7c0 100644
--- a/ClippyWeb.Tests/SemanticKernelClientTests.cs
+++ b/ClippyWeb.Tests/SemanticKernelClientTests.cs
@@ -1,3 +1,4 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
using SemanticKernelHelper;
namespace ClippyWeb.Tests
@@ -64,31 +65,27 @@ public async Task IfNullMessageThenReturnsDefaultResponse()
}
[TestMethod]
- [ExpectedException(typeof(ArgumentNullException))]
public void IfApiUrlIsNullThenThrowsArgumentNullException()
{
- _ = new SemanticKernelClient(null!, TestModel, TestApiKey);
+ Assert.ThrowsExactly(() => new SemanticKernelClient(null!, TestModel, TestApiKey));
}
[TestMethod]
- [ExpectedException(typeof(ArgumentNullException))]
public void IfModelIsNullThenThrowsArgumentNullException()
{
- _ = new SemanticKernelClient(TestApiUrl, null!, TestApiKey);
+ Assert.ThrowsExactly(() => new SemanticKernelClient(TestApiUrl, null!, TestApiKey));
}
[TestMethod]
- [ExpectedException(typeof(UriFormatException))]
public void IfApiUrlIsInvalidThenThrowsUriFormatException()
{
- _ = new SemanticKernelClient("not-a-valid-url", TestModel, TestApiKey);
+ Assert.ThrowsExactly(() => new SemanticKernelClient("not-a-valid-url", TestModel, TestApiKey));
}
[TestMethod]
- [ExpectedException(typeof(UriFormatException))]
public void IfApiUrlIsNotHttpOrHttpsThenThrowsUriFormatException()
{
- _ = new SemanticKernelClient("ftp://localhost:11434", TestModel, TestApiKey);
+ Assert.ThrowsExactly(() => new SemanticKernelClient("ftp://localhost:11434", TestModel, TestApiKey));
}
}
}
diff --git a/ClippyWeb.Tests/Util/ConnectionValidatorTests.cs b/ClippyWeb.Tests/Util/ConnectionValidatorTests.cs
index d55c38a..7ccab2e 100644
--- a/ClippyWeb.Tests/Util/ConnectionValidatorTests.cs
+++ b/ClippyWeb.Tests/Util/ConnectionValidatorTests.cs
@@ -33,7 +33,7 @@ public void TestInitialize()
_sut = new ConnectionValidator(_mockPingService.Object, _mockTcpClientFactory.Object);
}
- [DataTestMethod]
+ [TestMethod]
[DataRow(null, DisplayName = "Null ServiceUrl")]
[DataRow("", DisplayName = "Empty ServiceUrl")]
public async Task IfServiceUrlIsNullOrEmptyThenLogsWarningAndReturns(string? serviceUrl)
@@ -49,7 +49,7 @@ public async Task IfServiceUrlIsNullOrEmptyThenLogsWarningAndReturns(string? ser
_mockConfiguration.VerifyNoOtherCalls();
}
- [DataTestMethod]
+ [TestMethod]
[DataRow("http://localhost:11434", DisplayName = "Localhost with port")]
[DataRow("http://127.0.0.1:8080", DisplayName = "127.0.0.1 with port")]
[DataRow("http://localhost", DisplayName = "Localhost without port")]
@@ -69,7 +69,7 @@ public async Task ValidateConnectionAsync_LocalhostTest(string serviceUrl)
_mockTcpClient.Verify(t => t.ConnectAsync(It.IsAny(), It.IsAny(), It.IsAny()), Times.AtLeastOnce);
}
- [DataTestMethod]
+ [TestMethod]
[DataRow("http://example.com", DisplayName = "Remote host, no port")]
[DataRow("http://example.com:8080", DisplayName = "Remote host with port")]
[DataRow("http://testhost:5000", DisplayName = "Test host with port")]
@@ -86,7 +86,7 @@ public async Task ValidateConnectionAsync_RemoteTest(string serviceUrl)
_mockPingService.Verify(t => t.PingAsync(It.IsAny(), It.IsAny()), Times.AtLeastOnce);
}
- [DataTestMethod]
+ [TestMethod]
[DataRow("not-a-valid-uri", DisplayName = "Invalid URI format")]
[DataRow(" ", DisplayName = "Whitespace only")]
public async Task IfServiceUrlIsInvalidUriThenThrowsException(string serviceUrl)
@@ -95,7 +95,7 @@ public async Task IfServiceUrlIsInvalidUriThenThrowsException(string serviceUrl)
_mockConfiguration.Setup(x => x["ServiceUrl"]).Returns(serviceUrl);
// Act & Assert
- await Assert.ThrowsExceptionAsync(() => _sut.ValidateConnectionAsync(_mockConfiguration.Object));
+ await Assert.ThrowsAsync(() => _sut.ValidateConnectionAsync(_mockConfiguration.Object));
}
}
}
\ No newline at end of file
diff --git a/ClippyWeb.Tests/Util/TcpClientWrapperTests.cs b/ClippyWeb.Tests/Util/TcpClientWrapperTests.cs
index ea1a9f4..d94c22b 100644
--- a/ClippyWeb.Tests/Util/TcpClientWrapperTests.cs
+++ b/ClippyWeb.Tests/Util/TcpClientWrapperTests.cs
@@ -246,7 +246,7 @@ public async Task ConnectAsync_NullHost_ThrowsArgumentNullException()
var cancellationToken = CancellationToken.None;
// Act & Assert
- await Assert.ThrowsExceptionAsync(async () =>
+ await Assert.ThrowsAsync(async () =>
{
await sut.ConnectAsync(host!, port, cancellationToken);
});
@@ -265,7 +265,7 @@ public async Task ConnectAsync_NegativePort_ThrowsArgumentOutOfRangeException()
var cancellationToken = CancellationToken.None;
// Act & Assert
- await Assert.ThrowsExceptionAsync(async () =>
+ await Assert.ThrowsAsync(async () =>
{
await sut.ConnectAsync(host, port, cancellationToken);
});
@@ -284,7 +284,7 @@ public async Task ConnectAsync_ZeroPort_ThrowsArgumentOutOfRangeException()
var cancellationToken = CancellationToken.None;
// Act & Assert
- await Assert.ThrowsExceptionAsync(async () =>
+ await Assert.ThrowsAsync(async () =>
{
await sut.ConnectAsync(host, port, cancellationToken);
});
@@ -303,7 +303,7 @@ public async Task ConnectAsync_PortAboveMaximum_ThrowsArgumentOutOfRangeExceptio
var cancellationToken = CancellationToken.None;
// Act & Assert
- await Assert.ThrowsExceptionAsync(async () =>
+ await Assert.ThrowsAsync(async () =>
{
await sut.ConnectAsync(host, port, cancellationToken);
});
@@ -322,7 +322,7 @@ public async Task ConnectAsync_PortMaxValue_ThrowsArgumentOutOfRangeException()
var cancellationToken = CancellationToken.None;
// Act & Assert
- await Assert.ThrowsExceptionAsync(async () =>
+ await Assert.ThrowsAsync(async () =>
{
await sut.ConnectAsync(host, port, cancellationToken);
});
@@ -341,7 +341,7 @@ public async Task ConnectAsync_PortMinValue_ThrowsArgumentOutOfRangeException()
var cancellationToken = CancellationToken.None;
// Act & Assert
- await Assert.ThrowsExceptionAsync(async () =>
+ await Assert.ThrowsAsync(async () =>
{
await sut.ConnectAsync(host, port, cancellationToken);
});
@@ -361,7 +361,7 @@ public async Task ConnectAsync_CancelledToken_ThrowsOperationCanceledException()
cts.Cancel();
// Act & Assert
- await Assert.ThrowsExceptionAsync(async () =>
+ await Assert.ThrowsAsync(async () =>
{
await sut.ConnectAsync(host, port, cts.Token);
});
@@ -382,7 +382,7 @@ public async Task ConnectAsync_EmptyHost_ThrowsArgumentException()
// Act & Assert
// Empty host should throw an exception from the underlying TcpClient
- await Assert.ThrowsExceptionAsync(async () =>
+ await Assert.ThrowsAsync(async () =>
{
await sut.ConnectAsync(host, port, cancellationToken);
});
diff --git a/ClippyWeb/ClippyWeb.csproj b/ClippyWeb/ClippyWeb.csproj
index 6aed97c..2644dc1 100644
--- a/ClippyWeb/ClippyWeb.csproj
+++ b/ClippyWeb/ClippyWeb.csproj
@@ -16,7 +16,7 @@
-
+
diff --git a/SemanticKernelHelper/ChatClientFactory.cs b/SemanticKernelHelper/ChatClientFactory.cs
index a85bdc0..865a634 100644
--- a/SemanticKernelHelper/ChatClientFactory.cs
+++ b/SemanticKernelHelper/ChatClientFactory.cs
@@ -38,6 +38,7 @@ public IChatClient GetOrCreateClient(string sessionKey)
SlidingExpiration = TimeSpan.FromMinutes(30),
Priority = CacheItemPriority.Normal
};
+
_cache.Set(cacheKey, newClient, cacheOptions);
return newClient;
}
diff --git a/SemanticKernelHelper/SemanticKernelHelper.csproj b/SemanticKernelHelper/SemanticKernelHelper.csproj
index 38f6014..f4d7699 100644
--- a/SemanticKernelHelper/SemanticKernelHelper.csproj
+++ b/SemanticKernelHelper/SemanticKernelHelper.csproj
@@ -8,8 +8,9 @@
-
-
+
+
+
diff --git a/SharedInterfaces/SharedInterfaces.csproj b/SharedInterfaces/SharedInterfaces.csproj
index 2c9bb71..6eb4c80 100644
--- a/SharedInterfaces/SharedInterfaces.csproj
+++ b/SharedInterfaces/SharedInterfaces.csproj
@@ -5,6 +5,6 @@
enable
-
+
diff --git a/TestConsole/TestConsole.csproj b/TestConsole/TestConsole.csproj
index 03e5670..dba4b39 100644
--- a/TestConsole/TestConsole.csproj
+++ b/TestConsole/TestConsole.csproj
@@ -7,7 +7,7 @@
-
+