Skip to content

Commit

Permalink
Automatically record and play back test recordings (#2033)
Browse files Browse the repository at this point in the history
* Automatically record and play back test recordings

Resolves #1876

* Require `Result<T, E>` from recorded tests

This is simpler to support in the `#[recorded::test]` macro and sets a good precedent we want to show users anyway instead of `unwrap()` or `expect()`.

* Fix azure_core_test_macro tests

* Switch to multi-threaded async runtime and send correct headers

The default single-threaded async runtime runner was deadlocking in `Recording::drop()`. I opened #2049 to revisit this but I don't foresee any problem. We already don't support test-proxy on wasm32 and our mix of agents we do test on support multi-threaded async runtimes just fine.

* Fix Windows tests

* Safely log headers and body

We need to sanitize the headers later, but easier just to sanitize them all right now. This is still better than before which only showed how many headers were in the request.

* Fix regression to auto-start of test-proxy

* Resolve PR feedback
  • Loading branch information
heaths authored Feb 3, 2025
1 parent 4688e67 commit 719003d
Show file tree
Hide file tree
Showing 31 changed files with 1,097 additions and 328 deletions.
3 changes: 3 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ indent_size = 4
indent_style = space
trim_trailing_whitespace = true

[*.bicep]
indent_size = 2

[*.json]
indent_size = 2

Expand Down
7 changes: 5 additions & 2 deletions .vscode/cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,14 @@
"hmac",
"iothub",
"keyvault",
"lldb",
"maxresults",
"maxsize",
"msrc",
"newtonsoft",
"oidc",
"pageable",
"pageables",
"pkce",
"pkcs",
"posix",
Expand All @@ -60,7 +62,8 @@
"upvote",
"userdelegationkey",
"versionid",
"virtualmachine"
"virtualmachine",
"worktree"
],
"dictionaryDefinitions": [
{
Expand Down Expand Up @@ -160,4 +163,4 @@
]
}
]
}
}
76 changes: 75 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

