package testing

import (
	"bytes"
	"context"
	"sort"
	"strings"
	"testing"
	"time"

	"github.com/google/go-cmp/cmp"
	"github.com/influxdata/influxdb/v2"
	"github.com/influxdata/influxdb/v2/kit/platform"
	"github.com/influxdata/influxdb/v2/kit/platform/errors"
	"github.com/influxdata/influxdb/v2/mock"
	bolt "go.etcd.io/bbolt"
)

var orgBucketsIDGenerator = mock.NewMockIDGenerator()

var organizationCmpOptions = cmp.Options{
	cmp.Comparer(func(x, y []byte) bool {
		return bytes.Equal(x, y)
	}),
	cmp.Comparer(func(x, y *influxdb.Organization) bool {
		if x == nil && y == nil {
			return true
		}
		if x != nil && y == nil || y != nil && x == nil {
			return false
		}
		return x.Name == y.Name && x.Description == y.Description
	}),
	cmp.Transformer("Sort", func(in []*influxdb.Organization) []*influxdb.Organization {
		out := append([]*influxdb.Organization(nil), in...) // Copy input to avoid mutating it
		sort.Slice(out, func(i, j int) bool {
			return out[i].Name > out[j].Name
		})
		return out
	}),
}

// OrganizationFields will include the IDGenerator, and organizations
type OrganizationFields struct {
	IDGenerator   platform.IDGenerator
	OrgBucketIDs  platform.IDGenerator
	Organizations []*influxdb.Organization
	TimeGenerator influxdb.TimeGenerator
}

// OrganizationService tests all the service functions.
func OrganizationService(init func(OrganizationFields, *testing.T) (influxdb.OrganizationService, string, func()), isInMem bool, t *testing.T) {
	tests := []struct {
		name string
		fn   func(init func(OrganizationFields, *testing.T) (influxdb.OrganizationService, string, func()),
			isInMem bool,
			t *testing.T)
	}{
		{
			name: "CreateOrganization",
			fn:   CreateOrganization,
		},
		{
			name: "FindOrganizationByID",
			fn:   FindOrganizationByID,
		},
		{
			name: "FindOrganizations",
			fn:   FindOrganizations,
		},
		{
			name: "DeleteOrganization",
			fn:   DeleteOrganization,
		},
		{
			name: "FindOrganization",
			fn:   FindOrganization,
		},
		{
			name: "UpdateOrganization",
			fn:   UpdateOrganization,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			tt := tt
			t.Parallel()
			tt.fn(init, isInMem, t)
		})
	}
}

