Compose ordinary source defaults with the reviewed Text workspace
This commit is contained in:
@@ -75,6 +75,164 @@ fn assert_pair(current: &StoredProject) {
|
||||
assert_eq!(compiled.bundle, current.source_project);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn explicit_empty_port_anchors_survive_http_move_and_restart_but_export_compactly() {
|
||||
fn replace_once(source: &mut String, before: &str, after: &str) {
|
||||
assert_eq!(
|
||||
source.matches(before).count(),
|
||||
1,
|
||||
"ambiguous or absent anchor: {before}"
|
||||
);
|
||||
let replacement = source.replacen(before, after, 1);
|
||||
assert_ne!(*source, replacement, "fixture mutation must change source");
|
||||
*source = replacement;
|
||||
}
|
||||
|
||||
async fn assert_exports(server: &TestServer, current: &StoredProject, node: Id) -> String {
|
||||
let response = server
|
||||
.client
|
||||
.get(server.url(&format!("/projects/{}/source-project", current.resource_id)))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(response.headers()["etag"], current.etag());
|
||||
assert_eq!(
|
||||
response.json::<SourceBundle>().await.unwrap(),
|
||||
current.source_project
|
||||
);
|
||||
|
||||
let response = server
|
||||
.client
|
||||
.get(server.url(&format!("/projects/{}/source", current.resource_id)))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
let canonical = response.text().await.unwrap();
|
||||
assert_eq!(oa_core::language::parse(&canonical).unwrap(), current.model);
|
||||
assert_eq!(canonical, oa_core::language::format(¤t.model));
|
||||
let exported = compile_bundle(
|
||||
&SourceBundle {
|
||||
entry: "project.arcadia".into(),
|
||||
files: BTreeMap::from([("project.arcadia".into(), canonical.clone())]),
|
||||
},
|
||||
ProjectLimits::default(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(exported.decoded.project, current.model);
|
||||
assert!(exported.index.declarations.contains_key(&node));
|
||||
assert!(
|
||||
!exported
|
||||
.index
|
||||
.fields
|
||||
.contains_key(&(node, "port_anchors".into()))
|
||||
);
|
||||
canonical
|
||||
}
|
||||
|
||||
let mut authored = source_bundle();
|
||||
replace_once(
|
||||
authored.files.get_mut("views.arcadia").unwrap(),
|
||||
"diagram @architecture; element @vessel;",
|
||||
"diagram @architecture; element @vessel;\n position { x 12.5; y -4.0; };\n port_anchors [] ; // Keep explicit automatic placement.\n",
|
||||
);
|
||||
let compiled = compile_bundle(&authored, ProjectLimits::default()).unwrap();
|
||||
assert_eq!(
|
||||
compiled.bundle, authored,
|
||||
"the HTTP fixture must already be stable"
|
||||
);
|
||||
let diagram = id(&compiled.decoded.project, "architecture");
|
||||
let node = id(&compiled.decoded.project, "actor_view");
|
||||
assert!(
|
||||
compiled
|
||||
.index
|
||||
.fields
|
||||
.contains_key(&(node, "port_anchors".into()))
|
||||
);
|
||||
assert_eq!(
|
||||
compiled.decoded.project.diagrams[&diagram].nodes[&node].position,
|
||||
oa_core::Position { x: 12.5, y: -4.0 }
|
||||
);
|
||||
assert!(
|
||||
compiled.decoded.project.diagrams[&diagram].nodes[&node]
|
||||
.port_anchors
|
||||
.is_empty()
|
||||
);
|
||||
|
||||
// Independent full-model oracle: only this placement coordinate changes.
|
||||
let mut expected_model = compiled.decoded.project.clone();
|
||||
expected_model
|
||||
.diagrams
|
||||
.get_mut(&diagram)
|
||||
.unwrap()
|
||||
.nodes
|
||||
.get_mut(&node)
|
||||
.unwrap()
|
||||
.position
|
||||
.x = 25.0;
|
||||
let mut expected_source = authored.clone();
|
||||
replace_once(
|
||||
expected_source.files.get_mut("views.arcadia").unwrap(),
|
||||
"x 12.5;",
|
||||
"x 25.0;",
|
||||
);
|
||||
|
||||
let directory = tempfile::tempdir().unwrap();
|
||||
let server = TestServer::start(directory.path(), None).await;
|
||||
let response = server
|
||||
.client
|
||||
.post(server.url("/projects/import-source-project"))
|
||||
.json(&authored)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
let status = response.status();
|
||||
let value: Value = response.json().await.unwrap();
|
||||
assert_eq!(status, StatusCode::CREATED, "{value}");
|
||||
let original = server
|
||||
.get(value["resource_id"].as_str().unwrap().parse().unwrap())
|
||||
.await;
|
||||
assert_eq!(original.model, compiled.decoded.project);
|
||||
assert_eq!(original.source_project, authored);
|
||||
assert_pair(&original);
|
||||
assert_exports(&server, &original, node).await;
|
||||
|
||||
let response = transact(
|
||||
&server,
|
||||
&original,
|
||||
json!([{"MoveNode": {"diagram": diagram, "node": node,
|
||||
"position": {"x": 25.0, "y": -4.0}, "with_descendants": false}}]),
|
||||
false,
|
||||
)
|
||||
.await;
|
||||
let status = response.status();
|
||||
let value: Value = response.json().await.unwrap();
|
||||
assert_eq!(status, StatusCode::OK, "{value}");
|
||||
assert_eq!(value["changed"], true);
|
||||
let current = server.get(original.resource_id).await;
|
||||
assert_eq!(current.revision, original.revision + 1);
|
||||
assert_eq!(current.model, expected_model);
|
||||
// Exact bundle equality covers the explicit empty field, its rationale,
|
||||
// sibling placement, and all untouched files and source trivia.
|
||||
assert_eq!(current.source_project, expected_source);
|
||||
assert_pair(¤t);
|
||||
let canonical = assert_exports(&server, ¤t, node).await;
|
||||
|
||||
server.stop().await;
|
||||
let server = TestServer::start(directory.path(), None).await;
|
||||
let reopened = server.get(current.resource_id).await;
|
||||
assert_eq!(reopened.model, expected_model);
|
||||
assert_eq!(reopened.source_project, expected_source);
|
||||
assert_eq!(
|
||||
serde_json::to_value(&reopened).unwrap(),
|
||||
serde_json::to_value(¤t).unwrap()
|
||||
);
|
||||
assert_pair(&reopened);
|
||||
assert_eq!(assert_exports(&server, &reopened, node).await, canonical);
|
||||
server.stop().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn port_anchor_http_commands_and_source_edits_preserve_per_view_state_after_restart() {
|
||||
let directory = tempfile::tempdir().unwrap();
|
||||
|
||||
Reference in New Issue
Block a user