Exemplo n.º 1
0
async def test_train_add_remove_pull_idempotant(repository,
                                                monkepatched_traincar):
    t = merge_train.Train(repository, "branch")
    await t.load()

    config = get_config("five", priority=0)

    await t.add_pull(await fake_context(repository, 1), config)
    await t.add_pull(await fake_context(repository, 2), config)
    await t.add_pull(await fake_context(repository, 3), config)
    await t.refresh()
    assert [[1], [1, 2], [1, 2, 3]] == get_cars_content(t)

    config = get_config("five", priority=10)

    await t.add_pull(await fake_context(repository, 1), config)
    await t.refresh()
    assert [[1], [1, 2], [1, 2, 3]] == get_cars_content(t)

    t = merge_train.Train(repository, "branch")
    await t.load()
    assert [[1], [1, 2], [1, 2, 3]] == get_cars_content(t)

    await t.remove_pull(await fake_context(repository, 2))
    await t.refresh()
    assert [[1], [1, 3]] == get_cars_content(t)

    await t.remove_pull(await fake_context(repository, 2))
    await t.refresh()
    assert [[1], [1, 3]] == get_cars_content(t)

    t = merge_train.Train(repository, "branch")
    await t.load()
    assert [[1], [1, 3]] == get_cars_content(t)
Exemplo n.º 2
0
async def test_train_add_remove_pull_idempotant(
    repository: context.Repository, context_getter: conftest.ContextGetterFixture
) -> None:
    t = merge_train.Train(repository, github_types.GitHubRefType("main"))
    await t.load()

    config = get_config("5x1", priority=0)

    await t.add_pull(await context_getter(1), config)
    await t.add_pull(await context_getter(2), config)
    await t.add_pull(await context_getter(3), config)
    await t.refresh()
    assert [[1], [1, 2], [1, 2, 3]] == get_cars_content(t)

    config = get_config("5x1", priority=10)

    await t.add_pull(await context_getter(1), config)
    await t.refresh()
    assert [[1], [1, 2], [1, 2, 3]] == get_cars_content(t)

    t = merge_train.Train(repository, github_types.GitHubRefType("main"))
    await t.load()
    assert [[1], [1, 2], [1, 2, 3]] == get_cars_content(t)

    await t.remove_pull(await context_getter(2))
    await t.refresh()
    assert [[1], [1, 3]] == get_cars_content(t)

    await t.remove_pull(await context_getter(2))
    await t.refresh()
    assert [[1], [1, 3]] == get_cars_content(t)

    t = merge_train.Train(repository, github_types.GitHubRefType("main"))
    await t.load()
    assert [[1], [1, 3]] == get_cars_content(t)
Exemplo n.º 3
0
async def test_train_mutiple_queue(
    repository: context.Repository, context_getter: conftest.ContextGetterFixture
) -> None:
    t = merge_train.Train(repository, github_types.GitHubRefType("main"))
    await t.load()

    config_two = get_config("2x1", priority=0)
    config_five = get_config("5x1", priority=0)

    await t.add_pull(await context_getter(1), config_two)
    await t.add_pull(await context_getter(2), config_two)
    await t.add_pull(await context_getter(3), config_five)
    await t.add_pull(await context_getter(4), config_five)
    await t.refresh()
    assert [[1], [1, 2]] == get_cars_content(t)
    assert [3, 4] == get_waiting_content(t)

    # Ensure we don't got over the train_size
    await t.add_pull(await context_getter(5), config_two)
    await t.refresh()
    assert [[1], [1, 2]] == get_cars_content(t)
    assert [5, 3, 4] == get_waiting_content(t)

    await t.add_pull(await context_getter(6), config_five)
    await t.add_pull(await context_getter(7), config_five)
    await t.add_pull(await context_getter(8), config_five)
    await t.add_pull(await context_getter(9), config_five)
    await t.refresh()
    assert [[1], [1, 2]] == get_cars_content(t)
    assert [5, 3, 4, 6, 7, 8, 9] == get_waiting_content(t)

    t = merge_train.Train(repository, github_types.GitHubRefType("main"))
    await t.load()
    assert [[1], [1, 2]] == get_cars_content(t)
    assert [5, 3, 4, 6, 7, 8, 9] == get_waiting_content(t)

    await t.remove_pull(await context_getter(2))
    await t.refresh()
    assert [[1], [1, 5]] == get_cars_content(
        t
    ), f"{get_cars_content(t)} {get_waiting_content(t)}"
    assert [3, 4, 6, 7, 8, 9] == get_waiting_content(t)

    await t.remove_pull(await context_getter(1))
    await t.remove_pull(await context_getter(5))
    await t.refresh()
    assert [[3], [3, 4], [3, 4, 6], [3, 4, 6, 7], [3, 4, 6, 7, 8]] == get_cars_content(
        t
    )
    assert [9] == get_waiting_content(t)

    t = merge_train.Train(repository, github_types.GitHubRefType("main"))
    await t.load()
    assert [[3], [3, 4], [3, 4, 6], [3, 4, 6, 7], [3, 4, 6, 7, 8]] == get_cars_content(
        t
    )
    assert [9] == get_waiting_content(t)
