package testing

import (
	"bytes"
	"context"
	"sort"
	"testing"

	"github.com/google/go-cmp/cmp"
	"github.com/influxdata/influxdb/v2"
	"github.com/influxdata/influxdb/v2/mock"
)

const (
	targetOneID   = "020f755c3c082000"
	targetTwoID   = "020f755c3c082001"
	targetThreeID = "020f755c3c082002"
)

var (
	target1 = influxdb.ScraperTarget{
		Name:     "name1",
		Type:     influxdb.PrometheusScraperType,
		OrgID:    MustIDBase16(orgOneID),
		BucketID: MustIDBase16(bucketOneID),
		URL:      "url1",
		ID:       MustIDBase16(targetOneID),
	}
	target2 = influxdb.ScraperTarget{
		Name:     "name2",
		Type:     influxdb.PrometheusScraperType,
		OrgID:    MustIDBase16(orgTwoID),
		BucketID: MustIDBase16(bucketTwoID),
		URL:      "url2",
		ID:       MustIDBase16(targetTwoID),
	}
	target3 = influxdb.ScraperTarget{
		Name:     "name3",
		Type:     influxdb.PrometheusScraperType,
		OrgID:    MustIDBase16(orgOneID),
		BucketID: MustIDBase16(bucketThreeID),
		URL:      "url3",
		ID:       MustIDBase16(targetThreeID),
	}
	org1 = influxdb.Organization{
		ID:   MustIDBase16(orgOneID),
		Name: "org1",
	}
	org2 = influxdb.Organization{
		ID:   MustIDBase16(orgTwoID),
		Name: "org2",
	}
)

// TargetFields will include the IDGenerator, and targets
type TargetFields struct {
	IDGenerator          influxdb.IDGenerator
	Targets              []*influxdb.ScraperTarget
	UserResourceMappings []*influxdb.UserResourceMapping
	Organizations        []*influxdb.Organization
}

var targetCmpOptions = cmp.Options{
	cmp.Comparer(func(x, y []byte) bool {
		return bytes.Equal(x, y)
	}),
	cmp.Transformer("Sort", func(in []influxdb.ScraperTarget) []influxdb.ScraperTarget {
		out := append([]influxdb.ScraperTarget(nil), in...) // Copy input to avoid mutating it
		sort.Slice(out, func(i, j int) bool {
			return out[i].ID.String() > out[j].ID.String()
		})
		return out
	}),
}

// ScraperService tests all the service functions.
func ScraperService(
	init func(TargetFields, *testing.T) (influxdb.ScraperTargetStoreService, string, func()), t *testing.T,
) {
	t.Helper()
	tests := []struct {
		name string
		fn   func(init func(TargetFields, *testing.T) (influxdb.ScraperTargetStoreService, string, func()),
			t *testing.T)
	}{
		{
			name: "AddTarget",
			fn:   AddTarget,
		},
		{
			name: "ListTargets",
			fn:   ListTargets,
		},
		{
			name: "GetTargetByID",
			fn:   GetTargetByID,
		},
		{
			name: "RemoveTarget",
			fn:   RemoveTarget,
		},
		{
			name: "UpdateTarget",
			fn:   UpdateTarget,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			tt.fn(init, t)
		})
	}
}

