def test_delete_node(cur, nd1, nd2_1, nd2_1_1, nd2_leaf): """ Tree layout before delete: / - nd1 - nd2-1 - nd2-1-1 - nd2-leaf - nd2 - nd3 Expected tree layout after move: / - nd1 - nd2-1 - nd2 - nd3 """ delete_node(cur, nd2_1_1, auto_position=False) # Deleted node doesn't exist anymore with pytest.raises(ValueError): get_node(cur, nd2_1_1.id) # nd2-1 has no children and no descendants assert set(get_child_ids(cur, nd2_1)) == set() assert set(get_child_ids(cur, nd2_1_1)) == set() assert set(get_descendant_ids(cur, nd2_1)) == set() # nd1 just contains nd2-1 assert set(get_child_ids(cur, nd1)) == {nd2_1.id} assert set(get_descendant_ids(cur, nd1)) == {nd2_1.id} # Ancestor and descendant sets of nd2-1-1 and nd2-leaf are empty # (or raise error in the future because they don't exist anymore) assert set(get_ancestor_ids(cur, nd2_1_1)) == set() assert set(get_ancestor_ids(cur, nd2_leaf)) == set() assert set(get_descendant_ids(cur, nd2_1_1)) == set() assert set(get_descendant_ids(cur, nd2_leaf)) == set()
def test_delete_node(cur, nd1, nd2_1, nd2_1_1, nd2_leaf): """ Tree layout before delete: / - nd1 - nd2-1 - nd2-1-1 - nd2-leaf - nd2 - nd3 Expected tree layout after move: / - nd1 - nd2-1 - nd2 - nd3 """ delete_node(cur, nd2_1_1, auto_position=False) # Deleted node doesn't exist anymore with pytest.raises(exceptions.NodeNotFound): get_node(cur, nd2_1_1.id) # nd2-1 has no children and no descendants assert set(get_child_ids(cur, nd2_1)) == set() assert set(get_child_ids(cur, nd2_1_1)) == set() assert set(get_descendant_ids(cur, nd2_1)) == set() # nd1 just contains nd2-1 assert set(get_child_ids(cur, nd1)) == {nd2_1.id} assert set(get_descendant_ids(cur, nd1)) == {nd2_1.id} # Ancestor and descendant sets of nd2-1-1 and nd2-leaf are empty # (or raise error in the future because they don't exist anymore) assert set(get_ancestor_ids(cur, nd2_1_1)) == set() assert set(get_ancestor_ids(cur, nd2_leaf)) == set() assert set(get_descendant_ids(cur, nd2_1_1)) == set() assert set(get_descendant_ids(cur, nd2_leaf)) == set()
def change_parent(cur, node, new_parent, position=None, auto_position=True): """ Move node and its subtree from its current to another parent node. Return updated ``Node`` object with new parent set. Raise ``ValueError`` if ``new_parent`` is inside ``node`` s subtree. :param node: :type node: Node or int :param new_parent: Reference to the new parent node :type new_parent: Node or int :param int position: Position in between siblings. If 0, the node will be inserted at the beginning of the parents children. If -1, the node will be inserted the the end of the parents children. If `auto_position` is disabled, this is just a value. :param bool auto_position: See :ref:`coreapi-positioning`. """ new_parent_id = int(new_parent) if new_parent_id in get_descendant_ids(cur, node): raise ValueError('Cannot move node into its own subtree.') # Can't run set_position() here because the node hasn't been moved yet, # must do it manually if auto_position: if type(position) == int and position >= 0: ensure_free_position(cur, new_parent_id, position) else: position = find_highest_position(cur, new_parent_id) + 1 sql = """ UPDATE nodes SET parent=%s, position=%s WHERE id=%s; """ cur.execute(sql, (new_parent_id, position, int(node))) if type(node) == int: node = get_node(cur, node) kwargs = node.to_dict() kwargs['parent'] = new_parent_id kwargs['position'] = position return NodeData(**kwargs)
def change_parent(cur, node, new_parent, position=None, auto_position=True): """ Move node and its subtree from its current to another parent node. Return updated ``Node`` object with new parent set. Raise ``ValueError`` if ``new_parent`` is inside ``node`` s subtree. :param node: :type node: Node or int :param new_parent: Reference to the new parent node :type new_parent: Node or int :param int position: Position in between siblings. If 0, the node will be inserted at the beginning of the parents children. If -1, the node will be inserted the the end of the parents children. If `auto_position` is disabled, this is just a value. :param bool auto_position: See :ref:`core-positioning`. """ new_parent_id = int(new_parent) if new_parent_id in get_descendant_ids(cur, node): raise ValueError('Cannot move node into its own subtree.') # Can't run set_position() here because the node hasn't been moved yet, # must do it manually if auto_position: if type(position) == int and position >= 0: ensure_free_position(cur, new_parent_id, position) else: position = find_highest_position(cur, new_parent_id) + 1 sql = """ UPDATE nodes SET parent=%s, position=%s WHERE id=%s; """ cur.execute(sql, (new_parent_id, position, int(node))) if type(node) == int: node = get_node(cur, node) kwargs = node.to_dict() kwargs['parent'] = new_parent_id kwargs['position'] = position return NodeData(**kwargs)
def test_get_descendant_ids(cur, root, nd1, nd2, nd3, nd2_1, nd2_1_1, nd2_leaf): ids = get_descendant_ids(cur, root) expected_nodes = {nd1, nd2, nd3, nd2_1, nd2_1_1, nd2_leaf} expected_ids = {node.id for node in expected_nodes} assert set(ids) == expected_ids
def test_change_parent(cur, root, nd1, nd2, nd2_1, nd2_1_1, nd2_leaf): """ Tree layout before move: / - nd1 - nd2 - nd2-1 - nd2-1-1 - nd2-leaf - nd3 Expected tree layout after move: / - nd1 - nd2-1 - nd2-1-1 - nd2-leaf - nd2 - nd3 """ # We expect nd2-1 to be child of nd2 and nd2-1-1 to be child # of nd2-1. # Move nd2-1 from nd2 to nd1 _temp_node = change_parent(cur, nd2_1.id, nd1, auto_position=False) # Return value should have new parent set assert _temp_node.parent == nd1.id # nd2-1 should have nd1 as parent node = get_node(cur, nd2_1.id) assert node.parent == nd1.id # nd2-1-1 should still have the same parent (nd2-1) child_node = get_node(cur, nd2_1_1.id) assert child_node.parent == nd2_1.id # nd2-leaf should still have the same parent (nd2-1-1) child_node = get_node(cur, nd2_leaf.id) assert child_node.parent == nd2_1_1.id # The ancestor set of nd2-1 should now contain nd1 and root assert set(get_ancestor_ids(cur, nd2_1)) == {root.id, nd1.id} # The ancestor set of nd2-1-1 should now contain nd2-1, nd1 and root expected = {root.id, nd1.id, nd2_1.id} assert set(get_ancestor_ids(cur, nd2_1_1)) == expected # The ancestor set of nd2-leaf should now contain node-2-1-1, nd2-1, # nd1 and root expected = {root.id, nd1.id, nd2_1.id, nd2_1_1.id} assert set(get_ancestor_ids(cur, nd2_leaf)) == expected # The ancestor set of nd2 should now only contain root assert set(get_ancestor_ids(cur, nd2)) == {root.id} # Check if nd2-1, nd2-1-1 and nd2-leaf are part of nd1's descendant # set now expected = {nd2_1.id, nd2_1_1.id, nd2_leaf.id} assert set(get_descendant_ids(cur, nd1)) == expected # nd2's descendant set should be empty now assert set(get_descendant_ids(cur, nd2)) == set() # Last but not least, the children function proof what we checked above too assert len(set(get_children(cur, nd1))) == 1 assert len(set(get_children(cur, nd2))) == 0