Skip to content

Commit

Permalink
Merge branch 'main' into feature/tests-agent
Browse files Browse the repository at this point in the history
  • Loading branch information
WoodenMaiden authored Nov 28, 2023
2 parents bfcc819 + d24989b commit 92a4c8e
Show file tree
Hide file tree
Showing 5 changed files with 443 additions and 60 deletions.
2 changes: 2 additions & 0 deletions api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ rand = "0.8.4"
tokio = { version = "1.0", features = ["rt-multi-thread", "macros", "process"] }
tonic = { version = "0.10.2", features = ["transport"] }
prost = "0.12.1"
async-trait = "0.1.74"
mockall = "0.11.4"

[build-dependencies]
tonic-build = { version = "0.10.2", features = ["prost"] }
Expand Down
181 changes: 161 additions & 20 deletions api/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,29 @@ use actix_web::{post, web, Responder};
use log::{debug, error, info, trace, warn};

use crate::{
api::service::LambdoApiService,
api::service::{LambdoApiService, LambdoApiServiceTrait},
model::{RunRequest, RunResponse},
vm_manager::{self, grpc_definitions::ExecuteResponse},
};
use std::error::Error;

#[post("/run")]
async fn run(
run_body: web::Json<RunRequest>,
service: web::Data<LambdoApiService>,
) -> Result<impl Responder, Box<dyn Error>> {
debug!(
"Received code execution request from http (language: {}, version: {})",
run_body.language, run_body.version
);
trace!("Request body: {:?}", run_body);

let response = service.run_code(run_body.into_inner()).await;
async fn run_code(run_resquest: RunRequest, service: &dyn LambdoApiServiceTrait) -> RunResponse {
let response = service.run_code(run_resquest).await;

let response = match response {
match response {
Ok(response) => {
info!("Execution ended for {:?}", response.id);
trace!("Response: {:?}", response);
parse_response(response)
}
// for the moment just signal an internal server error
Err(e) => match e {
vm_manager::Error::Timeout => {
warn!("Timeout while executing code");
return Ok(web::Json(RunResponse {
RunResponse {
status: 128,
stdout: "".to_string(),
stderr: "Timeout".to_string(),
}));
}
}
_ => {
error!("Error while executing code: {:?}", e);
Expand All @@ -48,12 +37,35 @@ async fn run(
}
}
},
};
}
}

#[post("/run")]
pub async fn post_run_route(
run_body: web::Json<RunRequest>,
api_service: web::Data<LambdoApiService>,
) -> Result<impl Responder, Box<dyn Error>> {
debug!(
"Received code execution request from http (language: {}, version: {})",
run_body.language, run_body.version
);
trace!("Request body: {:?}", run_body);

let service = api_service.get_ref();
let result = run_code(run_body.into_inner(), service);

Ok(web::Json(response))
Ok(web::Json(result.await))
}

fn parse_response(response: ExecuteResponse) -> RunResponse {
if response.steps.is_empty() {
return RunResponse {
status: 1,
stdout: "".to_string(),
stderr: "Nothing was run".to_string(),
};
}

let mut stdout = String::new();
let mut stderr = String::new();
for step in response.steps.as_slice() {
Expand All @@ -67,8 +79,137 @@ fn parse_response(response: ExecuteResponse) -> RunResponse {
status: response.steps[response.steps.len() - 1]
.exit_code
.try_into()
.unwrap(),
.unwrap_or(1),
stdout,
stderr,
}
}

#[cfg(test)]
mod test {
use std::vec;

use crate::{
api::{parse_response, run_code},
model::RunRequest,
vm_manager::grpc_definitions::{ExecuteResponse, ExecuteResponseStep, FileModel},
};

use super::service::MockLambdoApiServiceTrait;

#[test]
fn test_parse_response_stdout() {
let response = ExecuteResponse {
id: "test".to_string(),
steps: vec![
ExecuteResponseStep {
command: "echo Hello".to_string(),
stdout: "Hello".to_string(),
stderr: "".to_string(),
exit_code: 0,
},
ExecuteResponseStep {
command: "echo World".to_string(),
stdout: "World".to_string(),
stderr: "".to_string(),
exit_code: 0,
},
],
};

let parsed = parse_response(response);

assert_eq!(parsed.stdout, "HelloWorld");
assert_eq!(parsed.stderr, "");
assert_eq!(parsed.status, 0);
}

#[test]
fn test_parse_response_with_error() {
let response = ExecuteResponse {
id: "test".to_string(),
steps: vec![
ExecuteResponseStep {
command: "echo Hello".to_string(),
stdout: "Hello".to_string(),
stderr: "".to_string(),
exit_code: 0,
},
ExecuteResponseStep {
command: "echo World".to_string(),
stdout: "".to_string(),
stderr: "Error".to_string(),
exit_code: 1,
},
],
};

let parsed = parse_response(response);

assert_eq!(parsed.stdout, "Hello");
assert_eq!(parsed.stderr, "Error");
assert_eq!(parsed.status, 1);
}

#[tokio::test]
async fn test_run_code_with_no_steps() {
let mut mock_service = MockLambdoApiServiceTrait::new();
mock_service.expect_run_code().once().returning(|_| {
Ok(ExecuteResponse {
id: "test".to_string(),
steps: vec![],
})
});

let run_request = RunRequest {
language: "Node".to_string(),
version: "1".to_string(),
code: vec![],
input: "".to_string(),
};

let response = run_code(run_request, &mock_service).await;
assert_eq!(response.status, 1);
assert_eq!(response.stdout, "");
assert_eq!(response.stderr, "Nothing was run");
}

#[tokio::test]
async fn test_run_with_steps() {
let mut mock_service = MockLambdoApiServiceTrait::new();
mock_service.expect_run_code().once().returning(|_| {
Ok(ExecuteResponse {
id: "test".to_string(),
steps: vec![
ExecuteResponseStep {
command: "echo Hello".to_string(),
stdout: "Hello".to_string(),
stderr: "".to_string(),
exit_code: 0,
},
ExecuteResponseStep {
command: "echo World".to_string(),
stdout: "World".to_string(),
stderr: "".to_string(),
exit_code: 0,
},
],
})
});

let run_request = RunRequest {
language: "Node".to_string(),
version: "1".to_string(),
code: vec![FileModel {
filename: "test.js".to_string(),
content: "console.log('Hello World')".to_string(),
}],
input: "test.js".to_string(),
};

let response = run_code(run_request, &mock_service).await;
assert_eq!(response.status, 0);
assert_eq!(response.stdout, "HelloWorld");
assert_eq!(response.stderr, "");
}
}
Loading

0 comments on commit 92a4c8e

Please sign in to comment.