use super::*;
fn fixture() -> poolster_core::Api {
let mut api = poolster_core::Api {
name: "Layout boundary".into(),
version: "1.0.0".into(),
..Default::default()
};
api.operations = (0..201)
.map(|index| poolster_core::Operation {
id: format!("listItems{index}{}", "y".repeat(64)),
method: poolster_core::HttpMethod::Get,
path: format!("/items/{index}"),
parameters: (0..48)
.map(|field| poolster_core::OperationParameter {
name: format!("field{field}{}", "x".repeat(112)),
location: "query".into(),
required: false,
schema: Some(poolster_core::SchemaValue::new(
poolster_core::SchemaKind::String,
)),
description: None,
annotations: Default::default(),
})
.collect(),
annotations: std::collections::BTreeMap::from([(
"tags".into(),
serde_json::json!(["Items"]),
)]),
..Default::default()
})
.collect();
api.schemas.push(poolster_core::Schema::new(
"Large",
poolster_core::SchemaValue::new(poolster_core::SchemaKind::Object {
fields: (0..900)
.map(|index| poolster_core::Field {
name: format!("field{index}{}", "z".repeat(64)),
value: poolster_core::SchemaValue::new(poolster_core::SchemaKind::String),
required: false,
annotations: Default::default(),
})
.collect(),
additional_properties: poolster_core::AdditionalProperties::Any,
}),
));
api
}
#[test]
fn byte_budget_splits_verbose_single_resource_before_count_limit() {
let tree = presence::render(
&fixture(),
"sdk",
Some("io.poolster.layout"),
SdkClientStyle::Namespaced,
false,
true,
)
.unwrap();
let chunks = tree
.iter()
.filter(|(path, _)| {
path.to_string_lossy()
.starts_with("sdk/src/main/java/io/poolster/layout/internal/Operations")
&& path.extension().and_then(|extension| extension.to_str()) == Some("java")
})
.collect::<Vec<_>>();
assert!(chunks.len() > 2);
for (path, source) in chunks {
assert!(
source.len() <= 128 * 1024,
"{}: {}",
path.display(),
source.len()
);
}
let holders = tree
.iter()
.filter(|(path, _)| path.to_string_lossy().contains("model/PoolsterModelPart"))
.collect::<Vec<_>>();
assert!(holders.len() > 1);
for (path, source) in holders {
assert!(
source.len() <= 128 * 1024,
"{} {}",
path.display(),
source.len()
);
assert!(source.contains("Presence<String>"));
}
let resources = tree
.iter()
.filter(|(path, _)| {
path.to_string_lossy()
.starts_with("sdk/src/main/java/io/poolster/layout/internal/resources")
&& path.extension().and_then(|value| value.to_str()) == Some("java")
})
.collect::<Vec<_>>();
assert!(resources.len() > 1);
for (path, source) in resources {
assert!(
source.len() <= 128 * 1024,
"{} {}",
path.display(),
source.len()
);
}
let root = tempfile::tempdir().unwrap();
tree.write_to(root.path()).unwrap();
let path = tree
.iter()
.find(|(path, _)| {
path.to_string_lossy()
.starts_with("sdk/src/main/java/io/poolster/layout/internal/Operations")
&& path.extension().and_then(|extension| extension.to_str()) == Some("java")
})
.unwrap()
.0;
std::fs::write(root.path().join(path), "customer edit").unwrap();
assert!(
tree.write_to(root.path())
.unwrap_err()
.to_string()
.contains("locally modified generated file")
);
}
#[test]
#[ignore = "requires native SDK toolchain and cached dependencies"]
fn native_byte_grouped_sdk_compiles() {
let tree = presence::render(
&fixture(),
"sdk",
Some("io.poolster.layout"),
SdkClientStyle::Namespaced,
false,
true,
)
.unwrap();
let root = tempfile::tempdir().unwrap();
tree.write_to(root.path()).unwrap();
let probe = root
.path()
.join("sdk/src/test/java/io/poolster/layout/LayoutProbe.java");
std::fs::create_dir_all(probe.parent().unwrap()).unwrap();
std::fs::write(probe, r#"package io.poolster.layout;
import io.poolster.layout.model.*;
import com.fasterxml.jackson.databind.*;
public class LayoutProbe { public static void main(String[] args) throws Exception {
var mapper=new ObjectMapper();var first="field0"+"z".repeat(64);var last="field899"+"z".repeat(64);
var input=mapper.createObjectNode().putNull(first).put(last,"last");input.set("future",mapper.readTree("{\"nested\":[1,null,true]}"));
var model=mapper.treeToValue(input,Large.class);var output=mapper.valueToTree(model);
if(!input.equals(output))throw new AssertionError("split holder roundtrip: "+output);
var result=model.getClass().getMethod(last,Presence.class).invoke(model,Presence.of("updated"));
if(result!=model || !mapper.valueToTree(model).get(last).asText().equals("updated"))throw new AssertionError("inherited fluent setter");
try { model.poolsterAdditionalProperty(last, null);throw new AssertionError("known field shadowed"); } catch(IllegalArgumentException expected) {}
}}"#).unwrap();
let result = std::process::Command::new("mvn")
.args([
"-q",
"test-compile",
"org.codehaus.mojo:exec-maven-plugin:3.5.0:java",
"-Dexec.mainClass=io.poolster.layout.LayoutProbe",
"-Dexec.classpathScope=test",
])
.current_dir(root.path().join("sdk"))
.output()
.unwrap();
assert!(
result.status.success(),
"{}{}",
String::from_utf8_lossy(&result.stdout),
String::from_utf8_lossy(&result.stderr)
);
}
#[test]
fn oversized_atomic_operation_is_diagnosed_without_slicing() {
let mut api = fixture();
api.schemas.clear();
api.operations.truncate(1);
let parameter = api.operations[0].parameters[0].clone();
api.operations[0].parameters = (0..800)
.map(|index| {
let mut parameter = parameter.clone();
parameter.name = format!("parameter{index}{}", "p".repeat(128));
parameter
})
.collect();
let tree = render_test_sdk(&api, "sdk", Some("io.poolster.layout")).unwrap();
let diagnostics: serde_json::Value = serde_json::from_str(
tree.get("sdk/.poolster/source-layout-diagnostics.json")
.unwrap(),
)
.unwrap();
assert!(
diagnostics
.as_array()
.unwrap()
.iter()
.any(|entry| entry["bytes"].as_u64().unwrap() > 128 * 1024)
);
let sources = tree.iter().map(|(_, source)| source).collect::<String>();
assert!(sources.contains(&format!("parameter799{}", "p".repeat(128))));
}