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 ("os.environ" , {"TASKCLUSTER_ROOT_URL" : "https://tc.example.com" })
3064 def test_create_tasks (self ):
65+ created_tasks = {}
66+ mock_taskcluster_api (created_tasks = created_tasks )
67+
3168 tasks = {
3269 "tid-a" : Task (
3370 kind = "test" , label = "a" , attributes = {}, task = {"payload" : "hello world" }
@@ -48,18 +85,24 @@ def test_create_tasks(self):
4885 decision_task_id = "decisiontask" ,
4986 )
5087
51- for tid , task in self .created_tasks .items ():
88+ assert created_tasks
89+ for tid , task in created_tasks .items ():
5290 self .assertEqual (task ["payload" ], "hello world" )
5391 self .assertEqual (task ["schedulerId" ], "domain-level-4" )
5492 # make sure the dependencies exist, at least
5593 for depid in task .get ("dependencies" , []):
5694 if depid == "decisiontask" :
5795 # Don't look for decisiontask here
5896 continue
59- self .assertIn (depid , self . created_tasks )
97+ self .assertIn (depid , created_tasks )
6098
99+ @responses .activate
100+ @mock .patch .dict ("os.environ" , {"TASKCLUSTER_ROOT_URL" : "https://tc.example.com" })
61101 def test_create_task_without_dependencies (self ):
62102 "a task with no dependencies depends on the decision task"
103+ created_tasks = {}
104+ mock_taskcluster_api (created_tasks = created_tasks )
105+
63106 tasks = {
64107 "tid-a" : Task (
65108 kind = "test" , label = "a" , attributes = {}, task = {"payload" : "hello world" }
@@ -77,12 +120,16 @@ def test_create_task_without_dependencies(self):
77120 decision_task_id = "decisiontask" ,
78121 )
79122
80- for tid , task in self .created_tasks .items ():
123+ assert created_tasks
124+ for tid , task in created_tasks .items ():
81125 self .assertEqual (task .get ("dependencies" ), ["decisiontask" ])
82126
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"
127+ @responses .activate
128+ @mock .patch .dict ("os.environ" , {"TASKCLUSTER_ROOT_URL" : "https://tc.example.com" })
129+ def test_create_tasks_fails_if_create_fails (self ):
130+ "create_tasks fails if a single create_task call fails"
131+ mock_taskcluster_api (error_status = 403 , error_message = "oh no!" )
132+
86133 tasks = {
87134 "tid-a" : Task (
88135 kind = "test" , label = "a" , attributes = {}, task = {"payload" : "hello world" }
@@ -92,13 +139,7 @@ def test_create_tasks_fails_if_create_fails(self, create_task):
92139 graph = Graph (nodes = {"tid-a" }, edges = set ())
93140 taskgraph = TaskGraph (tasks , graph )
94141
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 ):
142+ with self .assertRaises (TaskclusterRestFailure ):
102143 create .create_tasks (
103144 GRAPH_CONFIG ,
104145 taskgraph ,
0 commit comments