// CreateOrganization testing
func CreateOrganization(init func(OrganizationFields, *testing.T) (influxdb.OrganizationService, string, func()), isInMem bool, t *testing.T) {
	type args struct {
		organization *influxdb.Organization
	}
	type wants struct {
		err           error
		inMemNoError  bool
		organizations []*influxdb.Organization
	}

	tests := []struct {
		name   string
		fields OrganizationFields
		args   args
		wants  wants
	}{
		{
			name: "create organizations with empty set",
			fields: OrganizationFields{
				IDGenerator:   mock.NewMockIDGenerator(),
				OrgBucketIDs:  mock.NewMockIDGenerator(),
				TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC)},
				Organizations: []*influxdb.Organization{},
			},
			args: args{
				organization: &influxdb.Organization{
					Name:        "name1",
					ID:          idOne,
					Description: "desc1",
				},
			},
			wants: wants{
				organizations: []*influxdb.Organization{
					{
						Name:        "name1",
						ID:          platform.ID(mock.FirstMockID),
						Description: "desc1",
						CRUDLog: influxdb.CRUDLog{
							CreatedAt: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC),
							UpdatedAt: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC),
						},
					},
				},
			},
		},
		{
			name: "basic create organization",
			fields: OrganizationFields{
				IDGenerator:   mock.NewMockIDGenerator(),
				OrgBucketIDs:  mock.NewMockIDGenerator(),
				TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC)},
				Organizations: []*influxdb.Organization{
					{
						ID:   idOne,
						Name: "organization1",
					},
				},
			},
			args: args{
				organization: &influxdb.Organization{
					ID:   idTwo,
					Name: "organization2",
				},
			},
			wants: wants{
				organizations: []*influxdb.Organization{
					{
						ID:   idOne,
						Name: "organization1",
					},
					{
						ID:   platform.ID(mock.FirstMockID),
						Name: "organization2",
						CRUDLog: influxdb.CRUDLog{
							CreatedAt: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC),
							UpdatedAt: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC),
						},
					},
				},
			},
		},
		{
			name: "huge name",
			fields: OrganizationFields{
				IDGenerator:   mock.NewMockIDGenerator(),
				OrgBucketIDs:  orgBucketsIDGenerator,
				TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC)},
				Organizations: []*influxdb.Organization{
					{
						ID:   idOne,
						Name: "organization1",
					},
				},
			},
			args: args{
				organization: &influxdb.Organization{
					ID:   idTwo,
					Name: strings.Repeat("A Huge Organization Name", 10_000),
				},
			},
			wants: wants{
				organizations: []*influxdb.Organization{
					{
						ID:   idOne,
						Name: "organization1",
					},
				},
				err:          &errors.Error{Code: errors.ETooLarge, Err: bolt.ErrKeyTooLarge},
				inMemNoError: true,
			},
		},
		{
			name: "empty name",
			fields: OrganizationFields{
				IDGenerator:   mock.NewMockIDGenerator(),
				OrgBucketIDs:  orgBucketsIDGenerator,
				TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC)},
				Organizations: []*influxdb.Organization{
					{
						ID:   idOne,
						Name: "organization1",
					},
				},
			},
			args: args{
				organization: &influxdb.Organization{
					ID: idTwo,
				},
			},
			wants: wants{
				organizations: []*influxdb.Organization{
					{
						ID:   idOne,
						Name: "organization1",
					},
				},
				err: influxdb.ErrOrgNameisEmpty,
			},
		},
		{
			name: "name only have spaces",
			fields: OrganizationFields{
				IDGenerator:   mock.NewMockIDGenerator(),
				OrgBucketIDs:  orgBucketsIDGenerator,
				TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC)},
				Organizations: []*influxdb.Organization{
					{
						ID:   idOne,
						Name: "organization1",
					},
				},
			},
			args: args{
				organization: &influxdb.Organization{
					ID:   idTwo,
					Name: "  ",
				},
			},
			wants: wants{
				organizations: []*influxdb.Organization{
					{
						ID:   idOne,
						Name: "organization1",
					},
				},
				err: influxdb.ErrOrgNameisEmpty,
			},
		},
		{
			name: "names should be unique",
			fields: OrganizationFields{
				IDGenerator:   mock.NewMockIDGenerator(),
				OrgBucketIDs:  orgBucketsIDGenerator,
				TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC)},
				Organizations: []*influxdb.Organization{
					{
						ID:   idOne,
						Name: "organization1",
					},
				},
			},
			args: args{
				organization: &influxdb.Organization{
					ID:   idTwo,
					Name: "organization1",
				},
			},
			wants: wants{
				organizations: []*influxdb.Organization{
					{
						ID:   idOne,
						Name: "organization1",
					},
				},
				err: &errors.Error{
					Code: errors.EConflict,
					Op:   influxdb.OpCreateOrganization,
					Msg:  "organization with name organization1 already exists",
				},
			},
		},
		{
			name: "create organization with no id",
			fields: OrganizationFields{
				IDGenerator:   mock.NewMockIDGenerator(),
				OrgBucketIDs:  mock.NewMockIDGenerator(),
				TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC)},
				Organizations: []*influxdb.Organization{
					{
						ID:   idOne,
						Name: "organization1",
					},
				},
			},
			args: args{
				organization: &influxdb.Organization{
					Name: "organization2",
				},
			},
			wants: wants{
				organizations: []*influxdb.Organization{
					{
						ID:   idOne,
						Name: "organization1",
					},
					{
						ID:   platform.ID(mock.FirstMockID),
						Name: "organization2",
						CRUDLog: influxdb.CRUDLog{
							CreatedAt: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC),
							UpdatedAt: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC),
						},
					},
				},
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			s, _, done := init(tt.fields, t)
			defer done()
			ctx := context.Background()
			errCreate := s.CreateOrganization(ctx, tt.args.organization)
			diffPlatformErrors(tt.name, errCreate, tt.wants.err, tt.wants.inMemNoError, isInMem, t)

			// Delete only newly created organizations
			// if tt.args.organization.ID != nil {
			defer s.DeleteOrganization(ctx, tt.args.organization.ID)

			organizations, _, err := s.FindOrganizations(ctx, influxdb.OrganizationFilter{})
			diffPlatformErrors(tt.name, err, nil, tt.wants.inMemNoError, false, t)

			// Our test cases wants list is for the case when the error occurs
			// If the error does not occur (usually because of the inmem store)
			// our list of wanted organizations will be wrong.
			if tt.wants.inMemNoError && errCreate == nil {
				return
			}
			if diff := cmp.Diff(organizations, tt.wants.organizations, organizationCmpOptions...); diff != "" {
				t.Errorf("organizations are different -got/+want\ndiff %s", diff)
			}
		})
	}
}

