diff --git a/src/cache/store.zig b/src/cache/store.zig index 7fdc52e..494c8d8 100644 --- a/src/cache/store.zig +++ b/src/cache/store.zig @@ -2740,11 +2740,15 @@ test "writeSupplement Dividend: no existing file establishes an initial TTL" { var s = Store.init(io, allocator, dir_path); - const now_s = std.Io.Timestamp.now(io, .real).toSeconds(); + // Bracket the write: `writeSupplement` reads the clock itself, so + // an instant captured only before it can be a second behind the + // one it stamps, which flakes the upper bound below. + const before_s = std.Io.Timestamp.now(io, .real).toSeconds(); var incoming = [_]Dividend{ .{ .ex_date = Date.fromYmd(2024, 5, 15), .amount = 0.50 }, }; s.writeSupplement(Dividend, "TEST", incoming[0..], "tiingo"); + const after_s = std.Io.Timestamp.now(io, .real).toSeconds(); const path = try std.fs.path.join(allocator, &.{ dir_path, "TEST", "dividends.srf" }); defer allocator.free(path); @@ -2760,8 +2764,8 @@ test "writeSupplement Dividend: no existing file establishes an initial TTL" { const spec = DataType.dividends.ttl(); const jitter = @divFloor(spec.seconds * @as(i64, spec.jitter_pct), 100); const new_expires = it.expires orelse return error.ExpiresMissing; - try std.testing.expect(new_expires - now_s >= spec.seconds - jitter); - try std.testing.expect(new_expires - now_s <= spec.seconds + jitter); + try std.testing.expect(new_expires - before_s >= spec.seconds - jitter); + try std.testing.expect(new_expires - after_s <= spec.seconds + jitter); } test "writeMerged Dividend: field-level upgrade fills nulls (Tiingo-then-Polygon)" { diff --git a/src/service.zig b/src/service.zig index c16c8a0..0203c94 100644 --- a/src/service.zig +++ b/src/service.zig @@ -5485,24 +5485,43 @@ test "dividendTtl: the chosen clock is what actually lands on disk" { var store = cache.Store.init(io, allocator, dir_path); const today = fmt.todayDate(io); - const now_s = std.Io.Timestamp.now(io, .real).toSeconds(); + + // `store.write` reads the clock itself, so a single `now` captured + // here can be a second behind the one it stamps - which flaked this + // test as `expected 516344, found 516345`. Bracket each write + // instead: the stamped expiry must be `offset` past SOME instant in + // [before, after]. + const Bracket = struct { + fn expect(expires: i64, offset: i64, before_s: i64, after_s: i64) !void { + try std.testing.expect(expires >= before_s + offset); + try std.testing.expect(expires <= after_s + offset); + } + }; // Established payer -> ~14 days. var established = quarterlySeries(6, today.addDays(-30), 91); + const long_before = std.Io.Timestamp.now(io, .real).toSeconds(); store.write(Dividend, "TESTA", established[0..], DataService.dividendTtl(&established)); + const long_after = std.Io.Timestamp.now(io, .real).toSeconds(); const long_expires = readExpires(io, allocator, dir_path, "TESTA") orelse return error.TestUnexpectedResult; - try std.testing.expectEqual( + try Bracket.expect( + long_expires, cache.computeExpires(0, DataService.dividendTtl(&established), "TESTA"), - long_expires - now_s, + long_before, + long_after, ); // Sparse history -> ~6 days. var sparse = divsFromYmd(.{ .{ 2026, 6, 15 }, .{ 2026, 3, 17 } }); + const short_before = std.Io.Timestamp.now(io, .real).toSeconds(); store.write(Dividend, "TESTB", sparse[0..], DataService.dividendTtl(&sparse)); + const short_after = std.Io.Timestamp.now(io, .real).toSeconds(); const short_expires = readExpires(io, allocator, dir_path, "TESTB") orelse return error.TestUnexpectedResult; - try std.testing.expectEqual( + try Bracket.expect( + short_expires, cache.computeExpires(0, DataService.dividendTtl(&sparse), "TESTB"), - short_expires - now_s, + short_before, + short_after, ); // And the long one really is longer, so nobody can "simplify" the two