feat: CoverageStore contract test and configurable eviction - #15
Conversation
Add a reusable CoverageStoreContract JUnit base that any store can extend, run InMemoryCoverageStore against it, and let the in-memory store choose oldest-first or reject-when-full eviction via reqover.mvc.snapshot-eviction / reqover.webflux.snapshot-eviction. Document both next to max-snapshots in the integration guide. Fixes reqover-labs#14 Signed-off-by: Vedant Madane <6527493+VedantMadane@users.noreply.github.com>
lsmin3388
left a comment
There was a problem hiding this comment.
Thank you for this — and sorry for the slow first response. This is the first
contribution to Reqover from outside the team, and it landed as a better piece
of work than the issue asked for.
Things I want to name specifically, because they were the hard parts:
CoverageStoreContracttests the property that actually matters — that
snapshots()hands back a list which stays stable while another thread is
flushing into the store. That is the invariant a second implementation is
most likely to get wrong, and it is not the obvious thing to test.- Reserving the slot with a CAS before adding in
REJECT_WHEN_FULLis right.
The naive version overshoots the bound under concurrent flushes. hitsAfterFlushDoNotMutateStoredSnapshotasserts both halves — the stored
snapshot frozen and the live bucket still recording. Good.
One change before merge, then two notes.
SnapshotEvictionPolicy.fromProperty is unreachable, and has a latent bug
Nothing in src/main calls it. Spring's relaxed binding already converts
reqover.mvc.snapshot-eviction=reject-when-full straight to the enum constant —
your own configuresSnapshotEvictionOnTheDefaultStore test proves it, since it
passes without fromProperty participating. So the method is exercised only by
parsesEvictionPolicyTokens.
That matters because it is a second, divergent parser for something Spring
already parses, and it has the Turkish-locale bug:
$ java -Duser.language=tr -Duser.country=TR ...
default locale : tr_TR
"OLDEST_FIRST".toLowerCase() -> oldest-fırst // dotless ı
matches token : false
with Locale.ROOT -> oldest-first -> true
On a JVM with a Turkish default locale, an OLDEST_FIRST token throws
"Unknown snapshot eviction policy".
Preferred fix: delete fromProperty and its test. Spring is already doing
this job, and one parser is better than two that can drift apart. If you would
rather keep it for callers constructing the store outside Spring, that is a
reasonable argument — in that case make it toLowerCase(Locale.ROOT) and say in
the Javadoc that it is for non-Spring callers, so the next person does not
wonder why there are two paths.
Either way, please keep the accepted-token set narrow. oldest/reject as
aliases will outlive the moment they seemed convenient.
Notes, no action needed from you
- Korean docs.
docs/17_integration_guide.ko.mdneeds the same two rows and
the retention paragraph. Per CONTRIBUTING that is explicitly not your job — a
maintainer follows up on the other language. We will take it. - Stray newline.
ReqoverMvcConfiguration.javaand
ReqoverWebFluxConfiguration.javaeach pick up a trailing blank line at EOF.
Worth dropping while you are in there.
CI was waiting on maintainer approval because this is a first contribution from
a fork; I have approved it, so the checks are running now.
One last thing: #14 asked for the contract test, and you also worked out
where the policy had to be threaded through both adapters. If you want another,
#2 (Servlet async
attribution) is the most interesting open problem in the repository — no
pressure at all.
Spring's relaxed binding already turns `oldest-first` and `reject-when-full` into the enum constants, so `fromProperty` had no caller in main and was a second parser that could drift from the first. It also lower-cased with the default locale, which turns `OLDEST_FIRST` into `oldest-fırst` under tr_TR and throws. While finishing the review of reqover-labs#15: the Korean integration guide gets the same two property rows and the retention paragraph, the changelog records the feature under Unreleased, the English guide stops attributing the policy to 0.2.0, and the two configuration classes lose a trailing blank line.
|
Following up on the review above. It has been just over a week, and I would rather have this in What the follow-up commit does:
Build is green locally with 130 tests. Merging once CI agrees. Thank you again — this was a good first outside contribution, and the contract test in particular will keep earning its place. |
Summary
CoverageStorelanded in 0.2.0 without a shared contract test or a choice of eviction policy (#14).Changes
CoverageStoreContractabstract JUnit class inreqover-coretests covering:snapshots()snapshots()returns a copy safe to iterate while another thread flushesclear()empties the storeflushdo not mutate the retained snapshotInMemoryCoverageStoreTestextends the contract (replacing the parts it subsumes) and keeps capacity / reject-when-full coverageSnapshotEvictionPolicy:OLDEST_FIRST(default) andREJECT_WHEN_FULLreqover.mvc.snapshot-eviction/reqover.webflux.snapshot-eviction(wired besidemax-snapshots)max-snapshotsNon-goals
No production database store.
Test plan
./gradlew :reqover-core:test --tests io.reqover.core.InMemoryCoverageStoreTest./gradlew :reqover-spring-mvc:test --tests io.reqover.spring.mvc.ReqoverMvcAutoConfigurationTestFixes #14