package permissionauthoringv1

import (
	"reflect"
	"testing"
)

// Reading authoring state must not repair or reject historical grant values.
// An administrator must be able to see and correct them without first granting
// additional access. Identity, ordering, token and payload bounds still apply.
func TestSnapshotPreservesEveryStoredObjectGrantCombination(t *testing.T) {
	for bits := 0; bits < 64; bits++ {
		response := validSnapshotResponse(t)
		grant := ObjectGrant{ResourceType: "Account", Read: bits&1 != 0, Create: bits&2 != 0,
			Edit: bits&4 != 0, Delete: bits&8 != 0, ViewAll: bits&16 != 0, ModifyAll: bits&32 != 0}
		response.Grants.Objects = []ObjectGrant{grant}
		normalized := NormalizeSnapshotResponse(response)
		if err := ValidateSnapshotResponse(validSnapshotRequest(), normalized); err != nil {
			t.Errorf("stored bits %06b: %v", bits, err)
		}
		if !reflect.DeepEqual(normalized.Grants.Objects, []ObjectGrant{grant}) {
			t.Fatalf("reading stored bits %06b changed access", bits)
		}
	}
}

func TestObjectWriteDependenciesDoNotGrantCreateWithModifyAll(t *testing.T) {
	for _, test := range []struct {
		name  string
		grant ObjectGrantChange
		valid bool
	}{
		{"modify existing records without create", ObjectGrantChange{ResourceType: "Account", Read: true, Edit: true, Delete: true, ViewAll: true, ModifyAll: true}, true},
		{"modify all without delete", ObjectGrantChange{ResourceType: "Account", Read: true, Create: true, Edit: true, ViewAll: true, ModifyAll: true}, false},
		{"modify all without edit", ObjectGrantChange{ResourceType: "Account", Read: true, Delete: true, ViewAll: true, ModifyAll: true}, false},
		{"create without read", ObjectGrantChange{ResourceType: "Account", Create: true}, false},
		{"revoke all", ObjectGrantChange{ResourceType: "Account"}, true},
	} {
		t.Run(test.name, func(t *testing.T) {
			request := validRequest()
			request.Changes = Changes{Objects: []ObjectGrantChange{test.grant}}
			_, err := CanonicalCommandBytes(request)
			if (err == nil) != test.valid {
				t.Fatalf("error=%v, want valid=%v", err, test.valid)
			}
		})
	}
}

func TestStoredObjectCompatibilityStillRejectsInvalidIdentities(t *testing.T) {
	for _, grants := range [][]ObjectGrant{
		{{ResourceType: "bad.name"}},
		{{ResourceType: "Account"}, {ResourceType: "account"}},
		{{ResourceType: "Contact"}, {ResourceType: "Account"}},
	} {
		response := validSnapshotResponse(t)
		response.Grants.Objects = grants
		if err := ValidateSnapshotResponse(validSnapshotRequest(), response); err == nil {
			t.Fatalf("accepted malformed stored identities: %+v", grants)
		}
	}
}
