Skip to content

Commit

Permalink
tests: Add tests for new config crate. (#862)
Browse files Browse the repository at this point in the history
* Update schematic.

* Add more toolchain tests.

* Rename types.

* Start on workspace config.

* Start on workspace tests.

* Test projects.

* Update loaders.

* Start on project tests.

* Add more.

* Add task tests.

* Remove cache.
  • Loading branch information
milesj committed May 30, 2023
1 parent 107718d commit 83057e2
Show file tree
Hide file tree
Showing 31 changed files with 2,281 additions and 261 deletions.
2 changes: 0 additions & 2 deletions .github/workflows/moon.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ jobs:
with:
fetch-depth: 0
- uses: actions/setup-node@v3
with:
cache: yarn
- uses: moonrepo/setup-rust@v0
- uses: moonrepo/tool-version-action@v1
with:
Expand Down
9 changes: 5 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions crates/core/config/tests/project_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -732,9 +732,7 @@ mod tags {
}

#[test]
#[should_panic(
expected = "Invalid identifier foo bar. May only contain alpha-numeric characters, dashes (-), slashes (/), underscores (_), and dots (.)"
)]
#[should_panic(expected = "Invalid format for foo bar")]
fn invalid_format() {
figment::Jail::expect_with(|jail| {
jail.create_file(super::CONFIG_PROJECT_FILENAME, "tags: ['foo bar']")?;
Expand Down
2 changes: 1 addition & 1 deletion nextgen/common/src/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub static ID_PATTERN: Lazy<Regex> =
Lazy::new(|| Regex::new(&format!("^([A-Za-z@]{{1}}{})$", ID_CHARS)).unwrap());

#[derive(Error, Debug)]
#[error("Invalid identifier {}. May only contain alpha-numeric characters, dashes (-), slashes (/), underscores (_), and dots (.).", .0.style(Style::Id))]
#[error("Invalid format for {}, may only contain alpha-numeric characters, dashes (-), slashes (/), underscores (_), and dots (.).", .0.style(Style::Id))]
pub struct IdError(String);

#[derive(Clone, Debug, Default, Eq, Hash, JsonSchema, Ord, PartialEq, PartialOrd, Serialize)]
Expand Down
3 changes: 2 additions & 1 deletion nextgen/config/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ moon_common = { path = "../common" }
moon_target = { path = "../target" }
proto_cli = { workspace = true }
rustc-hash = { workspace = true }
schematic = { version = "0.4.0", default-features = false, features = ["yaml", "valid_url"] }
schematic = { version = "0.4.2", default-features = false, features = ["yaml", "valid_url"] }
semver = "1.0.17"
serde = { workspace = true }
serde_yaml = { workspace = true }
strum = { workspace = true }

[dev-dependencies]
Expand Down
22 changes: 13 additions & 9 deletions nextgen/config/src/inherited_tasks_config.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::language_platform::{LanguageType, PlatformType};
use crate::portable_path::PortablePath;
use crate::project::TaskConfig;
use crate::project_config::ProjectType;
use crate::relative_path::RelativePath;
use crate::FilePath;
use moon_common::{consts, Id};
use moon_target::Target;
Expand All @@ -26,7 +26,7 @@ where
}

/// Docs: https://moonrepo.dev/docs/config/tasks
#[derive(Debug, Default, Clone, Config)]
#[derive(Debug, Clone, Config)]
pub struct InheritedTasksConfig {
#[setting(
default = "https://moonrepo.dev/schemas/tasks.json",
Expand All @@ -38,13 +38,13 @@ pub struct InheritedTasksConfig {
pub extends: Option<String>,

#[setting(merge = merge_fxhashmap)]
pub file_groups: FxHashMap<Id, Vec<RelativePath>>,
pub file_groups: FxHashMap<Id, Vec<PortablePath>>,

#[setting(merge = merge::append_vec)]
pub implicit_deps: Vec<Target>,

#[setting(merge = merge::append_vec)]
pub implicit_inputs: Vec<RelativePath>,
pub implicit_inputs: Vec<PortablePath>,

#[setting(nested, merge = merge::merge_btreemap)]
pub tasks: BTreeMap<Id, TaskConfig>,
Expand Down Expand Up @@ -74,7 +74,11 @@ pub struct InheritedTasksManager {

impl InheritedTasksManager {
pub fn add_config(&mut self, path: &Path, config: PartialInheritedTasksConfig) {
let name = path.file_name().unwrap_or_default().to_str().unwrap();
let name = path
.file_name()
.unwrap_or_default()
.to_str()
.unwrap_or_default();

let name = if name == consts::CONFIG_TASKS_FILENAME {
"*"
Expand Down Expand Up @@ -124,7 +128,7 @@ impl InheritedTasksManager {
language: &LanguageType,
project: &ProjectType,
tags: &[Id],
) -> InheritedTasksConfig {
) -> Result<InheritedTasksConfig, ConfigError> {
let mut config = PartialInheritedTasksConfig::default();

for lookup in self.get_lookup_order(platform, language, project, tags) {
Expand All @@ -135,7 +139,7 @@ impl InheritedTasksManager {
if let Some(tasks) = &mut managed_config.tasks {
for task in tasks.values_mut() {
// Automatically set this lookup as an input
let global_lookup = RelativePath::WorkspaceFile(FilePath(format!(
let global_lookup = PortablePath::WorkspaceFile(FilePath(format!(
".moon/tasks/{lookup}.yml"
)));

Expand All @@ -153,10 +157,10 @@ impl InheritedTasksManager {
}
}

config.merge(&(), managed_config).unwrap();
config.merge(&(), managed_config)?;
}
}

InheritedTasksConfig::from_partial(config)
InheritedTasksConfig::from_partial(&(), config, false)
}
}
2 changes: 1 addition & 1 deletion nextgen/config/src/language_platform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ impl From<LanguageType> for PlatformType {
// Deno and Bun are not covered here!
LanguageType::JavaScript | LanguageType::TypeScript => PlatformType::Node,
LanguageType::Rust => PlatformType::Rust,
// TODO: Move to these to their own platform once it's been implemented!
// TODO: Move these to their own platform once it's been implemented!
LanguageType::Go
| LanguageType::Php
| LanguageType::Python
Expand Down
4 changes: 2 additions & 2 deletions nextgen/config/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
mod inherited_tasks_config;
mod language_platform;
mod portable_path;
mod project;
mod project_config;
mod relative_path;
mod template;
mod template_config;
mod toolchain;
Expand All @@ -13,9 +13,9 @@ mod workspace_config;

pub use inherited_tasks_config::*;
pub use language_platform::*;
pub use portable_path::*;
pub use project::*;
pub use project_config::*;
pub use relative_path::*;
pub use template::*;
pub use template_config::*;
pub use toolchain::*;
Expand Down
156 changes: 156 additions & 0 deletions nextgen/config/src/portable_path.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
use crate::validate::{validate_child_or_root_path, validate_child_relative_path};
use schematic::ValidateError;
use serde::{de, Deserialize, Deserializer, Serialize};

// Not accurate at all but good enough...
fn is_glob(value: &str) -> bool {
value.contains("**") || value.contains('*') || value.contains('{') || value.contains('[')
}

pub trait Portable: Sized {
fn from_str(path: &str) -> Result<Self, ValidateError>;
}

macro_rules! path_type {
($name:ident) => {
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize)]
pub struct $name(pub String);

impl TryFrom<String> for $name {
type Error = ValidateError;

fn try_from(value: String) -> Result<Self, Self::Error> {
$name::from_str(&value)
}
}

impl TryFrom<&String> for $name {
type Error = ValidateError;

fn try_from(value: &String) -> Result<Self, Self::Error> {
$name::from_str(value)
}
}

impl TryFrom<&str> for $name {
type Error = ValidateError;

fn try_from(value: &str) -> Result<Self, Self::Error> {
$name::from_str(value)
}
}

impl<'de> Deserialize<'de> for $name {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let value = String::deserialize(deserializer)?;

$name::from_str(&value).map_err(|error| de::Error::custom(error.message))
}
}
};
}

// Represents any file glob pattern.
path_type!(GlobPath);

impl Portable for GlobPath {
fn from_str(value: &str) -> Result<Self, ValidateError> {
Ok(GlobPath(value.into()))
}
}

// Represents a project-relative file glob pattern.
path_type!(ProjectFileGlob);

impl Portable for ProjectFileGlob {
fn from_str(value: &str) -> Result<Self, ValidateError> {
validate_child_relative_path(value)?;

if value.starts_with('/') {
return Err(ValidateError::new(
"workspace relative paths are not supported",
));
}

Ok(ProjectFileGlob(value.into()))
}
}

// Represents any file system path.
path_type!(FilePath);

impl Portable for FilePath {
fn from_str(value: &str) -> Result<Self, ValidateError> {
if is_glob(value) {
return Err(ValidateError::new(
"globs are not supported, expected a literal file path",
));
}

Ok(FilePath(value.into()))
}
}

// Represents a project-relative file system path.
path_type!(ProjectFilePath);

impl Portable for ProjectFilePath {
fn from_str(value: &str) -> Result<Self, ValidateError> {
if is_glob(value) {
return Err(ValidateError::new(
"globs are not supported, expected a literal file path",
));
}

validate_child_relative_path(value)?;

if value.starts_with('/') {
return Err(ValidateError::new(
"workspace relative paths are not supported",
));
}

Ok(ProjectFilePath(value.into()))
}
}

// Represents either a workspace or project relative glob/path, or env var.
// Workspace paths are prefixed with "/", and env vars with "$".
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub enum PortablePath {
EnvVar(String),
ProjectFile(FilePath),
ProjectGlob(GlobPath),
WorkspaceFile(FilePath),
WorkspaceGlob(GlobPath),
}

impl Portable for PortablePath {
fn from_str(value: &str) -> Result<Self, ValidateError> {
if let Some(env_var) = value.strip_prefix('$') {
return Ok(PortablePath::EnvVar(env_var.to_owned()));
}

validate_child_or_root_path(value)?;

Ok(match (value.starts_with('/'), is_glob(value)) {
(true, true) => PortablePath::WorkspaceGlob(GlobPath::from_str(&value[1..])?),
(true, false) => PortablePath::WorkspaceFile(FilePath::from_str(&value[1..])?),
(false, true) => PortablePath::ProjectGlob(GlobPath::from_str(value)?),
(false, false) => PortablePath::ProjectFile(FilePath::from_str(value)?),
})
}
}

impl<'de> Deserialize<'de> for PortablePath {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
PortablePath::from_str(&String::deserialize(deserializer)?)
.map_err(|error| de::Error::custom(error.message))
}
}
Loading

0 comments on commit 83057e2

Please sign in to comment.