1
0
Fork 0
mirror of https://github.com/imjasonh/testscript-rs synced 2026-07-08 17:16:38 +00:00
testscript-rs/tests/update_scripts.rs
Copilot fdb53b03fa
Add UpdateScripts functionality for test maintenance (#20)
- [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>
2025-09-26 21:47:09 -04:00

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"
);
}