mirror of
https://github.com/imjasonh/testscript-rs
synced 2026-07-08 17:16:38 +00:00
- [x] Analyze existing codebase structure and architecture
- [x] Review test files to understand expected UpdateScripts
functionality
- [x] Understand current API and parameter structure
- [x] Add `update_scripts` parameter to RunParams struct
- [x] Add `update_scripts` method to Builder API
- [x] Support UPDATE_SCRIPTS environment variable detection
- [x] Implement update functionality in stdout/stderr command execution
- [x] Modify output comparison to capture and update actual output when
in update mode
- [x] Add script file modification logic to update test files
- [x] Create focused tests for update functionality
- [x] Validate changes work end-to-end
- [x] Update documentation in README.md
- [x] Clean up temporary files
- [x] Move UpdateScripts section to bottom of README per review feedback
- [x] Fix code formatting issues with cargo fmt
## Implementation Summary
Successfully implemented complete UpdateScripts functionality for test
maintenance in testscript-rs:
### Key Features
- **API support**: `.update_scripts(true)` method on Builder
- **Environment variable**: `UPDATE_SCRIPTS=1` environment variable
support
- **Smart updating**: Only updates `stdout`/`stderr` expectations,
preserves file structure
- **Proper quoting**: Handles complex output with appropriate shell
quoting
- **Error handling**: Continues execution instead of failing in update
mode
### API Usage
```rust
// Via API
testscript::run("testdata")
.update_scripts(true)
.execute()
.unwrap();
// Via environment variable
UPDATE_SCRIPTS=1 cargo test
```
### Implementation Details
- Added `update_scripts: bool` field to `RunParams`
- Modified `run_script_impl` to collect updates instead of failing on
mismatches
- Implemented `apply_script_updates` function for file modification
- Added comprehensive test coverage with 4 test cases
- Updated README.md with usage examples (moved to bottom per review
feedback)
- Fixed code formatting issues identified by CI
### Testing
- All existing tests continue to pass
- New functionality tested via API and environment variable
- Manual validation confirms proper file updating
- Normal mode behavior preserved (still fails on mismatches when
disabled)
- Code passes formatting checks and linting
The implementation matches Go's testscript behavior and provides
essential test maintenance functionality.
<!-- START COPILOT CODING AGENT SUFFIX -->
<details>
<summary>Original prompt</summary>
>
> ----
>
> *This section details on the original issue you should resolve*
>
> <issue_title>Add UpdateScripts functionality for test
maintenance</issue_title>
> <issue_description>## Feature Request: UpdateScripts (Test Maintenance
Mode)
>
> Go's testscript supports an `UpdateScripts` parameter that
automatically updates test files with actual command output. This is
invaluable for test maintenance.
>
> ## Proposed API
>
> ```rust
> testscript::run("testdata")
> .update_scripts(true)
> .execute()
> .unwrap();
> ```
>
> Or via environment variable:
> ```bash
> UPDATE_SCRIPTS=1 cargo test
> ```
>
> ## How It Works
>
> When enabled, instead of failing on output mismatches:
> 1. **Capture actual output**
> 2. **Update the test file** with the actual output
> 3. **Continue to next test**
>
> ## Example
>
> Before (failing test):
> ```
> exec my-tool --version
> stdout "my-tool 1.0"
> ```
>
> After running with update mode:
> ```
> exec my-tool --version
> stdout "my-tool 2.1.0"
> ```
>
> ## Benefits
>
> - **Easy test maintenance**: Update expected outputs after changes
> - **Reduce manual work**: No hand-editing of test files
> - **Go compatibility**: Matches upstream behavior
> - **Development workflow**: Essential for evolving CLI tools
>
> ## Implementation Notes
>
> - Should only update `stdout`/`stderr` expectations
> - Preserve comments and file structure
> - Add safety checks to prevent accidental
overwrites</issue_description>
>
> ## Comments on the Issue (you are @copilot in this section)
>
> <comments>
> </comments>
>
</details>
Fixes imjasonh/testscript-rs#8
<!-- START COPILOT CODING AGENT TIPS -->
---
💬 Share your feedback on Copilot coding agent for the chance to win a
$200 gift card! Click
[here](https://survey3.medallia.com/?EAHeSx-AP01bZqG0Ld9QLQ) to start
the survey.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: imjasonh <210737+imjasonh@users.noreply.github.com>
142 lines
4.3 KiB
Rust
142 lines
4.3 KiB
Rust
//! Tests for update scripts functionality
|
|
|
|
use std::fs;
|
|
use tempfile::TempDir;
|
|
use testscript_rs::testscript;
|
|
|
|
#[test]
|
|
fn test_update_scripts_stdout() {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
let testdata_dir = temp_dir.path().join("testdata");
|
|
fs::create_dir(&testdata_dir).unwrap();
|
|
|
|
// Create a test script with incorrect expected output
|
|
let test_content = r#"exec echo "actual output"
|
|
stdout "expected output"
|
|
"#;
|
|
|
|
let test_file = testdata_dir.join("update_test.txt");
|
|
fs::write(&test_file, test_content).unwrap();
|
|
|
|
// Run with update_scripts enabled - should update the file instead of failing
|
|
let result = testscript::run(testdata_dir.to_string_lossy())
|
|
.update_scripts(true)
|
|
.execute();
|
|
|
|
assert!(result.is_ok(), "Update should succeed: {:?}", result);
|
|
|
|
// Check that the file was updated with the actual output
|
|
let updated_content = fs::read_to_string(&test_file).unwrap();
|
|
assert!(
|
|
updated_content.contains("stdout \"actual output\""),
|
|
"File should be updated with actual output, got: {}",
|
|
updated_content
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_update_scripts_stderr() {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
let testdata_dir = temp_dir.path().join("testdata");
|
|
fs::create_dir(&testdata_dir).unwrap();
|
|
|
|
// Create a test script with incorrect expected stderr output
|
|
let test_content = r#"exec sh -c "echo 'error message' >&2"
|
|
stderr "wrong error message"
|
|
"#;
|
|
|
|
let test_file = testdata_dir.join("stderr_update_test.txt");
|
|
fs::write(&test_file, test_content).unwrap();
|
|
|
|
// Run with update_scripts enabled
|
|
let result = testscript::run(testdata_dir.to_string_lossy())
|
|
.update_scripts(true)
|
|
.execute();
|
|
|
|
assert!(result.is_ok(), "Update should succeed: {:?}", result);
|
|
|
|
// Check that the file was updated
|
|
let updated_content = fs::read_to_string(&test_file).unwrap();
|
|
assert!(
|
|
updated_content.contains("stderr \"error message\""),
|
|
"File should be updated with actual stderr output, got: {}",
|
|
updated_content
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_update_scripts_via_env_var() {
|
|
// Ensure clean state
|
|
std::env::remove_var("UPDATE_SCRIPTS");
|
|
|
|
let temp_dir = TempDir::new().unwrap();
|
|
let testdata_dir = temp_dir.path().join("testdata");
|
|
fs::create_dir(&testdata_dir).unwrap();
|
|
|
|
// Create a test script with incorrect expected output
|
|
let test_content = r#"exec echo "env test output"
|
|
stdout "wrong output"
|
|
"#;
|
|
|
|
let test_file = testdata_dir.join("env_update_test.txt");
|
|
fs::write(&test_file, test_content).unwrap();
|
|
|
|
// Set the environment variable
|
|
std::env::set_var("UPDATE_SCRIPTS", "1");
|
|
|
|
// Run without explicitly setting update_scripts - should read from env var
|
|
let result = testscript::run(testdata_dir.to_string_lossy()).execute();
|
|
|
|
// Clean up the env var immediately after the test
|
|
std::env::remove_var("UPDATE_SCRIPTS");
|
|
|
|
assert!(
|
|
result.is_ok(),
|
|
"Update via env var should succeed: {:?}",
|
|
result
|
|
);
|
|
|
|
// Check that the file was updated
|
|
let updated_content = fs::read_to_string(&test_file).unwrap();
|
|
assert!(
|
|
updated_content.contains("stdout \"env test output\""),
|
|
"File should be updated via env var, got: {}",
|
|
updated_content
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_normal_mode_still_fails() {
|
|
// Ensure UPDATE_SCRIPTS env var is not set
|
|
std::env::remove_var("UPDATE_SCRIPTS");
|
|
|
|
let temp_dir = TempDir::new().unwrap();
|
|
let testdata_dir = temp_dir.path().join("testdata");
|
|
fs::create_dir(&testdata_dir).unwrap();
|
|
|
|
// Create a test script with incorrect expected output
|
|
let test_content = r#"exec echo "actual output"
|
|
stdout "expected output"
|
|
"#;
|
|
|
|
let test_file = testdata_dir.join("normal_test.txt");
|
|
fs::write(&test_file, test_content).unwrap();
|
|
|
|
// Explicitly set update_scripts to false to override any env var
|
|
let result = testscript::run(testdata_dir.to_string_lossy())
|
|
.update_scripts(false)
|
|
.execute();
|
|
|
|
assert!(
|
|
result.is_err(),
|
|
"Normal mode should still fail on mismatch: {:?}",
|
|
result
|
|
);
|
|
|
|
// Check that the file was NOT updated
|
|
let content = fs::read_to_string(&test_file).unwrap();
|
|
assert_eq!(
|
|
content, test_content,
|
|
"File should not be modified in normal mode"
|
|
);
|
|
}
|