// FindOrganizationByID testing
func FindOrganizationByID(init func(OrganizationFields, *testing.T) (influxdb.OrganizationService, string, func()), isInMem bool, t *testing.T) {
	type args struct {
		id platform.ID
	}
	type wants struct {
		err          error
		organization *influxdb.Organization
	}

	tests := []struct {
		name   string
		fields OrganizationFields
		args   args
		wants  wants
	}{
		{
			name: "basic find organization by id",
			fields: OrganizationFields{
				OrgBucketIDs: mock.NewIncrementingIDGenerator(idOne),
				Organizations: []*influxdb.Organization{
					{
						// ID(1)
						Name: "organization1",
					},
					{
						// ID(2)
						Name: "organization2",
					},
				},
			},
			args: args{
				id: idTwo,
			},
			wants: wants{
				organization: &influxdb.Organization{
					ID:   idTwo,
					Name: "organization2",
				},
			},
		},
		{
			name: "didn't find organization by id",
			fields: OrganizationFields{
				OrgBucketIDs: mock.NewIncrementingIDGenerator(idOne),
				Organizations: []*influxdb.Organization{
					{
						// ID(1)
						Name: "organization1",
					},
					{
						// ID(2)
						Name: "organization2",
					},
				},
			},
			args: args{
				id: idThree,
			},
			wants: wants{
				organization: nil,
				err: &errors.Error{
					Code: errors.ENotFound,
					Op:   influxdb.OpFindOrganizationByID,
					Msg:  "organization not found",
				},
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			s, _, done := init(tt.fields, t)
			defer done()
			ctx := context.Background()

			organization, err := s.FindOrganizationByID(ctx, tt.args.id)
			diffPlatformErrors(tt.name, err, tt.wants.err, false, false, t)

			if diff := cmp.Diff(organization, tt.wants.organization, organizationCmpOptions...); diff != "" {
				t.Errorf("organization is different -got/+want\ndiff %s", diff)
			}
		})
	}
}

