use agm_core::error::codes::ErrorCode;
use agm_core::memory::schema::MAX_MEMORY_VALUE_BYTES;
use agm_core::parser;
use agm_core::validator::{self, ValidateOptions};
fn fixture(relative: &str) -> String {
let manifest = env!("CARGO_MANIFEST_DIR");
let path = std::path::Path::new(manifest)
.join("../..")
.join("tests/fixtures")
.join(relative);
std::fs::read_to_string(&path)
.unwrap_or_else(|e| panic!("cannot read fixture {}: {e}", path.display()))
}
fn parse_and_validate(
src: &str,
file_name: &str,
) -> agm_core::error::diagnostic::DiagnosticCollection {
let file = parser::parse(src).expect("parse should succeed");
validator::validate(&file, src, file_name, &ValidateOptions::default())
}
fn build_agm_with_memory_value(value_content: &str) -> String {
format!(
"agm: 1.0\n\
package: test.memory\n\
version: 0.1.0\n\
\n\
node data.size_test\n\
type: facts\n\
summary: test memory value size limit\n\
memory:\n\
{INDENT}- key: test.large_value\n\
{INDENT} topic: infrastructure\n\
{INDENT} action: upsert\n\
{INDENT} value: {value_content}\n\
{INDENT} scope: project\n\
{INDENT} ttl: permanent\n",
INDENT = " ",
)
}
#[test]
fn test_memory_value_large_valid_fixture_produces_no_errors() {
let src = fixture("valid/memory_value_large.agm");
let result = parse_and_validate(&src, "memory_value_large.agm");
assert!(
!result.has_errors(),
"Expected no errors for valid large memory value, got: {:?}",
result.diagnostics()
);
}
#[test]
fn test_memory_value_at_exact_limit_produces_no_errors() {
let value = "a".repeat(MAX_MEMORY_VALUE_BYTES);
let src = build_agm_with_memory_value(&value);
let result = parse_and_validate(&src, "at_limit.agm");
let v027_errors: Vec<_> = result
.diagnostics()
.iter()
.filter(|d| d.code == ErrorCode::V027)
.collect();
assert!(
v027_errors.is_empty(),
"Value at exactly 32 KiB should NOT produce V027, got: {v027_errors:?}"
);
}
#[test]
fn test_memory_value_one_byte_over_limit_produces_v027() {
let value = "a".repeat(MAX_MEMORY_VALUE_BYTES + 1);
let src = build_agm_with_memory_value(&value);
let result = parse_and_validate(&src, "over_limit.agm");
assert!(
result
.diagnostics()
.iter()
.any(|d| d.code == ErrorCode::V027),
"Value at 32 KiB + 1 byte should produce V027, got: {:?}",
result.diagnostics()
);
}
#[test]
fn test_memory_value_way_over_limit_produces_v027() {
let value = "b".repeat(MAX_MEMORY_VALUE_BYTES * 2);
let src = build_agm_with_memory_value(&value);
let result = parse_and_validate(&src, "way_over.agm");
assert!(
result
.diagnostics()
.iter()
.any(|d| d.code == ErrorCode::V027),
"Value at 64 KiB should produce V027, got: {:?}",
result.diagnostics()
);
}
#[test]
fn test_memory_value_v027_message_contains_byte_counts() {
let value = "c".repeat(MAX_MEMORY_VALUE_BYTES + 500);
let src = build_agm_with_memory_value(&value);
let result = parse_and_validate(&src, "message_check.agm");
let v027 = result
.diagnostics()
.iter()
.find(|d| d.code == ErrorCode::V027)
.expect("should produce V027");
assert!(
v027.message.contains("exceeds maximum size"),
"V027 message should mention size: {}",
v027.message
);
assert!(
v027.message
.contains(&format!("{}", MAX_MEMORY_VALUE_BYTES)),
"V027 message should mention the limit ({}): {}",
MAX_MEMORY_VALUE_BYTES,
v027.message
);
}
#[test]
fn test_memory_value_v027_is_severity_error() {
assert_eq!(
ErrorCode::V027.default_severity(),
agm_core::error::diagnostic::Severity::Error
);
}
#[test]
fn test_memory_value_empty_does_not_trigger_v027() {
let src = build_agm_with_memory_value("");
let result = parse_and_validate(&src, "empty_value.agm");
let v027_errors: Vec<_> = result
.diagnostics()
.iter()
.filter(|d| d.code == ErrorCode::V027)
.collect();
assert!(
v027_errors.is_empty(),
"Empty value should not produce V027, got: {v027_errors:?}"
);
}
#[test]
fn test_memory_value_just_under_limit_does_not_trigger_v027() {
let value = "d".repeat(MAX_MEMORY_VALUE_BYTES - 1);
let src = build_agm_with_memory_value(&value);
let result = parse_and_validate(&src, "under_limit.agm");
let v027_errors: Vec<_> = result
.diagnostics()
.iter()
.filter(|d| d.code == ErrorCode::V027)
.collect();
assert!(
v027_errors.is_empty(),
"Value at 32 KiB - 1 byte should not produce V027, got: {v027_errors:?}"
);
}
#[test]
fn test_memory_value_multibyte_utf8_counts_bytes_not_chars() {
let value = "\u{1F600}".repeat(MAX_MEMORY_VALUE_BYTES / 4 + 1);
assert!(
value.len() > MAX_MEMORY_VALUE_BYTES,
"precondition: value should exceed byte limit"
);
let src = build_agm_with_memory_value(&value);
let result = parse_and_validate(&src, "utf8_boundary.agm");
assert!(
result
.diagnostics()
.iter()
.any(|d| d.code == ErrorCode::V027),
"Multi-byte UTF-8 value exceeding byte limit should produce V027"
);
}