Skip to content

test: enhance unit test coverage and edge case handling across core conversion packages - #367

Open
lusains wants to merge 6 commits into
spf13:masterfrom
lusains:test/improve-coverage-edge-cases
Open

lusains wants to merge 6 commits into
spf13:masterfrom
lusains:test/improve-coverage-edge-cases

Conversation

@lusains

@lusains lusains commented Oct 1, 2026 •

Copy link
Copy Markdown

PR: test: enhance unit test coverage and edge case handling across core conversion packages


1. Motivation & Executive Summary

1.1 Motivation

spf13/cast is one of the most widely used Go utility libraries across the Go ecosystem, providing seamless and safe type casting between arbitrary Go data types. In mission-critical applications, type casting reliability and graceful error handling under extreme boundary conditions (e.g. integer overflows, floating-point parsing anomalies, malformed timestamps, pointer indirections, reflection error paths) are paramount.

Prior to this pull request:

  • The total statement test coverage of the repository was 87.6%.
  • The internal package had no test files at all (0.0% coverage).
  • 14 core type conversion functions in time.go and zz_generated.go had 0.0% coverage (e.g. StringToDate, ToTimeInDefaultLocation, ToInt8SliceE through ToUint64SliceE, ToFloat32SliceE).
  • Critical error paths and edge cases in cast.go, number.go, and map.go (such as cast.Must panics, float64Provider/float64EProvider error propagation, negative time casting to unsigned numbers, and map[any]T reflection error handling) were untested.

1.2 Executive Summary

This PR delivers a comprehensive, zero-intrusion test suite blitz:

  • Zero Production Code Intrusion: Absolutely zero lines of production code were altered. Every modification is strictly contained within new or enhanced *_test.go files (git diff origin/master --name-only | grep -v "_test\.go" returns empty).
  • Coverage Surge: Total statement test coverage surged from 87.6% to 99.1% (+11.5% overall gain). The internal package reached 100.0% statement coverage, and cast.go reached 100.0%.
  • Atomic Git Commit History: Changes are organized into 6 clean, semantic, atomic commits following the Conventional Commits specification.
  • Concurrency & Race Freedom: All test suites execute cleanly under go test -v -race ./... with zero race conditions, zero flakes, and zero warnings.
  • Static Analysis Compliance: Passes golangci-lint run ./..., go vet ./..., and gofmt -d . with 0 issues.

2. Test Coverage Comparison

2.1 Overall & Package-Level Coverage Comparison

Package Baseline Statement Coverage Final Statement Coverage Absolute Gain
github.com/spf13/cast 90.0% 99.1% +9.1%
github.com/spf13/cast/internal 0.0% 100.0% +100.0%
Total Repository 87.6% (523/595) 99.1% (590/595) +11.5%

2.2 Per-File Statement Coverage Comparison

Source File Baseline Statements Final Statements Baseline % Final % Coverage Delta
alias.go 24 / 24 24 / 24 100.0% 100.0% +0.0%
basic.go 54 / 55 55 / 55 98.2% 100.0% +1.8%
cast.go 19 / 28 28 / 28 67.9% 100.0% +32.1%
indirect.go 10 / 10 10 / 10 100.0% 100.0% +0.0%
internal/time.go 0 / 11 11 / 11 0.0% 100.0% +100.0%
internal/timeformattype_string.go 0 / 3 3 / 3 0.0% 100.0% +100.0%
map.go 83 / 88 87 / 88 94.3% 98.9% +4.5%
number.go 191 / 220 218 / 220 86.8% 99.1% +12.3%
slice.go 41 / 42 41 / 42 97.6% 97.6% +0.0%
time.go 35 / 38 37 / 38 92.1% 97.4% +5.3%
zz_generated.go 66 / 76 76 / 76 86.8% 100.0% +13.2%
Total 523 / 595 590 / 595 87.6% 99.1% +11.5%

Note on Remaining Statements: The 5 uncovered statements in the codebase correspond to provably unreachable defensive code paths in production files:

  1. map.go:136: fn := func(i any) any { return i } in ToStringMapE (unreachable due to prior type switch branches in toMapE).
  2. number.go:76-78: default: in ToNumberE (unreachable; Number constraint is a closed union of 12 types, all handled).
  3. number.go:402-404: default: in parseNumber (unreachable for the same reason).
  4. slice.go:103: default: in ToStringSliceE (toSliceEOk handles nil, and case any: matches all non-nil values).
  5. time.go:72-75: error return on ToInt64E(s) for Go integer primitives (unreachable; ToInt64E always succeeds on Go integer primitives).

2.3 Under-Covered Functions Elevated

