Restore approved state (tree of 4effcc7 "Added LICENSE")

Roll the working tree back to the last approved shape, before the post-LICENSE span that false-greened the AFI parity matrix with symbol-presence probes and smuggled an unauthorized SQLAlchemy dependency into FastAPI's Shapes binding.

Forward commit, not a history rewrite — the six commits since 4effcc7 stay in the log as the record of what happened.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-04 14:59:53 -04:00
parent adcc027894
commit ae684a36cb
126 changed files with 1711 additions and 13265 deletions

View File

@@ -1,120 +0,0 @@
//! Cross-language pin: Rust `derive_cache_key` must be byte-identical to the
//! Python reference (`cores/mizan-python/.../cache/keys.py`) and to the
//! committed cross-language vectors that `tests/afi` and `mizan-ts` also pin.
//!
//! The Python reference is the oracle: a subprocess mints the key with fixed
//! inputs and the Rust output must match exactly. `never if backend == X` —
//! one spec, pinned both ways.
use mizan_core::derive_cache_key;
use serde_json::{json, Value};
use std::collections::BTreeMap;
use std::path::PathBuf;
use std::process::Command;
/// The `tests/afi` dir, whose venv has `mizan_core` + PyJWT installed.
fn afi_dir() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("../../tests/afi")
.canonicalize()
.expect("tests/afi exists")
}
/// Run the Python reference via `uv run python -c <code>` in tests/afi and
/// return its single stdout line, trimmed.
fn py(code: &str) -> String {
let out = Command::new("uv")
.args(["run", "python", "-c", code])
.current_dir(afi_dir())
.output()
.expect("invoke uv run python");
assert!(
out.status.success(),
"python reference failed:\nstdout: {}\nstderr: {}",
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr),
);
String::from_utf8(out.stdout).unwrap().trim().to_string()
}
fn tree(pairs: &[(&str, Value)]) -> BTreeMap<String, Value> {
pairs.iter().map(|(k, v)| (k.to_string(), v.clone())).collect()
}
#[test]
fn committed_vectors_match() {
// The exact pins committed in cores/mizan-python/tests/test_keys.py and
// backends/mizan-ts/tests — the canonical cross-language anchor.
let secret = "test-pin-secret-that-is-32bytes!";
let public = derive_cache_key(secret, "user", &tree(&[("user_id", json!("5"))]), None, 0);
assert_eq!(
public,
"ctx:user:605a1ca5ad5994e9b765c8d1b330474c2a0d51a7b8fbbdc402f992da7ba902f6"
);
let scoped = derive_cache_key(
secret,
"user",
&tree(&[("user_id", json!("5"))]),
Some("5"),
0,
);
assert_eq!(
scoped,
"ctx:user:30fc08eb46ee4ff2cf7d317e97dca90fd616511e0587304416f71dc863338dc2"
);
}
#[test]
fn matches_python_reference_across_inputs() {
// A spread of shapes: multi-param (order-independence), numeric vs string,
// bool/null normalization, user-scoped, nonzero rev.
let cases: Vec<(&str, BTreeMap<String, Value>, Option<&str>, i64)> = vec![
("user", tree(&[("user_id", json!("5"))]), None, 0),
("user", tree(&[("user_id", json!("5"))]), Some("5"), 0),
("user", tree(&[("user_id", json!("5"))]), Some("5"), 3),
(
"search",
tree(&[("q", json!("hello world")), ("page", json!(2))]),
None,
0,
),
(
"flags",
tree(&[("on", json!(true)), ("off", json!(false)), ("nil", json!(null))]),
Some("42"),
1,
),
("empty", tree(&[]), None, 0),
(
"unicode",
tree(&[("name", json!("café—ñ"))]),
None,
0,
),
];
for (ctx, params, uid, rev) in cases {
let rust = derive_cache_key("pin-secret-xyz", ctx, &params, uid, rev);
// Build the Python call: derive_cache_key(secret, ctx, params, user_id, rev).
let params_json = serde_json::to_string(
&params.iter().map(|(k, v)| (k.clone(), v.clone())).collect::<serde_json::Map<_, _>>(),
)
.unwrap();
let uid_arg = match uid {
Some(u) => format!("'{u}'"),
None => "None".to_string(),
};
let code = format!(
"import json; from mizan_core.cache.keys import derive_cache_key; \
print(derive_cache_key('pin-secret-xyz', {ctx:?}, json.loads(r'''{params_json}'''), {uid_arg}, {rev}))",
);
let expected = py(&code);
assert_eq!(
rust, expected,
"cache-key mismatch for ctx={ctx} params={params_json} uid={uid:?} rev={rev}",
);
}
}

