Skip to content
Draft
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
63 changes: 61 additions & 2 deletions crates/ogar-az/tests/correspond.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@

use ogar_ad::AdEntry;
use ogar_dir_core::correspond::{
AdStatus, Binding, Index, Lanes, Output, Planes, Profile, Ruler, Source, flag, fold,
fold_aligned,
AdStatus, Binding, Index, Lanes, NO_ROW, Output, Planes, Profile, Ruler, Source, UNBOUND, flag,
fold, fold_aligned, state,
};
use ogar_dir_core::identity::{
BACKSYNC_TO_ENTRA_ID, CONSISTENCY_GUID_TO_IMMUTABLE_ID, ENTRA_ID_TO_EXO_EXTERNAL_ID,
Expand Down Expand Up @@ -280,6 +280,11 @@ fn run_encoded(e: &Encoded, anchor: IdentityRule, b: &[Binding]) -> Run {
let mut out = Output::for_index(&lanes, &ix);
fold(&lanes, &ix, &mut out);
assert_eq!(out.edges.len(), ix.edges, "the index counts every edge");
assert_eq!(
out.entra_mailbox,
mailbox_oracle(&lanes),
"the fold's mailbox ordinal is the unique in-scope match"
);
// The same rows on one ruler: the position-wise fold confirms exactly
// the slots the join confirms.
let ruler = Ruler::from_fold(&lanes, &ix, &out);
Expand All @@ -297,6 +302,29 @@ fn run_encoded(e: &Encoded, anchor: IdentityRule, b: &[Binding]) -> Run {
Run { lanes, out }
}

/// Scalar reference for [`Output::entra_mailbox`]: a linear scan, without the
/// index or the merge. Exactly one present, same-scope Exchange Online row
/// naming the Entra id, else `NO_ROW`.
fn mailbox_oracle(l: &Lanes) -> Vec<u32> {
(0..l.entra.len())
.map(|j| {
let scope = l.entra.scope[j];
if scope == UNBOUND {
return NO_ROW;
}
let mut hits = (0..l.exo.len()).filter(|&x| {
l.exo.scope[x] == scope
&& l.exo_external.state[x] == state::PRESENT
&& l.exo_external.id[x] == l.entra.owner[j]
});
match (hits.next(), hits.next()) {
(Some(x), None) => x as u32,
_ => NO_ROW,
}
})
.collect()
}

fn statuses(r: &Run) -> Vec<AdStatus> {
r.out.ad.iter().map(|&f| AdStatus::of(f)).collect()
}
Expand Down Expand Up @@ -814,6 +842,7 @@ fn f14_observations_are_not_touched() {
);
assert_ne!(r.out.entra[0] & flag::E_EXO_ONE, 0);
assert_ne!(r.out.exo[0] & flag::X_ONE, 0);
assert_eq!(r.out.entra_mailbox, [0], "the one mailbox, by row ordinal");
}

#[test]
Expand All @@ -836,12 +865,42 @@ fn exchange_online_dangling_and_duplicate() {
};
let r = run(&w, CONSISTENCY_GUID_TO_IMMUTABLE_ID);
assert_ne!(r.out.entra[0] & flag::E_EXO_MANY, 0);
assert_eq!(r.out.entra_mailbox, [NO_ROW], "two mailboxes: no choice");
assert_eq!(
r.out.exo,
[flag::X_ONE, flag::X_ONE, flag::X_DANGLING, flag::X_ABSENT]
);
}

/// The mailbox ordinal is scoped: the same external id in another bound
/// tenant names a different Entra row, never this one.
#[test]
fn mailbox_ordinal_does_not_cross_tenants() {
let x = g(3);
let mut there = entra(x, Wire::Absent);
there.tenant = g(OTHER_TENANT);
let exo = |n, tenant| Exo {
guid: g(n),
tenant: g(tenant),
external: Wire::Id(x),
};
let w = World {
entra: vec![entra(x, Wire::Absent), there],
exo: vec![exo(40, OTHER_TENANT), exo(41, TENANT)],
..World::default()
};
let both = [
bindings()[0],
Binding {
ad_domain: g(OTHER_DOMAIN),
tenant: g(OTHER_TENANT),
},
];
let r = run_encoded(&encode(&w), CONSISTENCY_GUID_TO_IMMUTABLE_ID, &both);
assert_ne!(r.lanes.entra.scope[0], r.lanes.entra.scope[1]);
assert_eq!(r.out.entra_mailbox, [1, 0]);
}

// ---- randomized comparison --------------------------------------------------

struct Rng(u64);
Expand Down
20 changes: 12 additions & 8 deletions crates/ogar-dir-core/src/correspond.rs
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,9 @@ pub struct Output {
pub ad_back: Vec<u32>,
/// Per Entra row.
pub entra: Vec<u32>,
/// Per Entra row: the one Exchange Online row whose external id names
/// it, else [`NO_ROW`] (none, or several: a choice is never made).
pub entra_mailbox: Vec<u32>,
/// Per Entra row: the AD row whose anchor alone claims it, else [`NO_ROW`].
pub entra_claimer: Vec<u32>,
/// Per Entra row: the AD row whose backsync alone names it, else [`NO_ROW`].
Expand All @@ -416,6 +419,7 @@ impl Output {
ad_target: vec![NO_ROW; l.ad.len()],
ad_back: vec![NO_ROW; l.ad.len()],
entra: vec![0; l.entra.len()],
entra_mailbox: vec![NO_ROW; l.entra.len()],
entra_claimer: vec![NO_ROW; l.entra.len()],
entra_namer: vec![NO_ROW; l.entra.len()],
exo: vec![0; l.exo.len()],
Expand Down Expand Up @@ -585,6 +589,7 @@ pub fn fold(l: &Lanes, ix: &Index, out: &mut Output) {
let mbx = count_flag(x.len(), 0, flag::E_EXO_ONE, flag::E_EXO_MANY);
for y in e {
out.entra[y.row as usize] |= mbx;
out.entra_mailbox[y.row as usize] = sole(x);
}
});

Expand Down Expand Up @@ -735,7 +740,10 @@ impl Ruler {
/// failing that exactly one backsync target; exactly one mailbox);
/// anything else leaves the cell empty, so ambiguity can never be read
/// back as agreement.
pub fn from_fold(l: &Lanes, ix: &Index, out: &Output) -> Self {
///
/// `_ix` is unused since the mailbox ordinal moved into [`Output`]; it
/// stays so callers keep compiling.
pub fn from_fold(l: &Lanes, _ix: &Index, out: &Output) -> Self {
let n = l.ad.len();
let mut r = Self {
ad_anchor: vec![Guid128::NIL; n],
Expand All @@ -747,13 +755,9 @@ impl Ruler {
exo_external: vec![Guid128::NIL; n],
max_skew_ms: l.max_skew_ms,
};
// The one mailbox of each Entra row, if exactly one.
let mut mailbox = vec![NO_ROW; l.entra.len()];
merge(&ix.exo_by_external, &ix.entra_by_id, |x, e| {
for y in e {
mailbox[y.row as usize] = sole(x);
}
});
// The one mailbox of each Entra row comes from the fold; no second
// merge.
let mailbox = &out.entra_mailbox;
for i in 0..n {
if l.ad_anchor.state[i] == state::PRESENT && out.ad[i] & flag::ANCHOR_SHARED == 0 {
r.ad_anchor[i] = l.ad_anchor.id[i];
Expand Down
Loading