Skip to content

Commit 0819df5

Browse files
PouyanpiRobGeada
authored andcommitted
test(recorded): add replay harness (1/5) (NVIDIA-NeMo#1974)
Introduced recorded testing framework enabling deterministic integration tests using cassette-based API interactions without live network access.
1 parent 405d7ec commit 0819df5

22 files changed

Lines changed: 2724 additions & 2 deletions

Makefile

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
.PHONY: help
2-
.PHONY: test test-parallel test-serial test-benchmark test-watch test-coverage test-profile warm-fastembed-cache
2+
.PHONY: test test-parallel test-serial test-benchmark test-watch test-coverage test-profile record-cassettes rewrite-cassettes replay-cassettes snapshot-cassettes check-record-test-env warm-fastembed-cache
33
.PHONY: docs-fern docs-fern-strict docs-fern-live docs-fern-preview-watch docs-fern-generate-sdk docs-fern-fix-empty-links docs-check-redirects docs-fern-publish-staging docs-fern-publish-public
44
.PHONY: pre-commit
55

@@ -13,6 +13,11 @@ WORKERS ?= auto
1313
DIST ?= worksteal
1414

1515
PYTEST ?= poetry run pytest
16+
RECORDED_TESTS ?= tests/recorded
17+
RECORDED_RECORD_MODE ?= once
18+
RECORDED_SNAPSHOT_MODE ?= create
19+
RECORDED_REQUIRED_KEYS ?= OPENAI_API_KEY NVIDIA_API_KEY
20+
RECORDED_REPLAY_ENV ?= env -u HTTP_PROXY -u HTTPS_PROXY -u ALL_PROXY -u http_proxy -u https_proxy -u all_proxy
1621
# These targets assume a Unix-like shell for env -u; use bash, Git Bash, or WSL on Windows.
1722
UNIT_TEST_ENV ?= env -u OPENAI_API_KEY -u NVIDIA_API_KEY \
1823
-u LIVE_TEST -u LIVE_TEST_MODE -u TEST_LIVE_MODE
@@ -43,6 +48,33 @@ test-coverage:
4348
test-profile:
4449
$(PYTEST) -vv --profile-svg $(ARGS) $(TEST)
4550

51+
record-cassettes: check-record-test-env
52+
$(PYTEST) $(RECORDED_TESTS) --record-mode=$(RECORDED_RECORD_MODE) -m "not fake_cassette"
53+
$(RECORDED_REPLAY_ENV) $(PYTEST) $(RECORDED_TESTS) --block-network --inline-snapshot=$(RECORDED_SNAPSHOT_MODE)
54+
$(RECORDED_REPLAY_ENV) $(PYTEST) $(RECORDED_TESTS) --block-network
55+
56+
rewrite-cassettes:
57+
$(MAKE) record-cassettes RECORDED_RECORD_MODE=rewrite RECORDED_SNAPSHOT_MODE=fix
58+
59+
replay-cassettes:
60+
$(RECORDED_REPLAY_ENV) $(PYTEST) $(RECORDED_TESTS) --block-network
61+
62+
snapshot-cassettes:
63+
$(RECORDED_REPLAY_ENV) $(PYTEST) $(RECORDED_TESTS) --block-network --inline-snapshot=fix
64+
65+
check-record-test-env:
66+
@missing=""; \
67+
for key in $(RECORDED_REQUIRED_KEYS); do \
68+
if [ -z "$$(printenv "$$key")" ]; then \
69+
missing="$$missing $$key"; \
70+
fi; \
71+
done; \
72+
if [ -n "$$missing" ]; then \
73+
printf '%s\n' "Missing required env var(s):$$missing" \
74+
"Set them before make record-cassettes, or override RECORDED_REQUIRED_KEYS for a focused refresh."; \
75+
exit 2; \
76+
fi
77+
4678
warm-fastembed-cache:
4779
$(FASTEMBED_ENV) poetry run python -c 'from fastembed import TextEmbedding; model = TextEmbedding("$(FASTEMBED_MODEL)"); next(model.embed(["warmup"]))'
4880

@@ -86,6 +118,10 @@ help:
86118
' make test-benchmark [ARGS="-q"]' \
87119
' make test-parallel [TEST=path] [WORKERS=auto] [ARGS="-q --tb=short"]' \
88120
' make test-watch [TEST=path]' \
121+
' make record-cassettes [RECORDED_TESTS=tests/recorded] [RECORDED_RECORD_MODE=once] [RECORDED_SNAPSHOT_MODE=create] [RECORDED_REQUIRED_KEYS="OPENAI_API_KEY NVIDIA_API_KEY"]' \
122+
' make rewrite-cassettes [RECORDED_TESTS=tests/recorded] [RECORDED_REQUIRED_KEYS="OPENAI_API_KEY NVIDIA_API_KEY"]' \
123+
' make replay-cassettes [RECORDED_TESTS=tests/recorded]' \
124+
' make snapshot-cassettes [RECORDED_TESTS=tests/recorded]' \
89125
'' \
90126
'Tests:' \
91127
' test Run pytest.ini testpaths with pytest-xdist' \
@@ -95,6 +131,10 @@ help:
95131
' test-watch Run pytest in watch mode' \
96132
' test-coverage Run pytest with coverage' \
97133
' test-profile Run pytest with profiling' \
134+
' record-cassettes Record missing or selected cassettes, fill snapshots, and verify replay' \
135+
' rewrite-cassettes Rewrite selected cassettes, fill snapshots, and verify replay' \
136+
' replay-cassettes Verify selected cassettes offline without recording' \
137+
' snapshot-cassettes Update inline snapshots from existing cassettes offline' \
98138
' warm-fastembed-cache Prime the repo-local FastEmbed cache' \
99139
'' \
100140
'Docs:' \

poetry.lock

Lines changed: 90 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ pytest-asyncio = ">=0.21.0, <1.0.0"
159159
pytest-cov = ">=4.1.0"
160160
pytest-httpx = ">=0.22.0"
161161
pytest-xdist = "^3.8.0"
162+
pytest-recording = "^0.13.4"
162163
streamlit = ">=1.37.0"
163164
tox = "^4.23.2"
164165
pytest-profiling = "^1.7.0"
@@ -182,6 +183,7 @@ langchain-core = ">=0.2.14,<2.0.0"
182183
langchain-community = ">=0.2.5,<2.0.0"
183184
langchain-openai = ">=0.1.0"
184185
langchain-nvidia-ai-endpoints = ">=0.2.0"
186+
inline-snapshot = "^0.33.0"
185187

186188
# Directories in which to run Pyright type-checking
187189
[tool.pyright]
@@ -230,6 +232,9 @@ log-level = "DEBUG"
230232
# phase, which will cause tests to fail or "magically" ignored.
231233
log_cli = "False"
232234

235+
[tool.inline-snapshot]
236+
format-command = "ruff format --stdin-filename {filename} -"
237+
233238
[build-system]
234239
requires = ["poetry-core>=1.0.0,<2.0.0"]
235240
build-backend = "poetry.core.masonry.api"

pytest.ini

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,13 @@ log_cli = False
1111
asyncio_default_fixture_loop_scope = function
1212

1313
markers =
14+
recorded: deterministic cassette replay tests
1415
serial: documentation-only marker for tests that may need serial scheduling
1516
slow: high-runtime tests that may need separate scheduling
1617
perf: wall-clock performance/timing tests; excluded from CI by default, run on demand with -m perf
1718
live: tests that require real provider credentials or external services
19+
vcr: marker used by pytest-recording
20+
fake_cassette: cassette is hand-authored; refresh workflow must skip these tests
1821
real_embeddings: tests that intentionally exercise configured real embedding providers
1922

2023
testpaths =

0 commit comments

Comments
 (0)