Skip to content

Commit bc17fef

Browse files
jquirkeclaude
andcommitted
Add comprehensive tests for soft value groups with map consumption
Soft value groups only contain values from already-executed constructors, without triggering additional constructor execution. This commit adds comprehensive testing to ensure this behavior works correctly with the new map value group consumption feature. **Tests Added:** - Soft map providers not called when only consuming soft groups - Soft maps correctly contain values from executed constructors - Soft maps exclude values from non-executed constructors - Soft map consumption works alongside regular slice consumption - Soft map and regular map consumption produce equivalent results **Key Findings:** - Soft value groups work correctly with map consumption - Same execution semantics as slice consumption (only executed providers) - Map and slice soft consumption behavior is consistent - No additional implementation needed - existing soft group logic handles maps **Coverage:** This completes testing of soft groups with map value groups, ensuring the feature works reliably across all consumption patterns. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 29b6d41 commit bc17fef

File tree

2 files changed

+117
-2
lines changed

2 files changed

+117
-2
lines changed

CLAUDE.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,10 @@ This is the Uber dig dependency injection framework. Recent work added map value
5555
- Risk: May return wrong types or lose map key information
5656
- Documentation: See `DECORATION_TEST_GAPS.md`
5757

58-
2. **Soft Groups + Maps** (MEDIUM PRIORITY)
59-
- Behavior verification needed for soft group consumption as maps
58+
2. **Soft Groups + Maps** ✅ COMPLETED
59+
- Verified soft group consumption as maps works correctly
60+
- Added comprehensive tests covering all soft map scenarios
61+
- Tests cover: empty soft maps, executed vs non-executed providers, mixed consumption
6062

6163
## Code Patterns & Usage
6264

dig_test.go

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1829,6 +1829,119 @@ func TestGroups(t *testing.T) {
18291829
})
18301830
})
18311831

1832+
t.Run("soft map value groups", func(t *testing.T) {
1833+
t.Run("soft map provider not called when only soft group consumed", func(t *testing.T) {
1834+
c := digtest.New(t)
1835+
1836+
type Result struct {
1837+
dig.Out
1838+
Value string `name:"val1" group:"handlers"`
1839+
}
1840+
1841+
// This provider should NOT be called because we're only consuming
1842+
// the soft group and there are no other dependencies forcing it to run
1843+
c.RequireProvide(func() (Result, int) {
1844+
require.FailNow(t, "this function should not be called for soft map groups")
1845+
return Result{Value: "should not see this"}, 42
1846+
})
1847+
1848+
type SoftMapConsumer struct {
1849+
dig.In
1850+
Handlers map[string]string `group:"handlers,soft"`
1851+
}
1852+
1853+
c.RequireInvoke(func(p SoftMapConsumer) {
1854+
assert.Empty(t, p.Handlers, "soft map group should be empty when no providers executed")
1855+
})
1856+
})
1857+
1858+
t.Run("soft map gets values from already-executed constructors", func(t *testing.T) {
1859+
c := digtest.New(t)
1860+
1861+
type HandlerResult struct {
1862+
dig.Out
1863+
Handler string `name:"handler1" group:"handlers"`
1864+
Service int // This forces the constructor to run
1865+
}
1866+
1867+
// This provider will be called because we need the int service
1868+
c.RequireProvide(func() HandlerResult {
1869+
return HandlerResult{
1870+
Handler: "executed_handler",
1871+
Service: 100,
1872+
}
1873+
})
1874+
1875+
// Additional provider that won't be executed
1876+
type UnexecutedResult struct {
1877+
dig.Out
1878+
Handler string `name:"handler2" group:"handlers"`
1879+
}
1880+
c.RequireProvide(func() UnexecutedResult {
1881+
require.FailNow(t, "this should not be called")
1882+
return UnexecutedResult{Handler: "never_called"}
1883+
})
1884+
1885+
type ConsumerParams struct {
1886+
dig.In
1887+
Service int // This triggers the first provider
1888+
SoftHandlerMap map[string]string `group:"handlers,soft"`
1889+
SoftHandlerSlice []string `group:"handlers,soft"`
1890+
}
1891+
1892+
c.RequireInvoke(func(p ConsumerParams) {
1893+
assert.Equal(t, 100, p.Service)
1894+
1895+
// Soft map should only contain the handler from the executed constructor
1896+
assert.Len(t, p.SoftHandlerMap, 1)
1897+
assert.Equal(t, "executed_handler", p.SoftHandlerMap["handler1"])
1898+
assert.NotContains(t, p.SoftHandlerMap, "handler2")
1899+
1900+
// Verify slice consumption works the same way
1901+
assert.Len(t, p.SoftHandlerSlice, 1)
1902+
assert.Equal(t, "executed_handler", p.SoftHandlerSlice[0])
1903+
})
1904+
})
1905+
1906+
t.Run("soft map combined with regular map consumption", func(t *testing.T) {
1907+
c := digtest.New(t)
1908+
1909+
type ServiceResult struct {
1910+
dig.Out
1911+
Service string `name:"service1" group:"services"`
1912+
Config int // Forces execution
1913+
}
1914+
1915+
c.RequireProvide(func() ServiceResult {
1916+
return ServiceResult{
1917+
Service: "auth_service",
1918+
Config: 42,
1919+
}
1920+
})
1921+
1922+
type MultiConsumerParams struct {
1923+
dig.In
1924+
Config int // Triggers provider
1925+
SoftServices map[string]string `group:"services,soft"`
1926+
RegularServices map[string]string `group:"services"`
1927+
}
1928+
1929+
c.RequireInvoke(func(p MultiConsumerParams) {
1930+
assert.Equal(t, 42, p.Config)
1931+
1932+
// Both should have the same content since the provider was executed
1933+
assert.Len(t, p.SoftServices, 1)
1934+
assert.Equal(t, "auth_service", p.SoftServices["service1"])
1935+
1936+
assert.Len(t, p.RegularServices, 1)
1937+
assert.Equal(t, "auth_service", p.RegularServices["service1"])
1938+
1939+
// They should be equivalent
1940+
assert.Equal(t, p.SoftServices, p.RegularServices)
1941+
})
1942+
})
1943+
})
1944+
18321945
t.Run("map value group using dig.Name and dig.Group", func(t *testing.T) {
18331946
c := digtest.New(t, dig.SetRand(rand.New(rand.NewSource(0))))
18341947

0 commit comments

Comments
 (0)