Skip to content

Commit

Permalink
Add some tests around syncing complete_process recipes
Browse files Browse the repository at this point in the history
  • Loading branch information
kylewlacy committed Jun 8, 2024
1 parent 67c1be3 commit 93c8448
Show file tree
Hide file tree
Showing 3 changed files with 290 additions and 0 deletions.
84 changes: 84 additions & 0 deletions crates/brioche-core/src/recipe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,54 @@ pub struct CompleteProcessRecipe {
pub networking: bool,
}

impl TryFrom<ProcessRecipe> for CompleteProcessRecipe {
type Error = anyhow::Error;

fn try_from(recipe: ProcessRecipe) -> anyhow::Result<Self> {
let ProcessRecipe {
command,
args,
env,
dependencies,
work_dir,
output_scaffold,
platform,
is_unsafe,
networking,
} = recipe;

anyhow::ensure!(
dependencies.is_empty(),
"tried to convert process recipe to complete process recipe, but it has dependencies"
);

let work_dir = work_dir.value.try_into()?;
let output_scaffold = output_scaffold
.map(|output_scaffold| {
let artifact: Artifact = output_scaffold.value.try_into()?;
anyhow::Ok(Box::new(artifact))
})
.transpose()?;

Ok(Self {
command: command.try_into()?,
args: args
.into_iter()
.map(TryInto::try_into)
.collect::<Result<_, RecipeIncomplete>>()?,
env: env
.into_iter()
.map(|(key, value)| Ok((key, value.try_into()?)))
.collect::<anyhow::Result<_>>()?,
work_dir,
output_scaffold,
platform,
is_unsafe,
networking,
})
}
}

#[serde_with::serde_as]
#[derive(
Debug,
Expand Down Expand Up @@ -1033,6 +1081,8 @@ impl ProxyRecipe {
}
}

#[derive(Debug, thiserror::Error)]
#[error("tried to convert a non-artifact recipe into an artifact")]
pub struct RecipeIncomplete;

#[derive(Debug, thiserror::Error)]
Expand Down Expand Up @@ -1207,6 +1257,19 @@ impl CompleteProcessTemplate {
}
}

impl TryFrom<ProcessTemplate> for CompleteProcessTemplate {
type Error = RecipeIncomplete;

fn try_from(value: ProcessTemplate) -> Result<Self, RecipeIncomplete> {
let components = value
.components
.into_iter()
.map(TryInto::try_into)
.collect::<Result<_, RecipeIncomplete>>()?;
Ok(Self { components })
}
}

#[serde_with::serde_as]
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
#[serde(tag = "type")]
Expand Down Expand Up @@ -1236,6 +1299,27 @@ impl CompleteProcessTemplateComponent {
}
}