// AddTarget testing.
func AddTarget(
	init func(TargetFields, *testing.T) (influxdb.ScraperTargetStoreService, string, func()),
	t *testing.T,
) {
	t.Helper()
	type args struct {
		userID influxdb.ID
		target *influxdb.ScraperTarget
	}
	type wants struct {
		err                  error
		targets              []influxdb.ScraperTarget
		userResourceMappings []*influxdb.UserResourceMapping
	}
	tests := []struct {
		name   string
		fields TargetFields
		args   args
		wants  wants
	}{
		{
			name: "create targets with empty set",
			fields: TargetFields{
				IDGenerator:          mock.NewIDGenerator(targetOneID, t),
				Targets:              []*influxdb.ScraperTarget{},
				UserResourceMappings: []*influxdb.UserResourceMapping{},
				Organizations:        []*influxdb.Organization{&org1},
			},
			args: args{
				userID: MustIDBase16(threeID),
				target: &influxdb.ScraperTarget{
					Name:     "name1",
					Type:     influxdb.PrometheusScraperType,
					OrgID:    MustIDBase16(orgOneID),
					BucketID: MustIDBase16(bucketOneID),
					URL:      "url1",
				},
			},
			wants: wants{
				userResourceMappings: []*influxdb.UserResourceMapping{
					{
						ResourceID:   MustIDBase16(oneID),
						ResourceType: influxdb.ScraperResourceType,
						UserID:       MustIDBase16(threeID),
						UserType:     influxdb.Owner,
					},
				},
				targets: []influxdb.ScraperTarget{
					{
						Name:     "name1",
						Type:     influxdb.PrometheusScraperType,
						OrgID:    MustIDBase16(orgOneID),
						BucketID: MustIDBase16(bucketOneID),
						URL:      "url1",
						ID:       MustIDBase16(targetOneID),
					},
				},
			},
		},
		{
			name: "create target with invalid org id",
			fields: TargetFields{
				IDGenerator:          mock.NewIDGenerator(targetTwoID, t),
				UserResourceMappings: []*influxdb.UserResourceMapping{},
				Organizations:        []*influxdb.Organization{&org1},
				Targets: []*influxdb.ScraperTarget{
					{
						Name:     "name1",
						Type:     influxdb.PrometheusScraperType,
						OrgID:    MustIDBase16(orgOneID),
						BucketID: MustIDBase16(bucketOneID),
						URL:      "url1",
						ID:       MustIDBase16(targetOneID),
					},
				},
			},
			args: args{
				target: &influxdb.ScraperTarget{
					ID:       MustIDBase16(targetTwoID),
					Name:     "name2",
					Type:     influxdb.PrometheusScraperType,
					BucketID: MustIDBase16(bucketTwoID),
					URL:      "url2",
				},
			},
			wants: wants{
				err: &influxdb.Error{
					Code: influxdb.EInvalid,
					Msg:  "provided organization ID has invalid format",
					Op:   influxdb.OpAddTarget,
				},
				userResourceMappings: []*influxdb.UserResourceMapping{},
				targets: []influxdb.ScraperTarget{
					{
						Name:     "name1",
						Type:     influxdb.PrometheusScraperType,
						OrgID:    MustIDBase16(orgOneID),
						BucketID: MustIDBase16(bucketOneID),
						URL:      "url1",
						ID:       MustIDBase16(targetOneID),
					},
				},
			},
		},
		{
			name: "create target with invalid bucket id",
			fields: TargetFields{
				IDGenerator:          mock.NewIDGenerator(targetTwoID, t),
				UserResourceMappings: []*influxdb.UserResourceMapping{},
				Organizations:        []*influxdb.Organization{&org1},
				Targets: []*influxdb.ScraperTarget{
					{
						Name:     "name1",
						Type:     influxdb.PrometheusScraperType,
						OrgID:    MustIDBase16(orgOneID),
						BucketID: MustIDBase16(bucketOneID),
						URL:      "url1",
						ID:       MustIDBase16(targetOneID),
					},
				},
			},
			args: args{
				target: &influxdb.ScraperTarget{
					ID:    MustIDBase16(targetTwoID),
					Name:  "name2",
					Type:  influxdb.PrometheusScraperType,
					OrgID: MustIDBase16(orgTwoID),
					URL:   "url2",
				},
			},
			wants: wants{
				err: &influxdb.Error{
					Code: influxdb.EInvalid,
					Msg:  "provided bucket ID has invalid format",
					Op:   influxdb.OpAddTarget,
				},
				userResourceMappings: []*influxdb.UserResourceMapping{},
				targets: []influxdb.ScraperTarget{
					{
						Name:     "name1",
						Type:     influxdb.PrometheusScraperType,
						OrgID:    MustIDBase16(orgOneID),
						BucketID: MustIDBase16(bucketOneID),
						URL:      "url1",
						ID:       MustIDBase16(targetOneID),
					},
				},
			},
		},
		{
			name: "basic create target",
			fields: TargetFields{
				IDGenerator: mock.NewIDGenerator(targetTwoID, t),
				Targets: []*influxdb.ScraperTarget{
					{
						Name:     "name1",
						Type:     influxdb.PrometheusScraperType,
						OrgID:    MustIDBase16(orgOneID),
						BucketID: MustIDBase16(bucketOneID),
						URL:      "url1",
						ID:       MustIDBase16(targetOneID),
					},
				},
				Organizations: []*influxdb.Organization{&org1, &org2},
				UserResourceMappings: []*influxdb.UserResourceMapping{
					{
						ResourceID:   MustIDBase16(targetOneID),
						ResourceType: influxdb.ScraperResourceType,
						UserID:       MustIDBase16(threeID),
						UserType:     influxdb.Member,
					},
				},
			},
			args: args{
				userID: MustIDBase16(threeID),
				target: &influxdb.ScraperTarget{
					ID:       MustIDBase16(targetTwoID),
					Name:     "name2",
					Type:     influxdb.PrometheusScraperType,
					OrgID:    MustIDBase16(orgTwoID),
					BucketID: MustIDBase16(bucketTwoID),
					URL:      "url2",
				},
			},
			wants: wants{
				userResourceMappings: []*influxdb.UserResourceMapping{
					{
						ResourceID:   MustIDBase16(oneID),
						ResourceType: influxdb.ScraperResourceType,
						UserID:       MustIDBase16(threeID),
						UserType:     influxdb.Member,
					},
					{
						ResourceID:   MustIDBase16(twoID),
						ResourceType: influxdb.ScraperResourceType,
						UserID:       MustIDBase16(threeID),
						UserType:     influxdb.Owner,
					},
				},
				targets: []influxdb.ScraperTarget{
					{
						Name:     "name1",
						Type:     influxdb.PrometheusScraperType,
						OrgID:    MustIDBase16(orgOneID),
						BucketID: MustIDBase16(bucketOneID),
						URL:      "url1",
						ID:       MustIDBase16(targetOneID),
					},
					{
						Name:     "name2",
						Type:     influxdb.PrometheusScraperType,
						OrgID:    MustIDBase16(orgTwoID),
						BucketID: MustIDBase16(bucketTwoID),
						URL:      "url2",
						ID:       MustIDBase16(targetTwoID),
					},
				},
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			s, opPrefix, done := init(tt.fields, t)
			defer done()
			ctx := context.Background()
			err := s.AddTarget(ctx, tt.args.target, tt.args.userID)
			diffPlatformErrors(tt.name, err, tt.wants.err, opPrefix, t)
			defer s.RemoveTarget(ctx, tt.args.target.ID)

			targets, err := s.ListTargets(ctx, influxdb.ScraperTargetFilter{})
			if err != nil {
				t.Fatalf("failed to retrieve scraper targets: %v", err)
			}
			if diff := cmp.Diff(targets, tt.wants.targets, targetCmpOptions...); diff != "" {
				t.Errorf("scraper targets are different -got/+want\ndiff %s", diff)
			}
			urms, _, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{
				UserID:       tt.args.userID,
				ResourceType: influxdb.ScraperResourceType,
			})
			if err != nil {
				t.Fatalf("failed to retrieve user resource mappings: %v", err)
			}
			if diff := cmp.Diff(urms, tt.wants.userResourceMappings, userResourceMappingCmpOptions...); diff != "" {
				t.Errorf("user resource mappings are different -got/+want\ndiff %s", diff)
			}
		})

	}
}