View File

@@ -1,90 +0,0 @@
//! Cross-language pin: Rust `format_invalidate_header` must be byte-identical
//! to `cores/mizan-python/.../invalidation.py::format_invalidate_header`.
//!
//! The `X-Mizan-Invalidate` header is co-equal with the JSON body channel in
//! the spec; Edge parses it to purge. The Python reference is the oracle.
use mizan_core::{format_invalidate_header, InvalidationTarget};
use serde_json::json;
use std::path::PathBuf;
use std::process::Command;
fn afi_dir() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("../../tests/afi")
.canonicalize()
.expect("tests/afi exists")
}
fn py_header(json_list: &str) -> String {
let code = format!(
"import json; from mizan_core.invalidation import format_invalidate_header; \
print(format_invalidate_header(json.loads(r'''{json_list}''')))",
);
let out = Command::new("uv")
.args(["run", "python", "-c", &code])
.current_dir(afi_dir())
.output()
.expect("invoke uv run python");
assert!(
out.status.success(),
"python reference failed: {}",
String::from_utf8_lossy(&out.stderr)
);
// Trim the trailing newline only — the header value itself may be empty.
let s = String::from_utf8(out.stdout).unwrap();
s.strip_suffix('\n').unwrap_or(&s).to_string()
}
fn scoped(ctx: &str, params: &[(&str, serde_json::Value)]) -> InvalidationTarget {
InvalidationTarget::ScopedContext {
context: ctx.to_string(),
params: params.iter().map(|(k, v)| (k.to_string(), v.clone())).collect(),
}
}
#[test]
fn matches_python_reference() {
let cases: Vec<(Vec<InvalidationTarget>, &str)> = vec![
(vec![InvalidationTarget::Context("user".into())], r#"["user"]"#),
(
vec![
InvalidationTarget::Context("user".into()),
InvalidationTarget::Context("notifications".into()),
],
r#"["user", "notifications"]"#,
),
(
vec![scoped("user", &[("user_id", json!(5))])],
r#"[{"context": "user", "params": {"user_id": 5}}]"#,
),
(
vec![scoped("search", &[("q", json!("hello world"))])],
r#"[{"context": "search", "params": {"q": "hello world"}}]"#,
),
(
// Multiple params → sorted by key, semicolon-joined.
vec![scoped("u", &[("b", json!("2")), ("a", json!("1"))])],
r#"[{"context": "u", "params": {"b": "2", "a": "1"}}]"#,
),
(
// Special chars that must percent-encode: &, =, /, space, unicode.
vec![scoped("c", &[("k", json!("a&b=c/d e—ñ"))])],
r#"[{"context": "c", "params": {"k": "a&b=c/d e—ñ"}}]"#,
),
(
// Mixed bare + scoped.
vec![
scoped("user", &[("user_id", json!(5))]),
InvalidationTarget::Context("notifications".into()),
],
r#"[{"context": "user", "params": {"user_id": 5}}, "notifications"]"#,
),
];
for (targets, json_list) in cases {
let rust = format_invalidate_header(&targets);
let expected = py_header(json_list);
assert_eq!(rust, expected, "header mismatch for {json_list}");
}
}

View File

@@ -1,89 +0,0 @@
//! Behavior tests for the Shapes projection + edge-manifest derivation,
//! driven off a small registered fixture (same graph the AFI fixture uses:
//! a nested struct, a user context with a shared `user_id` param, and an
//! `affects` mutation).
use mizan_core as mizan;
use mizan_core::prelude::*;
use mizan_core::{generate_edge_manifest, shapes, RequestHandle};
use serde::{Deserialize, Serialize};
#[derive(Mizan, Serialize, Deserialize, Debug, Clone)]
pub struct Address {
pub city: String,
pub zip: String,
}
#[derive(Mizan, Serialize, Deserialize, Debug, Clone)]
pub struct Person {
pub user_id: i64,
pub name: String,
pub address: Address,
}
#[derive(Mizan, Serialize, Deserialize, Debug, Clone)]
pub struct Ok {
pub ok: bool,
}
#[mizan::context("people")]
pub struct PeopleCtx;
#[mizan::client(context = PeopleCtx)]
pub async fn person(_req: &RequestHandle<'_>, user_id: i64) -> Person {
Person {
user_id,
name: "x".into(),
address: Address {
city: "c".into(),
zip: "z".into(),
},
}
}
#[mizan::client(affects = PeopleCtx)]
pub async fn rename_person(_req: &RequestHandle<'_>, user_id: i64, _name: String) -> Ok {
let _ = user_id;
Ok { ok: true }
}
#[test]
fn shapes_projection_descends_nested_structs() {
let proj = shapes::project_function_output("person").expect("projects");
assert_eq!(proj.type_name, "personOutput");
// Scalar leaves at the top level.
let leaves = proj.leaf_names();
assert!(leaves.contains(&"user_id"));
assert!(leaves.contains(&"name"));
// `address` is a nested struct → a sub-projection, not a leaf.
assert!(!leaves.contains(&"address"));
let nested = proj.nested();
assert_eq!(nested.len(), 1);
let (name, sub) = nested[0];
assert_eq!(name, "address");
let sub_leaves = sub.leaf_names();
assert!(sub_leaves.contains(&"city") && sub_leaves.contains(&"zip"));
}
#[test]
fn edge_manifest_has_context_render_strategy_and_mutation() {
let m = generate_edge_manifest("/api/mizan");
// Context: user-scoped (has `user_id`) → render_strategy dynamic_cached.
let people = &m["contexts"]["people"];
assert_eq!(people["user_scoped"], serde_json::json!(true));
assert_eq!(people["render_strategy"], serde_json::json!("dynamic_cached"));
assert_eq!(
people["endpoints"],
serde_json::json!(["/api/mizan/ctx/people/"])
);
assert_eq!(people["params"], serde_json::json!(["user_id"]));
// Mutation: rename_person affects people, auto-scopes user_id.
let mutation = &m["mutations"]["rename_person"];
assert_eq!(mutation["affects"], serde_json::json!(["people"]));
assert_eq!(
mutation["auto_scoped_params"],
serde_json::json!(["user_id"])
);
}

View File

@@ -1,105 +0,0 @@
//! Behavior test for the SSR bridge's framing + request/response correlation.
//!
//! Bun isn't required (it isn't installed in CI): a stub worker speaking the
//! exact same newline-delimited JSON-RPC protocol stands in. The stub emits
//! the `{"id":0,"ready":true}` handshake, then for each `render` request
//! echoes back `{"id":N,"html":"<rendered:FILE props=PROPS>"}` — exercising
//! the ready-gate, the per-request id correlation, and the html extraction
//! that the real Bun worker drives.
use mizan_core::{SsrBridge, WorkerCommand};
use serde_json::json;
use std::io::Write;
use std::time::Duration;
/// A tiny Python stub that speaks the SSR worker protocol. Written to a temp
/// file and launched via `python3 <file>`.
const STUB: &str = r#"
import sys, json
# Handshake: announce readiness exactly as the Bun worker does.
sys.stdout.write(json.dumps({"id": 0, "ready": True}) + "\n")
sys.stdout.flush()
for line in sys.stdin:
line = line.strip()
if not line:
continue
msg = json.loads(line)
mid = msg.get("id")
if msg.get("method") == "render":
p = msg["params"]
# A sentinel file name forces the worker-error branch.
if p["file"] == "/boom.tsx":
sys.stdout.write(json.dumps({"id": mid, "error": "render exploded"}) + "\n")
else:
html = "<rendered:%s props=%s>" % (p["file"], json.dumps(p["props"], sort_keys=True))
sys.stdout.write(json.dumps({"id": mid, "html": html}) + "\n")
else:
sys.stdout.write(json.dumps({"id": mid, "error": "unknown method"}) + "\n")
sys.stdout.flush()
"#;
fn write_stub() -> std::path::PathBuf {
let mut path = std::env::temp_dir();
path.push(format!("mizan_ssr_stub_{}.py", std::process::id()));
let mut f = std::fs::File::create(&path).unwrap();
f.write_all(STUB.as_bytes()).unwrap();
path
}
#[test]
fn bridge_drives_worker_protocol() {
let stub = write_stub();
let bridge = SsrBridge::new(
WorkerCommand {
program: "python3".to_string(),
args: vec![stub.to_string_lossy().to_string()],
},
Duration::from_secs(5),
);
// First render — spawns the worker, waits for the ready handshake.
let html = bridge
.render("/abs/Hello.tsx", json!({"name": "World"}))
.expect("first render succeeds");
assert_eq!(
html,
r#"<rendered:/abs/Hello.tsx props={"name": "World"}>"#
);
// Second render reuses the same subprocess; id correlation must keep the
// responses matched to their requests.
let html2 = bridge
.render("/abs/Other.tsx", json!({"a": 1, "b": 2}))
.expect("second render succeeds");
assert_eq!(
html2,
r#"<rendered:/abs/Other.tsx props={"a": 1, "b": 2}>"#
);
bridge.shutdown();
let _ = std::fs::remove_file(&stub);
}
#[test]
fn bridge_propagates_worker_error() {
let stub = write_stub();
let bridge = SsrBridge::new(
WorkerCommand {
program: "python3".to_string(),
args: vec![stub.to_string_lossy().to_string()],
},
Duration::from_secs(5),
);
// The sentinel file makes the stub return an `error` frame; the bridge
// must surface it as `SsrError::Render`, not a successful empty render.
let err = bridge
.render("/boom.tsx", json!({}))
.expect_err("worker error propagates");
assert!(matches!(err, mizan_core::SsrError::Render(_)));
assert!(err.to_string().contains("render exploded"));
// A subsequent good render on the same worker still succeeds.
assert!(bridge.render("/ok.tsx", json!({})).is_ok());
bridge.shutdown();
let _ = std::fs::remove_file(&stub);
}

View File

@@ -1,153 +0,0 @@
//! Cross-language pin: Rust HS256 JWT + MWT must be byte-identical to the
//! Python core (`auth/jwt.py`, `mwt.py`, both PyJWT-backed).
//!
//! Byte-identity is the whole point — Edge and the origin cache key on these
//! tokens, so a one-byte divergence is a cache-key spoof surface. The Python
//! reference is the oracle: it mints with fixed claims + a fixed `iat`/`exp`
//! (we pin `now` on both sides) and the Rust token must match exactly. We also
//! prove round-trip: Rust decodes a Python-minted token and vice-versa.
use mizan_core::{
create_access_token, create_mwt, create_refresh_token, decode_jwt, decode_mwt,
compute_permission_key, JwtConfig,
};
use std::path::PathBuf;
use std::process::Command;
fn afi_dir() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("../../tests/afi")
.canonicalize()
.expect("tests/afi exists")
}
fn py(code: &str) -> String {
let out = Command::new("uv")
.args(["run", "python", "-c", code])
.current_dir(afi_dir())
.output()
.expect("invoke uv run python");
assert!(
out.status.success(),
"python reference failed:\nstdout: {}\nstderr: {}",
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr),
);
String::from_utf8(out.stdout).unwrap().trim().to_string()
}
const NOW: i64 = 1_700_000_000;
#[test]
fn jwt_access_token_matches_python() {
let cfg = JwtConfig::new("jwt-pin-secret");
let rust = create_access_token(&cfg, "42", "sess-abc", true, false, NOW);
// Python: freeze time to NOW, mint an access token with the same claims.
let code = format!(
"import time; from unittest import mock; \
from mizan_core.auth.jwt import JWTConfig, create_access_token; \
cfg = JWTConfig(private_key='jwt-pin-secret', public_key='jwt-pin-secret'); \
orig = time.time; \
time.time = lambda: {NOW}; \
print(create_access_token('42', 'sess-abc', cfg, is_staff=True, is_superuser=False)); \
time.time = orig",
);
let expected = py(&code);
assert_eq!(rust, expected, "JWT access-token byte mismatch");
}
#[test]
fn jwt_refresh_token_matches_python() {
let cfg = JwtConfig::new("jwt-pin-secret");
let rust = create_refresh_token(&cfg, "7", "sid-9", false, true, NOW);
let code = format!(
"import time; from mizan_core.auth.jwt import JWTConfig, create_refresh_token; \
cfg = JWTConfig(private_key='jwt-pin-secret', public_key='jwt-pin-secret'); \
time.time = lambda: {NOW}; \
print(create_refresh_token('7', 'sid-9', cfg, is_staff=False, is_superuser=True))",
);
assert_eq!(rust, py(&code), "JWT refresh-token byte mismatch");
}
#[test]
fn jwt_roundtrip_decode_python_minted() {
// A Python-minted access token must decode in Rust with matching claims.
let code = format!(
"import time; from mizan_core.auth.jwt import JWTConfig, create_access_token; \
cfg = JWTConfig(private_key='rt-secret', public_key='rt-secret'); \
time.time = lambda: {NOW}; \
print(create_access_token('99', 'sess-x', cfg, is_staff=False, is_superuser=True))",
);
let token = py(&code);
let cfg = JwtConfig::new("rt-secret");
let payload = decode_jwt(&token, &cfg, Some("access"), NOW + 10).expect("decodes");
assert_eq!(payload.sub, "99");
assert_eq!(payload.sid, "sess-x");
assert!(payload.superuser);
assert!(!payload.staff);
// Wrong secret → None; expired → None.
assert!(decode_jwt(&token, &JwtConfig::new("nope"), None, NOW + 10).is_none());
assert!(decode_jwt(&token, &cfg, Some("access"), NOW + 10_000).is_none());
// Type mismatch → None.
assert!(decode_jwt(&token, &cfg, Some("refresh"), NOW + 10).is_none());
}
#[test]
fn permission_key_matches_python() {
let perms = vec!["app.add_thing".to_string(), "app.view_thing".to_string()];
let rust = compute_permission_key(true, false, &perms);
let code =
"from mizan_core.mwt import compute_permission_key; \
from unittest.mock import MagicMock; \
u = MagicMock(); u.is_staff=True; u.is_superuser=False; \
u.get_all_permissions = MagicMock(return_value={'app.view_thing','app.add_thing'}); \
print(compute_permission_key(u))";
assert_eq!(rust, py(code), "pkey byte mismatch");
}
#[test]
fn mwt_matches_python() {
// Build the same pkey on both sides, then mint with frozen time + fixed
// kid/audience and compare bytes.
let perms = vec!["app.view_thing".to_string()];
let pkey = compute_permission_key(false, false, &perms);
let rust = create_mwt("mwt-pin-secret", "5", false, false, &pkey, 300, "mizan", "v1", NOW);
let code = format!(
"import time; from unittest.mock import MagicMock; \
from mizan_core.mwt import create_mwt; \
u = MagicMock(); u.pk=5; u.is_staff=False; u.is_superuser=False; \
u.get_all_permissions = MagicMock(return_value={{'app.view_thing'}}); \
time.time = lambda: {NOW}; \
print(create_mwt(u, 'mwt-pin-secret', ttl=300, audience='mizan', kid='v1'))",
);
assert_eq!(rust, py(&code), "MWT byte mismatch");
}
#[test]
fn mwt_roundtrip_and_rejections() {
let pkey = compute_permission_key(true, true, &[]);
let token = create_mwt("rt-mwt", "13", true, true, &pkey, 300, "mizan", "v1", NOW);
let p = decode_mwt(&token, "rt-mwt", "mizan", NOW + 5).expect("decodes");
assert_eq!(p.sub, "13");
assert!(p.staff && p.superuser);
assert_eq!(p.kid, "v1");
assert_eq!(p.pkey.len(), 64);
// Wrong secret, wrong audience, expired → None.
assert!(decode_mwt(&token, "wrong", "mizan", NOW + 5).is_none());
assert!(decode_mwt(&token, "rt-mwt", "other", NOW + 5).is_none());
assert!(decode_mwt(&token, "rt-mwt", "mizan", NOW + 10_000).is_none());
// And a Python-minted MWT decodes in Rust.
let code = format!(
"import time; from unittest.mock import MagicMock; from mizan_core.mwt import create_mwt; \
u = MagicMock(); u.pk=21; u.is_staff=True; u.is_superuser=False; \
u.get_all_permissions = MagicMock(return_value=set()); \
time.time = lambda: {NOW}; print(create_mwt(u, 'rt-mwt', ttl=300, audience='mizan', kid='v1'))",
);
let py_token = py(&code);
let pp = decode_mwt(&py_token, "rt-mwt", "mizan", NOW + 5).expect("py mwt decodes in rust");
assert_eq!(pp.sub, "21");
assert!(pp.staff && !pp.superuser);
}