impl TryFrom<ProcessTemplateComponent> for CompleteProcessTemplateComponent {
type Error = RecipeIncomplete;

fn try_from(value: ProcessTemplateComponent) -> Result<Self, Self::Error> {
match value {
ProcessTemplateComponent::Literal { value } => Ok(Self::Literal { value }),
ProcessTemplateComponent::Input { recipe } => {
let artifact = recipe.value.try_into()?;
let artifact = WithMeta::new(artifact, recipe.meta);
Ok(Self::Input { artifact })
}
ProcessTemplateComponent::OutputPath => Ok(Self::OutputPath),
ProcessTemplateComponent::ResourceDir => Ok(Self::ResourceDir),
ProcessTemplateComponent::InputResourceDirs => Ok(Self::InputResourceDirs),
ProcessTemplateComponent::HomeDir => Ok(Self::HomeDir),
ProcessTemplateComponent::WorkDir => Ok(Self::WorkDir),
ProcessTemplateComponent::TempDir => Ok(Self::TempDir),
}
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ArchiveFormat {
Expand Down
73 changes: 73 additions & 0 deletions crates/brioche-core/tests/sync_from_registry.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
use std::collections::HashSet;

use brioche_core::{
bake::BakeScope,
recipe::{Recipe, WithMeta},
};
use brioche_test::tpl;

mod brioche_test;

#[tokio::test]
async fn test_sync_from_registry_complete_process() -> anyhow::Result<()> {
let (brioche, mut context) = brioche_test::brioche_test().await;

// Create a process recipe and an equivalent complete_process recipe
let process_recipe = brioche_core::recipe::ProcessRecipe {
command: tpl("/usr/bin/env"),
args: vec![tpl("sh"), tpl("-c"), tpl("dummy_recipe")],
platform: brioche_core::platform::Platform::X86_64Linux,
..brioche_test::default_process()
};
let complete_process_recipe: brioche_core::recipe::CompleteProcessRecipe =
process_recipe.clone().try_into()?;
let complete_process_recipe_hash =
Recipe::CompleteProcess(complete_process_recipe.clone()).hash();

let dummy_blob = brioche_test::blob(&brioche, "dummy value").await;
let mocked_output = brioche_test::file(dummy_blob, false);

// Mock the registry to return the output artifact for the complete_process
// recipe. This should be the only registry request, needed since the
// output artifact and blob have already been saved locally
let mut registry_mocks = vec![];
registry_mocks.push(
context
.registry_server
.mock(
"GET",
&*format!(
"/v0/recipes/{}/bake?brioche={}",
complete_process_recipe_hash,
brioche_core::VERSION,
),
)
.with_body(serde_json::to_string(
&brioche_core::registry::GetBakeResponse {
output_hash: mocked_output.hash(),
output_artifact: mocked_output.clone(),
referenced_recipes: HashSet::new(),
referenced_blobs: HashSet::new(),
},
)?)
.create(),
);

// Bake the process recipe, which should be a cache hit to the registry
let output_artifact = brioche_core::bake::bake(
&brioche,
WithMeta::without_meta(Recipe::Process(process_recipe.clone())),
&BakeScope::Anonymous,
)
.await?;

// Ensure that we got the mock back
assert_eq!(output_artifact.value, mocked_output);

// Ensure all the registry mocks got called as expected
for registry_mock in registry_mocks {
registry_mock.assert_async().await;
}

Ok(())
}
133 changes: 133 additions & 0 deletions crates/brioche-core/tests/sync_to_registry.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
use brioche_core::{
bake::BakeScope,
recipe::{Recipe, WithMeta},
};
use brioche_test::tpl;

mod brioche_test;

#[tokio::test]
async fn test_sync_to_registry_process_and_complete_process() -> anyhow::Result<()> {
let (brioche, mut context) = brioche_test::brioche_test().await;

// Create a process recipe and an equivalent complete_process recipe
let process_recipe = brioche_core::recipe::ProcessRecipe {
command: tpl("/usr/bin/env"),
args: vec![tpl("sh"), tpl("-c"), tpl("dummy_recipe")],
platform: brioche_core::platform::Platform::X86_64Linux,
..brioche_test::default_process()
};
let process_recipe_hash = Recipe::Process(process_recipe.clone()).hash();
let complete_process_recipe: brioche_core::recipe::CompleteProcessRecipe =
process_recipe.clone().try_into()?;
let complete_process_recipe_hash =
Recipe::CompleteProcess(complete_process_recipe.clone()).hash();

// Create a mocked output for the complete_process recipe
let dummy_blob = brioche_test::blob(&brioche, "dummy value").await;
let mocked_output = brioche_test::file(dummy_blob, false);
brioche_test::mock_bake(
&brioche,
&Recipe::CompleteProcess(complete_process_recipe.clone()),
&mocked_output,
)
.await;

// Create a dummy project that we can associate the baked output with
let (_, project_hash, _) = context
.temp_project(|path| async move {
tokio::fs::write(
path.join("project.bri"),
r#"
// dummy project
export const project = {};
"#,
)
.await
.unwrap();
})
.await;

// Bake the process recipe (and tie the result to our dummy project)
let output_artifact = brioche_core::bake::bake(
&brioche,
WithMeta::without_meta(Recipe::Process(process_recipe.clone())),
&BakeScope::Project {
project_hash,
export: "default".to_string(),
},
)
.await?;

// Ensure that we got the mock back
assert_eq!(output_artifact.value, mocked_output);

// Create mocks indicating the registry already has all the blobs
// and recipes, but no bakes
let mut registry_mocks = vec![];
registry_mocks.push(
context
.registry_server
.mock(
"POST",
&*format!("/v0/known-blobs?brioche={}", brioche_core::VERSION),
)
.with_body(serde_json::to_string(&[dummy_blob])?)
.create(),
);
registry_mocks.push(
context
.registry_server
.mock(
"POST",
&*format!("/v0/known-recipes?brioche={}", brioche_core::VERSION),
)
.with_body(serde_json::to_string(&[
process_recipe_hash,
complete_process_recipe_hash,
mocked_output.hash(),
])?)
.create(),
);
registry_mocks.push(
context
.registry_server
.mock(
"POST",
&*format!("/v0/known-bakes?brioche={}", brioche_core::VERSION),
)
.with_body("[]")
.create(),
);

// Create a mock to ensure that the result for complete_process
// gets created
registry_mocks.push(
context
.registry_server
.mock(
"POST",
&*format!(
"/v0/recipes/{}/bake?brioche={}",
complete_process_recipe_hash,
brioche_core::VERSION
),
)
.with_body(serde_json::to_string(
&brioche_core::registry::CreateBakeResponse {
canonical_output_hash: mocked_output.hash(),
},
)?)
.create(),
);

// Sync the project to the registry
brioche_core::sync::sync_project(&brioche, project_hash, "default").await?;

// Ensure all the mocks got called as expected
for registry_mock in registry_mocks {
registry_mock.assert_async().await;
}

Ok(())
}

0 comments on commit 93c8448

Please sign in to comment.