Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 143 additions & 0 deletions crates/od-ontology/tests/region_digest.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
//! **Region digest (Edit 3)** — the consumer half of the region-grammar
//! knowledge transfer, closed end-to-end over REAL Odoo `account` views.
//!
//! Edit 2 (ruff `ruff_python_spo::extract_odoo_view_regions`, branch
//! `claude/odoo-region-grammar-arm`) harvested the vendored `data/nav/*.xml`
//! into `docked_at`/`tab_order`/`opens_popup` triples; the byte-frozen result
//! is carried here as `data/nav/account_regions.spo.ndjson` (corpus carriage,
//! same pattern as `account_nav.spo.ndjson`). This test folds it through the
//! `region=` convention (`odoo_regions.conf`) using ruff's OWN
//! `build_nav_digest` — the consumer REUSES the digest, never reimplements it
//! (no parallel structure).
//!
//! The load-bearing proof: **every dock token the real arm emitted over real
//! Odoo source is covered by the config** (no `unmapped:` leak) and resolves
//! to one of the six canonical regions. That is the structure-oracle render
//! half proven on real data, decoupled from the ruff merge (float on main;
//! when Edit 2 lands, the live harvest replaces the committed corpus).

use std::collections::BTreeSet;
use std::path::{Path, PathBuf};

use ruff_python_spo::{extract_odoo_view_regions, region_triples};
use ruff_spo_triplet::{build_nav_digest, from_ndjson, parse, to_ndjson};

/// Repo `data/` root (mirror of `klickweg_parity.rs`).
fn data_root() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("../../data")
}

const REGIONS_NDJSON: &str = include_str!("../../../data/nav/account_regions.spo.ndjson");
const REGIONS_CONF: &str = include_str!("../../../data/nav/odoo_regions.conf");

const CANONICAL_REGIONS: &[&str] = &[
"top_bar",
"left_nav",
"center",
"right_panel",
"bottom_bar",
"popup",
];

#[test]
fn live_harvest_reproduces_the_frozen_corpus() {
// The loop, closed LIVE (ruff #79 on main): run the real arm over the
// vendored `account` views TODAY and assert it reproduces the committed
// corpus byte-for-byte. The frozen ndjson is no longer a hand-authored
// stand-in — it is exactly what `extract_odoo_view_regions` emits. If the
// arm intentionally changes, regenerate the corpus (this fails loudly).
let facts = extract_odoo_view_regions(&data_root().join("nav"));
let live = to_ndjson(&region_triples(&facts));
assert_eq!(
live, REGIONS_NDJSON,
"the live ruff arm output must equal data/nav/account_regions.spo.ndjson"
);
}

#[test]
fn region_corpus_round_trips_byte_for_byte() {
// Corpus carriage drift fuse: the committed ndjson survives a closed-vocab
// from_ndjson -> to_ndjson round-trip unchanged (a hand-edit or a harvester
// regression that reshapes a triple fails here).
let triples = from_ndjson(REGIONS_NDJSON).expect("committed region corpus parses");
assert!(!triples.is_empty(), "region corpus is non-empty");
assert_eq!(
to_ndjson(&triples),
REGIONS_NDJSON,
"data/nav/account_regions.spo.ndjson must be byte-stable through the closed vocab"
);
}

#[test]
fn every_real_dock_token_is_covered_by_the_config() {
// THE proof: fold the REAL harvest through the REAL config and assert no
// token leaks. Every `docked_at` object emitted over the vendored account
// views must be a key in odoo_regions.conf — the render frame is total.
let triples = from_ndjson(REGIONS_NDJSON).expect("corpus parses");
let cfg = parse(REGIONS_CONF);
let mapped: BTreeSet<&str> = cfg.regions.iter().map(|(t, _)| t.as_str()).collect();

let dock_tokens: BTreeSet<String> = triples
.iter()
.filter(|t| t.p == "docked_at")
.map(|t| t.o.clone())
.collect();
assert!(!dock_tokens.is_empty(), "corpus carries docked_at facts");

for tok in &dock_tokens {
assert!(
mapped.contains(tok.as_str()),
"real dock token {tok:?} is NOT in odoo_regions.conf — it would render \
`unmapped:{tok}`; add a `region={tok}:<region>` row"
);
}
}

#[test]
fn every_resolved_region_is_canonical() {
let triples = from_ndjson(REGIONS_NDJSON).expect("corpus parses");
let cfg = parse(REGIONS_CONF);
let token_to_region: std::collections::HashMap<&str, &str> = cfg
.regions
.iter()
.map(|(t, r)| (t.as_str(), r.as_str()))
.collect();

let regions: BTreeSet<&str> = triples
.iter()
.filter(|t| t.p == "docked_at")
.filter_map(|t| token_to_region.get(t.o.as_str()).copied())
.collect();

for region in &regions {
assert!(
CANONICAL_REGIONS.contains(region),
"real harvest resolves to non-canonical region {region:?}"
);
}
// The real account views populate more than one region (a genuine frame,
// not everything-in-center).
assert!(
regions.len() >= 2,
"real account views should populate multiple regions, got {regions:?}"
);
}

