hunk.code_window: export function StackContainersDatatable({ environment, stackName }: Props) {
     const settings = useStore();
     const columns = useColumns(false);
     const hidableColumns = _.compact(
       columns.filter((col) => col.canHide).map((col) => col.id)
     );
  
hunk.labels: keep keep keep replace keep keep keep
hunk.after_edit:   const isGPUsColumnVisible = useShowGPUsColumn(environment.Id);
     const columns = useColumns(false, isGPUsColumnVisible);
  
hunk.file_path: app/react/docker/stacks/ItemView/StackContainersDatatable.tsx
hunk.type: replace
file: package apikey
  
  import (
  	"testing"
  
  	portainer "github.com/portainer/portainer/api"
  	"github.com/stretchr/testify/assert"
  )
  
  func Test_apiKeyCacheGet(t *testing.T) {
  	is := assert.New(t)
  
  	keyCache := NewAPIKeyCache(10)
  
  	// pre-populate cache
  	keyCache.cache.Add(string("foo"), entry{user: portainer.User{}, apiKey: portainer.APIKey{}})
  	keyCache.cache.Add(string(""), entry{user: portainer.User{}, apiKey: portainer.APIKey{}})
  
  	tests := []struct {
  		digest []byte
  		found  bool
  	}{
  		{
  			digest: []byte("foo"),
  			found:  true,
  		},
  		{
  			digest: []byte(""),
  			found:  true,
  		},
  		{
  			digest: []byte("bar"),
  			found:  false,
  		},
  	}
  
  	for _, test := range tests {
  		t.Run(string(test.digest), func(t *testing.T) {
  			_, _, found := keyCache.Get(test.digest)
  			is.Equal(test.found, found)
  		})
  	}
  }
  
  func Test_apiKeyCacheSet(t *testing.T) {
  	is := assert.New(t)
  
  	keyCache := NewAPIKeyCache(10)
  
  	// pre-populate cache
  	keyCache.Set([]byte("bar"), portainer.User{ID: 2}, portainer.APIKey{})
  	keyCache.Set([]byte("foo"), portainer.User{ID: 1}, portainer.APIKey{})
  
  	// overwrite existing entry
  	keyCache.Set([]byte("foo"), portainer.User{ID: 3}, portainer.APIKey{})
  
  	val, ok := keyCache.cache.Get(string("bar"))
  	is.True(ok)
  
  	tuple := val.(entry)
  	is.Equal(portainer.User{ID: 2}, tuple.user)
  
  	val, ok = keyCache.cache.Get(string("foo"))
  	is.True(ok)
  
  	tuple = val.(entry)
  	is.Equal(portainer.User{ID: 3}, tuple.user)
  }
  
  func Test_apiKeyCacheDelete(t *testing.T) {
  	is := assert.New(t)
  
  	keyCache := NewAPIKeyCache(10)
  
  	t.Run("Delete an existing entry", func(t *testing.T) {
  		keyCache.cache.Add(string("foo"), entry{user: portainer.User{ID: 1}, apiKey: portainer.APIKey{}})
  		keyCache.Delete([]byte("foo"))
  
  		_, ok := keyCache.cache.Get(string("foo"))
  		is.False(ok)
  	})
  
  	t.Run("Delete a non-existing entry", func(t *testing.T) {
  		nonPanicFunc := func() { keyCache.Delete([]byte("non-existent-key")) }
  		is.NotPanics(nonPanicFunc)
  	})
  }
  
  func Test_apiKeyCacheLRU(t *testing.T) {
  	is := assert.New(t)
  
  	tests := []struct {
  		name        string
  		cacheLen    int
  		key         []string
  		foundKeys   []string
  		evictedKeys []string
  	}{
  		{
  			name:        "Cache length is 1, add 2 keys",
  			cacheLen:    1,
  			key:         []string{"foo", "bar"},
  			foundKeys:   []string{"bar"},
  			evictedKeys: []string{"foo"},
  		},
  		{
  			name:        "Cache length is 1, add 3 keys",
  			cacheLen:    1,
  			key:         []string{"foo", "bar", "baz"},
  			foundKeys:   []string{"baz"},
  			evictedKeys: []string{"foo", "bar"},
  		},
  		{
  			name:        "Cache length is 2, add 3 keys",
  			cacheLen:    2,
  			key:         []string{"foo", "bar", "baz"},
  			foundKeys:   []string{"bar", "baz"},
  			evictedKeys: []string{"foo"},
  		},
  		{
  			name:        "Cache length is 2, add 4 keys",
  			cacheLen:    2,
  			key:         []string{"foo", "bar", "baz", "qux"},
  			foundKeys:   []string{"baz", "qux"},
  			evictedKeys: []string{"foo", "bar"},
  		},
  	}
  
  	for _, test := range tests {
  		t.Run(test.name, func(t *testing.T) {
  			keyCache := NewAPIKeyCache(test.cacheLen)
  
  			for _, key := range test.key {
  				keyCache.Set([]byte(key), portainer.User{ID: 1}, portainer.APIKey{})
  			}
  
  			for _, key := range test.foundKeys {
  				_, _, found := keyCache.Get([]byte(key))
  				is.True(found, "Key %s not found", key)
  			}
  
  			for _, key := range test.evictedKeys {
  				_, _, found := keyCache.Get([]byte(key))
  				is.False(found, "key %s should have been evicted", key)
  			}
  		})
  	}
  }
  
  func Test_apiKeyCacheInvalidateUserKeyCache(t *testing.T) {
  	is := assert.New(t)
  
  	keyCache := NewAPIKeyCache(10)
  
  	t.Run("Removes users keys from cache", func(t *testing.T) {
  		keyCache.cache.Add(string("foo"), entry{user: portainer.User{ID: 1}, apiKey: portainer.APIKey{}})
  
  		ok := keyCache.InvalidateUserKeyCache(1)
  		is.True(ok)
  
  		_, ok = keyCache.cache.Get(string("foo"))
  		is.False(ok)
  	})
  
  	t.Run("Does not affect other keys", func(t *testing.T) {
  		keyCache.cache.Add(string("foo"), entry{user: portainer.User{ID: 1}, apiKey: portainer.APIKey{}})
  		keyCache.cache.Add(string("bar"), entry{user: portainer.User{ID: 2}, apiKey: portainer.APIKey{}})
  
  		ok := keyCache.InvalidateUserKeyCache(1)
  		is.True(ok)
  
  		ok = keyCache.InvalidateUserKeyCache(1)
  		is.False(ok)
  
  		_, ok = keyCache.cache.Get(string("foo"))
  		is.False(ok)
  
  		_, ok = keyCache.cache.Get(string("bar"))
  		is.True(ok)
  	})
  }
  