|
3 | 3 | using Microsoft.Extensions.Logging; |
4 | 4 | using SecRandom.Core.Abstraction; |
5 | 5 | using SecRandom.Core.Abstraction.Services; |
| 6 | +using SecRandom.Core.Enums; |
6 | 7 | using SecRandom.Core.Enums.Configs; |
7 | 8 | using SecRandom.Core.Models; |
8 | 9 | using SecRandom.Core.Services; |
9 | 10 | using SecRandom.Core.Services.Config; |
10 | 11 | using SecRandom.Core.Services.Draw; |
| 12 | +using SecRandom.Core.Services.Draw.Exceptions; |
11 | 13 | using SecRandom.Core.Models.Draw; |
12 | 14 | using SecRandom.Shared; |
13 | 15 | using SecRandom.Shared.Models.Profile; |
@@ -250,6 +252,75 @@ public void ProfileCatalogEditor_UsesRecordIdsAndPersistsMutations() |
250 | 252 | Assert.Empty(profile.CurrentPrizeList!.Prizes); |
251 | 253 | } |
252 | 254 |
|
| 255 | + [Fact] |
| 256 | + public void StudentTemporaryRecords_AreNotHiddenBySwitchingTheDrawFilter() |
| 257 | + { |
| 258 | + using var provider = CreateProvider(); |
| 259 | + var temporary = provider.GetRequiredService<IDrawTemporaryRecordService>(); |
| 260 | + var student = new Student { Name = "Switch", RecordId = Guid.NewGuid() }; |
| 261 | + |
| 262 | + temporary.RecordStudents("filter-class", "男", "A组", [student]); |
| 263 | + |
| 264 | + var recordId = student.RecordId.ToString("D"); |
| 265 | + Assert.Equal(1, temporary.GetStudentCounts("filter-class", "男", "A组")[recordId]); |
| 266 | + // Switching the group/gender filter must not restart the repeat limit for an already drawn member. |
| 267 | + Assert.Equal(1, temporary.GetStudentCounts("filter-class", string.Empty, string.Empty)[recordId]); |
| 268 | + Assert.Equal(1, temporary.GetStudentCounts("filter-class", "女", "B组")[recordId]); |
| 269 | + } |
| 270 | + |
| 271 | + [Fact] |
| 272 | + public void StudentTemporaryRecords_MergeLegacyFilterBucketsOnUpgrade() |
| 273 | + { |
| 274 | + using var provider = CreateProvider(); |
| 275 | + var temporary = provider.GetRequiredService<IDrawTemporaryRecordService>(); |
| 276 | + var student = new Student { Name = "Legacy", RecordId = Guid.NewGuid() }; |
| 277 | + var recordId = ProfileRecordIdentity.EnsureRecordId(student); |
| 278 | + |
| 279 | + var legacy = $$""" |
| 280 | + { |
| 281 | + "listName": "legacy-class", |
| 282 | + "updatedAt": "2026-01-02T00:00:00+00:00", |
| 283 | + "scopes": { |
| 284 | + "gender=男|group=A组": { "records": { "{{recordId}}": { "name": "Legacy", "id": "1", "count": 2, "lastDrawnTime": "2026-01-01T00:00:00+00:00" } } }, |
| 285 | + "gender=女|group=B组": { "records": { "{{recordId}}": { "name": "Legacy", "id": "1", "count": 3, "lastDrawnTime": "2026-01-02T00:00:00+00:00" } } } |
| 286 | + } |
| 287 | + } |
| 288 | + """; |
| 289 | + var legacyPath = Utils.GetFilePath("TEMP", "roll_call_record_legacy-class.json"); |
| 290 | + Directory.CreateDirectory(Path.GetDirectoryName(legacyPath)!); |
| 291 | + File.WriteAllText(legacyPath, legacy); |
| 292 | + |
| 293 | + // The upgrade must sum the old per-filter buckets instead of silently restarting the count. |
| 294 | + Assert.Equal(5, temporary.GetStudentCounts("legacy-class", string.Empty, string.Empty)[recordId]); |
| 295 | + } |
| 296 | + |
| 297 | + [Fact] |
| 298 | + public void VerificationInput_ReportsNoEligibleCandidatesWhenEveryMemberIsInsideTheShieldWindow() |
| 299 | + { |
| 300 | + using var provider = CreateProvider(); |
| 301 | + var config = provider.GetRequiredService<MainConfigHandler>(); |
| 302 | + config.Data.RollCallSettings.DefaultClass = "shield-class"; |
| 303 | + config.Data.RollCallSettings.AlgorithmId = "builtin.fair"; |
| 304 | + config.Data.FairDrawSettings.FairDraw = true; |
| 305 | + config.Data.FairDrawSettings.FairDrawGroup = false; |
| 306 | + config.Data.FairDrawSettings.FairDrawGender = false; |
| 307 | + config.Data.FairDrawSettings.ShieldEnabled = true; |
| 308 | + config.Data.FairDrawSettings.ShieldTime = 60; |
| 309 | + config.Data.FairDrawSettings.ShieldTimeUnit = ShieldTimeUnit.Minutes; |
| 310 | + config.Save(); |
| 311 | + |
| 312 | + var profile = provider.GetRequiredService<IProfileService>(); |
| 313 | + var student = new Student { Name = "Shielded", RecordId = Guid.NewGuid() }; |
| 314 | + profile.CurrentStudentList!.Students.Add(student); |
| 315 | + profile.SaveProfile(); |
| 316 | + profile.RecordStudentHistory([student], DateTime.Now, 1); |
| 317 | + |
| 318 | + var engine = provider.GetRequiredService<DrawEngine>(); |
| 319 | + |
| 320 | + Assert.Throws<NoEligibleCandidatesException>(() => |
| 321 | + engine.CreateStudentVerificationInput(1, [student], DrawSettingsType.RollCall)); |
| 322 | + } |
| 323 | + |
253 | 324 | public void Dispose() |
254 | 325 | { |
255 | 326 | ResetDataRootForTests(); |
|
0 commit comments