SeibroRollingSweepTests.cs 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. using System.Diagnostics;
  2. using Application.Helpers;
  3. namespace Application.Tests;
  4. [TestClass]
  5. public sealed class SeibroRollingSweepTests
  6. {
  7. [TestMethod]
  8. public async Task RunAsync_UnprocessedFirst_ThenStaleOrder_StableForTies()
  9. {
  10. // 미처리(null) 우선(입력 순서 유지), 그다음 오래된(stale) 순
  11. var targets = new List<(string Key, DateTime? LastUpdatedAt)>
  12. {
  13. ("A", new DateTime(2026, 1, 5)),
  14. ("B", null),
  15. ("C", new DateTime(2026, 1, 1)),
  16. ("D", null)
  17. };
  18. var processed = new List<string>();
  19. var count = await SeibroRollingSweep.RunAsync(
  20. targets: targets,
  21. fetchAndUpsert: (key, _) => { processed.Add(key); return Task.CompletedTask; },
  22. maxPerRun: 100,
  23. delayMs: 0,
  24. ct: CancellationToken.None);
  25. Assert.AreEqual(4, count);
  26. CollectionAssert.AreEqual(new[] { "B", "D", "C", "A" }, processed, "미처리(B,D 입력순) → stale(C=1/1) → 최신(A=1/5)");
  27. }
  28. [TestMethod]
  29. public async Task RunAsync_StopsAfterMaxPerRun()
  30. {
  31. var targets = Enumerable.Range(0, 10).Select(c => (Key: c, LastUpdatedAt: (DateTime?)null)).ToList();
  32. var fetched = 0;
  33. var count = await SeibroRollingSweep.RunAsync(
  34. targets: targets,
  35. fetchAndUpsert: (_, _) => { fetched++; return Task.CompletedTask; },
  36. maxPerRun: 3,
  37. delayMs: 0,
  38. ct: CancellationToken.None);
  39. Assert.AreEqual(3, count, "maxPerRun 도달 시 즉시 멈춤 (quota 상한)");
  40. Assert.AreEqual(3, fetched);
  41. }
  42. [TestMethod]
  43. public async Task RunAsync_MaxPerRunZeroOrNegative_FetchesNothing()
  44. {
  45. var fetched = 0;
  46. var count = await SeibroRollingSweep.RunAsync(
  47. targets: [("A", (DateTime?)null)],
  48. fetchAndUpsert: (_, _) => { fetched++; return Task.CompletedTask; },
  49. maxPerRun: 0,
  50. delayMs: 0,
  51. ct: CancellationToken.None);
  52. Assert.AreEqual(0, count);
  53. Assert.AreEqual(0, fetched, "maxPerRun<=0 이면 fetch 호출 자체가 없다");
  54. }
  55. [TestMethod]
  56. public async Task RunAsync_DelaysBetweenFetches_NotBeforeFirst()
  57. {
  58. // 지연 계약: 호출 사이에만 delayMs — 3건 × 60ms → 최소 120ms (첫 호출 전엔 지연 없음)
  59. var targets = Enumerable.Range(0, 3).Select(c => (Key: c, LastUpdatedAt: (DateTime?)null)).ToList();
  60. var sw = Stopwatch.StartNew();
  61. await SeibroRollingSweep.RunAsync(
  62. targets: targets,
  63. fetchAndUpsert: (_, _) => Task.CompletedTask,
  64. maxPerRun: 100,
  65. delayMs: 60,
  66. ct: CancellationToken.None);
  67. Assert.IsTrue(sw.ElapsedMilliseconds >= 120, $"3건이면 사이 지연 2회 ≥ 120ms (실측 {sw.ElapsedMilliseconds}ms)");
  68. // 단건이면 지연 없음 — delayMs 가 매우 커도 즉시 끝난다
  69. sw.Restart();
  70. await SeibroRollingSweep.RunAsync(
  71. targets: [(0, (DateTime?)null)],
  72. fetchAndUpsert: (_, _) => Task.CompletedTask,
  73. maxPerRun: 1,
  74. delayMs: 30_000,
  75. ct: CancellationToken.None);
  76. Assert.IsTrue(sw.ElapsedMilliseconds < 5_000, $"첫 호출 전에는 지연이 없다 (실측 {sw.ElapsedMilliseconds}ms)");
  77. }
  78. [TestMethod]
  79. public async Task RunAsync_EmptyTargets_ReturnsZero()
  80. {
  81. var count = await SeibroRollingSweep.RunAsync<string>(
  82. targets: [],
  83. fetchAndUpsert: (_, _) => Task.CompletedTask,
  84. maxPerRun: 100,
  85. delayMs: 0,
  86. ct: CancellationToken.None);
  87. Assert.AreEqual(0, count);
  88. }
  89. }