Description
Introduce a FloatStrategy so that users can constrain the range of randomly generated Float values during fixture generation. Today, FloatResolver (src/main/kotlin/dev/appoutlet/some/resolver/FloatResolver.kt:9) always calls random.nextFloat(), producing values in [0.0, 1.0). There is no way for users to widen or shift that range, or to pin a value, without writing a custom resolver or factory.
This follows the same pattern as CollectionStrategy (src/main/kotlin/dev/appoutlet/some/config/CollectionStrategy.kt:7): a data class wrapping a ClosedFloatingPointRange<Float> with input validation, a companion object default, and a corresponding resolver update that consumes the active strategy via StrategyProvider. The default range is 0.0f..1.0f to preserve the current behavior of FloatResolver and avoid a breaking change for existing users.
A secondary constructor FloatStrategy(fixed: Float) is provided as a convenience for callers who want to pin generation to a single value; it delegates to the primary constructor with range = fixed..fixed.
Scope
- Add
FloatStrategy in dev.appoutlet.some.config (new file, one class per file) with:
- Primary constructor
FloatStrategy(range: ClosedFloatingPointRange<Float> = 0.0f..1.0f).
- Secondary constructor
FloatStrategy(fixed: Float) that calls the primary constructor with range = fixed..fixed.
- Validation that throws
IllegalArgumentException when the range start is greater than the range end (zero-width ranges are allowed).
companion object with default: FloatStrategy returning FloatStrategy().
- Update
FloatResolver to accept a StrategyProvider and produce values within the configured range using the same approach as ListResolver (src/main/kotlin/dev/appoutlet/some/resolver/ListResolver.kt:21). For a zero-width range, the resolver returns the fixed value directly.
- Register
FloatStrategy::class to FloatStrategy.default in SomeConfig.defaultStrategies() (src/main/kotlin/dev/appoutlet/some/config/SomeConfig.kt:194).
- Update
FloatResolver wiring in SomeConfig to pass the StrategyProvider to the constructor.
- Add unit tests for
FloatStrategy (default range, validation of inverted ranges, zero-width behavior, secondary constructor equivalence) and updated tests for FloatResolver.
- Update KDoc on
Strategy and SomeConfigBuilder to list FloatStrategy alongside the other built-in strategies.
Out of scope: an equivalent DoubleStrategy (can be a follow-up), changing the default range away from 0.0f..1.0f, and changing behavior of any other primitive resolvers.
Acceptance criteria
Description
Introduce a
FloatStrategyso that users can constrain the range of randomly generatedFloatvalues during fixture generation. Today,FloatResolver(src/main/kotlin/dev/appoutlet/some/resolver/FloatResolver.kt:9) always callsrandom.nextFloat(), producing values in[0.0, 1.0). There is no way for users to widen or shift that range, or to pin a value, without writing a custom resolver or factory.This follows the same pattern as
CollectionStrategy(src/main/kotlin/dev/appoutlet/some/config/CollectionStrategy.kt:7): a data class wrapping aClosedFloatingPointRange<Float>with input validation, acompanion object default, and a corresponding resolver update that consumes the active strategy viaStrategyProvider. The default range is0.0f..1.0fto preserve the current behavior ofFloatResolverand avoid a breaking change for existing users.A secondary constructor
FloatStrategy(fixed: Float)is provided as a convenience for callers who want to pin generation to a single value; it delegates to the primary constructor withrange = fixed..fixed.Scope
FloatStrategyindev.appoutlet.some.config(new file, one class per file) with:FloatStrategy(range: ClosedFloatingPointRange<Float> = 0.0f..1.0f).FloatStrategy(fixed: Float)that calls the primary constructor withrange = fixed..fixed.IllegalArgumentExceptionwhen the range start is greater than the range end (zero-width ranges are allowed).companion objectwithdefault: FloatStrategyreturningFloatStrategy().FloatResolverto accept aStrategyProviderand produce values within the configured range using the same approach asListResolver(src/main/kotlin/dev/appoutlet/some/resolver/ListResolver.kt:21). For a zero-width range, the resolver returns the fixed value directly.FloatStrategy::class to FloatStrategy.defaultinSomeConfig.defaultStrategies()(src/main/kotlin/dev/appoutlet/some/config/SomeConfig.kt:194).FloatResolverwiring inSomeConfigto pass theStrategyProviderto the constructor.FloatStrategy(default range, validation of inverted ranges, zero-width behavior, secondary constructor equivalence) and updated tests forFloatResolver.StrategyandSomeConfigBuilderto listFloatStrategyalongside the other built-in strategies.Out of scope: an equivalent
DoubleStrategy(can be a follow-up), changing the default range away from0.0f..1.0f, and changing behavior of any other primitive resolvers.Acceptance criteria
SomeConfigwith no customFloatStrategyregisteredWHEN
some<Float>()is invokedTHEN every generated value is a
Floatin the range0.0f..1.0f(exclusive end), matching the currentrandom.nextFloat()behavior — no compatibility break for existing users.strategy(FloatStrategy(range = 0.0f..10.0f))in theirsomeSetup { }blockWHEN
some<Float>()is invoked repeatedlyTHEN every generated value lies inside
0.0f..10.0fand the range is honored end-to-end (exclusive end, matchingrandom.nextDouble(from, until)semantics).strategy(FloatStrategy(fixed = 5.0f))in theirsomeSetup { }blockWHEN
some<Float>()is invoked repeatedlyTHEN every generated value equals
5.0f, and this is equivalent tostrategy(FloatStrategy(range = 5.0f..5.0f)).FloatStrategy(range = 5.0f..5.0f)(zero-width)WHEN the strategy is consumed by
FloatResolverTHEN it always returns
5.0fand no exception is thrown.strategy(FloatStrategy(range = 5.0f..2.0f))(inverted)WHEN the strategy is constructed
THEN it throws an
IllegalArgumentExceptionwith a descriptive message, matching the validation style ofCollectionStrategy(src/main/kotlin/dev/appoutlet/some/config/CollectionStrategy.kt:12).FloatResolverTestWHEN the new tests are added
THEN the test class verifies: (a) the resolver accepts
Floatand rejectsDouble/Int, (b) generated values respect the activeFloatStrategyrange, (c) the default range produces values in0.0f..1.0f, and (d) afixedstrategy always returns the pinned value.Strategy.ktandSomeConfigBuilder.ktWHEN this change is merged
THEN both files reference
FloatStrategyin their "built-in strategies" lists andSomeConfigBuilderexposes astrategy(FloatStrategy(...))example mirroring theCollectionStrategy(5..10)example.