tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 29488c86661c4ae5d60ea6159965b07f38776ea4
parent 1015047dbd471e4c0b9d09da8490b97b9b825c1b
Author: Andrew Halberstadt <ahal@mozilla.com>
Date:   Fri, 24 Oct 2025 13:20:21 +0000

Bug 1996098 - Add tests for gecko_taskgraph actions, r=taskgraph-reviewers,bhearsum

Differential Revision: https://phabricator.services.mozilla.com/D269847

Diffstat:
Mpython/sites/python-test.txt | 1+
Mtaskcluster/gecko_taskgraph/test/python.toml | 2++
Ataskcluster/gecko_taskgraph/test/test_actions.py | 803+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 806 insertions(+), 0 deletions(-)

diff --git a/python/sites/python-test.txt b/python/sites/python-test.txt @@ -10,6 +10,7 @@ pypi:docutils==0.18.1 pypi:fluent.pygments==1.0 pypi:imagesize==1.4.1 pypi:pytest-mock==3.15.0 +pypi:pytest-taskgraph==0.2.0 pypi:pytest==8.4.2 pypi:pytz==2022.7.1 pypi:rstcheck==6.2.4 diff --git a/taskcluster/gecko_taskgraph/test/python.toml b/taskcluster/gecko_taskgraph/test/python.toml @@ -1,6 +1,8 @@ [DEFAULT] subsuite = "taskgraph" +["test_actions.py"] + ["test_actions_util.py"] ["test_decision.py"] diff --git a/taskcluster/gecko_taskgraph/test/test_actions.py b/taskcluster/gecko_taskgraph/test/test_actions.py @@ -0,0 +1,803 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +import pytest +import yaml +from mozunit import main +from pytest_taskgraph import make_graph, make_task +from taskgraph import create +from taskgraph.util import json +from taskgraph.util.taskcluster import get_task_definition + +from gecko_taskgraph import decision +from gecko_taskgraph.actions import trigger_action_callback + +ROOT_URL = "https://taskcluster.example.com" + + +@pytest.fixture(autouse=True) +def mock_root_url(monkeypatch): + monkeypatch.delenv("TASKCLUSTER_PROXY_URL", raising=False) + monkeypatch.setenv("TASKCLUSTER_ROOT_URL", ROOT_URL) + + +@pytest.fixture(autouse=True) +def clear_caches(): + yield + get_task_definition.cache_clear() + + +@pytest.fixture +def artifact_dir(monkeypatch, tmp_path): + artifact_dir = tmp_path / "artifacts" + monkeypatch.setattr(decision, "ARTIFACTS_DIR", str(artifact_dir)) + return artifact_dir + + +@pytest.fixture +def get_artifact(artifact_dir): + def inner(artifact_name): + return json.loads((artifact_dir / artifact_name).read_text()) + + return inner + + +@pytest.fixture +def run_action(mocker, monkeypatch, parameters, graph_config): + monkeypatch.setattr(create, "testing", True) + mocker.patch("gecko_taskgraph.actions.registry.sanity_check_task_scope") + + def inner(name, params=None, **kwargs): + if params: + parameters.update(params) + + kwargs.setdefault("task_group_id", "gid") + kwargs.setdefault("task_id", "tid") + kwargs.setdefault("input", None) + ret = trigger_action_callback( + callback=name, + parameters=parameters, + root=graph_config.root_dir, + **kwargs, + ) + return ret + + return inner + + +def test_cancel(responses, run_action): + task_id = "abc" + + responses.post(f"{ROOT_URL}/api/queue/v1/task/{task_id}/cancel", status=200) + + run_action("cancel", task_id=task_id, input={"task_id": task_id}) + + +def test_cancel_all(monkeypatch, responses, run_action): + group_id = "abc" + + # Validate action task doesn't cancel itself. + monkeypatch.setenv("TASK_ID", group_id) + + responses.get( + f"{ROOT_URL}/api/queue/v1/task-group/{group_id}/list", + status=200, + json={ + "tasks": [ + {"status": {"taskId": group_id, "state": "running"}}, + {"status": {"taskId": "a", "state": "running"}}, + {"status": {"taskId": "b", "state": "completed"}}, + {"status": {"taskId": "c", "state": "pending"}}, + {"status": {"taskId": "d", "state": "unscheduled"}}, + ] + }, + ) + + responses.post(f"{ROOT_URL}/api/queue/v1/task/a/cancel", status=200) + responses.post(f"{ROOT_URL}/api/queue/v1/task/c/cancel", status=200) + responses.post(f"{ROOT_URL}/api/queue/v1/task/d/cancel", status=200) + + run_action( + "cancel-all", + task_group_id=group_id, + input={"task_group_id": group_id}, + ) + + +def test_rebuild_cached_tasks(mocker, run_action, get_artifact): + graph = make_graph( + make_task( + label="foo", attributes={"cached_task": True}, task_def={"name": "foo"} + ), + make_task(label="bar", task_def={"name": "bar"}), + ) + m = mocker.patch( + "gecko_taskgraph.actions.rebuild_cached_tasks.fetch_graph_and_labels" + ) + m.return_value = ( + "gid", + graph, + {label: "tid" for label in graph.tasks.keys()}, + None, + ) + + run_action("rebuild-cached-tasks") + to_run = get_artifact("to-run.json") + assert "foo" in to_run + assert "bar" not in to_run + + +def test_add_new_jobs(mocker, run_action, get_artifact): + graph = make_graph( + make_task(label="foo", task_def={"name": "foo"}), + make_task(label="bar", task_def={"name": "bar"}), + ) + m = mocker.patch("gecko_taskgraph.actions.add_new_jobs.fetch_graph_and_labels") + m.return_value = ("gid", graph, {}, None) + + run_action("add-new-jobs", input={"tasks": ["foo"], "times": 1}) + + to_run = get_artifact("to-run.json") + assert "foo" in to_run + assert "bar" not in to_run + + +def test_add_talos(mocker, run_action, get_artifact): + graph = make_graph( + make_task( + label="test-linux-talos", + attributes={"talos_try_name": "talos"}, + task_def={"name": "test-linux-talos"}, + ), + make_task(label="build", task_def={"name": "build"}), + ) + m = mocker.patch("gecko_taskgraph.actions.add_talos.fetch_graph_and_labels") + m.return_value = ("gid", graph, {}, None) + mocker.patch("gecko_taskgraph.actions.add_talos.standard_filter", return_value=True) + + run_action("run-all-talos", input={"times": 1}) + + to_run = get_artifact("to-run.json") + assert "test-linux-talos" in to_run + assert "build" not in to_run + + +def test_purge_caches(responses, run_action): + task_id = "abc" + task_def = { + "payload": {"cache": {"cache1": "path1"}}, + "provisionerId": "proj-gecko", + "workerType": "linux", + } + + responses.get( + f"{ROOT_URL}/api/queue/v1/task/{task_id}", + status=200, + json=task_def, + ) + responses.post( + f"{ROOT_URL}/api/purge-cache/v1/purge-cache/proj-gecko%2Flinux", + status=200, + json={}, + ) + + run_action("purge-cache", task_id=task_id) + + +def test_openh264(mocker, run_action, get_artifact): + graph = make_graph( + make_task( + label="openh264-build", kind="openh264", task_def={"name": "openh264-build"} + ), + make_task(label="build", kind="build", task_def={"name": "build"}), + ) + m = mocker.patch("gecko_taskgraph.actions.openh264.fetch_graph_and_labels") + m.return_value = ("gid", graph, {}, None) + + run_action("openh264") + + to_run = get_artifact("to-run.json") + assert "openh264-build" in to_run + assert "build" not in to_run + + +def test_googleplay(mocker, run_action, get_artifact): + graph = make_graph( + make_task( + label="push-fenix", + kind="push-bundle", + attributes={"build-type": "fenix-nightly"}, + task_def={"name": "push-fenix"}, + ), + make_task(label="build", kind="build", task_def={"name": "build"}), + ) + m = mocker.patch("gecko_taskgraph.actions.googleplay.fetch_graph_and_labels") + m.return_value = ("gid", graph, {}, None) + + run_action("googleplay", params={"project": "mozilla-central"}) + + to_run = get_artifact("to-run.json") + assert "push-fenix" in to_run + assert "build" not in to_run + + +def test_raptor_extra_options(mocker, responses, run_action, get_artifact): + task_id = "tid" + task_def = { + "metadata": {"name": "test-raptor"}, + "payload": {"env": {}}, + "extra": {"treeherder": {"symbol": "rap", "groupName": "Raptor"}}, + } + graph = make_graph(make_task(label="test-raptor", task_def=task_def)) + + responses.get( + f"{ROOT_URL}/api/queue/v1/task/{task_id}", + status=200, + json=task_def, + ) + m = mocker.patch( + "gecko_taskgraph.actions.raptor_extra_options.fetch_graph_and_labels" + ) + m.return_value = ("gid", graph, {"test-raptor": "tid"}, None) + + run_action( + "raptor-extra-options", task_id=task_id, input={"extra_options": "verbose"} + ) + + to_run = get_artifact("to-run.json") + assert "test-raptor" in to_run + + +def test_run_missing_tests(mocker, responses, run_action, get_artifact): + graph = make_graph( + make_task(label="test-foo", kind="test", task_def={"name": "test-foo"}), + make_task(label="test-bar", kind="test", task_def={"name": "test-bar"}), + make_task(label="build", kind="build", task_def={"name": "build"}), + ) + m = mocker.patch("gecko_taskgraph.actions.run_missing_tests.fetch_graph_and_labels") + m.return_value = ("gid", graph, {"test-foo": "tid1"}, None) + + responses.get( + f"{ROOT_URL}/api/queue/v1/task/gid/artifacts/public%2Ftarget-tasks.json", + status=200, + json={"test-foo": {}, "test-bar": {}}, + ) + + run_action("run-missing-tests") + + to_run = get_artifact("to-run.json") + assert "test-bar" in to_run + assert "test-foo" not in to_run + assert "build" not in to_run + + +def test_scriptworker_canary(mocker, run_action, graph_config): + m = mocker.patch("gecko_taskgraph.actions.scriptworker_canary.taskgraph_decision") + + run_action("scriptworker-canary", input={"scriptworkers": ["balrog", "shipit"]}) + + m.assert_called_once() + args, kwargs = m.call_args + assert args[0] == {"root": graph_config.root_dir} + assert kwargs["parameters"]["target_tasks_method"] == "scriptworker_canary" + assert kwargs["parameters"]["try_task_config"] == { + "scriptworker-canary-workers": ["balrog", "shipit"] + } + assert kwargs["parameters"]["tasks_for"] == "action" + + +def test_merge_automation(mocker, run_action, graph_config): + m = mocker.patch("gecko_taskgraph.actions.merge_automation.taskgraph_decision") + + run_action( + "merge-automation", + params={"project": "mozilla-central"}, + input={"behavior": "bump-main"}, + ) + + m.assert_called_once() + args, kwargs = m.call_args + assert args[0] == {"root": graph_config.root_dir} + assert kwargs["parameters"]["target_tasks_method"] == "merge_automation" + assert kwargs["parameters"]["merge_config"] == { + "force-dry-run": False, + "behavior": "bump-main", + } + assert kwargs["parameters"]["tasks_for"] == "action" + + +def test_retrigger(mocker, responses, run_action, get_artifact): + task_def = { + "metadata": {"name": "test-task"}, + "payload": {}, + } + graph = make_graph( + make_task(label="test-task", attributes={"retrigger": True}, task_def=task_def) + ) + + responses.get( + f"{ROOT_URL}/api/queue/v1/task/tid", + status=200, + json=task_def, + ) + m = mocker.patch("gecko_taskgraph.actions.retrigger.fetch_graph_and_labels") + m.return_value = ("gid", graph, {}, None) + + run_action("retrigger", input={"force": True}) + + to_run = get_artifact("to-run.json") + assert "test-task" in to_run + + +def test_retrigger_custom(mocker, responses, run_action, capsys): + task_def = { + "metadata": {"name": "test-mochitest"}, + "payload": {"command": ["run"], "env": {}}, + "tags": {"test-type": "mochitest"}, + "extra": {"treeherder": {"symbol": "M"}}, + } + graph = make_graph(make_task(label="test-mochitest", task_def=task_def)) + + responses.get( + f"{ROOT_URL}/api/queue/v1/task/tid", + status=200, + json=task_def, + ) + m = mocker.patch("gecko_taskgraph.actions.retrigger_custom.fetch_graph_and_labels") + m.return_value = ("gid", graph, {"test-mochitest": "dtid"}, None) + + run_action("retrigger-custom", input={"path": "test/path"}) + + captured = capsys.readouterr() + assert "test-mochitest" in captured.out + assert "--no-run-tests" in captured.out + assert "test/path" in captured.out + assert "M-custom" in captured.out + + +def test_create_interactive(mocker, responses, monkeypatch, run_action, get_artifact): + monkeypatch.setenv("TASK_ID", "action-task-id") + task_def = { + "metadata": {"name": "test-task"}, + "payload": { + "env": {}, + "maxRunTime": 3600, + "cache": {}, + "artifacts": {}, + }, + "scopes": [], + "extra": {"treeherder": {"symbol": "T"}}, + } + graph = make_graph(make_task(label="test-task", task_def=task_def)) + + responses.get( + f"{ROOT_URL}/api/queue/v1/task/tid", + status=200, + json=task_def, + ) + m = mocker.patch( + "gecko_taskgraph.actions.create_interactive.fetch_graph_and_labels" + ) + m.return_value = ("gid", graph, {}, None) + + run_action("create-interactive", input={"notify": "test@example.com"}) + + to_run = get_artifact("to-run.json") + assert "test-task" in to_run + + +def test_backfill_task(mocker, run_action, get_artifact): + graph = make_graph( + make_task(label="test-task", task_def={"name": "test-task"}), + ) + m = mocker.patch("gecko_taskgraph.actions.backfill.fetch_graph_and_labels") + m.return_value = ("gid", graph, {}, None) + mocker.patch("gecko_taskgraph.actions.backfill.combine_task_graph_files") + + run_action( + "backfill-task", + input={"label": "test-task", "revision": "abc123", "symbol": "T"}, + ) + + to_run = get_artifact("to-run-0.json") + assert "test-task" in to_run + + +def test_confirm_failures(mocker, responses, run_action, get_artifact): + task_id = "test-task-id" + task_def = { + "metadata": {"name": "test-mochitest"}, + "extra": {"suite": "mochitest"}, + "payload": {"command": ["run-tests"], "env": {}}, + } + graph = make_graph( + make_task( + label="test-mochitest-cf", + task_def={ + "name": "test-mochitest-cf", + "payload": {"command": ["run-tests"], "env": {}}, + "metadata": {"name": "test-mochitest-cf"}, + "tags": {}, + }, + ), + ) + + responses.get( + f"{ROOT_URL}/api/queue/v1/task/{task_id}/artifacts", + status=200, + json={ + "artifacts": [ + {"name": "public/logs/live_backing.log"}, + {"name": "public/logs/errorsummary.log"}, + ] + }, + ) + + errorsummary_content = b"\n".join( + [ + b'{"test": "dom/tests/test_example.html", "status": "FAIL", "expected": "PASS", "group": "dom/tests"}', + b'{"test": "dom/tests/test_another.html", "status": "FAIL", "expected": "PASS", "group": "dom/tests"}', + ] + ) + responses.get( + f"{ROOT_URL}/api/queue/v1/task/{task_id}/artifacts/public%2Flogs%2Ferrorsummary.log", + status=200, + body=errorsummary_content, + ) + + responses.get( + f"{ROOT_URL}/api/queue/v1/task/{task_id}", + status=200, + json=task_def, + ) + m = mocker.patch("gecko_taskgraph.actions.confirm_failure.fetch_graph_and_labels") + m.return_value = ("gid", graph, {}, None) + + run_action("confirm-failures", task_id=task_id) + + to_run = get_artifact("to-run.json") + assert "test-mochitest-cf" in to_run + + +def test_confirm_failures_retrigger(mocker, responses, run_action): + task_id = "test-task-id" + task_def = { + "metadata": {"name": "test-mochitest"}, + "extra": {"suite": "mochitest"}, + } + graph = make_graph( + make_task( + label="test-mochitest", + attributes={"retrigger": True}, + task_def={"name": "test-mochitest"}, + ), + make_task(label="test-mochitest-cf", task_def={"name": "test-mochitest-cf"}), + ) + + responses.get( + f"{ROOT_URL}/api/queue/v1/task/{task_id}/artifacts", + status=200, + json={"artifacts": [{"name": "public/logs/live_backing.log"}]}, + ) + + responses.get( + f"{ROOT_URL}/api/queue/v1/task/{task_id}", + status=200, + json=task_def, + ) + m = mocker.patch("gecko_taskgraph.actions.confirm_failure.fetch_graph_and_labels") + m.return_value = ("gid", graph, {}, None) + retrigger_mock = mocker.patch( + "gecko_taskgraph.actions.confirm_failure.retrigger_action" + ) + + run_action("confirm-failures", task_id=task_id) + + retrigger_mock.assert_called_once() + + +def test_rerun(mocker, responses, run_action): + task_id = "tid" + task_def = {"metadata": {"name": "test-task"}} + + responses.get( + f"{ROOT_URL}/api/queue/v1/task/{task_id}", + status=200, + json=task_def, + ) + responses.get( + f"{ROOT_URL}/api/queue/v1/task/{task_id}/status", + status=200, + json={"status": {"state": "failed"}}, + ) + responses.post(f"{ROOT_URL}/api/queue/v1/task/{task_id}/rerun", status=200) + + graph = make_graph(make_task(label="test-task", task_def=task_def)) + m = mocker.patch("gecko_taskgraph.actions.retrigger.fetch_graph_and_labels") + m.return_value = ("gid", graph, {"test-task": [task_id]}, {"test-task": [task_id]}) + + run_action("rerun", task_id=task_id) + + +def test_retrigger_decision(responses, run_action, capsys): + task_def = { + "taskGroupId": "tgid", + "schedulerId": "scheduler", + "provisionerId": "provisioner", + "workerType": "worker", + "created": "2024-01-01T00:00:00.000Z", + "deadline": "2024-01-01T01:00:00.000Z", + "expires": "2024-01-02T00:00:00.000Z", + "metadata": {"name": "decision-task"}, + "payload": {}, + "tags": {}, + "extra": {}, + } + + responses.get( + f"{ROOT_URL}/api/queue/v1/task/tid", + status=200, + json=task_def, + ) + + run_action("retrigger-decision", params={"level": "1"}) + + captured = capsys.readouterr() + assert "decision-task" in captured.out + assert "gecko-level-1" in captured.out + assert "retrigger-decision-task" in captured.out + + +def test_retrigger_multiple(mocker, run_action, get_artifact): + graph = make_graph( + make_task( + label="test-task", + attributes={"retrigger": True}, + task_def={"name": "test-task"}, + ), + ) + + m = mocker.patch("gecko_taskgraph.actions.retrigger.fetch_graph_and_labels") + m.return_value = ("gid", graph, {}, {"test-task": ["tid"]}) + + run_action( + "retrigger-multiple", + input={"requests": [{"tasks": ["test-task"], "times": 2}]}, + ) + + to_run = get_artifact("to-run.json") + assert "test-task" in to_run + + +def test_retrigger_multiple_rerun(mocker, responses, run_action): + task_id = "rerun-task-id" + graph = make_graph( + make_task( + label="test-task", + attributes={"retrigger": False}, + task_def={"name": "test-task"}, + ), + ) + + m = mocker.patch("gecko_taskgraph.actions.retrigger.fetch_graph_and_labels") + m.return_value = ("gid", graph, {}, {"test-task": [task_id]}) + + responses.get( + f"{ROOT_URL}/api/queue/v1/task/{task_id}/status", + status=200, + json={"status": {"state": "failed"}}, + ) + responses.post(f"{ROOT_URL}/api/queue/v1/task/{task_id}/rerun", status=200) + + run_action( + "retrigger-multiple", + input={"requests": [{"tasks": ["test-task"], "times": 2}]}, + ) + + +def test_add_all_browsertime(mocker, run_action, get_artifact): + graph = make_graph( + make_task( + label="raptor-browsertime", + kind="test", + attributes={ + "raptor_try_name": "browsertime-firefox", + "test_platform": "linux64-shippable-qr/opt", + "run_on_projects": ["mozilla-central"], + }, + task_def={"name": "raptor-browsertime", "extra": {"suite": "raptor"}}, + ), + make_task(label="build", kind="build", task_def={"name": "build"}), + ) + + m = mocker.patch("gecko_taskgraph.actions.backfill.fetch_graph_and_labels") + m.return_value = ("gid", graph, {}, None) + + run_action("add-all-browsertime", params={"project": "mozilla-central"}) + + to_run = get_artifact("to-run.json") + assert "raptor-browsertime" in to_run + assert "build" not in to_run + + +@pytest.mark.xfail( + reason="Index API artifact handling issue - _handle_artifact doesn't parse YAML correctly for index artifacts" +) +def test_gecko_profile(mocker, responses, run_action, get_artifact): + task_id = "tid" + task_def = { + "metadata": {"name": "test-raptor"}, + "payload": {"command": [["run-tests"]], "env": {}}, + "extra": { + "suite": "raptor", + "treeherder": {"symbol": "R", "groupName": "Raptor"}, + }, + } + graph = make_graph( + make_task( + label="test-raptor", + kind="test", + attributes={"unittest_suite": "raptor"}, + task_def={ + "name": "test-raptor", + "payload": {"command": [["run-tests"]], "env": {}}, + "extra": { + "suite": "raptor", + "treeherder": {"symbol": "R", "groupName": "Raptor"}, + }, + }, + ) + ) + + responses.get( + "http://hg.example.com/json-pushes?version=2&startID=99&endID=100", + status=200, + json={"pushes": {"100": {"changesets": ["abc123"]}}}, + ) + + responses.get( + f"{ROOT_URL}/api/queue/v1/task/{task_id}", + status=200, + json=task_def, + ) + + responses.get( + f"{ROOT_URL}/api/index/v1/task/gecko.v2.some-project.pushlog-id.100.decision/artifacts/public%2Fparameters.yml", + status=200, + body=yaml.dump({"pushlog_id": "100", "project": "autoland", "level": "1"}), + content_type="application/x-yaml", + ) + m = mocker.patch("gecko_taskgraph.actions.gecko_profile.fetch_graph_and_labels") + m.return_value = ("gid", graph, {"test-raptor": "tid"}, None) + mocker.patch("gecko_taskgraph.actions.gecko_profile.combine_task_graph_files") + + run_action( + "geckoprofile", + task_id=task_id, + params={"pushlog_id": "100", "head_repository": "http://hg.example.com"}, + input={"depth": 1, "gecko_profile_interval": 5}, + ) + + to_run = get_artifact("to-run-100.json") + assert "test-raptor" in to_run + + +def test_side_by_side(mocker, responses, run_action, get_artifact): + task_id = "tid" + task_def = { + "metadata": {"name": "linux/opt-browsertime-tp6"}, + "extra": {"treeherder": {"symbol": "tp6"}}, + "payload": {"command": [["run"], ["perf-test {test_name}"]]}, + } + graph = make_graph( + make_task( + label="perftest-linux-side-by-side", + task_def={ + "name": "perftest-linux-side-by-side", + "payload": {"command": [["run"], ["perf-test {test_name}"]]}, + "extra": {"treeherder": {"symbol": "sxs"}}, + "metadata": {"name": "perftest-linux-side-by-side"}, + }, + ) + ) + + responses.get( + f"{ROOT_URL}/api/queue/v1/task/{task_id}", + status=200, + json=task_def, + ) + m = mocker.patch("gecko_taskgraph.actions.side_by_side.fetch_graph_and_labels") + m.return_value = ("gid", graph, {}, None) + + run_action( + "side-by-side", + task_id=task_id, + params={"head_rev": "newrev123", "pushlog_id": "100"}, + input={"revision": "baserev456", "project": "autoland"}, + ) + + to_run = get_artifact("to-run.json") + assert "perftest-linux-side-by-side" in to_run + + +def test_release_promotion( + mocker, monkeypatch, responses, run_action, parameters, graph_config +): + m = mocker.patch("gecko_taskgraph.actions.release_promotion.taskgraph_decision") + + action_task_id = "action-task-id" + monkeypatch.setenv("TASK_ID", action_task_id) + + responses.get( + f"{ROOT_URL}/api/index/v1/task/gecko.v2.try.revision.abcdef.taskgraph.decision", + status=200, + json={"taskId": "decision-task-id"}, + ) + + responses.get( + f"{ROOT_URL}/api/queue/v1/task/decision-task-id/artifacts/public%2Fparameters.yml", + status=200, + body=yaml.dump( + { + "base_repository": "http://hg.example.com", + "head_repository": "http://hg.example.com", + "head_rev": "abcdef", + "project": "try", + "level": "1", + "pushlog_id": "100", + "required_signoffs": [], + "signoff_urls": {}, + "release_product": "firefox", + "release_type": "nightly", + } + ), + content_type="application/x-yaml", + ) + responses.get( + f"{ROOT_URL}/api/queue/v1/task/decision-task-id/artifacts/public%2Ffull-task-graph.json", + status=200, + json={}, + ) + + responses.get( + f"{ROOT_URL}/api/queue/v1/task-group/{action_task_id}/list", + status=200, + json={ + "tasks": [ + {"status": {"taskId": action_task_id, "state": "running"}}, + ] + }, + ) + + mocker.patch( + "gecko_taskgraph.actions.release_promotion.find_existing_tasks_from_previous_kinds", + return_value={}, + ) + + run_action( + "release-promotion", + params={ + "project": "try", + "level": "1", + }, + input={ + "release_promotion_flavor": "promote_firefox", + "build_number": 1, + "version": "", + "partial_updates": {}, + "release_enable_partner_repack": False, + "release_enable_partner_attribution": False, + "release_enable_emefree": False, + }, + ) + + m.assert_called_once() + args, kwargs = m.call_args + assert args[0] == {"root": graph_config.root_dir} + assert kwargs["parameters"]["target_tasks_method"] == "promote_desktop" + + +if __name__ == "__main__": + main()