Exemplo n.º 4
0
async def test_train_mutiple_queue(repository, monkepatched_traincar):
    t = merge_train.Train(repository, "branch")
    await t.load()

    config_two = get_config("two", priority=0)
    config_five = get_config("five", priority=0)

    await t.add_pull(await fake_context(repository, 1), config_two)
    await t.add_pull(await fake_context(repository, 2), config_two)
    await t.add_pull(await fake_context(repository, 3), config_five)
    await t.add_pull(await fake_context(repository, 4), config_five)
    await t.refresh()
    assert [[1], [1, 2]] == get_cars_content(t)
    assert [3, 4] == get_waiting_content(t)

    # Ensure we don't got over the train_size
    await t.add_pull(await fake_context(repository, 5), config_two)
    await t.refresh()
    assert [[1], [1, 2]] == get_cars_content(t)
    assert [5, 3, 4] == get_waiting_content(t)

    await t.add_pull(await fake_context(repository, 6), config_five)
    await t.add_pull(await fake_context(repository, 7), config_five)
    await t.add_pull(await fake_context(repository, 8), config_five)
    await t.add_pull(await fake_context(repository, 9), config_five)
    await t.refresh()
    assert [[1], [1, 2]] == get_cars_content(t)
    assert [5, 3, 4, 6, 7, 8, 9] == get_waiting_content(t)

    t = merge_train.Train(repository, "branch")
    await t.load()
    assert [[1], [1, 2]] == get_cars_content(t)
    assert [5, 3, 4, 6, 7, 8, 9] == get_waiting_content(t)

    await t.remove_pull(await fake_context(repository, 2))
    await t.refresh()
    assert [[1], [1, 5]] == get_cars_content(t)
    assert [3, 4, 6, 7, 8, 9] == get_waiting_content(t)

    await t.remove_pull(await fake_context(repository, 1))
    await t.remove_pull(await fake_context(repository, 5))
    await t.refresh()
    assert [[3], [3, 4], [3, 4, 6], [3, 4, 6, 7], [3, 4, 6, 7, 8]] == get_cars_content(
        t
    )
    assert [9] == get_waiting_content(t)

    t = merge_train.Train(repository, "branch")
    await t.load()
    assert [[3], [3, 4], [3, 4, 6], [3, 4, 6, 7], [3, 4, 6, 7, 8]] == get_cars_content(
        t
    )
    assert [9] == get_waiting_content(t)
Exemplo n.º 5
0
async def test_train_remove_duplicates(
    repository: context.Repository, context_getter: conftest.ContextGetterFixture
) -> None:
    t = merge_train.Train(repository, github_types.GitHubRefType("main"))
    await t.load()

    await t.add_pull(await context_getter(1), get_config("2x1", 1000))
    await t.add_pull(await context_getter(2), get_config("2x1", 1000))
    await t.add_pull(await context_getter(3), get_config("2x1", 1000))
    await t.add_pull(await context_getter(4), get_config("2x1", 1000))

    await t.refresh()
    assert [[1], [1, 2]] == get_cars_content(t)
    assert [3, 4] == get_waiting_content(t)

    # Insert bugs in queue
    t._waiting_pulls.extend(
        [
            merge_train.EmbarkedPull(
                t,
                t._cars[0].still_queued_embarked_pulls[0].user_pull_request_number,
                t._cars[0].still_queued_embarked_pulls[0].config,
                t._cars[0].still_queued_embarked_pulls[0].queued_at,
            ),
            t._waiting_pulls[0],
        ]
    )
    t._cars = t._cars + t._cars
    assert [[1], [1, 2], [1], [1, 2]] == get_cars_content(t)
    assert [1, 3, 3, 4] == get_waiting_content(t)

    # Everything should be back to normal
    await t.refresh()
    assert [[1], [1, 2]] == get_cars_content(t)
    assert [3, 4] == get_waiting_content(t)
Exemplo n.º 6
0
async def test_train_queue_config_deleted(
    report_failure: mock.Mock,
    repository: context.Repository,
    context_getter: conftest.ContextGetterFixture,
) -> None:
    t = merge_train.Train(repository, github_types.GitHubRefType("main"))
    await t.load()

    await t.add_pull(await context_getter(1), get_config("2x1", 1000))
    await t.add_pull(await context_getter(2), get_config("2x1", 1000))
    await t.add_pull(await context_getter(3), get_config("5x1", 1000))

    await t.refresh()
    assert [[1], [1, 2]] == get_cars_content(t)
    assert [3] == get_waiting_content(t)

    with mock.patch.object(
        sys.modules[__name__],
        "MERGIFY_CONFIG",
        """
queue_rules:
  - name: five
    conditions: []
    speculative_checks: 5
""",
    ):
        repository._caches.mergify_config.delete()
        repository._caches.mergify_config_file.delete()
        await t.refresh()
    assert [] == get_cars_content(t)
    assert [1, 2, 3] == get_waiting_content(t)
    assert len(report_failure.mock_calls) == 1
Exemplo n.º 7
0
async def test_train_priority_change(
    repository: context.Repository,
    context_getter: conftest.ContextGetterFixture,
) -> None:
    t = merge_train.Train(repository, github_types.GitHubRefType("main"))
    await t.load()

    await t.add_pull(await context_getter(1), get_config("2x1", 1000))
    await t.add_pull(await context_getter(2), get_config("2x1", 1000))
    await t.add_pull(await context_getter(3), get_config("2x1", 1000))

    await t.refresh()
    assert [[1], [1, 2]] == get_cars_content(t)
    assert [3] == get_waiting_content(t)

    assert (
        t._cars[0].still_queued_embarked_pulls[0].config["effective_priority"]
        == QUEUE_RULES["2x1"].config["priority"] * queue.QUEUE_PRIORITY_OFFSET + 1000
    )

    # NOTE(sileht): pull request got requeued with new configuration that don't
    # update the position but update the prio
    await t.add_pull(await context_getter(1), get_config("2x1", 2000))
    await t.refresh()
    assert [[1], [1, 2]] == get_cars_content(t)
    assert [3] == get_waiting_content(t)

    assert (
        t._cars[0].still_queued_embarked_pulls[0].config["effective_priority"]
        == QUEUE_RULES["2x1"].config["priority"] * queue.QUEUE_PRIORITY_OFFSET + 2000
    )
