#![allow(
clippy::expect_used,
clippy::unwrap_used,
clippy::indexing_slicing,
clippy::panic
)]
use std::{
path::{Path, PathBuf},
sync::Arc,
};
use tfparser_core::{
discovery::{Discoverer, DiscoveryOptions, FsDiscoverer},
ir::{AttributeMap, BlockKind, Expression, Value},
loader::{HclEditLoader, LoadContext, Loader, LoaderLimits, RawBlock, SourceMap},
};
const _ASSERT_BODY_IS_ATTRIBUTE_MAP: fn(&RawBlock) -> &AttributeMap = |b| &b.body;
fn workspace_root() -> PathBuf {
let manifest = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
manifest
.ancestors()
.nth(2)
.map(Path::to_path_buf)
.expect("workspace root")
}
fn fixture(name: &str) -> PathBuf {
workspace_root().join("fixtures").join(name)
}
#[test]
fn test_should_discover_single_component_fixture() {
let root = fixture("single-component");
let discovered = FsDiscoverer
.discover(&root, &DiscoveryOptions::defaults())
.expect("discovery succeeds");
assert_eq!(discovered.components.len(), 1, "{discovered:?}");
let c = &discovered.components[0];
assert_eq!(c.path.as_ref(), Path::new(""));
assert_eq!(c.kind, tfparser_core::discovery::DirKind::Component);
}
#[test]
fn test_should_discover_multi_provider_fixture() {
let root = fixture("multi-provider");
let discovered = FsDiscoverer
.discover(&root, &DiscoveryOptions::defaults())
.expect("discovery succeeds");
assert_eq!(discovered.components.len(), 1);
let c = &discovered.components[0];
assert_eq!(c.files.len(), 2, "expected main.tf + variables.tf");
}
#[test]
fn test_should_load_single_component_fixture_blocks() {
let root = fixture("single-component");
let discovered = FsDiscoverer
.discover(&root, &DiscoveryOptions::defaults())
.expect("discovery succeeds");
let sources = SourceMap::new();
let limits = LoaderLimits::default();
let ctx = LoadContext::new(&discovered.root, &sources, &limits);
let raw = HclEditLoader
.load(&discovered.components[0], &ctx)
.expect("load succeeds");
let kinds: Vec<BlockKind> = raw.raw_blocks.iter().map(|b| b.kind).collect();
assert!(kinds.contains(&BlockKind::Terraform), "{kinds:?}");
assert!(kinds.contains(&BlockKind::Provider), "{kinds:?}");
assert!(kinds.contains(&BlockKind::Variable), "{kinds:?}");
assert!(kinds.contains(&BlockKind::Locals), "{kinds:?}");
assert!(kinds.contains(&BlockKind::Resource), "{kinds:?}");
assert!(kinds.contains(&BlockKind::Output), "{kinds:?}");
}
#[test]
fn test_iload2_lowered_body_contains_no_hcl_edit_types() {
let root = fixture("single-component");
let discovered = FsDiscoverer
.discover(&root, &DiscoveryOptions::defaults())
.expect("discovery");
let sources = SourceMap::new();
let limits = LoaderLimits::default();
let ctx = LoadContext::new(&discovered.root, &sources, &limits);
let raw = HclEditLoader
.load(&discovered.components[0], &ctx)
.expect("load");
for block in &raw.raw_blocks {
for (key, expr) in &block.body {
let json = serde_json::to_string(expr).expect("serialize");
for forbidden in ["Decor", "Decorated", "Spanned", "RawString"] {
assert!(
!json.contains(forbidden),
"key={key:?} contains forbidden hcl_edit type `{forbidden}`: {json}"
);
}
}
}
}
#[test]
fn test_should_resolve_known_unresolved_references_in_single_component() {
let root = fixture("single-component");
let discovered = FsDiscoverer
.discover(&root, &DiscoveryOptions::defaults())
.expect("discovery");
let sources = SourceMap::new();
let limits = LoaderLimits::default();
let ctx = LoadContext::new(&discovered.root, &sources, &limits);
let raw = HclEditLoader
.load(&discovered.components[0], &ctx)
.expect("load");
let provider = raw
.raw_blocks
.iter()
.find(|b| b.kind == BlockKind::Provider)
.expect("provider block present");
let region_attr = provider
.body
.iter()
.find(|(k, _)| k.as_ref() == "region")
.expect("provider has region attr");
match ®ion_attr.1 {
Expression::Unresolved(s) => {
assert_eq!(s.source.as_ref(), "var.region");
}
other => panic!("expected Unresolved(var.region), got {other:?}"),
}
}
#[test]
fn test_should_load_multi_provider_with_aliases() {
let root = fixture("multi-provider");
let discovered = FsDiscoverer
.discover(&root, &DiscoveryOptions::defaults())
.expect("discovery");
let sources = SourceMap::new();
let limits = LoaderLimits::default();
let ctx = LoadContext::new(&discovered.root, &sources, &limits);
let raw = HclEditLoader
.load(&discovered.components[0], &ctx)
.expect("load");
let provider_blocks: Vec<_> = raw
.raw_blocks
.iter()
.filter(|b| b.kind == BlockKind::Provider)
.collect();
assert_eq!(provider_blocks.len(), 2, "expected two provider blocks");
for p in provider_blocks {
let alias = p.body.iter().find(|(k, _)| k.as_ref() == "alias");
assert!(alias.is_some(), "provider missing alias: {p:?}");
}
}
#[test]
fn test_should_classify_modules_under_large_monorepo_root() {
let root = workspace_root().join("fixtures/large-monorepo");
if !root.exists() {
return;
}
let discovered = FsDiscoverer
.discover(&root, &DiscoveryOptions::defaults())
.expect("discovery");
let module_paths: Vec<_> = discovered
.modules
.iter()
.map(|m| m.path.to_string_lossy().into_owned())
.collect();
assert!(
module_paths
.iter()
.any(|p| p.starts_with("terraform/modules/")),
"expected `terraform/modules/*` modules in the discovered set; got {module_paths:?}"
);
}
#[test]
fn test_should_keep_arc_path_for_block_source() {
let root = fixture("single-component");
let discovered = FsDiscoverer
.discover(&root, &DiscoveryOptions::defaults())
.expect("discovery");
let sources = SourceMap::new();
let limits = LoaderLimits::default();
let ctx = LoadContext::new(&discovered.root, &sources, &limits);
let raw = HclEditLoader
.load(&discovered.components[0], &ctx)
.expect("load");
let block = raw.raw_blocks.first().expect("at least one block");
let _: &Arc<Path> = &block.source; assert_eq!(block.source.as_ref(), Path::new("main.tf"));
assert!(
block.span.byte_range.start <= block.span.byte_range.end,
"{:?}",
block.span
);
}
#[test]
fn test_should_drop_unresolved_when_array_has_literal_only_values() {
let root = fixture("multi-provider");
let discovered = FsDiscoverer
.discover(&root, &DiscoveryOptions::defaults())
.expect("discovery");
let sources = SourceMap::new();
let limits = LoaderLimits::default();
let ctx = LoadContext::new(&discovered.root, &sources, &limits);
let raw = HclEditLoader
.load(&discovered.components[0], &ctx)
.expect("load");
let var = raw
.raw_blocks
.iter()
.find(|b| b.kind == BlockKind::Variable && b.labels.iter().any(|l| l.as_ref() == "tags"))
.expect("tags variable");
let default = var
.body
.iter()
.find(|(k, _)| k.as_ref() == "default")
.expect("default attr");
assert!(matches!(default.1, Expression::Literal(Value::Map(_))));
}