Skip to content

About

Provides a mock server and mock proxy for testing rustup

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

9 Commits

Folders and files

Repository files navigation

rustup-mocks

Standalone mock distribution server and forward proxy for testing rustup, together with the integration tests that use them.

  • rustup-mock-server — serves a mock distribution tree over HTTP, optionally requiring Basic authentication on every request (Authorization). When started without a --directory, it generates the mock tree itself.
  • rustup-mock-proxy — a forward proxy that requires Proxy-Authorization credentials and strips them before forwarding the request to the real target.
  • tests/ — integration tests for the rustup-init binary, the rustup program, and the rustup-init.sh script: installing from the mock server and through the mock proxy, with and without Basic authentication.

Why this crate exists

rustup has no test environment of its own that covers authenticated downloads (rust-lang/rustup#1343). An attempt to add mock programs to the rustup repository was proposed and later submitted as a pull request; this crate keeps the mock programs and their tests out of the rustup source tree so that the rustup pull request only contains the feature they test (the RUSTUP_AUTHORIZATION_HEADER and RUSTUP_PROXY_AUTHORIZATION_HEADER environment variables).

The crate does not link against rustup: the rustup directory is a git submodule that is a build input only — build.rs builds rustup-init from it and records the path in RUSTUP_INIT_BIN, and the tests run that binary. src/dist_tree.rs vendors the small amount of mock distribution tree generation that the tests need (attributed in its header), so the crate works without a rustup library dependency.

Layout

rustup/                       git submodule: the rustup checkout that is
                              built to provide `rustup-init` and `rustup-init.sh`
build.rs                      builds the submodule's rustup-init and the mock
                              toolchain binary; emits RUSTUP_INIT_BIN,
                              RUSTUP_INIT_SH, MOCK_BIN
src/lib.rs                    crate root (mock_data, dist_tree, test_support)
src/mock_data.rs              the program data file (readiness + address)
src/dist_tree.rs              mock distribution tree generation (vendored)
src/bin/rustup-mock-server.rs
src/bin/rustup-mock-proxy.rs
tests/proxy.rs                binary/program/server tests
tests/init_sh.rs              rustup-init.sh tests (Unix only)

Building and testing

git clone --recursive <this repository>
cd rustup-mocks
cargo test

cargo test builds everything that is needed: build.rs first compiles rustup-init from the rustup submodule (into rustup/target/), then compiles the mock toolchain binary that the mock distribution tree ships.

The tests/init_sh.rs tests run only on Unix and require sh, curl, and wget in PATH (they force the script onto a specific downloader by running it with a restricted PATH).

Testing a different rustup revision

The rustup submodule pins the revision the tests exercise. To test a PR head or any other revision:

git -C rustup fetch <remote> <ref>
git -C rustup checkout <commit>
cd ..
git add rustup
git commit -m "rustup: test against <commit>"

For CI, the rustup input of the rustup-mocks-ci workflow fetches and checks out a revision in the submodule without committing it.

Using rustup-mock-server for offline development

The mock server is also a standalone developer tool: it lets you run a rustup build against a local distribution server, without network access and with controllable authentication. The mock tree it generates has one rust package (stable channel, version 1.30.0) whose tarball contains a precompiled mock rustup binary, so toolchain installs succeed offline.

# Build rustup (in this crate or a separate checkout) and the mock server
cargo build --bin rustup-mock-server

# Create a scratch RUSTUP_HOME/CARGO_HOME
export RUSTUP_HOME="$(mktemp -d)"
export CARGO_HOME="$RUSTUP_HOME"

# Start the mock server in the background. It picks a free port and records
# it in its data file ($HOME/.local/share/rustup-mock-server.data by
# default; %LOCALAPPDATA%\rustup-mock-server.data on Windows).
./target/debug/rustup-mock-server &
MOCK_PID=$!

# Wait for the server to start (its data file appears once it is listening)
DATA_FILE="$HOME/.local/share/rustup-mock-server.data"
while ! grep -q '^port=' "$DATA_FILE" 2>/dev/null; do sleep 1; done
PORT=$(grep '^port=' "$DATA_FILE" | cut -d= -f2)

# Point rustup at the mock server
export RUSTUP_DIST_SERVER="http://127.0.0.1:${PORT}"
export RUSTUP_UPDATE_ROOT="${RUSTUP_DIST_SERVER}/rustup"

# Run a rustup build against it, for example:
RUSTUP_HOME="$RUSTUP_HOME" CARGO_HOME="$CARGO_HOME" \
    ./rustup/target/debug/rustup-init --no-modify-path
RUSTUP_HOME="$RUSTUP_HOME" CARGO_HOME="$CARGO_HOME" \
    RUSTUP_FORCE_ARG0=rustup ./rustup/target/debug/rustup-init --default stable

# Clean up (the server removes its data file on exit)
kill $MOCK_PID
rm -rf "$RUSTUP_HOME"

For authenticated downloads, start the server with a credential and present it through RUSTUP_AUTHORIZATION_HEADER:

./target/debug/rustup-mock-server --basic-test-credential "testuser:testpass" &
export RUSTUP_AUTHORIZATION_HEADER="Basic $(echo -n 'testuser:testpass' | base64)"

To route the downloads through an authenticating proxy, also start rustup-mock-proxy with a credential, point http_proxy/https_proxy at it, and set RUSTUP_PROXY_AUTHORIZATION_HEADER:

./target/debug/rustup-mock-proxy --basic-test-credential "proxyuser:proxypass" &
PROXY_PORT=$(grep '^port=' "$HOME/.local/share/rustup-mock-proxy.data" | cut -d= -f2)
export http_proxy="http://127.0.0.1:${PROXY_PORT}"
export https_proxy="http://127.0.0.1:${PROXY_PORT}"
export RUSTUP_PROXY_AUTHORIZATION_HEADER="Basic $(echo -n 'proxyuser:proxypass' | base64)"

See the command-line help of each program for all options (--addr, --port, --directory, --basic-test-credential, --data-file, and --arch for the server).

Data files

Both programs write a data file once their listener is bound, and remove it when they exit (after a normal exit or SIGINT/SIGTERM). The presence of the file is therefore a readiness signal, and it tells the client the address and port the program is listening on (important when the port is OS-assigned, which is the default). The file contains one key=value pair per line:

Key Description
addr The address the program is listening on
port The port the program is listening on
pid The process id of the program
credential The basic test credential in use (only when --basic-test-credential was given)
directory The directory being served (rustup-mock-server only)

CI

.github/workflows/ci.yaml runs cargo build and cargo test on Ubuntu. The workflow can also be started manually with a rustup input to test against a specific rustup revision (for example the head of a pull request).

About

Provides a mock server and mock proxy for testing rustup

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages