package
1.10.0
Repository: https://github.com/azure/azure-sdk-for-go.git
Documentation: pkg.go.dev

# README

Azure SDK for Go Recorded Test Framework

Build Status

The recording package makes it easy to add recorded tests to your track-2 client package. Below are some examples that walk through setting up a recorded test end to end.

Set Up

The recording package supports three different test modes. These modes are set by setting the AZURE_RECORD_MODE environment variable to one of the three values:

  1. record: Used when making requests against real resources. In this mode new recording will be generated and saved to file.
  2. playback: This mode is for running tests against local recordings.
  3. live: This mode should not be used locally, it is used by the nightly live pipelines to run against real resources and skip any routing to the proxy. This mode closely mimics how our customers will use the libraries.

After you've set the AZURE_RECORD_MODE, set the PROXY_CERT environment variable to:

$ENV:PROXY_CERT="C:/ <path-to-repo> /azure-sdk-for-go/eng/common/testproxy/dotnet-devcert.crt"

Running the test proxy

Recording and playing back tests relies on the Test Proxy to intercept traffic. The recording package can automatically install and run an instance of the test-proxy server per package. The below code needs to be added to test setup and teardown in order to achieve this. The service directory value should correspond to the testdata directory within the directory containing the assets.json config, see examples.

const recordingDirectory = "<path to service directory with assets.json file>/testdata"

func TestMain(m *testing.M) {
	code := run(m)
	os.Exit(code)
}

func run(m *testing.M) int {
	if recording.GetRecordMode() == recording.PlaybackMode || recording.GetRecordMode() == recording.RecordingMode {
        proxy, err := recording.StartTestProxy(recordingDirectory, nil)
        if err != nil {
            panic(err)
        }

        // NOTE: defer should not be used directly within TestMain as it will not be executed due to os.Exit()
		defer func() {
			err := recording.StopTestProxy(proxy)
			if err != nil {
				panic(err)
			}
		}()
    }

    ... all other test code, including proxy recording setup ...
	return m.Run()
}

Routing Traffic

The first step in instrumenting a client to interact with recorded tests is to direct traffic to the proxy through a custom policy. In these examples we'll use testify's require library but you can use the framework of your choice. Each test has to call recording.Start and recording.Stop, the rest is taken care of by the recording library and the test-proxy.

The snippet below demonstrates an example test policy:

type recordingPolicy struct {
	options recording.RecordingOptions
	t       *testing.T
}

func (r recordingPolicy) Host() string {
	if r.options.UseHTTPS {
		return "localhost:5001"
	}
	return "localhost:5000"
}

func (r recordingPolicy) Scheme() string {
	if r.options.UseHTTPS {
		return "https"
	}
	return "http"
}

func NewRecordingPolicy(t *testing.T, o *recording.RecordingOptions) policy.Policy {
	if o == nil {
		o = &recording.RecordingOptions{UseHTTPS: true}
	}
	p := &recordingPolicy{options: *o, t: t}
	return p
}

func (p *recordingPolicy) Do(req *policy.Request) (resp *http.Response, err error) {
	if recording.GetRecordMode() != "live" {
		p.options.ReplaceAuthority(t, req.Raw())
	}
	return req.Next()
}

After creating a recording policy, it has to be added to the client on the ClientOptions.PerCallPolicies option:

func TestSomething(t *testing.T) {
    p := NewRecordingPolicy(t)
    httpClient, err := recording.GetHTTPClient(t)
    require.NoError(t, err)

	options := &ClientOptions{
		ClientOptions: azcore.ClientOptions{
			PerCallPolicies: []policy.Policy{p},
			Transport:       client,
		},
	}

    client, err := NewClient("https://mystorageaccount.table.core.windows.net", myCred, options)
    require.NoError(t, err)
    // Continue test
}

Starting and Stopping a Test

To start and stop your tests use the recording.Start and recording.Stop (make sure to use defer recording.Stop to ensure the proxy cleans up your test on failure) methods:

func TestSomething(t *testing.T) {
    err := recording.Start(t, recordingDirectory, nil)
    defer recording.Stop(t, recordingDirectory, nil)

    // Continue test
}

Using Sanitizers

The recording files generated by the test-proxy are committed along with the code to the public repository. We have to keep our recording files free of secrets that can be used by bad actors to infilitrate services. To do so, the recording package has several sanitizers for taking care of this. Sanitizers are added at the session level (ie. for an entire test run) to apply to all recordings generated during a test run. For example, to replace the account name from a storage url use the recording.AddURISanitizer method:

