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 @@ - +