#[test]
fn build_nav_digest_folds_the_regions_section() {
// Consume ruff's OWN digest builder (no reimplementation). It must produce
// a non-empty digest that carries the [regions] section for our facts.
let triples = from_ndjson(REGIONS_NDJSON).expect("corpus parses");
let cfg = parse(REGIONS_CONF);
let digest = build_nav_digest(&triples, &cfg);
assert!(!digest.is_empty(), "digest is non-empty");
assert!(
digest.contains("[regions]"),
"digest must carry the [regions] section (build_nav_digest folds docked_at)"
);
// No token leaked as `unmapped:` (the config is total over the real harvest).
assert!(
!digest.contains("unmapped:"),
"no dock token should render `unmapped:` — the config covers the real harvest"
);
}
124 changes: 124 additions & 0 deletions crates/od-ontology/tests/region_grammar.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
//! **Region-grammar config pin** — proof-by-execution for the knowledge
//! transfer of ruff PR #76's region grammar into the odoo-rs transcode.
//!
//! `docs/knowledge/ODOO-REGION-GRAMMAR.md` carries #76's six-region pattern
//! (`docked_at`/`tab_order`/`opens_popup` → `{top_bar, left_nav, center,
//! right_panel, bottom_bar, popup}`) onto Odoo's XML view arch. The mapping
//! lives as data — `data/nav/odoo_regions.conf` — consumed by ruff's own
//! `region=` directive (`ruff_spo_triplet::parse`). This test proves the
//! config PARSES through that real directive and produces the pinned
//! arch-token → region map, and that every region is one of the six
//! canonical names (a malformed row can never silently mis-dock).
//!
//! This pins the convention-config half. Edit 1 (vocab) is on ruff main;
//! Edit 2 (the ruff Odoo `docked_at` harvester arm,
//! `ruff_python_spo::extract_odoo_view_regions`) is built on branch
//! `claude/odoo-region-grammar-arm`; Edit 3 (the `[regions]` digest over the
//! real harvest) is `tests/region_digest.rs`. All three now exist.

use ruff_spo_triplet::parse;

/// The six canonical regions (`exam_config.rs:36-37`, verbatim on ruff main).
const CANONICAL_REGIONS: &[&str] = &[
"top_bar",
"left_nav",
"center",
"right_panel",
"bottom_bar",
"popup",
];

/// The committed Odoo arch-token → region convention config.
const ODOO_REGIONS_CONF: &str = include_str!("../../../data/nav/odoo_regions.conf");

/// The full pinned map (drift fuse — a hand-edit to the conf that changes a
/// mapping or adds/drops a token fails here loudly).
const EXPECTED: &[(&str, &str)] = &[
// top_bar — top action/filter strip
("header", "top_bar"),
("search", "top_bar"),
("filter", "top_bar"),
// left_nav — left category rail
("searchpanel", "left_nav"),
// center — main body / primary data view
("sheet", "center"),
("form", "center"),
("group", "center"),
("field", "center"),
("separator", "center"),
("list", "center"),
("tree", "center"),
("kanban", "center"),
("notebook", "center"),
("page", "center"),
("pivot", "center"),
("graph", "center"),
("calendar", "center"),
("activity", "center"),
("gantt", "center"),
("root", "center"),
// right_panel — chatter / activity
("chatter", "right_panel"),
// bottom_bar — wizard dialog action bar
("footer", "bottom_bar"),
// popup — dropdown / cog / act_window target="new"
("action_menu", "popup"),
];

#[test]
fn odoo_regions_conf_parses_through_the_ruff_region_directive() {
let cfg = parse(ODOO_REGIONS_CONF);

// Every pinned (token -> region) pair is present, exactly once, verbatim.
for (tok, region) in EXPECTED {
let hits: Vec<&String> = cfg
.regions
.iter()
.filter(|(t, _)| t == tok)
.map(|(_, r)| r)
.collect();
assert_eq!(
hits.len(),
1,
"dock token {tok:?} must map exactly once (found {})",
hits.len()
);
assert_eq!(
hits[0], region,
"dock token {tok:?} maps to the wrong region"
);
}

// No extra rows leaked in (comments/blanks dropped, malformed rows dropped
// by the `:`-split — so the parsed count is exactly the pinned count).
assert_eq!(
cfg.regions.len(),
EXPECTED.len(),
"odoo_regions.conf has {} region rows; pin expects {}",
cfg.regions.len(),
EXPECTED.len()
);
}

#[test]
fn every_mapped_region_is_one_of_the_six_canonical() {
let cfg = parse(ODOO_REGIONS_CONF);
for (tok, region) in &cfg.regions {
assert!(
CANONICAL_REGIONS.contains(&region.as_str()),
"token {tok:?} docks to non-canonical region {region:?} (allowed: {CANONICAL_REGIONS:?})"
);
}
}

#[test]
fn all_six_canonical_regions_are_covered_by_at_least_one_token() {
let cfg = parse(ODOO_REGIONS_CONF);
for region in CANONICAL_REGIONS {
assert!(
cfg.regions.iter().any(|(_, r)| r == region),
"no Odoo arch token maps to canonical region {region:?} — the six-region \
frame must be fully reachable from Odoo arch"
);
}
}
Loading