Skip to content

Commit c51c73f

Browse files
committed
Trim default_workflow and pin explicit-null bucket-default behavior
Trim the settings default_workflow before mapping to WorkflowIntent so a whitespace-only value resolves to BucketDefault, matching the UI's workflow_intent() and opt_from_string. Pin the explicit-null default_workflow case and tighten the non-string-key assertion with a message check.
1 parent ff1ca80 commit c51c73f

2 files changed

Lines changed: 53 additions & 3 deletions

File tree

quilt-rs/src/io/remote/workflow.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,42 @@ workflows:
461461
err,
462462
Error::RemoteCatalog(RemoteCatalogError::Workflow(_))
463463
));
464+
assert!(err.to_string().contains("must be a string"));
465+
466+
Ok(())
467+
}
468+
469+
#[test(tokio::test)]
470+
async fn test_bucket_default_explicit_null_key() -> Res<()> {
471+
let remote = MockRemote::default();
472+
let host = None;
473+
let uri: S3Uri = "s3://any/.quilt/workflows/config.yml".parse()?;
474+
475+
// `default_workflow:` with no value is a YAML explicit null — not a string,
476+
// so the bucket-default intent errors loudly rather than governing silently.
477+
//
478+
// quilt3 agrees in direction: its config JSON schema types `default_workflow`
479+
// as a string and quilt3 schema-validates the whole config on load, so this
480+
// config fails every push there. quilt-rs rejecting only the bucket-default
481+
// intent is the narrower behavior.
482+
let config = r"
483+
default_workflow:
484+
workflows:
485+
foo:
486+
metadata_schema: bar
487+
";
488+
remote
489+
.put_object(&None, &uri, config.as_bytes().to_vec())
490+
.await?;
491+
492+
let err = resolve_workflow(&remote, &host, WorkflowIntent::BucketDefault, &uri)
493+
.await
494+
.unwrap_err();
495+
assert!(matches!(
496+
err,
497+
Error::RemoteCatalog(RemoteCatalogError::Workflow(_))
498+
));
499+
assert!(err.to_string().contains("must be a string"));
464500

465501
Ok(())
466502
}

quilt-sync/src-tauri/src/model/ops.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -215,9 +215,10 @@ pub async fn publish_with_settings(
215215
},
216216
);
217217
let metadata = settings.default_metadata.clone().unwrap_or_default();
218-
// A missing or empty `default_workflow` means "no opinion" — honour the
219-
// bucket's default workflow; a non-empty id enforces that named workflow.
220-
let workflow = match settings.default_workflow.as_deref() {
218+
// A missing, empty, or whitespace-only `default_workflow` means "no opinion"
219+
// — honour the bucket's default workflow; a non-empty id (after trimming)
220+
// enforces that named workflow.
221+
let workflow = match settings.default_workflow.as_deref().map(str::trim) {
221222
Some(id) if !id.is_empty() => WorkflowIntent::Named(id.to_string()),
222223
_ => WorkflowIntent::BucketDefault,
223224
};
@@ -413,4 +414,17 @@ mod tests {
413414
publish_with_settings(&model, &namespace, &settings, status).await?;
414415
Ok(())
415416
}
417+
418+
#[tokio::test]
419+
async fn publish_with_settings_whitespace_maps_to_bucket_default() -> Result<(), Error> {
420+
let namespace: quilt_uri::Namespace = ("acme", "demo").into();
421+
let model = model_expecting_intent(WorkflowIntent::BucketDefault);
422+
let settings = PublishSettings {
423+
default_workflow: Some(" ".into()),
424+
..PublishSettings::default()
425+
};
426+
let status = quilt::lineage::InstalledPackageStatus::default();
427+
publish_with_settings(&model, &namespace, &settings, status).await?;
428+
Ok(())
429+
}
416430
}

0 commit comments

Comments
 (0)