@@ -1769,6 +1769,44 @@ func TestCacheMiddleware_CacheHitAndCacheMiss_RealK8s(t *testing.T) {
17691769 assert .Equal (t , "true" , secondFromCache , "second request should be from cache" )
17701770}
17711771
1772+ // Integration test to verify CacheMiddleware does not cause excessive goroutine usage with a real Kubernetes API server.
1773+ func TestCacheMiddleware_GoRoutines (t * testing.T ) {
1774+ if os .Getenv ("HEADLAMP_RUN_INTEGRATION_TESTS" ) != strconv .FormatBool (istrue ) {
1775+ t .Skip ("skipping integration test" )
1776+ }
1777+
1778+ start := runtime .NumGoroutine ()
1779+
1780+ c , clusterName := newRealK8sHeadlampConfig (t )
1781+ handler := createHeadlampHandler (c )
1782+ ts := httptest .NewServer (handler )
1783+ defer ts .Close ()
1784+
1785+ apiPath := "/clusters/" + clusterName + "/api/v1/pods"
1786+ ctx := context .Background ()
1787+
1788+ _ , err := httpRequestWithContext (ctx , ts .URL + apiPath , "GET" )
1789+ require .NoError (t , err )
1790+
1791+ time .Sleep (3 * time .Second )
1792+
1793+ running := runtime .NumGoroutine ()
1794+
1795+ time .Sleep (3 * time .Second )
1796+
1797+ after := runtime .NumGoroutine ()
1798+
1799+ expected := false
1800+ afterExpected := false
1801+ if (running >= 50 && running < 200 ) && (after >= 50 && after < 200 ) {
1802+ expected = true
1803+ afterExpected = true
1804+ }
1805+ assert .Equal (t , 3 , start )
1806+ assert .Equal (t , running >= 50 && running < 200 , expected )
1807+ assert .Equal (t , after >= 50 && after < 200 , afterExpected )
1808+ }
1809+
17721810// TestCacheMiddleware_CacheInvalidation_RealK8s tests cache invalidation with a
17731811// real Kubernetes API server. Creates a ConfigMap, invalidates via DELETE, then
17741812// verifies the next GET fetches fresh data. Requires HEADLAMP_RUN_INTEGRATION_TESTS=true
0 commit comments