// FindOrganizations testing
func FindOrganizations(init func(OrganizationFields, *testing.T) (influxdb.OrganizationService, string, func()), isInMem bool, t *testing.T) {
	type args struct {
		ID          platform.ID
		name        string
		findOptions influxdb.FindOptions
	}

	type wants struct {
		organizations []*influxdb.Organization
		err           error
	}
	tests := []struct {
		name   string
		fields OrganizationFields
		args   args
		wants  wants
	}{
		{
			name: "find all organizations",
			fields: OrganizationFields{
				OrgBucketIDs: mock.NewIncrementingIDGenerator(idOne),
				Organizations: []*influxdb.Organization{
					{
						// ID(1)
						Name: "abc",
					},
					{
						// ID(2)
						Name:        "xyz",
						Description: "desc xyz",
					},
				},
			},
			args: args{},
			wants: wants{
				organizations: []*influxdb.Organization{
					{
						ID:   idOne,
						Name: "abc",
					},
					{
						ID:          idTwo,
						Name:        "xyz",
						Description: "desc xyz",
					},
				},
			},
		},
		{
			name: "find all organizations by offset and limit",
			fields: OrganizationFields{
				OrgBucketIDs: mock.NewIncrementingIDGenerator(idOne),
				Organizations: []*influxdb.Organization{
					{
						// ID(1)
						Name: "abc",
					},
					{
						// ID(2)
						Name:        "xyz",
						Description: "desc xyz",
					},
					{
						// ID(3)
						Name: "ijk",
					},
				},
			},
			args: args{
				findOptions: influxdb.FindOptions{
					Offset: 1,
					Limit:  1,
				},
			},
			wants: wants{
				organizations: []*influxdb.Organization{
					{
						ID:          idTwo,
						Name:        "xyz",
						Description: "desc xyz",
					},
				},
			},
		},
		{
			name: "find organization by id",
			fields: OrganizationFields{
				OrgBucketIDs: mock.NewIncrementingIDGenerator(idOne),
				Organizations: []*influxdb.Organization{
					{
						// ID(1)
						Name: "abc",
					},
					{
						// ID(2)
						Name: "xyz",
					},
				},
			},
			args: args{
				ID: idTwo,
			},
			wants: wants{
				organizations: []*influxdb.Organization{
					{
						ID:   idTwo,
						Name: "xyz",
					},
				},
			},
		},
		{
			name: "find organization by name",
			fields: OrganizationFields{
				OrgBucketIDs: mock.NewIncrementingIDGenerator(idOne),
				Organizations: []*influxdb.Organization{
					{
						// ID(1)
						Name: "abc",
					},
					{
						// ID(2)
						Name: "xyz",
					},
				},
			},
			args: args{
				name: "xyz",
			},
			wants: wants{
				organizations: []*influxdb.Organization{
					{
						ID:   idTwo,
						Name: "xyz",
					},
				},
			},
		},
		{
			name: "find organization by id not exists",
			fields: OrganizationFields{
				OrgBucketIDs: mock.NewIncrementingIDGenerator(idOne),
				Organizations: []*influxdb.Organization{
					{
						// ID(1)
						Name: "abc",
					},
					{
						// ID(2)
						Name: "xyz",
					},
				},
			},
			args: args{
				ID: idThree,
			},
			wants: wants{
				organizations: []*influxdb.Organization{},
				err: &errors.Error{
					Code: errors.ENotFound,
					Op:   influxdb.OpFindOrganizations,
					Msg:  "organization not found",
				},
			},
		},
		{
			name: "find organization by name not exists",
			fields: OrganizationFields{
				OrgBucketIDs: mock.NewIncrementingIDGenerator(idOne),
				Organizations: []*influxdb.Organization{
					{
						// ID(1)
						Name: "abc",
					},
					{
						// ID(2)
						Name: "xyz",
					},
				},
			},
			args: args{
				name: "na",
			},
			wants: wants{
				organizations: []*influxdb.Organization{},
				err: &errors.Error{
					Code: errors.ENotFound,
					Op:   influxdb.OpFindOrganizations,
					Msg:  "organization name \"na\" not found",
				},
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			s, _, done := init(tt.fields, t)
			defer done()
			ctx := context.Background()

			filter := influxdb.OrganizationFilter{}
			if tt.args.ID.Valid() {
				filter.ID = &tt.args.ID
			}
			if tt.args.name != "" {
				filter.Name = &tt.args.name
			}

			organizations, _, err := s.FindOrganizations(ctx, filter, tt.args.findOptions)
			diffPlatformErrors(tt.name, err, tt.wants.err, false, false, t)

			if diff := cmp.Diff(organizations, tt.wants.organizations, organizationCmpOptions...); diff != "" {
				t.Errorf("organizations are different -got/+want\ndiff %s", diff)
			}
		})
	}
}

// DeleteOrganization testing
func DeleteOrganization(init func(OrganizationFields, *testing.T) (influxdb.OrganizationService, string, func()), isInMem bool, t *testing.T) {
	type args struct {
		ID platform.ID
	}
	type wants struct {
		err           error
		organizations []*influxdb.Organization
	}

	tests := []struct {
		name   string
		fields OrganizationFields
		args   args
		wants  wants
	}{
		{
			name: "delete organizations using exist id",
			fields: OrganizationFields{
				OrgBucketIDs: mock.NewIncrementingIDGenerator(idOne),
				Organizations: []*influxdb.Organization{
					{
						// ID(1)
						Name: "orgA",
					},
					{
						// ID(2)
						Name: "orgB",
					},
				},
			},
			args: args{
				ID: idOne,
			},
			wants: wants{
				organizations: []*influxdb.Organization{
					{
						Name: "orgB",
						ID:   idTwo,
					},
				},
			},
		},
		{
			name: "delete organizations using id that does not exist",
			fields: OrganizationFields{
				OrgBucketIDs:  mock.NewIncrementingIDGenerator(idOne),
				TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC)},
				Organizations: []*influxdb.Organization{
					{
						// ID(1)
						Name: "orgA",
					},
					{
						// ID(2)
						Name: "orgB",
					},
				},
			},
			args: args{
				ID: MustIDBase16("1234567890654321"),
			},
			wants: wants{
				err: &errors.Error{
					Code: errors.ENotFound,
					Op:   influxdb.OpDeleteOrganization,
					Msg:  "organization not found",
				},
				organizations: []*influxdb.Organization{
					{
						Name: "orgA",
						ID:   idOne,
					},
					{
						Name: "orgB",
						ID:   idTwo,
					},
				},
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			s, _, done := init(tt.fields, t)
			defer done()
			ctx := context.Background()
			err := s.DeleteOrganization(ctx, tt.args.ID)
			diffPlatformErrors(tt.name, err, tt.wants.err, false, false, t)

			filter := influxdb.OrganizationFilter{}
			organizations, _, err := s.FindOrganizations(ctx, filter)
			diffPlatformErrors(tt.name, err, nil, false, false, t)

			if diff := cmp.Diff(organizations, tt.wants.organizations, organizationCmpOptions...); diff != "" {
				t.Errorf("organizations are different -got/+want\ndiff %s", diff)
			}
		})
	}
}

