Exemplo n.º 1
0
def test_tags_shows_open_tasks_without_tag(
    repo: Repository, config: Config, capsys: CaptureFixture[Any]
) -> None:
    """
    Given: Two tasks with no tags, only one open
    When: Printing the tags report
    Then: Only the open task is shown in the report
    """
    tasks = factories.TaskFactory.create_batch(2)
    tasks[1].close("done")
    for task in tasks:
        repo.add(task)
    repo.commit()
    expected_output = [
        r".*",
        r" +Name +│ Open Tasks *",
        r".*",
        r" +None +│ +1",
        r".*",
    ]
    capsys.readouterr()

    out, err = run_report("tags", {"repo": repo}, capsys)  # act

    assert report_prints_expected(out, expected_output, err)
Exemplo n.º 2
0
    def test_print_recurring_report(self, runner: CliRunner,
                                    repo_e2e: Repository) -> None:
        """Test that recurring returns the expected output."""
        parent = RecurrentTaskFactory.create(description="D",
                                             priority=1,
                                             area="A")
        repo_e2e.add(parent)
        repo_e2e.commit()

        # ECE001: Expression is too complex. Life is tough
        expected_output = [  # noqa: ECE001
            r".*",
            r" +ID +│ +Descr.* +│ +Recur +│ +RecurType +│ +Area +| +Pri +│ +Due.*",
            r".*",
            fr" +{parent.id_} +│ +{parent.description} +│ +{parent.recurrence} +│ +"
            fr"{parent.recurrence_type.value.title()} +│ +{parent.area} +│ +"
            fr"{parent.priority} +│ +{parent.due.year}.*",
            r".*",
        ]

        result = runner.invoke(cli, ["recurring"])

        assert result.exit_code == 0
        assert report_prints_expected(result.stdout, expected_output,
                                      result.stderr)
Exemplo n.º 3
0
def test_areas_prints_only_counts_open_tasks(
    repo: Repository, config: Config, capsys: CaptureFixture[Any]
) -> None:
    """
    Given: Three tasks with the same area, one open, one completed, and other deleted
    When: Printing the areas report
    Then: Only the open task is shown
    """
    tasks = factories.TaskFactory.create_batch(3, area="Area 1")
    tasks[1].close("done")
    tasks[2].close("deleted")
    for task in tasks:
        repo.add(task)
    repo.commit()
    expected_output = [
        r".*",
        r" +Name +│ Open Tasks *",
        r".*",
        r" +Area 1 +│ +1",
        r".*",
    ]
    capsys.readouterr()

    out, err = run_report("areas", {"repo": repo}, capsys)  # act

    assert report_prints_expected(out, expected_output, err)
Exemplo n.º 4
0
def task_(repo: Repository) -> Task:
    """Insert a Task in the FakeRepository."""
    task = factories.TaskFactory.create(state="backlog")
    repo.add(task)
    repo.commit()

    return task
Exemplo n.º 5
0
def test_areas_prints_only_areas_with_open_tasks(
    repo: Repository, config: Config, capsys: CaptureFixture[Any]
) -> None:
    """
    Given: Three tasks with different areas, where only one is open
    When: Printing the areas report
    Then: Only the area with the open task is shown
    """
    tasks = factories.TaskFactory.create_batch(3)
    tasks[1].close("done")
    tasks[2].close("deleted")
    for task in tasks:
        repo.add(task)
    repo.commit()
    expected_output = [
        r".*",
        r" +Name +│ Open Tasks *",
        r".*",
        fr" +{tasks[0].area} +│ +1",
        r".*",
    ]
    capsys.readouterr()

    out, err = run_report("areas", {"repo": repo}, capsys)  # act

    assert report_prints_expected(out, expected_output, err)
Exemplo n.º 6
0
    def test_print_frozen_report_can_specify_filter(
        self,
        runner: CliRunner,
        insert_frozen_parent_task_e2e: RecurrentTask,
        repo_e2e: Repository,
    ) -> None:
        """Test that frozen accepts a task filter."""
        task = insert_frozen_parent_task_e2e
        task.description = "d"
        task.area = "special"
        task.priority = 1
        repo_e2e.add(task)
        repo_e2e.commit()
        # ECE001: Expression is too complex. Life is tough
        expected_output = [  # noqa: ECE001
            r".*",
            r" +ID +│ +Description +│ +Recur +│ +RecurType +│ +Area +| +Pri +│ +Due.*",
            r".*",
            fr" +{task.id_} +│ +{task.description} +│ +{task.recurrence} +│ +"
            fr"{task.recurrence_type.value.title()} +│ +{task.area} +│ +"
            fr"{task.priority} +│ +{task.due.year}.*",
            r".*",
        ]

        result = runner.invoke(cli, ["frozen", "area:special"])

        assert result.exit_code == 0
        assert report_prints_expected(result.stdout, expected_output,
                                      result.stderr)
Exemplo n.º 7
0
def test_task_report_can_print_tags(
    repo: Repository, config: Config, capsys: CaptureFixture[Any]
) -> None:
    """Test that the open report prints the task tags."""
    # Generate the tasks
    task = factories.TaskFactory.create(
        description="Description", tags=["tag1", "tag2"]
    )
    repo.add(task)
    repo.commit()
    # Generate the output
    expected_output = [
        r".*",
        r" +ID.*│ Tags.*",
        r".*",
        r".* +│ tag1, tag2",
        r".*",
    ]

    out, err = run_report(
        "print_task_report",
        {"repo": repo, "config": config, "report_name": "open"},
        capsys,
    )  # act

    assert report_prints_expected(out, expected_output, err)
Exemplo n.º 8
0
    def test_modify_task_can_remove_tag_that_starts_with_p(
        self,
        runner: CliRunner,
        insert_tasks_e2e: List[Task],
        faker: Faker,
        repo_e2e: Repository,
        caplog: LogCaptureFixture,
    ) -> None:
        """
        Given: A task with a tag that starts with a p
        When: removing the tag
        Then: the tag is removed

        It's necessary in case we start using the `--parent` flag as `-p`, in that case
        when using `pydo mod 0 -python`, it interprets that the parent flag is set
        and that the tag is ython.
        """
        task = insert_tasks_e2e[0]
        task.tags = ["python"]
        repo_e2e.add(task)
        repo_e2e.commit()

        result = runner.invoke(cli, ["mod", str(task.id_), "-python"])

        modified_task = repo_e2e.get(task.id_, [Task])
        assert result.exit_code == 0
        assert re.match(f"Modified task {task.id_}", caplog.records[0].msg)
        assert modified_task.tags == []
        assert modified_task.description == task.description
Exemplo n.º 9
0
    def test_print_report_report_allows_sorting(
        self,
        runner: CliRunner,
        repo_e2e: Repository,
        faker: Faker,
    ) -> None:
        """
        Given: Three tasks
        When: printing the open report sorting first by ascending priority, and then
            by descending id.
        Then: The tasks are printed in the desired order
        """
        tasks = [
            Task(id_=0, description="Last", priority=3),
            Task(id_=1, description="Middle", priority=3),
            Task(id_=2, description="First", priority=1),
        ]
        for task in tasks:
            repo_e2e.add(task)
        repo_e2e.commit()
        expected_output = [
            r".*",
            r" +ID +│ +Description +│ +Pri.*",
            r".*",
            fr" +{tasks[2].id_} +│ +{tasks[2].description} +│ +{tasks[2].priority}.*",
            fr" +{tasks[1].id_} +│ +{tasks[1].description} +│ +{tasks[1].priority}.*",
            fr" +{tasks[0].id_} +│ +{tasks[0].description} +│ +{tasks[0].priority}.*",
            r".*",
        ]

        result = runner.invoke(cli, ["report", "open", "sort:+priority,-id_"])

        assert result.exit_code == 0
        assert report_prints_expected(result.stdout, expected_output,
                                      result.stderr)
Exemplo n.º 10
0
    def test_thaw_task_by_id(
        self,
        runner: CliRunner,
        insert_frozen_parent_task_e2e: RecurrentTask,
        repo_e2e: Repository,
        caplog: LogCaptureFixture,
    ) -> None:
        """
        Given: A frozen recurrent task
        When: Thawed
        Then: The task is back active and it's next children is breed.
        """
        parent_task = insert_frozen_parent_task_e2e
        now = datetime.now()

        result = runner.invoke(cli, ["thaw", str(parent_task.id_)])

        parent_task = repo_e2e.get(parent_task.id_, [RecurrentTask])
        child_task = repo_e2e.search({"parent_id": parent_task.id_}, [Task])[0]
        assert result.exit_code == 0
        assert (
            "pydo.services",
            logging.INFO,
            f"Thawed task {parent_task.id_}: {parent_task.description}, and created "
            f"it's next child task with id {child_task.id_}",
        ) in caplog.record_tuples
        assert parent_task.state == TaskState.BACKLOG
        assert parent_task.active is True
        assert child_task.state == TaskState.BACKLOG
        assert child_task.active is True
        assert child_task.due is not None
        assert child_task.due > now
Exemplo n.º 11
0
    def test_print_open_report(
        self,
        runner: CliRunner,
        repo_e2e: Repository,
        faker: Faker,
    ) -> None:
        """Test that open returns the expected output."""
        task = Task(description="Description",
                    due=faker.date_time(),
                    priority=3)
        repo_e2e.add(task)
        repo_e2e.commit()
        expected_output = [
            r".*",
            r" +ID +│ +Description +│ +Pri.*",
            r".*",
            fr" +{task.id_} +│ +{task.description} +│ +{task.priority}.*",
            r".*",
        ]

        result = runner.invoke(cli, ["open"])

        assert result.exit_code == 0
        assert report_prints_expected(result.stdout, expected_output,
                                      result.stderr)
