Skip to content

test(java): jsonb open-bag runtime-write gate + api-contract harness (#98)#113

Merged
dmealing merged 1 commit into
mainfrom
fix/java-jsonb-runtime-write-98
Jun 29, 2026
Merged

test(java): jsonb open-bag runtime-write gate + api-contract harness (#98)#113
dmealing merged 1 commit into
mainfrom
fix/java-jsonb-runtime-write-98

Conversation

@dmealing

Copy link
Copy Markdown
Member

Completes the Java side of the jsonb-open-bag (field.string @dbColumnType:jsonb) cross-port consistency work. Two mandates.

Mandate 1 — OMDB runtime write path: did it have the gap? No (well-evidenced).

The TS gap (PR #110 found) was a runtime write validator rejecting a posted object. OMDB has no analogous gap:

  • OMDB's open-bag write path correctly serializes + binds a JSON-text value: isOpenJsonbFieldserializeOpenJsonbbindJsonbParameter (Postgres setObject(.., Types.OTHER)).
  • OMDB is pure data-access (ADR-0015) — there is no write-side validator to reject anything.
  • The persistence corpus never exercised the open bag through the runtime write codec: asset-uuid-roundtrip.yaml seeds payload via raw SQL and only READS; roundtrip-all-types.yaml writes the typed @storage:jsonb path (Settings), not the open @dbColumnType:jsonb bag. OpenJsonbWriteRoundtripTest fills that gate — it INSERTs an Asset whose payload is JSON text through ObjectManagerDB.createObject (NOT raw SQL) and reads it back parsed.

The "object is rejected" symptom is not an OMDB write-path defect. OMDB's ValueObject model is statically typed by field subtype (ADR-0017), so a structured value assigned to a field.string is coerced to its java toString() at set-time (DataObjectBase._setObjectAttributeDataConverter.toType(STRING, …)), identical for every string field — by design. Verified empirically: a Map bound to the bag binds the malformed text {a=1, b=2}, which Postgres rejects with invalid input syntax for type json. The cross-port "write an OBJECT" half lives at the DTO/serialization boundary (the generated DTO is Object, #103): a consumer serializes the posted object to JSON text before it reaches OMDB's field.string, and the read side re-parses it.

No OMDB production change was needed — hence the test() prefix. (GenericSQLDriver is byte-pristine; I prototyped a writeValueFor raw-read fix, proved via empirical RED that set-time coercion defeats it for ValueObject, and reverted it.)

Mandate 2 — Java api-contract jsonb harness (both lanes)

Mirrors the single-entity Author + m2m/tph harnesses for the new fixtures/api-contract-conformance/jsonb/ corpus:

  • Reference laneJsonbReferenceServer (JDK HttpServer + Postgres testcontainer, documents table with payload JSONB).
  • Generated laneGeneratedJsonbControllerHarness (codegen-spring DocumentController/DocumentDto compiled + booted over MockMvc behind the in-memory DocumentRepository seam).

The generated DocumentDto types the bag as Object (#103), so a posted JSON object binds and reads back parsed, never a JSON-encoded string. This LOCKS the API-boundary contract.

Test evidence (Testcontainers Postgres)

OpenJsonbWriteRoundtripTest                  1/1 green
JsonbApiContractConformanceTest (reference)  1/1 green
JsonbGeneratedApiContractConformanceTest     1/1 green
ApiContract*/M2m*/Tph* (existing)            48/48 green (no regression)

Deferred

  • Kotlin + C# api-contract jsonb harnesses (sibling worktrees).
  • A cross-port persistence-conformance open-bag op: roundtrip scenario (would require all five ports' roundtrip writers — out of this Java-scoped change).

Part of #98

🤖 Generated with Claude Code

…98)

Complete the Java side of the jsonb-open-bag cross-port consistency work.

Mandate 1 — OMDB runtime write path audit: OMDB has NO write-path gap for
the open bag (`field.string @dbColumnType:jsonb`). Its write path correctly
serializes + binds a JSON-text value via isOpenJsonbField -> serializeOpenJsonb
-> bindJsonbParameter (Postgres setObject(.., Types.OTHER)). The persistence
corpus never exercised this through the runtime write codec (asset-uuid seeds
payload via raw SQL and only READS; roundtrip-all-types writes the *typed*
@storage:jsonb path), so OpenJsonbWriteRoundtripTest fills the gap: it INSERTs
an Asset whose payload is JSON text through ObjectManagerDB.createObject (NOT
raw SQL) and reads it back parsed.

The apparent "object is rejected" is NOT an OMDB write-path defect: OMDB's
ValueObject model is statically typed by field subtype (ADR-0017), so a
structured value assigned to a field.string is coerced to its java toString()
at SET time (DataObjectBase._setObjectAttribute -> DataConverter.toType(STRING,
..)) — generic field-typing, identical for every string field, by design.
(Verified empirically: a Map bound to the bag binds the malformed text
"{a=1, b=2}" and Postgres rejects it with "invalid input syntax for type
json".) The cross-port "write an OBJECT" half lives at the DTO/serialization
boundary (the generated DTO is Object, #103): a consumer serializes the posted
object to JSON text before it reaches OMDB's field.string, and the read side
re-parses it. No OMDB production change was needed — hence the test() prefix.

Mandate 2 — api-contract Java harness for the jsonb sub-corpus, mirroring the
single-entity Author and m2m/tph harnesses, both lanes:
  - JsonbReferenceServer (hand-rolled JDK HttpServer + Postgres testcontainer).
  - GeneratedJsonbControllerHarness (codegen-spring DocumentController/DocumentDto
    compiled + booted over MockMvc behind the in-memory DocumentRepository seam).
The generated DocumentDto types the bag as Object (#103), so a posted JSON
object binds and reads back PARSED, never a JSON-encoded string. This LOCKS the
API-boundary contract.

Tests (Testcontainers Postgres, mvn -f integration-tests/pom.xml):
  - OpenJsonbWriteRoundtripTest                  1/1 green
  - JsonbApiContractConformanceTest (reference)  1/1 green
  - JsonbGeneratedApiContractConformanceTest     1/1 green
  - existing ApiContract*/M2m*/Tph*              48/48 green (no regression)

Deferred: Kotlin + C# api-contract jsonb harnesses (sibling worktrees); a
cross-port persistence-conformance open-bag roundtrip scenario (would require
all five ports' roundtrip writers, out of this Java-scoped change).

Part of #98

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LuZWKnWzYGVnESijL7uuky
@dmealing
dmealing force-pushed the fix/java-jsonb-runtime-write-98 branch from 288e959 to 500a250 Compare June 29, 2026 18:02
@dmealing
dmealing merged commit 2411cf4 into main Jun 29, 2026
11 checks passed
@dmealing
dmealing deleted the fix/java-jsonb-runtime-write-98 branch June 29, 2026 18:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant