package inmemory import ( "testing" "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain" "git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine" "github.com/stretchr/testify/require" ) const ( resrc = "native:::object/ExYw/*" chainID = "ingress:ExYw" nonExistChainId = "ingress:LxGyWyL" ) func testInmemLocalStorage() *inmemoryLocalStorage { return NewInmemoryLocalStorage().(*inmemoryLocalStorage) } func TestAddOverride(t *testing.T) { inmem := testInmemLocalStorage() inmem.AddOverride(chain.Ingress, resrc, &chain.Chain{ Rules: []chain.Rule{ { Status: chain.AccessDenied, Actions: chain.Actions{Names: []string{"native::object::delete"}}, Resources: chain.Resources{Names: []string{"native::object::*"}}, }, }, }) ingressChains, ok := inmem.nameToResourceChains[chain.Ingress] require.True(t, ok) resourceChains, ok := ingressChains[resrc] require.True(t, ok) require.Len(t, resourceChains, 1) require.Len(t, resourceChains[0].Rules, 1) inmem.AddOverride(chain.Ingress, resrc, &chain.Chain{ Rules: []chain.Rule{ { Status: chain.QuotaLimitReached, Actions: chain.Actions{Names: []string{"native::object::put"}}, Resources: chain.Resources{Names: []string{"native::object::*"}}, }, { Status: chain.AccessDenied, Actions: chain.Actions{Names: []string{"native::object::get"}}, Resources: chain.Resources{Names: []string{"native::object::*"}}, }, }, }) ingressChains, ok = inmem.nameToResourceChains[chain.Ingress] require.True(t, ok) resourceChains, ok = ingressChains[resrc] require.True(t, ok) require.Len(t, resourceChains, 2) require.Len(t, resourceChains[1].Rules, 2) } func TestRemoveOverride(t *testing.T) { t.Run("remove from empty storage", func(t *testing.T) { inmem := testInmemLocalStorage() err := inmem.RemoveOverride(chain.Ingress, resrc, chain.ID(chainID)) require.ErrorIs(t, err, engine.ErrChainNameNotFound) }) t.Run("remove not added chain id", func(t *testing.T) { inmem := testInmemLocalStorage() inmem.AddOverride(chain.Ingress, resrc, &chain.Chain{ ID: chain.ID(chainID), Rules: []chain.Rule{ { // Restrict to remove ANY object from the namespace. Status: chain.AccessDenied, Actions: chain.Actions{Names: []string{"native::object::delete"}}, Resources: chain.Resources{Names: []string{"native::object::*"}}, }, }, }) err := inmem.RemoveOverride(chain.Ingress, resrc, chain.ID(nonExistChainId)) require.ErrorIs(t, err, engine.ErrChainNotFound) }) t.Run("remove existing chain id", func(t *testing.T) { inmem := testInmemLocalStorage() inmem.AddOverride(chain.Ingress, resrc, &chain.Chain{ ID: chain.ID(chainID), Rules: []chain.Rule{ { // Restrict to remove ANY object from the namespace. Status: chain.AccessDenied, Actions: chain.Actions{Names: []string{"native::object::delete"}}, Resources: chain.Resources{Names: []string{"native::object::*"}}, }, }, }) err := inmem.RemoveOverride(chain.Ingress, resrc, chain.ID(chainID)) require.NoError(t, err) ingressChains, ok := inmem.nameToResourceChains[chain.Ingress] require.True(t, ok) require.Len(t, ingressChains, 1) resourceChains, ok := ingressChains[resrc] require.True(t, ok) require.Len(t, resourceChains, 0) }) } func TestGetOverride(t *testing.T) { addChain := &chain.Chain{ ID: chain.ID(chainID), Rules: []chain.Rule{ { // Restrict to remove ANY object from the namespace. Status: chain.AccessDenied, Actions: chain.Actions{Names: []string{"native::object::delete"}}, Resources: chain.Resources{Names: []string{"native::object::*"}}, }, }, } t.Run("get from empty storage", func(t *testing.T) { inmem := testInmemLocalStorage() _, err := inmem.GetOverride(chain.Ingress, resrc, chain.ID(chainID)) require.ErrorIs(t, err, engine.ErrChainNameNotFound) }) t.Run("get not added chain id", func(t *testing.T) { inmem := testInmemLocalStorage() inmem.AddOverride(chain.Ingress, resrc, addChain) const nonExistingChainID = "ingress:LxGyWyL" _, err := inmem.GetOverride(chain.Ingress, resrc, chain.ID(nonExistingChainID)) require.ErrorIs(t, err, engine.ErrChainNotFound) }) t.Run("get existing chain id", func(t *testing.T) { inmem := testInmemLocalStorage() inmem.AddOverride(chain.Ingress, resrc, addChain) c, err := inmem.GetOverride(chain.Ingress, resrc, chain.ID(chainID)) require.NoError(t, err) require.EqualValues(t, *addChain, *c) }) t.Run("get removed chain id", func(t *testing.T) { inmem := testInmemLocalStorage() inmem.AddOverride(chain.Ingress, resrc, addChain) err := inmem.RemoveOverride(chain.Ingress, resrc, chain.ID(chainID)) require.NoError(t, err) _, err = inmem.GetOverride(chain.Ingress, resrc, chain.ID(chainID)) require.ErrorIs(t, err, engine.ErrChainNotFound) }) } func TestListOverrides(t *testing.T) { addChain := &chain.Chain{ ID: chain.ID(chainID), Rules: []chain.Rule{ { // Restrict to remove ANY object from the namespace. Status: chain.AccessDenied, Actions: chain.Actions{Names: []string{"native::object::delete"}}, Resources: chain.Resources{Names: []string{"native::object::*"}}, }, }, } t.Run("list empty storage", func(t *testing.T) { inmem := testInmemLocalStorage() l, _ := inmem.ListOverrides(chain.Ingress, resrc) require.Len(t, l, 0) }) t.Run("list with one added resource", func(t *testing.T) { inmem := testInmemLocalStorage() inmem.AddOverride(chain.Ingress, resrc, addChain) l, _ := inmem.ListOverrides(chain.Ingress, resrc) require.Len(t, l, 1) }) t.Run("list after drop", func(t *testing.T) { inmem := testInmemLocalStorage() inmem.AddOverride(chain.Ingress, resrc, addChain) l, _ := inmem.ListOverrides(chain.Ingress, resrc) require.Len(t, l, 1) _ = inmem.DropAllOverrides(chain.Ingress) l, _ = inmem.ListOverrides(chain.Ingress, resrc) require.Len(t, l, 0) }) } func TestGenerateID(t *testing.T) { inmem := testInmemLocalStorage() ids := make([]chain.ID, 0, 100) for i := 0; i < 100; i++ { ids = append(ids, inmem.generateChainID(chain.Ingress, resrc)) } require.False(t, hasDuplicates(ids)) } func hasDuplicates(ids []chain.ID) bool { seen := make(map[chain.ID]bool) for _, id := range ids { if seen[id] { return true } seen[id] = true } return false }