Repository: pingcap/tidb
File: pkg/objstore/s3store/client_test.go (Go)

Code before:
	}, resp.Objects)
	require.True(t, s.Controller.Satisfied())

	s.MockS3.EXPECT().ListObjects(gomock.Any(), gomock.Any()).Return(nil, errors.New("mock list error"))
	_, err = cli.ListObjects(ctx, "target", nil, 100)
	require.ErrorContains(t, err, "mock list error")
	require.True(t, s.Controller.Satisfied())
}

func assertContentMD5Option(t *testing.T, optFns []func(*s3.Options), expect bool) {
	t.Helper()
	if !expect {
		require.Len(t, optFns, 0)
		return
	}
	require.Len(t, optFns, 1)
	require.Equal(t, reflect.ValueOf(withContentMD5).Pointer(), reflect.ValueOf(optFns[0]).Pointer())
}

func TestContentMD5OptionForKS3WriteRequests(t *testing.T) {
	ctx := context.Background()

	t.Run("PutObject", func(t *testing.T) {
		for _, tc := range []struct {
			name         string
			s3Compatible bool
		}{
			{name: "official_s3", s3Compatible: false},
			{name: "ks3", s3Compatible: true},
		} {
			t.Run(tc.name, func(t *testing.T) {
				s := CreateS3Suite(t)
				cli := &s3Client{
					svc:          s.MockS3,
					BucketPrefix: storeapi.NewBucketPrefix("bucket", "prefix/"),
					options:      &backuppb.S3{Bucket: "bucket"},
					isKS3:        tc.s3Compatible,
				}

				s.MockS3.EXPECT().PutObject(gomock.Any(), gomock.Any(), gomock.Any()).
					DoAndReturn(func(_ context.Context, input *s3.PutObjectInput, optFns ...func(*s3.Options)) (*s3.PutObjectOutput, error) {
						require.Equal(t, "bucket", aws.ToString(input.Bucket))
						require.Equal(t, "prefix/object", aws.ToString(input.Key))
						assertContentMD5Option(t, optFns, tc.s3Compatible)
						return &s3.PutObjectOutput{}, nil
					})

				require.NoError(t, cli.PutObject(ctx, "object", []byte("data")))
				require.True(t, s.Controller.Satisfied())
			})
		}

Review comment (suggestion):
The test case variable name `s3Compatible` is misleading. Since this PR narrows the scope from general "S3-compatible" storage to specifically "KS3", the variable should be renamed to something like `isKS3` to better reflect what it actually tests and to match the field name it's assigned to.

Code after:
	}, resp.Objects)
	require.True(t, s.Controller.Satisfied())

	s.MockS3.EXPECT().ListObjects(gomock.Any(), gomock.Any()).Return(nil, errors.New("mock list error"))
	_, err = cli.ListObjects(ctx, "target", nil, 100)
	require.ErrorContains(t, err, "mock list error")
	require.True(t, s.Controller.Satisfied())
}

func assertContentMD5Option(t *testing.T, optFns []func(*s3.Options), expect bool) {
	t.Helper()
	if !expect {
		require.Len(t, optFns, 0)
		return
	}
	require.Len(t, optFns, 1)
	require.Equal(t, reflect.ValueOf(withContentMD5).Pointer(), reflect.ValueOf(optFns[0]).Pointer())
}

func TestContentMD5OptionForS3Compatible(t *testing.T) {
	ctx := context.Background()

	t.Run("PutObject", func(t *testing.T) {
		for _, tc := range []struct {
			name         string
			s3Compatible bool
		}{
			{name: "official_s3", s3Compatible: false},
			{name: "s3_compatible", s3Compatible: true},
		} {
			t.Run(tc.name, func(t *testing.T) {
				s := CreateS3Suite(t)
				cli := &s3Client{
					svc:          s.MockS3,
					BucketPrefix: storeapi.NewBucketPrefix("bucket", "prefix/"),
					options:      &backuppb.S3{Bucket: "bucket"},
					s3Compatible: tc.s3Compatible,
				}

				s.MockS3.EXPECT().PutObject(gomock.Any(), gomock.Any(), gomock.Any()).
					DoAndReturn(func(_ context.Context, input *s3.PutObjectInput, optFns ...func(*s3.Options)) (*s3.PutObjectOutput, error) {
						require.Equal(t, "bucket", aws.ToString(input.Bucket))
						require.Equal(t, "prefix/object", aws.ToString(input.Key))
						assertContentMD5Option(t, optFns, tc.s3Compatible)
						return &s3.PutObjectOutput{}, nil
					})

				require.NoError(t, cli.PutObject(ctx, "object", []byte("data")))
				require.True(t, s.Controller.Satisfied())
			})
		}