func TestSomething(t *testing.T) {
    err := recording.AddURISanitizer("fakeaccountname", "my-real-account-name", nil)
    require.NoError(t, err)

    // To remove the sanitizer after this test use the following:
    defer recording.ResetSanitizers(nil)

    err := recording.Start(t, recordingDirectory, nil)
    defer recording.Stop(t, recordingDirectory, nil)

    // Continue test
}

In addition to URI sanitizers, there are sanitizers for headers, response bodies, OAuth responses, continuation tokens, and more. For more information about all the sanitizers check out the source code

Reading Environment Variables

The CI pipelines for PRs do not run against live resources, you will need to make sure that the values that are replaced in the recording files are also replaced in your requests when running in playback. The best way to do this is to use the recording.GetEnvVariable and use the replaced value as the recordedValue argument:

func TestSomething(t *testing.T) {
    accountName := recording.GetEnvVariable(t, "TABLES_PRIMARY_ACCOUNT_NAME", "fakeaccountname")
    if recording.GetRecordMode() = recording.RecordMode {
        err := recording.AddURISanitizer("fakeaccountname", accountName, nil)
        require.NoError(t, err)
    }

    // Continue test
}

In this snippet, if the test is running in live mode and we have the real account name, we want to add a URI sanitizer for the account name to ensure the value does not appear in any recordings.

# Functions

AddBodyKeySanitizer adds a sanitizer for JSON Bodies.
AddBodyRegexSanitizer offers regex replace within a returned JSON body.
AddContinuationSanitizer is used to anonymize private keys in response/request pairs.
AddGeneralRegexSanitizer adds a general regex across request/response Body, Headers, and URI.
AddHeaderRegexSanitizer can be used to replace a key with a specific value: set regex to "" OR can be used to do a simple regex replace operation by setting key, value, and regex.
AddOAuthResponseSanitizer cleans all request/response pairs taht match an oauth regex in their URI.
AddRemoveHeaderSanitizer removes a list of headers from request/responses.
AddURISanitizer sanitizes URIs via regex.
AddURISubscriptionIDSanitizer replaces real subscriptionIDs within a URI with a default or configured fake value.
Deprecated: the local sanitizer API that uses this function is no longer supported.
GenerateAlphaNumericID will generate a recorded random alpha numeric id.
GetEnvVariable looks up an environment variable and if it is not found, returns the recordedValue.
No description provided by the author
No description provided by the author
No description provided by the author
GetVariables returns access to the variables stored by the test proxy for a specific test.
No description provided by the author
No description provided by the author
NewRecording initializes a new Recording instance.
NewRecordingHTTPClient returns a type that implements `azcore.Transporter`.
NewTestContext initializes a new TestContext.
RemoveRegisteredSanitizers selectively disables sanitizers that are enabled by default such as "AZSDK1001".
ResetProxy restores the proxy's default sanitizers, matchers, and transforms.
SetBodilessMatcher adjusts the "match" operation to exclude the body when matching a request to a recording's entries.
SetDefaultMatcher adjusts the "match" operation to exclude the body when matching a request to a recording's entries.
Sleep during a test for `duration` seconds.
No description provided by the author
No description provided by the author
Stop tells the test proxy to stop accepting requests for a given test.
NOTE: The process will be killed if the user hits ctrl-c mid-way through tests, as go will kill child processes when the main process does not exit cleanly.

# Constants

No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
NoSanitization indicates that the recorded value should not be sanitized.
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
SanitizedBase64Value is the default placeholder value to be used for sanitized base-64 encoded strings.
SanitizedValue is the default placeholder value to be used for sanitized strings.
Secret_Base64String indicates that the recorded value should be replaced with a sanitized valid base-64 string value.
Secret_String indicates that the recorded value should be replaced with a sanitized value.
No description provided by the author

# Structs

Optional parameters for the SetBodilessMatcher operation.
Deprecated: the local recording API that uses this type is no longer supported.
No description provided by the author
No description provided by the author
Deprecated: the local recording API that uses this type is no longer supported.
Deprecated: the local recording API that uses this type is no longer supported.
No description provided by the author
No description provided by the author

# Interfaces

Deprecated: only deprecated methods use this type.

# Type aliases

Deprecated: only deprecated methods use this type.
Deprecated: only deprecated methods use this type.
Deprecated: only deprecated methods use this type.
No description provided by the author
Deprecated: only deprecated methods use this type.
StringSanitizer is a func that will modify the string pointed to by the parameter into a sanitized value.
Deprecated: only deprecated methods use this type.