/
common.go
225 lines (196 loc) · 7.53 KB
/
common.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
//go:build go1.18
// +build go1.18
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
// Contains common helpers for TESTS ONLY
package testcommon
import (
"bytes"
"context"
"encoding/base64"
"encoding/binary"
"errors"
"fmt"
"hash/crc64"
"io"
"math/rand"
"os"
"runtime"
"strconv"
"strings"
"testing"
"time"
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/streaming"
"github.com/Azure/azure-sdk-for-go/sdk/internal/recording"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/bloberror"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blockblob"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/internal/shared"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
)
const (
RecordingDirectory = "sdk/storage/azblob/testdata"
ContainerPrefix = "goc"
BlobPrefix = "gotestblob"
BlockBlobDefaultData = "GoBlockBlobData"
InvalidHeaderErrorSubstring = "invalid header field" // error thrown by the http client
)
func GenerateContainerName(testName string) string {
return ContainerPrefix + GenerateEntityName(testName)
}
// This function generates an entity name by concatenating the passed prefix,
// the name of the test requesting the entity name, and the minute, second, and nanoseconds of the call.
// This should make it easy to associate the entities with their test, uniquely identify
// them, and determine the order in which they were created.
// Note that this imposes a restriction on the length of test names
func GenerateName(prefix string) string {
// These next lines up through the for loop are obtaining and walking up the stack
// trace to extract the test name, which is stored in name
pc := make([]uintptr, 10)
runtime.Callers(0, pc)
frames := runtime.CallersFrames(pc)
name := ""
for f, next := frames.Next(); next; f, next = frames.Next() {
name = f.Function
if strings.Contains(name, "Suite") {
break
}
}
funcNameStart := strings.Index(name, "Test")
name = name[funcNameStart+len("Test"):] // Just get the name of the test and not any of the garbage at the beginning
name = strings.ToLower(name) // Ensure it is a valid resource name
currentTime := time.Now()
name = fmt.Sprintf("%s%s%d%d%d", prefix, strings.ToLower(name), currentTime.Minute(), currentTime.Second(), currentTime.Nanosecond())
return name
}
func GenerateEntityName(testName string) string {
return strings.ReplaceAll(strings.ReplaceAll(strings.ToLower(testName), "/", ""), "test", "")
}
func GenerateBlobName(testName string) string {
return BlobPrefix + GenerateEntityName(testName)
}
func GetReaderToGeneratedBytes(n int) io.ReadSeekCloser {
r, _ := GenerateData(n)
return streaming.NopCloser(r)
}
func GetDataAndReader(testName string, n int) (*bytes.Reader, []byte) {
// Random seed for data generation
seed := int64(crc64.Checksum([]byte(testName), shared.CRC64Table))
random := rand.New(rand.NewSource(seed))
data := make([]byte, n)
_, _ = random.Read(data)
return bytes.NewReader(data), data
}
const random64BString string = "2SDgZj6RkKYzJpu04sweQek4uWHO8ndPnYlZ0tnFS61hjnFZ5IkvIGGY44eKABov"
func GenerateData(sizeInBytes int) (io.ReadSeekCloser, []byte) {
data := make([]byte, sizeInBytes)
_len := len(random64BString)
if sizeInBytes > _len {
count := sizeInBytes / _len
if sizeInBytes%_len != 0 {
count = count + 1
}
copy(data[:], strings.Repeat(random64BString, count))
} else {
copy(data[:], random64BString)
}
return streaming.NopCloser(bytes.NewReader(data)), data
}
func GetRelativeTimeGMT(amount time.Duration) time.Time {
currentTime := time.Now().In(time.FixedZone("GMT", 0))
currentTime = currentTime.Add(amount * time.Second)
return currentTime
}
func GetRelativeTimeFromAnchor(anchorTime *time.Time, amount time.Duration) time.Time {
return anchorTime.Add(amount * time.Second)
}
func GenerateBlockIDsList(count int) []string {
blockIDs := make([]string, count)
for i := 0; i < count; i++ {
blockIDs[i] = BlockIDIntToBase64(i)
}
return blockIDs
}
// BlockIDIntToBase64 functions convert an int block ID to a base-64 string and vice versa
func BlockIDIntToBase64(blockID int) string {
binaryBlockID := (&[4]byte{})[:]
binary.LittleEndian.PutUint32(binaryBlockID, uint32(blockID))
return base64.StdEncoding.EncodeToString(binaryBlockID)
}
func BlobListToMap(list []string) map[string]bool {
out := make(map[string]bool)
for _, v := range list {
out[v] = true
}
return out
}
func ValidateHTTPErrorCode(_require *require.Assertions, err error, code int) {
_require.Error(err)
var responseErr *azcore.ResponseError
errors.As(err, &responseErr)
if responseErr != nil {
_require.Equal(responseErr.StatusCode, code)
} else {
_require.Equal(strings.Contains(err.Error(), strconv.Itoa(code)), true)
}
}
func ValidateBlobErrorCode(_require *require.Assertions, err error, code bloberror.Code) {
_require.Error(err)
var responseErr *azcore.ResponseError
errors.As(err, &responseErr)
if responseErr != nil {
_require.Equal(string(code), responseErr.ErrorCode)
} else {
_require.Contains(err.Error(), code)
}
}
func ValidateUpload(ctx context.Context, _require *require.Assertions, blobClient *blockblob.Client) {
resp, err := blobClient.DownloadStream(ctx, nil)
_require.NoError(err)
data, err := io.ReadAll(resp.Body)
_require.NoError(err)
_require.Len(data, 0)
}
// GetRequiredEnv gets an environment variable by name and returns an error if it is not found
func GetRequiredEnv(name string) (string, error) {
env, ok := os.LookupEnv(name)
if ok {
return env, nil
} else {
return "", errors.New("Required environment variable not set: " + name)
}
}
func SetupSuite(suite *suite.Suite) *recording.TestProxyInstance {
proxy, err := recording.StartTestProxy(RecordingDirectory, nil)
if err != nil {
suite.T().Fatal(err)
}
return proxy
}
func TearDownSuite(suite *suite.Suite, proxy *recording.TestProxyInstance) {
err := recording.StopTestProxy(proxy)
if err != nil {
suite.T().Fatal(err)
}
}
func BeforeTest(t *testing.T, suite string, test string) {
const urlRegex = `https://\S+\.blob\.core\.windows\.net`
const tokenRegex = `(?:Bearer\s).*`
//const queryParamRegex = `=([^&|\n|\t\s]+)` // Note: Add query param name before this
require.NoError(t, recording.AddURISanitizer(FakeStorageURL, urlRegex, nil))
require.NoError(t, recording.AddHeaderRegexSanitizer("x-ms-copy-source", FakeStorageURL, urlRegex, nil))
require.NoError(t, recording.AddHeaderRegexSanitizer("x-ms-copy-source-authorization", FakeToken, tokenRegex, nil))
// we freeze request IDs and timestamps to avoid creating noisy diffs
// NOTE: we can't freeze time stamps as that breaks some tests that use if-modified-since etc (maybe it can be fixed?)
//testframework.AddHeaderRegexSanitizer("X-Ms-Date", "Wed, 10 Aug 2022 23:34:14 GMT", "", nil)
require.NoError(t, recording.AddHeaderRegexSanitizer("x-ms-request-id", "00000000-0000-0000-0000-000000000000", "", nil))
//testframework.AddHeaderRegexSanitizer("Date", "Wed, 10 Aug 2022 23:34:14 GMT", "", nil)
// TODO: more freezing
//testframework.AddBodyRegexSanitizer("RequestId:00000000-0000-0000-0000-000000000000", `RequestId:\w{8}-\w{4}-\w{4}-\w{4}-\w{12}`, nil)
//testframework.AddBodyRegexSanitizer("Time:2022-08-11T00:21:56.4562741Z", `Time:\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d*)?Z`, nil)
require.NoError(t, recording.Start(t, RecordingDirectory, nil))
}
func AfterTest(t *testing.T, suite string, test string) {
require.NoError(t, recording.Stop(t, nil))
}