-
Notifications
You must be signed in to change notification settings - Fork 908
test(ts): change-based selective integration testing #2921
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
Open
yonib05
wants to merge
12
commits into
strands-agents:main
Choose a base branch
from
yonib05:selective-integ-testing
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
935d9b5
test(ts): add selective integration-test script
yonib05 08bb749
fix(ts): strip strands-wasm/wit path prefixes in selective run
yonib05 952c54c
test(ts): add test:integ:selective npm script
yonib05 cfba53b
ci(ts): use selective integration testing in PR workflow
yonib05 596b650
docs(ts): document selective integration testing
yonib05 d1e5e13
fix(ts): fall back to full suite when base ref is unresolvable
yonib05 210266d
fix(ts): close selective-test fail-safe gaps from review
yonib05 6750edc
fix(ts): trace package-specifier imports and include untracked files
yonib05 0910064
test(ts): add classification regression test for selective runner
yonib05 6d13dc1
refactor(ts): readable structural pattern list, drop dead wasm/wit refs
yonib05 07743c9
refactor(ts): extract run_full_suite helper, dedupe alias comment
yonib05 42f26ef
docs(ts): pin the vitest related exit-0-on-empty assumption
yonib05 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| #!/usr/bin/env bash | ||
| set -euo pipefail | ||
|
|
||
| # Selective TypeScript integration testing. | ||
| # Runs only the integ specs whose module graph depends on changed files. | ||
| # Falls back to the full suite on structural changes; skips when no TS source changed. | ||
| # Shared by local dev (npm run test:integ:selective) and CI. | ||
|
|
||
| # --- Resolve repo root so the script is callable from anywhere --- | ||
| ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" | ||
| cd "$ROOT" | ||
|
|
||
| # DRY_RUN prints the chosen branch and exits before invoking any test command. | ||
| # Used by verification scenarios so they never trigger live AWS integ runs. | ||
| # Defined up top so it also short-circuits the early full-suite fallbacks below. | ||
| DRY_RUN="${SELECTIVE_DRY_RUN:-}" | ||
|
|
||
| # Run the entire integration suite. Used by the structural branch and by every | ||
| # fail-safe fallback, so the "what does a full run mean" decision lives in one | ||
| # place. Honours DRY_RUN (print intent, run nothing) and exits with the suite's | ||
| # status so a test failure surfaces as a non-zero script exit. | ||
| run_full_suite() { | ||
| echo "$1" >&2 | ||
| [[ -n "$DRY_RUN" ]] && exit 0 | ||
| npm run test:integ:all | ||
| exit $? | ||
| } | ||
|
|
||
| # --- Compute changed files --- | ||
| # Test seam: when SELECTIVE_CHANGED_FILES is defined (even empty) it overrides | ||
| # the git-derived change set, so the classification logic below can be exercised | ||
| # in isolation with no git state or network. ${VAR+x} detects "is it set", | ||
| # which lets a test assert the empty-set (skip) case distinctly from "unset". | ||
| # See test/classify.test.sh. | ||
| if [[ -n "${SELECTIVE_CHANGED_FILES+x}" ]]; then | ||
| CHANGED="$(printf '%s' "$SELECTIVE_CHANGED_FILES" | grep -v '^$' || true)" | ||
| else | ||
| # --- Determine the base ref to diff against --- | ||
| # CI passes SELECTIVE_BASE_REF (the PR base SHA). Locally, discover the | ||
| # closest of {origin/main, main, master, */main} — mirrors get-diff.sh. | ||
| BASE="${SELECTIVE_BASE_REF:-}" | ||
| if [[ -z "$BASE" ]]; then | ||
| candidates=() | ||
| for ref in main master; do | ||
| git rev-parse --verify "$ref" &>/dev/null && candidates+=("$ref") | ||
| for remote_ref in $(git for-each-ref --format='%(refname:short)' "refs/remotes/*/$ref" 2>/dev/null); do | ||
| candidates+=("$remote_ref") | ||
| done | ||
| done | ||
| if [[ ${#candidates[@]} -eq 0 ]]; then | ||
| run_full_suite "WARNING: no base branch found; running full integration suite." | ||
| fi | ||
| BASE="${candidates[0]}" | ||
| best=$(git rev-list --count "$BASE"..HEAD 2>/dev/null || echo 999999) | ||
| for ref in "${candidates[@]:1}"; do | ||
| d=$(git rev-list --count "$ref"..HEAD 2>/dev/null || echo 999999) | ||
| if [[ "$d" -lt "$best" ]]; then BASE="$ref"; best="$d"; fi | ||
| done | ||
| fi | ||
|
|
||
| # Diff the working tree against the merge-base so the local inner loop tests | ||
| # what you just edited, including uncommitted edits. git diff only reports | ||
| # tracked files, so untracked (new, not-yet-added) files are appended via | ||
| # ls-files --others so brand-new source files select their covering tests too. | ||
| # --exclude-standard honours .gitignore. On CI (HEAD == base SHA) there are no | ||
| # local edits, so this reduces to the committed diff. | ||
| MERGE_BASE="$(git merge-base "$BASE" HEAD 2>/dev/null || echo "$BASE")" | ||
| CHANGED="$(git diff --name-only "$MERGE_BASE" 2>/dev/null)" || \ | ||
| run_full_suite "WARNING: cannot diff against $MERGE_BASE; running full integration suite." | ||
| UNTRACKED="$(git ls-files --others --exclude-standard 2>/dev/null || true)" | ||
| CHANGED="$(printf '%s\n%s' "$CHANGED" "$UNTRACKED" | grep -v '^$' || true)" | ||
| fi | ||
|
|
||
| if [[ -z "$CHANGED" ]]; then | ||
| echo "No changes detected vs ${BASE:-base} — skipping integration tests." | ||
| exit 0 | ||
| fi | ||
|
|
||
| # --- Branch 1: structural fallback --- | ||
| # These paths force the FULL suite: the module-graph tracer cannot see them as | ||
| # dependencies of a test, so a change to any of them must fail safe rather than | ||
| # risk a narrowed (or empty) selective run. | ||
| STRUCTURAL_PATTERNS=( | ||
| '^package\.json$' # root dependency manifest | ||
| '^package-lock\.json$' # root lockfile | ||
| '^strands-ts/package\.json$' # workspace manifest | ||
| '^strands-ts/(.*/)?tsconfig.*\.json$' # any tsconfig (src/ + test/integ/ define the $/sdk alias) | ||
| '^strands-ts/vitest\.config\.ts$' # vitest config (projects, aliases) | ||
| '^strands-ts/test/integ/__fixtures__/' # shared integ setup/fixtures | ||
| '^strands-ts/test/integ/__resources__/' # binary assets imported via Vite `?url` (not traced in reverse) | ||
| '^strandly/' # workspace member CI triggers on but the graph cannot trace | ||
| '^test-infra/scripts/run-selective-ts\.sh$' # this orchestration script | ||
| '^\.github/workflows/typescript-' # TypeScript CI workflows | ||
| ) | ||
| STRUCTURAL="$(IFS='|'; echo "${STRUCTURAL_PATTERNS[*]}")" | ||
| if echo "$CHANGED" | grep -qE "$STRUCTURAL"; then | ||
| run_full_suite "Structural change detected — running full integration suite." | ||
| fi | ||
|
|
||
| # --- Branch 2: no TS source changed --- | ||
| TS_SOURCE="$(echo "$CHANGED" | grep -E '^strands-ts/' || true)" | ||
| if [[ -z "$TS_SOURCE" ]]; then | ||
| echo "No strands-ts source changes — skipping integration tests." | ||
| exit 0 | ||
| fi | ||
|
|
||
| # --- Branch 3: selective --- | ||
| # Pass changed source files to Vitest's module-graph tracer, scoped to both | ||
| # integ projects. vitest related exits 0 with "No test files found" when none | ||
| # depend on the changes — a valid skip. | ||
| # | ||
| # LOAD-BEARING ASSUMPTION (vitest v4, pinned in strands-ts/package.json): the | ||
| # "no covering spec" case exits 0, not non-zero. The whole "never skip a test | ||
| # that should run" invariant relies on this — a non-zero here would turn safe | ||
| # skips into red CI. If a future vitest major changes this, this branch must be | ||
| # revisited (and the version floor in package.json bumped deliberately). | ||
| echo "Selective run for changed files:" | ||
| echo "$TS_SOURCE" | sed 's/^/ /' | ||
| [[ -n "$DRY_RUN" ]] && exit 0 | ||
| # Collect paths into an array (relative to strands-ts/) so filenames with | ||
| # spaces survive. while-read keeps this portable to macOS bash 3.2 (mapfile | ||
| # is bash 4+). TS_SOURCE is guaranteed non-empty by the Branch 2 check above. | ||
| files=() | ||
| while IFS= read -r f; do | ||
| [[ -n "$f" ]] && files+=("$f") | ||
| done < <(echo "$TS_SOURCE" | sed -E 's#^strands-ts/##') | ||
| ( cd strands-ts && npx vitest related "${files[@]}" \ | ||
|
yonib05 marked this conversation as resolved.
|
||
| --project integ-node --project integ-browser --run ) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| #!/usr/bin/env bash | ||
| set -euo pipefail | ||
|
|
||
| # Regression test for run-selective-ts.sh change classification. | ||
| # | ||
| # Feeds synthetic changed-file lists through the SELECTIVE_CHANGED_FILES test | ||
| # seam with SELECTIVE_DRY_RUN=1, so the script classifies into structural / | ||
| # skip / selective and exits before invoking git or vitest. No git state, no | ||
| # network, no live AWS. Run: bash test-infra/scripts/test/classify.test.sh | ||
|
|
||
| SCRIPT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/run-selective-ts.sh" | ||
|
|
||
| pass=0 | ||
| fail=0 | ||
|
|
||
| # expect_branch <expected> <description> <changed-files...> | ||
| # <expected> is one of: structural | skip | selective | ||
| expect_branch() { | ||
| local expected="$1" desc="$2" | ||
| shift 2 | ||
| local changed | ||
| changed="$(printf '%s\n' "$@")" | ||
|
|
||
| local out | ||
| out="$(SELECTIVE_DRY_RUN=1 SELECTIVE_CHANGED_FILES="$changed" bash "$SCRIPT" 2>&1)" | ||
|
|
||
| local actual | ||
| case "$out" in | ||
| *"Structural change detected"*) actual="structural" ;; | ||
| *"No strands-ts source changes"*) actual="skip" ;; | ||
| *"No changes detected"*) actual="skip" ;; | ||
| *"Selective run for changed files"*) actual="selective" ;; | ||
| *) actual="unknown" ;; | ||
| esac | ||
|
|
||
| if [[ "$actual" == "$expected" ]]; then | ||
| pass=$((pass + 1)) | ||
| echo "ok - $desc ($actual)" | ||
| else | ||
| fail=$((fail + 1)) | ||
| echo "FAIL - $desc: expected $expected, got $actual" | ||
| echo " output: $out" | ||
| fi | ||
| } | ||
|
|
||
| # --- Structural: must force the full suite --- | ||
| expect_branch structural "root package.json" "package.json" | ||
| expect_branch structural "root lockfile" "package-lock.json" | ||
| expect_branch structural "strands-ts package.json" "strands-ts/package.json" | ||
| expect_branch structural "top-level tsconfig" "strands-ts/tsconfig.json" | ||
| expect_branch structural "nested src tsconfig" "strands-ts/src/tsconfig.json" | ||
| expect_branch structural "nested integ tsconfig" "strands-ts/test/integ/tsconfig.json" | ||
| expect_branch structural "vitest config" "strands-ts/vitest.config.ts" | ||
| expect_branch structural "shared integ fixture" "strands-ts/test/integ/__fixtures__/_setup-global.ts" | ||
| expect_branch structural "binary integ resource" "strands-ts/test/integ/__resources__/yellow.png" | ||
| expect_branch structural "strandly member" "strandly/src/cli.ts" | ||
| expect_branch structural "the orchestration script itself" "test-infra/scripts/run-selective-ts.sh" | ||
| expect_branch structural "typescript CI workflow" ".github/workflows/typescript-ts-test.yml" | ||
| expect_branch structural "structural mixed with source" "README.md" "strands-ts/src/agent/agent.ts" "package.json" | ||
|
|
||
| # --- Skip: no strands-ts source touched --- | ||
| expect_branch skip "docs only" "site/docs/index.md" | ||
| expect_branch skip "root readme" "README.md" | ||
| expect_branch skip "python only" "strands-py/src/strands/agent.py" | ||
| expect_branch skip "empty change set" "" | ||
|
|
||
| # --- Selective: traceable source change --- | ||
| expect_branch selective "single src file" "strands-ts/src/agent/agent.ts" | ||
| expect_branch selective "src plus unrelated docs" "strands-ts/src/agent/agent.ts" "site/docs/x.md" | ||
| expect_branch selective "nested src file" "strands-ts/src/vended-tools/bash/types.ts" | ||
|
|
||
| echo "---" | ||
| echo "passed: $pass, failed: $fail" | ||
| [[ "$fail" -eq 0 ]] |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.