fix(cli): reject unknown fields in manifest entry specs (fail-loud on typos)

`ManifestScript`, `ManifestRoute`, and every trigger spec
(`KvTriggerSpec`…`QueueTriggerSpec`) lacked `#[serde(deny_unknown_fields)]`,
while their parent structs all had it. So a typo inside a `[[triggers.kv]]` /
`[[routes]]` / `[[scripts]]` entry (e.g. `collection` for `collection_glob`,
`methid` for `method`) was silently dropped instead of erroring — the manifest
applied with the mistyped directive missing. Add the attribute to the 9 entry
structs (all fields are exhaustive, so `pull`-emitted TOML still round-trips —
verified against the full journey suite, 151/151). Pinned by a new
`unknown_key_in_an_entry_spec_is_rejected` unit test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-07-13 19:50:02 +02:00
parent 486cc44a06
commit bc2538b5b5

View File

@@ -372,6 +372,7 @@ impl CollectionDecl {
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ManifestScript {
pub name: String,
/// Path to the `.rhai` source, relative to the manifest's directory.
@@ -397,6 +398,7 @@ pub struct ManifestScript {
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ManifestRoute {
/// Name of the script this route binds to.
pub script: String,
@@ -459,6 +461,7 @@ impl ManifestTriggers {
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct KvTriggerSpec {
pub script: String,
pub collection_glob: String,
@@ -479,6 +482,7 @@ pub struct KvTriggerSpec {
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct DocsTriggerSpec {
pub script: String,
pub collection_glob: String,
@@ -497,6 +501,7 @@ pub struct DocsTriggerSpec {
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct FilesTriggerSpec {
pub script: String,
pub collection_glob: String,
@@ -515,6 +520,7 @@ pub struct FilesTriggerSpec {
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct CronTriggerSpec {
pub script: String,
/// 6-field cron expression (with seconds).
@@ -528,6 +534,7 @@ pub struct CronTriggerSpec {
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct PubsubTriggerSpec {
pub script: String,
pub topic_pattern: String,
@@ -546,6 +553,7 @@ pub struct PubsubTriggerSpec {
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct EmailTriggerSpec {
pub script: String,
/// Name of the secret (set via `pic secret set`) holding the inbound
@@ -558,6 +566,7 @@ pub struct EmailTriggerSpec {
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct QueueTriggerSpec {
pub script: String,
pub queue_name: String,
@@ -1080,6 +1089,31 @@ mod tests {
assert_eq!(m.project.unwrap().parent_group.as_deref(), Some("acme"));
}
#[test]
fn unknown_key_in_an_entry_spec_is_rejected() {
// Entry specs (scripts/routes/triggers) are `deny_unknown_fields`, so a
// typo inside a `[[…]]` entry is a hard error, not silently dropped.
// A misspelled trigger field (`collection` for `collection_glob`):
Manifest::parse(
"[app]\nslug = \"a\"\nname = \"A\"\n\n\
[[triggers.kv]]\nscript = \"h\"\ncollection = \"c\"\n",
)
.expect_err("a misspelled [[triggers.kv]] field must be rejected");
// A misspelled route field:
Manifest::parse(
"[app]\nslug = \"a\"\nname = \"A\"\n\n\
[[routes]]\nscript = \"h\"\nhost_kind = \"any\"\npath_kind = \"exact\"\n\
path = \"/x\"\nmethid = \"GET\"\n",
)
.expect_err("a misspelled [[routes]] field must be rejected");
// The correctly-spelled form still parses.
Manifest::parse(
"[app]\nslug = \"a\"\nname = \"A\"\n\n\
[[triggers.kv]]\nscript = \"h\"\ncollection_glob = \"c\"\n",
)
.expect("a correct [[triggers.kv]] entry parses");
}
#[test]
fn overlay_vars_override_base_per_key() {
let mut base = sample();