73
95
class TestModuleHelpers(TestCase):
75
def test_extract_oauth_key_extracts_oauth_token_from_oauth_header(self):
76
token = factory.getRandomString(18)
79
extract_oauth_key(factory.make_oauth_header(oauth_token=token)))
81
def test_extract_oauth_key_returns_None_without_oauth_key(self):
82
self.assertIs(None, extract_oauth_key(''))
97
def make_fake_request(self, auth_header):
98
"""Create a very simple fake request, with just an auth header."""
99
FakeRequest = namedtuple('FakeRequest', ['META'])
100
return FakeRequest(META={'HTTP_AUTHORIZATION': auth_header})
102
def test_extract_oauth_key_from_auth_header_returns_key(self):
103
token = factory.getRandomString(18)
106
extract_oauth_key_from_auth_header(
107
factory.make_oauth_header(oauth_token=token)))
109
def test_extract_oauth_key_from_auth_header_returns_None_if_missing(self):
110
self.assertIs(None, extract_oauth_key_from_auth_header(''))
112
def test_extract_oauth_key_raises_Unauthorized_if_no_auth_header(self):
115
extract_oauth_key, self.make_fake_request(None))
117
def test_extract_oauth_key_raises_Unauthorized_if_no_key(self):
120
extract_oauth_key, self.make_fake_request(''))
122
def test_extract_oauth_key_returns_key(self):
123
token = factory.getRandomString(18)
126
extract_oauth_key(self.make_fake_request(
127
factory.make_oauth_header(oauth_token=token))))
129
def test_get_oauth_token_finds_token(self):
130
user = factory.make_user()
131
consumer, token = user.get_profile().create_authorisation_token()
135
self.make_fake_request(
136
factory.make_oauth_header(oauth_token=token.key))))
138
def test_get_oauth_token_raises_Unauthorized_for_unknown_token(self):
139
fake_token = factory.getRandomString(18)
140
header = factory.make_oauth_header(oauth_token=fake_token)
143
get_oauth_token, self.make_fake_request(header))
84
145
def test_extract_constraints_ignores_unknown_parameters(self):
85
146
unknown_parameter = "%s=%s" % (
96
157
extract_constraints(QueryDict('name=%s' % name)))
99
class EnlistmentAPITest(APIv10TestMixin):
100
# This is a mixin containing enlistement tests. We will run this for:
101
# an anonymous user, a simple (non-admin) user and an admin user.
102
# XXX: rvb 2012-04-10 bug=978035: It would be better to use
103
# testscenarios for this.
159
def test_get_overrided_query_dict_returns_QueryDict(self):
160
defaults = {factory.getRandomString(): factory.getRandomString()}
161
results = get_overrided_query_dict(defaults, QueryDict(''))
162
expected_results = QueryDict('').copy()
163
expected_results.update(defaults)
164
self.assertEqual(expected_results, results)
166
def test_get_overrided_query_dict_values_in_data_replaces_defaults(self):
167
key = factory.getRandomString()
168
defaults = {key: factory.getRandomString()}
169
data_value = factory.getRandomString()
170
data = {key: data_value}
171
results = get_overrided_query_dict(defaults, data)
172
self.assertEqual([data_value], results.getlist(key))
175
class MultipleUsersScenarios:
176
"""A mixin that uses testscenarios to repeat a testcase as different
179
The scenarios should inject a `userfactory` variable that will
180
be called to produce the user used in the tests e.g.:
182
class ExampleTest(MultipleUsersScenarios, TestCase):
184
('anon', dict(userfactory=lambda: AnonymousUser())),
185
('user', dict(userfactory=factory.make_user)),
186
('admin', dict(userfactory=factory.make_admin)),
189
def test_something(self):
192
The test `test_something` with be run 3 times: one with a anonymous user
193
logged in, once with a simple (non-admin) user logged in and once with
194
an admin user logged in.
197
__metaclass__ = ABCMeta
199
scenarios = abstractproperty(
200
"The scenarios as defined by testscenarios.")
203
super(MultipleUsersScenarios, self).setUp()
204
user = self.userfactory()
205
if not user.is_anonymous():
206
password = factory.getRandomString()
207
user.set_password(password)
209
self.logged_in_user = user
211
username=self.logged_in_user.username, password=password)
214
class EnlistmentAPITest(APIv10TestMixin, MultipleUsersScenarios, TestCase):
215
"""Enlistment tests."""
217
('anon', dict(userfactory=lambda: AnonymousUser())),
218
('user', dict(userfactory=factory.make_user)),
219
('admin', dict(userfactory=factory.make_admin)),
105
222
def test_POST_new_creates_node(self):
106
223
# The API allows a Node to be created.
334
444
'architecture': factory.getRandomChoice(ARCHITECTURE_CHOICES),
335
445
'hostname': factory.getRandomString(),
336
'after_commissioning_action': '2',
446
'after_commissioning_action':
447
NODE_AFTER_COMMISSIONING_ACTION.DEFAULT,
337
448
'mac_addresses': ['aa:bb:cc:dd:ee:ff', '22:bb:cc:dd:ee:ff'],
339
450
parsed_result = json.loads(response.content)
340
451
self.assertItemsEqual(
342
'hostname', 'system_id', 'macaddress_set', 'architecture',
345
462
list(parsed_result))
348
class SimpleUserLoggedInEnlistmentAPITest(NonAdminEnlistmentAPITest,
350
# This is an actual test case that uses the NonAdminEnlistmentAPITest
351
# mixin plus enlistement tests specific to simple (non-admin) users.
465
class SimpleUserLoggedInEnlistmentAPITest(APIv10TestMixin, LoggedInTestCase):
466
# Enlistment tests specific to simple (non-admin) users.
353
468
def test_POST_accept_not_allowed(self):
354
469
# An non-admin user is not allowed to accept an anonymously
363
478
"following node(s): %s." % node_id),
364
479
(response.status_code, response.content))
481
def test_POST_simple_user_cannot_set_power_type_and_parameters(self):
482
new_power_address = factory.getRandomString()
483
response = self.client.post(
484
self.get_uri('nodes/'), {
486
'architecture': factory.getRandomChoice(ARCHITECTURE_CHOICES),
487
'power_type': POWER_TYPE.WAKE_ON_LAN,
488
'power_parameters_power_address': new_power_address,
489
'mac_addresses': ['AA:BB:CC:DD:EE:FF'],
492
node = Node.objects.get(
493
system_id=json.loads(response.content)['system_id'])
495
(httplib.OK, '', POWER_TYPE.DEFAULT),
496
(response.status_code, node.power_parameters,
366
499
def test_POST_returns_limited_fields(self):
367
500
response = self.client.post(
368
501
self.get_uri('nodes/'),
371
504
'hostname': factory.getRandomString(),
372
505
'architecture': factory.getRandomChoice(ARCHITECTURE_CHOICES),
373
'after_commissioning_action': '2',
506
'after_commissioning_action':
507
NODE_AFTER_COMMISSIONING_ACTION.DEFAULT,
374
508
'mac_addresses': ['aa:bb:cc:dd:ee:ff', '22:bb:cc:dd:ee:ff'],
376
510
parsed_result = json.loads(response.content)
377
511
self.assertItemsEqual(
379
'hostname', 'system_id', 'macaddress_set', 'architecture',
380
'status', 'resource_uri',
382
523
list(parsed_result))
385
class AdminLoggedInEnlistmentAPITest(EnlistmentAPITest,
386
AdminLoggedInTestCase):
387
# This is an actual test case that uses the EnlistmentAPITest mixin
388
# and adds enlistement tests specific to admin users.
526
class AdminLoggedInEnlistmentAPITest(APIv10TestMixin, AdminLoggedInTestCase):
527
# Enlistment tests specific to admin users.
529
def test_POST_new_creates_node_default_values_for_power_settings(self):
530
architecture = factory.getRandomChoice(ARCHITECTURE_CHOICES)
531
mac_address = 'AA:BB:CC:DD:EE:FF'
532
response = self.client.post(
533
self.get_uri('nodes/'), {
535
'architecture': architecture,
536
'mac_addresses': [mac_address],
538
node = Node.objects.get(
539
system_id=json.loads(response.content)['system_id'])
540
self.assertAttributes(
543
architecture=architecture, power_type=POWER_TYPE.DEFAULT,
544
power_parameters=''))
546
def test_POST_new_sets_power_type_if_admin(self):
547
response = self.client.post(
548
self.get_uri('nodes/'), {
550
'architecture': factory.getRandomChoice(ARCHITECTURE_CHOICES),
551
'power_type': POWER_TYPE.WAKE_ON_LAN,
552
'mac_addresses': ['00:11:22:33:44:55'],
554
node = Node.objects.get(
555
system_id=json.loads(response.content)['system_id'])
556
self.assertEqual(POWER_TYPE.WAKE_ON_LAN, node.power_type)
557
self.assertEqual('', node.power_parameters)
559
def test_POST_new_sets_power_parameters_field(self):
560
# The api allows the setting of a Node's power_parameters field.
561
# Create a power_parameter valid for the selected power_type.
562
new_power_address = factory.getRandomString()
563
response = self.client.post(
564
self.get_uri('nodes/'), {
566
'architecture': factory.getRandomChoice(ARCHITECTURE_CHOICES),
567
'power_type': POWER_TYPE.WAKE_ON_LAN,
568
'power_parameters_power_address': new_power_address,
569
'mac_addresses': ['AA:BB:CC:DD:EE:FF'],
572
node = Node.objects.get(
573
system_id=json.loads(response.content)['system_id'])
574
self.assertEqual(httplib.OK, response.status_code)
576
{'power_address': new_power_address},
577
reload_object(node).power_parameters)
579
def test_POST_updates_power_parameters_rejects_unknown_param(self):
580
hostname = factory.getRandomString()
581
response = self.client.post(
582
self.get_uri('nodes/'), {
584
'hostname': hostname,
585
'architecture': factory.getRandomChoice(ARCHITECTURE_CHOICES),
586
'power_type': POWER_TYPE.WAKE_ON_LAN,
587
'power_parameters_unknown_param': factory.getRandomString(),
588
'mac_addresses': [factory.getRandomMACAddress()],
594
{'power_parameters': ["Unknown parameter(s): unknown_param."]}
596
(response.status_code, json.loads(response.content)))
597
self.assertFalse(Node.objects.filter(hostname=hostname).exists())
599
def test_POST_new_sets_power_parameters_skip_check(self):
600
# The api allows to skip the validation step and set arbitrary
602
param = factory.getRandomString()
603
response = self.client.post(
604
self.get_uri('nodes/'), {
606
'architecture': factory.getRandomChoice(ARCHITECTURE_CHOICES),
607
'power_type': POWER_TYPE.WAKE_ON_LAN,
608
'power_parameters_param': param,
609
'power_parameters_skip_check': 'true',
610
'mac_addresses': ['AA:BB:CC:DD:EE:FF'],
613
node = Node.objects.get(
614
system_id=json.loads(response.content)['system_id'])
615
self.assertEqual(httplib.OK, response.status_code)
618
reload_object(node).power_parameters)
390
620
def test_POST_admin_creates_node_in_commissioning_state(self):
391
621
# When an admin user enlists a node, it goes into the
595
836
self.assertEqual(httplib.FORBIDDEN, response.status_code)
597
838
def test_POST_start_returns_node(self):
598
node = factory.make_node(owner=self.logged_in_user)
839
node = factory.make_node(
840
owner=self.logged_in_user, mac=True,
841
power_type=POWER_TYPE.WAKE_ON_LAN)
599
842
response = self.client.post(self.get_node_uri(node), {'op': 'start'})
600
843
self.assertEqual(httplib.OK, response.status_code)
601
844
self.assertEqual(
602
845
node.system_id, json.loads(response.content)['system_id'])
604
847
def test_POST_start_may_be_repeated(self):
605
node = factory.make_node(owner=self.logged_in_user)
848
node = factory.make_node(
849
owner=self.logged_in_user, mac=True,
850
power_type=POWER_TYPE.WAKE_ON_LAN)
606
851
self.client.post(self.get_node_uri(node), {'op': 'start'})
607
852
response = self.client.post(self.get_node_uri(node), {'op': 'start'})
608
853
self.assertEqual(httplib.OK, response.status_code)
610
855
def test_POST_start_stores_user_data(self):
611
node = factory.make_node(owner=self.logged_in_user)
856
node = factory.make_node(
857
owner=self.logged_in_user, mac=True,
858
power_type=POWER_TYPE.WAKE_ON_LAN)
613
860
b'\xff\x00\xff\xfe\xff\xff\xfe' +
614
861
factory.getRandomString().encode('ascii'))
738
985
self.assertEqual(0, Node.objects.filter(hostname='diane').count())
739
986
self.assertEqual(1, Node.objects.filter(hostname='francis').count())
988
def test_PUT_ignores_unknown_fields(self):
989
node = factory.make_node(
990
owner=self.logged_in_user,
991
after_commissioning_action=(
992
NODE_AFTER_COMMISSIONING_ACTION.DEFAULT))
993
field = factory.getRandomString()
994
response = self.client.put(
995
self.get_node_uri(node),
996
{field: factory.getRandomString()}
999
self.assertEqual(httplib.OK, response.status_code)
1001
def test_PUT_admin_can_change_power_type(self):
1003
original_power_type = factory.getRandomChoice(
1005
new_power_type = factory.getRandomChoice(
1006
POWER_TYPE_CHOICES, but_not=original_power_type)
1007
node = factory.make_node(
1008
owner=self.logged_in_user,
1009
power_type=original_power_type,
1010
after_commissioning_action=(
1011
NODE_AFTER_COMMISSIONING_ACTION.DEFAULT))
1013
self.get_node_uri(node),
1014
{'power_type': new_power_type}
1018
new_power_type, reload_object(node).power_type)
1020
def test_PUT_non_admin_cannot_change_power_type(self):
1021
original_power_type = factory.getRandomChoice(
1023
new_power_type = factory.getRandomChoice(
1024
POWER_TYPE_CHOICES, but_not=original_power_type)
1025
node = factory.make_node(
1026
owner=self.logged_in_user,
1027
power_type=original_power_type,
1028
after_commissioning_action=(
1029
NODE_AFTER_COMMISSIONING_ACTION.DEFAULT))
1031
self.get_node_uri(node),
1032
{'power_type': new_power_type}
1036
original_power_type, reload_object(node).power_type)
741
1038
def test_resource_uri_points_back_at_node(self):
742
1039
# When a Node is returned by the API, the field 'resource_uri'
743
1040
# provides the URI for this Node.
783
1080
self.assertEqual(httplib.NOT_FOUND, response.status_code)
1082
def test_PUT_updates_power_parameters_field(self):
1083
# The api allows the updating of a Node's power_parameters field.
1085
node = factory.make_node(
1086
owner=self.logged_in_user,
1087
power_type=POWER_TYPE.WAKE_ON_LAN)
1088
# Create a power_parameter valid for the selected power_type.
1089
new_power_address = factory.getRandomString()
1090
response = self.client.put(
1091
self.get_node_uri(node),
1092
{'power_parameters_power_address': new_power_address})
1094
self.assertEqual(httplib.OK, response.status_code)
1096
{'power_address': new_power_address},
1097
reload_object(node).power_parameters)
1099
def test_PUT_updates_power_parameters_rejects_unknown_param(self):
1101
power_parameters = factory.getRandomString()
1102
node = factory.make_node(
1103
owner=self.logged_in_user,
1104
power_type=POWER_TYPE.WAKE_ON_LAN,
1105
power_parameters=power_parameters)
1106
response = self.client.put(
1107
self.get_node_uri(node),
1108
{'power_parameters_unknown_param': factory.getRandomString()})
1112
httplib.BAD_REQUEST,
1113
{'power_parameters': ["Unknown parameter(s): unknown_param."]}
1115
(response.status_code, json.loads(response.content)))
1117
power_parameters, reload_object(node).power_parameters)
1119
def test_PUT_updates_power_type_default_resets_params(self):
1120
# If one sets power_type to DEFAULT, power_parameter gets
1121
# reset by default (if skip_check is not set).
1123
power_parameters = factory.getRandomString()
1124
node = factory.make_node(
1125
owner=self.logged_in_user,
1126
power_type=POWER_TYPE.WAKE_ON_LAN,
1127
power_parameters=power_parameters)
1128
response = self.client.put(
1129
self.get_node_uri(node),
1130
{'power_type': POWER_TYPE.DEFAULT})
1132
node = reload_object(node)
1134
(httplib.OK, node.power_type, node.power_parameters),
1135
(response.status_code, POWER_TYPE.DEFAULT, ''))
1137
def test_PUT_updates_power_type_default_rejects_params(self):
1138
# If one sets power_type to DEFAULT, on cannot set power_parameters.
1140
power_parameters = factory.getRandomString()
1141
node = factory.make_node(
1142
owner=self.logged_in_user,
1143
power_type=POWER_TYPE.WAKE_ON_LAN,
1144
power_parameters=power_parameters)
1145
new_param = factory.getRandomString()
1146
response = self.client.put(
1147
self.get_node_uri(node),
1149
'power_type': POWER_TYPE.DEFAULT,
1150
'power_parameters_address': new_param,
1153
node = reload_object(node)
1156
httplib.BAD_REQUEST,
1157
{'power_parameters': ["Unknown parameter(s): address."]}
1159
(response.status_code, json.loads(response.content)))
1161
power_parameters, reload_object(node).power_parameters)
1163
def test_PUT_updates_power_type_default_skip_check_to_force_params(self):
1164
# If one sets power_type to DEFAULT, it is possible to pass
1165
# power_parameter_skip_check='true' to force power_parameters.
1167
power_parameters = factory.getRandomString()
1168
node = factory.make_node(
1169
owner=self.logged_in_user,
1170
power_type=POWER_TYPE.WAKE_ON_LAN,
1171
power_parameters=power_parameters)
1172
new_param = factory.getRandomString()
1173
response = self.client.put(
1174
self.get_node_uri(node),
1176
'power_type': POWER_TYPE.DEFAULT,
1177
'power_parameters_param': new_param,
1178
'power_parameters_skip_check': 'true',
1181
node = reload_object(node)
1183
(httplib.OK, node.power_type, node.power_parameters),
1184
(response.status_code, POWER_TYPE.DEFAULT, {'param': new_param}))
1186
def test_PUT_updates_power_parameters_skip_ckeck(self):
1187
# With power_parameters_skip_check, arbitrary data
1188
# can be put in a Node's power_parameter field.
1190
node = factory.make_node(owner=self.logged_in_user)
1191
new_param = factory.getRandomString()
1192
new_value = factory.getRandomString()
1193
response = self.client.put(
1194
self.get_node_uri(node),
1196
'power_parameters_%s' % new_param: new_value,
1197
'power_parameters_skip_check': 'true',
1200
self.assertEqual(httplib.OK, response.status_code)
1202
{new_param: new_value}, reload_object(node).power_parameters)
1204
def test_PUT_updates_power_parameters_empty_string(self):
1206
node = factory.make_node(
1207
owner=self.logged_in_user,
1208
power_type=POWER_TYPE.WAKE_ON_LAN,
1209
power_parameters=factory.getRandomString())
1210
response = self.client.put(
1211
self.get_node_uri(node),
1212
{'power_parameters_power_address': ''})
1214
self.assertEqual(httplib.OK, response.status_code)
1216
{'power_address': ''},
1217
reload_object(node).power_parameters)
785
1219
def test_DELETE_deletes_node(self):
786
1220
# The api allows to delete a Node.
787
1221
self.become_admin()
1743
2178
(response.status_code, response.content))
1744
2179
self.assertRaises(
1745
2180
Node.DoesNotExist, Node.objects.get, hostname=hostname)
2183
class TestAnonymousCommissioningTimeout(APIv10TestMixin, TestCase):
2184
"""Testing of commissioning timeout API."""
2186
def test_check_with_no_action(self):
2187
node = factory.make_node(status=NODE_STATUS.READY)
2189
self.get_uri('nodes/'), {'op': 'check_commissioning'})
2190
# Anything that's not commissioning should be ignored.
2191
node = reload_object(node)
2192
self.assertEqual(NODE_STATUS.READY, node.status)
2194
def test_check_with_commissioning_but_not_expired_node(self):
2195
node = factory.make_node(
2196
status=NODE_STATUS.COMMISSIONING)
2198
self.get_uri('nodes/'), {'op': 'check_commissioning'})
2199
node = reload_object(node)
2200
self.assertEqual(NODE_STATUS.COMMISSIONING, node.status)
2202
def test_check_with_commissioning_and_expired_node(self):
2203
# Have an interval 1 second longer than the timeout.
2204
interval = timedelta(seconds=1, minutes=settings.COMMISSIONING_TIMEOUT)
2205
updated_at = datetime.now() - interval
2206
node = factory.make_node(
2207
status=NODE_STATUS.COMMISSIONING, created=datetime.now(),
2211
self.get_uri('nodes/'), {'op': 'check_commissioning'})
2212
node = reload_object(node)
2213
self.assertEqual(NODE_STATUS.FAILED_TESTS, node.status)