Exemplo n.º 8
0
async def test_train_disallow_checks_interruption_scenario_2(
    repository: context.Repository, context_getter: conftest.ContextGetterFixture
) -> None:
    t = merge_train.Train(repository, github_types.GitHubRefType("main"))
    await t.load()

    urgent = get_config("urgent-1x4")
    fastlane = get_config("fastlane-1x8-noint")
    regular = get_config("regular-1x8-noint-from-fastlane-and-regular")

    await t.add_pull(await context_getter(1), regular)
    await t.add_pull(await context_getter(2), regular)
    await t.refresh()
    assert [[1, 2]] == get_cars_content(t)
    assert [] == get_waiting_content(t)

    # fastlane doesn't interrupt the checks as
    # disallow_checks_interruption_from_queues of regular disallow it
    await t.add_pull(await context_getter(3), fastlane)
    await t.refresh()
    assert [[1, 2]] == get_cars_content(t)
    assert [3] == get_waiting_content(t)

    # fastlane doesn't interrupt the checks because of noint, but goes before
    # regular
    await t.add_pull(await context_getter(4), regular)
    await t.refresh()
    assert [[1, 2]] == get_cars_content(t)
    assert [3, 4] == get_waiting_content(t)

    # urgent breaks everything, then we put the fastlane one, and all regulars goes behind
    await t.add_pull(await context_getter(5), urgent)
    await t.refresh()
    assert [[5]] == get_cars_content(t)
    assert [3, 1, 2, 4] == get_waiting_content(t)
Exemplo n.º 9
0
async def test_train_interrupt_mixed_across_queue(
    repository: context.Repository, context_getter: conftest.ContextGetterFixture
) -> None:
    t = merge_train.Train(repository, github_types.GitHubRefType("main"))
    await t.load()

    config = get_config("low-1x5-noint")

    await t.add_pull(await context_getter(1), config)
    await t.refresh()
    assert [[1]] == get_cars_content(t)
    assert [] == get_waiting_content(t)

    await t.add_pull(await context_getter(2), config)
    await t.refresh()
    assert [[1]] == get_cars_content(t)
    assert [2] == get_waiting_content(t)

    await t.add_pull(await context_getter(3), config)
    await t.refresh()
    assert [[1]] == get_cars_content(t)
    assert [2, 3] == get_waiting_content(t)

    # Inserting pr in high queue always break started speculative checks
    await t.add_pull(await context_getter(4), get_config("high-1x2"))
    await t.refresh()
    assert [[4]] == get_cars_content(t)
    assert [1, 2, 3] == get_waiting_content(t)
Exemplo n.º 10
0
async def test_train_no_interrupt_add_pull(
    repository: context.Repository, context_getter: conftest.ContextGetterFixture
) -> None:
    t = merge_train.Train(repository, github_types.GitHubRefType("main"))
    await t.load()

    config = get_config("high-2x5-noint")

    await t.add_pull(await context_getter(1), config)
    await t.refresh()
    assert [[1]] == get_cars_content(t)
    assert [] == get_waiting_content(t)

    await t.add_pull(await context_getter(2), config)
    await t.refresh()
    assert [[1], [1, 2]] == get_cars_content(t)
    assert [] == get_waiting_content(t)

    await t.add_pull(await context_getter(3), config)
    await t.refresh()
    assert [[1], [1, 2]] == get_cars_content(t)
    assert [3] == get_waiting_content(t)

    # Inserting high prio didn't break started speculative checks, but the PR
    # move above other
    await t.add_pull(await context_getter(4), get_config("high-2x5-noint", 20000))
    await t.refresh()
    assert [[1], [1, 2]] == get_cars_content(t)
    assert [4, 3] == get_waiting_content(t)
Exemplo n.º 11
0
def test_train_batch_split(repository: context.Repository) -> None:
    now = datetime.datetime.utcnow()
    t = merge_train.Train(repository, github_types.GitHubRefType("main"))
    p1_two = merge_train.EmbarkedPull(
        t, github_types.GitHubPullRequestNumber(1), get_config("2x1"), now
    )
    p2_two = merge_train.EmbarkedPull(
        t, github_types.GitHubPullRequestNumber(2), get_config("2x1"), now
    )
    p3_two = merge_train.EmbarkedPull(
        t, github_types.GitHubPullRequestNumber(3), get_config("2x1"), now
    )
    p4_five = merge_train.EmbarkedPull(
        t, github_types.GitHubPullRequestNumber(4), get_config("5x1"), now
    )

    assert ([p1_two], [p2_two, p3_two, p4_five]) == t._get_next_batch(
        [p1_two, p2_two, p3_two, p4_five], "2x1", 1
    )
    assert ([p1_two, p2_two], [p3_two, p4_five]) == t._get_next_batch(
        [p1_two, p2_two, p3_two, p4_five], "2x1", 2
    )
    assert ([p1_two, p2_two, p3_two], [p4_five]) == t._get_next_batch(
        [p1_two, p2_two, p3_two, p4_five], "2x1", 10
    )
    assert ([], [p1_two, p2_two, p3_two, p4_five]) == t._get_next_batch(
        [p1_two, p2_two, p3_two, p4_five], "5x1", 10
    )
Exemplo n.º 12
0
async def test_train_add_remove_pull_idempotant(repository, monkepatched_traincar):
    t = merge_train.Train(repository, "branch")
    await t.load()

    config = queue.QueueConfig(
        name="foo",
        strict_method="merge",
        priority=0,
        effective_priority=0,
        bot_account=None,
        update_bot_account=None,
    )

    await t.add_pull(await fake_context(repository, 1), config)
    await t.add_pull(await fake_context(repository, 2), config)
    await t.add_pull(await fake_context(repository, 3), config)
    await t.refresh()
    assert [[1], [1, 2], [1, 2, 3]] == get_cars_content(t)

    config = queue.QueueConfig(
        name="foo",
        strict_method="merge",
        priority=10,
        effective_priority=10,
        bot_account=None,
        update_bot_account=None,
    )
    await t.add_pull(await fake_context(repository, 1), config)
    await t.refresh()
    assert [[1], [1, 2], [1, 2, 3]] == get_cars_content(t)

    t = merge_train.Train(repository, "branch")
    await t.load()
    assert [[1], [1, 2], [1, 2, 3]] == get_cars_content(t)

    await t.remove_pull(await fake_context(repository, 2))
    await t.refresh()
    assert [[1], [1, 3]] == get_cars_content(t)

    await t.remove_pull(await fake_context(repository, 2))
    await t.refresh()
    assert [[1], [1, 3]] == get_cars_content(t)

    t = merge_train.Train(repository, "branch")
    await t.load()
    assert [[1], [1, 3]] == get_cars_content(t)
