mirror of
https://github.com/ModernRelay/omnigraph.git
synced 2026-07-12 03:12:11 +02:00
feat(engine): apply ExtendEnum as a metadata-only schema step
The widening is planner-verified (superset, same shape), so every committed row is already valid under the wider set: the apply arm joins the AddConstraint metadata-only family — accepted-catalog update, no table work, no version bump — and the unified validator accepts the new variants on all three write surfaces immediately. Tests: enum_widening_apply_is_metadata_only_and_accepts_new_variant (no table-version bump; new variant accepted, original still accepted, out-of-set still rejected) and enum_narrowing_apply_is_refused (OG-MF-106 surfaced, graph left writable).
This commit is contained in:
parent
bc434fb577
commit
cc6f8602a9
2 changed files with 102 additions and 0 deletions
|
|
@ -257,7 +257,12 @@ where
|
|||
// critical path by ensure_indices/optimize (iss-848), so the apply
|
||||
// does no table work for it — a pure metadata change like the two
|
||||
// metadata steps below.
|
||||
// ExtendEnum is a pure widening (planner-verified superset): every
|
||||
// committed row is valid under the wider set, so no table data is
|
||||
// touched — the accepted catalog update alone makes the unified
|
||||
// validator accept the new variants on all three write surfaces.
|
||||
SchemaMigrationStep::AddConstraint { .. }
|
||||
| SchemaMigrationStep::ExtendEnum { .. }
|
||||
| SchemaMigrationStep::UpdateTypeMetadata { .. }
|
||||
| SchemaMigrationStep::UpdatePropertyMetadata { .. } => {}
|
||||
SchemaMigrationStep::DropProperty {
|
||||
|
|
|
|||
|
|
@ -841,3 +841,100 @@ async fn index_only_constraint_apply_touches_no_table_data() {
|
|||
"adding an @index must not bump the table version (no inline index build)"
|
||||
);
|
||||
}
|
||||
|
||||
// Enum widening (iss-enum-widening-migration): adding variants to an enum is
|
||||
// a PURE metadata change — the accepted catalog updates, no table data is
|
||||
// touched, and the widened set is enforced immediately on writes. Narrowing
|
||||
// stays OG-MF-106-refused.
|
||||
#[tokio::test]
|
||||
async fn enum_widening_apply_is_metadata_only_and_accepts_new_variant() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let uri = dir.path().to_str().unwrap();
|
||||
let v1 = "node Ticket {\n slug: String @key\n status: enum(todo, doing, done)\n}\n";
|
||||
let mut db = Omnigraph::init(uri, v1).await.unwrap();
|
||||
load_jsonl(
|
||||
&mut db,
|
||||
r#"{"type":"Ticket","data":{"slug":"t1","status":"todo"}}"#,
|
||||
LoadMode::Merge,
|
||||
)
|
||||
.await
|
||||
.expect("load a Ticket with an original variant");
|
||||
|
||||
let before = db
|
||||
.snapshot_of(ReadTarget::branch("main"))
|
||||
.await
|
||||
.unwrap()
|
||||
.entry("node:Ticket")
|
||||
.unwrap()
|
||||
.table_version;
|
||||
|
||||
let v2 =
|
||||
"node Ticket {\n slug: String @key\n status: enum(todo, doing, done, blocked)\n}\n";
|
||||
let result = db.apply_schema(v2).await.expect("enum widening must apply");
|
||||
assert!(result.supported, "widening must be a supported plan");
|
||||
assert!(result.applied, "widening must apply");
|
||||
|
||||
let after = db
|
||||
.snapshot_of(ReadTarget::branch("main"))
|
||||
.await
|
||||
.unwrap()
|
||||
.entry("node:Ticket")
|
||||
.unwrap()
|
||||
.table_version;
|
||||
assert_eq!(
|
||||
before, after,
|
||||
"enum widening must not bump the table version (metadata-only)"
|
||||
);
|
||||
|
||||
// The NEW variant is accepted on the write path...
|
||||
load_jsonl(
|
||||
&mut db,
|
||||
r#"{"type":"Ticket","data":{"slug":"t2","status":"blocked"}}"#,
|
||||
LoadMode::Merge,
|
||||
)
|
||||
.await
|
||||
.expect("new variant must be accepted after widening");
|
||||
// ...an original variant still is...
|
||||
load_jsonl(
|
||||
&mut db,
|
||||
r#"{"type":"Ticket","data":{"slug":"t3","status":"done"}}"#,
|
||||
LoadMode::Merge,
|
||||
)
|
||||
.await
|
||||
.expect("original variant must remain accepted");
|
||||
// ...and an out-of-set value is still rejected (the fence didn't widen to
|
||||
// free text).
|
||||
let err = load_jsonl(
|
||||
&mut db,
|
||||
r#"{"type":"Ticket","data":{"slug":"t4","status":"bogus"}}"#,
|
||||
LoadMode::Merge,
|
||||
)
|
||||
.await;
|
||||
assert!(err.is_err(), "out-of-set enum value must still be rejected");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn enum_narrowing_apply_is_refused() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let uri = dir.path().to_str().unwrap();
|
||||
let v1 = "node Ticket {\n slug: String @key\n status: enum(todo, doing, done)\n}\n";
|
||||
let mut db = Omnigraph::init(uri, v1).await.unwrap();
|
||||
|
||||
let narrowed = "node Ticket {\n slug: String @key\n status: enum(todo, done)\n}\n";
|
||||
let err = db.apply_schema(narrowed).await;
|
||||
assert!(err.is_err(), "narrowing must refuse at apply");
|
||||
let msg = format!("{}", err.unwrap_err());
|
||||
assert!(
|
||||
msg.contains("OG-MF-106"),
|
||||
"refusal must carry the stable lint code, got: {msg}"
|
||||
);
|
||||
|
||||
// The graph stays healthy and writable on the original schema.
|
||||
load_jsonl(
|
||||
&mut db,
|
||||
r#"{"type":"Ticket","data":{"slug":"t1","status":"doing"}}"#,
|
||||
LoadMode::Merge,
|
||||
)
|
||||
.await
|
||||
.expect("graph must remain writable after a refused narrowing");
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue