Add Daytona E2E test for artifact sync and update SDK

Update daytona-sdk-rust to 5d370099 which fixes multipart file upload
(upload_file was sending an empty form). Add E2E test that verifies
artifact pointers are rewritten and files uploaded to a live Daytona
sandbox.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Bryan Helmkamp 2026-02-27 16:53:59 -05:00
parent 648418a1b0
commit 7e4fda2a87
2 changed files with 58 additions and 3 deletions

6
Cargo.lock generated
View file

@ -649,7 +649,7 @@ checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea"
[[package]]
name = "daytona-api-client"
version = "0.1.0"
source = "git+https://github.com/brynary/daytona-sdk-rust#572e584d06ccc0c6f110679eacc2d3f8d45c03c6"
source = "git+https://github.com/brynary/daytona-sdk-rust#5d370099a8dedb3d3c4d9d0bf31037e580e170a5"
dependencies = [
"reqwest 0.12.28",
"reqwest-middleware",
@ -663,7 +663,7 @@ dependencies = [
[[package]]
name = "daytona-sdk"
version = "0.1.0"
source = "git+https://github.com/brynary/daytona-sdk-rust#572e584d06ccc0c6f110679eacc2d3f8d45c03c6"
source = "git+https://github.com/brynary/daytona-sdk-rust#5d370099a8dedb3d3c4d9d0bf31037e580e170a5"
dependencies = [
"daytona-api-client",
"daytona-toolbox-client",
@ -682,7 +682,7 @@ dependencies = [
[[package]]
name = "daytona-toolbox-client"
version = "0.1.0"
source = "git+https://github.com/brynary/daytona-sdk-rust#572e584d06ccc0c6f110679eacc2d3f8d45c03c6"
source = "git+https://github.com/brynary/daytona-sdk-rust#5d370099a8dedb3d3c4d9d0bf31037e580e170a5"
dependencies = [
"reqwest 0.12.28",
"reqwest-middleware",

View file

@ -3,7 +3,10 @@
//! These tests require a `DAYTONA_API_KEY` environment variable and network access.
//! Run with: `cargo test --package attractor -- --ignored daytona`
use std::collections::HashMap;
use agent::ExecutionEnvironment;
use attractor::artifact::sync_artifacts_to_env;
use attractor::daytona_env::{DaytonaConfig, DaytonaExecutionEnvironment};
async fn create_env() -> DaytonaExecutionEnvironment {
@ -135,3 +138,55 @@ async fn daytona_snapshot_sandbox() {
env.cleanup().await.unwrap();
}
#[tokio::test]
#[ignore]
async fn daytona_artifact_sync_uploads_and_rewrites_pointer() {
let env = create_env().await;
env.initialize().await.unwrap();
// Create a local artifact file (simulating what offload_large_values produces)
let dir = tempfile::tempdir().unwrap();
let artifact_content = "x".repeat(150 * 1024); // 150KB
let artifact_json = serde_json::json!(artifact_content);
let artifact_file = dir.path().join("response.plan.json");
std::fs::write(&artifact_file, serde_json::to_string(&artifact_json).unwrap()).unwrap();
// Build updates with a file:// pointer (as offload_large_values would)
let pointer = format!("file://{}", artifact_file.display());
let mut updates = HashMap::new();
updates.insert(
"response.plan".to_string(),
serde_json::json!(pointer),
);
// Sync — the local file doesn't exist in the Daytona sandbox, so it should upload
sync_artifacts_to_env(&mut updates, &env).await.unwrap();
// Pointer should be rewritten to the Daytona working directory
let new_pointer = updates["response.plan"].as_str().unwrap();
let expected_prefix = format!(
"file://{}/.attractor/artifacts/",
env.working_directory()
);
assert!(
new_pointer.starts_with(&expected_prefix),
"pointer should reference Daytona path, got: {new_pointer}"
);
// Verify the file actually exists in the sandbox by reading it back
let remote_path = new_pointer.strip_prefix("file://").unwrap();
assert!(
env.file_exists(remote_path).await.unwrap(),
"artifact file should exist in Daytona sandbox at {remote_path}"
);
let remote_content = env.read_file(remote_path, None, None).await.unwrap();
assert!(
remote_content.len() > 100 * 1024,
"remote artifact should be >100KB, got {} bytes",
remote_content.len()
);
env.cleanup().await.unwrap();
}