Exemplo n.º 13
0
async def test_train_remove_middle_not_merged(repository, monkepatched_traincar):
    t = merge_train.Train(repository, "branch")
    await t.load()

    await t.add_pull(await fake_context(repository, 1), get_config("five", 1000))
    await t.add_pull(await fake_context(repository, 3), get_config("five", 100))
    await t.add_pull(await fake_context(repository, 2), get_config("five", 1000))

    await t.refresh()
    assert [[1], [1, 2], [1, 2, 3]] == get_cars_content(t)

    await t.remove_pull(await fake_context(repository, 2))
    await t.refresh()
    assert [[1], [1, 3]] == get_cars_content(t)
Exemplo n.º 14
0
async def test_train_remove_middle_not_merged(
    repository: context.Repository, context_getter: conftest.ContextGetterFixture
) -> None:
    t = merge_train.Train(repository, github_types.GitHubRefType("main"))
    await t.load()

    await t.add_pull(await context_getter(1), get_config("5x1", 1000))
    await t.add_pull(await context_getter(3), get_config("5x1", 100))
    await t.add_pull(await context_getter(2), get_config("5x1", 1000))

    await t.refresh()
    assert [[1], [1, 2], [1, 2, 3]] == get_cars_content(t)

    await t.remove_pull(await context_getter(2))
    await t.refresh()
    assert [[1], [1, 3]] == get_cars_content(t)
Exemplo n.º 15
0
async def test_train_remove_middle_merged(repository, monkepatched_traincar):
    t = merge_train.Train(repository, "branch")
    await t.load()

    config = get_config("five")
    await t.add_pull(await fake_context(repository, 1), config)
    await t.add_pull(await fake_context(repository, 2), config)
    await t.add_pull(await fake_context(repository, 3), config)
    await t.refresh()
    assert [[1], [1, 2], [1, 2, 3]] == get_cars_content(t)

    await t.remove_pull(
        await fake_context(repository, 2, merged=True, merge_commit_sha="new_sha1")
    )
    await t.refresh()
    assert [[1], [1, 3]] == get_cars_content(t)
Exemplo n.º 16
0
async def test_train_queue_pr_with_higher_prio_enters_in_queue_during_merging_2x5(
    report_failure: mock.Mock,
    repository: context.Repository,
    context_getter: conftest.ContextGetterFixture,
    fake_client: mock.Mock,
) -> None:
    t = merge_train.Train(repository, github_types.GitHubRefType("main"))
    await t.load()

    for i in range(41, 52):
        await t.add_pull(await context_getter(i), get_config("2x5", 1000))

    await t.refresh()
    assert [
        [41, 42, 43, 44, 45],
        [41, 42, 43, 44, 45, 46, 47, 48, 49, 50],
    ] == get_cars_content(t)
    assert [51] == get_waiting_content(t)

    t._cars[0].checks_conclusion = check_api.Conclusion.SUCCESS
    await t.save()
    await t.refresh()
    assert [
        [41, 42, 43, 44, 45],
        [41, 42, 43, 44, 45, 46, 47, 48, 49, 50],
    ] == get_cars_content(t)
    assert [51] == get_waiting_content(t)

    # merge half of the batch
    for i in range(41, 44):
        fake_client.update_base_sha(f"sha{i}")
        await t.remove_pull(
            await context_getter(i, merged=True, merge_commit_sha=f"sha{i}")
        )

    await t.refresh()
    assert [
        [44, 45],
        [41, 42, 43, 44, 45, 46, 47, 48, 49, 50],
    ] == get_cars_content(t)
    assert [51] == get_waiting_content(t)

    await t.add_pull(await context_getter(7), get_config("2x5", 2000))

    await t.refresh()
    assert [[44, 45], [44, 45, 7, 46, 47, 48, 49]] == get_cars_content(t)
    assert [50, 51] == get_waiting_content(t)
Exemplo n.º 17
0
async def test_train_queue_splitted_on_failure_1x2(
    report_failure: mock.Mock,
    repository: context.Repository,
    fake_client: mock.Mock,
    context_getter: conftest.ContextGetterFixture,
) -> None:
    t = merge_train.Train(repository, github_types.GitHubRefType("main"))
    await t.load()

    for i in range(41, 43):
        await t.add_pull(await context_getter(i), get_config("high-1x2", 1000))
    for i in range(6, 20):
        await t.add_pull(await context_getter(i), get_config("high-1x2", 1000))

    await t.refresh()
    assert [[41, 42]] == get_cars_content(t)
    assert list(range(6, 20)) == get_waiting_content(t)

    t._cars[0].checks_conclusion = check_api.Conclusion.FAILURE
    await t.save()
    assert [[41, 42]] == get_cars_content(t)
    assert list(range(6, 20)) == get_waiting_content(t)

    await t.load()
    await t.refresh()
    assert [
        [41],
        [41, 42],
    ] == get_cars_content(t)
    assert list(range(6, 20)) == get_waiting_content(t)
    assert len(t._cars[0].failure_history) == 1
    assert len(t._cars[1].failure_history) == 0
    assert t._cars[0].creation_state == "updated"
    assert t._cars[1].creation_state == "created"

    # mark [41] as failed
    t._cars[1].checks_conclusion = check_api.Conclusion.FAILURE
    await t.save()
    await t.remove_pull(await context_getter(41, merged=False))

    # It's 41 fault, we restart the train on 42
    await t.refresh()
    assert [[42, 6]] == get_cars_content(t)
    assert list(range(7, 20)) == get_waiting_content(t)
    assert len(t._cars[0].failure_history) == 0
    assert t._cars[0].creation_state == "created"  # type: ignore[comparison-overlap]
Exemplo n.º 18
0
async def test_train_remove_last_cars(
    repository: context.Repository, context_getter: conftest.ContextGetterFixture
) -> None:
    t = merge_train.Train(repository, github_types.GitHubRefType("main"))
    await t.load()

    await t.add_pull(await context_getter(1), get_config("high-1x1", 1000))
    await t.add_pull(await context_getter(2), get_config("high-1x1", 1000))
    await t.add_pull(await context_getter(3), get_config("high-1x1", 1000))

    await t.refresh()
    assert [[1]] == get_cars_content(t)
    assert [2, 3] == get_waiting_content(t)

    await t.remove_pull(await context_getter(1))
    await t.refresh()
    assert [[2]] == get_cars_content(t)
    assert [3] == get_waiting_content(t)
Exemplo n.º 19
0
async def test_train_remove_last_cars(repository, monkepatched_traincar):
    t = merge_train.Train(repository, "branch")
    await t.load()

    await t.add_pull(await fake_context(repository, 1),
                     get_config("one", 1000))
    await t.add_pull(await fake_context(repository, 2),
                     get_config("one", 1000))
    await t.add_pull(await fake_context(repository, 3),
                     get_config("one", 1000))

    await t.refresh()
    assert [[1]] == get_cars_content(t)
    assert [2, 3] == get_waiting_content(t)

    await t.remove_pull(await fake_context(repository, 1))
    await t.refresh()
    assert [[2]] == get_cars_content(t)
    assert [3] == get_waiting_content(t)
Exemplo n.º 20
0
async def test_train_remove_head_merged(
    repository: context.Repository, context_getter: conftest.ContextGetterFixture
) -> None:
    t = merge_train.Train(repository, github_types.GitHubRefType("main"))
    await t.load()

    config = get_config("5x1")

    await t.add_pull(await context_getter(1), config)
    await t.add_pull(await context_getter(2), config)
    await t.add_pull(await context_getter(3), config)
    await t.refresh()
    assert [[1], [1, 2], [1, 2, 3]] == get_cars_content(t)

    await t.remove_pull(
        await context_getter(1, merged=True, merge_commit_sha="new_sha1")
    )
    await t.refresh()
    assert [[1, 2], [1, 2, 3]] == get_cars_content(t)
Exemplo n.º 21
0
async def test_train_with_speculative_checks_decreased(
    repository: context.Repository, context_getter: conftest.ContextGetterFixture
) -> None:
    t = merge_train.Train(repository, github_types.GitHubRefType("main"))
    await t.load()

    config = get_config("5x1", 1000)
    await t.add_pull(await context_getter(1), config)

    QUEUE_RULES["5x1"].config["speculative_checks"] = 2

    await t.add_pull(await context_getter(2), config)
    await t.add_pull(await context_getter(3), config)
    await t.add_pull(await context_getter(4), config)
    await t.add_pull(await context_getter(5), config)

    await t.refresh()
    assert [[1], [1, 2], [1, 2, 3], [1, 2, 3, 4], [1, 2, 3, 4, 5]] == get_cars_content(
        t
    )
    assert [] == get_waiting_content(t)

    await t.remove_pull(
        await context_getter(1, merged=True, merge_commit_sha="new_sha1")
    )

    with mock.patch.object(
        sys.modules[__name__],
        "MERGIFY_CONFIG",
        """
queue_rules:
  - name: 5x1
    conditions: []
    speculative_checks: 2
""",
    ):
        repository._caches.mergify_config.delete()
        repository._caches.mergify_config_file.delete()
        await t.refresh()
    assert [[1, 2], [1, 2, 3]] == get_cars_content(t)
    assert [4, 5] == get_waiting_content(t)
Exemplo n.º 22
0
async def test_train_batch_max_wait_time(
    repository: context.Repository, context_getter: conftest.ContextGetterFixture
) -> None:
    with freeze_time("2021-09-22T08:00:00") as freezed_time:
        t = merge_train.Train(repository, github_types.GitHubRefType("main"))
        await t.load()

        config = get_config("batch-wait-time")

        await t.add_pull(await context_getter(1), config)
        await t.refresh()
        assert [] == get_cars_content(t)
        assert [1] == get_waiting_content(t)

        # Enought PR to batch!
        await t.add_pull(await context_getter(2), config)
        await t.refresh()
        assert [[1, 2]] == get_cars_content(t)
        assert [] == get_waiting_content(t)

        await t.add_pull(await context_getter(3), config)
        await t.refresh()
        assert [[1, 2]] == get_cars_content(t)
        assert [3] == get_waiting_content(t)

        d = await delayed_refresh._get_current_refresh_datetime(
            repository, github_types.GitHubPullRequestNumber(3)
        )
        assert d is not None
        assert d == freezed_time().replace(
            tzinfo=datetime.timezone.utc
        ) + datetime.timedelta(minutes=5)

    with freeze_time("2021-09-22T08:05:02"):
        await t.refresh()
        assert [[1, 2], [1, 2, 3]] == get_cars_content(t)
        assert [] == get_waiting_content(t)
Exemplo n.º 23
0
async def report(
    url: str,
) -> typing.Union[context.Context, github.AsyncGithubInstallationClient, None]:
    redis_links = redis_utils.RedisLinks(max_idle_time=0)

    try:
        owner_login, repo, pull_number = _url_parser(url)
    except ValueError:
        print(f"{url} is not valid")
        return None

    try:
        installation_json = await github.get_installation_from_login(
            owner_login)
        client = github.aget_client(installation_json)
    except exceptions.MergifyNotInstalled:
        print(f"* Mergify is not installed on account {owner_login}")
        return None

    # Do a dumb request just to authenticate
    await client.get("/")

    print(f"* INSTALLATION ID: {installation_json['id']}")

    if repo is None:
        slug = None
    else:
        slug = owner_login + "/" + repo

    owner_id = installation_json["account"]["id"]
    cached_sub = await subscription.Subscription.get_subscription(
        redis_links.cache, owner_id)
    db_sub = await subscription.Subscription._retrieve_subscription_from_db(
        redis_links.cache, owner_id)

    cached_tokens = await user_tokens.UserTokens.get(redis_links.cache,
                                                     owner_id)
    if config.SAAS_MODE:
        db_tokens = typing.cast(
            user_tokens.UserTokens,
            (await user_tokens.UserTokensSaas._retrieve_from_db(
                redis_links.cache, owner_id)),
        )
    else:
        db_tokens = cached_tokens

    print("* Features (db):")
    for v in sorted(f.value for f in db_sub.features):
        print(f"  - {v}")
    print("* Features (cache):")
    for v in sorted(f.value for f in cached_sub.features):
        print(f"  - {v}")

    installation = context.Installation(installation_json, cached_sub, client,
                                        redis_links)

    await report_dashboard_synchro(installation.installation["id"], cached_sub,
                                   cached_tokens, "ENGINE-CACHE", slug)
    await report_dashboard_synchro(installation.installation["id"], db_sub,
                                   db_tokens, "DASHBOARD", slug)

    await report_worker_status(owner_login)

    if repo is not None:
        repository = await installation.get_repository_by_name(repo)

        print(
            f"* REPOSITORY IS {'PRIVATE' if repository.repo['private'] else 'PUBLIC'}"
        )

        print(f"* DEFAULT BRANCH: {repository.repo['default_branch']}")

        print("* CONFIGURATION:")
        mergify_config = None
        config_file = await repository.get_mergify_config_file()
        if not config_file:
            print(".mergify.yml is missing")
        else:
            print(f"Config filename: {config_file['path']}")
            print(config_file["decoded_content"])
            try:
                mergify_config = await repository.get_mergify_config()
            except rules.InvalidRules as e:  # pragma: no cover
                print(f"configuration is invalid {str(e)}")

        if pull_number is None:
            async for branch in typing.cast(
                    typing.AsyncGenerator[github_types.GitHubBranch, None],
                    client.items(
                        f"/repos/{owner_login}/{repo}/branches",
                        resource_name="branches",
                        page_limit=100,
                    ),
            ):
                q = merge_train.Train(repository, branch["name"])
                await q.load()
                await report_queue("TRAIN", q)

        else:
            repository = await installation.get_repository_by_name(
                github_types.GitHubRepositoryName(repo))
            try:
                ctxt = await repository.get_pull_request_context(
                    github_types.GitHubPullRequestNumber(int(pull_number)))
            except http.HTTPNotFound:
                print(f"Pull request `{url}` does not exist")
                return client

            # FIXME queues could also be printed if no pull number given
            # TODO(sileht): display train if any
            q = await merge_train.Train.from_context(ctxt)
            print(
                f"* TRAIN: {', '.join([f'#{p}' for p in await q.get_pulls()])}"
            )
            print("* PULL REQUEST:")
            pr_data = await ctxt.pull_request.items()
            pprint.pprint(pr_data, width=160)

            is_behind = await ctxt.is_behind
            print(f"is_behind: {is_behind}")

            print(f"mergeable_state: {ctxt.pull['mergeable_state']}")

            print("* MERGIFY LAST CHECKS:")
            for c in await ctxt.pull_engine_check_runs:
                print(
                    f"[{c['name']}]: {c['conclusion']} | {c['output'].get('title')} | {c['html_url']}"
                )
                print("> " + "\n> ".join(
                    ("No Summary", ) if c["output"]["summary"] is None else
                    c["output"]["summary"].split("\n")))

            if mergify_config is not None:
                print("* MERGIFY LIVE MATCHES:")
                pull_request_rules = mergify_config["pull_request_rules"]
                match = await pull_request_rules.get_pull_request_rule(ctxt)
                summary_title, summary = await actions_runner.gen_summary(
                    ctxt, pull_request_rules, match)
                print(f"[Summary]: success | {summary_title}")
                print("> " + "\n> ".join(summary.strip().split("\n")))

            return ctxt

    return client
Exemplo n.º 24
0
async def report(
    url: str,
) -> typing.Union[context.Context, github.AsyncGithubInstallationClient, None]:
    redis_cache = utils.create_aredis_for_cache(max_idle_time=0)

    try:
        owner, repo, pull_number = _url_parser(url)
    except ValueError:
        print(f"{url} is not valid")
        return None

    try:
        client = github.aget_client(owner)
    except exceptions.MergifyNotInstalled:
        print(f"* Mergify is not installed on account {owner}")
        return None

    # Do a dumb request just to authenticate
    await client.get("/")

    if client.auth.installation is None:
        print("No installation detected")
        return None

    print(f"* INSTALLATION ID: {client.auth.installation['id']}")

    if client.auth.owner_id is None:
        raise RuntimeError("Unable to get owner_id")

    if repo is None:
        slug = None
    else:
        slug = owner + "/" + repo

    cached_sub = await subscription.Subscription.get_subscription(
        redis_cache, client.auth.owner_id)
    db_sub = await subscription.Subscription._retrieve_subscription_from_db(
        redis_cache, client.auth.owner_id)

    cached_tokens = await user_tokens.UserTokens.get(redis_cache,
                                                     client.auth.owner_id)
    db_tokens = await user_tokens.UserTokens._retrieve_from_db(
        redis_cache, client.auth.owner_id)

    print(f"* SUBSCRIBED (cache/db): {cached_sub.active} / {db_sub.active}")
    print("* Features (cache):")
    for f in db_sub.features:
        print(f"  - {f.value}")
    print("* Features (db):")
    for f in cached_sub.features:
        print(f"  - {f.value}")

    await report_dashboard_synchro(client.auth.installation["id"], cached_sub,
                                   cached_tokens, "ENGINE-CACHE", slug)
    await report_dashboard_synchro(client.auth.installation["id"], db_sub,
                                   db_tokens, "DASHBOARD", slug)

    await report_worker_status(owner)

    installation = context.Installation(client.auth.owner_id, owner,
                                        cached_sub, client, redis_cache)

    if repo is not None:
        repo_info: github_types.GitHubRepository = await client.item(
            f"/repos/{owner}/{repo}")
        repository = context.Repository(installation, repo_info["name"],
                                        repo_info["id"])

        print(
            f"* REPOSITORY IS {'PRIVATE' if repo_info['private'] else 'PUBLIC'}"
        )

        print("* CONFIGURATION:")
        mergify_config = None
        config_file = await repository.get_mergify_config_file()
        if config_file is None:
            print(".mergify.yml is missing")
        else:
            print(f"Config filename: {config_file['path']}")
            print(config_file["decoded_content"].decode())
            try:
                mergify_config = rules.get_mergify_config(config_file)
            except rules.InvalidRules as e:  # pragma: no cover
                print(f"configuration is invalid {str(e)}")
            else:
                mergify_config["pull_request_rules"].rules.extend(
                    engine.MERGIFY_BUILTIN_CONFIG["pull_request_rules"].rules)

        if pull_number is None:
            async for branch in typing.cast(
                    typing.AsyncGenerator[github_types.GitHubBranch, None],
                    client.items(f"/repos/{owner}/{repo}/branches"),
            ):
                # TODO(sileht): Add some informations on the train
                q: queue.QueueBase = naive.Queue(repository, branch["name"])
                await report_queue("QUEUES", q)

                q = merge_train.Train(repository, branch["name"])
                await q.load()
                await report_queue("TRAIN", q)

        else:
            repository = context.Repository(
                installation, github_types.GitHubRepositoryName(repo))
            ctxt = await repository.get_pull_request_context(
                github_types.GitHubPullRequestNumber(int(pull_number)))

            # FIXME queues could also be printed if no pull number given
            # TODO(sileht): display train if any
            q = await naive.Queue.from_context(ctxt)
            print(
                f"* QUEUES: {', '.join([f'#{p}' for p in await q.get_pulls()])}"
            )
            q = await merge_train.Train.from_context(ctxt)
            print(
                f"* TRAIN: {', '.join([f'#{p}' for p in await q.get_pulls()])}"
            )
            print("* PULL REQUEST:")
            pr_data = await ctxt.pull_request.items()
            pprint.pprint(pr_data, width=160)

            is_behind = await ctxt.is_behind
            print(f"is_behind: {is_behind}")

            print(f"mergeable_state: {ctxt.pull['mergeable_state']}")

            print("* MERGIFY LAST CHECKS:")
            for c in await ctxt.pull_engine_check_runs:
                print(
                    f"[{c['name']}]: {c['conclusion']} | {c['output'].get('title')}"
                )
                print("> " + "\n> ".join(
                    ("No Summary", ) if c["output"]["summary"] is None else
                    c["output"]["summary"].split("\n")))

            if mergify_config is not None:
                print("* MERGIFY LIVE MATCHES:")
                match = await mergify_config["pull_request_rules"
                                             ].get_pull_request_rule(ctxt)
                summary_title, summary = await actions_runner.gen_summary(
                    ctxt, match)
                print(f"[Summary]: success | {summary_title}")
                print("> " + "\n> ".join(summary.strip().split("\n")))

            return ctxt

    return client