// FindOrganization testing
func FindOrganization(init func(OrganizationFields, *testing.T) (influxdb.OrganizationService, string, func()), isInMem bool, t *testing.T) {
	type args struct {
		name string
		id   platform.ID
	}

	type wants struct {
		organization *influxdb.Organization
		err          error
	}

	tests := []struct {
		name   string
		fields OrganizationFields
		args   args
		wants  wants
	}{
		{
			name: "find organization by name",
			fields: OrganizationFields{
				OrgBucketIDs: mock.NewIncrementingIDGenerator(idOne),
				Organizations: []*influxdb.Organization{
					{
						// ID(1)
						Name: "abc",
					},
					{
						// ID(2)
						Name: "xyz",
					},
				},
			},
			args: args{
				name: "abc",
			},
			wants: wants{
				organization: &influxdb.Organization{
					ID:   idOne,
					Name: "abc",
				},
			},
		},
		{
			name: "find organization in which no name filter matches should return no org",
			args: args{
				name: "unknown",
			},
			wants: wants{
				err: &errors.Error{
					Code: errors.ENotFound,
					Op:   influxdb.OpFindOrganization,
					Msg:  "organization name \"unknown\" not found",
				},
			},
		},
		{
			name: "find organization in which no id filter matches should return no org",
			args: args{
				id: platform.ID(3),
			},
			wants: wants{
				err: &errors.Error{
					Code: errors.ENotFound,
					Msg:  "organization not found",
				},
			},
		},
		{
			name: "find organization no filter is set returns an error about filters not provided",
			fields: OrganizationFields{
				OrgBucketIDs: mock.NewIncrementingIDGenerator(idOne),
				Organizations: []*influxdb.Organization{
					{
						// ID(1)
						Name: "o1",
					},
				},
			},
			wants: wants{
				err: influxdb.ErrInvalidOrgFilter,
			},
		},
		{
			name: "missing organization returns error",
			fields: OrganizationFields{
				OrgBucketIDs:  mock.NewIncrementingIDGenerator(idOne),
				Organizations: []*influxdb.Organization{},
			},
			args: args{
				name: "abc",
			},
			wants: wants{
				err: &errors.Error{
					Code: errors.ENotFound,
					Op:   influxdb.OpFindOrganization,
					Msg:  "organization name \"abc\" not found",
				},
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			s, _, done := init(tt.fields, t)
			defer done()
			ctx := context.Background()
			filter := influxdb.OrganizationFilter{}
			if tt.args.name != "" {
				filter.Name = &tt.args.name
			}
			if tt.args.id != platform.InvalidID() {
				filter.ID = &tt.args.id
			}

			organization, err := s.FindOrganization(ctx, filter)
			diffPlatformErrors(tt.name, err, tt.wants.err, false, false, t)

			if diff := cmp.Diff(organization, tt.wants.organization, organizationCmpOptions...); diff != "" {
				t.Errorf("organizations are different -got/+want\ndiff %s", diff)
			}
		})
	}
}

