예제 #1
0
def test_restore_goals_from_db():
    file_name = NamedTemporaryFile().name
    with sqlite3.connect(file_name) as conn:
        run_migrations(conn)
        setup_sample_db(conn)
    actual_goals = load(file_name)
    actual_goals.accept(ToggleOpenView())
    expected_goals = Goals("Root")
    expected_goals.accept_all(
        Add("A"),
        Add("B"),
        Select(2),
        HoldSelect(),
        Select(3),
        ToggleLink(),
        Select(3),
        ToggleClose(),
        Select(1),
        HoldSelect(),
        Select(2),
    )
    keys = "name,edge,open,select"
    assert expected_goals.q(keys=keys) == actual_goals.q(keys=keys)
    assert not actual_goals.events()
예제 #2
0
class GoalsTest(TestCase):
    def setUp(self):
        self.messages = []
        self.goals = Goals("Root", self._register_message)

    def _register_message(self, msg):
        self.messages.append(msg)

    def build(self, *goal_prototypes):
        return build_goaltree(*goal_prototypes,
                              message_fn=self._register_message)

    def test_there_is_one_goal_at_start(self):
        assert self.goals.q(keys="name,switchable") == {
            1: {
                "name": "Root",
                "switchable": True
            }
        }

    def test_new_goal_moves_to_top(self):
        self.goals.accept(Add("A"))
        assert self.goals.q(keys="name,switchable") == {
            1: {
                "name": "Root",
                "switchable": False
            },
            2: {
                "name": "A",
                "switchable": True
            },
        }

    def test_added_goal_has_strong_link_with_parent(self):
        self.goals.accept(Add("New"))
        assert self.goals.q(keys="name,edge") == {
            1: {
                "name": "Root",
                "edge": [(2, EdgeType.PARENT)]
            },
            2: {
                "name": "New",
                "edge": []
            },
        }

    def test_two_new_goals_move_to_top(self):
        self.goals.accept_all(Add("A"), Add("B"))
        assert self.goals.q(keys="name,switchable") == {
            1: {
                "name": "Root",
                "switchable": False
            },
            2: {
                "name": "A",
                "switchable": True
            },
            3: {
                "name": "B",
                "switchable": True
            },
        }

    def test_two_goals_in_a_chain(self):
        self.goals.accept_all(Add("A"), Add("AA", 2))
        assert self.goals.q(keys="name,switchable") == {
            1: {
                "name": "Root",
                "switchable": False
            },
            2: {
                "name": "A",
                "switchable": False
            },
            3: {
                "name": "AA",
                "switchable": True
            },
        }

    def test_rename_goal(self):
        self.goals.accept_all(Add("Boom"), Select(2), Rename("A"))
        assert self.goals.q() == {1: {"name": "Root"}, 2: {"name": "A"}}

    def test_insert_goal_in_the_middle(self):
        self.goals.accept_all(Add("B"), HoldSelect(), Select(2))
        assert self.goals.q(keys="name,edge,switchable") == {
            1: {
                "name": "Root",
                "edge": [(2, EdgeType.PARENT)],
                "switchable": False
            },
            2: {
                "name": "B",
                "edge": [],
                "switchable": True
            },
        }
        self.goals.accept(Insert("A"))
        assert self.goals.q(keys="name,edge,switchable") == {
            1: {
                "name": "Root",
                "edge": [(3, EdgeType.PARENT)],
                "switchable": False
            },
            2: {
                "name": "B",
                "edge": [],
                "switchable": True
            },
            3: {
                "name": "A",
                "edge": [(2, EdgeType.PARENT)],
                "switchable": False
            },
        }

    def test_insert_goal_between_independent_goals(self):
        self.goals = self.build(
            open_(1, "Root", [2, 3]),
            open_(2, "A", select=previous),
            open_(3, "B", select=selected),
        )
        self.goals.accept(Insert("Wow"))
        assert self.goals.q(keys="name,edge,switchable") == {
            1: {
                "name": "Root",
                "edge": [(2, EdgeType.PARENT), (3, EdgeType.PARENT)],
                "switchable": False,
            },
            2: {
                "name": "A",
                "edge": [(4, EdgeType.BLOCKER)],
                "switchable": False
            },
            3: {
                "name": "B",
                "edge": [],
                "switchable": True
            },
            4: {
                "name": "Wow",
                "edge": [(3, EdgeType.BLOCKER)],
                "switchable": False
            },
        }

    def test_reverse_insertion(self):
        """Not sure whether such trick should be legal"""
        self.goals = self.build(
            open_(1, "Root", [2], select=selected),
            open_(2, "Selected", select=previous),
        )
        self.goals.accept(Insert("Intermediate?"))
        # No, it's not intermediate
        assert self.goals.q("name,edge") == {
            1: {
                "name": "Root",
                "edge": [(2, EdgeType.PARENT)]
            },
            2: {
                "name": "Selected",
                "edge": [(3, EdgeType.BLOCKER)]
            },
            3: {
                "name": "Intermediate?",
                "edge": []
            },
        }

    def test_close_single_goal(self):
        assert self.goals.q(keys="name,open") == {
            1: {
                "name": "Root",
                "open": True
            }
        }
        self.goals.accept(ToggleClose())
        assert self.goals.q(keys="name,open,switchable") == {
            1: {
                "name": "Root",
                "open": False,
                "switchable": True
            }
        }

    def test_reopen_goal(self):
        self.goals = self.build(open_(1, "Root", [2]),
                                clos_(2, "A", select=selected))
        assert self.goals.q(keys="open") == {
            1: {
                "open": True
            },
            2: {
                "open": False
            }
        }
        self.goals.accept(ToggleClose())
        assert self.goals.q(keys="open,switchable") == {
            1: {
                "open": True,
                "switchable": False
            },
            2: {
                "open": True,
                "switchable": True
            },
        }

    def test_close_goal_again(self):
        self.goals = self.build(
            open_(1, "Root", [2], select=selected),
            open_(2, "A", [3]),
            clos_(3, "Ab"),
        )
        self.goals.accept_all(Select(2), ToggleClose())
        assert self.goals.q(keys="open,switchable") == {
            1: {
                "open": True,
                "switchable": True
            },
            2: {
                "open": False,
                "switchable": True
            },
            3: {
                "open": False,
                "switchable": False
            },
        }
        self.goals.accept_all(Select(2), ToggleClose())
        assert self.goals.q(keys="open,switchable") == {
            1: {
                "open": True,
                "switchable": False
            },
            2: {
                "open": True,
                "switchable": True
            },
            3: {
                "open": False,
                "switchable": True
            },
        }
        self.goals.accept_all(Select(2), ToggleClose())
        assert self.goals.q(keys="open,switchable") == {
            1: {
                "open": True,
                "switchable": True
            },
            2: {
                "open": False,
                "switchable": True
            },
            3: {
                "open": False,
                "switchable": False
            },
        }

    def test_closed_leaf_goal_could_not_be_reopened(self):
        self.goals = self.build(open_(1, "Root", [2], select=selected),
                                clos_(2, "A", [3]), clos_(3, "B"))
        assert self.goals.q(keys="open,switchable") == {
            1: {
                "open": True,
                "switchable": True
            },
            2: {
                "open": False,
                "switchable": True
            },
            3: {
                "open": False,
                "switchable": False
            },
        }
        self.goals.accept_all(Select(3), ToggleClose())
        # nothing should change
        assert self.goals.q(keys="open,switchable") == {
            1: {
                "open": True,
                "switchable": True
            },
            2: {
                "open": False,
                "switchable": True
            },
            3: {
                "open": False,
                "switchable": False
            },
        }

    def test_goal_in_the_middle_could_not_be_closed(self):
        self.goals = self.build(
            open_(1, "Root", [2, 3]),
            open_(2, "A", blockers=[4]),
            open_(3, "B", [4], select=selected),
            open_(4, "C"),
        )
        self.goals.accept(ToggleClose())
        assert self.goals.q(keys="open") == {
            1: {
                "open": True
            },
            2: {
                "open": True
            },
            3: {
                "open": True
            },
            4: {
                "open": True
            },
        }

    def test_delete_single_goal(self):
        self.goals = self.build(open_(1, "Root", [2]),
                                open_(2, "A", select=selected))
        self.goals.accept(Delete())
        assert self.goals.q(keys="name,select,switchable") == {
            1: {
                "name": "Root",
                "select": "select",
                "switchable": True
            },
        }

    def test_enumeration_should_not_be_changed_after_delete(self):
        self.goals = self.build(open_(1, "Root", [2, 3]),
                                open_(2, "A", select=selected), open_(3, "B"))
        self.goals.accept(Delete())
        assert self.goals.q(keys="name,switchable") == {
            1: {
                "name": "Root",
                "switchable": False
            },
            3: {
                "name": "B",
                "switchable": True
            },
        }

    def test_remove_goal_chain_with_children(self):
        self.goals = self.build(open_(1, "Root", [2]),
                                open_(2, "A", [3], select=selected),
                                open_(3, "B"))
        self.goals.accept(Delete())
        assert self.goals.q() == {1: {"name": "Root"}}

    def test_relink_goal_chain_with_blockers(self):
        self.goals = self.build(
            open_(1, "Root", [2]),
            open_(2, "A", blockers=[3], select=selected),
            open_(3, "B"),
        )
        self.goals.accept(Delete())
        assert self.goals.q("name,edge") == {
            1: {
                "name": "Root",
                "edge": [(3, EdgeType.BLOCKER)]
            },
            3: {
                "name": "B",
                "edge": []
            },
        }

    def test_select_parent_after_delete(self):
        self.goals = self.build(
            open_(1, "Root", [2], select=previous),
            open_(2, "Parent", [3]),
            open_(3, "Delete me", select=selected),
        )
        self.goals.accept(Delete())
        assert self.goals.q("name,edge,select") == {
            1: {
                "name": "Root",
                "edge": [(2, EdgeType.PARENT)],
                "select": None
            },
            2: {
                "name": "Parent",
                "edge": [],
                "select": "select"
            },
        }

    def test_add_link_between_goals(self):
        self.goals = self.build(
            open_(1, "Root", [2, 3]),
            open_(2, "A", select=previous),
            open_(3, "B", select=selected),
        )
        assert self.goals.q(keys="switchable,edge") == {
            1: {
                "switchable": False,
                "edge": [(2, EdgeType.PARENT), (3, EdgeType.PARENT)],
            },
            2: {
                "switchable": True,
                "edge": []
            },
            3: {
                "switchable": True,
                "edge": []
            },
        }
        self.goals.accept(ToggleLink())
        assert self.goals.q(keys="switchable,edge") == {
            1: {
                "switchable": False,
                "edge": [(2, EdgeType.PARENT), (3, EdgeType.PARENT)],
            },
            2: {
                "switchable": False,
                "edge": [(3, EdgeType.BLOCKER)]
            },
            3: {
                "switchable": True,
                "edge": []
            },
        }

    def test_view_edges(self):
        self.goals = self.build(
            open_(1, "Root", [2, 3]),
            open_(2, "A", [4]),
            open_(3, "B", blockers=[4], select=previous),
            open_(4, "C", select=selected),
        )
        assert self.goals.q(keys="edge,switchable") == {
            1: {
                "edge": [(2, EdgeType.PARENT), (3, EdgeType.PARENT)],
                "switchable": False,
            },
            2: {
                "edge": [(4, EdgeType.PARENT)],
                "switchable": False
            },
            3: {
                "edge": [(4, EdgeType.BLOCKER)],
                "switchable": False
            },
            4: {
                "edge": [],
                "switchable": True
            },
        }

    def test_no_link_to_self_is_allowed(self):
        self.goals.accept(ToggleLink())
        assert self.goals.q(keys="edge") == {1: {"edge": []}}

    def test_no_loops_allowed(self):
        self.goals = self.build(
            open_(1, "Root", [2], select=selected),
            open_(2, "step", [3]),
            open_(3, "next", [4]),
            open_(4, "more", select=previous),
        )
        self.goals.accept(ToggleLink())
        assert self.goals.q(keys="edge") == {
            1: {
                "edge": [(2, EdgeType.PARENT)]
            },
            2: {
                "edge": [(3, EdgeType.PARENT)]
            },
            3: {
                "edge": [(4, EdgeType.PARENT)]
            },
            4: {
                "edge": []
            },
        }

    def test_new_parent_link_replaces_old_one(self):
        self.goals = self.build(
            open_(1, "Root", [2, 3]),
            open_(2, "Old parent", [4]),
            open_(3, "New parent", select=previous),
            open_(4, "Child", select=selected),
        )
        self.goals.accept(ToggleLink(edge_type=EdgeType.PARENT))
        assert self.goals.q(keys="edge") == {
            1: {
                "edge": [(2, EdgeType.PARENT), (3, EdgeType.PARENT)]
            },
            2: {
                "edge": [(4, EdgeType.BLOCKER)]
            },
            3: {
                "edge": [(4, EdgeType.PARENT)]
            },
            4: {
                "edge": []
            },
        }

    def test_new_parent_link_replaces_old_one_when_changed_from_blocker(self):
        self.goals = self.build(
            open_(1, "Root", [2, 3]),
            open_(2, "A", select=selected),
            open_(3, "B", blockers=[2], select=previous),
        )
        self.goals.accept(ToggleLink(edge_type=EdgeType.PARENT))
        assert self.goals.q("name,edge") == {
            1: {
                "name": "Root",
                "edge": [(2, EdgeType.BLOCKER), (3, EdgeType.PARENT)]
            },
            2: {
                "name": "A",
                "edge": []
            },
            3: {
                "name": "B",
                "edge": [(2, EdgeType.PARENT)]
            },
        }

    def test_remove_link_between_goals(self):
        self.goals = self.build(
            open_(1, "Root", [2, 3]),
            open_(2, "A", blockers=[3], select=previous),
            open_(3, "B", select=selected),
        )
        self.goals.accept(ToggleLink(edge_type=EdgeType.BLOCKER))
        assert self.goals.q(keys="edge,switchable") == {
            1: {
                "edge": [(2, EdgeType.PARENT), (3, EdgeType.PARENT)],
                "switchable": False,
            },
            2: {
                "edge": [],
                "switchable": True
            },
            3: {
                "edge": [],
                "switchable": True
            },
        }

    def test_change_link_type(self):
        self.goals = self.build(open_(1, "Root", [2], select=previous),
                                open_(2, "Top", [], select=selected))
        assert self.goals.q(keys="name,edge") == {
            1: {
                "name": "Root",
                "edge": [(2, EdgeType.PARENT)]
            },
            2: {
                "name": "Top",
                "edge": []
            },
        }
        self.goals.accept(ToggleLink())
        assert self.goals.q(keys="name,edge") == {
            1: {
                "name": "Root",
                "edge": [(2, EdgeType.BLOCKER)]
            },
            2: {
                "name": "Top",
                "edge": []
            },
        }
        self.goals.accept(ToggleLink(edge_type=EdgeType.PARENT))
        assert self.goals.q(keys="name,edge") == {
            1: {
                "name": "Root",
                "edge": [(2, EdgeType.PARENT)]
            },
            2: {
                "name": "Top",
                "edge": []
            },
        }

    def test_remove_blocked_goal_without_children(self):
        self.goals = self.build(
            open_(1, "Root", [2, 3]),
            open_(2, "A", [4]),
            open_(3, "B", blockers=[4]),
            open_(4, "C", select=selected),
        )
        assert self.goals.q(keys="name,edge") == {
            1: {
                "name": "Root",
                "edge": [(2, EdgeType.PARENT), (3, EdgeType.PARENT)]
            },
            2: {
                "name": "A",
                "edge": [(4, EdgeType.PARENT)]
            },
            3: {
                "name": "B",
                "edge": [(4, EdgeType.BLOCKER)]
            },
            4: {
                "name": "C",
                "edge": []
            },
        }
        self.goals.accept_all(Select(3), Delete())
        assert self.goals.q(keys="name,edge,switchable") == {
            1: {
                "name": "Root",
                "edge": [(2, EdgeType.PARENT)],
                "switchable": False
            },
            2: {
                "name": "A",
                "edge": [(4, EdgeType.PARENT)],
                "switchable": False
            },
            4: {
                "name": "C",
                "edge": [],
                "switchable": True
            },
        }

    def test_root_goal_is_selected_by_default(self):
        assert self.goals.q(keys="select") == {1: {"select": "select"}}
        self.goals.accept(Add("A"))
        assert self.goals.q(keys="select") == {
            1: {
                "select": "select"
            },
            2: {
                "select": None
            },
        }
        self.goals.accept(Add("B"))
        assert self.goals.q(keys="select") == {
            1: {
                "select": "select"
            },
            2: {
                "select": None
            },
            3: {
                "select": None
            },
        }

    def test_new_goal_is_added_to_the_selected_node(self):
        self.goals.accept_all(Add("A"), Select(2))
        assert self.goals.q(keys="name,select") == {
            1: {
                "name": "Root",
                "select": "prev"
            },
            2: {
                "name": "A",
                "select": "select"
            },
        }
        self.goals.accept(Add("B"))
        assert self.goals.q(keys="name,select,edge") == {
            1: {
                "name": "Root",
                "select": "prev",
                "edge": [(2, EdgeType.PARENT)]
            },
            2: {
                "name": "A",
                "select": "select",
                "edge": [(3, EdgeType.PARENT)]
            },
            3: {
                "name": "B",
                "select": None,
                "edge": []
            },
        }

    def test_move_selection_to_another_open_goal_after_closing(self):
        self.goals = self.build(
            open_(1, "Root", [2, 3, 4]),
            open_(2, "A", select=selected),
            open_(3, "B"),
            open_(4, "C"),
        )
        self.goals.accept(ToggleClose())
        assert self.goals.q(keys="open,select") == {
            1: {
                "open": True,
                "select": None
            },
            2: {
                "open": False,
                "select": None
            },
            3: {
                "open": True,
                "select": "select"
            },
            4: {
                "open": True,
                "select": None
            },
        }

    def test_move_selection_to_previously_selected_goal_after_closing(self):
        self.goals = self.build(
            open_(1, "Root", [2, 3, 4]),
            open_(2, "A", select=selected),
            open_(3, "B"),
            open_(4, "C", select=previous),
        )
        self.goals.accept(ToggleClose())
        assert self.goals.q(keys="open,select") == {
            1: {
                "open": True,
                "select": None
            },
            2: {
                "open": False,
                "select": None
            },
            3: {
                "open": True,
                "select": None
            },
            4: {
                "open": True,
                "select": "select"
            },
        }

    def test_move_selection_to_another_open_goal_with_given_root_after_closing(
            self):
        self.goals = self.build(
            open_(1, "Root", [2, 3]),
            open_(2, "Should not be selected"),
            open_(3, "Subroot", [4, 5]),
            open_(4, "Must be selected"),
            open_(5, "Closing", select=selected),
        )
        self.goals.accept(ToggleClose(3))
        assert self.goals.q(keys="open,select") == {
            1: {
                "open": True,
                "select": None
            },
            2: {
                "open": True,
                "select": None
            },
            3: {
                "open": True,
                "select": None
            },
            4: {
                "open": True,
                "select": "select"
            },
            5: {
                "open": False,
                "select": None
            },
        }

    def test_do_not_select_unswitchable_goal_after_closing(self):
        self.goals = self.build(
            open_(1, "Root", [2, 3]),
            open_(2, "Should not be selected"),
            open_(3, "Subroot", [4, 5]),
            open_(4, "intermediate", [6]),
            open_(5, "Closing", select=selected),
            open_(6, "Must be selected"),
        )
        self.goals.accept(ToggleClose(3))
        assert self.goals.q(keys="open,select") == {
            1: {
                "open": True,
                "select": None
            },
            2: {
                "open": True,
                "select": None
            },
            3: {
                "open": True,
                "select": None
            },
            4: {
                "open": True,
                "select": None
            },
            5: {
                "open": False,
                "select": None
            },
            6: {
                "open": True,
                "select": "select"
            },
        }

    def test_ignore_wrong_selection(self):
        self.goals.accept(Select(2))
        assert self.goals.q(keys="select") == {1: {"select": "select"}}

    def test_do_not_select_deleted_goals(self):
        self.goals = self.build(open_(1, "Root", [2]),
                                open_(2, "broken", select=selected))
        self.goals.accept_all(Delete(), Select(2))
        assert self.goals.q(keys="select") == {1: {"select": "select"}}

    def test_selection_should_be_instant(self):
        self.goals = self.build(
            open_(1, "Root", [2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
                  select=selected),
            open_(2, "A"),
            open_(3, "B"),
            open_(4, "C"),
            open_(5, "D"),
            open_(6, "E"),
            open_(7, "F"),
            open_(8, "G"),
            open_(9, "H"),
            open_(10, "I"),
            open_(11, "J"),
        )
        self.goals.accept(Select(2))
        assert self.goals.q(keys="select") == {
            1: {
                "select": "prev"
            },
            2: {
                "select": "select"
            },
            3: {
                "select": None
            },
            4: {
                "select": None
            },
            5: {
                "select": None
            },
            6: {
                "select": None
            },
            7: {
                "select": None
            },
            8: {
                "select": None
            },
            9: {
                "select": None
            },
            10: {
                "select": None
            },
            11: {
                "select": None
            },
        }
        self.goals.accept(Select(11))
        assert self.goals.q(keys="select") == {
            1: {
                "select": "prev"
            },
            2: {
                "select": None
            },
            3: {
                "select": None
            },
            4: {
                "select": None
            },
            5: {
                "select": None
            },
            6: {
                "select": None
            },
            7: {
                "select": None
            },
            8: {
                "select": None
            },
            9: {
                "select": None
            },
            10: {
                "select": None
            },
            11: {
                "select": "select"
            },
        }

    def test_add_events(self):
        assert self.goals.events().pop() == ("add", 1, "Root", True)
        self.goals.accept(Add("Next"))
        assert self.goals.events()[-2] == ("add", 2, "Next", True)
        assert self.goals.events()[-1] == ("link", 1, 2, EdgeType.PARENT)

    def test_select_events(self):
        self.goals.accept_all(Add("Next"), Select(2))
        assert self.goals.events()[-1] == ("select", 2)
        self.goals.accept_all(HoldSelect(), Select(1))
        assert self.goals.events()[-2] == ("hold_select", 2)
        assert self.goals.events()[-1] == ("select", 1)

    def test_toggle_close_events(self):
        self.goals.accept(ToggleClose())
        assert self.goals.events()[-3] == ("toggle_close", False, 1)
        assert self.goals.events()[-2] == ("select", 1)
        assert self.goals.events()[-1] == ("hold_select", 1)
        self.goals.accept(ToggleClose())
        assert self.goals.events()[-1] == ("toggle_close", True, 1)

    def test_rename_event(self):
        self.goals.accept(Rename("New"))
        assert self.goals.events()[-1] == ("rename", "New", 1)

    def test_delete_events(self):
        self.goals.accept_all(Add("Sheep"), Select(2), Delete())
        assert self.goals.events()[-3] == ("delete", 2)
        assert self.goals.events()[-2] == ("select", 1)
        assert self.goals.events()[-1] == ("hold_select", 1)

    def test_link_events(self):
        self.goals.accept_all(Add("Next"), Add("More"), Select(2),
                              HoldSelect(), Select(3), ToggleLink())
        assert self.goals.events()[-1] == ("link", 2, 3, EdgeType.BLOCKER)
        self.goals.accept(ToggleLink())
        assert self.goals.events()[-1] == ("unlink", 2, 3, EdgeType.BLOCKER)

    def test_change_link_type_events(self):
        self.goals = self.build(
            open_(1, "Root", [2, 3]),
            open_(2, "Lower", blockers=[3], select=previous),
            open_(3, "Upper", [], select=selected),
        )
        self.goals.accept(ToggleLink(edge_type=EdgeType.PARENT))
        assert self.goals.events()[-4] == ("link", 2, 3, EdgeType.PARENT)
        assert self.goals.events()[-3] == ("unlink", 2, 3, EdgeType.BLOCKER)
        assert self.goals.events()[-2] == ("link", 1, 3, EdgeType.BLOCKER)
        assert self.goals.events()[-1] == ("unlink", 1, 3, EdgeType.PARENT)

    def test_no_messages_at_start(self):
        assert self.messages == []

    def test_no_message_on_good_add(self):
        self.goals = self.build(open_(1, "Root", select=selected))
        self.goals.accept(Add("Success"))
        assert self.messages == []

    def test_message_on_wrong_add(self):
        self.goals = self.build(clos_(1, "Root", select=selected))
        self.goals.accept(Add("Failed"))
        assert len(self.messages) == 1

    def test_no_message_on_good_insert(self):
        self.goals = self.build(open_(1, "Root", [2], select=previous),
                                open_(2, "Top", select=selected))
        self.goals.accept(Insert("Success"))
        assert self.messages == []

    def test_message_on_insert_without_two_goals(self):
        self.goals = self.build(open_(1, "Root", select=selected))
        self.goals.accept(Insert("Failed"))
        assert len(self.messages) == 1

    def test_message_on_circular_insert(self):
        self.goals = self.build(open_(1, "Root", [2], select=selected),
                                open_(2, "Top", [], select=previous))
        self.goals.accept(Insert("Failed"))
        assert len(self.messages) == 1

    def test_no_message_on_valid_closing(self):
        self.goals = self.build(open_(1, "Root", [2]),
                                open_(2, "Top", [], select=selected))
        self.goals.accept(ToggleClose())
        assert self.messages == []

    def test_message_on_closing_blocked_goal(self):
        self.goals = self.build(open_(1, "Root", [2], select=selected),
                                open_(2, "Top"))
        self.goals.accept(ToggleClose())
        assert len(self.messages) == 1

    def test_no_message_on_valid_reopening(self):
        self.goals = self.build(clos_(1, "Root", [2], select=selected),
                                clos_(2, "Top"))
        self.goals.accept(ToggleClose())
        assert self.messages == []

    def test_message_on_reopening_blocked_goal(self):
        self.goals = self.build(clos_(1, "Root", [2]),
                                clos_(2, "Top", [], select=selected))
        self.goals.accept(ToggleClose())
        assert len(self.messages) == 1

    def test_no_message_on_delete_non_root_goal(self):
        self.goals = self.build(clos_(1, "Root", [2]),
                                clos_(2, "Top", [], select=selected))
        self.goals.accept(Delete())
        assert self.messages == []

    def test_message_on_delete_root_goal(self):
        self.goals = self.build(clos_(1, "Root", [2], select=selected),
                                clos_(2, "Top"))
        self.goals.accept(Delete())
        assert len(self.messages) == 1

    def test_no_message_on_allowed_link(self):
        self.goals = self.build(
            open_(1, "Root", [2], select=previous),
            open_(2, "Middle", [3]),
            open_(3, "Top", [], select=selected),
        )
        self.goals.accept(ToggleLink())
        assert self.messages == []

    def test_message_on_link_to_self(self):
        self.goals = self.build(
            open_(1, "Root", [2]),
            open_(2, "Middle", [3]),
            open_(3, "Top", [], select=selected),
        )
        self.goals.accept(ToggleLink())
        assert len(self.messages) == 1

    def test_no_message_when_remove_not_last_link(self):
        self.goals = self.build(
            open_(1, "Root", [2, 3], select=previous),
            open_(2, "Middle", blockers=[3]),
            open_(3, "Top", [], select=selected),
        )
        self.goals.accept(ToggleLink())
        assert self.messages == []

    def test_message_when_remove_last_link(self):
        self.goals = self.build(
            open_(1, "Root", [2]),
            open_(2, "Middle", [3], select=previous),
            open_(3, "Top", [], select=selected),
        )
        self.goals.accept(ToggleLink(edge_type=EdgeType.PARENT))
        assert len(self.messages) == 1

    def test_message_when_closed_goal_is_blocked_by_open_one(self):
        self.goals = self.build(
            open_(1, "Root", [2, 3]),
            clos_(2, "Middle", [], select=previous),
            open_(3, "Top", [], select=selected),
        )
        self.goals.accept(ToggleLink())
        assert len(self.messages) == 1