fix: preserve string and table Profile scope authoring (T-710)
Resolve MR sequence 5 profile_authoring_rejects_valid_string_scope_intents. Decodal 0.4 has no union constraint: retain an opaque scope range with non-materialized table completion hints so string intents remain valid. Keep shared Backend semantic validation authoritative for scope forms/types/unknown keys without changing persisted values or weakening worker/feature editor diagnostics.
Prove actual-provider native/WASM scalar/table, inline/import/patch, nested intent/deny_write/symlink_policy completion and evaluation invariance. Add real save/projection/Runtime scope equivalence and invalid-scope save non-loss coverage. Regenerated WASM artifact is byte-identical; the changed shape is transported via the fingerprint-bound Backend schema metadata.
Validation: root cargo check; config-source39; value_profiles4; unfiltered server643+11; worker-runtime340+17+17; server-api --features typescript77; wasm32 release/wasm-bindgen0.2.117/Binaryen132 -Oz; Deno39; deno task check0/0; fmt/diff checks. Logs target/t710-scope-{check,server,server-api,runtime}.log. No target/ref integration changes.
This commit is contained in:
@@ -228,6 +228,81 @@ async fn value_profiles_save_project_and_runtime_consume_the_same_revision() {
|
||||
assert!(restored.feature.task.enabled);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn value_profiles_scalar_and_table_scopes_analyze_save_and_resolve_equally() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let api = test_api(dir.path()).await;
|
||||
let scalar = format!(
|
||||
r#"{PROFILE} // {{ scope = "workspace_read"; delegation_scope = "workspace_write"; }}"#
|
||||
);
|
||||
let table = format!(
|
||||
r#"{PROFILE} // {{ scope = {{ intent = "workspace_read"; }}; delegation_scope = {{ intent = "workspace_write"; }}; }}"#
|
||||
);
|
||||
let mut scopes = Vec::new();
|
||||
for (index, form) in [scalar.clone(), table, r#"import "./scoped.dcdl""#.into()]
|
||||
.iter()
|
||||
.enumerate()
|
||||
{
|
||||
let extra = if index == 0 {
|
||||
vec![config_source::ConfigTreeChange::Create {
|
||||
path: config_source::VirtualPath::parse("scoped.dcdl").unwrap(),
|
||||
content_type: config_source::ConfigContentType::Decodal,
|
||||
content: scalar.clone(),
|
||||
}]
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
let authored = format!("{} as WorkspaceConfigSchema", source(form));
|
||||
let state =
|
||||
commit_workspace_config_tree(&api, TEST_WORKSPACE_ID, &request(&api, &authored, extra))
|
||||
.unwrap();
|
||||
let environment = config_source::SnapshotEnvironment::new(state.snapshot.clone())
|
||||
.with_schema_bundle(state.contract.schema_bundle.clone());
|
||||
assert!(
|
||||
environment
|
||||
.analyze(
|
||||
&config_source::VirtualPath::parse("main.dcdl").unwrap(),
|
||||
None
|
||||
)
|
||||
.is_empty(),
|
||||
"{authored}"
|
||||
);
|
||||
let projection = project_profiles_from_workspace_config(TEST_WORKSPACE_ID, &state).unwrap();
|
||||
let bundle = build_virtual_profile_config_bundle(
|
||||
&projection,
|
||||
&state,
|
||||
TEST_WORKSPACE_ID,
|
||||
"created",
|
||||
"project:alpha",
|
||||
)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let resolved = bundle
|
||||
.profile_source_archive
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.verify()
|
||||
.unwrap()
|
||||
.resolve_profile(
|
||||
"project:alpha",
|
||||
Path::new("/runtime-worker"),
|
||||
"scoped-worker",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(resolved.scope.allow.len(), 1);
|
||||
assert_eq!(resolved.delegation_scope.allow.len(), 1);
|
||||
let scope = serde_json::to_value(&resolved.scope).unwrap();
|
||||
let delegation = serde_json::to_value(&resolved.delegation_scope).unwrap();
|
||||
assert_eq!(scope["allow"][0]["target"], "/runtime-worker");
|
||||
assert_eq!(scope["allow"][0]["permission"], "read");
|
||||
assert_eq!(delegation["allow"][0]["target"], "/runtime-worker");
|
||||
assert_eq!(delegation["allow"][0]["permission"], "write");
|
||||
scopes.push((scope, delegation));
|
||||
}
|
||||
assert_eq!(scopes[0], scopes[1]);
|
||||
assert_eq!(scopes[1], scopes[2]);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn value_profiles_invalid_saves_preserve_the_active_config() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
@@ -242,6 +317,12 @@ async fn value_profiles_invalid_saves_preserve_the_active_config() {
|
||||
(source("42"), ""),
|
||||
(source(r#"{ worker = { mode = "not-a-mode"; }; }"#), "profile_value_invalid"),
|
||||
(source(r#"{ worker = { name = "runtime-only"; }; }"#), "profile_value_invalid"),
|
||||
(source(r#"{ scope = 42; }"#), "profile_value_invalid"),
|
||||
(source(r#"{ scope = "not-an-intent"; }"#), "profile_value_invalid"),
|
||||
(source(r#"{ delegation_scope = { intent = 42; }; }"#), "profile_value_invalid"),
|
||||
(source(r#"{ scope = { intent = "workspace_read"; unknown = true; }; }"#), "profile_value_invalid"),
|
||||
(source(r#"{ scope = { intent = "workspace_read"; deny_write = 42; }; }"#), "profile_value_invalid"),
|
||||
(source(r#"{ delegation_scope = { intent = "workspace_write"; symlink_policy = 42; }; }"#), "profile_value_invalid"),
|
||||
(source(r#"import "./missing.dcdl""#), ""),
|
||||
(source(r#"import "../outside.dcdl""#), ""),
|
||||
(r#"{ profile = { entries = [{ selector = "project:alpha"; profile = {}; }, { selector = "project:alpha"; profile = {}; }]; }; }"#.into(), "profile_selector_duplicate"),
|
||||
|
||||
Reference in New Issue
Block a user