| package scopes
|
|
|
| import (
|
| "context"
|
| "errors"
|
| "testing"
|
|
|
| "github.com/stretchr/testify/require"
|
|
|
| "github.com/looplj/axonhub/internal/ent"
|
| "github.com/looplj/axonhub/internal/ent/privacy"
|
| )
|
|
|
|
|
| type mockQueryRule struct {
|
| decision error
|
| }
|
|
|
| func (m mockQueryRule) EvalQuery(ctx context.Context, q ent.Query) error {
|
| return m.decision
|
| }
|
|
|
|
|
| type mockMutationRule struct {
|
| decision error
|
| }
|
|
|
| func (m mockMutationRule) EvalMutation(ctx context.Context, mutation ent.Mutation) error {
|
| return m.decision
|
| }
|
|
|
| func TestQueryPolicy_EvalQuery(t *testing.T) {
|
| tests := []struct {
|
| name string
|
| policies QueryPolicy
|
| expected error
|
| }{
|
| {
|
| name: "empty policy should deny",
|
| policies: QueryPolicy{},
|
| expected: privacy.Deny,
|
| },
|
| {
|
| name: "policy with allow rule should allow",
|
| policies: QueryPolicy{
|
| mockQueryRule{decision: privacy.Allow},
|
| },
|
| expected: privacy.Allow,
|
| },
|
| {
|
| name: "policy with deny rule should deny",
|
| policies: QueryPolicy{
|
| mockQueryRule{decision: privacy.Deny},
|
| },
|
| expected: privacy.Deny,
|
| },
|
| {
|
| name: "policy with skip rule should deny by default",
|
| policies: QueryPolicy{
|
| mockQueryRule{decision: privacy.Skip},
|
| },
|
| expected: privacy.Deny,
|
| },
|
| {
|
| name: "policy with nil rule should deny by default",
|
| policies: QueryPolicy{
|
| mockQueryRule{decision: nil},
|
| },
|
| expected: privacy.Deny,
|
| },
|
| {
|
| name: "multiple rules - first allow should return allow",
|
| policies: QueryPolicy{
|
| mockQueryRule{decision: privacy.Allow},
|
| mockQueryRule{decision: privacy.Deny},
|
| },
|
| expected: privacy.Allow,
|
| },
|
| {
|
| name: "multiple rules - first deny should return deny",
|
| policies: QueryPolicy{
|
| mockQueryRule{decision: privacy.Deny},
|
| mockQueryRule{decision: privacy.Allow},
|
| },
|
| expected: privacy.Deny,
|
| },
|
| {
|
| name: "multiple rules - skip then allow should return allow",
|
| policies: QueryPolicy{
|
| mockQueryRule{decision: privacy.Skip},
|
| mockQueryRule{decision: privacy.Allow},
|
| },
|
| expected: privacy.Allow,
|
| },
|
| {
|
| name: "multiple rules - all skip should deny by default",
|
| policies: QueryPolicy{
|
| mockQueryRule{decision: privacy.Skip},
|
| mockQueryRule{decision: privacy.Skip},
|
| },
|
| expected: privacy.Deny,
|
| },
|
| {
|
| name: "custom error should be returned",
|
| policies: QueryPolicy{
|
| mockQueryRule{decision: errors.New("custom error")},
|
| },
|
| expected: errors.New("custom error"),
|
| },
|
| }
|
|
|
| for _, tt := range tests {
|
| t.Run(tt.name, func(t *testing.T) {
|
| ctx := context.Background()
|
| err := tt.policies.EvalQuery(ctx, nil)
|
|
|
| if tt.expected == nil {
|
| require.NoError(t, err)
|
| } else if errors.Is(tt.expected, privacy.Allow) {
|
| require.ErrorIs(t, err, privacy.Allow)
|
| } else if errors.Is(tt.expected, privacy.Deny) {
|
| require.ErrorIs(t, err, privacy.Deny)
|
| } else if errors.Is(tt.expected, privacy.Skip) {
|
| require.ErrorIs(t, err, privacy.Skip)
|
| } else {
|
| require.EqualError(t, err, tt.expected.Error())
|
| }
|
| })
|
| }
|
| }
|
|
|
| func TestMutationPolicy_EvalMutation(t *testing.T) {
|
| tests := []struct {
|
| name string
|
| policies MutationPolicy
|
| expected error
|
| }{
|
| {
|
| name: "empty policy should deny",
|
| policies: MutationPolicy{},
|
| expected: privacy.Deny,
|
| },
|
| {
|
| name: "policy with allow rule should allow",
|
| policies: MutationPolicy{
|
| mockMutationRule{decision: privacy.Allow},
|
| },
|
| expected: privacy.Allow,
|
| },
|
| {
|
| name: "policy with deny rule should deny",
|
| policies: MutationPolicy{
|
| mockMutationRule{decision: privacy.Deny},
|
| },
|
| expected: privacy.Deny,
|
| },
|
| {
|
| name: "policy with skip rule should deny by default",
|
| policies: MutationPolicy{
|
| mockMutationRule{decision: privacy.Skip},
|
| },
|
| expected: privacy.Deny,
|
| },
|
| {
|
| name: "policy with nil rule should deny by default",
|
| policies: MutationPolicy{
|
| mockMutationRule{decision: nil},
|
| },
|
| expected: privacy.Deny,
|
| },
|
| {
|
| name: "multiple rules - first allow should return allow",
|
| policies: MutationPolicy{
|
| mockMutationRule{decision: privacy.Allow},
|
| mockMutationRule{decision: privacy.Deny},
|
| },
|
| expected: privacy.Allow,
|
| },
|
| {
|
| name: "multiple rules - first deny should return deny",
|
| policies: MutationPolicy{
|
| mockMutationRule{decision: privacy.Deny},
|
| mockMutationRule{decision: privacy.Allow},
|
| },
|
| expected: privacy.Deny,
|
| },
|
| {
|
| name: "multiple rules - skip then allow should return allow",
|
| policies: MutationPolicy{
|
| mockMutationRule{decision: privacy.Skip},
|
| mockMutationRule{decision: privacy.Allow},
|
| },
|
| expected: privacy.Allow,
|
| },
|
| {
|
| name: "multiple rules - all skip should deny by default",
|
| policies: MutationPolicy{
|
| mockMutationRule{decision: privacy.Skip},
|
| mockMutationRule{decision: privacy.Skip},
|
| },
|
| expected: privacy.Deny,
|
| },
|
| {
|
| name: "custom error should be returned",
|
| policies: MutationPolicy{
|
| mockMutationRule{decision: errors.New("custom error")},
|
| },
|
| expected: errors.New("custom error"),
|
| },
|
| }
|
|
|
| for _, tt := range tests {
|
| t.Run(tt.name, func(t *testing.T) {
|
| ctx := context.Background()
|
| err := tt.policies.EvalMutation(ctx, nil)
|
|
|
| if tt.expected == nil {
|
| require.NoError(t, err)
|
| } else if errors.Is(tt.expected, privacy.Allow) {
|
| require.ErrorIs(t, err, privacy.Allow)
|
| } else if errors.Is(tt.expected, privacy.Deny) {
|
| require.ErrorIs(t, err, privacy.Deny)
|
| } else if errors.Is(tt.expected, privacy.Skip) {
|
| require.ErrorIs(t, err, privacy.Skip)
|
| } else {
|
| require.EqualError(t, err, tt.expected.Error())
|
| }
|
| })
|
| }
|
| }
|
|
|
| func TestPolicy_Structure(t *testing.T) {
|
|
|
| policy := Policy{
|
| Query: QueryPolicy{
|
| mockQueryRule{decision: privacy.Allow},
|
| },
|
| Mutation: MutationPolicy{
|
| mockMutationRule{decision: privacy.Allow},
|
| },
|
| }
|
|
|
| require.NotNil(t, policy.Query)
|
| require.NotNil(t, policy.Mutation)
|
| require.Len(t, policy.Query, 1)
|
| require.Len(t, policy.Mutation, 1)
|
|
|
|
|
| ctx := context.Background()
|
|
|
| queryErr := policy.Query.EvalQuery(ctx, nil)
|
| require.ErrorIs(t, queryErr, privacy.Allow)
|
|
|
| mutationErr := policy.Mutation.EvalMutation(ctx, nil)
|
| require.ErrorIs(t, mutationErr, privacy.Allow)
|
| }
|
|
|
| func TestPolicy_EvalQuery(t *testing.T) {
|
| tests := []struct {
|
| name string
|
| policy Policy
|
| expected error
|
| }{
|
| {
|
| name: "policy with allow query rule should allow",
|
| policy: Policy{
|
| Query: QueryPolicy{
|
| mockQueryRule{decision: privacy.Allow},
|
| },
|
| },
|
| expected: privacy.Allow,
|
| },
|
| {
|
| name: "policy with deny query rule should deny",
|
| policy: Policy{
|
| Query: QueryPolicy{
|
| mockQueryRule{decision: privacy.Deny},
|
| },
|
| },
|
| expected: privacy.Deny,
|
| },
|
| {
|
| name: "policy with skip query rule should deny by default",
|
| policy: Policy{
|
| Query: QueryPolicy{
|
| mockQueryRule{decision: privacy.Skip},
|
| },
|
| },
|
| expected: privacy.Deny,
|
| },
|
| {
|
| name: "policy with empty query rules should deny by default",
|
| policy: Policy{
|
| Query: QueryPolicy{},
|
| },
|
| expected: privacy.Deny,
|
| },
|
| {
|
| name: "policy with multiple query rules - first allow wins",
|
| policy: Policy{
|
| Query: QueryPolicy{
|
| mockQueryRule{decision: privacy.Allow},
|
| mockQueryRule{decision: privacy.Deny},
|
| },
|
| },
|
| expected: privacy.Allow,
|
| },
|
| {
|
| name: "policy with multiple query rules - skip then allow",
|
| policy: Policy{
|
| Query: QueryPolicy{
|
| mockQueryRule{decision: privacy.Skip},
|
| mockQueryRule{decision: privacy.Allow},
|
| },
|
| },
|
| expected: privacy.Allow,
|
| },
|
| {
|
| name: "policy with custom error should return custom error",
|
| policy: Policy{
|
| Query: QueryPolicy{
|
| mockQueryRule{decision: errors.New("custom query error")},
|
| },
|
| },
|
| expected: errors.New("custom query error"),
|
| },
|
| }
|
|
|
| for _, tt := range tests {
|
| t.Run(tt.name, func(t *testing.T) {
|
| ctx := context.Background()
|
| err := tt.policy.EvalQuery(ctx, nil)
|
|
|
| if tt.expected == nil {
|
| require.NoError(t, err)
|
| } else if errors.Is(tt.expected, privacy.Allow) {
|
| require.ErrorIs(t, err, privacy.Allow)
|
| } else if errors.Is(tt.expected, privacy.Deny) {
|
| require.ErrorIs(t, err, privacy.Deny)
|
| } else if errors.Is(tt.expected, privacy.Skip) {
|
| require.ErrorIs(t, err, privacy.Skip)
|
| } else {
|
| require.EqualError(t, err, tt.expected.Error())
|
| }
|
| })
|
| }
|
| }
|
|
|
| func TestPolicy_EvalMutation(t *testing.T) {
|
| tests := []struct {
|
| name string
|
| policy Policy
|
| expected error
|
| }{
|
| {
|
| name: "policy with allow mutation rule should allow",
|
| policy: Policy{
|
| Mutation: MutationPolicy{
|
| mockMutationRule{decision: privacy.Allow},
|
| },
|
| },
|
| expected: privacy.Allow,
|
| },
|
| {
|
| name: "policy with deny mutation rule should deny",
|
| policy: Policy{
|
| Mutation: MutationPolicy{
|
| mockMutationRule{decision: privacy.Deny},
|
| },
|
| },
|
| expected: privacy.Deny,
|
| },
|
| {
|
| name: "policy with skip mutation rule should deny by default",
|
| policy: Policy{
|
| Mutation: MutationPolicy{
|
| mockMutationRule{decision: privacy.Skip},
|
| },
|
| },
|
| expected: privacy.Deny,
|
| },
|
| {
|
| name: "policy with empty mutation rules should deny by default",
|
| policy: Policy{
|
| Mutation: MutationPolicy{},
|
| },
|
| expected: privacy.Deny,
|
| },
|
| {
|
| name: "policy with multiple mutation rules - first allow wins",
|
| policy: Policy{
|
| Mutation: MutationPolicy{
|
| mockMutationRule{decision: privacy.Allow},
|
| mockMutationRule{decision: privacy.Deny},
|
| },
|
| },
|
| expected: privacy.Allow,
|
| },
|
| {
|
| name: "policy with multiple mutation rules - skip then allow",
|
| policy: Policy{
|
| Mutation: MutationPolicy{
|
| mockMutationRule{decision: privacy.Skip},
|
| mockMutationRule{decision: privacy.Allow},
|
| },
|
| },
|
| expected: privacy.Allow,
|
| },
|
| {
|
| name: "policy with custom error should return custom error",
|
| policy: Policy{
|
| Mutation: MutationPolicy{
|
| mockMutationRule{decision: errors.New("custom mutation error")},
|
| },
|
| },
|
| expected: errors.New("custom mutation error"),
|
| },
|
| }
|
|
|
| for _, tt := range tests {
|
| t.Run(tt.name, func(t *testing.T) {
|
| ctx := context.Background()
|
| err := tt.policy.EvalMutation(ctx, nil)
|
|
|
| if tt.expected == nil {
|
| require.NoError(t, err)
|
| } else if errors.Is(tt.expected, privacy.Allow) {
|
| require.ErrorIs(t, err, privacy.Allow)
|
| } else if errors.Is(tt.expected, privacy.Deny) {
|
| require.ErrorIs(t, err, privacy.Deny)
|
| } else if errors.Is(tt.expected, privacy.Skip) {
|
| require.ErrorIs(t, err, privacy.Skip)
|
| } else {
|
| require.EqualError(t, err, tt.expected.Error())
|
| }
|
| })
|
| }
|
| }
|
|
|
| func TestPolicy_Complete(t *testing.T) {
|
|
|
| policy := Policy{
|
| Query: QueryPolicy{
|
| mockQueryRule{decision: privacy.Allow},
|
| mockQueryRule{decision: privacy.Deny},
|
| },
|
| Mutation: MutationPolicy{
|
| mockMutationRule{decision: privacy.Skip},
|
| mockMutationRule{decision: privacy.Allow},
|
| },
|
| }
|
|
|
| ctx := context.Background()
|
|
|
|
|
| queryErr := policy.EvalQuery(ctx, nil)
|
| require.ErrorIs(t, queryErr, privacy.Allow)
|
|
|
|
|
| mutationErr := policy.EvalMutation(ctx, nil)
|
| require.ErrorIs(t, mutationErr, privacy.Allow)
|
| }
|
|
|