Function File Baseline Coverage Final Coverage Status
internal.HasTimezone internal/time.go:25 0.0% 100.0% Fully Covered
internal.ParseDateWith internal/time.go:59 0.0% 100.0% Fully Covered
internal.TimeFormatType.String internal/timeformattype_string.go:22 0.0% 100.0% Fully Covered
cast.StringToDate time.go:107 0.0% 100.0% Fully Covered
cast.ToTimeInDefaultLocation zz_generated.go:26 0.0% 100.0% Fully Covered
cast.ToInt8SliceE zz_generated.go:209 0.0% 100.0% Fully Covered
cast.ToInt16SliceE zz_generated.go:214 0.0% 100.0% Fully Covered
cast.ToInt32SliceE zz_generated.go:219 0.0% 100.0% Fully Covered
cast.ToUint8SliceE zz_generated.go:234 0.0% 100.0% Fully Covered
cast.ToUint16SliceE zz_generated.go:239 0.0% 100.0% Fully Covered
cast.ToUint32SliceE zz_generated.go:244 0.0% 100.0% Fully Covered
cast.ToUint64SliceE zz_generated.go:249 0.0% 100.0% Fully Covered
cast.ToFloat32SliceE zz_generated.go:254 0.0% 100.0% Fully Covered
cast.ToE cast.go:22 65.2% 100.0% Fully Covered
cast.Must cast.go:71 66.7% 100.0% Fully Covered
cast.toNumberE number.go:138 65.5% 100.0% Fully Covered
cast.toUnsignedNumber number.go:195 95.0% 100.0% Fully Covered
cast.toUnsignedNumberE number.go:278 61.1% 100.0% Fully Covered
cast.trimZeroDecimal number.go:507 87.5% 100.0% Fully Covered
cast.toStringMapIntE map.go:141 84.6% 100.0% Fully Covered
cast.ToDurationE time.go:64 88.9% 94.4% Error Branch Covered
cast.ToBoolE basic.go:17 96.0% 100.0% Fully Covered

3. Detailed Test Inventory Table

Test Function Location Tested Target & Scope Key Scenarios Covered
TestTimeFormatHasTimezone internal/time_test.go internal.TimeFormat.HasTimezone All 5 enum formats, negative & upper bound out-of-range checks, and iteration across all 24 predefined formats in internal.TimeFormats.
TestTimeFormatTypeString internal/time_test.go internal.TimeFormatType.String Valid constants (NoTimezone through TimeOnly), out-of-bounds inputs (-1, 5, 100).
TestParseDateWith internal/time_test.go internal.ParseDateWith Nil location fallback to time.Local, custom location (America/New_York), timezone offset preservation, kitchen time format, multi-format fallback, parse failure on unmatched format, empty format slice, empty input string.
TestStringToDate time_test.go cast.StringToDate Valid date string formats in UTC (RFC1123Z, ISO date, RFC3339, numeric offset), invalid date format error, empty string error.
TestToTimeInDefaultLocation time_test.go cast.ToTimeInDefaultLocation Parsing with UTC location, custom timezone location (America/New_York), and invalid inputs returning zero time time.Time{}.
TestDurationProvidersAndEdgeCases time_test.go cast.ToDurationE float64Provider success, float64EProvider success and error paths, negative string durations ("-500", "-5s"), invalid duration strings, type alias fallback, struct fallback, negative numeric types.
TestTimeEdgeCases time_test.go cast.ToTimeE json.Number zero decimal trimming ("1482597504.000000"), invalid json.Number, negative integer timestamps, pointer indirection (*int, *json.Number, nil pointer), unsupported types.
TestTimeConcurrentSafety time_test.go cast time conversions 50 concurrent goroutines x 100 iterations verifying race safety across ToTimeE, ToDurationE, StringToDate, and ToTimeInDefaultLocation.
runSliceETests slice_test.go Generic slice test helper Parallel execution of value and pointer indirection test cases for all generic basic slice types.
TestInt8Slice slice_test.go cast.ToInt8SliceE Value, pointer, interface slice, cross-type (int, float64, bool, string), 64-bit bounds overflow/underflow, nil, and struct errors.
TestInt16Slice slice_test.go cast.ToInt16SliceE Value, pointer, interface slice, cross-type (int, float64, bool, string), 64-bit bounds overflow/underflow, nil, and struct errors.
TestInt32Slice slice_test.go cast.ToInt32SliceE Value, pointer, interface slice, cross-type (int, float64, bool, string), 64-bit bounds overflow/underflow, nil, and struct errors.
TestUint8Slice slice_test.go cast.ToUint8SliceE Value, pointer, interface slice, cross-type, negative value/string rejection (-1), 64-bit overflow, nil, and struct errors.
TestUint16Slice slice_test.go cast.ToUint16SliceE Value, pointer, interface slice, cross-type, negative value/string rejection (-1), 64-bit overflow, nil, and struct errors.
TestUint32Slice slice_test.go cast.ToUint32SliceE Value, pointer, interface slice, cross-type, negative value/string rejection (-1), 64-bit overflow, nil, and struct errors.
TestUint64Slice slice_test.go cast.ToUint64SliceE Value, pointer, interface slice, cross-type, negative value/string rejection (-1), overflow, nil, and struct errors.
TestFloat32Slice slice_test.go cast.ToFloat32SliceE Value, pointer, interface slice, cross-type (int, float64, bool, string, array), nil, and struct errors.
TestStringMapInt (enhanced) map_test.go cast.ToStringMapIntE, cast.toStringMapIntE map[any]int (case map[any]T:) fast path, reflection error propagation on non-castable value (map[string]string{"v1": "foo"}).
TestStringMapInt64 (enhanced) map_test.go cast.ToStringMapInt64E, cast.toStringMapIntE map[any]int64 (case map[any]T:) fast path, reflection error propagation on non-castable value.
TestNumber (enhanced) number_test.go cast.To[T], cast.ToE[T] Subtests ToBasic and ToBasicE exercising all 12 basic numeric types for both direct values and pointer dereferences.
TestFloat64Providers_Public number_test.go cast.toNumberE, cast.toUnsignedNumberE mockFloat64Provider and mockFloat64EProvider on float conversions, non-float type mismatch errors, and provider error propagation.
TestToUnsignedNumber_NegativeTime number_test.go cast.toUnsignedNumber Negative time.Weekday(-1) and negative time.Month(-1) error rejection across all unsigned types (uint, uint8, uint16, uint32, uint64).
TestJSONNumber_Errors number_test.go cast.toNumberE, basic.go:ToBoolE json.Number("not_a_valid_number") error propagation on integer and float conversions, and basic.go:59 error handling in ToBoolE.
TestNumber_FormatsAndOverflow number_test.go cast.parseNumber, cast.trimDecimal Hex (0x10, -0x10), octal (0o77, 077), binary (0b1010), float overflow (1e40, 1e309), hex float notation (0x10p0), whitespace rejection, decimal trim special cases (.5, +8., +0.25, -0.25).
TestPointersAndInterfaces number_test.go cast.indirect, cast.ToStringE Nil pointers (*int, *uint, *float64, *string, *bool), multi-level pointers (***int), multi-level nil pointers, fmt.Stringer interface, and error interface.
TestTrimZeroDecimal_Additional number_internal_test.go cast.trimZeroDecimal Internal fallback branch for strings without trailing zeros or decimals ("", "0", "000", ".", "123", "abc").
TestToUnsignedNumberE_Float64Providers number_internal_test.go cast.toUnsignedNumberE Internal provider handling for unsigned casting with positive, negative, and provider error results.
TestMust cast_test.go cast.Must[T] Valid values pass through (int, string, bool), and panic assertions on non-nil error values.
TestToAndToE_AllBasicTypes cast_test.go cast.To[T], cast.ToE[T] Direct testing of cast.To[T] and cast.ToE[T] for all 16 supported types (string, bool, int8..int64, uint8..uint64, float32, float64, time.Time, time.Duration) across both success and failure cases.