When you run `cargo build`, toolchain version [1.80](https://releases.rs/docs/1.80.0/) and necessary components will be installed automatically.

- (Recommended) If you use [Visual Studio Code](https://code.visualstudio.com), install recommended extensions to improve your development experience.
- (Recommended) If you use [Visual Studio Code], install recommended extensions to improve your development experience.

## Generated code

Expand All @@ -21,6 +21,77 @@ To build any library in the Azure SDK for Rust navigate to the library's project
[TODO] Add instructions on how to run tests for a specific project.
[TODO] Add instructions for write new tests.

### Debugging with Visual Studio Code

[Visual Studio Code] with recommended extensions installed can be used to run and debug tests for a module or individual tests.

If you need to debug a test, you can use the LLDB extension and set environment variables as needed. For example, to debug recording a specific test,
your `.vscode/launch.json` file might look something like:

```json
{
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Record secret_roundtrip",
"cargo": {
"args": [
"test",
"--no-run",
"--test=secret_client",
"--package=azure_security_keyvault_secrets",
"secret_roundtrip"
],
"filter": {
"name": "secret_client",
"kind": "test"
},
"env": {
}
},
"cwd": "${workspaceFolder}",
"env": {
"AZURE_KEYVAULT_URL": "https://my-vault.vault.azure.net/",
"PROXY_MANUAL_START": "true",
"RUST_LOG": "trace"
}
},
{
"type": "lldb",
"request": "launch",
"name": "Play back secret_roundtrip",
"cargo": {
"args": [
"test",
"--no-run",
"--test=secret_client",
"--package=azure_security_keyvault_secrets",
"secret_roundtrip"
],
"filter": {
"name": "secret_client",
"kind": "test"
},
"env": {
"AZURE_TEST_MODE": "playback"
}
},
"cwd": "${workspaceFolder}",
"env": {
"RUST_LOG": "trace"
}
}
]
}
```

You can also start the [Test Proxy] manually, in which can you add to the outer `env` above to `"PROXY_MANUAL_START": "true"`.

To enable tracing, you can add the `RUST_LOG` environment variable as shown above using the [same format supported by `env_logger`](https://docs.rs/env_logger/latest/env_logger/#enabling-logging).
The targets are the crate names if you want to trace more or less for specific targets e.g., `RUST_LOG=info,azure_core=trace` to trace information messages by default but detailed traces for the `azure_core` crate.

## Code Review Process

Before a pull request will be considered by the Azure SDK team, the following requirements must be met:
Expand Down Expand Up @@ -94,3 +165,6 @@ Samples may take the following categories of dependencies:
- **Tiered licensed**: Offerings that enable readers to use the license tier that corresponds to their characteristics. For example, tiers may be available for students, hobbyists, or companies with defined revenue thresholds. For offerings with tiered licenses, strive to limit our use in tutorials to the features available in the lowest tier. This policy enables the widest audience for the article. [Docker](https://www.docker.com/), [IdentityServer](https://duendesoftware.com/products/identityserver), [ImageSharp](https://sixlabors.com/products/imagesharp/), and [Visual Studio](https://visualstudio.com) are examples of this license type.

In general, we prefer taking dependencies on licensed components in the order of the listed categories. In cases where the category may not be well known, we'll document the category so that readers understand the choice that they're making by using that dependency.

[Test Proxy]: https://github.com/Azure/azure-sdk-tools/blob/main/tools/test-proxy/Azure.Sdk.Tools.TestProxy/README.md
[Visual Studio Code]: https://code.visualstudio.com
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 8 additions & 2 deletions sdk/core/azure_core/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ impl fmt::Debug for TestMode {
}
}

impl From<&TestMode> for &'static str {
fn from(mode: &TestMode) -> Self {
impl From<TestMode> for &'static str {
fn from(mode: TestMode) -> Self {
match mode {
TestMode::Playback => "playback",
TestMode::Record => "record",
Expand All @@ -46,6 +46,12 @@ impl From<&TestMode> for &'static str {
}
}

impl From<&TestMode> for &'static str {
fn from(mode: &TestMode) -> Self {
TestMode::into(*mode)
}
}

impl FromStr for TestMode {
type Err = Error;

Expand Down
20 changes: 19 additions & 1 deletion sdk/core/azure_core_test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,36 @@ keywords = ["sdk", "azure", "rest", "iot", "cloud"]
categories = ["development-tools::testing"]
edition.workspace = true
rust-version.workspace = true
publish = false

[features]
default = []
tracing = ["tracing-subscriber"]

[dependencies]
async-trait.workspace = true
azure_core = { workspace = true, features = ["test"] }
azure_core_test_macros.workspace = true
azure_identity.workspace = true
futures.workspace = true
serde.workspace = true
serde_json.workspace = true
tracing.workspace = true
tracing-subscriber = { workspace = true, features = [
"env-filter",
"fmt",
], optional = true }
typespec_client_core = { workspace = true, features = ["derive"] }
url.workspace = true

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
tokio = { workspace = true, features = ["io-util", "process", "sync", "time"] }
tokio = { workspace = true, features = [
"io-util",
"process",
"rt-multi-thread",
"sync",
"time",
] }

[dev-dependencies]
clap.workspace = true
Expand Down
10 changes: 6 additions & 4 deletions sdk/core/azure_core_test/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,24 @@ The types and functions in this crate help test client libraries built on `azure

## Client methods

To test client methods using our [Test Proxy], you can attribute both synchronous and asynchronous (recommend) tests
To test client methods using our [Test Proxy] or run against live resources, you can attribute asynchronous tests
using the `#[recorded::test]` attribute:

```rust
use azure_core_test::{recorded, TestContext};
use azure_core::Result;
use azure_core_test::{recorded, TestContext};

#[recorded::test]
async fn get_secret(ctx: TestContext) -> Result<()> {
todo!()
}
```

The `TestContext` parameter is required unless your test function is attribute as `#[recorded::test(live)]` (live-only),
in which case it is optional. You can name the parameter whatever you want.
The `TestContext` parameter is required unless your test function is attributed as `#[recorded::test(live)]` (live-only).
You can name the parameter whatever you want.
The `TestContext` parameter is used to initialize an HTTP client to play back or record tests
and provides other information to test functions that may be useful.

These tests must also return a `std::result::Result<T, E>`, which can be redefined e.g., `azure_core::Result<T>`.

[Test Proxy]: https://github.com/Azure/azure-sdk-tools/blob/main/tools/test-proxy/Azure.Sdk.Tools.TestProxy/README.md
Loading

0 comments on commit 719003d

Please sign in to comment.