Migrate schema 2 ports through frozen readers and expose schema 3 source API
Rust checks / check (ubuntu-latest) (push) Canceled after 0s
Rust checks / check (windows-latest) (push) Canceled after 0s
Rust checks / check (ubuntu-latest) (push) Canceled after 0s
Rust checks / check (windows-latest) (push) Canceled after 0s
This commit is contained in:
@@ -8,6 +8,20 @@ use serde_json::{Value, json};
|
||||
use std::{path::Path, sync::Arc};
|
||||
use tokio::{sync::oneshot, task::JoinHandle};
|
||||
|
||||
fn v2_missing_ports() -> Value {
|
||||
let mut old: Value = serde_json::from_slice(include_bytes!(
|
||||
"../../oa-core/tests/fixtures/v2-coastal-rescue.json"
|
||||
))
|
||||
.unwrap();
|
||||
for relation in old["relations"].as_object_mut().unwrap().values_mut() {
|
||||
if let Some(exchange) = relation["body"].get_mut("FunctionalExchange") {
|
||||
exchange["source_port"] = Value::Null;
|
||||
exchange["target_port"] = Value::Null;
|
||||
}
|
||||
}
|
||||
old
|
||||
}
|
||||
|
||||
struct TestServer {
|
||||
base: String,
|
||||
client: Client,
|
||||
@@ -1318,6 +1332,7 @@ fn storage_rejects_second_owners_corruption_and_identity_mismatch() {
|
||||
std::fs::write(&path, "{broken").unwrap();
|
||||
assert!(Store::open(directory.path()).is_err());
|
||||
let mismatched = StoredProject {
|
||||
migration: None,
|
||||
resource_id: Id::new_v4(),
|
||||
revision: 1,
|
||||
model: Project::empty(),
|
||||
@@ -1325,6 +1340,7 @@ fn storage_rejects_second_owners_corruption_and_identity_mismatch() {
|
||||
std::fs::write(&path, serde_json::to_vec(&mismatched).unwrap()).unwrap();
|
||||
assert!(Store::open(directory.path()).is_err());
|
||||
let invalid = StoredProject {
|
||||
migration: None,
|
||||
resource_id: filename,
|
||||
revision: 0,
|
||||
model: Project::empty(),
|
||||
@@ -1484,6 +1500,7 @@ async fn fixed_store_capacity_is_enforced_on_http_and_restart() {
|
||||
let mut last = None;
|
||||
for _ in 0..oa_server::store::MAX_PROJECTS {
|
||||
let envelope = StoredProject {
|
||||
migration: None,
|
||||
resource_id: Id::new_v4(),
|
||||
revision: 1,
|
||||
model: Project::empty(),
|
||||
@@ -1624,3 +1641,154 @@ async fn legacy_json_import_and_model_reidentification_keep_the_service_address(
|
||||
);
|
||||
server.stop().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn schema3_migration_reports_and_invalid_typed_transaction_preserve_http_state() {
|
||||
let directory = tempfile::tempdir().unwrap();
|
||||
let server = TestServer::start(directory.path(), None).await;
|
||||
let old = v2_missing_ports();
|
||||
let expected =
|
||||
oa_core::persistence::decode_with_report(&serde_json::to_vec(&old).unwrap()).unwrap();
|
||||
let imported = server
|
||||
.client
|
||||
.post(server.url("/projects/import"))
|
||||
.json(&old)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(imported.status(), StatusCode::CREATED);
|
||||
let etag = imported.headers()["etag"].to_str().unwrap().to_owned();
|
||||
let summary: Value = imported.json().await.unwrap();
|
||||
assert_eq!(summary["schema_version"], 3);
|
||||
assert_eq!(
|
||||
summary["migration"],
|
||||
serde_json::to_value(&expected.migration).unwrap()
|
||||
);
|
||||
let id: Id = summary["resource_id"].as_str().unwrap().parse().unwrap();
|
||||
let stored = server.get(id).await;
|
||||
assert_eq!(stored.model, expected.project);
|
||||
assert_eq!(stored.migration, expected.migration);
|
||||
let filename = directory.path().join(format!("{id}.json"));
|
||||
let before = std::fs::read(&filename).unwrap();
|
||||
let envelope: Value = serde_json::from_slice(&before).unwrap();
|
||||
assert!(envelope.get("migration").is_none());
|
||||
assert_eq!(envelope["model"]["schema_version"], 3);
|
||||
let validation = server
|
||||
.client
|
||||
.post(server.url(&format!("/projects/{id}/validation")))
|
||||
.json(&json!({"format":"json", "model":old}))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(validation.status(), StatusCode::OK);
|
||||
let validation: Value = validation.json().await.unwrap();
|
||||
assert_eq!(validation["valid"], true);
|
||||
assert_eq!(validation["migration"], summary["migration"]);
|
||||
let mut invalid = stored
|
||||
.model
|
||||
.relations
|
||||
.values()
|
||||
.find(|r| r.kind() == RelationKind::FunctionalExchange)
|
||||
.unwrap()
|
||||
.clone();
|
||||
if let oa_core::RelationBody::FunctionalExchange { source_port, .. } = &mut invalid.body {
|
||||
*source_port = Id::nil();
|
||||
}
|
||||
let rejected = server
|
||||
.client
|
||||
.post(server.url(&format!("/projects/{id}/transactions")))
|
||||
.header("if-match", &etag)
|
||||
.json(&json!({"commands":[Command::EditRelation(invalid.id, invalid)]}))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(rejected.status(), StatusCode::UNPROCESSABLE_ENTITY);
|
||||
assert_eq!(server.get(id).await.revision, 1);
|
||||
assert_eq!(std::fs::read(&filename).unwrap(), before);
|
||||
let source = server
|
||||
.client
|
||||
.get(server.url(&format!("/projects/{id}/source")))
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.text()
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(source.starts_with("arcadia 3;"));
|
||||
assert_eq!(oa_core::language::parse(&source).unwrap(), stored.model);
|
||||
let no_op = server
|
||||
.client
|
||||
.put(server.url(&format!("/projects/{id}/source")))
|
||||
.header("content-type", "text/plain")
|
||||
.header("if-match", &etag)
|
||||
.body(source)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(no_op.status(), StatusCode::OK);
|
||||
assert_eq!(no_op.headers()["etag"].to_str().unwrap(), etag);
|
||||
assert_eq!(std::fs::read(&filename).unwrap(), before);
|
||||
server.stop().await;
|
||||
let restarted = TestServer::start(directory.path(), None).await;
|
||||
let reopened = restarted.get(id).await;
|
||||
assert_eq!(reopened.model, stored.model);
|
||||
assert_eq!(reopened.revision, 1);
|
||||
assert!(reopened.migration.is_none());
|
||||
let no_op_legacy = restarted
|
||||
.client
|
||||
.put(restarted.url(&format!("/projects/{id}/model")))
|
||||
.header("if-match", &etag)
|
||||
.json(&old)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(no_op_legacy.status(), StatusCode::OK);
|
||||
assert_eq!(no_op_legacy.headers()["etag"].to_str().unwrap(), etag);
|
||||
let no_op_legacy: Value = no_op_legacy.json().await.unwrap();
|
||||
assert_eq!(no_op_legacy["changed"], false);
|
||||
assert_eq!(no_op_legacy["migration"], summary["migration"]);
|
||||
assert_eq!(std::fs::read(&filename).unwrap(), before);
|
||||
restarted.stop().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn reopening_v2_envelope_reports_migration_without_saving_or_advancing_revision() {
|
||||
let directory = tempfile::tempdir().unwrap();
|
||||
let id = Id::new_v4();
|
||||
let filename = directory.path().join(format!("{id}.json"));
|
||||
let old = v2_missing_ports();
|
||||
let original = serde_json::to_vec(&json!({"resource_id":id,"revision":7,"model":old})).unwrap();
|
||||
std::fs::write(&filename, &original).unwrap();
|
||||
let server = TestServer::start(directory.path(), None).await;
|
||||
let migrated = server.get(id).await;
|
||||
assert_eq!(migrated.resource_id, id);
|
||||
assert_eq!(migrated.revision, 7);
|
||||
assert_eq!(migrated.model.schema_version, 3);
|
||||
assert_eq!(
|
||||
migrated
|
||||
.migration
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.generated_endpoints
|
||||
.len(),
|
||||
2
|
||||
);
|
||||
assert_eq!(std::fs::read(&filename).unwrap(), original);
|
||||
let rename = server
|
||||
.client
|
||||
.patch(server.url(&format!("/projects/{id}")))
|
||||
.header("if-match", migrated.etag())
|
||||
.json(&json!({"name":"Reviewed migrated model"}))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(rename.status(), StatusCode::OK);
|
||||
let current = server.get(id).await;
|
||||
assert_eq!(current.revision, 8);
|
||||
assert_eq!(current.model.name, "Reviewed migrated model");
|
||||
let saved: Value = serde_json::from_slice(&std::fs::read(&filename).unwrap()).unwrap();
|
||||
assert_eq!(saved["revision"], 8);
|
||||
assert_eq!(saved["model"]["schema_version"], 3);
|
||||
assert!(saved.get("migration").is_none());
|
||||
server.stop().await;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user