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 ( container = "native:::object/ExYw/*" chainID = "ingress:ExYw" nonExistChainId = "ingress:LxGyWyL" ) var resrc = engine.ContainerTarget(container) 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) }) t.Run("remove by target", func(t *testing.T) { inmem := testInmemLocalStorage() t0 := engine.ContainerTarget("name0") t1 := engine.ContainerTarget("name1") inmem.AddOverride(chain.Ingress, t0, &chain.Chain{ ID: chain.ID(chainID), Rules: []chain.Rule{ { Status: chain.AccessDenied, Actions: chain.Actions{Names: []string{"native::object::delete"}}, Resources: chain.Resources{Names: []string{"native::object::*"}}, }, }, }) inmem.AddOverride(chain.Ingress, t0, &chain.Chain{ ID: chain.ID(chainID), Rules: []chain.Rule{ { Status: chain.Allow, Actions: chain.Actions{Names: []string{"native::object::delete"}}, Resources: chain.Resources{Names: []string{"native::object::*"}}, }, }, }) inmem.AddOverride(chain.Ingress, t1, &chain.Chain{ ID: chain.ID(chainID), Rules: []chain.Rule{ { Status: chain.Allow, Actions: chain.Actions{Names: []string{"native::object::delete"}}, Resources: chain.Resources{Names: []string{"native::object::*"}}, }, }, }) err := inmem.RemoveOverridesByTarget(chain.Ingress, t0) require.NoError(t, err) ingressChains, ok := inmem.nameToResourceChains[chain.Ingress] require.True(t, ok) require.Len(t, ingressChains, 1) resourceChains, ok := ingressChains[t1] require.True(t, ok) require.Len(t, resourceChains, 1) }) } 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) targets, err := inmem.ListOverrideDefinedTargets(chain.Ingress) require.NoError(t, err) require.Equal(t, []engine.Target{resrc}, targets) }) 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[string]bool) for _, id := range ids { if seen[string(id)] { return true } seen[string(id)] = true } return false }