Exemplo n.º 12
0
    def test_repo_add_entity_merges_with_stored_values_before_adding(
        self,
        database: Any,
        repo: Repository,
        repo_tester: RepositoryTester[Repository],
    ) -> None:
        """
        Given: A repository with an entity
        When: Adding that entity with updated values
        Then: The entities are merged before they are commited.

        The Genre model has the `rating` attribute in the `skip_on_merge` configuration
        therefore even if the added entity has a different value, it's not propagated.
        """
        entity = GenreFactory.build()
        repo_tester.insert_entity(database, entity)
        original_entity = entity.copy()
        entity.rating = 3
        entity.name = "new name"
        repo.add(entity, merge=True)

        repo.commit()  # act

        stored_entity = repo_tester.get_all(database, Genre)[0]
        assert stored_entity.rating == original_entity.rating
        assert stored_entity.name == "new name"
Exemplo n.º 13
0
def add_task(repo: Repository,
             change: TaskChanges) -> Union[RecurrentTask, Task]:
    """Create a new task.

    If it's a RecurrentTask, it returns the parent.
    """
    task: Optional[TaskType] = None

    if len(change.tags_to_add) > 0:
        change.task_attributes["tags"] = change.tags_to_add

    if change.task_attributes.get("recurrence_type", None) in [
            "recurring",
            "repeating",
    ]:
        task = repo.add(RecurrentTask(**change.task_attributes))
        child_task = repo.add(task.breed_children())

        log.info(f"Added {task.recurrence_type} task {task.id_}:"
                 f" {task.description}")
        log.info(f"Added first child task with id {child_task.id_}")
    else:
        task = repo.add(Task(**change.task_attributes))
        log.info(f"Added task {task.id_}: {task.description}")

    repo.commit()

    return task
Exemplo n.º 14
0
def insert_multiple_tasks(repo: Repository) -> List[Task]:
    """Insert three Tasks in the repository."""
    tasks = sorted(factories.TaskFactory.create_batch(20, state="backlog"))
    [repo.add(task) for task in tasks]
    repo.commit()

    return tasks
Exemplo n.º 15
0
def tasks_(repo: Repository) -> List[Task]:
    """Insert three Tasks in the FakeRepository."""
    tasks = sorted(factories.TaskFactory.create_batch(3, state="backlog"))
    for task in tasks:
        repo.add(task)
    repo.commit()

    return tasks
Exemplo n.º 16
0
def insert_frozen_parent_task_e2e(repo_e2e: Repository) -> RecurrentTask:
    """Insert a RecurrentTask in frozen state."""
    parent_task = factories.RecurrentTaskFactory.create(state="backlog")
    parent_task.freeze()
    repo_e2e.add(parent_task)
    repo_e2e.commit()

    return parent_task
Exemplo n.º 17
0
 def test_repository_handles_connection_errors(self, repo: Repository) -> None:
     """
     Given: A database url pointing to an inexistent file
     When: the repository is initialized with an inexistent directory
     Then: a ConnectionError is raised. This doesn't apply to FakeRepository as it
         doesn't create a database
     """
     with pytest.raises(ConnectionError):
         repo.__class__(database_url="/inexistent_dir/database.db")  # act
Exemplo n.º 18
0
    def test_repository_closes_connection(self, repo: Repository) -> None:
        """
        Given: A configured repository
        When: calling the close method
        Then: the connection to the database is closed.
        """
        repo.close()  # act

        assert repo.is_closed
Exemplo n.º 19
0
    def test_repository_close_is_idempotent(self, repo: Repository) -> None:
        """
        Given: A closed repository
        When: calling the close method
        Then: the connection to the database is closed and no exception is raised.
        """
        repo.close()

        repo.close()  # act

        assert repo.is_closed
Exemplo n.º 20
0
def insert_frozen_parent_tasks_e2e(
        repo_e2e: Repository) -> List[RecurrentTask]:
    """Insert many RecurrentTask in frozen state."""
    parent_tasks = factories.RecurrentTaskFactory.create_batch(3,
                                                               state="backlog")
    for parent_task in parent_tasks:
        parent_task.freeze()
        repo_e2e.add(parent_task)
    repo_e2e.commit()

    return parent_tasks
Exemplo n.º 21
0
def insert_tasks_e2e(repo_e2e: Repository) -> List[Task]:
    """Insert many tasks in the end to end repository."""
    tasks = factories.TaskFactory.create_batch(3, priority=3, state="backlog")
    different_task = factories.TaskFactory.create(priority=2, state="backlog")
    tasks.append(different_task)

    for task in tasks:
        repo_e2e.add(task)
        repo_e2e.commit()

    return tasks