4. Concurrency & Race Detector Verification

The entire repository test suite was executed under Go's race detector (-race) without caching:

go test -count=1 -race ./...

Verification Output:

ok  	github.com/spf13/cast	2.299s
ok  	github.com/spf13/cast/internal	1.596s

All 50-goroutine concurrent stress tests (TestTimeConcurrentSafety) and extensive parallel subtests (slice_test.go, number_test.go, cast_test.go) ran without race conditions or memory anomalies.

  • Total Data Races Detected: 0
  • Test Failures: 0
  • Flaky Tests: 0

5. CI Quality & Linter Verification

Static analysis and linting checks were executed across the entire repository:

5.1 Formatting Check (gofmt -d .)

$ gofmt -d .
# Clean output (0 diff)

5.2 Go Vet (go vet ./...)

$ go vet ./...
# Clean output (exit code 0, 0 issues)

5.3 GolangCI-Lint (golangci-lint run ./...)

$ golangci-lint run ./...
0 issues.

6. Zero Production Code Modification Attestation

This contribution adheres strictly to the zero production code modification constraint.
Only test files (*_test.go) have been added or updated.

Verification Command:

$ git diff origin/master --name-only | grep -v "_test\.go"
# Output: (empty)

List of Modified / Added Files on Branch:

$ git diff origin/master --name-only
cast_test.go
internal/time_test.go
map_test.go
number_internal_test.go
number_test.go
slice_test.go
time_test.go

Total production .go files modified: 0


7. Atomic Git Commit History

The commits on test/improve-coverage-edge-cases follow the Conventional Commits specification:

54c6ee5 test(cast): add generic To, ToE, and Must panic tests
1a36c3e test(number): add edge case, overflow, provider, and error tests for number casting
8e6e677 test(map): add tests for generic map types and error branches in string map casting
344ba98 test(slice): add unit tests for missing slice conversions and boundary error cases
6e70aeb test(time): add edge case, provider, and concurrency tests for time conversions
54803a9 test(internal): add comprehensive unit tests for time parsing and timezone detection

Each commit is isolated, modular, compile-clean, and independently verifiable.

@CLAassistant

CLAassistant commented Oct 1, 2026 •

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@lusains
lusains force-pushed the test/improve-coverage-edge-cases branch from 761c964 to 54c6ee5 Compare October 1, 2026 03:46
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.

2 participants