From 4ccfd6d055a79859e06a747be7ce52f678e6ae97 Mon Sep 17 00:00:00 2001 From: cesnimda Date: Sun, 22 Mar 2026 14:01:55 +0100 Subject: [PATCH] perf: use stable sha256 cache keys for summarizer requests --- .../AttachmentsControllerTests.cs | 9 +++++++++ JobTrackerApi.Tests/OwnershipGuardTests.cs | 16 ++++++++++++++++ JobTrackerApi/Services/SummarizerService.cs | 12 +++++++++++- 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/JobTrackerApi.Tests/AttachmentsControllerTests.cs b/JobTrackerApi.Tests/AttachmentsControllerTests.cs index ed6680c..7d54420 100644 --- a/JobTrackerApi.Tests/AttachmentsControllerTests.cs +++ b/JobTrackerApi.Tests/AttachmentsControllerTests.cs @@ -1,5 +1,6 @@ using System.Reflection; using JobTrackerApi.Controllers; +using JobTrackerApi.Services; using Xunit; namespace JobTrackerApi.Tests; @@ -20,4 +21,12 @@ public sealed class AttachmentsControllerTests Assert.Contains(".png", values); Assert.DoesNotContain(".exe", values); } + + [Fact] + public void Max_file_size_limit_is_10_mb() + { + var field = typeof(AttachmentsController).GetField("MaxFileSizeBytes", BindingFlags.NonPublic | BindingFlags.Static); + Assert.NotNull(field); + Assert.Equal(10 * 1024 * 1024, (long)field!.GetValue(null)!); + } } diff --git a/JobTrackerApi.Tests/OwnershipGuardTests.cs b/JobTrackerApi.Tests/OwnershipGuardTests.cs index 5f2262d..f7bb8ba 100644 --- a/JobTrackerApi.Tests/OwnershipGuardTests.cs +++ b/JobTrackerApi.Tests/OwnershipGuardTests.cs @@ -1,5 +1,6 @@ using System.Reflection; using JobTrackerApi.Controllers; +using JobTrackerApi.Services; using Xunit; namespace JobTrackerApi.Tests; @@ -19,4 +20,19 @@ public sealed class OwnershipGuardTests var method = typeof(CorrespondenceController).GetMethod("FindOwnedMessageAsync", BindingFlags.NonPublic | BindingFlags.Instance); Assert.NotNull(method); } + + [Fact] + public void Summarizer_service_uses_sha256_cache_key_builder() + { + var method = typeof(SummarizerService).GetMethod("BuildCacheKey", BindingFlags.NonPublic | BindingFlags.Static); + Assert.NotNull(method); + + var first = (string)method!.Invoke(null, new object[] { "example text", 150, 30 })!; + var second = (string)method.Invoke(null, new object[] { "example text", 150, 30 })!; + var different = (string)method.Invoke(null, new object[] { "different text", 150, 30 })!; + + Assert.Equal(first, second); + Assert.StartsWith("summ:", first); + Assert.NotEqual(first, different); + } } diff --git a/JobTrackerApi/Services/SummarizerService.cs b/JobTrackerApi/Services/SummarizerService.cs index 23a1a75..d4c70e3 100644 --- a/JobTrackerApi/Services/SummarizerService.cs +++ b/JobTrackerApi/Services/SummarizerService.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics; using System.Net.Http; +using System.Security.Cryptography; using System.Text; using System.Text.Json; using System.Threading; @@ -63,11 +64,20 @@ namespace JobTrackerApi.Services _cache = cache; } + private static string BuildCacheKey(string text, int maxLength, int minLength) + { + var payload = $"{text}\n::{maxLength}:{minLength}"; + var hash = Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(payload))).ToLowerInvariant(); + return $"summ:{hash}"; + } + public async Task SummarizeAsync(string text, int maxLength = 150, int minLength = 30) { if (string.IsNullOrWhiteSpace(text)) return null; - var key = $"summ:{text.GetHashCode()}:{maxLength}:{minLength}"; + // Use a deterministic content hash instead of string.GetHashCode() so cache keys + // are collision-resistant and stable across process restarts. + var key = BuildCacheKey(text, maxLength, minLength); Interlocked.Increment(ref _requests); if (_cache.TryGetValue(key, out var cached))