22# License, v. 2.0. If a copy of the MPL was not distributed with this
33# file, You can obtain one at http://mozilla.org/MPL/2.0/.
44
5-
5+ import json
6+ import re
67import unittest
78from unittest import mock
89
10+ import responses
11+ from taskcluster .exceptions import TaskclusterRestFailure
12+
913from taskgraph import create
1014from taskgraph .config import GraphConfig
1115from taskgraph .graph import Graph
1216from taskgraph .task import Task
1317from taskgraph .taskgraph import TaskGraph
18+ from taskgraph .util import taskcluster as tc_util
1419
1520GRAPH_CONFIG = GraphConfig ({"trust-domain" : "domain" }, "/var/empty" )
1621
1722
18- class TestCreate (unittest .TestCase ):
19- def setUp (self ):
20- self .created_tasks = {}
21- self .old_create_task = create .create_task
22- create .create_task = self .fake_create_task
23+ def mock_taskcluster_api (
24+ created_tasks = None , error_status = None , error_message = None , error_task_ids = None
25+ ):
26+ """Mock the Taskcluster Queue API for create task calls."""
27+
28+ def request_callback (request ):
29+ task_id = request .url .split ("/" )[- 1 ]
30+
31+ # Check if this task should error
32+ if error_status is not None :
33+ if error_task_ids is None or task_id in error_task_ids :
34+ # Support per-task error messages
35+ if isinstance (error_message , dict ):
36+ message = error_message .get (task_id , "error" )
37+ else :
38+ message = error_message or "error"
39+ return (error_status , {}, f'{{"message": "{ message } "}}' )
40+
41+ # Success case - capture task definition if requested
42+ if created_tasks is not None :
43+ task_def = json .loads (request .body )
44+ created_tasks [task_id ] = task_def
45+
46+ return (200 , {}, f'{{"status": {{"taskId": "{ task_id } "}}}}' )
2347
24- def tearDown (self ):
25- create .create_task = self .old_create_task
48+ responses .add_callback (
49+ responses .PUT ,
50+ re .compile (r"https://tc\.example\.com/api/queue/v1/task/.*" ),
51+ callback = request_callback ,
52+ content_type = "application/json" ,
53+ )
2654
27- def fake_create_task (self , session , task_id , label , task_def ):
28- self .created_tasks [task_id ] = task_def
2955
56+ class TestCreate (unittest .TestCase ):
57+ def setUp (self ):
58+ # Clear cached Taskcluster clients/sessions since we're mocking the environment
59+ tc_util .get_taskcluster_client .cache_clear ()
60+ tc_util .get_session .cache_clear ()
61+
62+ @responses .activate
63+ @mock .patch .dict (
64+ "os.environ" ,
65+ {"TASKCLUSTER_ROOT_URL" : "https://tc.example.com" },
66+ clear = True ,
67+ )
3068 def test_create_tasks (self ):
69+ created_tasks = {}
70+ mock_taskcluster_api (created_tasks = created_tasks )
71+
3172 tasks = {
3273 "tid-a" : Task (
3374 kind = "test" , label = "a" , attributes = {}, task = {"payload" : "hello world" }
@@ -48,18 +89,28 @@ def test_create_tasks(self):
4889 decision_task_id = "decisiontask" ,
4990 )
5091
51- for tid , task in self .created_tasks .items ():
92+ assert created_tasks
93+ for tid , task in created_tasks .items ():
5294 self .assertEqual (task ["payload" ], "hello world" )
5395 self .assertEqual (task ["schedulerId" ], "domain-level-4" )
5496 # make sure the dependencies exist, at least
5597 for depid in task .get ("dependencies" , []):
5698 if depid == "decisiontask" :
5799 # Don't look for decisiontask here
58100 continue
59- self .assertIn (depid , self .created_tasks )
60-
101+ self .assertIn (depid , created_tasks )
102+
103+ @responses .activate
104+ @mock .patch .dict (
105+ "os.environ" ,
106+ {"TASKCLUSTER_ROOT_URL" : "https://tc.example.com" },
107+ clear = True ,
108+ )
61109 def test_create_task_without_dependencies (self ):
62110 "a task with no dependencies depends on the decision task"
111+ created_tasks = {}
112+ mock_taskcluster_api (created_tasks = created_tasks )
113+
63114 tasks = {
64115 "tid-a" : Task (
65116 kind = "test" , label = "a" , attributes = {}, task = {"payload" : "hello world" }
@@ -77,12 +128,20 @@ def test_create_task_without_dependencies(self):
77128 decision_task_id = "decisiontask" ,
78129 )
79130
80- for tid , task in self .created_tasks .items ():
131+ assert created_tasks
132+ for tid , task in created_tasks .items ():
81133 self .assertEqual (task .get ("dependencies" ), ["decisiontask" ])
82134
83- @mock .patch ("taskgraph.create.create_task" )
84- def test_create_tasks_fails_if_create_fails (self , create_task ):
85- "creat_tasks fails if a single create_task call fails"
135+ @responses .activate
136+ @mock .patch .dict (
137+ "os.environ" ,
138+ {"TASKCLUSTER_ROOT_URL" : "https://tc.example.com" },
139+ clear = True ,
140+ )
141+ def test_create_tasks_fails_if_create_fails (self ):
142+ "create_tasks fails if a single create_task call fails"
143+ mock_taskcluster_api (error_status = 403 , error_message = "oh no!" )
144+
86145 tasks = {
87146 "tid-a" : Task (
88147 kind = "test" , label = "a" , attributes = {}, task = {"payload" : "hello world" }
@@ -92,13 +151,7 @@ def test_create_tasks_fails_if_create_fails(self, create_task):
92151 graph = Graph (nodes = {"tid-a" }, edges = set ())
93152 taskgraph = TaskGraph (tasks , graph )
94153
95- def fail (* args ):
96- print ("UHOH" )
97- raise RuntimeError ("oh no!" )
98-
99- create_task .side_effect = fail
100-
101- with self .assertRaises (RuntimeError ):
154+ with self .assertRaises (TaskclusterRestFailure ):
102155 create .create_tasks (
103156 GRAPH_CONFIG ,
104157 taskgraph ,
0 commit comments