// UpdateOrganization testing
func UpdateOrganization(init func(OrganizationFields, *testing.T) (influxdb.OrganizationService, string, func()), isInMem bool, t *testing.T) {
	type args struct {
		id          platform.ID
		name        *string
		description *string
	}
	type wants struct {
		err          error
		organization *influxdb.Organization
	}

	tests := []struct {
		name   string
		fields OrganizationFields
		args   args
		wants  wants
	}{
		{
			name: "update id not exists",
			fields: OrganizationFields{
				OrgBucketIDs:  mock.NewIncrementingIDGenerator(idOne),
				TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC)},
				Organizations: []*influxdb.Organization{
					{
						// ID(1)
						Name: "organization1",
					},
					{
						// ID(2)
						Name: "organization2",
					},
				},
			},
			args: args{
				id:   MustIDBase16(threeID),
				name: strPtr("changed"),
			},
			wants: wants{
				err: &errors.Error{
					Code: errors.ENotFound,
					Op:   influxdb.OpUpdateOrganization,
					Msg:  "organization not found",
				},
			},
		},
		{
			name: "update name",
			fields: OrganizationFields{
				OrgBucketIDs:  mock.NewIncrementingIDGenerator(idOne),
				TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC)},
				Organizations: []*influxdb.Organization{
					{
						// ID(1)
						Name: "organization1",
					},
					{
						// ID(2)
						Name: "organization2",
					},
				},
			},
			args: args{
				id:   idOne,
				name: strPtr("changed"),
			},
			wants: wants{
				organization: &influxdb.Organization{
					ID:   idOne,
					Name: "changed",
					CRUDLog: influxdb.CRUDLog{
						UpdatedAt: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC),
					},
				},
			},
		},
		{
			name: "update name to same name",
			fields: OrganizationFields{
				OrgBucketIDs:  mock.NewIncrementingIDGenerator(idOne),
				TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC)},
				Organizations: []*influxdb.Organization{
					{
						// ID(1)
						Name: "organization1",
					},
					{
						// ID(2)
						Name: "organization2",
					},
				},
			},
			args: args{
				id:   idOne,
				name: strPtr("organization1"),
			},
			wants: wants{
				organization: &influxdb.Organization{
					ID:   idOne,
					Name: "organization1",
					CRUDLog: influxdb.CRUDLog{
						UpdatedAt: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC),
					},
				},
			},
		}, {
			name: "update name not unique",
			fields: OrganizationFields{
				OrgBucketIDs:  mock.NewIncrementingIDGenerator(idOne),
				TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC)},
				Organizations: []*influxdb.Organization{
					{
						// ID(1)
						Name: "organization1",
					},
					{
						// ID(2)
						Name: "organization2",
					},
				},
			},
			args: args{
				id:   idOne,
				name: strPtr("organization2"),
			},
			wants: wants{
				err: &errors.Error{
					Code: errors.EConflict,
					Op:   influxdb.OpUpdateOrganization,
					Msg:  "organization with name organization2 already exists",
				},
			},
		},
		{
			name: "update name is empty",
			fields: OrganizationFields{
				OrgBucketIDs:  mock.NewIncrementingIDGenerator(idOne),
				TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC)},
				Organizations: []*influxdb.Organization{
					{
						// ID(1)
						Name: "organization1",
					},
					{
						// ID(2)
						Name: "organization2",
					},
				},
			},
			args: args{
				id:   idOne,
				name: strPtr(""),
			},
			wants: wants{
				err: influxdb.ErrOrgNameisEmpty,
			},
		},
		{
			name: "update name only has space",
			fields: OrganizationFields{
				OrgBucketIDs:  mock.NewIncrementingIDGenerator(idOne),
				TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC)},
				Organizations: []*influxdb.Organization{
					{
						// ID(1)
						Name: "organization1",
					},
					{
						// ID(2)
						Name: "organization2",
					},
				},
			},
			args: args{
				id:   idOne,
				name: strPtr("            "),
			},
			wants: wants{
				err: influxdb.ErrOrgNameisEmpty,
			},
		},
		{
			name: "update description",
			fields: OrganizationFields{
				OrgBucketIDs:  mock.NewIncrementingIDGenerator(idOne),
				TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC)},
				Organizations: []*influxdb.Organization{
					{
						// ID(1)
						Name:        "organization1",
						Description: "organization1 description",
					},
					{
						// ID(2)
						Name:        "organization2",
						Description: "organization2 description",
					},
				},
			},
			args: args{
				id:          idOne,
				description: strPtr("changed"),
			},
			wants: wants{
				organization: &influxdb.Organization{
					ID:          idOne,
					Name:        "organization1",
					Description: "changed",
					CRUDLog: influxdb.CRUDLog{
						UpdatedAt: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC),
					},
				},
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			s, _, done := init(tt.fields, t)
			defer done()
			ctx := context.Background()

			upd := influxdb.OrganizationUpdate{}
			upd.Name = tt.args.name
			upd.Description = tt.args.description

			organization, err := s.UpdateOrganization(ctx, tt.args.id, upd)
			diffPlatformErrors(tt.name, err, tt.wants.err, false, false, t)

			if diff := cmp.Diff(organization, tt.wants.organization, organizationCmpOptions...); diff != "" {
				t.Errorf("organization is different -got/+want\ndiff %s", diff)
			}
		})
	}
}
