Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Internal] Variable storage #16375

Merged
merged 6 commits into from
Dec 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion sdk/internal/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
# Release History

## 0.8.3 (2021-11-30)
## 0.8.3 (2021-12-07)

### Features Added
* If `AZURE_RECORD_MODE` is not set, default to `playback`
* If `PROXY_CERT` is not set, try to find it based on the `GOPATH` environment variable and the path to `eng/common/testproxy/dotnet-devcert.crt`
* Adds `NewRecordingHTTPClient()` method which returns an `azcore.Transporter` interface that routes requests to the test proxy [#16221](https://github.com/Azure/azure-sdk-for-go/pull/16221).
* Adds the `SetBodilessMatcher` method [#16256](https://github.com/Azure/azure-sdk-for-go/pull/16256)
* Added variables storage to the `Stop` function. Pass in a `map[string]interface{}` to the `Stop` method options and the values can be retrieved with the `GetVariables(t *testing.T)` function [#16375](https://github.com/Azure/azure-sdk-for-go/pull/16375).

### Breaking Changes
* Renames `ResetSanitizers` to `ResetProxy` [#16256](https://github.com/Azure/azure-sdk-for-go/pull/16256)
Expand Down
58 changes: 56 additions & 2 deletions sdk/internal/recording/recording.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"bytes"
"crypto/tls"
"crypto/x509"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
Expand Down Expand Up @@ -492,6 +493,7 @@ const (
type recordedTest struct {
recordingId string
liveOnly bool
variables map[string]interface{}
}

var testSuite = map[string]recordedTest{}
Expand All @@ -505,6 +507,7 @@ var client = http.Client{
type RecordingOptions struct {
UseHTTPS bool
GroupForReplace string
Variables map[string]interface{}
}

func defaultOptions() *RecordingOptions {
Expand Down Expand Up @@ -548,6 +551,7 @@ func getTestId(pathToRecordings string, t *testing.T) string {
return path.Join(pathToRecordings, "recordings", t.Name()+".json")
}

// Start tells the test proxy to begin accepting requests for a given test
func Start(t *testing.T, pathToRecordings string, options *RecordingOptions) error {
if options == nil {
options = defaultOptions()
Expand Down Expand Up @@ -580,20 +584,42 @@ func Start(t *testing.T, pathToRecordings string, options *RecordingOptions) err
recId := resp.Header.Get(IDHeader)
if recId == "" {
b, err := ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return err
}
return fmt.Errorf("Recording ID was not returned by the response. Response body: %s", b)
}

// Unmarshal any variables returned by the proxy
var m map[string]interface{}
body, err := ioutil.ReadAll(resp.Body)
jhendrixMSFT marked this conversation as resolved.
Show resolved Hide resolved
defer resp.Body.Close()
if err != nil {
return err
}
if len(body) > 0 {
err = json.Unmarshal(body, &m)
if err != nil {
return err
}
}

if val, ok := testSuite[t.Name()]; ok {
val.recordingId = recId
val.variables = m
testSuite[t.Name()] = val
} else {
testSuite[t.Name()] = recordedTest{recordingId: recId, liveOnly: false}
testSuite[t.Name()] = recordedTest{
recordingId: recId,
liveOnly: false,
variables: m,
}
}
return nil
}

// Stop tells the test proxy to stop accepting requests for a given test
func Stop(t *testing.T, options *RecordingOptions) error {
if options == nil {
options = defaultOptions()
Expand All @@ -614,13 +640,32 @@ func Stop(t *testing.T, options *RecordingOptions) error {
if err != nil {
return err
}
if len(options.Variables) > 0 {
req.Header.Set("Content-Type", "application/json")
marshalled, err := json.Marshal(options.Variables)
if err != nil {
return err
}
req.Body = ioutil.NopCloser(bytes.NewReader(marshalled))
req.ContentLength = int64(len(marshalled))
}

var recTest recordedTest
var ok bool
if recTest, ok = testSuite[t.Name()]; !ok {
return errors.New("Recording ID was never set. Did you call StartRecording?")
}
req.Header.Set("x-recording-id", recTest.recordingId)
_, err = client.Do(req)
resp, err := client.Do(req)
jhendrixMSFT marked this conversation as resolved.
Show resolved Hide resolved
if resp.StatusCode != 200 {
b, err := ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
if err == nil {
return fmt.Errorf("proxy did not stop the recording properly: %s", string(b))
}
return fmt.Errorf("proxy did not stop the recording properly: %s", err.Error())
}
_ = resp.Body.Close()
return err
}

Expand Down Expand Up @@ -687,6 +732,7 @@ func (c RecordingHTTPClient) Do(req *http.Request) (*http.Response, error) {
return c.defaultClient.Do(req)
}

// NewRecordingHTTPClient returns a type that implements `azcore.Transporter`. This will automatically route tests on the `Do` call.
func NewRecordingHTTPClient(t *testing.T, options *RecordingOptions) (*RecordingHTTPClient, error) {
if options == nil {
options = &RecordingOptions{UseHTTPS: true}
Expand Down Expand Up @@ -721,3 +767,11 @@ func IsLiveOnly(t *testing.T) bool {
}
return false
}

// GetVariables returns access to the variables stored by the test proxy for a specific test
func GetVariables(t *testing.T) map[string]interface{} {
seankane-msft marked this conversation as resolved.
Show resolved Hide resolved
if s, ok := testSuite[t.Name()]; ok {
return s.variables
}
return nil
}
49 changes: 37 additions & 12 deletions sdk/internal/recording/recording_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -569,22 +569,47 @@ func TestFindProxyCertLocation(t *testing.T) {
require.Contains(t, location, filepath.Join("eng", "common", "testproxy", "dotnet-devcert.crt"))
}

func TestModeNotSet(t *testing.T) {
proxyMode, _ := os.LookupEnv("AZURE_RECORD_MODE")
defer os.Setenv("AZURE_RECORD_MODE", proxyMode)
func TestVariables(t *testing.T) {
temp := recordMode
recordMode = RecordingMode
defer func() { recordMode = temp }()

os.Unsetenv("AZURE_RECORD_MODE")
require.Equal(t, PlaybackMode, GetRecordMode())
}
err := Start(t, packagePath, nil)
require.NoError(t, err)

client, err := NewRecordingHTTPClient(t, nil)
require.NoError(t, err)

func TestModeNotSetStartStop(t *testing.T) {
proxyMode, _ := os.LookupEnv("AZURE_RECORD_MODE")
defer os.Setenv("AZURE_RECORD_MODE", proxyMode)
req, err := http.NewRequest("POST", "https://azsdkengsys.azurecr.io/acr/v1/some_registry/_tags", nil)
require.NoError(t, err)

os.Unsetenv("AZURE_RECORD_MODE")
err := Start(t, packagePath, nil)
resp, err := client.Do(req)
require.NoError(t, err)
require.NotNil(t, resp)

require.NotNil(t, GetRecordingId(t))

err = Stop(t, &RecordingOptions{Variables: map[string]interface{}{"key1": "value1", "key2": "1"}})
require.NoError(t, err)

recordMode = PlaybackMode
err = Start(t, packagePath, nil)
require.NoError(t, err)
require.Equal(t, PlaybackMode, GetRecordMode())

variables := GetVariables(t)
require.Equal(t, variables["key1"], "value1")
require.Equal(t, variables["key2"], "1")

err = Stop(t, nil)
require.NoError(t, err)

// Make sure the file is there
jsonFile, err := os.Open(fmt.Sprintf("./testdata/recordings/%s.json", t.Name()))
require.NoError(t, err)
defer func() {
err = jsonFile.Close()
require.NoError(t, err)
err = os.Remove(jsonFile.Name())
require.NoError(t, err)
}()
}
1 change: 1 addition & 0 deletions sdk/internal/recording/sanitizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ func handleProxyResponse(resp *http.Response, err error) error {
}

body, err := ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return err
}
Expand Down