Exemplo n.º 22
0
    def test_repository_can_search_by_bool_property(
        self,
        repo: Repository,
    ) -> None:
        """Search should return the objects that have a bool property."""
        expected_entity = BoolEntity(name="Name", active=True)
        repo.add(expected_entity)
        repo.commit()

        result = repo.search({"active": True}, BoolEntity)

        assert result == [expected_entity]
Exemplo n.º 23
0
    def test_print_tags_report(self, runner: CliRunner, repo_e2e: Repository,
                               insert_tasks_e2e: List[Task]) -> None:
        """Test that tags returns the expected output."""
        tasks = insert_tasks_e2e
        tasks[0].tags = ["tag1"]
        repo_e2e.add(tasks[0])
        repo_e2e.commit()

        result = runner.invoke(cli, ["tags"])

        assert result.exit_code == 0
        assert re.search(r" +Name.*Open Tasks", result.output)
Exemplo n.º 24
0
 def test_repository_doesnt_allow_adding_non_entity_types(
     self,
     repo: Repository,
     merge: bool,
 ) -> None:
     """
     Given: an empty repository.
     When: an object that is not an entity is added.
     Then: an error is returned.
     """
     with pytest.raises(ValueError,
                        match="Please add an entity or a list of entities"):
         repo.add(1, merge=merge)  # type: ignore
Exemplo n.º 25
0
    def test_repository_can_retrieve_an_entity_if_no_model_defined(
        self,
        repo: Repository,
        inserted_entity: Entity,
    ) -> None:
        """Given an entity_id the repository returns the entity object."""
        repo.models = [type(inserted_entity)]  # type: ignore
        with pytest.warns(UserWarning, match="In 2022-06-10.*deprecated"):

            result: Entity = repo.get(inserted_entity.id_)

        assert result == inserted_entity
        assert result.id_ == inserted_entity.id_
Exemplo n.º 26
0
 def test_repository_raises_error_if_no_entity_found_by_get(
     self,
     repo: Repository,
     entity: Entity,
 ) -> None:
     """As the entity is not inserted into the repository, it shouldn't be found."""
     with pytest.raises(
             EntityNotFoundError,
             match=(
                 f"There are no entities of type {entity.model_name} in the "
                 f"repository with id_ {entity.id_}."),
     ):
         repo.get(entity.id_, type(entity))
Exemplo n.º 27
0
    def test_repository_can_retrieve_an_entity_if_list_of_models_defined(
        self,
        repo: Repository,
        inserted_entity: Entity,
    ) -> None:
        """Given an entity_id the repository returns the entity object."""
        entity_models: List[Type[Entity]] = [type(inserted_entity), OtherEntity]
        repo.models = entity_models  # type: ignore
        with pytest.warns(UserWarning, match="In 2022-06-10.*deprecated"):

            result = repo.get(inserted_entity.id_, entity_models)

        assert result == inserted_entity
        assert result.id_ == inserted_entity.id_
Exemplo n.º 28
0
    def test_repository_can_search_by_property_specifying_a_list_of_types(
        self,
        repo: Repository,
        inserted_entities: List[Entity],
    ) -> None:
        """Search should return the objects that match the desired property."""
        entity_types: List[Type[Entity]] = [type(inserted_entities[0]), OtherEntity]
        expected_entity = inserted_entities[1]
        repo.models = entity_types  # type: ignore
        with pytest.warns(UserWarning, match="In 2022-06-10.*deprecated"):

            result = repo.search({"id_": expected_entity.id_}, entity_types)

        assert result == [expected_entity]
Exemplo n.º 29
0
    def test_repo_can_search_in_list_of_str_attribute(self, repo: Repository) -> None:
        """
        Given: A repository with an entity that contains an attribute with a list of str
        When: search is called with a regexp that  matches one of the list elements
        Then: the entity is returned
        """
        expected_entity = ListEntityFactory.build()
        repo.add(expected_entity)
        repo.commit()
        regexp = rf"{expected_entity.elements[0][:-1]}."

        result = repo.search({"elements": regexp}, ListEntity)

        assert result == [expected_entity]
Exemplo n.º 30
0
def insert_parent_tasks_e2e(
    repo_e2e: Repository, ) -> Tuple[List[RecurrentTask], List[Task]]:
    """Insert a RecurrentTask and it's children Task in the repository."""
    parent_tasks = factories.RecurrentTaskFactory.create_batch(3,
                                                               state="backlog")
    child_tasks = [
        parent_task.breed_children() for parent_task in parent_tasks
    ]

    [repo_e2e.add(parent_task) for parent_task in parent_tasks]
    [repo_e2e.add(child_task) for child_task in child_tasks]
    repo_e2e.commit()

    return parent_tasks, child_tasks