Skip to content

test: literal corpus × entry point against PostgreSQL (route B, step 3) - #135

Merged
CaffeinatedCoder merged 2 commits into
mainfrom
test/literal-corpus-probe
Oct 17, 2026
Merged

CaffeinatedCoder merged 2 commits into
mainfrom
test/literal-corpus-probe

Conversation

@CaffeinatedCoder

@CaffeinatedCoder CaffeinatedCoder commented Oct 17, 2026 •

Copy link
Copy Markdown
Owner

Closes #123

Problem

test/oracles/axes.md listed literal corpus × entry point against PostgreSQL on the same text as a pair no test varies. LiteralGrammarParityTests asked the server about quoted, escaped and whitespace bounds, through TryParse only. Everything else PostgreSQL's range_in, multirange_in, array_in and the element input functions tell apart had never been put to the server, and neither had the other entry points. #114 and #115 were single cells of this product. Each was found once and never kept as a test.

Change

  • LiteralCorpusProbeTests (integration project, route B step 3 of Consumer probing: port TypeKit's prober tooling (route A), or BLite's lightweight way with hand-written axes and MSTest probe sweeps (route B) #105). It has one [DynamicData] row per type: 10 range types, their 10 multiranges and 28 value set types. YearMonth is left out because its literal isn't the text of the daterange it's stored as.
    • Corpus. Generated per element kind, 65–135 literals per type, 4,715 in all (30,699 literal × entry point cells). The structural families are bounds, equal and inverted bounds for each bracket pair, empty in every case, blank input, quoting, escapes, stray punctuation, trailing junk, ASCII and Unicode whitespace, array dimensions and NULLs, and multirange canonicalization. Per element kind it adds sign, leading zeros, exponent, NaN/Infinity, non-decimal integers and digit separators, more than 28 digits, overflow, the extremes, infinity spellings, epoch/now/today/allballs, non-ISO dates, 24:00:00, the leap second, offsets and zone names, sub-microsecond digits, uuid spellings and NUL.
    • Authority. pg_input_is_valid($1, type) and the canonical text, with every literal of a type sent as a text[] in one statement (a fallback runs one statement per literal). An array is read without its dimension decoration.
    • Entry points. Parse(string), Parse(span), TryParse(string), TryParse(span), JSON (a range or multirange as its literal; a value set as [E] where JSON can spell the element), the TypeConverter and the EF Core JSON document reader. All of them must agree. A value the library reads is also written back through EF Core as a SQL constant and as a JSON document, and the server compares it with the original as typed values. That comparison is what catches a precision the parser dropped.
    • Counted, not reported. A refusal of a value outside the CLR domain, a refusal docs/serialization.md names (J07 strict ISO 8601, J06 the numeric grammar), and an extreme the store spells ±infinity (docs/efcore.md).
    • Without Docker the row is Inconclusive; under CI=true it fails. The whole class takes about 6 s.
  • Registered. Added to oracles.json, with the seed seeds/empty-keyword-matched-as-prefix.patch. New claim J34 quotes "The text is the PostgreSQL literal, the same text the server itself reads and prints". The class is added to the heldBy of J06, J11, J12, J33 and S12. In axes.md the pair moves into "Varied by", and the two pairs it leaves open are listed.
  • Candidates. 115 rows, keyed {type} {family} , each with an issue. No defect is fixed here.
  • Docs. A paragraph each in docs/oracles.md (route B) and docs/testing.md.

Findings

Issue Family Types Silent
#114 (known) inverted bounds parse as empty 20 ranges and multiranges yes
#115 (known) more than 28 digits rounded, seen on the EF write-back DecimalRange, its multirange, DecimalSet yes
#78 (known) whitespace in a uuid element accepted GuidSet, GuidSet<T>
#117 (known, column path) [x,infinity) loses the last day, and (-infinity,x] the first, on the literal and JSON-document paths too (scope added to the issue) DateRange, LocalDateRange, their multiranges yes
#126 sub-microsecond digits: kept to 100 ns, stored truncated; PostgreSQL rounds half-to-even 16 temporal types yes
#127 0x/0o/0b and 1_000 refused for integers 10
#128 [1:1]={…}, [0:1]={…}, {{}} refused 28 value sets
#129 GuidSet follows Guid.Parse: accepts (…), refuses hyphen groups 2
#130 LocalDateTime refuses Z and an offset that timestamp input drops 4
#131 DateTimeOffset refuses offsets beyond ±14:00 4
#132 (,max] raises 22003 in PostgreSQL and isn't in the boundary table (documentation) Int32Range, Int64Range
#133 a NUL character accepted into a text element 2

I confirmed each one by hand against postgres:17-alpine with psql (repros are in the issues). No 24:00:00 cell disagrees, so nothing is registered against #108: the library refuses 24:00:00, and the server's value can't be a TimeOnly.

Tests

  • dotnet test test/CodoMetis.ValueRanges.EFCore.PostgreSQL.IntegrationTests passes with Docker: 200 tests in 11 s, LiteralCorpusProbeTests included (49 tests, about 6 s).
  • dotnet test test/CodoMetis.ValueRanges.Conventions.Tests passes (217 tests); Oracle*Tests hold the registry, claims, seed and candidates.
  • dotnet run tools/oracles.cs seeds gives every seed is caught: 48 (seed, oracle) pairs over both route B steps. LiteralCorpusProbeTests catches empty-keyword-matched-as-prefix (10 of 49 rows; LiteralGrammarParityTests and RangeLiteralGrammarContractTests stay green under it), quoted-bound-whitespace-kept (14) and temporal-set-element-whitespace-kept (16).
  • dotnet run tools/oracles.cs status reports 0 oracles without a seed and 0 candidates without an issue.
  • The EF write-back leg can't go quiet unnoticed. The Decimal text beyond 28 significant digits is rounded silently when a literal or JSON array is read #115 and A temporal literal with sub-microsecond digits is a different value than PostgreSQL reads from the same text #126 rows for DecimalRange and TimeRange come only from that leg, so a leg that stopped comparing would fail as candidate fixed.

Bugs this sweep would have caught (retroactive check over every bug issue)

I added found-by/oracle where it was missing and posted a comment naming the row and how it was verified:

Rebased onto main after route B step 2 (#125) merged; both steps' probe classes and seeds are kept.

🤖 Generated with Claude Code

@CaffeinatedCoder CaffeinatedCoder added tests Adds or changes tests only (oracles, harnesses); no library code package/nodatime Affects CodoMetis.ValueRanges.NodaTime package/efcore Affects CodoMetis.ValueRanges.EFCore.PostgreSQL area/literals Parse, TryParse and formatting package/core Affects CodoMetis.ValueRanges labels Oct 17, 2026
LiteralCorpusProbeTests asks the server pg_input_is_valid and the canonical
text for about 4,500 generated literals over every range, multirange and
value set type, and requires Parse, TryParse (string and span), JSON, the
TypeConverter and the EF JSON document reader to agree with it, and the EF
write-back (SQL constant, JSON document) to be the value the server reads.

Registered in oracles.json with a seed, new claim J34, axes.md pair moved to
"Varied by". Disagreements are candidates against #114, #115, #78 and the new

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… probe

Adds [x,infinity), (-infinity,x] and friends for the temporal range types
and quoted or escaped whitespace bounds and elements. The infinity rows
reproduce #117 on the literal and JSON-document paths and are registered
against it; the whitespace rows catch the reverts of #45 and #70, so both
seeds now name LiteralCorpusProbeTests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@CaffeinatedCoder
CaffeinatedCoder force-pushed the test/literal-corpus-probe branch from 3affabe to 92a2b5e Compare October 17, 2026 23:20
@CaffeinatedCoder
CaffeinatedCoder merged commit 609dd57 into main Oct 17, 2026
9 of 10 checks passed
@CaffeinatedCoder
CaffeinatedCoder deleted the test/literal-corpus-probe branch October 17, 2026 23:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/literals Parse, TryParse and formatting package/core Affects CodoMetis.ValueRanges package/efcore Affects CodoMetis.ValueRanges.EFCore.PostgreSQL package/nodatime Affects CodoMetis.ValueRanges.NodaTime tests Adds or changes tests only (oracles, harnesses); no library code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Route B, step 3: literal corpus × entry point against PostgreSQL

1 participant