diff --git a/Core/Configurations/IAppConfiguration.cs b/Core/Configurations/IAppConfiguration.cs
new file mode 100644
index 0000000..ab5826b
--- /dev/null
+++ b/Core/Configurations/IAppConfiguration.cs
@@ -0,0 +1,9 @@
+namespace Core.Configurations
+{
+ ///
+ /// Marker interface for application configurations that should be automatically registered in the DI container.
+ /// Classes implementing this interface will be loaded from configuration and registered as singletons.
+ ///
+ public interface IAppConfiguration { }
+
+}
diff --git a/Core/ModuleRegistry/SeqBackgroundServiceModule.cs b/Core/ModuleRegistry/SeqBackgroundServiceModule.cs
new file mode 100644
index 0000000..fba38a4
--- /dev/null
+++ b/Core/ModuleRegistry/SeqBackgroundServiceModule.cs
@@ -0,0 +1,20 @@
+using Autofac;
+using Core.Telemetry;
+
+namespace Core.ModuleRegistry
+{
+ public class SeqBackgroundServiceModule : Module
+ {
+ protected override void Load(ContainerBuilder builder)
+ {
+
+ builder.RegisterType()
+ .As()
+ .SingleInstance();
+
+ builder.RegisterType()
+ .As()
+ .SingleInstance();
+ }
+ }
+}
diff --git a/Core/Telemetry/IMessageChannel.cs b/Core/Telemetry/IMessageChannel.cs
new file mode 100644
index 0000000..e048fbc
--- /dev/null
+++ b/Core/Telemetry/IMessageChannel.cs
@@ -0,0 +1,9 @@
+using System.Threading.Channels;
+namespace Core.Telemetry
+{
+ public interface IMessageChannel : IDisposable
+ {
+ ChannelWriter Writer { get; }
+ ChannelReader Reader { get; }
+ }
+}
diff --git a/Core/Telemetry/MessageChannel.cs b/Core/Telemetry/MessageChannel.cs
new file mode 100644
index 0000000..c325d63
--- /dev/null
+++ b/Core/Telemetry/MessageChannel.cs
@@ -0,0 +1,22 @@
+using System.Threading.Channels;
+
+namespace Core.Telemetry
+{
+ public class MessageChannel : IMessageChannel
+ {
+ private readonly Channel _channel;
+
+ public MessageChannel()
+ {
+ _channel = Channel.CreateUnbounded();
+ }
+
+ public ChannelWriter Writer => _channel.Writer;
+ public ChannelReader Reader => _channel.Reader;
+
+ public void Dispose()
+ {
+ _channel.Writer.Complete();
+ }
+ }
+}
diff --git a/Core/Telemetry/SeqBackgroundService.cs b/Core/Telemetry/SeqBackgroundService.cs
index 91ddcc5..312a789 100644
--- a/Core/Telemetry/SeqBackgroundService.cs
+++ b/Core/Telemetry/SeqBackgroundService.cs
@@ -1,55 +1,63 @@
using Microsoft.ApplicationInsights;
using Microsoft.Extensions.Hosting;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Channels;
-using System.Threading.Tasks;
namespace Core.Telemetry
{
public class SeqBackgroundService : BackgroundService
{
- private readonly Channel _channel;
- private readonly HttpClient _client;
- private readonly string _url;
+ private readonly IMessageChannel _messageChannel;
private readonly TelemetryClient _telemetryClient;
+ private readonly HttpClient _httpClient;
- public SeqBackgroundService(TelemetryClient telemetryClient, Configurations.IConfiguration configuration)
+ public SeqBackgroundService(
+ TelemetryClient telemetryClient,
+ IMessageChannel messageChannel,
+ HttpClient httpClient)
{
_telemetryClient = telemetryClient;
- _url = configuration["Seq:Url"]; // eller hvor din Seq URL kommer fra
- _client = new HttpClient();
- _channel = Channel.CreateUnbounded();
- }
-
- public void EnqueueMessage(string message)
- {
- _channel.Writer.TryWrite(message);
+ _messageChannel = messageChannel;
+ _httpClient = httpClient;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
- while (!stoppingToken.IsCancellationRequested)
+ try
{
- try
- {
- await foreach (var message in _channel.Reader.ReadAllAsync(stoppingToken))
+ while (!stoppingToken.IsCancellationRequested)
+ await foreach (var message in _messageChannel.Reader.ReadAllAsync(stoppingToken))
{
- await _client.PostAsync(_url, new StringContent(message), stoppingToken);
+
+ try
+ {
+ //using var response = await _httpClient.SendAsync(message, stoppingToken);
+ //if (!response.IsSuccessStatusCode)
+ //{
+ // _telemetryClient.TrackTrace($"HTTP kald fejlede med status {response.StatusCode}", Microsoft.ApplicationInsights.DataContracts.SeverityLevel.Warning);
+ // continue;
+ //}
+ }
+ catch (Exception ex)
+ {
+ _telemetryClient.TrackException(ex);
+ }
}
- }
- catch (Exception ex)
+ }
+ catch (Exception ex)
+ {
+ if (ex is not OperationCanceledException)
{
_telemetryClient.TrackException(ex);
+ throw;
}
+
+ _telemetryClient.TrackTrace("Service shutdown påbegyndt");
}
}
public override async Task StopAsync(CancellationToken cancellationToken)
{
- _channel.Writer.Complete();
+
+ _messageChannel.Dispose();
await base.StopAsync(cancellationToken);
}
}
diff --git a/Core/Telemetry/Class1.cs b/Core/Telemetry/SeqLogger.cs
similarity index 100%
rename from Core/Telemetry/Class1.cs
rename to Core/Telemetry/SeqLogger.cs
diff --git a/Tests/ConfigurationTests/SmartConfigProviderTests.cs b/Tests/ConfigurationTests/SmartConfigProviderTests.cs
index 3bc95e8..bc24771 100644
--- a/Tests/ConfigurationTests/SmartConfigProviderTests.cs
+++ b/Tests/ConfigurationTests/SmartConfigProviderTests.cs
@@ -63,33 +63,6 @@ namespace Tests.ConfigurationTests
}
- [TestMethod]
- public void GetPostgresSearchPath()
- {
- //ALTER USER sathumper SET search_path TO ptmain, public;
-
- var conn = Container.Resolve();
-
- var result = conn.QuerySql("SHOW search_path;");
- using (var connw = new NpgsqlConnection(conn.ConnectionString + ";Password=3911"))
- {
- connw.Open();
- using (var cmd = new NpgsqlCommand("SHOW search_path; SELECT current_user;", connw))
- {
- using (var reader = cmd.ExecuteReader())
- {
- reader.Read();
- var r1 = $"Search path: {reader.GetString(0)}";
- reader.NextResult();
- reader.Read();
- var r2 = $"Current schema: {reader.GetString(0)}";
- }
- }
-
- }
-
-
- }
[TestMethod]
public void TryGetActiveConfigurations()
{
diff --git a/Tests/MessageChannelIntegrationTests.cs b/Tests/MessageChannelIntegrationTests.cs
new file mode 100644
index 0000000..8a43d43
--- /dev/null
+++ b/Tests/MessageChannelIntegrationTests.cs
@@ -0,0 +1,74 @@
+using Autofac;
+using System.Data;
+using Insight.Database;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Microsoft.Extensions.Logging;
+using Core.Telemetry;
+using Microsoft.ApplicationInsights;
+
+namespace Tests
+{
+ [TestClass]
+ public class MessageChannelIntegrationTests : TestFixture
+ {
+ private IMessageChannel _messageChannel;
+ private SeqBackgroundService _service;
+ private CancellationTokenSource _cts;
+
+ [TestInitialize]
+ public void SetupThis()
+ {
+ _messageChannel = new MessageChannel();
+ var telemetryClient = Container.Resolve();
+ var httpClient = new HttpClient(new TestMessageHandler());
+ _service = new SeqBackgroundService(telemetryClient, _messageChannel, httpClient);
+ _cts = new CancellationTokenSource();
+ }
+
+ [TestMethod]
+ public async Task Messages_ShouldBeProcessedFromQueue()
+ {
+ // Arrange
+ var processedMessages = new List();
+
+ // Start service
+ var serviceTask = _service.StartAsync(_cts.Token);
+
+ // Act
+ // Send nogle beskeder til køen
+ for (int i = 0; i < 5; i++)
+ {
+ var message = new HttpRequestMessage(HttpMethod.Post, $"http://test.com/{i}");
+ await _messageChannel.Writer.WriteAsync(message);
+ }
+
+ // Vent lidt for at sikre processing
+ await Task.Delay(5000);
+
+ // Stop servicen
+ _cts.Cancel();
+ await _service.StopAsync(CancellationToken.None);
+
+ // Assert
+ // Check at køen er tom
+ bool hasMoreMessages = await _messageChannel.Reader.WaitToReadAsync();
+ Assert.IsFalse(hasMoreMessages, "Køen burde være tom");
+ }
+
+ private class TestMessageHandler : HttpMessageHandler
+ {
+ protected override Task SendAsync(
+ HttpRequestMessage request,
+ CancellationToken cancellationToken)
+ {
+ return Task.FromResult(new HttpResponseMessage(System.Net.HttpStatusCode.OK));
+ }
+ }
+
+ [TestCleanup]
+ public void Cleanup()
+ {
+ _cts?.Dispose();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Tests/PasswordHasherTest.cs b/Tests/PasswordHasherTest.cs
new file mode 100644
index 0000000..a635ac2
--- /dev/null
+++ b/Tests/PasswordHasherTest.cs
@@ -0,0 +1,72 @@
+using Autofac;
+using System.Data;
+using Insight.Database;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Microsoft.Extensions.Logging;
+using Core.Telemetry;
+using Core.Entities.Users;
+
+namespace Tests
+{
+ [TestClass]
+ public class PasswordHasherTests : TestFixture
+ {
+ [TestMethod]
+ public void HashPassword_ShouldCreateValidHashFormat()
+ {
+ // Arrange
+ string password = "TestPassword123";
+
+ // Act
+ string hashedPassword = PasswordHasher.HashPassword(password);
+ string[] parts = hashedPassword.Split('.');
+
+ // Assert
+ Assert.AreEqual(3, parts.Length);
+ Assert.AreEqual("100000", parts[0]);
+ }
+
+ [TestMethod]
+ public void VerifyPassword_WithCorrectPassword_ShouldReturnTrue()
+ {
+ // Arrange
+ string password = "TestPassword123";
+ string hashedPassword = PasswordHasher.HashPassword(password);
+
+ // Act
+ bool result = PasswordHasher.VerifyPassword(hashedPassword, password);
+
+ // Assert
+ Assert.IsTrue(result);
+ }
+
+ [TestMethod]
+ public void VerifyPassword_WithWrongPassword_ShouldReturnFalse()
+ {
+ // Arrange
+ string correctPassword = "TestPassword123";
+ string wrongPassword = "WrongPassword123";
+ string hashedPassword = PasswordHasher.HashPassword(correctPassword);
+
+ // Act
+ bool result = PasswordHasher.VerifyPassword(hashedPassword, wrongPassword);
+
+ // Assert
+ Assert.IsFalse(result);
+ }
+
+ [TestMethod]
+ public void VerifyPassword_WithInvalidHashFormat_ShouldReturnFalse()
+ {
+ // Arrange
+ string password = "TestPassword123";
+ string invalidHash = "InvalidHash";
+
+ // Act
+ bool result = PasswordHasher.VerifyPassword(invalidHash, password);
+
+ // Assert
+ Assert.IsFalse(result);
+ }
+ }
+}
\ No newline at end of file