Exemplo n.º 25
0
async def test_train_queue_splitted_on_failure_5x3(
    report_failure: mock.Mock,
    repository: context.Repository,
    context_getter: conftest.ContextGetterFixture,
    fake_client: mock.Mock,
) -> None:
    t = merge_train.Train(repository, github_types.GitHubRefType("main"))
    await t.load()

    for i in range(41, 47):
        await t.add_pull(await context_getter(i), get_config("5x3", 1000))
    for i in range(7, 22):
        await t.add_pull(await context_getter(i), get_config("5x3", 1000))

    await t.refresh()
    assert [
        [41, 42, 43],
        [41, 42, 43, 44, 45, 46],
        [41, 42, 43, 44, 45, 46, 7, 8, 9],
        [41, 42, 43, 44, 45, 46, 7, 8, 9, 10, 11, 12],
        [41, 42, 43, 44, 45, 46, 7, 8, 9, 10, 11, 12, 13, 14, 15],
    ] == get_cars_content(t)
    assert list(range(16, 22)) == get_waiting_content(t)

    t._cars[0].checks_conclusion = check_api.Conclusion.FAILURE
    await t.save()
    assert [
        [41, 42, 43],
        [41, 42, 43, 44, 45, 46],
        [41, 42, 43, 44, 45, 46, 7, 8, 9],
        [41, 42, 43, 44, 45, 46, 7, 8, 9, 10, 11, 12],
        [41, 42, 43, 44, 45, 46, 7, 8, 9, 10, 11, 12, 13, 14, 15],
    ] == get_cars_content(t)
    assert list(range(16, 22)) == get_waiting_content(t)

    await t.load()
    await t.refresh()
    assert [
        [41],
        [41, 42],
        [41, 42, 43],
    ] == get_cars_content(t)
    assert [44, 45, 46] + list(range(7, 22)) == get_waiting_content(t)
    assert len(t._cars[0].failure_history) == 1
    assert len(t._cars[1].failure_history) == 1
    assert len(t._cars[2].failure_history) == 0

    # mark [41] as failed
    t._cars[0].checks_conclusion = check_api.Conclusion.FAILURE
    await t.save()
    await t.remove_pull(await context_getter(41, merged=False))

    # nothing should move yet as we don't known yet if [41+42] is broken or not
    await t.refresh()
    assert [
        [42, 43, 44],
        [42, 43, 44, 45, 46, 7],
        [42, 43, 44, 45, 46, 7, 8, 9, 10],
        [42, 43, 44, 45, 46, 7, 8, 9, 10, 11, 12, 13],
        [42, 43, 44, 45, 46, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
    ] == get_cars_content(t)
    assert list(range(17, 22)) == get_waiting_content(t)
    assert len(t._cars[0].failure_history) == 0
    assert len(t._cars[1].failure_history) == 0
    assert len(t._cars[2].failure_history) == 0
    assert len(t._cars[3].failure_history) == 0
    assert len(t._cars[4].failure_history) == 0

    # mark [42+43+44] as ready and merge it
    t._cars[0].checks_conclusion = check_api.Conclusion.SUCCESS
    t._cars[1].checks_conclusion = check_api.Conclusion.FAILURE
    await t.save()
    fake_client.update_base_sha("sha42")
    await t.remove_pull(await context_getter(42, merged=True, merge_commit_sha="sha42"))
    fake_client.update_base_sha("sha43")
    await t.remove_pull(await context_getter(43, merged=True, merge_commit_sha="sha43"))
    fake_client.update_base_sha("sha44")
    await t.remove_pull(await context_getter(44, merged=True, merge_commit_sha="sha44"))

    await t.refresh()
    assert [
        [42, 43, 44, 45],
        [42, 43, 44, 45, 46],
        [42, 43, 44, 45, 46, 7],
    ] == get_cars_content(t)
    assert list(range(8, 22)) == get_waiting_content(t)
    assert len(t._cars[0].failure_history) == 1
    assert len(t._cars[1].failure_history) == 1
    assert len(t._cars[2].failure_history) == 0

    # mark [45] and [46+46] as success, so it's 7 fault !
    t._cars[0].checks_conclusion = check_api.Conclusion.SUCCESS
    t._cars[1].checks_conclusion = check_api.Conclusion.SUCCESS
    await t.save()

    # Nothing change yet!
    await t.refresh()
    assert [
        [42, 43, 44, 45],
        [42, 43, 44, 45, 46],
        [42, 43, 44, 45, 46, 7],
    ] == get_cars_content(t)
    assert list(range(8, 22)) == get_waiting_content(t)
    assert len(t._cars[0].failure_history) == 1
    assert len(t._cars[1].failure_history) == 1
    assert len(t._cars[2].failure_history) == 0
    # Merge 45 and 46
    fake_client.update_base_sha("sha45")
    await t.remove_pull(await context_getter(45, merged=True, merge_commit_sha="sha45"))
    fake_client.update_base_sha("sha46")
    await t.remove_pull(await context_getter(46, merged=True, merge_commit_sha="sha46"))
    await t.refresh()
    assert [
        [42, 43, 44, 45, 46, 7],
    ] == get_cars_content(t)
    assert t._cars[0].checks_conclusion == check_api.Conclusion.FAILURE
    assert len(t._cars[0].failure_history) == 0

    # remove the failed 7
    await t.remove_pull(await context_getter(7, merged=False))

    # Train got cut after 43, and we restart from the begining
    await t.refresh()
    assert [
        [8, 9, 10],
        [8, 9, 10, 11, 12, 13],
        [8, 9, 10, 11, 12, 13, 14, 15, 16],
        [8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
        [8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21],
    ] == get_cars_content(t)
    assert [] == get_waiting_content(t)
Exemplo n.º 26
0
async def test_train_queue_splitted_on_failure_2x5(
    report_failure: mock.Mock,
    repository: context.Repository,
    fake_client: mock.Mock,
    context_getter: conftest.ContextGetterFixture,
) -> None:
    t = merge_train.Train(repository, github_types.GitHubRefType("main"))
    await t.load()

    for i in range(41, 46):
        await t.add_pull(await context_getter(i), get_config("2x5", 1000))
    for i in range(6, 20):
        await t.add_pull(await context_getter(i), get_config("2x5", 1000))

    await t.refresh()
    assert [
        [41, 42, 43, 44, 45],
        [41, 42, 43, 44, 45, 6, 7, 8, 9, 10],
    ] == get_cars_content(t)
    assert list(range(11, 20)) == get_waiting_content(t)

    t._cars[0].checks_conclusion = check_api.Conclusion.FAILURE
    await t.save()
    assert [
        [41, 42, 43, 44, 45],
        [41, 42, 43, 44, 45, 6, 7, 8, 9, 10],
    ] == get_cars_content(t)
    assert list(range(11, 20)) == get_waiting_content(t)

    await t.load()
    await t.refresh()
    assert [
        [41, 42],
        [41, 42, 43, 44],
        [41, 42, 43, 44, 45],
    ] == get_cars_content(t)
    assert list(range(6, 20)) == get_waiting_content(t)
    assert len(t._cars[0].failure_history) == 1
    assert len(t._cars[1].failure_history) == 1
    assert len(t._cars[2].failure_history) == 0
    assert t._cars[0].creation_state == "created"
    assert t._cars[1].creation_state == "created"
    assert t._cars[2].creation_state == "created"

    # mark [43+44] as failed
    t._cars[1].checks_conclusion = check_api.Conclusion.FAILURE
    await t.save()

    # nothing should move yet as we don't known yet if [41+42] is broken or not
    await t.refresh()
    assert [
        [41, 42],
        [41, 42, 43, 44],
        [41, 42, 43, 44, 45],
    ] == get_cars_content(t)
    assert list(range(6, 20)) == get_waiting_content(t)
    assert len(t._cars[0].failure_history) == 1
    assert len(t._cars[1].failure_history) == 1
    assert len(t._cars[2].failure_history) == 0
    assert t._cars[0].creation_state == "created"
    assert t._cars[1].creation_state == "created"
    assert t._cars[2].creation_state == "created"

    # mark [41+42] as ready and merge it
    t._cars[0].checks_conclusion = check_api.Conclusion.SUCCESS
    await t.save()
    fake_client.update_base_sha("sha41")
    await t.remove_pull(await context_getter(41, merged=True, merge_commit_sha="sha41"))
    fake_client.update_base_sha("sha42")
    await t.remove_pull(await context_getter(42, merged=True, merge_commit_sha="sha42"))

    # [43+44] fail, so it's not 45, but is it 43 or 44?
    await t.refresh()
    assert [
        [41, 42, 43],
        [41, 42, 43, 44],
    ] == get_cars_content(t)
    assert [45] + list(range(6, 20)) == get_waiting_content(t)
    assert len(t._cars[0].failure_history) == 2
    assert len(t._cars[1].failure_history) == 1
    assert t._cars[0].creation_state == "created"
    assert t._cars[1].creation_state == "created"

    # mark [43] as failure
    t._cars[0].checks_conclusion = check_api.Conclusion.FAILURE
    await t.save()
    await t.remove_pull(await context_getter(43, merged=False))

    # Train got cut after 43, and we restart from the begining
    await t.refresh()
    assert [
        [44, 45, 6, 7, 8],
        [44, 45, 6, 7, 8, 9, 10, 11, 12, 13],
    ] == get_cars_content(t)
    assert list(range(14, 20)) == get_waiting_content(t)
    assert len(t._cars[0].failure_history) == 0
    assert len(t._cars[1].failure_history) == 0
    assert t._cars[0].creation_state == "created"
    assert t._cars[1].creation_state == "created"