refactor(quartz): swap FsEventStore clock ctor param for protected now()
The clock injection existed only for NIP-40 expiration tests. A public constructor parameter that ~every caller ignores is API clutter, so move it to a subclass seam: - FsEventStore is now `open class`; no more `clock: () -> Long` param. - `protected open fun now(): Long = TimeUtils.now()` is the override point. Production always takes the default; tests subclass. - FsExpirationTest gains a private `ClockedStore(root, source)` that overrides `now()`. Semantics unchanged, all 113 fs tests still green. Public `FsEventStore` ctor is now `(root, indexingStrategy, relay)` — no behavioural-drift surface that callers have to learn.
This commit is contained in:
+11
-8
@@ -57,14 +57,9 @@ import kotlin.io.path.readText
|
||||
* addressable slots, NIP-09 tombstones, NIP-40 expiration, NIP-50 FTS,
|
||||
* NIP-62 vanish, transactions, scrub.
|
||||
*/
|
||||
class FsEventStore(
|
||||
open class FsEventStore(
|
||||
val root: Path,
|
||||
indexingStrategy: IndexingStrategy = DefaultIndexingStrategy(),
|
||||
/**
|
||||
* Source of "now" in unix seconds. Injectable so tests can drive
|
||||
* NIP-40 expiration deterministically. Defaults to `TimeUtils.now()`.
|
||||
*/
|
||||
private val clock: () -> Long = { TimeUtils.now() },
|
||||
/**
|
||||
* Optional relay URL the store is acting on behalf of. Used by
|
||||
* NIP-62 [RequestToVanishEvent.shouldVanishFrom] scoping. When
|
||||
@@ -153,6 +148,14 @@ class FsEventStore(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Source of "now" in unix seconds. Defaults to [TimeUtils.now]; tests
|
||||
* override this via a subclass so NIP-40 expiration behaviour (insert
|
||||
* guard and periodic sweep) can be driven deterministically without
|
||||
* waiting on the wall clock or patching a global time source.
|
||||
*/
|
||||
protected open fun now(): Long = TimeUtils.now()
|
||||
|
||||
/**
|
||||
* NIP-40 pre-insert guard. Parity with SQLite's `reject_expired_events`
|
||||
* trigger: an event whose expiration tag is `<= now` is rejected
|
||||
@@ -162,7 +165,7 @@ class FsEventStore(
|
||||
private fun isAlreadyExpired(event: Event): Boolean {
|
||||
val exp = event.expiration() ?: return false
|
||||
if (exp <= 0) return false
|
||||
return exp <= clock()
|
||||
return exp <= now()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -384,7 +387,7 @@ class FsEventStore(
|
||||
override fun deleteExpiredEvents() =
|
||||
lockManager.withWriteLock {
|
||||
if (!Files.isDirectory(layout.idxExpiresAt)) return@withWriteLock
|
||||
val now = clock()
|
||||
val now = now()
|
||||
val toDelete = ArrayList<HexKey>()
|
||||
Files.list(layout.idxExpiresAt).use { stream ->
|
||||
for (entry in stream) {
|
||||
|
||||
@@ -130,8 +130,6 @@ val store = FsEventStore(
|
||||
root = Path.of(System.getProperty("user.home"), ".amy", "events-store"),
|
||||
// optional NIP-62 relay scoping; null = only ALL_RELAYS vanish requests cascade
|
||||
relay = null,
|
||||
// optional clock for tests
|
||||
clock = { com.vitorpamplona.quartz.utils.TimeUtils.now() },
|
||||
)
|
||||
```
|
||||
|
||||
@@ -140,6 +138,11 @@ bytes) is generated atomically on first open and read on every
|
||||
subsequent open — it salts every tag / owner hash, so it must persist
|
||||
or every previously-written index entry becomes unreachable.
|
||||
|
||||
`FsEventStore` is `open` and exposes a `protected open fun now(): Long
|
||||
= TimeUtils.now()`. Tests override it with a subclass to drive NIP-40
|
||||
expiration at exact timestamps without relying on the wall clock; see
|
||||
`FsExpirationTest.ClockedStore`.
|
||||
|
||||
### Insert
|
||||
|
||||
```kotlin
|
||||
|
||||
+13
-2
@@ -40,13 +40,24 @@ class FsExpirationTest {
|
||||
private lateinit var root: Path
|
||||
private var clockNow: Long = 1_000_000
|
||||
|
||||
private lateinit var store: FsEventStore
|
||||
/**
|
||||
* Test subclass overriding the `now()` seam so we can drive NIP-40
|
||||
* expiration at exact timestamps — no wall-clock sleeps, no flakes.
|
||||
*/
|
||||
private class ClockedStore(
|
||||
root: Path,
|
||||
private val source: () -> Long,
|
||||
) : FsEventStore(root) {
|
||||
override fun now(): Long = source()
|
||||
}
|
||||
|
||||
private lateinit var store: ClockedStore
|
||||
|
||||
@BeforeTest
|
||||
fun setup() {
|
||||
Secp256k1Instance
|
||||
root = Files.createTempDirectory("fs-exp-")
|
||||
store = FsEventStore(root, clock = { clockNow })
|
||||
store = ClockedStore(root) { clockNow }
|
||||
}
|
||||
|
||||
@AfterTest
|
||||
|
||||
Reference in New Issue
Block a user