From 7e4fda2a871269366aad630317539092dd377319 Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Fri, 27 Feb 2026 16:53:59 -0500 Subject: [PATCH] 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 --- Cargo.lock | 6 +- crates/attractor/tests/daytona_integration.rs | 55 +++++++++++++++++++ 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 26ee8f49d..04f003478 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/crates/attractor/tests/daytona_integration.rs b/crates/attractor/tests/daytona_integration.rs index a535f0881..a3e2d91b2 100644 --- a/crates/attractor/tests/daytona_integration.rs +++ b/crates/attractor/tests/daytona_integration.rs @@ -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(); +}