1use std::collections::{BTreeMap, BTreeSet};
14use std::sync::Arc;
15
16use crate::entry::{Entry, EntryCommitted, EntrySealed};
17use crate::event::{CreateEvent, DeleteEvent, ModifyEvent};
18use crate::plugins::Plugin;
19use crate::prelude::*;
20use crate::value::PartialValue;
21
22pub struct MemberOf;
23
24fn do_group_memberof(
25    qs: &mut QueryServerWriteTransaction,
26    uuid: Uuid,
27    tgte: &mut EntryInvalidCommitted,
28) -> Result<(), OperationError> {
29    let groups = qs
31        .internal_search(filter!(f_and!([
32            f_eq(Attribute::Class, EntryClass::Group.into()),
33            f_or!([
34                f_eq(Attribute::Member, PartialValue::Refer(uuid)),
35                f_eq(Attribute::DynMember, PartialValue::Refer(uuid))
36            ])
37        ])))
38        .map_err(|e| {
39            admin_error!("internal search failure -> {:?}", e);
40            e
41        })?;
42
43    tgte.add_ava_if_not_exist(Attribute::Class, EntryClass::MemberOf.into());
45    tgte.purge_ava(Attribute::MemberOf);
48    tgte.purge_ava(Attribute::DirectMemberOf);
49
50    let dmo = ValueSetRefer::from_iter(groups.iter().map(|g| g.get_uuid()));
52
53    let mut mo = ValueSetRefer::from_iter(
54        groups
55            .iter()
56            .filter_map(|g| {
57                g.get_ava_set(Attribute::MemberOf)
58                    .and_then(|s| s.as_refer_set())
59                    .map(|s| s.iter())
60            })
61            .flatten()
62            .copied(),
63    );
64
65    if let Some(dmo) = dmo {
67        tgte.set_ava_set(&Attribute::DirectMemberOf, dmo.clone());
69
70        if let Some(mo) = &mut mo {
71            let dmo = dmo as ValueSet;
72            mo.merge(&dmo)?;
73        } else {
74            mo = Some(dmo);
77        };
78    };
79
80    if let Some(mo) = mo {
81        tgte.set_ava_set(&Attribute::MemberOf, mo);
82    }
83
84    trace!(
85        "Updating {:?} to be dir mo {:?}",
86        uuid,
87        tgte.get_ava_set(Attribute::DirectMemberOf)
88    );
89    trace!(
90        "Updating {:?} to be mo {:?}",
91        uuid,
92        tgte.get_ava_set(Attribute::MemberOf)
93    );
94    Ok(())
95}
96
97fn do_leaf_memberof(
98    qs: &mut QueryServerWriteTransaction,
99    all_affected_uuids: BTreeSet<Uuid>,
100) -> Result<(), OperationError> {
101    trace!("---");
102
103    let all_affected_filter: Vec<_> = all_affected_uuids
106        .into_iter()
107        .map(|u| f_eq(Attribute::Uuid, PartialValue::Uuid(u)))
108        .collect();
109
110    if all_affected_filter.is_empty() {
111        trace!("all affected filter is empty, return");
112        return Ok(());
113    }
114
115    let leaf_entries = qs.internal_search_writeable(&filter!(f_and!([
117        f_andnot(f_eq(Attribute::Class, EntryClass::Group.into())),
118        FC::Or(all_affected_filter)
119    ])))?;
120
121    if leaf_entries.is_empty() {
122        trace!("leaf entries empty, return");
123        return Ok(());
124    }
125
126    let mut leaf_entries: BTreeMap<_, _> = leaf_entries
127        .into_iter()
128        .map(|entry_tuple| (entry_tuple.0.get_uuid(), entry_tuple))
129        .collect();
130
131    let mut changes = Vec::with_capacity(leaf_entries.len());
132
133    let mut groups_or = Vec::with_capacity(leaf_entries.len() * 2);
138
139    for uuid in leaf_entries.keys().copied() {
140        groups_or.push(f_eq(Attribute::Member, PartialValue::Refer(uuid)));
141        groups_or.push(f_eq(Attribute::DynMember, PartialValue::Refer(uuid)));
142    }
143
144    let all_groups = qs
145        .internal_search(filter!(f_and!([
146            f_eq(Attribute::Class, EntryClass::Group.into()),
147            FC::Or(groups_or)
148        ])))
149        .map_err(|err| {
150            error!(?err, "internal search failure");
151            err
152        })?;
153
154    for (_pre, tgte) in leaf_entries.values_mut() {
172        tgte.add_ava_if_not_exist(Attribute::Class, EntryClass::MemberOf.into());
174        tgte.purge_ava(Attribute::MemberOf);
177        tgte.purge_ava(Attribute::DirectMemberOf);
178    }
179
180    for group in all_groups {
183        trace!(group_id = %group.get_display_id());
184        let group_uuid = group.get_uuid();
186
187        let memberof_ref = group.get_ava_refer(Attribute::MemberOf);
188
189        let member_ref = group.get_ava_refer(Attribute::Member);
190        let dynmember_ref = group.get_ava_refer(Attribute::DynMember);
191
192        let dir_members = member_ref
193            .iter()
194            .flat_map(|set| set.iter())
195            .chain(dynmember_ref.iter().flat_map(|set| set.iter()))
196            .copied();
197
198        for dir_member in dir_members {
201            if let Some((_pre, tgte)) = leaf_entries.get_mut(&dir_member) {
202                trace!(?dir_member, entry_id = ?tgte.get_display_id());
203                if let Some(dmo_set) = tgte.get_ava_refer_mut(Attribute::DirectMemberOf) {
205                    dmo_set.insert(group_uuid);
206                } else {
207                    let dmo = ValueSetRefer::new(group_uuid);
208                    tgte.set_ava_set(&Attribute::DirectMemberOf, dmo);
209                }
210
211                if let Some(mo_set) = tgte.get_ava_refer_mut(Attribute::MemberOf) {
213                    mo_set.insert(group_uuid);
214                } else {
215                    let mo = ValueSetRefer::new(group_uuid);
216                    tgte.set_ava_set(&Attribute::MemberOf, mo);
217                }
218
219                if let Some(group_mo) = memberof_ref {
222                    if let Some(mo_set) = tgte.get_ava_refer_mut(Attribute::MemberOf) {
225                        mo_set.extend(group_mo.iter())
226                    }
227                }
228
229                if cfg!(debug_assertions) {
230                    if let Some(dmo) = group.get_ava_refer(Attribute::DirectMemberOf) {
231                        if let Some(mo) = group.get_ava_refer(Attribute::MemberOf) {
232                            debug_assert!(mo.is_superset(dmo))
233                        }
234                    }
235                }
236            }
237            }
241        }
243
244    leaf_entries
247        .into_iter()
248        .try_for_each(|(auuid, (pre, tgte))| {
249            if pre.get_ava_set(Attribute::MemberOf) != tgte.get_ava_set(Attribute::MemberOf)
251                || pre.get_ava_set(Attribute::DirectMemberOf)
252                    != tgte.get_ava_set(Attribute::DirectMemberOf)
253            {
254                trace!("=> processing affected uuid {:?}", auuid);
255
256                if cfg!(debug_assertions) {
257                    if let Some(dmo_set) = tgte.get_ava_refer(Attribute::DirectMemberOf) {
258                        trace!(?dmo_set);
259
260                        if let Some(mo_set) = tgte.get_ava_refer(Attribute::MemberOf) {
261                            trace!(?mo_set);
262                            debug_assert!(mo_set.is_superset(dmo_set));
263                        } else {
264                            unreachable!();
265                        }
266                    } else {
267                        trace!("NONE");
268                    };
269
270                    if let Some(pre_dmo_set) = pre.get_ava_refer(Attribute::DirectMemberOf) {
271                        trace!(?pre_dmo_set);
272
273                        if let Some(pre_mo_set) = pre.get_ava_refer(Attribute::MemberOf) {
274                            trace!(?pre_mo_set);
275                            debug_assert!(pre_mo_set.is_superset(pre_dmo_set));
276                        } else {
277                            unreachable!();
278                        }
279                    } else {
280                        trace!("NONE");
281                    };
282                };
283
284                changes.push((pre, tgte));
285            } else {
286                trace!("=> ignoring unmodified uuid {:?}", auuid);
287            }
288            Ok(())
289        })?;
290
291    qs.internal_apply_writable(changes)
293    }
295
296#[allow(clippy::cognitive_complexity)]
298fn apply_memberof(
299    qs: &mut QueryServerWriteTransaction,
300    mut affected_uuids: BTreeSet<Uuid>,
303) -> Result<(), OperationError> {
304    trace!(" => entering apply_memberof");
305
306    let mut all_affected_uuids: BTreeSet<_> = affected_uuids.iter().copied().collect();
313
314    while !affected_uuids.is_empty() {
316        trace!(?affected_uuids);
317
318        let filt = filter!(f_and!([
320            f_eq(Attribute::Class, EntryClass::Group.into()),
321            FC::Or(
322                affected_uuids
323                    .iter()
324                    .copied()
325                    .map(|u| f_eq(Attribute::Uuid, PartialValue::Uuid(u)))
326                    .collect()
327            )
328        ]));
329
330        affected_uuids.clear();
332
333        let work_set = qs.internal_search_writeable(&filt)?;
334        let mut changes = Vec::with_capacity(work_set.len());
335
336        for (pre, mut tgte) in work_set.into_iter() {
337            let guuid = pre.get_uuid();
338
339            trace!(
340                "=> processing group update -> {:?} {}",
341                guuid,
342                tgte.get_display_id()
343            );
344
345            do_group_memberof(qs, guuid, &mut tgte)?;
346
347            if pre.get_ava_set(Attribute::MemberOf) != tgte.get_ava_set(Attribute::MemberOf)
349                || pre.get_ava_set(Attribute::DirectMemberOf)
350                    != tgte.get_ava_set(Attribute::DirectMemberOf)
351            {
352                trace!(
356                    "{:?} {} changed, flagging members as groups to change. ",
357                    guuid,
358                    tgte.get_display_id()
359                );
360
361                let pre_member = pre.get_ava_refer(Attribute::Member);
365                let post_member = tgte.get_ava_refer(Attribute::Member);
366
367                match (pre_member, post_member) {
368                    (Some(pre_m), Some(post_m)) => {
369                        affected_uuids.extend(pre_m);
370                        affected_uuids.extend(post_m);
371                    }
372                    (Some(members), None) | (None, Some(members)) => {
373                        affected_uuids.extend(members);
375                    }
376                    (None, None) => {}
377                };
378
379                let pre_dynmember = pre.get_ava_refer(Attribute::DynMember);
380                let post_dynmember = tgte.get_ava_refer(Attribute::DynMember);
381
382                match (pre_dynmember, post_dynmember) {
383                    (Some(pre_m), Some(post_m)) => {
384                        affected_uuids.extend(pre_m);
385                        affected_uuids.extend(post_m);
386                    }
387                    (Some(members), None) | (None, Some(members)) => {
388                        affected_uuids.extend(members);
390                    }
391                    (None, None) => {}
392                };
393
394                changes.push((pre, tgte));
396            } else {
397                trace!("{:?} {} stable", guuid, tgte.get_display_id());
409            }
410        }
411
412        if !changes.is_empty() {
414            trace!("wrote stripe {}", changes.len());
415            qs.internal_apply_writable(changes).map_err(|err| {
416                error!(?err, "Failed to commit memberof group set");
417                err
418            })?;
419        }
420
421        all_affected_uuids.extend(affected_uuids.iter());
423
424        trace!("-------------------------------------");
426    }
427
428    do_leaf_memberof(qs, all_affected_uuids)
430}
431
432impl Plugin for MemberOf {
433    fn id() -> &'static str {
434        Attribute::MemberOf.as_ref()
435    }
436
437    #[instrument(level = "debug", name = "memberof_post_create", skip_all)]
438    fn post_create(
439        qs: &mut QueryServerWriteTransaction,
440        cand: &[Entry<EntrySealed, EntryCommitted>],
441        ce: &CreateEvent,
442    ) -> Result<(), OperationError> {
443        Self::post_create_inner(qs, cand, &ce.ident)
444    }
445
446    #[instrument(level = "debug", name = "memberof_post_repl_refresh", skip_all)]
447    fn post_repl_refresh(
448        qs: &mut QueryServerWriteTransaction,
449        cand: &[Entry<EntrySealed, EntryCommitted>],
450    ) -> Result<(), OperationError> {
451        let ident = Identity::from_internal();
452        Self::post_create_inner(qs, cand, &ident)
453    }
454
455    #[instrument(level = "debug", name = "memberof_post_repl_incremental", skip_all)]
456    fn post_repl_incremental(
457        qs: &mut QueryServerWriteTransaction,
458        pre_cand: &[Arc<EntrySealedCommitted>],
459        cand: &[EntrySealedCommitted],
460        conflict_uuids: &BTreeSet<Uuid>,
461    ) -> Result<(), OperationError> {
462        let force_dyngroup_cand_update = !conflict_uuids.is_empty();
469
470        let ident_internal = Identity::from_internal();
473        Self::post_modify_inner(
474            qs,
475            pre_cand,
476            cand,
477            &ident_internal,
478            force_dyngroup_cand_update,
479        )
480    }
481
482    #[instrument(level = "debug", name = "memberof_post_modify", skip_all)]
483    fn post_modify(
484        qs: &mut QueryServerWriteTransaction,
485        pre_cand: &[Arc<Entry<EntrySealed, EntryCommitted>>],
486        cand: &[Entry<EntrySealed, EntryCommitted>],
487        me: &ModifyEvent,
488    ) -> Result<(), OperationError> {
489        Self::post_modify_inner(qs, pre_cand, cand, &me.ident, false)
490    }
491
492    #[instrument(level = "debug", name = "memberof_post_batch_modify", skip_all)]
493    fn post_batch_modify(
494        qs: &mut QueryServerWriteTransaction,
495        pre_cand: &[Arc<Entry<EntrySealed, EntryCommitted>>],
496        cand: &[Entry<EntrySealed, EntryCommitted>],
497        me: &BatchModifyEvent,
498    ) -> Result<(), OperationError> {
499        Self::post_modify_inner(qs, pre_cand, cand, &me.ident, false)
500    }
501
502    #[instrument(level = "debug", name = "memberof_pre_delete", skip_all)]
503    fn pre_delete(
504        _qs: &mut QueryServerWriteTransaction,
505        cand: &mut Vec<EntryInvalidCommitted>,
506        _de: &DeleteEvent,
507    ) -> Result<(), OperationError> {
508        for entry in cand.iter_mut() {
512            if let Some(direct_mo_vs) = entry.pop_ava(Attribute::DirectMemberOf) {
513                entry.set_ava_set(&Attribute::RecycledDirectMemberOf, direct_mo_vs);
514            } else {
515                entry.purge_ava(Attribute::RecycledDirectMemberOf);
517            }
518            entry.purge_ava(Attribute::MemberOf);
519        }
520
521        Ok(())
522    }
523
524    #[instrument(level = "debug", name = "memberof_post_delete", skip_all)]
525    fn post_delete(
526        qs: &mut QueryServerWriteTransaction,
527        cand: &[Entry<EntrySealed, EntryCommitted>],
528        _de: &DeleteEvent,
529    ) -> Result<(), OperationError> {
530        let affected_uuids = cand
533            .iter()
534            .filter_map(|e| {
535                if e.attribute_equality(Attribute::Class, &EntryClass::Group.into()) {
537                    e.get_ava_as_refuuid(Attribute::Member)
538                } else {
539                    None
540                }
541            })
542            .flatten()
543            .chain(
544                cand.iter()
546                    .filter_map(|post| {
547                        if post.attribute_equality(Attribute::Class, &EntryClass::DynGroup.into()) {
548                            post.get_ava_as_refuuid(Attribute::DynMember)
549                        } else {
550                            None
551                        }
552                    })
553                    .flatten(),
554            )
555            .collect();
556
557        apply_memberof(qs, affected_uuids)
558    }
559
560    #[instrument(level = "debug", name = "memberof::verify", skip_all)]
561    fn verify(qs: &mut QueryServerReadTransaction) -> Vec<Result<(), ConsistencyError>> {
562        let mut r = Vec::with_capacity(0);
563
564        let filt_in = filter!(f_pres(Attribute::Class));
565
566        let all_cand = match qs
567            .internal_search(filt_in)
568            .map_err(|_| Err(ConsistencyError::QueryServerSearchFailure))
569        {
570            Ok(all_cand) => all_cand,
571            Err(e) => return vec![e],
572        };
573
574        let mut direct_membership_map: BTreeMap<Uuid, BTreeSet<Uuid>> = Default::default();
578
579        let pv_class: PartialValue = EntryClass::Group.into();
580
581        for entry in all_cand.iter() {
582            if !entry.attribute_equality(Attribute::Class, &pv_class) {
583                continue;
585            }
586
587            let group_uuid = entry.get_uuid();
588
589            let member_iter = entry
590                .get_ava_refer(Attribute::Member)
591                .into_iter()
592                .flat_map(|set| set.iter())
593                .chain(
594                    entry
595                        .get_ava_refer(Attribute::DynMember)
596                        .into_iter()
597                        .flat_map(|set| set.iter()),
598                );
599
600            for member_uuid in member_iter {
601                let member_groups = direct_membership_map.entry(*member_uuid).or_default();
602                member_groups.insert(group_uuid);
603            }
604        }
605
606        for e in all_cand {
608            let uuid = e.get_uuid();
609
610            let d_groups_set: Option<&BTreeSet<Uuid>> = direct_membership_map.get(&uuid);
611
612            trace!(
613                "DMO search groups {:?} -> {:?}",
614                e.get_display_id(),
615                d_groups_set
616            );
617
618            match (e.get_ava_set(Attribute::DirectMemberOf), d_groups_set) {
623                (Some(edmos), Some(b)) => {
624                    match edmos.as_refer_set() {
626                        Some(a) => {
627                            let diff: Vec<_> = a.symmetric_difference(b).collect();
628                            if !diff.is_empty() {
629                                error!(
630                                    "MemberOfInvalid: Entry {}, DMO has inconsistencies",
631                                    e.get_display_id(),
632                                );
633                                trace!(entry_direct_member_of = ?a);
634                                trace!(expected_direct_groups = ?b);
635                                trace!(?diff);
636
637                                r.push(Err(ConsistencyError::MemberOfInvalid(e.get_id())));
638                            }
639                        }
640                        _ => {
641                            error!("MemberOfInvalid: Entry {}, DMO has incorrect syntax - should be reference uuid set", e.get_display_id());
642                            r.push(Err(ConsistencyError::MemberOfInvalid(e.get_id())));
643                        }
644                    }
645                }
646                (None, None) => {
647                    }
649                (entry_direct_member_of, expected_direct_groups) => {
650                    error!(
651                        "MemberOfInvalid directmemberof set and DMO search set differ in presence: {}",
652                        e.get_display_id()
653                    );
654                    trace!(?entry_direct_member_of);
656                    trace!(?expected_direct_groups);
657                    r.push(Err(ConsistencyError::MemberOfInvalid(e.get_id())));
658                }
659            }
660        }
661
662        r
663    }
664}
665
666impl MemberOf {
667    fn post_create_inner(
668        qs: &mut QueryServerWriteTransaction,
669        cand: &[Entry<EntrySealed, EntryCommitted>],
670        ident: &Identity,
671    ) -> Result<(), OperationError> {
672        let dyngroup_change = super::dyngroup::DynGroup::post_create(qs, cand, ident)?;
673
674        let affected_uuids = cand
675            .iter()
676            .map(|e| e.get_uuid())
677            .chain(dyngroup_change)
678            .chain(
680                cand.iter()
681                    .filter_map(|e| {
682                        if e.attribute_equality(Attribute::Class, &EntryClass::Group.into()) {
684                            e.get_ava_as_refuuid(Attribute::Member)
685                        } else {
686                            None
687                        }
688                    })
689                    .flatten(),
690            )
691            .collect();
692
693        apply_memberof(qs, affected_uuids)
694    }
695
696    fn post_modify_inner(
697        qs: &mut QueryServerWriteTransaction,
698        pre_cand: &[Arc<EntrySealedCommitted>],
699        cand: &[EntrySealedCommitted],
700        ident: &Identity,
701        force_dyngroup_cand_update: bool,
702    ) -> Result<(), OperationError> {
703        let dyngroup_change = super::dyngroup::DynGroup::post_modify(
704            qs,
705            pre_cand,
706            cand,
707            ident,
708            force_dyngroup_cand_update,
709        )?;
710
711        let mut affected_uuids: BTreeSet<_> = cand
712            .iter()
713            .map(|post| post.get_uuid())
714            .chain(dyngroup_change)
715            .collect();
716
717        for (pre, post) in pre_cand.iter().zip(cand.iter()).filter(|(pre, post)| {
718            post.attribute_equality(Attribute::Class, &EntryClass::Group.into())
719                || pre.attribute_equality(Attribute::Class, &EntryClass::Group.into())
720        }) {
721            let pre_member = pre.get_ava_refer(Attribute::Member);
722            let post_member = post.get_ava_refer(Attribute::Member);
723
724            match (pre_member, post_member) {
725                (Some(pre_m), Some(post_m)) => {
726                    affected_uuids.extend(pre_m.symmetric_difference(post_m));
728                }
729                (Some(members), None) | (None, Some(members)) => {
730                    affected_uuids.extend(members);
732                }
733                (None, None) => {}
734            };
735
736            let pre_dynmember = pre.get_ava_refer(Attribute::DynMember);
737            let post_dynmember = post.get_ava_refer(Attribute::DynMember);
738
739            match (pre_dynmember, post_dynmember) {
740                (Some(pre_m), Some(post_m)) => {
741                    affected_uuids.extend(pre_m.symmetric_difference(post_m));
743                }
744                (Some(members), None) | (None, Some(members)) => {
745                    affected_uuids.extend(members);
747                }
748                (None, None) => {}
749            };
750        }
751
752        apply_memberof(qs, affected_uuids)
753    }
754}
755
756#[cfg(test)]
757mod tests {
758    use crate::prelude::*;
759
760    const UUID_A: &str = "aaaaaaaa-f82e-4484-a407-181aa03bda5c";
761    const UUID_B: &str = "bbbbbbbb-2438-4384-9891-48f4c8172e9b";
762    const UUID_C: &str = "cccccccc-9b01-423f-9ba6-51aa4bbd5dd2";
763    const UUID_D: &str = "dddddddd-2ab3-48e3-938d-1b4754cd2984";
764
765    lazy_static! {
766        static ref EA: EntryInitNew = entry_init!(
767            (Attribute::Class, EntryClass::Group.to_value()),
768            (Attribute::Class, EntryClass::MemberOf.to_value()),
769            (Attribute::Name, Value::new_iname("testgroup_a")),
770            (Attribute::Uuid, Value::Uuid(uuid::uuid!(UUID_A)))
771        );
772        static ref EB: EntryInitNew = entry_init!(
773            (Attribute::Class, EntryClass::Group.to_value()),
774            (Attribute::Class, EntryClass::MemberOf.to_value()),
775            (Attribute::Name, Value::new_iname("testgroup_b")),
776            (Attribute::Uuid, Value::Uuid(uuid::uuid!(UUID_B)))
777        );
778        static ref EC: EntryInitNew = entry_init!(
779            (Attribute::Class, EntryClass::Group.to_value()),
780            (Attribute::Class, EntryClass::MemberOf.to_value()),
781            (Attribute::Name, Value::new_iname("testgroup_c")),
782            (Attribute::Uuid, Value::Uuid(uuid::uuid!(UUID_C)))
783        );
784        static ref ED: EntryInitNew = entry_init!(
785            (Attribute::Class, EntryClass::Group.to_value()),
786            (Attribute::Class, EntryClass::MemberOf.to_value()),
787            (Attribute::Name, Value::new_iname("testgroup_d")),
788            (Attribute::Uuid, Value::Uuid(uuid::uuid!(UUID_D)))
789        );
790    }
791
792    macro_rules! assert_memberof_int {
793        (
794            $qs:expr,
795            $ea:expr,
796            $eb:expr,
797            $mo:expr,
798            $cand:expr
799        ) => {{
800            let filt = filter!(f_and!([
801                f_eq(Attribute::Uuid, PartialValue::new_uuid_s($ea).unwrap()),
802                f_eq($mo, PartialValue::new_refer_s($eb).unwrap())
803            ]));
804            let cands = $qs.internal_search(filt).expect("Internal search failure");
805            debug!("assert_mo_cands {:?}", cands);
806            assert_eq!(cands.len(), $cand);
807        }};
808    }
809
810    macro_rules! assert_memberof {
811        (
812            $qs:expr,
813            $ea:expr,
814            $eb:expr
815        ) => {{
816            assert_memberof_int!($qs, $ea, $eb, Attribute::MemberOf, 1);
817        }};
818    }
819
820    macro_rules! assert_dirmemberof {
821        (
822            $qs:expr,
823            $ea:expr,
824            $eb:expr
825        ) => {{
826            assert_memberof_int!($qs, $ea, $eb, Attribute::DirectMemberOf, 1);
827        }};
828    }
829
830    macro_rules! assert_not_memberof {
831        (
832            $qs:expr,
833            $ea:expr,
834            $eb:expr
835        ) => {{
836            assert_memberof_int!($qs, $ea, $eb, Attribute::MemberOf, 0);
837        }};
838    }
839
840    macro_rules! assert_not_dirmemberof {
841        (
842            $qs:expr,
843            $ea:expr,
844            $eb:expr
845        ) => {{
846            assert_memberof_int!($qs, $ea, $eb, Attribute::DirectMemberOf, 0);
847        }};
848    }
849
850    #[test]
851    fn test_create_mo_single() {
852        let mut ea = EA.clone();
854        let eb = EB.clone();
855
856        ea.add_ava(Attribute::Member, Value::new_refer_s(UUID_B).unwrap());
857
858        let preload = Vec::with_capacity(0);
859        let create = vec![ea, eb];
860        run_create_test!(
861            Ok(None),
862            preload,
863            create,
864            None,
865            |qs: &mut QueryServerWriteTransaction| {
866                assert_memberof!(qs, UUID_B, UUID_A);
869                assert_not_memberof!(qs, UUID_A, UUID_B);
870
871                assert_dirmemberof!(qs, UUID_B, UUID_A);
872                assert_not_dirmemberof!(qs, UUID_A, UUID_B);
873            }
874        );
875    }
876
877    #[test]
878    fn test_create_mo_nested() {
879        let mut ea = EA.clone();
881
882        let mut eb = EB.clone();
883
884        let ec = EC.clone();
885
886        ea.add_ava(Attribute::Member, Value::new_refer_s(UUID_B).unwrap());
887        eb.add_ava(Attribute::Member, Value::new_refer_s(UUID_C).unwrap());
888
889        let preload = Vec::with_capacity(0);
890        let create = vec![ea, eb, ec];
891        run_create_test!(
892            Ok(None),
893            preload,
894            create,
895            None,
896            |qs: &mut QueryServerWriteTransaction| {
897                assert_not_memberof!(qs, UUID_A, UUID_A);
900                assert_not_memberof!(qs, UUID_A, UUID_B);
901                assert_not_memberof!(qs, UUID_A, UUID_C);
902
903                assert_memberof!(qs, UUID_B, UUID_A);
904                assert_not_memberof!(qs, UUID_B, UUID_B);
905                assert_not_memberof!(qs, UUID_B, UUID_C);
906
907                assert_memberof!(qs, UUID_C, UUID_A);
909                assert_memberof!(qs, UUID_C, UUID_B);
910                assert_not_memberof!(qs, UUID_C, UUID_C);
911
912                assert_not_dirmemberof!(qs, UUID_A, UUID_A);
913                assert_not_dirmemberof!(qs, UUID_A, UUID_B);
914                assert_not_dirmemberof!(qs, UUID_A, UUID_C);
915
916                assert_dirmemberof!(qs, UUID_B, UUID_A);
917                assert_not_dirmemberof!(qs, UUID_B, UUID_B);
918                assert_not_dirmemberof!(qs, UUID_B, UUID_C);
919
920                assert_not_dirmemberof!(qs, UUID_C, UUID_A);
921                assert_dirmemberof!(qs, UUID_C, UUID_B);
922                assert_not_dirmemberof!(qs, UUID_C, UUID_C);
923            }
924        );
925    }
926
927    #[test]
928    fn test_create_mo_cycle() {
929        let mut ea = EA.clone();
932
933        let mut eb = EB.clone();
934
935        let mut ec = EC.clone();
936
937        ea.add_ava(Attribute::Member, Value::new_refer_s(UUID_B).unwrap());
938        eb.add_ava(Attribute::Member, Value::new_refer_s(UUID_C).unwrap());
939        ec.add_ava(Attribute::Member, Value::new_refer_s(UUID_A).unwrap());
940
941        let preload = Vec::with_capacity(0);
942        let create = vec![ea, eb, ec];
943        run_create_test!(
944            Ok(None),
945            preload,
946            create,
947            None,
948            |qs: &mut QueryServerWriteTransaction| {
949                assert_memberof!(qs, UUID_A, UUID_A);
952                assert_memberof!(qs, UUID_A, UUID_B);
953                assert_memberof!(qs, UUID_A, UUID_C);
954
955                assert_memberof!(qs, UUID_B, UUID_A);
956                assert_memberof!(qs, UUID_B, UUID_B);
957                assert_memberof!(qs, UUID_B, UUID_C);
958
959                assert_memberof!(qs, UUID_C, UUID_A);
960                assert_memberof!(qs, UUID_C, UUID_B);
961                assert_memberof!(qs, UUID_C, UUID_C);
962
963                assert_not_dirmemberof!(qs, UUID_A, UUID_A);
964                assert_not_dirmemberof!(qs, UUID_A, UUID_B);
965                assert_dirmemberof!(qs, UUID_A, UUID_C);
966
967                assert_dirmemberof!(qs, UUID_B, UUID_A);
968                assert_not_dirmemberof!(qs, UUID_B, UUID_B);
969                assert_not_dirmemberof!(qs, UUID_B, UUID_C);
970
971                assert_not_dirmemberof!(qs, UUID_C, UUID_A);
972                assert_dirmemberof!(qs, UUID_C, UUID_B);
973                assert_not_dirmemberof!(qs, UUID_C, UUID_C);
974            }
975        );
976    }
977
978    #[test]
979    fn test_create_mo_multi_cycle() {
980        let mut ea = EA.clone();
984
985        let mut eb = EB.clone();
986
987        let mut ec = EC.clone();
988
989        let mut ed = ED.clone();
990
991        ea.add_ava(Attribute::Member, Value::new_refer_s(UUID_B).unwrap());
992        eb.add_ava(Attribute::Member, Value::new_refer_s(UUID_C).unwrap());
993
994        ec.add_ava(Attribute::Member, Value::new_refer_s(UUID_A).unwrap());
995        ec.add_ava(Attribute::Member, Value::new_refer_s(UUID_D).unwrap());
996
997        ed.add_ava(Attribute::Member, Value::new_refer_s(UUID_A).unwrap());
998
999        let preload = Vec::with_capacity(0);
1000        let create = vec![ea, eb, ec, ed];
1001        run_create_test!(
1002            Ok(None),
1003            preload,
1004            create,
1005            None,
1006            |qs: &mut QueryServerWriteTransaction| {
1007                assert_memberof!(qs, UUID_A, UUID_A);
1010                assert_memberof!(qs, UUID_A, UUID_B);
1011                assert_memberof!(qs, UUID_A, UUID_C);
1012                assert_memberof!(qs, UUID_A, UUID_D);
1013
1014                assert_memberof!(qs, UUID_B, UUID_A);
1015                assert_memberof!(qs, UUID_B, UUID_B);
1016                assert_memberof!(qs, UUID_B, UUID_C);
1017                assert_memberof!(qs, UUID_B, UUID_D);
1018
1019                assert_memberof!(qs, UUID_C, UUID_A);
1020                assert_memberof!(qs, UUID_C, UUID_B);
1021                assert_memberof!(qs, UUID_C, UUID_C);
1022                assert_memberof!(qs, UUID_C, UUID_D);
1023
1024                assert_memberof!(qs, UUID_D, UUID_A);
1025                assert_memberof!(qs, UUID_D, UUID_B);
1026                assert_memberof!(qs, UUID_D, UUID_C);
1027                assert_memberof!(qs, UUID_D, UUID_D);
1028
1029                assert_not_dirmemberof!(qs, UUID_A, UUID_A);
1030                assert_not_dirmemberof!(qs, UUID_A, UUID_B);
1031                assert_dirmemberof!(qs, UUID_A, UUID_C);
1032                assert_dirmemberof!(qs, UUID_A, UUID_D);
1033
1034                assert_dirmemberof!(qs, UUID_B, UUID_A);
1035                assert_not_dirmemberof!(qs, UUID_B, UUID_B);
1036                assert_not_dirmemberof!(qs, UUID_B, UUID_C);
1037                assert_not_dirmemberof!(qs, UUID_B, UUID_D);
1038
1039                assert_not_dirmemberof!(qs, UUID_C, UUID_A);
1040                assert_dirmemberof!(qs, UUID_C, UUID_B);
1041                assert_not_dirmemberof!(qs, UUID_C, UUID_C);
1042                assert_not_dirmemberof!(qs, UUID_C, UUID_D);
1043
1044                assert_not_dirmemberof!(qs, UUID_D, UUID_A);
1045                assert_not_dirmemberof!(qs, UUID_D, UUID_B);
1046                assert_dirmemberof!(qs, UUID_D, UUID_C);
1047                assert_not_dirmemberof!(qs, UUID_D, UUID_D);
1048            }
1049        );
1050    }
1051
1052    #[test]
1053    fn test_modify_mo_add_simple() {
1054        let ea = EA.clone();
1058        let eb = EB.clone();
1059
1060        let preload = vec![ea, eb];
1061        run_modify_test!(
1062            Ok(()),
1063            preload,
1064            filter!(f_eq(
1065                Attribute::Uuid,
1066                PartialValue::new_uuid_s(UUID_A).unwrap()
1067            )),
1068            ModifyList::new_list(vec![Modify::Present(
1069                Attribute::Member,
1070                Value::new_refer_s(UUID_B).unwrap()
1071            )]),
1072            None,
1073            |_| {},
1074            |qs: &mut QueryServerWriteTransaction| {
1075                assert_memberof!(qs, UUID_B, UUID_A);
1078                assert_not_memberof!(qs, UUID_A, UUID_B);
1079
1080                assert_dirmemberof!(qs, UUID_B, UUID_A);
1081                assert_not_dirmemberof!(qs, UUID_A, UUID_B);
1082            }
1083        );
1084    }
1085
1086    #[test]
1087    fn test_modify_mo_add_nested_1() {
1088        let ea = EA.clone();
1092        let mut eb = EB.clone();
1093        let ec = EC.clone();
1094
1095        eb.add_ava(Attribute::Member, Value::new_refer_s(UUID_C).unwrap());
1096
1097        let preload = vec![ea, eb, ec];
1098        run_modify_test!(
1099            Ok(()),
1100            preload,
1101            filter!(f_eq(
1102                Attribute::Uuid,
1103                PartialValue::new_uuid_s(UUID_A).unwrap()
1104            )),
1105            ModifyList::new_list(vec![Modify::Present(
1106                Attribute::Member,
1107                Value::new_refer_s(UUID_B).unwrap()
1108            )]),
1109            None,
1110            |_| {},
1111            |qs: &mut QueryServerWriteTransaction| {
1112                assert_not_memberof!(qs, UUID_A, UUID_A);
1115                assert_not_memberof!(qs, UUID_A, UUID_B);
1116                assert_not_memberof!(qs, UUID_A, UUID_C);
1117
1118                assert_memberof!(qs, UUID_B, UUID_A);
1119                assert_not_memberof!(qs, UUID_B, UUID_B);
1120                assert_not_memberof!(qs, UUID_B, UUID_C);
1121
1122                assert_memberof!(qs, UUID_C, UUID_A);
1123                assert_memberof!(qs, UUID_C, UUID_B);
1124                assert_not_memberof!(qs, UUID_C, UUID_C);
1125
1126                assert_not_dirmemberof!(qs, UUID_A, UUID_A);
1127                assert_not_dirmemberof!(qs, UUID_A, UUID_B);
1128                assert_not_dirmemberof!(qs, UUID_A, UUID_C);
1129
1130                assert_dirmemberof!(qs, UUID_B, UUID_A);
1131                assert_not_dirmemberof!(qs, UUID_B, UUID_B);
1132                assert_not_dirmemberof!(qs, UUID_B, UUID_C);
1133
1134                assert_not_dirmemberof!(qs, UUID_C, UUID_A);
1135                assert_dirmemberof!(qs, UUID_C, UUID_B);
1136                assert_not_dirmemberof!(qs, UUID_C, UUID_C);
1137            }
1138        );
1139    }
1140
1141    #[test]
1142    fn test_modify_mo_add_nested_2() {
1143        let mut ea = EA.clone();
1147        let eb = EB.clone();
1148        let ec = EC.clone();
1149
1150        ea.add_ava(Attribute::Member, Value::new_refer_s(UUID_B).unwrap());
1151
1152        let preload = vec![ea, eb, ec];
1153        run_modify_test!(
1154            Ok(()),
1155            preload,
1156            filter!(f_eq(
1157                Attribute::Uuid,
1158                PartialValue::new_uuid_s(UUID_B).unwrap()
1159            )),
1160            ModifyList::new_list(vec![Modify::Present(
1161                Attribute::Member,
1162                Value::new_refer_s(UUID_C).unwrap()
1163            )]),
1164            None,
1165            |_| {},
1166            |qs: &mut QueryServerWriteTransaction| {
1167                assert_not_memberof!(qs, UUID_A, UUID_A);
1170                assert_not_memberof!(qs, UUID_A, UUID_B);
1171                assert_not_memberof!(qs, UUID_A, UUID_C);
1172
1173                assert_memberof!(qs, UUID_B, UUID_A);
1174                assert_not_memberof!(qs, UUID_B, UUID_B);
1175                assert_not_memberof!(qs, UUID_B, UUID_C);
1176
1177                assert_memberof!(qs, UUID_C, UUID_A);
1178                assert_memberof!(qs, UUID_C, UUID_B);
1179                assert_not_memberof!(qs, UUID_C, UUID_C);
1180
1181                assert_not_dirmemberof!(qs, UUID_A, UUID_A);
1182                assert_not_dirmemberof!(qs, UUID_A, UUID_B);
1183                assert_not_dirmemberof!(qs, UUID_A, UUID_C);
1184
1185                assert_dirmemberof!(qs, UUID_B, UUID_A);
1186                assert_not_dirmemberof!(qs, UUID_B, UUID_B);
1187                assert_not_dirmemberof!(qs, UUID_B, UUID_C);
1188
1189                assert_not_dirmemberof!(qs, UUID_C, UUID_A);
1190                assert_dirmemberof!(qs, UUID_C, UUID_B);
1191                assert_not_dirmemberof!(qs, UUID_C, UUID_C);
1192            }
1193        );
1194    }
1195
1196    #[test]
1197    fn test_modify_mo_add_cycle() {
1198        let mut ea = EA.clone();
1204        let mut eb = EB.clone();
1205        let ec = EC.clone();
1206
1207        ea.add_ava(Attribute::Member, Value::new_refer_s(UUID_B).unwrap());
1208        eb.add_ava(Attribute::Member, Value::new_refer_s(UUID_C).unwrap());
1209
1210        let preload = vec![ea, eb, ec];
1211        run_modify_test!(
1212            Ok(()),
1213            preload,
1214            filter!(f_eq(
1215                Attribute::Uuid,
1216                PartialValue::new_uuid_s(UUID_C).unwrap()
1217            )),
1218            ModifyList::new_list(vec![Modify::Present(
1219                Attribute::Member,
1220                Value::new_refer_s(UUID_A).unwrap()
1221            )]),
1222            None,
1223            |_| {},
1224            |qs: &mut QueryServerWriteTransaction| {
1225                assert_memberof!(qs, UUID_A, UUID_A);
1228                assert_memberof!(qs, UUID_A, UUID_B);
1229                assert_memberof!(qs, UUID_A, UUID_C);
1230
1231                assert_memberof!(qs, UUID_B, UUID_A);
1232                assert_memberof!(qs, UUID_B, UUID_B);
1233                assert_memberof!(qs, UUID_B, UUID_C);
1234
1235                assert_memberof!(qs, UUID_C, UUID_A);
1236                assert_memberof!(qs, UUID_C, UUID_B);
1237                assert_memberof!(qs, UUID_C, UUID_C);
1238
1239                assert_not_dirmemberof!(qs, UUID_A, UUID_A);
1240                assert_not_dirmemberof!(qs, UUID_A, UUID_B);
1241                assert_dirmemberof!(qs, UUID_A, UUID_C);
1242
1243                assert_dirmemberof!(qs, UUID_B, UUID_A);
1244                assert_not_dirmemberof!(qs, UUID_B, UUID_B);
1245                assert_not_dirmemberof!(qs, UUID_B, UUID_C);
1246
1247                assert_not_dirmemberof!(qs, UUID_C, UUID_A);
1248                assert_dirmemberof!(qs, UUID_C, UUID_B);
1249                assert_not_dirmemberof!(qs, UUID_C, UUID_C);
1250            }
1251        );
1252    }
1253
1254    #[test]
1255    fn test_modify_mo_add_multi_cycle() {
1256        let mut ea = EA.clone();
1266        let mut eb = EB.clone();
1267        let mut ec = EC.clone();
1268        let ed = ED.clone();
1269
1270        ea.add_ava(Attribute::Member, Value::new_refer_s(UUID_B).unwrap());
1271        eb.add_ava(Attribute::Member, Value::new_refer_s(UUID_C).unwrap());
1272        ec.add_ava(Attribute::Member, Value::new_refer_s(UUID_D).unwrap());
1273
1274        let preload = vec![ea, eb, ec, ed];
1275        run_modify_test!(
1276            Ok(()),
1277            preload,
1278            filter!(f_or!([
1279                f_eq(Attribute::Uuid, PartialValue::new_uuid_s(UUID_C).unwrap()),
1280                f_eq(Attribute::Uuid, PartialValue::new_uuid_s(UUID_D).unwrap()),
1281            ])),
1282            ModifyList::new_list(vec![Modify::Present(
1283                Attribute::Member,
1284                Value::new_refer_s(UUID_A).unwrap()
1285            )]),
1286            None,
1287            |_| {},
1288            |qs: &mut QueryServerWriteTransaction| {
1289                assert_memberof!(qs, UUID_A, UUID_A);
1292                assert_memberof!(qs, UUID_A, UUID_B);
1293                assert_memberof!(qs, UUID_A, UUID_C);
1294                assert_memberof!(qs, UUID_A, UUID_D);
1295
1296                assert_memberof!(qs, UUID_B, UUID_A);
1297                assert_memberof!(qs, UUID_B, UUID_B);
1298                assert_memberof!(qs, UUID_B, UUID_C);
1299                assert_memberof!(qs, UUID_B, UUID_D);
1300
1301                assert_memberof!(qs, UUID_C, UUID_A);
1302                assert_memberof!(qs, UUID_C, UUID_B);
1303                assert_memberof!(qs, UUID_C, UUID_C);
1304                assert_memberof!(qs, UUID_C, UUID_D);
1305
1306                assert_memberof!(qs, UUID_D, UUID_A);
1307                assert_memberof!(qs, UUID_D, UUID_B);
1308                assert_memberof!(qs, UUID_D, UUID_C);
1309                assert_memberof!(qs, UUID_D, UUID_D);
1310
1311                assert_not_dirmemberof!(qs, UUID_A, UUID_A);
1312                assert_not_dirmemberof!(qs, UUID_A, UUID_B);
1313                assert_dirmemberof!(qs, UUID_A, UUID_C);
1314                assert_dirmemberof!(qs, UUID_A, UUID_D);
1315
1316                assert_dirmemberof!(qs, UUID_B, UUID_A);
1317                assert_not_dirmemberof!(qs, UUID_B, UUID_B);
1318                assert_not_dirmemberof!(qs, UUID_B, UUID_C);
1319                assert_not_dirmemberof!(qs, UUID_B, UUID_D);
1320
1321                assert_not_dirmemberof!(qs, UUID_C, UUID_A);
1322                assert_dirmemberof!(qs, UUID_C, UUID_B);
1323                assert_not_dirmemberof!(qs, UUID_C, UUID_C);
1324                assert_not_dirmemberof!(qs, UUID_C, UUID_D);
1325
1326                assert_not_dirmemberof!(qs, UUID_D, UUID_A);
1327                assert_not_dirmemberof!(qs, UUID_D, UUID_B);
1328                assert_dirmemberof!(qs, UUID_D, UUID_C);
1329                assert_not_dirmemberof!(qs, UUID_D, UUID_D);
1330            }
1331        );
1332    }
1333
1334    #[test]
1335    fn test_modify_mo_del_simple() {
1336        let mut ea = EA.clone();
1340        let mut eb = EB.clone();
1341
1342        ea.add_ava(Attribute::Member, Value::new_refer_s(UUID_B).unwrap());
1343        eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
1344
1345        let preload = vec![ea, eb];
1346        run_modify_test!(
1347            Ok(()),
1348            preload,
1349            filter!(f_eq(
1350                Attribute::Uuid,
1351                PartialValue::new_uuid_s(UUID_A).unwrap()
1352            )),
1353            ModifyList::new_list(vec![Modify::Removed(
1354                Attribute::Member,
1355                PartialValue::new_refer_s(UUID_B).unwrap()
1356            )]),
1357            None,
1358            |_| {},
1359            |qs: &mut QueryServerWriteTransaction| {
1360                assert_not_memberof!(qs, UUID_B, UUID_A);
1363                assert_not_memberof!(qs, UUID_A, UUID_B);
1364
1365                assert_not_dirmemberof!(qs, UUID_B, UUID_A);
1366                assert_not_dirmemberof!(qs, UUID_A, UUID_B);
1367            }
1368        );
1369    }
1370
1371    #[test]
1372    fn test_modify_mo_del_nested_1() {
1373        let mut ea = EA.clone();
1377        let mut eb = EB.clone();
1378        let mut ec = EC.clone();
1379
1380        ea.add_ava(Attribute::Member, Value::new_refer_s(UUID_B).unwrap());
1381        eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
1382        eb.add_ava(Attribute::Member, Value::new_refer_s(UUID_C).unwrap());
1383        ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_B).unwrap());
1384
1385        let preload = vec![ea, eb, ec];
1386        run_modify_test!(
1387            Ok(()),
1388            preload,
1389            filter!(f_eq(
1390                Attribute::Uuid,
1391                PartialValue::new_uuid_s(UUID_A).unwrap()
1392            )),
1393            ModifyList::new_list(vec![Modify::Removed(
1394                Attribute::Member,
1395                PartialValue::new_refer_s(UUID_B).unwrap()
1396            )]),
1397            None,
1398            |_| {},
1399            |qs: &mut QueryServerWriteTransaction| {
1400                assert_not_memberof!(qs, UUID_A, UUID_A);
1403                assert_not_memberof!(qs, UUID_A, UUID_B);
1404                assert_not_memberof!(qs, UUID_A, UUID_C);
1405
1406                assert_not_memberof!(qs, UUID_B, UUID_A);
1407                assert_not_memberof!(qs, UUID_B, UUID_B);
1408                assert_not_memberof!(qs, UUID_B, UUID_C);
1409
1410                assert_not_memberof!(qs, UUID_C, UUID_A);
1411                assert_memberof!(qs, UUID_C, UUID_B);
1412                assert_not_memberof!(qs, UUID_C, UUID_C);
1413
1414                assert_not_dirmemberof!(qs, UUID_A, UUID_A);
1415                assert_not_dirmemberof!(qs, UUID_A, UUID_B);
1416                assert_not_dirmemberof!(qs, UUID_A, UUID_C);
1417
1418                assert_not_dirmemberof!(qs, UUID_B, UUID_A);
1419                assert_not_dirmemberof!(qs, UUID_B, UUID_B);
1420                assert_not_dirmemberof!(qs, UUID_B, UUID_C);
1421
1422                assert_not_dirmemberof!(qs, UUID_C, UUID_A);
1423                assert_dirmemberof!(qs, UUID_C, UUID_B);
1424                assert_not_dirmemberof!(qs, UUID_C, UUID_C);
1425            }
1426        );
1427    }
1428
1429    #[test]
1430    fn test_modify_mo_del_nested_2() {
1431        let mut ea = EA.clone();
1435        let mut eb = EB.clone();
1436        let mut ec = EC.clone();
1437
1438        ea.add_ava(Attribute::Member, Value::new_refer_s(UUID_B).unwrap());
1439        eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
1440        eb.add_ava(Attribute::Member, Value::new_refer_s(UUID_C).unwrap());
1441        ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_B).unwrap());
1442        ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
1443
1444        let preload = vec![ea, eb, ec];
1445        run_modify_test!(
1446            Ok(()),
1447            preload,
1448            filter!(f_eq(
1449                Attribute::Uuid,
1450                PartialValue::new_uuid_s(UUID_B).unwrap()
1451            )),
1452            ModifyList::new_list(vec![Modify::Removed(
1453                Attribute::Member,
1454                PartialValue::new_refer_s(UUID_C).unwrap()
1455            )]),
1456            None,
1457            |_| {},
1458            |qs: &mut QueryServerWriteTransaction| {
1459                assert_not_memberof!(qs, UUID_A, UUID_A);
1462                assert_not_memberof!(qs, UUID_A, UUID_B);
1463                assert_not_memberof!(qs, UUID_A, UUID_C);
1464
1465                assert_memberof!(qs, UUID_B, UUID_A);
1466                assert_not_memberof!(qs, UUID_B, UUID_B);
1467                assert_not_memberof!(qs, UUID_B, UUID_C);
1468
1469                assert_not_memberof!(qs, UUID_C, UUID_A);
1470                assert_not_memberof!(qs, UUID_C, UUID_B);
1471                assert_not_memberof!(qs, UUID_C, UUID_C);
1472
1473                assert_not_dirmemberof!(qs, UUID_A, UUID_A);
1474                assert_not_dirmemberof!(qs, UUID_A, UUID_B);
1475                assert_not_dirmemberof!(qs, UUID_A, UUID_C);
1476
1477                assert_dirmemberof!(qs, UUID_B, UUID_A);
1478                assert_not_dirmemberof!(qs, UUID_B, UUID_B);
1479                assert_not_dirmemberof!(qs, UUID_B, UUID_C);
1480
1481                assert_not_dirmemberof!(qs, UUID_C, UUID_A);
1482                assert_not_dirmemberof!(qs, UUID_C, UUID_B);
1483                assert_not_dirmemberof!(qs, UUID_C, UUID_C);
1484            }
1485        );
1486    }
1487
1488    #[test]
1489    fn test_modify_mo_del_cycle() {
1490        let mut ea = EA.clone();
1495        let mut eb = EB.clone();
1496        let mut ec = EC.clone();
1497
1498        ea.add_ava(Attribute::Member, Value::new_refer_s(UUID_B).unwrap());
1499        ea.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_C).unwrap());
1500        ea.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_B).unwrap());
1501        ea.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
1502
1503        eb.add_ava(Attribute::Member, Value::new_refer_s(UUID_C).unwrap());
1504        eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_C).unwrap());
1505        eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_B).unwrap());
1506        eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
1507
1508        ec.add_ava(Attribute::Member, Value::new_refer_s(UUID_A).unwrap());
1509        ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_C).unwrap());
1510        ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_B).unwrap());
1511        ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
1512
1513        let preload = vec![ea, eb, ec];
1514        run_modify_test!(
1515            Ok(()),
1516            preload,
1517            filter!(f_eq(
1518                Attribute::Uuid,
1519                PartialValue::new_uuid_s(UUID_C).unwrap()
1520            )),
1521            ModifyList::new_list(vec![Modify::Removed(
1522                Attribute::Member,
1523                PartialValue::new_refer_s(UUID_A).unwrap()
1524            )]),
1525            None,
1526            |_| {},
1527            |qs: &mut QueryServerWriteTransaction| {
1528                assert_not_memberof!(qs, UUID_A, UUID_A);
1531                assert_not_memberof!(qs, UUID_A, UUID_B);
1532                assert_not_memberof!(qs, UUID_A, UUID_C);
1533
1534                assert_memberof!(qs, UUID_B, UUID_A);
1535                assert_not_memberof!(qs, UUID_B, UUID_B);
1536                assert_not_memberof!(qs, UUID_B, UUID_C);
1537
1538                assert_memberof!(qs, UUID_C, UUID_A);
1539                assert_memberof!(qs, UUID_C, UUID_B);
1540                assert_not_memberof!(qs, UUID_C, UUID_C);
1541
1542                assert_not_dirmemberof!(qs, UUID_A, UUID_A);
1543                assert_not_dirmemberof!(qs, UUID_A, UUID_B);
1544                assert_not_dirmemberof!(qs, UUID_A, UUID_C);
1545
1546                assert_dirmemberof!(qs, UUID_B, UUID_A);
1547                assert_not_dirmemberof!(qs, UUID_B, UUID_B);
1548                assert_not_dirmemberof!(qs, UUID_B, UUID_C);
1549
1550                assert_not_dirmemberof!(qs, UUID_C, UUID_A);
1551                assert_dirmemberof!(qs, UUID_C, UUID_B);
1552                assert_not_dirmemberof!(qs, UUID_C, UUID_C);
1553            }
1554        );
1555    }
1556
1557    #[test]
1558    fn test_modify_mo_del_multi_cycle() {
1559        let mut ea = EA.clone();
1570        let mut eb = EB.clone();
1571        let mut ec = EC.clone();
1572        let mut ed = ED.clone();
1573
1574        ea.add_ava(Attribute::Member, Value::new_refer_s(UUID_B).unwrap());
1575        ea.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_D).unwrap());
1576        ea.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_C).unwrap());
1577        ea.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_B).unwrap());
1578        ea.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
1579
1580        eb.add_ava(Attribute::Member, Value::new_refer_s(UUID_C).unwrap());
1581        eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_D).unwrap());
1582        eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_C).unwrap());
1583        eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_B).unwrap());
1584        eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
1585
1586        ec.add_ava(Attribute::Member, Value::new_refer_s(UUID_A).unwrap());
1587        ec.add_ava(Attribute::Member, Value::new_refer_s(UUID_D).unwrap());
1588        ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_D).unwrap());
1589        ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_C).unwrap());
1590        ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_B).unwrap());
1591        ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
1592
1593        ed.add_ava(Attribute::Member, Value::new_refer_s(UUID_A).unwrap());
1594        ed.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_D).unwrap());
1595        ed.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_C).unwrap());
1596        ed.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_B).unwrap());
1597        ed.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
1598
1599        let preload = vec![ea, eb, ec, ed];
1600        run_modify_test!(
1601            Ok(()),
1602            preload,
1603            filter!(f_eq(
1604                Attribute::Uuid,
1605                PartialValue::new_uuid_s(UUID_C).unwrap()
1606            )),
1607            ModifyList::new_list(vec![
1608                Modify::Removed(
1609                    Attribute::Member,
1610                    PartialValue::new_refer_s(UUID_A).unwrap()
1611                ),
1612                Modify::Removed(
1613                    Attribute::Member,
1614                    PartialValue::new_refer_s(UUID_D).unwrap()
1615                ),
1616            ]),
1617            None,
1618            |_| {},
1619            |qs: &mut QueryServerWriteTransaction| {
1620                assert_not_memberof!(qs, UUID_A, UUID_A);
1623                assert_not_memberof!(qs, UUID_A, UUID_B);
1624                assert_not_memberof!(qs, UUID_A, UUID_C);
1625                assert_memberof!(qs, UUID_A, UUID_D);
1626
1627                assert_memberof!(qs, UUID_B, UUID_A);
1628                assert_not_memberof!(qs, UUID_B, UUID_B);
1629                assert_not_memberof!(qs, UUID_B, UUID_C);
1630                assert_memberof!(qs, UUID_B, UUID_D);
1631
1632                assert_memberof!(qs, UUID_C, UUID_A);
1633                assert_memberof!(qs, UUID_C, UUID_B);
1634                assert_not_memberof!(qs, UUID_C, UUID_C);
1635                assert_memberof!(qs, UUID_C, UUID_D);
1636
1637                assert_not_memberof!(qs, UUID_D, UUID_A);
1638                assert_not_memberof!(qs, UUID_D, UUID_B);
1639                assert_not_memberof!(qs, UUID_D, UUID_C);
1640                assert_not_memberof!(qs, UUID_D, UUID_D);
1641
1642                assert_not_dirmemberof!(qs, UUID_A, UUID_A);
1643                assert_not_dirmemberof!(qs, UUID_A, UUID_B);
1644                assert_not_dirmemberof!(qs, UUID_A, UUID_C);
1645                assert_dirmemberof!(qs, UUID_A, UUID_D);
1646
1647                assert_dirmemberof!(qs, UUID_B, UUID_A);
1648                assert_not_dirmemberof!(qs, UUID_B, UUID_B);
1649                assert_not_dirmemberof!(qs, UUID_B, UUID_C);
1650                assert_not_dirmemberof!(qs, UUID_B, UUID_D);
1651
1652                assert_not_dirmemberof!(qs, UUID_C, UUID_A);
1653                assert_dirmemberof!(qs, UUID_C, UUID_B);
1654                assert_not_dirmemberof!(qs, UUID_C, UUID_C);
1655                assert_not_dirmemberof!(qs, UUID_C, UUID_D);
1656
1657                assert_not_dirmemberof!(qs, UUID_D, UUID_A);
1658                assert_not_dirmemberof!(qs, UUID_D, UUID_B);
1659                assert_not_dirmemberof!(qs, UUID_D, UUID_C);
1660                assert_not_dirmemberof!(qs, UUID_D, UUID_D);
1661            }
1662        );
1663    }
1664
1665    #[test]
1666    fn test_delete_mo_simple() {
1667        let mut ea = EA.clone();
1669        let mut eb = EB.clone();
1670
1671        ea.add_ava(Attribute::Member, Value::new_refer_s(UUID_B).unwrap());
1672        eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
1673
1674        let preload = vec![ea, eb];
1675        run_delete_test!(
1676            Ok(()),
1677            preload,
1678            filter!(f_eq(
1679                Attribute::Uuid,
1680                PartialValue::new_uuid_s(UUID_A).unwrap()
1681            )),
1682            None,
1683            |qs: &mut QueryServerWriteTransaction| {
1684                assert_not_memberof!(qs, UUID_B, UUID_A);
1687                assert_not_memberof!(qs, UUID_A, UUID_B);
1688
1689                assert_not_dirmemberof!(qs, UUID_B, UUID_A);
1690                assert_not_dirmemberof!(qs, UUID_A, UUID_B);
1691            }
1692        );
1693    }
1694
1695    #[test]
1696    fn test_delete_mo_nested_head() {
1697        let mut ea = EA.clone();
1699        let mut eb = EB.clone();
1700        let mut ec = EC.clone();
1701
1702        ea.add_ava(Attribute::Member, Value::new_refer_s(UUID_B).unwrap());
1703        eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
1704
1705        eb.add_ava(Attribute::Member, Value::new_refer_s(UUID_C).unwrap());
1706        ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
1707        ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_B).unwrap());
1708
1709        let preload = vec![ea, eb, ec];
1710        run_delete_test!(
1711            Ok(()),
1712            preload,
1713            filter!(f_eq(
1714                Attribute::Uuid,
1715                PartialValue::new_uuid_s(UUID_A).unwrap()
1716            )),
1717            None,
1718            |qs: &mut QueryServerWriteTransaction| {
1719                assert_not_memberof!(qs, UUID_B, UUID_A);
1722                assert_not_memberof!(qs, UUID_B, UUID_B);
1723                assert_not_memberof!(qs, UUID_B, UUID_C);
1724
1725                assert_not_memberof!(qs, UUID_C, UUID_A);
1726                assert_memberof!(qs, UUID_C, UUID_B);
1727                assert_not_memberof!(qs, UUID_C, UUID_C);
1728
1729                assert_not_dirmemberof!(qs, UUID_B, UUID_A);
1730                assert_not_dirmemberof!(qs, UUID_B, UUID_B);
1731                assert_not_dirmemberof!(qs, UUID_B, UUID_C);
1732
1733                assert_not_dirmemberof!(qs, UUID_C, UUID_A);
1734                assert_dirmemberof!(qs, UUID_C, UUID_B);
1735                assert_not_dirmemberof!(qs, UUID_C, UUID_C);
1736            }
1737        );
1738    }
1739
1740    #[test]
1741    fn test_delete_mo_nested_branch() {
1742        let mut ea = EA.clone();
1744        let mut eb = EB.clone();
1745        let mut ec = EC.clone();
1746
1747        ea.add_ava(Attribute::Member, Value::new_refer_s(UUID_B).unwrap());
1748        eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
1749
1750        eb.add_ava(Attribute::Member, Value::new_refer_s(UUID_C).unwrap());
1751        ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
1752        ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_B).unwrap());
1753
1754        let preload = vec![ea, eb, ec];
1755        run_delete_test!(
1756            Ok(()),
1757            preload,
1758            filter!(f_eq(
1759                Attribute::Uuid,
1760                PartialValue::new_uuid_s(UUID_B).unwrap()
1761            )),
1762            None,
1763            |qs: &mut QueryServerWriteTransaction| {
1764                assert_not_memberof!(qs, UUID_A, UUID_A);
1767                assert_not_memberof!(qs, UUID_A, UUID_B);
1768                assert_not_memberof!(qs, UUID_A, UUID_C);
1769
1770                assert_not_memberof!(qs, UUID_C, UUID_A);
1771                assert_not_memberof!(qs, UUID_C, UUID_B);
1772                assert_not_memberof!(qs, UUID_C, UUID_C);
1773
1774                assert_not_dirmemberof!(qs, UUID_A, UUID_A);
1775                assert_not_dirmemberof!(qs, UUID_A, UUID_B);
1776                assert_not_dirmemberof!(qs, UUID_A, UUID_C);
1777
1778                assert_not_dirmemberof!(qs, UUID_C, UUID_A);
1779                assert_not_dirmemberof!(qs, UUID_C, UUID_B);
1780                assert_not_dirmemberof!(qs, UUID_C, UUID_C);
1781            }
1782        );
1783    }
1784
1785    #[test]
1786    fn test_delete_mo_cycle() {
1787        let mut ea = EA.clone();
1790        let mut eb = EB.clone();
1791        let mut ec = EC.clone();
1792
1793        ea.add_ava(Attribute::Member, Value::new_refer_s(UUID_B).unwrap());
1794        ea.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
1795        ea.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_B).unwrap());
1796        ea.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_C).unwrap());
1797
1798        eb.add_ava(Attribute::Member, Value::new_refer_s(UUID_C).unwrap());
1799        eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
1800        eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_B).unwrap());
1801        eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_C).unwrap());
1802
1803        ec.add_ava(Attribute::Member, Value::new_refer_s(UUID_A).unwrap());
1804        ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
1805        ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_B).unwrap());
1806        ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_C).unwrap());
1807
1808        let preload = vec![ea, eb, ec];
1809        run_delete_test!(
1810            Ok(()),
1811            preload,
1812            filter!(f_eq(
1813                Attribute::Uuid,
1814                PartialValue::new_uuid_s(UUID_A).unwrap()
1815            )),
1816            None,
1817            |qs: &mut QueryServerWriteTransaction| {
1818                assert_not_memberof!(qs, UUID_B, UUID_A);
1821                assert_not_memberof!(qs, UUID_B, UUID_B);
1822                assert_not_memberof!(qs, UUID_B, UUID_C);
1823
1824                assert_not_memberof!(qs, UUID_C, UUID_A);
1825                assert_memberof!(qs, UUID_C, UUID_B);
1826                assert_not_memberof!(qs, UUID_C, UUID_C);
1827
1828                assert_not_dirmemberof!(qs, UUID_B, UUID_A);
1829                assert_not_dirmemberof!(qs, UUID_B, UUID_B);
1830                assert_not_dirmemberof!(qs, UUID_B, UUID_C);
1831
1832                assert_not_dirmemberof!(qs, UUID_C, UUID_A);
1833                assert_dirmemberof!(qs, UUID_C, UUID_B);
1834                assert_not_dirmemberof!(qs, UUID_C, UUID_C);
1835            }
1836        );
1837    }
1838
1839    #[test]
1840    fn test_delete_mo_multi_cycle() {
1841        let mut ea = EA.clone();
1845        let mut eb = EB.clone();
1846        let mut ec = EC.clone();
1847        let mut ed = ED.clone();
1848
1849        ea.add_ava(Attribute::Member, Value::new_refer_s(UUID_B).unwrap());
1850        ea.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
1851        ea.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_B).unwrap());
1852        ea.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_C).unwrap());
1853        ea.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_D).unwrap());
1854
1855        eb.add_ava(Attribute::Member, Value::new_refer_s(UUID_C).unwrap());
1856        eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
1857        eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_B).unwrap());
1858        eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_C).unwrap());
1859        eb.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_D).unwrap());
1860
1861        ec.add_ava(Attribute::Member, Value::new_refer_s(UUID_A).unwrap());
1862        ec.add_ava(Attribute::Member, Value::new_refer_s(UUID_D).unwrap());
1863        ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
1864        ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_B).unwrap());
1865        ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_C).unwrap());
1866        ec.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_D).unwrap());
1867
1868        ed.add_ava(Attribute::Member, Value::new_refer_s(UUID_A).unwrap());
1869        ed.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_A).unwrap());
1870        ed.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_B).unwrap());
1871        ed.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_C).unwrap());
1872        ed.add_ava(Attribute::MemberOf, Value::new_refer_s(UUID_D).unwrap());
1873
1874        let preload = vec![ea, eb, ec, ed];
1875        run_delete_test!(
1876            Ok(()),
1877            preload,
1878            filter!(f_eq(
1879                Attribute::Uuid,
1880                PartialValue::new_uuid_s(UUID_B).unwrap()
1881            )),
1882            None,
1883            |qs: &mut QueryServerWriteTransaction| {
1884                assert_not_memberof!(qs, UUID_A, UUID_B);
1887                assert_not_memberof!(qs, UUID_A, UUID_A);
1888                assert_memberof!(qs, UUID_A, UUID_C);
1889                assert_memberof!(qs, UUID_A, UUID_D);
1890
1891                assert_not_memberof!(qs, UUID_C, UUID_A);
1892                assert_not_memberof!(qs, UUID_C, UUID_B);
1893                assert_not_memberof!(qs, UUID_C, UUID_C);
1894                assert_not_memberof!(qs, UUID_C, UUID_D);
1895
1896                assert_not_memberof!(qs, UUID_D, UUID_A);
1897                assert_not_memberof!(qs, UUID_D, UUID_B);
1898                assert_memberof!(qs, UUID_D, UUID_C);
1899                assert_not_memberof!(qs, UUID_D, UUID_D);
1900
1901                assert_not_dirmemberof!(qs, UUID_A, UUID_A);
1902                assert_not_dirmemberof!(qs, UUID_A, UUID_B);
1903                assert_dirmemberof!(qs, UUID_A, UUID_C);
1904                assert_dirmemberof!(qs, UUID_A, UUID_D);
1905
1906                assert_not_dirmemberof!(qs, UUID_C, UUID_A);
1907                assert_not_dirmemberof!(qs, UUID_C, UUID_B);
1908                assert_not_dirmemberof!(qs, UUID_C, UUID_C);
1909                assert_not_dirmemberof!(qs, UUID_C, UUID_D);
1910
1911                assert_not_dirmemberof!(qs, UUID_D, UUID_A);
1912                assert_not_dirmemberof!(qs, UUID_C, UUID_B);
1913                assert_dirmemberof!(qs, UUID_D, UUID_C);
1914                assert_not_dirmemberof!(qs, UUID_D, UUID_D);
1915            }
1916        );
1917    }
1918}