def test_invalid_jws(self, api_client, shipment_alice_with_device): response = api_client.post(self.url_device, {'payload': { 'this': 'is not a jws' }}) AssertionHelper.HTTP_400( response, error='This value does not match the required pattern.') response = api_client.post(self.url_device, {'payload': 'neither.is.this'}) AssertionHelper.HTTP_400( response, error= "Invalid JWS: Invalid header string: 'utf-8' codec can't decode byte 0x9d in position 0: invalid start byte" ) response = api_client.post(self.url_device, {'payload': 'or.this'}) AssertionHelper.HTTP_400( response, error='This value does not match the required pattern.') response = api_client.post( self.url_device, {'payload': 'bm9ybm9ybm9y.aXNpc2lz.dGhpc3RoaXN0aGlz'}) AssertionHelper.HTTP_400( response, error= 'Invalid JWS: Invalid header string: Expecting value: line 1 column 1 (char 0)' )
def test_tracking_telemetry_ro(self, shipment_alice, client_bob, new_access_request_bob, access_request_rw_attributes): response = client_bob.patch( self.detail_url, {'tracking_permission': PermissionLevel.READ_WRITE.name}) AssertionHelper.HTTP_400(response, 'Cannot request write access to this field') response = client_bob.patch( self.detail_url, {'telemetry_permission': PermissionLevel.READ_WRITE.name}) AssertionHelper.HTTP_400(response, 'Cannot request write access to this field')
def test_create_shipment_note(user_alice, shipper_user, api_client, shipper_api_client, client_alice, shipment, mocked_is_shipper, successful_wallet_owner_calls_assertions): url = reverse('shipment-notes-list', kwargs={'version': 'v1', 'shipment_pk': shipment.id}) create_note_data, content_type = create_form_content({'message': MESSAGE_1}) # An unauthenticated user cannot create a shipment note response = api_client.post(url, {'message': MESSAGE_1}) AssertionHelper.HTTP_403(response, error='You do not have permission to perform this action.') # An authenticated request with a empty message should fail response = client_alice.post(url, {'message': ''}) AssertionHelper.HTTP_400(response, error='This field may not be blank.', pointer='message') # An authenticated request with a message with more than 500 characters should fail response = client_alice.post(url, {'message': MESSAGE_2}) AssertionHelper.HTTP_400(response, error='Ensure this value has at most 500 characters (it has 632).', pointer='message') # An authenticated user can create a shipment note response = client_alice.post(url, create_note_data, content_type=content_type) AssertionHelper.HTTP_201(response, entity_refs=AssertionHelper.EntityRef(resource='ShipmentNote', attributes={ 'message': MESSAGE_1, 'user_id': user_alice.id, 'username': get_username(user_alice), 'organization_name': get_user_org_name( user_alice)}, relationships={'shipment': AssertionHelper.EntityRef( resource='Shipment', pk=shipment.id)}) ) # A shipper also valid for moderator and carrier can add a shipment note response = shipper_api_client.post(url, {'message': MESSAGE_1}) AssertionHelper.HTTP_201(response, entity_refs=AssertionHelper.EntityRef(resource='ShipmentNote', attributes={ 'message': MESSAGE_1, 'user_id': shipper_user.id, 'username': get_username(shipper_user)}, relationships={ 'shipment': AssertionHelper.EntityRef( resource='Shipment', pk=shipment.id)}) ) mocked_is_shipper.assert_calls(successful_wallet_owner_calls_assertions)
def test_profiles_disabled_shipment_tag_creation( api_client, shipment, shipment_tag_creation_data, shipment_tag_creation_missing_owner_id, entity_shipment_relationship): url = reverse('shipment-tags-list', kwargs={ 'version': 'v1', 'shipment_pk': shipment.id }) # A request without user_id should fail response = api_client.post(url, shipment_tag_creation_missing_owner_id) AssertionHelper.HTTP_400(response, error='This field is required.') response = api_client.post(url, shipment_tag_creation_data) AssertionHelper.HTTP_201( response, entity_refs=AssertionHelper.EntityRef( resource='ShipmentTag', attributes={ 'tag_type': shipment_tag_creation_data['tag_type'], 'tag_value': shipment_tag_creation_data['tag_value'], 'owner_id': USER_ID }, relationships={'shipment': entity_shipment_relationship}))
def test_pickup(client_alice, shipment): assert shipment.pickup_act is None url = reverse('shipment-actions', kwargs={ 'version': 'v1', 'shipment_pk': shipment.id }) action = {'action_type': ActionType.PICK_UP.name} response = client_alice.post(url, data=action) AssertionHelper.HTTP_200( response, entity_refs=AssertionHelper.EntityRef( resource='Shipment', pk=shipment.id, attributes={'state': TransitState.IN_TRANSIT.name})) updated_parameters = response.json()['data']['attributes'] assert datetimeAlmostEqual(dt_parse(updated_parameters['pickup_act'])) # Can't pickup when IN_TRANSIT response = client_alice.post(url, data=action) AssertionHelper.HTTP_400( response, error= 'Action PICK_UP not available while Shipment is in state IN_TRANSIT', pointer='action_type')
def test_shipment_creation_with_assignee(client_alice, mocked_is_shipper, mocked_storage_credential, shipment): url = reverse('shipment-list', kwargs={'version': 'v1'}) valid_uuid4_data = { 'storage_credentials_id': shipment.storage_credentials_id, 'shipper_wallet_id': shipment.shipper_wallet_id, 'carrier_wallet_id': shipment.carrier_wallet_id, 'assignee_id': VALID_UUID4, } invalid_uuid4_data = { 'storage_credentials_id': shipment.storage_credentials_id, 'shipper_wallet_id': shipment.shipper_wallet_id, 'carrier_wallet_id': shipment.carrier_wallet_id, 'assignee_id': VALID_UUID4[:-1], } # A shipment cannot be created with an invalid assignee ID response = client_alice.post(url, data=invalid_uuid4_data) AssertionHelper.HTTP_400(response, error='Must be a valid UUID.', pointer='assignee_id') # With a valid assignee ID the request should succeed response = client_alice.post(url, data=valid_uuid4_data) AssertionHelper.HTTP_202(response, entity_refs=AssertionHelper.EntityRef( resource='Shipment', attributes={'assignee_id': VALID_UUID4}))
def test_shipment_update_with_assignee(client_alice, shipment): url = reverse('shipment-detail', kwargs={ 'version': 'v1', 'pk': shipment.id }) valid_uuid4_data = { 'assignee_id': VALID_UUID4, } invalid_uuid4_data = { 'assignee_id': VALID_UUID4 + '12', } # A shipment cannot be updated with an invalid assignee ID response = client_alice.patch(url, data=invalid_uuid4_data) AssertionHelper.HTTP_400(response, error='Must be a valid UUID.', pointer='assignee_id') # With a valid assignee ID the request should succeed response = client_alice.patch(url, data=valid_uuid4_data) AssertionHelper.HTTP_202(response, entity_refs=AssertionHelper.EntityRef( resource='Shipment', pk=shipment.id, attributes={'assignee_id': VALID_UUID4}))
def test_ro_fields(self, client_bob, user_alice_id, user_bob_id, access_request_ro_attributes, current_datetime): response = client_bob.post(self.list_url, { **access_request_ro_attributes, **{ 'approved': True } }) AssertionHelper.HTTP_400( response, 'User does not have access to approve this access request') response = client_bob.post(self.list_url, { **access_request_ro_attributes, **{ 'approved_at': current_datetime } }) AssertionHelper.HTTP_201(response, attributes={'approved_at': None}) response = client_bob.post(self.list_url, { **access_request_ro_attributes, **{ 'approved_by': user_alice_id } }) AssertionHelper.HTTP_201(response, attributes={'approved_by': None}) response = client_bob.post(self.list_url, { **access_request_ro_attributes, **{ 'requester_id': user_alice_id } }) AssertionHelper.HTTP_201(response, attributes={'requester_id': user_bob_id})
def test_file_types(self, client_alice, entity_ref_shipment_alice): attributes = { 'name': 'Test BOL', 'file_type': 'NOT A FILE TYPE', 'document_type': 'BOL', } response = client_alice.post(self.shipment_alice_url, attributes) AssertionHelper.HTTP_400(response, error=f'"{attributes["file_type"]}" is not a valid choice.', pointer='file_type') attributes['file_type'] = FileType.PDF.name response = client_alice.post(self.shipment_alice_url, attributes) AssertionHelper.HTTP_201(response, entity_refs=AssertionHelper.EntityRef( resource='Document', attributes={ 'upload_status': UploadStatus.PENDING.name, **attributes }, relationships=[{ 'shipment': entity_ref_shipment_alice }], meta={ 'presigned_s3_thumbnail': None } )) assert isinstance(response.json()['data']['meta']['presigned_s3'], dict)
def test_timestamp(self, client_alice, unsigned_telemetry_different_hardware, current_datetime): telemetry_copy = deepcopy(self.unsigned_telemetry) telemetry_copy['timestamp'] = (current_datetime + timedelta(days=1)).isoformat().replace('+00:00', 'Z') add_telemetry_data_to_model([telemetry_copy], self.shipment_alice_with_device) valid_timestamp = (current_datetime + timedelta(hours=12)).isoformat().replace('+00:00', 'Z') invalid_before_timestamp = (current_datetime + timedelta(hours=18)).isoformat().replace('+00:00', 'Z') invalid_after_timestamp = (current_datetime + timedelta(hours=6)).isoformat().replace('+00:00', 'Z') response = client_alice.get(f'{self.telemetry_url}?before=NOT A TIMESTAMP') AssertionHelper.HTTP_400(response) assert response.json()['before'] == ['Enter a valid date/time.'] response = client_alice.get(f'{self.telemetry_url}?before={valid_timestamp}') self.unsigned_telemetry.pop('version') AssertionHelper.HTTP_200(response, is_list=True, vnd=False, attributes=self.unsigned_telemetry, count=1) response = client_alice.get(f'{self.telemetry_url}?after={valid_timestamp}') telemetry_copy.pop('version') AssertionHelper.HTTP_200(response, is_list=True, vnd=False, attributes=telemetry_copy, count=1) response = client_alice.get(f'{self.telemetry_url}?before={valid_timestamp}&after={invalid_after_timestamp}') assert response.status_code == status.HTTP_400_BAD_REQUEST assert response.json()[0] == \ f'Invalid timemismatch applied. Before timestamp {valid_timestamp} is greater than after: {invalid_after_timestamp}' response = client_alice.get(f'{self.telemetry_url}?after={valid_timestamp}&before={invalid_before_timestamp}') assert response.status_code == status.HTTP_400_BAD_REQUEST assert response.json()[0] == \ f'Invalid timemismatch applied. Before timestamp {invalid_before_timestamp} is greater than after: {valid_timestamp}'
def test_arrival(client_alice, shipment): assert shipment.port_arrival_act is None url = reverse('shipment-actions', kwargs={ 'version': 'v1', 'shipment_pk': shipment.id }) action = {'action_type': ActionType.ARRIVAL.name} response = client_alice.post(url, data=action) AssertionHelper.HTTP_400( response, error= 'Action ARRIVAL not available while Shipment is in state AWAITING_PICKUP', pointer='action_type') shipment.pick_up() shipment.save() response = client_alice.post(url, data=action) AssertionHelper.HTTP_200( response, entity_refs=AssertionHelper.EntityRef( resource='Shipment', pk=shipment.id, attributes={'state': TransitState.AWAITING_DELIVERY.name})) updated_parameters = response.json()['data']['attributes'] assert datetimeAlmostEqual(dt_parse( updated_parameters['port_arrival_act'])) # Can't pickup or arrive when AWAITING_DELIVERY response = client_alice.post(url, data=action) AssertionHelper.HTTP_400( response, error= 'Action ARRIVAL not available while Shipment is in state AWAITING_DELIVERY', pointer='action_type') action = {'action_type': ActionType.PICK_UP.name} response = client_alice.post(url, data=action) AssertionHelper.HTTP_400( response, error= 'Action PICK_UP not available while Shipment is in state AWAITING_DELIVERY', pointer='action_type')
def test_route_in_progress_fails(self, client_alice, leg_attributes, shipment_on_new_route, shipment_alice): shipment_on_new_route.pick_up() shipment_on_new_route.save() leg_attributes['shipment_id'] = shipment_alice.pk response = client_alice.post(self.url_route, data=leg_attributes) AssertionHelper.HTTP_400(response, error='Cannot add shipment to route after transit has begun')
def test_double_approval(self, client_alice, approved_access_request_bob, access_request_ro_attributes): response = client_alice.patch(self.detail_url, { 'approved': True, **access_request_ro_attributes }) AssertionHelper.HTTP_400( response, 'This access request has already been approved')
def test_shipment_in_progress_fails(self, client_alice, leg_attributes, shipment): shipment.pick_up() shipment.save() leg_attributes['shipment_id'] = shipment.pk response = client_alice.post(self.url_route, data=leg_attributes) AssertionHelper.HTTP_400(response, error='Shipment already picked up, cannot add to route')
def test_with_device_not_authorized_fails(self, client_alice, route_attributes, device): route_attributes['device_id'] = device.id with mock.patch('apps.shipments.models.Device.get_or_create_with_permission') as mock_device: mock_error = 'mock unauthorized error' mock_device.side_effect = ValidationError(mock_error) response = client_alice.post(self.url, route_attributes) AssertionHelper.HTTP_400(response, error=mock_error)
def test_permission_link_email_validation(self, client_alice): response = client_alice.post( self.url_permission_link_create, { 'name': 'Permission Link Name', 'emails': ['not an email', '*****@*****.**'] }) AssertionHelper.HTTP_400(response, error='Enter a valid email address.', pointer='emails')
def test_required_fields(self, client_alice): response = client_alice.post(self.shipment_alice_url, { 'document_type': 'Bol', 'file_type': 'Pdf' }) AssertionHelper.HTTP_400(response, error='This field is required.', pointer='name') response = client_alice.post(self.shipment_alice_url, { 'name': 'Test BOL', 'file_type': 'Pdf' }) AssertionHelper.HTTP_400(response, error='This field is required.', pointer='document_type') response = client_alice.post(self.shipment_alice_url, { 'name': 'Test BOL', 'document_type': 'Bol', }) AssertionHelper.HTTP_400(response, error='This field is required.', pointer='file_type')
def test_tags_without_shipment(self, shipment_alice, client_bob): response = client_bob.patch( self.detail_url, { 'shipment_permission': PermissionLevel.NONE.name, 'tags_permission': PermissionLevel.READ_WRITE.name }) AssertionHelper.HTTP_400( response, 'Cannot request to view tags without shipment read access')
def test_create_requires_fields(self, client_alice): self.base_call['tags'] = [ { 'tag_type': 'tag_type' }, ] response = client_alice.post(self.url, self.base_call) AssertionHelper.HTTP_400(response, 'Tags items must contain `tag_value` and `tag_type`.') self.wallet_mocking.assert_calls(self.assertions) self.base_call['tags'] = [ { 'tag_value': 'tag_value' }, ] response = client_alice.post(self.url, self.base_call) AssertionHelper.HTTP_400(response, 'Tags items must contain `tag_value` and `tag_type`.') self.wallet_mocking.assert_calls(self.assertions)
def test_route_complete_fails(self, client_alice, shipment): shipment.pick_up() shipment.save() shipment.arrival() shipment.save() shipment.drop_off() shipment.save() response = client_alice.delete(self.url_route) AssertionHelper.HTTP_400(response, error='Cannot remove shipment from route after transit has begun')
def test_org_user_shipment_tag(org_id_alice, api_client, client_alice, shipment, shipment_tag_creation_data, missing_tag_type_creation_data, missing_tag_value_creation_data, space_in_tag_type_creation_data, space_in_tag_value_creation_data): url = reverse('shipment-tags-list', kwargs={'version': 'v1', 'shipment_pk': shipment.id}) # An unauthenticated user cannot tag a shipment response = api_client.post(url, shipment_tag_creation_data) AssertionHelper.HTTP_403(response, error='You do not have permission to perform this action.') # An org user cannot tag a shipment with missing tag_type in creation data response = client_alice.post(url, missing_tag_type_creation_data) AssertionHelper.HTTP_400(response, error='This field is required.') # An org user cannot tag a shipment with missing tag_value in creation data response = client_alice.post(url, missing_tag_value_creation_data) AssertionHelper.HTTP_400(response, error='This field is required.') # An org user cannot tag a shipment with space in tag_value field creation data response = client_alice.post(url, space_in_tag_value_creation_data) AssertionHelper.HTTP_400(response, error='Space(s) not allowed in this field') # An org user cannot tag a shipment with space in tag_type field creation data response = client_alice.post(url, space_in_tag_type_creation_data) AssertionHelper.HTTP_400(response, error='Space(s) not allowed in this field') # An org user with proper tag data definition, should tag a shipment response = client_alice.post(url, shipment_tag_creation_data) AssertionHelper.HTTP_201(response, entity_refs=AssertionHelper.EntityRef( resource='ShipmentTag', attributes={'tag_type': shipment_tag_creation_data['tag_type'], 'tag_value': shipment_tag_creation_data['tag_value'], 'owner_id': org_id_alice}, relationships={ 'shipment': AssertionHelper.EntityRef(resource='Shipment', pk=shipment.id) }) ) # Trying to tag a shipment with an existing (tag_type, tag_value) pair should fail response = client_alice.post(url, shipment_tag_creation_data) AssertionHelper.HTTP_400(response, error='This shipment already has a tag with the provided [tag_type] and [tag_value].')
def test_cannot_approve_and_modify(self, new_access_request_bob, client_alice, access_request_rw_attributes): response = client_alice.patch(self.detail_url, { 'approved': True, **access_request_rw_attributes }) AssertionHelper.HTTP_400( response, 'Only the requester can modify permissions in a pending or denied access request' )
def test_with_device_blocked_on_shipment_fails(self, client_alice, route_attributes, shipment_alice_with_device): route_attributes['device_id'] = shipment_alice_with_device.device.id shipment_alice_with_device.pick_up() shipment_alice_with_device.save() with mock.patch('apps.shipments.models.Device.get_or_create_with_permission') as mock_device: mock_device.return_value = shipment_alice_with_device.device response = client_alice.post(self.url, route_attributes) AssertionHelper.HTTP_400(response, error='Device is already assigned to a Shipment in progress')
def test_with_device_blocked_on_route_fails(self, client_alice, route_attributes, new_route_with_device, shipment): route_attributes['device_id'] = new_route_with_device.device.id new_route_with_device.routeleg_set.create(shipment=shipment) shipment.pick_up() shipment.save() with mock.patch('apps.shipments.models.Device.get_or_create_with_permission') as mock_device: mock_device.return_value = new_route_with_device.device response = client_alice.patch(self.url_route, route_attributes) AssertionHelper.HTTP_400(response, error='Device is already assigned to a Route in progress')
def test_cant_request_no_permissions(self, client_bob): # All permissions default to NONE so a blank request should be rejected response = client_bob.post(self.list_url) AssertionHelper.HTTP_400( response, 'Access requests must contain at least one requested permission') # A request that explicitly requests all NONE permissions should also be rejected response = client_bob.post( self.list_url, { 'shipment_permission': PermissionLevel.NONE.name, 'tags_permission': PermissionLevel.NONE.name, 'documents_permission': PermissionLevel.NONE.name, 'notes_permission': PermissionLevel.NONE.name, 'tracking_permission': PermissionLevel.NONE.name, 'telemetry_permission': PermissionLevel.NONE.name }) AssertionHelper.HTTP_400( response, 'Access requests must contain at least one requested permission')
def test_requires_valid_expiration_date(self, client_alice): response = client_alice.post( self.url_permission_link_create, { 'name': 'Permission Link Name', 'expiration_date': datetime.utcnow() - timedelta(days=1) }) AssertionHelper.HTTP_400( response, error='The expiration date should be greater than actual date', pointer='expiration_date')
def test_approved_permissions_immutability(self, approved_access_request_bob, client_alice, client_bob, access_request_rw_attributes): assert approved_access_request_bob.approved is True for api_client in (client_alice, client_bob): response = api_client.patch(self.detail_url, access_request_rw_attributes) AssertionHelper.HTTP_400( response, 'Cannot modify the permission level of an approved access request' )
def test_tag_value_uniqueness(self, client_alice, org_id_alice, shipment): ShipmentTag.objects.create( tag_type=self.shipment_tags[0].tag_type, tag_value=self.shipment_tags[1].tag_value, shipment_id=shipment.id, owner_id=uuid.UUID(org_id_alice) ), response = client_alice.patch(getattr(self, f'url_{self.shipment_tags[0].tag_type}'), { 'tag_value': self.shipment_tags[1].tag_value }) AssertionHelper.HTTP_400(response, 'This shipment already has a tag with the provided [tag_type] and [tag_value].')
def test_cant_request_no_permissions(self, client_bob): response = client_bob.patch( self.detail_url, { 'shipment_permission': PermissionLevel.NONE.name, 'tags_permission': PermissionLevel.NONE.name, 'documents_permission': PermissionLevel.NONE.name, 'notes_permission': PermissionLevel.NONE.name, 'tracking_permission': PermissionLevel.NONE.name, 'telemetry_permission': PermissionLevel.NONE.name }) AssertionHelper.HTTP_400( response, 'Access requests must contain at least one requested permission')
def test_remove_device_in_progress_fails(self, client_alice, route_attributes, new_route_with_device, shipment): route_attributes['device_id'] = None new_route_with_device.routeleg_set.create(shipment=shipment) shipment.pick_up() shipment.save() response = client_alice.patch(self.url_route_device, data=route_attributes) print(response.json()) AssertionHelper.HTTP_400(response, error='Cannot remove device from Route in progress') route = Route.objects.get(pk=new_route_with_device.id) assert route.device