test_actions_util.py (5598B)
1 # This Source Code Form is subject to the terms of the Mozilla Public 2 # License, v. 2.0. If a copy of the MPL was not distributed with this 3 # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 5 6 import unittest 7 from pprint import pprint 8 from unittest.mock import patch 9 10 import pytest 11 from mozunit import MockedOpen, main 12 from taskgraph import create 13 from taskgraph.util import json, taskcluster 14 15 from gecko_taskgraph import actions 16 from gecko_taskgraph.actions.util import combine_task_graph_files, relativize_datestamps 17 from gecko_taskgraph.decision import read_artifact 18 19 TASK_DEF = { 20 "created": "2017-10-10T18:33:03.460Z", 21 # note that this is not an even number of seconds off! 22 "deadline": "2017-10-11T18:33:03.461Z", 23 "dependencies": [], 24 "expires": "2018-10-10T18:33:04.461Z", 25 "payload": { 26 "artifacts": { 27 "public": { 28 "expires": "2018-10-10T18:33:03.463Z", 29 "path": "/builds/worker/artifacts", 30 "type": "directory", 31 }, 32 }, 33 "maxRunTime": 1800, 34 }, 35 } 36 37 38 @pytest.fixture(scope="module", autouse=True) 39 def enable_test_mode(): 40 create.testing = True 41 taskcluster.testing = True 42 43 44 class TestRelativize(unittest.TestCase): 45 def test_relativize(self): 46 rel = relativize_datestamps(TASK_DEF) 47 import pprint 48 49 pprint.pprint(rel) 50 assert rel["created"] == {"relative-datestamp": "0 seconds"} 51 assert rel["deadline"] == {"relative-datestamp": "86400 seconds"} 52 assert rel["expires"] == {"relative-datestamp": "31536001 seconds"} 53 assert rel["payload"]["artifacts"]["public"]["expires"] == { 54 "relative-datestamp": "31536000 seconds" 55 } 56 57 58 class TestCombineTaskGraphFiles(unittest.TestCase): 59 def test_no_suffixes(self): 60 with MockedOpen({}): 61 combine_task_graph_files([]) 62 self.assertRaises(Exception, open("artifacts/to-run.json")) 63 64 @patch("gecko_taskgraph.actions.util.rename_artifact") 65 def test_one_suffix(self, rename_artifact): 66 combine_task_graph_files(["0"]) 67 rename_artifact.assert_any_call("task-graph-0.json", "task-graph.json") 68 rename_artifact.assert_any_call( 69 "label-to-taskid-0.json", "label-to-taskid.json" 70 ) 71 rename_artifact.assert_any_call("to-run-0.json", "to-run.json") 72 73 def test_several_suffixes(self): 74 files = { 75 "artifacts/task-graph-0.json": json.dumps({"taska": {}}), 76 "artifacts/label-to-taskid-0.json": json.dumps({"taska": "TASKA"}), 77 "artifacts/to-run-0.json": json.dumps(["taska"]), 78 "artifacts/task-graph-1.json": json.dumps({"taskb": {}}), 79 "artifacts/label-to-taskid-1.json": json.dumps({"taskb": "TASKB"}), 80 "artifacts/to-run-1.json": json.dumps(["taskb"]), 81 } 82 with MockedOpen(files): 83 combine_task_graph_files(["0", "1"]) 84 self.assertEqual( 85 read_artifact("task-graph.json"), 86 { 87 "taska": {}, 88 "taskb": {}, 89 }, 90 ) 91 self.assertEqual( 92 read_artifact("label-to-taskid.json"), 93 { 94 "taska": "TASKA", 95 "taskb": "TASKB", 96 }, 97 ) 98 self.assertEqual( 99 sorted(read_artifact("to-run.json")), 100 [ 101 "taska", 102 "taskb", 103 ], 104 ) 105 106 107 def is_subset(subset, superset): 108 if isinstance(subset, dict): 109 return all( 110 key in superset and is_subset(val, superset[key]) 111 for key, val in subset.items() 112 ) 113 114 if isinstance(subset, list) or isinstance(subset, set): 115 return all( 116 any(is_subset(subitem, superitem) for superitem in superset) 117 for subitem in subset 118 ) 119 120 if isinstance(subset, str): 121 return subset in superset 122 123 # assume that subset is a plain value if none of the above match 124 return subset == superset 125 126 127 @pytest.mark.parametrize( 128 "task_def,expected", 129 [ 130 pytest.param( 131 {"tags": {"kind": "decision-task"}}, 132 { 133 "hookPayload": { 134 "decision": { 135 "action": {"cb_name": "retrigger-decision"}, 136 }, 137 }, 138 }, 139 id="retrigger_decision", 140 ), 141 pytest.param( 142 {"tags": {"action": "backfill-task"}}, 143 { 144 "hookPayload": { 145 "decision": { 146 "action": {"cb_name": "retrigger-decision"}, 147 }, 148 }, 149 }, 150 id="retrigger_backfill", 151 ), 152 ], 153 ) 154 def test_extract_applicable_action( 155 responses, monkeypatch, actions_json, task_def, expected 156 ): 157 actions.util.get_task_definition.cache_clear() 158 base_url = "https://taskcluster" 159 decision_task_id = "dddd" 160 task_id = "tttt" 161 162 monkeypatch.setenv("TASK_ID", task_id) 163 monkeypatch.setenv("TASKCLUSTER_ROOT_URL", base_url) 164 monkeypatch.setenv("TASKCLUSTER_PROXY_URL", base_url) 165 responses.add( 166 responses.GET, 167 f"{base_url}/api/queue/v1/task/{task_id}", 168 status=200, 169 json=task_def, 170 ) 171 action = actions.util._extract_applicable_action( 172 actions_json, "retrigger", decision_task_id, task_id 173 ) 174 pprint(action, indent=2) 175 assert is_subset(expected, action) 176 177 178 if __name__ == "__main__": 179 main()