use std::collections::{BTreeMap, BTreeSet};
use std::sync::Arc;
use crate::entry::{Entry, EntryCommitted, EntrySealed};
use crate::event::{CreateEvent, DeleteEvent, ModifyEvent};
use crate::plugins::Plugin;
use crate::prelude::*;
use crate::value::PartialValue;
pub struct MemberOf;
fn do_group_memberof(
qs: &mut QueryServerWriteTransaction,
uuid: Uuid,
tgte: &mut EntryInvalidCommitted,
) -> Result<(), OperationError> {
let groups = qs
.internal_search(filter!(f_and!([
f_eq(Attribute::Class, EntryClass::Group.into()),
f_or!([
f_eq(Attribute::Member, PartialValue::Refer(uuid)),
f_eq(Attribute::DynMember, PartialValue::Refer(uuid))
])
])))
.map_err(|e| {
admin_error!("internal search failure -> {:?}", e);
e
})?;
tgte.add_ava_if_not_exist(Attribute::Class, EntryClass::MemberOf.into());
tgte.purge_ava(Attribute::MemberOf);
tgte.purge_ava(Attribute::DirectMemberOf);
let dmo = ValueSetRefer::from_iter(groups.iter().map(|g| g.get_uuid()));
let mut mo = ValueSetRefer::from_iter(
groups
.iter()
.filter_map(|g| {
g.get_ava_set(Attribute::MemberOf)
.and_then(|s| s.as_refer_set())
.map(|s| s.iter())
})
.flatten()
.copied(),
);
if let Some(dmo) = dmo {
tgte.set_ava_set(&Attribute::DirectMemberOf, dmo.clone());
if let Some(mo) = &mut mo {
let dmo = dmo as ValueSet;
mo.merge(&dmo)?;
} else {
mo = Some(dmo);
};
};
if let Some(mo) = mo {
tgte.set_ava_set(&Attribute::MemberOf, mo);
}
trace!(
"Updating {:?} to be dir mo {:?}",
uuid,
tgte.get_ava_set(Attribute::DirectMemberOf)
);
trace!(
"Updating {:?} to be mo {:?}",
uuid,
tgte.get_ava_set(Attribute::MemberOf)
);
Ok(())
}
fn do_leaf_memberof(
qs: &mut QueryServerWriteTransaction,
all_affected_uuids: BTreeSet<Uuid>,
) -> Result<(), OperationError> {
trace!("---");
let all_affected_filter: Vec<_> = all_affected_uuids
.into_iter()
.map(|u| f_eq(Attribute::Uuid, PartialValue::Uuid(u)))
.collect();
if all_affected_filter.is_empty() {
trace!("all affected filter is empty, return");
return Ok(());
}
let leaf_entries = qs.internal_search_writeable(&filter!(f_and!([
f_andnot(f_eq(Attribute::Class, EntryClass::Group.into())),
FC::Or(all_affected_filter)
])))?;
if leaf_entries.is_empty() {
trace!("leaf entries empty, return");
return Ok(());
}
let mut leaf_entries: BTreeMap<_, _> = leaf_entries
.into_iter()
.map(|entry_tuple| (entry_tuple.0.get_uuid(), entry_tuple))
.collect();
let mut changes = Vec::with_capacity(leaf_entries.len());
let mut groups_or = Vec::with_capacity(leaf_entries.len() * 2);
for uuid in leaf_entries.keys().copied() {
groups_or.push(f_eq(Attribute::Member, PartialValue::Refer(uuid)));
groups_or.push(f_eq(Attribute::DynMember, PartialValue::Refer(uuid)));
}
let all_groups = qs
.internal_search(filter!(f_and!([
f_eq(Attribute::Class, EntryClass::Group.into()),
FC::Or(groups_or)
])))
.map_err(|err| {
error!(?err, "internal search failure");
err
})?;
for (_pre, tgte) in leaf_entries.values_mut() {
tgte.add_ava_if_not_exist(Attribute::Class, EntryClass::MemberOf.into());
tgte.purge_ava(Attribute::MemberOf);
tgte.purge_ava(Attribute::DirectMemberOf);
}
for group in all_groups {
trace!(group_id = %group.get_display_id());
let group_uuid = group.get_uuid();
let memberof_ref = group.get_ava_refer(Attribute::MemberOf);
let member_ref = group.get_ava_refer(Attribute::Member);
let dynmember_ref = group.get_ava_refer(Attribute::DynMember);
let dir_members = member_ref
.iter()
.flat_map(|set| set.iter())
.chain(dynmember_ref.iter().flat_map(|set| set.iter()))
.copied();
for dir_member in dir_members {
if let Some((_pre, tgte)) = leaf_entries.get_mut(&dir_member) {
trace!(?dir_member, entry_id = ?tgte.get_display_id());
if let Some(dmo_set) = tgte.get_ava_refer_mut(Attribute::DirectMemberOf) {
dmo_set.insert(group_uuid);
} else {
let dmo = ValueSetRefer::new(group_uuid);
tgte.set_ava_set(&Attribute::DirectMemberOf, dmo);
}
if let Some(mo_set) = tgte.get_ava_refer_mut(Attribute::MemberOf) {
mo_set.insert(group_uuid);
} else {
let mo = ValueSetRefer::new(group_uuid);
tgte.set_ava_set(&Attribute::MemberOf, mo);
}
if let Some(group_mo) = memberof_ref {
if let Some(mo_set) = tgte.get_ava_refer_mut(Attribute::MemberOf) {
mo_set.extend(group_mo.iter())
}
}
if cfg!(debug_assertions) {
if let Some(dmo) = group.get_ava_refer(Attribute::DirectMemberOf) {
if let Some(mo) = group.get_ava_refer(Attribute::MemberOf) {
debug_assert!(mo.is_superset(dmo))
}
}
}
}
}
}
leaf_entries
.into_iter()
.try_for_each(|(auuid, (pre, tgte))| {
if pre.get_ava_set(Attribute::MemberOf) != tgte.get_ava_set(Attribute::MemberOf)
|| pre.get_ava_set(Attribute::DirectMemberOf)
!= tgte.get_ava_set(Attribute::DirectMemberOf)
{
trace!("=> processing affected uuid {:?}", auuid);
if cfg!(debug_assertions) {
if let Some(dmo_set) = tgte.get_ava_refer(Attribute::DirectMemberOf) {
trace!(?dmo_set);
if let Some(mo_set) = tgte.get_ava_refer(Attribute::MemberOf) {
trace!(?mo_set);
debug_assert!(mo_set.is_superset(dmo_set));
} else {
unreachable!();
}
} else {
trace!("NONE");
};
if let Some(pre_dmo_set) = pre.get_ava_refer(Attribute::DirectMemberOf) {
trace!(?pre_dmo_set);
if let Some(pre_mo_set) = pre.get_ava_refer(Attribute::MemberOf) {
trace!(?pre_mo_set);
debug_assert!(pre_mo_set.is_superset(pre_dmo_set));
} else {
unreachable!();
}
} else {
trace!("NONE");
};
};
changes.push((pre, tgte));
} else {
trace!("=> ignoring unmodified uuid {:?}", auuid);
}
Ok(())
})?;
qs.internal_apply_writable(changes)
}
#[allow(clippy::cognitive_complexity)]
fn apply_memberof(
qs: &mut QueryServerWriteTransaction,
mut affected_uuids: BTreeSet<Uuid>,
) -> Result<(), OperationError> {
trace!(" => entering apply_memberof");
let mut all_affected_uuids: BTreeSet<_> = affected_uuids.iter().copied().collect();
while !affected_uuids.is_empty() {
trace!(?affected_uuids);
let filt = filter!(f_and!([
f_eq(Attribute::Class, EntryClass::Group.into()),
FC::Or(
affected_uuids
.iter()
.copied()
.map(|u| f_eq(Attribute::Uuid, PartialValue::Uuid(u)))
.collect()
)
]));
affected_uuids.clear();
let work_set = qs.internal_search_writeable(&filt)?;
let mut changes = Vec::with_capacity(work_set.len());
for (pre, mut tgte) in work_set.into_iter() {
let guuid = pre.get_uuid();
trace!(
"=> processing group update -> {:?} {}",
guuid,
tgte.get_display_id()
);
do_group_memberof(qs, guuid, &mut tgte)?;
if pre.get_ava_set(Attribute::MemberOf) != tgte.get_ava_set(Attribute::MemberOf)
|| pre.get_ava_set(Attribute::DirectMemberOf)
!= tgte.get_ava_set(Attribute::DirectMemberOf)
{
trace!(
"{:?} {} changed, flagging members as groups to change. ",
guuid,
tgte.get_display_id()
);
let pre_member = pre.get_ava_refer(Attribute::Member);
let post_member = tgte.get_ava_refer(Attribute::Member);
match (pre_member, post_member) {
(Some(pre_m), Some(post_m)) => {
affected_uuids.extend(pre_m);
affected_uuids.extend(post_m);
}
(Some(members), None) | (None, Some(members)) => {
affected_uuids.extend(members);
}
(None, None) => {}
};
let pre_dynmember = pre.get_ava_refer(Attribute::DynMember);
let post_dynmember = tgte.get_ava_refer(Attribute::DynMember);
match (pre_dynmember, post_dynmember) {
(Some(pre_m), Some(post_m)) => {
affected_uuids.extend(pre_m);
affected_uuids.extend(post_m);
}
(Some(members), None) | (None, Some(members)) => {
affected_uuids.extend(members);
}
(None, None) => {}
};
changes.push((pre, tgte));
} else {
trace!("{:?} {} stable", guuid, tgte.get_display_id());
}
}
if !changes.is_empty() {
trace!("wrote stripe {}", changes.len());
qs.internal_apply_writable(changes).map_err(|err| {
error!(?err, "Failed to commit memberof group set");
err
})?;
}
all_affected_uuids.extend(affected_uuids.iter());
trace!("-------------------------------------");
}
do_leaf_memberof(qs, all_affected_uuids)
}
impl Plugin for MemberOf {
fn id() -> &'static str {
Attribute::MemberOf.as_ref()
}
#[instrument(level = "debug", name = "memberof_post_create", skip_all)]
fn post_create(
qs: &mut QueryServerWriteTransaction,
cand: &[Entry<EntrySealed, EntryCommitted>],
ce: &CreateEvent,
) -> Result<(), OperationError> {
Self::post_create_inner(qs, cand, &ce.ident)
}
#[instrument(level = "debug", name = "memberof_post_repl_refresh", skip_all)]
fn post_repl_refresh(
qs: &mut QueryServerWriteTransaction,
cand: &[Entry<EntrySealed, EntryCommitted>],
) -> Result<(), OperationError> {
let ident = Identity::from_internal();
Self::post_create_inner(qs, cand, &ident)
}
#[instrument(level = "debug", name = "memberof_post_repl_incremental", skip_all)]
fn post_repl_incremental(
qs: &mut QueryServerWriteTransaction,
pre_cand: &[Arc<EntrySealedCommitted>],
cand: &[EntrySealedCommitted],
conflict_uuids: &BTreeSet<Uuid>,
) -> Result<(), OperationError> {
let force_dyngroup_cand_update = !conflict_uuids.is_empty();
let ident_internal = Identity::from_internal();
Self::post_modify_inner(
qs,
pre_cand,
cand,
&ident_internal,
force_dyngroup_cand_update,
)
}
#[instrument(level = "debug", name = "memberof_post_modify", skip_all)]
fn post_modify(
qs: &mut QueryServerWriteTransaction,
pre_cand: &[Arc<Entry<EntrySealed, EntryCommitted>>],
cand: &[Entry<EntrySealed, EntryCommitted>],
me: &ModifyEvent,
) -> Result<(), OperationError> {
Self::post_modify_inner(qs, pre_cand, cand, &me.ident, false)
}
#[instrument(level = "debug", name = "memberof_post_batch_modify", skip_all)]
fn post_batch_modify(
qs: &mut QueryServerWriteTransaction,
pre_cand: &[Arc<Entry<EntrySealed, EntryCommitted>>],
cand: &[Entry<EntrySealed, EntryCommitted>],
me: &BatchModifyEvent,
) -> Result<(), OperationError> {
Self::post_modify_inner(qs, pre_cand, cand, &me.ident, false)
}
#[instrument(level = "debug", name = "memberof_pre_delete", skip_all)]
fn pre_delete(
_qs: &mut QueryServerWriteTransaction,
cand: &mut Vec<EntryInvalidCommitted>,
_de: &DeleteEvent,
) -> Result<(), OperationError> {
for entry in cand.iter_mut() {
if let Some(direct_mo_vs) = entry.pop_ava(Attribute::DirectMemberOf) {
entry.set_ava_set(&Attribute::RecycledDirectMemberOf, direct_mo_vs);
} else {
entry.purge_ava(Attribute::RecycledDirectMemberOf);
}
entry.purge_ava(Attribute::MemberOf);
}
Ok(())
}
#[instrument(level = "debug", name = "memberof_post_delete", skip_all)]
fn post_delete(
qs: &mut QueryServerWriteTransaction,
cand: &[Entry<EntrySealed, EntryCommitted>],
_de: &DeleteEvent,
) -> Result<(), OperationError> {
let affected_uuids = cand
.iter()
.filter_map(|e| {
if e.attribute_equality(Attribute::Class, &EntryClass::Group.into()) {
e.get_ava_as_refuuid(Attribute::Member)
} else {
None
}
})
.flatten()
.chain(
cand.iter()
.filter_map(|post| {
if post.attribute_equality(Attribute::Class, &EntryClass::DynGroup.into()) {
post.get_ava_as_refuuid(Attribute::DynMember)
} else {
None
}
})
.flatten(),
)
.collect();
apply_memberof(qs, affected_uuids)
}
#[instrument(level = "debug", name = "memberof::verify", skip_all)]
fn verify(qs: &mut QueryServerReadTransaction) -> Vec<Result<(), ConsistencyError>> {
let mut r = Vec::with_capacity(0);
let filt_in = filter!(f_pres(Attribute::Class));
let all_cand = match qs
.internal_search(filt_in)
.map_err(|_| Err(ConsistencyError::QueryServerSearchFailure))
{
Ok(all_cand) => all_cand,
Err(e) => return vec![e],
};
for e in all_cand {
let uuid = e.get_uuid();
let filt_in = filter!(f_and!([
f_eq(Attribute::Class, EntryClass::Group.into()),
f_or!([
f_eq(Attribute::Member, PartialValue::Refer(uuid)),
f_eq(Attribute::DynMember, PartialValue::Refer(uuid))
])
]));
let direct_memberof = match qs
.internal_search(filt_in)
.map_err(|_| ConsistencyError::QueryServerSearchFailure)
{
Ok(d_mo) => d_mo,
Err(e) => return vec![Err(e)],
};
let d_groups_set: BTreeSet<Uuid> =
direct_memberof.iter().map(|e| e.get_uuid()).collect();
let d_groups_set = if d_groups_set.is_empty() {
None
} else {
Some(d_groups_set)
};
trace!(
"DMO search groups {:?} -> {:?}",
e.get_display_id(),
d_groups_set
);
match (e.get_ava_set(Attribute::DirectMemberOf), d_groups_set) {
(Some(edmos), Some(b)) => {
match edmos.as_refer_set() {
Some(a) => {
let diff: Vec<_> = a.symmetric_difference(&b).collect();
if !diff.is_empty() {
error!(
"MemberOfInvalid: Entry {}, DMO has inconsistencies",
e.get_display_id(),
);
trace!(entry_direct_member_of = ?a);
trace!(expected_direct_groups = ?b);
trace!(?diff);
r.push(Err(ConsistencyError::MemberOfInvalid(e.get_id())));
}
}
_ => {
error!("MemberOfInvalid: Entry {}, DMO has incorrect syntax - should be reference uuid set", e.get_display_id());
r.push(Err(ConsistencyError::MemberOfInvalid(e.get_id())));
}
}
}
(None, None) => {
}
(entry_direct_member_of, expected_direct_groups) => {
error!(
"MemberOfInvalid directmemberof set and DMO search set differ in size: {}",
e.get_display_id()
);
trace!(?entry_direct_member_of);
trace!(?expected_direct_groups);
r.push(Err(ConsistencyError::MemberOfInvalid(e.get_id())));
}
}
}
r
}
}
impl MemberOf {
fn post_create_inner(
qs: &mut QueryServerWriteTransaction,
cand: &[Entry<EntrySealed, EntryCommitted>],
ident: &Identity,
) -> Result<(), OperationError> {
let dyngroup_change = super::dyngroup::DynGroup::post_create(qs, cand, ident)?;
let affected_uuids = cand
.iter()
.map(|e| e.get_uuid())
.chain(dyngroup_change)
.chain(
cand.iter()
.filter_map(|e| {
if e.attribute_equality(Attribute::Class, &EntryClass::Group.into()) {
e.get_ava_as_refuuid(Attribute::Member)
} else {
None
}
})
.flatten(),
)
.collect();
apply_memberof(qs, affected_uuids)
}
fn post_modify_inner(
qs: &mut QueryServerWriteTransaction,
pre_cand: &[Arc<EntrySealedCommitted>],
cand: &[EntrySealedCommitted],
ident: &Identity,
force_dyngroup_cand_update: bool,
) -> Result<(), OperationError> {
let dyngroup_change = super::dyngroup::DynGroup::post_modify(
qs,
pre_cand,
cand,
ident,
force_dyngroup_cand_update,
)?;
let mut affected_uuids: BTreeSet<_> = cand
.iter()
.map(|post| post.get_uuid())
.chain(dyngroup_change)
.collect();
for (pre, post) in pre_cand.iter().zip(cand.iter()).filter(|(pre, post)| {
post.attribute_equality(Attribute::Class, &EntryClass::Group.into())
|| pre.attribute_equality(Attribute::Class, &EntryClass::Group.into())
}) {
let pre_member = pre.get_ava_refer(Attribute::Member);
let post_member = post.get_ava_refer(Attribute::Member);
match (pre_member, post_member) {
(Some(pre_m), Some(post_m)) => {
affected_uuids.extend(pre_m.symmetric_difference(post_m));
}
(Some(members), None) | (None, Some(members)) => {
affected_uuids.extend(members);
}
(None, None) => {}
};
let pre_dynmember = pre.get_ava_refer(Attribute::DynMember);
let post_dynmember = post.get_ava_refer(Attribute::DynMember);
match (pre_dynmember, post_dynmember) {
(Some(pre_m), Some(post_m)) => {
affected_uuids.extend(pre_m.symmetric_difference(post_m));
}
(Some(members), None) | (None, Some(members)) => {
affected_uuids.extend(members);
}
(None, None) => {}
};
}
apply_memberof(qs, affected_uuids)
}
}
#[cfg(test)]
mod tests {
use crate::prelude::*;
const UUID_A: &str = "aaaaaaaa-f82e-4484-a407-181aa03bda5c";
const UUID_B: &str = "bbbbbbbb-2438-4384-9891-48f4c8172e9b";
const UUID_C: &str = "cccccccc-9b01-423f-9ba6-51aa4bbd5dd2";
const UUID_D: &str = "dddddddd-2ab3-48e3-938d-1b4754cd2984";
lazy_static! {
static ref EA: EntryInitNew = entry_init!(
(Attribute::Class, EntryClass::Group.to_value()),
(Attribute::Class, EntryClass::MemberOf.to_value()),
(Attribute::Name, Value::new_iname("testgroup_a")),
(Attribute::Uuid, Value::Uuid(uuid::uuid!(UUID_A)))
);
static ref EB: EntryInitNew = entry_init!(
(Attribute::Class, EntryClass::Group.to_value()),
(Attribute::Class, EntryClass::MemberOf.to_value()),
(Attribute::Name, Value::new_iname("testgroup_b")),
(Attribute::Uuid, Value::Uuid(uuid::uuid!(UUID_B)))
);
static ref EC: EntryInitNew = entry_init!(
(Attribute::Class, EntryClass::Group.to_value()),
(Attribute::Class, EntryClass::MemberOf.to_value()),
(Attribute::Name, Value::new_iname("testgroup_c")),
(Attribute::Uuid, Value::Uuid(uuid::uuid!(UUID_C)))
);
static ref ED: EntryInitNew = entry_init!(
(Attribute::Class, EntryClass::Group.to_value()),
(Attribute::Class, EntryClass::MemberOf.to_value()),
(Attribute::Name, Value::new_iname("testgroup_d")),
(Attribute::Uuid, Value::Uuid(uuid::uuid!(UUID_D)))
);
}
macro_rules! assert_memberof_int {
(
$qs:expr,
$ea:expr,
$eb:expr,
$mo:expr,
$cand:expr
) => {{
let filt = filter!(f_and!([
f_eq(Attribute::Uuid, PartialValue::new_uuid_s($ea).unwrap()),
f_eq($mo, PartialValue::new_refer_s($eb).unwrap())
]));
let cands = $qs.internal_search(filt).expect("Internal search failure");
debug!("assert_mo_cands {:?}", cands);
assert_eq!(cands.len(), $cand);
}};
}
macro_rules! assert_memberof {
(
$qs:expr,
$ea:expr,
$eb:expr
) => {{
assert_memberof_int!($qs, $ea, $eb, Attribute::MemberOf, 1);
}};
}
macro_rules! assert_dirmemberof {
(
$qs:expr,
$ea:expr,
$eb:expr
) => {{
assert_memberof_int!($qs, $ea, $eb, Attribute::DirectMemberOf, 1);
}};
}
macro_rules! assert_not_memberof {
(
$qs:expr,
$ea:expr,
$eb:expr
) => {{
assert_memberof_int!($qs, $ea, $eb, Attribute::MemberOf, 0);
}};
}
macro_rules! assert_not_dirmemberof {
(
$qs:expr,
$ea:expr,
$eb:expr
) => {{
assert_memberof_int!($qs, $ea, $eb, Attribute::DirectMemberOf, 0);
}};
}
#[test]
fn test_create_mo_single() {
let mut ea = EA.clone();
let eb = EB.clone();
ea.add_ava(Attribute::Member, Value::new_refer_s(UUID_B).unwrap());
let preload = Vec::with_capacity(0);
let create = vec![ea, eb];
run_create_test!(
Ok(()),
preload,
create,
None,
|qs: &mut QueryServerWriteTransaction| {
assert_memberof!(qs, UUID_B, UUID_A);
assert_not_memberof!(qs, UUID_A, UUID_B);
assert_dirmemberof!(qs, UUID_B, UUID_A);
assert_not_dirmemberof!(qs, UUID_A, UUID_B);
}
);
}
#[test]
fn test_create_mo_nested() {
let mut ea = EA.clone();
let mut eb = EB.clone();
let ec = EC.clone();
ea.add_ava(Attribute::Member, Value::new_refer_s(UUID_B).unwrap());
eb.add_ava(Attribute::Member, Value::new_refer_s(UUID_C).unwrap());
let preload = Vec::with_capacity(0);
let create = vec![ea, eb, ec];
run_create_test!(
Ok(()),
preload,
create,
None,
|qs: &mut QueryServerWriteTransaction| {
assert_not_memberof!(qs, UUID_A, UUID_A);
assert_not_memberof!(qs, UUID_A, UUID_B);
assert_not_memberof!(qs, UUID_A, UUID_C);
assert_memberof!(qs, UUID_B, UUID_A);
assert_not_memberof!(qs, UUID_B, UUID_B);
assert_not_memberof!(qs, UUID_B, UUID_C);
assert_memberof!(qs, UUID_C, UUID_A);
assert_memberof!(qs, UUID_C, UUID_B);
assert_not_memberof!(qs, UUID_C, UUID_C);
assert_not_dirmemberof!(qs, UUID_A, UUID_A);
assert_not_dirmemberof!(qs, UUID_A, UUID_B);
assert_not_dirmemberof!(qs, UUID_A, UUID_C);
assert_dirmemberof!(qs, UUID_B, UUID_A);
assert_not_dirmemberof!(qs, UUID_B, UUID_B);
assert_not_dirmemberof!(qs, UUID_B, UUID_C);
assert_not_dirmemberof!(qs, UUID_C, UUID_A);
assert_dirmemberof!(qs, UUID_C, UUID_B);
assert_not_dirmemberof!(qs, UUID_C, UUID_C);
}
);
}
#[test]
fn test_create_mo_cycle() {
let mut ea = EA.clone();
let mut eb = EB.clone();
let mut ec = EC.clone();
ea.add_ava(Attribute::Member, Value::new_refer_s(UUID_B).unwrap());
eb.add_ava(Attribute::Member, Value::new_refer_s(UUID_C).unwrap());
ec.add_ava(Attribute::Member, Value::new_refer_s(UUID_A).unwrap());
let preload = Vec::with_capacity(0);
let create = vec![ea, eb, ec];
run_create_test!(
Ok(()),
preload,
create,
None,
|qs: &mut QueryServerWriteTransaction| {
assert_memberof!(qs, UUID_A, UUID_A);
assert_memberof!(qs, UUID_A, UUID_B);
assert_memberof!(qs, UUID_A, UUID_C);
assert_memberof!(qs, UUID_B, UUID_A);
assert_memberof!(qs, UUID_B, UUID_B);
assert_memberof!(qs, UUID_B, UUID_C);
assert_memberof!(qs, UUID_C, UUID_A);
assert_memberof!(qs, UUID_C, UUID_B);
assert_memberof!(qs, UUID_C, UUID_C);
assert_not_dirmemberof!(qs, UUID_A, UUID_A);
assert_not_dirmemberof!(qs, UUID_A, UUID_B);
assert_dirmemberof!(qs, UUID_A, UUID_C);
assert_dirmemberof!(qs, UUID_B, UUID_A);
assert_not_dirmemberof!(qs, UUID_B, UUID_B);
assert_not_dirmemberof!(qs, UUID_B, UUID_C);
assert_not_dirmemberof!(qs, UUID_C, UUID_A);
assert_dirmemberof!(qs, UUID_C, UUID_B);
assert_not_dirmemberof!(qs, UUID_C, UUID_C);
}
);
}
#[test]
fn test_create_mo_multi_cycle() {
let mut ea = EA.clone();
let mut eb = EB.clone();
let mut ec = EC.clone();
let mut ed = ED.clone();
ea.add_ava(Attribute::Member, Value::new_refer_s(UUID_B).unwrap());
eb.add_ava(Attribute::Member, Value::new_refer_s(UUID_C).unwrap());
ec.add_ava(Attribute::Member, Value::new_refer_s(UUID_A).unwrap());
ec.add_ava(Attribute::Member, Value::new_refer_s(UUID_D).unwrap());
ed.add_ava(Attribute::Member, Value::new_refer_s(UUID_A).unwrap());
let preload = Vec::with_capacity(0);
let create = vec![ea, eb, ec, ed];
run_create_test!(
Ok(()),
preload,
create,
None,
|qs: &mut QueryServerWriteTransaction| {
assert_memberof!(qs, UUID_A, UUID_A);
assert_memberof!(qs, UUID_A, UUID_B);
assert_memberof!(qs, UUID_A, UUID_C);
assert_memberof!(qs, UUID_A, UUID_D);
assert_memberof!(qs, UUID_B, UUID_A);
assert_memberof!(qs, UUID_B, UUID_B);
assert_memberof!(qs, UUID_B, UUID_C);
assert_memberof!(qs, UUID_B, UUID_D);
assert_memberof!(qs, UUID_C, UUID_A);
assert_memberof!(qs, UUID_C, UUID_B);
assert_memberof!(qs, UUID_C, UUID_C);
assert_memberof!(qs, UUID_C, UUID_D);
assert_memberof!(qs, UUID_D, UUID_A);
assert_memberof!(qs, UUID_D, UUID_B);
assert_memberof!(qs, UUID_D, UUID_C);
assert_memberof!(qs, UUID_D, UUID_D);
assert_not_dirmemberof!(qs, UUID_A, UUID_A);
assert_not_dirmemberof!(qs, UUID_A, UUID_B);
assert_dirmemberof!(qs, UUID_A, UUID_C);
assert_dirmemberof!(qs, UUID_A, UUID_D);
assert_dirmemberof!(qs, UUID_B, UUID_A);
assert_not_dirmemberof!(qs, UUID_B, UUID_B);
assert_not_dirmemberof!(qs, UUID_B, UUID_C);
assert_not_dirmemberof!(qs, UUID_B, UUID_D);
assert_not_dirmemberof!(qs, UUID_C, UUID_A);
assert_dirmemberof!(qs, UUID_C, UUID_B);
assert_not_dirmemberof!(qs, UUID_C, UUID_C);
assert_not_dirmemberof!(qs, UUID_C, UUID_D);
assert_not_dirmemberof!(qs, UUID_D, UUID_A);
assert_not_dirmemberof!(qs, UUID_D, UUID_B);
assert_dirmemberof!(qs, UUID_D, UUID_C);
assert_not_dirmemberof!(qs, UUID_D, UUID_D);
}
);
}
#[test]
fn test_modify_mo_add_simple() {
let ea = EA.clone();
let eb = EB.clone();
let preload = vec![ea, eb];
run_modify_test!(
Ok(()),
preload,
filter!(f_eq(
Attribute::Uuid,
PartialValue::new_uuid_s(UUID_A).unwrap()
)),
ModifyList::new_list(vec![Modify::Present(
Attribute::Member,
Value::new_refer_s(UUID_B).unwrap()
)]),
None,
|_| {},
|qs: &mut QueryServerWriteTransaction| {
assert_memberof!(qs, UUID_B, UUID_A);
assert_not_memberof!(qs, UUID_A, UUID_B);
assert_dirmemberof!(qs, UUID_B, UUID_A);
assert_not_dirmemberof!(qs, UUID_A, UUID_B);
}
);
}
#[test]
fn test_modify_mo_add_nested_1() {
let ea = EA.clone();
let mut eb = EB.clone();
let ec = EC.clone();
eb.add_ava(Attribute::Member, Value::new_refer_s(UUID_C).unwrap());
let preload = vec![ea, eb, ec];
run_modify_test!(
Ok(()),
preload,
filter!(f_eq(
Attribute::Uuid,
PartialValue::new_uuid_s(UUID_A).unwrap()
)),
ModifyList::new_list(vec![Modify::Present(
Attribute::Member,
Value::new_refer_s(UUID_B).unwrap()
)]),
None,
|_| {},
|qs: &mut QueryServerWriteTransaction| {
assert_not_memberof!(qs, UUID_A, UUID_A);
assert_not_memberof!(qs, UUID_A, UUID_B);
assert_not_memberof!(qs, UUID_A, UUID_C);
assert_memberof!(qs, UUID_B, UUID_A);
assert_not_memberof!(qs, UUID_B, UUID_B);
assert_not_memberof!(qs, UUID_B, UUID_C);
assert_memberof!(qs, UUID_C, UUID_A);
assert_memberof!(qs, UUID_C, UUID_B);
assert_not_memberof!(qs, UUID_C, UUID_C);
assert_not_dirmemberof!(qs, UUID_A, UUID_A);
assert_not_dirmemberof!(qs, UUID_A, UUID_B);
assert_not_dirmemberof!(qs, UUID_A, UUID_C);
assert_dirmemberof!(qs, UUID_B, UUID_A);
assert_not_dirmemberof!(qs, UUID_B, UUID_B);
assert_not_dirmemberof!(qs, UUID_B, UUID_C);
assert_not_dirmemberof!(qs, UUID_C, UUID_A);
assert_dirmemberof!(qs, UUID_C, UUID_B);
assert_not_dirmemberof!(qs, UUID_C, UUID_C);
}
);
}
#[test]
fn test_modify_mo_add_nested_2() {
let mut ea = EA.clone();
let eb = EB.clone();
let ec = EC.clone();
ea.add_ava(Attribute::Member, Value::new_refer_s(UUID_B).unwrap());
let preload = vec![ea, eb, ec];
run_modify_test!(
Ok(()),
preload,
filter!(f_eq(
Attribute::Uuid,
PartialValue::new_uuid_s(UUID_B).unwrap()
)),
ModifyList::new_list(vec![Modify::Present(
Attribute::Member,
Value::new_refer_s(UUID_C).unwrap()
)]),
None,
|_| {},
|qs: &mut QueryServerWriteTransaction| {
assert_not_memberof!(qs, UUID_A, UUID_A);
assert_not_memberof!(qs, UUID_A, UUID_B);
assert_not_memberof!(qs, UUID_A, UUID_C);
assert_memberof!(qs, UUID_B, UUID_A);
assert_not_memberof!(qs, UUID_B, UUID_B);
assert_not_memberof!(qs, UUID_B, UUID_C);
assert_memberof!(qs, UUID_C, UUID_A);
assert_memberof!(qs, UUID_C, UUID_B);
assert_not_memberof!(qs, UUID_C, UUID_C);
assert_not_dirmemberof!(qs, UUID_A, UUID_A);
assert_not_dirmemberof!(qs, UUID_A, UUID_B);
assert_not_dirmemberof!(qs, UUID_A, UUID_C);
assert_dirmemberof!(qs, UUID_B, UUID_A);
assert_not_dirmemberof!(qs, UUID_B, UUID_B);
assert_not_dirmemberof!(qs, UUID_B, UUID_C);
assert_not_dirmemberof!(qs, UUID_C, UUID_A);
assert_dirmemberof!(qs, UUID_C, UUID_B);
assert_not_dirmemberof!(qs, UUID_C, UUID_C);
}
);
}
#[test]
fn test_modify_mo_add_cycle() {
let mut ea = EA.clone();
let mut eb = EB.clone();
let ec = EC.clone();
ea.add_ava(Attribute::Member, Value::new_refer_s(UUID_B).unwrap());
eb.add_ava(Attribute::Member, Value::new_refer_s(UUID_C).unwrap());
let preload = vec![ea, eb, ec];
run_modify_test!(
Ok(()),
preload,
filter!(f_eq(
Attribute::Uuid,
PartialValue::new_uuid_s(UUID_C).unwrap()
)),
ModifyList::new_list(vec![Modify::Present(
Attribute::Member,
Value::new_refer_s(UUID_A).unwrap()
)]),
None,
|_| {},
|qs: &mut QueryServerWriteTransaction| {
assert_memberof!(qs, UUID_A, UUID_A);
assert_memberof!(qs, UUID_A, UUID_B);
assert_memberof!(qs, UUID_A, UUID_C);
assert_memberof!(qs, UUID_B, UUID_A);
assert_memberof!(qs, UUID_B, UUID_B);
assert_memberof!(qs, UUID_B, UUID_C);
assert_memberof!(qs, UUID_C, UUID_A);
assert_memberof!(qs, UUID_C, UUID_B);
assert_memberof!(qs, UUID_C, UUID_C);
assert_not_dirmemberof!(qs, UUID_A, UUID_A);
assert_not_dirmemberof!(qs, UUID_A, UUID_B);
assert_dirmemberof!(qs, UUID_A, UUID_C);
assert_dirmemberof!(qs, UUID_B, UUID_A);
assert_not_dirmemberof!(qs, UUID_B, UUID_B);
assert_not_dirmemberof!(qs, UUID_B, UUID_C);
assert_not_dirmemberof!(qs, UUID_C, UUID_A);
assert_dirmemberof!(qs, UUID_C, UUID_B);
assert_not_dirmemberof!(qs, UUID_C, UUID_C);
}
);
}
#[test]
fn test_modify_mo_add_multi_cycle() {
let mut ea = EA.clone();
let mut eb = EB.clone();
let mut ec = EC.clone();
let ed = ED.clone();
ea.add_ava(Attribute::Member, Value::new_refer_s(UUID_B).unwrap());
eb.add_ava(Attribute::Member, Value::new_refer_s(UUID_C).unwrap());
ec.add_ava(Attribute::Member, Value::new_refer_s(UUID_D).unwrap());
let preload = vec![ea, eb, ec, ed];
run_modify_test!(
Ok(()),
preload,
filter!(f_or!([
f_eq(Attribute::Uuid, PartialValue::new_uuid_s(UUID_C).unwrap()),
f_eq(Attribute::Uuid, PartialValue::new_uuid_s(UUID_D).unwrap()),
])),
ModifyList::new_list(vec![Modify::Present(
Attribute::Member,
Value::new_refer_s(UUID_A).unwrap()
)]),
None,
|_| {},
|qs: &mut QueryServerWriteTransaction| {
assert_memberof!(qs, UUID_A, UUID_A);
assert_memberof!(qs, UUID_A, UUID_B);
assert_memberof!(qs, UUID_A, UUID_C);
assert_memberof!(qs, UUID_A, UUID_D);
assert_memberof!(qs, UUID_B, UUID_A);
assert_memberof!(qs, UUID_B, UUID_B);
assert_memberof!(qs, UUID_B, UUID_C);
assert_memberof!(qs, UUID_B, UUID_D);
assert_memberof!(qs, UUID_C, UUID_A);
assert_memberof!(qs, UUID_C, UUID_B);
assert_memberof!(qs, UUID_C, UUID_C);
assert_memberof!(qs, UUID_C, UUID_D);
assert_memberof!(qs, UUID_D, UUID_A);
assert_memberof!(qs, UUID_D, UUID_B);
assert_memberof!(qs, UUID_D, UUID_C);
assert_memberof!(qs, UUID_D, UUID_D);
assert_not_dirmemberof!(qs, UUID_A, UUID_A);
assert_not_dirmemberof!(qs, UUID_A, UUID_B);
assert_dirmemberof!(qs, UUID_A, UUID_C);
assert_dirmemberof!(qs, UUID_A, UUID_D);
assert_dirmemberof!(qs, UUID_B, UUID_A);
assert_not_dirmemberof!(qs, UUID_B, UUID_B);
assert_not_dirmemberof!(qs, UUID_B, UUID_C);
assert_not_dirmemberof!(qs, UUID_B, UUID_D);
assert_not_dirmemberof!(qs, UUID_C, UUID_A);
assert_dirmemberof!(qs, UUID_C, UUID_B);
assert_not_dirmemberof!(qs, UUID_C, UUID_C);
assert_not_dirmemberof!(qs, UUID_C, UUID_D);
assert_not_dirmemberof!(qs, UUID_D, UUID_A);
assert_not_dirmemberof!(qs, UUID_D, UUID_B);
assert_dirmemberof!(qs, UUID_D, UUID_C);
assert_not_dirmemberof!(qs, UUID_D, UUID_D);
}
);
}
#[test]
fn test_modify_mo_del_simple() {
let mut ea = EA.clone();
let mut eb = EB.clone();
ea.add_ava(Attribute::Member, Value::new_refer_s(UUID_B).unwrap());
eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
let preload = vec![ea, eb];
run_modify_test!(
Ok(()),
preload,
filter!(f_eq(
Attribute::Uuid,
PartialValue::new_uuid_s(UUID_A).unwrap()
)),
ModifyList::new_list(vec![Modify::Removed(
Attribute::Member,
PartialValue::new_refer_s(UUID_B).unwrap()
)]),
None,
|_| {},
|qs: &mut QueryServerWriteTransaction| {
assert_not_memberof!(qs, UUID_B, UUID_A);
assert_not_memberof!(qs, UUID_A, UUID_B);
assert_not_dirmemberof!(qs, UUID_B, UUID_A);
assert_not_dirmemberof!(qs, UUID_A, UUID_B);
}
);
}
#[test]
fn test_modify_mo_del_nested_1() {
let mut ea = EA.clone();
let mut eb = EB.clone();
let mut ec = EC.clone();
ea.add_ava(Attribute::Member, Value::new_refer_s(UUID_B).unwrap());
eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
eb.add_ava(Attribute::Member, Value::new_refer_s(UUID_C).unwrap());
ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_B).unwrap());
let preload = vec![ea, eb, ec];
run_modify_test!(
Ok(()),
preload,
filter!(f_eq(
Attribute::Uuid,
PartialValue::new_uuid_s(UUID_A).unwrap()
)),
ModifyList::new_list(vec![Modify::Removed(
Attribute::Member,
PartialValue::new_refer_s(UUID_B).unwrap()
)]),
None,
|_| {},
|qs: &mut QueryServerWriteTransaction| {
assert_not_memberof!(qs, UUID_A, UUID_A);
assert_not_memberof!(qs, UUID_A, UUID_B);
assert_not_memberof!(qs, UUID_A, UUID_C);
assert_not_memberof!(qs, UUID_B, UUID_A);
assert_not_memberof!(qs, UUID_B, UUID_B);
assert_not_memberof!(qs, UUID_B, UUID_C);
assert_not_memberof!(qs, UUID_C, UUID_A);
assert_memberof!(qs, UUID_C, UUID_B);
assert_not_memberof!(qs, UUID_C, UUID_C);
assert_not_dirmemberof!(qs, UUID_A, UUID_A);
assert_not_dirmemberof!(qs, UUID_A, UUID_B);
assert_not_dirmemberof!(qs, UUID_A, UUID_C);
assert_not_dirmemberof!(qs, UUID_B, UUID_A);
assert_not_dirmemberof!(qs, UUID_B, UUID_B);
assert_not_dirmemberof!(qs, UUID_B, UUID_C);
assert_not_dirmemberof!(qs, UUID_C, UUID_A);
assert_dirmemberof!(qs, UUID_C, UUID_B);
assert_not_dirmemberof!(qs, UUID_C, UUID_C);
}
);
}
#[test]
fn test_modify_mo_del_nested_2() {
let mut ea = EA.clone();
let mut eb = EB.clone();
let mut ec = EC.clone();
ea.add_ava(Attribute::Member, Value::new_refer_s(UUID_B).unwrap());
eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
eb.add_ava(Attribute::Member, Value::new_refer_s(UUID_C).unwrap());
ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_B).unwrap());
ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
let preload = vec![ea, eb, ec];
run_modify_test!(
Ok(()),
preload,
filter!(f_eq(
Attribute::Uuid,
PartialValue::new_uuid_s(UUID_B).unwrap()
)),
ModifyList::new_list(vec![Modify::Removed(
Attribute::Member,
PartialValue::new_refer_s(UUID_C).unwrap()
)]),
None,
|_| {},
|qs: &mut QueryServerWriteTransaction| {
assert_not_memberof!(qs, UUID_A, UUID_A);
assert_not_memberof!(qs, UUID_A, UUID_B);
assert_not_memberof!(qs, UUID_A, UUID_C);
assert_memberof!(qs, UUID_B, UUID_A);
assert_not_memberof!(qs, UUID_B, UUID_B);
assert_not_memberof!(qs, UUID_B, UUID_C);
assert_not_memberof!(qs, UUID_C, UUID_A);
assert_not_memberof!(qs, UUID_C, UUID_B);
assert_not_memberof!(qs, UUID_C, UUID_C);
assert_not_dirmemberof!(qs, UUID_A, UUID_A);
assert_not_dirmemberof!(qs, UUID_A, UUID_B);
assert_not_dirmemberof!(qs, UUID_A, UUID_C);
assert_dirmemberof!(qs, UUID_B, UUID_A);
assert_not_dirmemberof!(qs, UUID_B, UUID_B);
assert_not_dirmemberof!(qs, UUID_B, UUID_C);
assert_not_dirmemberof!(qs, UUID_C, UUID_A);
assert_not_dirmemberof!(qs, UUID_C, UUID_B);
assert_not_dirmemberof!(qs, UUID_C, UUID_C);
}
);
}
#[test]
fn test_modify_mo_del_cycle() {
let mut ea = EA.clone();
let mut eb = EB.clone();
let mut ec = EC.clone();
ea.add_ava(Attribute::Member, Value::new_refer_s(UUID_B).unwrap());
ea.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_C).unwrap());
ea.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_B).unwrap());
ea.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
eb.add_ava(Attribute::Member, Value::new_refer_s(UUID_C).unwrap());
eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_C).unwrap());
eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_B).unwrap());
eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
ec.add_ava(Attribute::Member, Value::new_refer_s(UUID_A).unwrap());
ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_C).unwrap());
ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_B).unwrap());
ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
let preload = vec![ea, eb, ec];
run_modify_test!(
Ok(()),
preload,
filter!(f_eq(
Attribute::Uuid,
PartialValue::new_uuid_s(UUID_C).unwrap()
)),
ModifyList::new_list(vec![Modify::Removed(
Attribute::Member,
PartialValue::new_refer_s(UUID_A).unwrap()
)]),
None,
|_| {},
|qs: &mut QueryServerWriteTransaction| {
assert_not_memberof!(qs, UUID_A, UUID_A);
assert_not_memberof!(qs, UUID_A, UUID_B);
assert_not_memberof!(qs, UUID_A, UUID_C);
assert_memberof!(qs, UUID_B, UUID_A);
assert_not_memberof!(qs, UUID_B, UUID_B);
assert_not_memberof!(qs, UUID_B, UUID_C);
assert_memberof!(qs, UUID_C, UUID_A);
assert_memberof!(qs, UUID_C, UUID_B);
assert_not_memberof!(qs, UUID_C, UUID_C);
assert_not_dirmemberof!(qs, UUID_A, UUID_A);
assert_not_dirmemberof!(qs, UUID_A, UUID_B);
assert_not_dirmemberof!(qs, UUID_A, UUID_C);
assert_dirmemberof!(qs, UUID_B, UUID_A);
assert_not_dirmemberof!(qs, UUID_B, UUID_B);
assert_not_dirmemberof!(qs, UUID_B, UUID_C);
assert_not_dirmemberof!(qs, UUID_C, UUID_A);
assert_dirmemberof!(qs, UUID_C, UUID_B);
assert_not_dirmemberof!(qs, UUID_C, UUID_C);
}
);
}
#[test]
fn test_modify_mo_del_multi_cycle() {
let mut ea = EA.clone();
let mut eb = EB.clone();
let mut ec = EC.clone();
let mut ed = ED.clone();
ea.add_ava(Attribute::Member, Value::new_refer_s(UUID_B).unwrap());
ea.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_D).unwrap());
ea.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_C).unwrap());
ea.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_B).unwrap());
ea.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
eb.add_ava(Attribute::Member, Value::new_refer_s(UUID_C).unwrap());
eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_D).unwrap());
eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_C).unwrap());
eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_B).unwrap());
eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
ec.add_ava(Attribute::Member, Value::new_refer_s(UUID_A).unwrap());
ec.add_ava(Attribute::Member, Value::new_refer_s(UUID_D).unwrap());
ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_D).unwrap());
ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_C).unwrap());
ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_B).unwrap());
ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
ed.add_ava(Attribute::Member, Value::new_refer_s(UUID_A).unwrap());
ed.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_D).unwrap());
ed.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_C).unwrap());
ed.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_B).unwrap());
ed.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
let preload = vec![ea, eb, ec, ed];
run_modify_test!(
Ok(()),
preload,
filter!(f_eq(
Attribute::Uuid,
PartialValue::new_uuid_s(UUID_C).unwrap()
)),
ModifyList::new_list(vec![
Modify::Removed(
Attribute::Member,
PartialValue::new_refer_s(UUID_A).unwrap()
),
Modify::Removed(
Attribute::Member,
PartialValue::new_refer_s(UUID_D).unwrap()
),
]),
None,
|_| {},
|qs: &mut QueryServerWriteTransaction| {
assert_not_memberof!(qs, UUID_A, UUID_A);
assert_not_memberof!(qs, UUID_A, UUID_B);
assert_not_memberof!(qs, UUID_A, UUID_C);
assert_memberof!(qs, UUID_A, UUID_D);
assert_memberof!(qs, UUID_B, UUID_A);
assert_not_memberof!(qs, UUID_B, UUID_B);
assert_not_memberof!(qs, UUID_B, UUID_C);
assert_memberof!(qs, UUID_B, UUID_D);
assert_memberof!(qs, UUID_C, UUID_A);
assert_memberof!(qs, UUID_C, UUID_B);
assert_not_memberof!(qs, UUID_C, UUID_C);
assert_memberof!(qs, UUID_C, UUID_D);
assert_not_memberof!(qs, UUID_D, UUID_A);
assert_not_memberof!(qs, UUID_D, UUID_B);
assert_not_memberof!(qs, UUID_D, UUID_C);
assert_not_memberof!(qs, UUID_D, UUID_D);
assert_not_dirmemberof!(qs, UUID_A, UUID_A);
assert_not_dirmemberof!(qs, UUID_A, UUID_B);
assert_not_dirmemberof!(qs, UUID_A, UUID_C);
assert_dirmemberof!(qs, UUID_A, UUID_D);
assert_dirmemberof!(qs, UUID_B, UUID_A);
assert_not_dirmemberof!(qs, UUID_B, UUID_B);
assert_not_dirmemberof!(qs, UUID_B, UUID_C);
assert_not_dirmemberof!(qs, UUID_B, UUID_D);
assert_not_dirmemberof!(qs, UUID_C, UUID_A);
assert_dirmemberof!(qs, UUID_C, UUID_B);
assert_not_dirmemberof!(qs, UUID_C, UUID_C);
assert_not_dirmemberof!(qs, UUID_C, UUID_D);
assert_not_dirmemberof!(qs, UUID_D, UUID_A);
assert_not_dirmemberof!(qs, UUID_D, UUID_B);
assert_not_dirmemberof!(qs, UUID_D, UUID_C);
assert_not_dirmemberof!(qs, UUID_D, UUID_D);
}
);
}
#[test]
fn test_delete_mo_simple() {
let mut ea = EA.clone();
let mut eb = EB.clone();
ea.add_ava(Attribute::Member, Value::new_refer_s(UUID_B).unwrap());
eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
let preload = vec![ea, eb];
run_delete_test!(
Ok(()),
preload,
filter!(f_eq(
Attribute::Uuid,
PartialValue::new_uuid_s(UUID_A).unwrap()
)),
None,
|qs: &mut QueryServerWriteTransaction| {
assert_not_memberof!(qs, UUID_B, UUID_A);
assert_not_memberof!(qs, UUID_A, UUID_B);
assert_not_dirmemberof!(qs, UUID_B, UUID_A);
assert_not_dirmemberof!(qs, UUID_A, UUID_B);
}
);
}
#[test]
fn test_delete_mo_nested_head() {
let mut ea = EA.clone();
let mut eb = EB.clone();
let mut ec = EC.clone();
ea.add_ava(Attribute::Member, Value::new_refer_s(UUID_B).unwrap());
eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
eb.add_ava(Attribute::Member, Value::new_refer_s(UUID_C).unwrap());
ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_B).unwrap());
let preload = vec![ea, eb, ec];
run_delete_test!(
Ok(()),
preload,
filter!(f_eq(
Attribute::Uuid,
PartialValue::new_uuid_s(UUID_A).unwrap()
)),
None,
|qs: &mut QueryServerWriteTransaction| {
assert_not_memberof!(qs, UUID_B, UUID_A);
assert_not_memberof!(qs, UUID_B, UUID_B);
assert_not_memberof!(qs, UUID_B, UUID_C);
assert_not_memberof!(qs, UUID_C, UUID_A);
assert_memberof!(qs, UUID_C, UUID_B);
assert_not_memberof!(qs, UUID_C, UUID_C);
assert_not_dirmemberof!(qs, UUID_B, UUID_A);
assert_not_dirmemberof!(qs, UUID_B, UUID_B);
assert_not_dirmemberof!(qs, UUID_B, UUID_C);
assert_not_dirmemberof!(qs, UUID_C, UUID_A);
assert_dirmemberof!(qs, UUID_C, UUID_B);
assert_not_dirmemberof!(qs, UUID_C, UUID_C);
}
);
}
#[test]
fn test_delete_mo_nested_branch() {
let mut ea = EA.clone();
let mut eb = EB.clone();
let mut ec = EC.clone();
ea.add_ava(Attribute::Member, Value::new_refer_s(UUID_B).unwrap());
eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
eb.add_ava(Attribute::Member, Value::new_refer_s(UUID_C).unwrap());
ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_B).unwrap());
let preload = vec![ea, eb, ec];
run_delete_test!(
Ok(()),
preload,
filter!(f_eq(
Attribute::Uuid,
PartialValue::new_uuid_s(UUID_B).unwrap()
)),
None,
|qs: &mut QueryServerWriteTransaction| {
assert_not_memberof!(qs, UUID_A, UUID_A);
assert_not_memberof!(qs, UUID_A, UUID_B);
assert_not_memberof!(qs, UUID_A, UUID_C);
assert_not_memberof!(qs, UUID_C, UUID_A);
assert_not_memberof!(qs, UUID_C, UUID_B);
assert_not_memberof!(qs, UUID_C, UUID_C);
assert_not_dirmemberof!(qs, UUID_A, UUID_A);
assert_not_dirmemberof!(qs, UUID_A, UUID_B);
assert_not_dirmemberof!(qs, UUID_A, UUID_C);
assert_not_dirmemberof!(qs, UUID_C, UUID_A);
assert_not_dirmemberof!(qs, UUID_C, UUID_B);
assert_not_dirmemberof!(qs, UUID_C, UUID_C);
}
);
}
#[test]
fn test_delete_mo_cycle() {
let mut ea = EA.clone();
let mut eb = EB.clone();
let mut ec = EC.clone();
ea.add_ava(Attribute::Member, Value::new_refer_s(UUID_B).unwrap());
ea.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
ea.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_B).unwrap());
ea.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_C).unwrap());
eb.add_ava(Attribute::Member, Value::new_refer_s(UUID_C).unwrap());
eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_B).unwrap());
eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_C).unwrap());
ec.add_ava(Attribute::Member, Value::new_refer_s(UUID_A).unwrap());
ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_B).unwrap());
ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_C).unwrap());
let preload = vec![ea, eb, ec];
run_delete_test!(
Ok(()),
preload,
filter!(f_eq(
Attribute::Uuid,
PartialValue::new_uuid_s(UUID_A).unwrap()
)),
None,
|qs: &mut QueryServerWriteTransaction| {
assert_not_memberof!(qs, UUID_B, UUID_A);
assert_not_memberof!(qs, UUID_B, UUID_B);
assert_not_memberof!(qs, UUID_B, UUID_C);
assert_not_memberof!(qs, UUID_C, UUID_A);
assert_memberof!(qs, UUID_C, UUID_B);
assert_not_memberof!(qs, UUID_C, UUID_C);
assert_not_dirmemberof!(qs, UUID_B, UUID_A);
assert_not_dirmemberof!(qs, UUID_B, UUID_B);
assert_not_dirmemberof!(qs, UUID_B, UUID_C);
assert_not_dirmemberof!(qs, UUID_C, UUID_A);
assert_dirmemberof!(qs, UUID_C, UUID_B);
assert_not_dirmemberof!(qs, UUID_C, UUID_C);
}
);
}
#[test]
fn test_delete_mo_multi_cycle() {
let mut ea = EA.clone();
let mut eb = EB.clone();
let mut ec = EC.clone();
let mut ed = ED.clone();
ea.add_ava(Attribute::Member, Value::new_refer_s(UUID_B).unwrap());
ea.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
ea.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_B).unwrap());
ea.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_C).unwrap());
ea.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_D).unwrap());
eb.add_ava(Attribute::Member, Value::new_refer_s(UUID_C).unwrap());
eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_B).unwrap());
eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_C).unwrap());
eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_D).unwrap());
ec.add_ava(Attribute::Member, Value::new_refer_s(UUID_A).unwrap());
ec.add_ava(Attribute::Member, Value::new_refer_s(UUID_D).unwrap());
ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_B).unwrap());
ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_C).unwrap());
ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_D).unwrap());
ed.add_ava(Attribute::Member, Value::new_refer_s(UUID_A).unwrap());
ed.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
ed.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_B).unwrap());
ed.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_C).unwrap());
ed.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_D).unwrap());
let preload = vec![ea, eb, ec, ed];
run_delete_test!(
Ok(()),
preload,
filter!(f_eq(
Attribute::Uuid,
PartialValue::new_uuid_s(UUID_B).unwrap()
)),
None,
|qs: &mut QueryServerWriteTransaction| {
assert_not_memberof!(qs, UUID_A, UUID_B);
assert_not_memberof!(qs, UUID_A, UUID_A);
assert_memberof!(qs, UUID_A, UUID_C);
assert_memberof!(qs, UUID_A, UUID_D);
assert_not_memberof!(qs, UUID_C, UUID_A);
assert_not_memberof!(qs, UUID_C, UUID_B);
assert_not_memberof!(qs, UUID_C, UUID_C);
assert_not_memberof!(qs, UUID_C, UUID_D);
assert_not_memberof!(qs, UUID_D, UUID_A);
assert_not_memberof!(qs, UUID_D, UUID_B);
assert_memberof!(qs, UUID_D, UUID_C);
assert_not_memberof!(qs, UUID_D, UUID_D);
assert_not_dirmemberof!(qs, UUID_A, UUID_A);
assert_not_dirmemberof!(qs, UUID_A, UUID_B);
assert_dirmemberof!(qs, UUID_A, UUID_C);
assert_dirmemberof!(qs, UUID_A, UUID_D);
assert_not_dirmemberof!(qs, UUID_C, UUID_A);
assert_not_dirmemberof!(qs, UUID_C, UUID_B);
assert_not_dirmemberof!(qs, UUID_C, UUID_C);
assert_not_dirmemberof!(qs, UUID_C, UUID_D);
assert_not_dirmemberof!(qs, UUID_D, UUID_A);
assert_not_dirmemberof!(qs, UUID_C, UUID_B);
assert_dirmemberof!(qs, UUID_D, UUID_C);
assert_not_dirmemberof!(qs, UUID_D, UUID_D);
}
);
}
}