// ListTargets testing
func ListTargets(
	init func(TargetFields, *testing.T) (influxdb.ScraperTargetStoreService, string, func()),
	t *testing.T,
) {
	type args struct {
		filter influxdb.ScraperTargetFilter
	}
	type wants struct {
		targets []influxdb.ScraperTarget
		err     error
	}

	tests := []struct {
		name   string
		fields TargetFields
		args   args
		wants  wants
	}{
		{
			name: "get all targets",
			fields: TargetFields{
				Organizations: []*influxdb.Organization{
					&org1,
					&org2,
				},
				Targets: []*influxdb.ScraperTarget{
					&target1,
					&target2,
					&target3,
				},
			},
			args: args{
				filter: influxdb.ScraperTargetFilter{},
			},
			wants: wants{
				targets: []influxdb.ScraperTarget{
					target1,
					target2,
					target3,
				},
			},
		},
		{
			name: "filter by name",
			fields: TargetFields{
				Organizations: []*influxdb.Organization{
					&org1,
					&org2,
				},
				Targets: []*influxdb.ScraperTarget{
					&target1,
					&target2,
					&target3,
				},
			},
			args: args{
				filter: influxdb.ScraperTargetFilter{
					Name: strPtr(target2.Name),
				},
			},
			wants: wants{
				targets: []influxdb.ScraperTarget{
					target2,
				},
			},
		},
		{
			name: "filter by id",
			fields: TargetFields{
				Organizations: []*influxdb.Organization{
					&org1,
					&org2,
				},
				Targets: []*influxdb.ScraperTarget{
					&target1,
					&target2,
					&target3,
				},
			},
			args: args{
				filter: influxdb.ScraperTargetFilter{
					IDs: map[influxdb.ID]bool{target2.ID: false},
				},
			},
			wants: wants{
				targets: []influxdb.ScraperTarget{
					target2,
				},
			},
		},
		{
			name: "filter targets by orgID",
			fields: TargetFields{
				Organizations: []*influxdb.Organization{
					&org1,
					&org2,
				},
				Targets: []*influxdb.ScraperTarget{
					&target1,
					&target2,
					&target3,
				},
			},
			args: args{
				filter: influxdb.ScraperTargetFilter{
					OrgID: idPtr(MustIDBase16(orgOneID)),
				},
			},
			wants: wants{
				targets: []influxdb.ScraperTarget{
					target1,
					target3,
				},
			},
		},
		{
			name: "filter targets by orgID not exist",
			fields: TargetFields{
				Organizations: []*influxdb.Organization{
					&org2,
				},
				Targets: []*influxdb.ScraperTarget{
					&target1,
					&target2,
					&target3,
				},
			},
			args: args{
				filter: influxdb.ScraperTargetFilter{
					OrgID: idPtr(MustIDBase16(orgOneID)),
				},
			},
			wants: wants{
				targets: []influxdb.ScraperTarget{},
				err: &influxdb.Error{
					Code: influxdb.ENotFound,
					Msg:  "organization not found",
				},
			},
		},
		{
			name: "filter targets by org name",
			fields: TargetFields{
				Organizations: []*influxdb.Organization{
					&org1,
					&org2,
				},
				Targets: []*influxdb.ScraperTarget{
					&target1,
					&target2,
					&target3,
				},
			},
			args: args{
				filter: influxdb.ScraperTargetFilter{
					Org: strPtr("org1"),
				},
			},
			wants: wants{
				targets: []influxdb.ScraperTarget{
					target1,
					target3,
				},
			},
		},
		{
			name: "filter targets by org name not exist",
			fields: TargetFields{
				Organizations: []*influxdb.Organization{
					&org1,
				},
				Targets: []*influxdb.ScraperTarget{
					&target1,
					&target2,
					&target3,
				},
			},
			args: args{
				filter: influxdb.ScraperTargetFilter{
					Org: strPtr("org2"),
				},
			},
			wants: wants{
				targets: []influxdb.ScraperTarget{},
				err: &influxdb.Error{
					Code: influxdb.ENotFound,
					Msg:  `organization name "org2" not found`,
				},
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			s, opPrefix, done := init(tt.fields, t)
			defer done()
			ctx := context.Background()
			targets, err := s.ListTargets(ctx, tt.args.filter)
			diffPlatformErrors(tt.name, err, tt.wants.err, opPrefix, t)

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

// GetTargetByID testing
func GetTargetByID(
	init func(TargetFields, *testing.T) (influxdb.ScraperTargetStoreService, string, func()),
	t *testing.T,
) {
	t.Helper()
	type args struct {
		id influxdb.ID
	}
	type wants struct {
		err    error
		target *influxdb.ScraperTarget
	}

	tests := []struct {
		name   string
		fields TargetFields
		args   args
		wants  wants
	}{
		{
			name: "basic find target by id",
			fields: TargetFields{
				Organizations: []*influxdb.Organization{&org1},
				Targets: []*influxdb.ScraperTarget{
					{
						ID:       MustIDBase16(targetOneID),
						Name:     "target1",
						OrgID:    MustIDBase16(orgOneID),
						BucketID: MustIDBase16(bucketOneID),
					},
					{
						ID:       MustIDBase16(targetTwoID),
						Name:     "target2",
						OrgID:    MustIDBase16(orgOneID),
						BucketID: MustIDBase16(bucketOneID),
					},
				},
			},
			args: args{
				id: MustIDBase16(targetTwoID),
			},
			wants: wants{
				target: &influxdb.ScraperTarget{
					ID:       MustIDBase16(targetTwoID),
					Name:     "target2",
					OrgID:    MustIDBase16(orgOneID),
					BucketID: MustIDBase16(bucketOneID),
				},
			},
		},
		{
			name: "find target by id not find",
			fields: TargetFields{
				Targets: []*influxdb.ScraperTarget{
					{
						ID:       MustIDBase16(targetOneID),
						Name:     "target1",
						OrgID:    MustIDBase16(orgOneID),
						BucketID: MustIDBase16(bucketOneID),
					},
					{
						ID:       MustIDBase16(targetTwoID),
						Name:     "target2",
						OrgID:    MustIDBase16(orgOneID),
						BucketID: MustIDBase16(bucketOneID),
					},
				},
			},
			args: args{
				id: MustIDBase16(threeID),
			},
			wants: wants{
				err: &influxdb.Error{
					Code: influxdb.ENotFound,
					Op:   influxdb.OpGetTargetByID,
					Msg:  "scraper target is not found",
				},
			},
		},
	}

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

			target, err := s.GetTargetByID(ctx, tt.args.id)
			diffPlatformErrors(tt.name, err, tt.wants.err, opPrefix, t)

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

// RemoveTarget testing
func RemoveTarget(init func(TargetFields, *testing.T) (influxdb.ScraperTargetStoreService, string, func()),
	t *testing.T) {
	type args struct {
		ID     influxdb.ID
		userID influxdb.ID
	}
	type wants struct {
		err                  error
		userResourceMappings []*influxdb.UserResourceMapping
		targets              []influxdb.ScraperTarget
	}
	tests := []struct {
		name   string
		fields TargetFields
		args   args
		wants  wants
	}{
		{
			name: "delete targets using exist id",
			fields: TargetFields{
				Organizations: []*influxdb.Organization{&org1},
				UserResourceMappings: []*influxdb.UserResourceMapping{
					{
						ResourceID:   MustIDBase16(oneID),
						UserID:       MustIDBase16(threeID),
						UserType:     influxdb.Owner,
						ResourceType: influxdb.ScraperResourceType,
					},
					{
						ResourceID:   MustIDBase16(twoID),
						UserID:       MustIDBase16(threeID),
						UserType:     influxdb.Member,
						ResourceType: influxdb.ScraperResourceType,
					},
				},
				Targets: []*influxdb.ScraperTarget{
					{
						ID:       MustIDBase16(targetOneID),
						OrgID:    MustIDBase16(orgOneID),
						BucketID: MustIDBase16(bucketOneID),
					},
					{
						ID:       MustIDBase16(targetTwoID),
						OrgID:    MustIDBase16(orgOneID),
						BucketID: MustIDBase16(bucketOneID),
					},
				},
			},
			args: args{
				ID:     MustIDBase16(targetOneID),
				userID: MustIDBase16(threeID),
			},
			wants: wants{
				userResourceMappings: []*influxdb.UserResourceMapping{
					{
						ResourceID:   MustIDBase16(twoID),
						UserID:       MustIDBase16(threeID),
						UserType:     influxdb.Member,
						ResourceType: influxdb.ScraperResourceType,
					},
				},
				targets: []influxdb.ScraperTarget{
					{
						ID:       MustIDBase16(targetTwoID),
						OrgID:    MustIDBase16(orgOneID),
						BucketID: MustIDBase16(bucketOneID),
					},
				},
			},
		},
		{
			name: "delete targets using id that does not exist",
			fields: TargetFields{
				Organizations: []*influxdb.Organization{&org1},
				UserResourceMappings: []*influxdb.UserResourceMapping{
					{
						ResourceID:   MustIDBase16(oneID),
						UserID:       MustIDBase16(threeID),
						UserType:     influxdb.Owner,
						ResourceType: influxdb.ScraperResourceType,
					},
					{
						ResourceID:   MustIDBase16(twoID),
						UserID:       MustIDBase16(threeID),
						UserType:     influxdb.Member,
						ResourceType: influxdb.ScraperResourceType,
					},
				},
				Targets: []*influxdb.ScraperTarget{
					{
						ID:       MustIDBase16(targetOneID),
						OrgID:    MustIDBase16(orgOneID),
						BucketID: MustIDBase16(bucketOneID),
					},
					{
						ID:       MustIDBase16(targetTwoID),
						OrgID:    MustIDBase16(orgOneID),
						BucketID: MustIDBase16(bucketOneID),
					},
				},
			},
			args: args{
				ID:     MustIDBase16(targetThreeID),
				userID: MustIDBase16(threeID),
			},
			wants: wants{
				err: &influxdb.Error{
					Code: influxdb.ENotFound,
					Op:   influxdb.OpRemoveTarget,
					Msg:  "scraper target is not found",
				},
				targets: []influxdb.ScraperTarget{
					{
						ID:       MustIDBase16(targetOneID),
						OrgID:    MustIDBase16(orgOneID),
						BucketID: MustIDBase16(bucketOneID),
					},
					{
						ID:       MustIDBase16(targetTwoID),
						OrgID:    MustIDBase16(orgOneID),
						BucketID: MustIDBase16(bucketOneID),
					},
				},
				userResourceMappings: []*influxdb.UserResourceMapping{
					{
						ResourceID:   MustIDBase16(oneID),
						UserID:       MustIDBase16(threeID),
						UserType:     influxdb.Owner,
						ResourceType: influxdb.ScraperResourceType,
					},
					{
						ResourceID:   MustIDBase16(twoID),
						UserID:       MustIDBase16(threeID),
						UserType:     influxdb.Member,
						ResourceType: influxdb.ScraperResourceType,
					},
				},
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			s, opPrefix, done := init(tt.fields, t)
			defer done()
			ctx := context.Background()
			err := s.RemoveTarget(ctx, tt.args.ID)
			diffPlatformErrors(tt.name, err, tt.wants.err, opPrefix, t)

			targets, err := s.ListTargets(ctx, influxdb.ScraperTargetFilter{})
			if err != nil {
				t.Fatalf("failed to retrieve targets: %v", err)
			}
			if diff := cmp.Diff(targets, tt.wants.targets, targetCmpOptions...); diff != "" {
				t.Errorf("targets are different -got/+want\ndiff %s", diff)
			}
			urms, _, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{
				UserID:       tt.args.userID,
				ResourceType: influxdb.ScraperResourceType,
			})
			if err != nil {
				t.Fatalf("failed to retrieve user resource mappings: %v", err)
			}
			if diff := cmp.Diff(urms, tt.wants.userResourceMappings, userResourceMappingCmpOptions...); diff != "" {
				t.Errorf("user resource mappings are different -got/+want\ndiff %s", diff)
			}
		})
	}
}

// UpdateTarget testing
func UpdateTarget(
	init func(TargetFields, *testing.T) (influxdb.ScraperTargetStoreService, string, func()),
	t *testing.T,
) {
	type args struct {
		url    string
		userID influxdb.ID
		id     influxdb.ID
	}
	type wants struct {
		err    error
		target *influxdb.ScraperTarget
	}

	tests := []struct {
		name   string
		fields TargetFields
		args   args
		wants  wants
	}{
		{
			name: "update url with blank id",
			fields: TargetFields{
				Organizations: []*influxdb.Organization{&org1},
				Targets: []*influxdb.ScraperTarget{
					{
						ID:       MustIDBase16(targetOneID),
						URL:      "url1",
						OrgID:    MustIDBase16(orgOneID),
						BucketID: MustIDBase16(bucketOneID),
					},
					{
						ID:       MustIDBase16(targetTwoID),
						URL:      "url2",
						OrgID:    MustIDBase16(orgOneID),
						BucketID: MustIDBase16(bucketOneID),
					},
				},
			},
			args: args{
				url: "changed",
			},
			wants: wants{
				err: &influxdb.Error{
					Code: influxdb.EInvalid,
					Op:   influxdb.OpUpdateTarget,
					Msg:  "provided scraper target ID has invalid format",
				},
			},
		},
		{
			name: "update url with non exist id",
			fields: TargetFields{
				Organizations: []*influxdb.Organization{&org1},
				Targets: []*influxdb.ScraperTarget{
					{
						ID:       MustIDBase16(targetOneID),
						URL:      "url1",
						OrgID:    MustIDBase16(orgOneID),
						BucketID: MustIDBase16(bucketOneID),
					},
					{
						ID:       MustIDBase16(targetTwoID),
						URL:      "url2",
						OrgID:    MustIDBase16(orgOneID),
						BucketID: MustIDBase16(bucketOneID),
					},
				},
			},
			args: args{
				id:  MustIDBase16(targetThreeID),
				url: "changed",
			},
			wants: wants{
				err: &influxdb.Error{
					Code: influxdb.ENotFound,
					Op:   influxdb.OpUpdateTarget,
					Msg:  "scraper target is not found",
				},
			},
		},
		{
			name: "update url",
			fields: TargetFields{
				Organizations: []*influxdb.Organization{&org1},
				Targets: []*influxdb.ScraperTarget{
					{
						ID:       MustIDBase16(targetOneID),
						URL:      "url1",
						OrgID:    MustIDBase16(orgOneID),
						BucketID: MustIDBase16(bucketOneID),
					},
					{
						ID:       MustIDBase16(targetTwoID),
						URL:      "url2",
						OrgID:    MustIDBase16(orgOneID),
						BucketID: MustIDBase16(bucketOneID),
					},
				},
			},
			args: args{
				id:  MustIDBase16(targetOneID),
				url: "changed",
			},
			wants: wants{
				target: &influxdb.ScraperTarget{
					ID:       MustIDBase16(targetOneID),
					URL:      "changed",
					OrgID:    MustIDBase16(orgOneID),
					BucketID: MustIDBase16(bucketOneID),
				},
			},
		},
	}

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

			upd := &influxdb.ScraperTarget{
				ID:  tt.args.id,
				URL: tt.args.url,
			}

			target, err := s.UpdateTarget(ctx, upd, tt.args.userID)
			diffPlatformErrors(tt.name, err, tt.wants.err, opPrefix, t)

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