Add unit test coverage across grails-data-mongodb - #16285
Conversation
Covers plugin descriptor metadata, MongoDB datastore bean registration, the Hibernate-secondary-datastore alias behavior, and the transactionManager alias, none of which had any test coverage. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Covers the ext module's only class, which had zero test coverage: the Groovy extension methods that map Groovy syntax (maps, operators) onto the MongoDB driver's Document/Bson API across MongoDatabase, MongoCollection, FindIterable and DistinctIterable, plus the Document-to-DBObject conversion helper. The GormEnhancer-backed asType/toList conversion methods are covered only for their short-circuit and error-propagation branches; their success path (decoding a Document/cursor into a GORM entity) has no active coverage anywhere in the module today - the one spec that exercised it (DBObjectConversionSpec in grails-data-mongodb-core) has been @Ignore-d since before the mono-repo migration. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QAjfckcY4EJsxranv1RyGV
Covers the 10 org.grails.datastore.bson.codecs.*Codec classes that had no direct test: BigDecimalCodec, BigIntegerCodec and the 8 temporal codecs (Instant/LocalDate/LocalDateTime/LocalTime/OffsetDateTime/ OffsetTime/Period/ZonedDateTime). The temporal codecs are thin Codec adapters over already exhaustively-tested *BsonConverter traits, so their tests verify the adapter's own delegation and getEncoderClass rather than re-testing conversion math covered by the converter specs. BigDecimalCodec/BigIntegerCodec have no converter to delegate to, so their Decimal128 conversion is tested directly. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QAjfckcY4EJsxranv1RyGV
Covers all 15 classes in org.grails.datastore.bson.codecs.decoders, none of which had any test coverage: the 8 small temporal TypeDecoder adapters (Instant/LocalDate/LocalDateTime/LocalTime/OffsetDateTime/ OffsetTime/Period/ZonedDateTime, adapters over already-tested *BsonConverter traits), IdentityDecoder's wire-type/declared-type dispatch and its error branches, SimpleDecoder's BsonType dispatch table including the array/list-codec fallback, TenantIdDecoder's reuse of that dispatch, CustomTypeDecoder's Codec-marshaller and plain-marshaller paths, BasicCollectionTypeDecoder's List/Set/Map conversion and dirty-checking-wrap behaviour, and EmbeddedDecoder/ EmbeddedCollectionDecoder's association decoding, dirty-change tracking and bidirectional inverse-side wiring. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QAjfckcY4EJsxranv1RyGV
Covers all 15 classes in org.grails.datastore.bson.codecs.encoders, none of which had any test coverage: the 8 small temporal TypeEncoder adapters (Instant/LocalDate/LocalDateTime/LocalTime/OffsetDateTime/ OffsetTime/Period/ZonedDateTime, adapters over already-tested *BsonConverter traits), SimpleEncoder's type-dispatch table including the array/component-type and enableBigDecimalEncoding paths, IdentityEncoder's storedAs coercion and its ObjectId/String fallback branches, CustomTypeEncoder's Codec-marshaller and plain-marshaller paths, BasicCollectionTypeEncoder's List/Set/Map and dirty-checking rewrap paths, EmbeddedEncoder's identifier-presence and subclass fallback paths, EmbeddedCollectionEncoder's List/Map dispatch, bidirectional inverse-side wiring and per-element subclass codec resolution, and TenantIdEncoder's delegation to the simple encoder table. Flagged (not fixed, out of scope for a test-only stage): IdentityEncoder.getIdentifierName() navigates property.owner.mapping.identifier without the null-safety that resolveStoredAs() applies to the same chain, so a missing ClassMapping throws NPE there instead of degrading gracefully like the storedAs lookup does. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QAjfckcY4EJsxranv1RyGV
Covers the two remaining untested classes in the codecs package root: CodecCustomTypeMarshaller's supports/targetType delegation and its unsupported write/query/read operations, and CodecExtensions' full surface - the CodecProvider.get() and getCodecForBsonType() lookups (including CodecRegistryAware wiring), every registered BsonValue converter (binary/objectId/timestamp/dateTime/string/regex/boolean/ null/double/int32/int64/array/document/decimal128, including the recursive BsonArray/BsonDocument conversions), and the nested MapCodec/ListCodec/IntRangeCodec/LocaleCodec/CurrencyCodec/GStringCodec implementations. PropertyDecoder/PropertyEncoder/CodecRegistryAware/CodecRegistryProvider were confirmed to be pure interfaces with no logic and are intentionally left untested. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QAjfckcY4EJsxranv1RyGV
Covers JsonReader and JsonWriter - a simplified fork of the MongoDB
driver's own JSON reader/writer that removes MongoDB Extended JSON
processing (visitExtendedJSON always treats '{' as a plain nested
document; there is no JSON syntax this parser recognizes that reaches
OBJECT_ID/BINARY/TIMESTAMP/DB_POINTER/MAX_KEY/MIN_KEY on the read
side, so those are untested by design). JsonScanner/JsonToken/
JsonTokenType are package-private internals with no other consumer,
so they're exercised indirectly through JsonReader's public
BsonReader API rather than tested directly, per this project's
test-via-public-APIs convention.
JsonReaderSpec covers strings (escapes, unicode), int32/int64
boundary, doubles (incl. scientific notation), unquoted literals
(true/false/null/undefined/NaN/Infinity), regular expression
literals, nested documents/arrays, skipValue, and malformed-input
error paths. JsonWriterSpec covers every writable type, array vs
document name handling, strict vs default regular expression output,
and several full write-then-reparse round trips through JsonReader.
Flagged (not fixed, out of scope for a test-only stage) - two
reproducible bugs found in JsonScanner while writing these tests:
- scanNumber()'s SAW_EXPONENT_DIGITS state has no end-of-input branch
(unlike its sibling states, which all treat EOF like a closing
delimiter), so a bare exponent-notation number with nothing
following it (e.g. top-level "1e2") throws JsonParseException
instead of parsing.
- scanNumber()'s SAW_MINUS_I case unconditionally appends the
character it reads on every loop iteration, including the
terminator read immediately after matching the final 'y' of
"-Infinity" - so the buffer handed to Double.parseDouble always has
one extra trailing character. "-Infinity" fails to parse in every
context (end-of-input, before a comma, before a closing bracket).
Also noted: JsonWriter.doWriteBinaryData() writes its base64 output
unquoted, so a document containing binary data is not valid JSON on
its own - low-impact since nothing in this codebase parses it back.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QAjfckcY4EJsxranv1RyGV
Two bugs surfaced while adding test coverage for JsonReader/JsonWriter: - SAW_EXPONENT_DIGITS was the only numeric scanner state whose terminator switch omitted an explicit end-of-input case (every sibling state treats EOF the same as a closing delimiter), so a bare exponent-notation number with nothing following it (e.g. a top-level "1e2") threw JsonParseException instead of parsing. - SAW_MINUS_I appended the character it read on every iteration of its "-Infinity" match loop, including the terminator character read immediately after matching the literal's final 'y' - so the buffer handed to Double.parseDouble always carried one extra trailing character, and "-Infinity" failed to parse in every context (end-of-input, before a comma, before a closing bracket). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QAjfckcY4EJsxranv1RyGV
Covers InMemoryMongoBackend and FlapdoodleMongoBackend, which had no test coverage. InMemoryMongoBackend wraps mongo-java-server, an in-JVM, no-download backend safe to run for real in a unit spec: covers name/availability, the persistent-database-dir guard, binding a real server, and - the non-obvious behaviour with no other coverage - that restart() after stop() preserves data, because RetainingMemoryBackend deliberately no-ops close() rather than clearing the backend the way MongoServer.shutdownNow() normally would. FlapdoodleMongoBackend wraps a real mongod child process, so only its non-process-spawning behaviour is unit tested here (name, availability, and the version-string validation that runs before any process starts); starting a real embedded mongod is already exercised by this module's existing functional-style specs (EmbeddedMongoLifecycleSpec, EmbeddedMongoInitializerSpec) and is out of scope for unit coverage. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QAjfckcY4EJsxranv1RyGV
The class's end-to-end contract - a shared MongoDB transaction spanning a GORM save and a Spring Data write, committed/rolled back together, with no resource leaked between transactions - is already proven functionally by UnifiedMongoTransactionSpec against a real replica set. This adds narrow unit coverage for the two branches that spec cannot reach without a second, non-transactional datastore: doBegin skipping the Spring Data resource bind when GORM has no active client session (server-side transactions disabled) or when the current session isn't a Mongo session, and doCleanupAfterCompletion correctly unbinding (or no-op'ing) that resource independently of the superclass's own session-holder cleanup. Both protected methods are exercised directly, as this class's own extension points, from the same package. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QAjfckcY4EJsxranv1RyGV
Covers the four genuine gaps in grails-data-mongodb-core that survived a full-module review (everything else in the module's 67 main classes is either trivial or already exercised by its 135-spec functional/TCK suite): - Distance.inRadians() - had zero test references anywhere; new DistanceSpec covers the radian conversion for every Metric plus valueOf/equality. - MongoIdCoercion - a pure static utility with documented null, missing-mapping, converter-rejection and converter-exception fallback branches, only ever exercised via unrelated happy-path functional specs; new MongoIdCoercionSpec covers every branch. - AbstractMongoConnectionSourceSettings.getUrl()/getDatabase() - the existing MongoConnectionSourceSettingsSpec only exercised the connectionString-supplied branch; added cases for the manually host/port/credential-built URL (including the omit-credentials and omit-port branches) and the databaseName fallback in both methods. - MongoStaticApi.withCollection/useCollection/withDatabase - had no direct test (their sibling useDatabase already did); added to the existing SwitchDatabaseAtRuntimeSpec alongside it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QAjfckcY4EJsxranv1RyGV
There was a problem hiding this comment.
Pull request overview
Expands unit test coverage across the grails-data-mongodb multi-module project (ext/bson/embedded/spring-data/core), and includes a small production fix in the BSON JSON parser (JsonScanner) discovered while adding those tests.
Changes:
- Add comprehensive Spock unit specs for previously-untested MongoDB extension-method and codec/encoder/decoder surfaces.
- Add targeted unit tests for embedded backend behaviors and Spring Data transaction manager branching logic.
- Fix two
JsonScannernumber-parsing edge cases (bare exponent at EOF;-Infinityparsing) with regression tests.
Reviewed changes
Copilot reviewed 33 out of 33 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| grails-data-mongodb/spring-data/src/test/groovy/org/grails/datastore/gorm/mongodb/springdata/GormSharedSessionMongoTransactionManagerSpec.groovy | Adds unit coverage for transaction manager resource binding/cleanup branches. |
| grails-data-mongodb/grails-plugin/src/test/groovy/grails/plugins/mongodb/MongodbGrailsPluginSpec.groovy | Adds unit coverage for plugin descriptor metadata and Spring bean/alias registration behavior. |
| grails-data-mongodb/grails-plugin/build.gradle | Adds test dependencies needed to run new plugin unit specs. |
| grails-data-mongodb/ext/src/test/groovy/org/grails/datastore/gorm/mongo/extensions/MongoExtensionsSpec.groovy | Adds broad unit coverage for Groovy extension-method surface over the MongoDB driver. |
| grails-data-mongodb/ext/build.gradle | Adds test dependencies needed to run new ext module specs. |
| grails-data-mongodb/embedded/src/test/groovy/org/grails/datastore/gorm/mongodb/embedded/InMemoryMongoBackendSpec.groovy | Adds unit coverage for in-memory embedded backend lifecycle and restart semantics. |
| grails-data-mongodb/embedded/src/test/groovy/org/grails/datastore/gorm/mongodb/embedded/FlapdoodleMongoBackendSpec.groovy | Adds unit coverage for flapdoodle backend name/availability and early validation failures. |
| grails-data-mongodb/core/src/test/groovy/org/grails/datastore/mapping/mongo/engine/MongoIdCoercionSpec.groovy | Adds unit coverage for ID coercion behavior and mapping edge cases. |
| grails-data-mongodb/core/src/test/groovy/org/grails/datastore/mapping/mongo/config/MongoConnectionSourceSettingsSpec.groovy | Adds tests for URL/database derivation logic in settings. |
| grails-data-mongodb/core/src/test/groovy/org/grails/datastore/gorm/mongo/SwitchDatabaseAtRuntimeSpec.groovy | Extends functional/TCK-style spec coverage for database/collection switching APIs. |
| grails-data-mongodb/core/src/test/groovy/grails/mongodb/geo/DistanceSpec.groovy | Adds unit tests for geo Distance value semantics. |
| grails-data-mongodb/bson/src/test/groovy/org/grails/datastore/bson/json/JsonWriterSpec.groovy | Adds unit coverage for BSON JSON writer output, including round-trip paths with JsonReader. |
| grails-data-mongodb/bson/src/test/groovy/org/grails/datastore/bson/json/JsonReaderSpec.groovy | Adds unit coverage for BSON JSON reader parsing, including regression cases. |
| grails-data-mongodb/bson/src/test/groovy/org/grails/datastore/bson/codecs/SimpleCodecsSpec.groovy | Adds unit tests for simple codec adapters and BigDecimal/BigInteger codecs. |
| grails-data-mongodb/bson/src/test/groovy/org/grails/datastore/bson/codecs/encoders/TenantIdEncoderSpec.groovy | Adds unit coverage for tenant-id encoder behavior. |
| grails-data-mongodb/bson/src/test/groovy/org/grails/datastore/bson/codecs/encoders/TemporalTypeEncodersSpec.groovy | Adds unit coverage for temporal type encoder adapters. |
| grails-data-mongodb/bson/src/test/groovy/org/grails/datastore/bson/codecs/encoders/SimpleEncoderSpec.groovy | Adds unit coverage for simple type encoder dispatch/encoding behaviors. |
| grails-data-mongodb/bson/src/test/groovy/org/grails/datastore/bson/codecs/encoders/IdentityEncoderSpec.groovy | Adds unit coverage for identity encoder name/type handling, including edge paths. |
| grails-data-mongodb/bson/src/test/groovy/org/grails/datastore/bson/codecs/encoders/EmbeddedEncoderSpec.groovy | Adds unit coverage for embedded association encoding behavior. |
| grails-data-mongodb/bson/src/test/groovy/org/grails/datastore/bson/codecs/encoders/EmbeddedCollectionEncoderSpec.groovy | Adds unit coverage for embedded collection encoding behavior. |
| grails-data-mongodb/bson/src/test/groovy/org/grails/datastore/bson/codecs/encoders/CustomTypeEncoderSpec.groovy | Adds unit coverage for custom type encoding behavior (codec-backed and marshaller-backed). |
| grails-data-mongodb/bson/src/test/groovy/org/grails/datastore/bson/codecs/encoders/BasicCollectionTypeEncoderSpec.groovy | Adds unit coverage for basic collection encoder behavior and dirty-check wrapping. |
| grails-data-mongodb/bson/src/test/groovy/org/grails/datastore/bson/codecs/decoders/TenantIdDecoderSpec.groovy | Adds unit coverage for tenant-id decoder behavior. |
| grails-data-mongodb/bson/src/test/groovy/org/grails/datastore/bson/codecs/decoders/TemporalTypeDecodersSpec.groovy | Adds unit coverage for temporal type decoder adapters. |
| grails-data-mongodb/bson/src/test/groovy/org/grails/datastore/bson/codecs/decoders/SimpleDecoderSpec.groovy | Adds unit coverage for simple type decoder dispatch/decoding behaviors. |
| grails-data-mongodb/bson/src/test/groovy/org/grails/datastore/bson/codecs/decoders/IdentityDecoderSpec.groovy | Adds unit coverage for identity decoder behavior and error paths. |
| grails-data-mongodb/bson/src/test/groovy/org/grails/datastore/bson/codecs/decoders/EmbeddedDecoderSpec.groovy | Adds unit coverage for embedded association decoding behavior. |
| grails-data-mongodb/bson/src/test/groovy/org/grails/datastore/bson/codecs/decoders/EmbeddedCollectionDecoderSpec.groovy | Adds unit coverage for embedded collection decoding behavior. |
| grails-data-mongodb/bson/src/test/groovy/org/grails/datastore/bson/codecs/decoders/CustomTypeDecoderSpec.groovy | Adds unit coverage for custom type decoding behavior (codec-backed and marshaller-backed). |
| grails-data-mongodb/bson/src/test/groovy/org/grails/datastore/bson/codecs/decoders/BasicCollectionTypeDecoderSpec.groovy | Adds unit coverage for basic collection decoding and conversion behavior. |
| grails-data-mongodb/bson/src/test/groovy/org/grails/datastore/bson/codecs/CodecExtensionsSpec.groovy | Adds unit coverage for codec extension provider behavior and BSON conversion helpers. |
| grails-data-mongodb/bson/src/test/groovy/org/grails/datastore/bson/codecs/CodecCustomTypeMarshallerSpec.groovy | Adds unit coverage for codec-backed custom type marshaller behavior and unsupported operations. |
| grails-data-mongodb/bson/src/main/groovy/org/grails/datastore/bson/json/JsonScanner.java | Fixes numeric scanning edge cases and supports exponent termination at EOF. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
🚨 TestLens detected 2 failed tests 🚨Here is what you can do:
Test SummaryCI / Functional Tests (Java 21, indy=true) > :grails-test-examples-scaffolding:integrationTest
Groovy Snapshot Canary Build / Build Grails (shard 2) > :grails-data-mongodb:test
🏷️ Commit: 06fde1d Test FailuresMongodbGrailsPluginSpec > exposes the expected Grails plugin descriptor metadata (:grails-data-mongodb:test in Groovy Snapshot Canary Build / Build Grails (shard 2))
UserControllerSpec > User list (:grails-test-examples-scaffolding:integrationTest in CI / Functional Tests (Java 21, indy=true))Rerun ControlsSelect tests to mute in this pull request:
Reuse successful test results:
Click the checkbox to trigger a rerun:
Learn more about TestLens at testlens.app/docs. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## 8.1.x #16285 +/- ##
===================================================
- Coverage 53.4149% 29.8834% -23.5315%
+ Complexity 19459 509 -18950
===================================================
Files 2081 79 -2002
Lines 98993 4715 -94278
Branches 17361 814 -16547
===================================================
- Hits 52877 1409 -51468
+ Misses 38566 3063 -35503
+ Partials 7550 243 -7307 🚀 New features to boost your workflow:
|
Summary
Staged pass adding unit test coverage across previously-untested surface in
grails-data-mongodb, identified via a jacoco coverage report plus a systematic gap analysis of every submodule:ext:MongoExtensions(the module's only class - Map/Bson conversion and the full Groovy extension-method surface over the MongoDB driver)bson: allcodecs/*Codec,codecs/decoders/*,codecs/encoders/*,CodecCustomTypeMarshaller,CodecExtensions, and the hand-writtenjson/parser (JsonReader/JsonWriter)embedded:InMemoryMongoBackendandFlapdoodleMongoBackendspring-data:GormSharedSessionMongoTransactionManagercore: targeted gaps (Distance,MongoIdCoercion,AbstractMongoConnectionSourceSettings,MongoStaticApi) not already covered by the module's large functional/TCK suiteTwo real
JsonScannerparsing bugs turned up while writing the json-parser tests (bare exponent-notation numbers at end-of-input,-Infinityfailing to parse in any context), and are fixed here too (same production change and regression test as #16284, which backports the fix to7.0.xsince the bug is present there as well).Test plan
ext,bson,embedded,spring-data,core), verified individually and togethercodeStyleclean for every touched module🤖 Generated with Claude Code
https://claude.ai/code/session_01QAjfckcY4EJsxranv1RyGV