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_wallet_owner_retrieval(client_bob, api_client, shipment_tracking_data, modified_http_pretty, profiles_ids, profiles_wallet_list_assertions): modified_http_pretty.register_uri(modified_http_pretty.GET, f"{settings.PROFILES_URL}/api/v1/wallet", body=json.dumps({'data': []}), status=status.HTTP_200_OK) url = reverse('shipment-overview', kwargs={'version': 'v1'}) response = client_bob.get(url) AssertionHelper.HTTP_200(response, vnd=True, is_list=True, count=1) modified_http_pretty.assert_calls(profiles_wallet_list_assertions) modified_http_pretty.register_uri( modified_http_pretty.GET, f"{settings.PROFILES_URL}/api/v1/wallet", body=json.dumps({ 'data': [ { 'id': profiles_ids['carrier_wallet_id'] }, { 'id': profiles_ids['shipper_wallet_id'] }, ] }), status=status.HTTP_200_OK) response = client_bob.get(url) AssertionHelper.HTTP_200(response, vnd=True, is_list=True, count=len(shipment_tracking_data)) assert len(response.json()['included']) == NUM_DEVICES - 1 modified_http_pretty.assert_calls(profiles_wallet_list_assertions)
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_history_documents(self, client_alice): document = Document.objects.create(name='Test Historical', owner_id=self.shipment.owner_id, shipment=self.shipment) response = client_alice.get(self.history_url) AssertionHelper.HTTP_200(response, is_list=True, count=2) # A document in upload_status pending shouldn't be present in the shipment diff history assert response.json()['data'][0]['relationships'] is None # A document with upload_status complete should be present in the shipment history document.upload_status = 1 document.save() response = client_alice.get(self.history_url) AssertionHelper.HTTP_200(response, is_list=True, count=3) history_documents = response.json()['data'][0]['relationships'].get( 'documents') assert history_documents assert 'upload_status' in self.get_changed_fields( history_documents['fields'], 'field') # Any subsequent document object update should be present in the diff change document.description = 'Document updated with some description for example' document.save() response = client_alice.get(self.history_url) AssertionHelper.HTTP_200(response, is_list=True, count=4) history_documents = response.json()['data'][0]['relationships'].get( 'documents') assert history_documents assert 'description' in self.get_changed_fields( history_documents['fields'], 'field')
def test_aggregate_success(self, client_alice, unsigned_telemetry_different_sensor): response = client_alice.get( f'{self.telemetry_url}?aggregate={Aggregates.average.name}&per={TimeTrunc.minutes.name}') AssertionHelper.HTTP_200(response, is_list=True, vnd=False, attributes={ 'value': self.unsigned_telemetry['value'] }) assert len(response.json()) == 1 self.unsigned_telemetry['value'] = 20 add_telemetry_data_to_model([self.unsigned_telemetry], self.shipment_alice_with_device) response = client_alice.get( f'{self.telemetry_url}?aggregate={Aggregates.average.name}&per={TimeTrunc.minutes.name}') self.unsigned_telemetry['value'] = 15 AssertionHelper.HTTP_200(response, is_list=True, vnd=False, attributes={ 'value': self.unsigned_telemetry['value'] }) assert len(response.json()) == 1 response = client_alice.get( f'{self.telemetry_url}?aggregate={Aggregates.minimum.name}&per={TimeTrunc.minutes.name}') AssertionHelper.HTTP_200(response, is_list=True, vnd=False, attributes={ 'value': 10 }) assert len(response.json()) == 1 response = client_alice.get( f'{self.telemetry_url}?aggregate={Aggregates.maximum.name}&per={TimeTrunc.minutes.name}') self.unsigned_telemetry['value'] = 20 AssertionHelper.HTTP_200(response, is_list=True, vnd=False, attributes={ 'value': self.unsigned_telemetry['value'] }) assert len(response.json()) == 1
def test_pickup_with_gtx_required(client_alice, shipment): assert shipment.pickup_act is None shipment.gtx_required = True shipment.save() url = reverse('shipment-actions', kwargs={ 'version': 'v1', 'shipment_pk': shipment.id }) action = {'action_type': ActionType.PICK_UP.name} # If gtx_required, pickup requires an asset_physical_id response = client_alice.post(url, data=action) AssertionHelper.HTTP_403( response, error= 'In order to proceed with this shipment pick up, you need to provide a ' 'value for the field [Shipment.asset_physical_id]') action['asset_physical_id'] = 'nfc_tag' 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}))
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_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_includes_legs(self, client_alice, new_route, shipment_alice_with_device, shipment): leg1 = new_route.routeleg_set.create(shipment=shipment) leg2 = new_route.routeleg_set.create(shipment=shipment_alice_with_device) leg1_entity = AssertionHelper.EntityRef(resource='RouteLeg', pk=leg1.pk, attributes={'shipment_id': shipment.pk}) leg2_entity = AssertionHelper.EntityRef(resource='RouteLeg', pk=leg2.pk, attributes={'shipment_id': shipment_alice_with_device.pk}) response = client_alice.get(self.url_route) AssertionHelper.HTTP_200( response, entity_refs=AssertionHelper.EntityRef( resource='Route', pk=new_route.id, attributes={ 'name': new_route.name, 'driver_id': new_route.driver_id, }, relationships={ 'legs': [leg1_entity, leg2_entity] }, ), included=[leg1_entity, leg2_entity] )
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_shipment_udate_customer_fields(self, client_alice): response = client_alice.patch(self.update_url, data={ 'customer_fields': { 'custom_field_1': 'value one', 'custom_field_2': 'value two' } }) AssertionHelper.HTTP_202(response) response = client_alice.get(self.history_url) AssertionHelper.HTTP_200(response, is_list=True, count=3) changed_fields = self.get_changed_fields( response.json()['data'][0]['fields'], 'field') assert 'customer_fields.custom_field_1' in changed_fields assert 'customer_fields.custom_field_2' in changed_fields response = client_alice.patch( self.update_url, data={'customer_fields': { 'custom_field_2': 'new value two' }}) AssertionHelper.HTTP_202(response) response = client_alice.get(self.history_url) AssertionHelper.HTTP_200(response, is_list=True, count=4) changed_fields = self.get_changed_fields( response.json()['data'][0]['fields'], 'field') assert 'customer_fields.custom_field_1' not in changed_fields assert 'customer_fields.custom_field_2' in changed_fields
def test_permission_link_email_send_multiform(self, client_alice, mock_aws_success, user_alice): multi_form_data, content_type = create_form_content({ 'name': 'Permission Link Name', 'emails': ['*****@*****.**', '*****@*****.**'] }) response = client_alice.post(self.url_permission_link_create, multi_form_data, content_type=content_type) AssertionHelper.HTTP_201( response, entity_refs=AssertionHelper.EntityRef( resource='PermissionLink', attributes={'name': 'Permission Link Name'}, relationships=self.shipment_alice_relationships)) assert len(mail.outbox) == 1 assert 'The ShipChain team' in str(mail.outbox[0].body) assert user_alice.username in str(mail.outbox[0].body) assert user_alice.username in str(mail.outbox[0].subject) pm_id = response.json()['data']['id'] mock_aws_success.assert_calls([{ 'path': '/test/', 'query': None, 'body': { 'long_url': f'http://localhost:3000/shipments/{self.shipment_alice.id}/?permission_link={pm_id}', 'expiration_date': None }, 'host': settings.URL_SHORTENER_HOST }])
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_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_tags_hidden_from_overview(self, shipment_alice, client_bob, new_rw_access_request_bob, devices, overview_tracking_data): new_rw_access_request_bob.tags_permission = PermissionLevel.NONE new_rw_access_request_bob.approved = True new_rw_access_request_bob.save() self.setup_devices_and_tracking(shipment_alice, devices[0], overview_tracking_data[0]) response = client_bob.get(self.shipment_overview) AssertionHelper.HTTP_200(response, is_list=True, entity_refs=[ AssertionHelper.EntityRef( resource='TrackingData', relationships=[{ 'shipment': AssertionHelper.EntityRef( resource='Shipment', pk=shipment_alice.id) }]), ]) response_json = response.json() for included in response_json['included']: assert included['type'] != 'ShipmentTag'
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_with_unassociated_device(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_device.return_value = device response = client_alice.post(self.url, route_attributes) AssertionHelper.HTTP_201( response, entity_refs=AssertionHelper.EntityRef( resource='Route', attributes={ 'name': route_attributes['name'], 'driver_id': route_attributes['driver_id'], }, relationships={ 'device': AssertionHelper.EntityRef( resource='Device', pk=route_attributes['device_id'] ), } ), included=AssertionHelper.EntityRef( resource='Device', pk=route_attributes['device_id'] ), )
def test_create_multiple_access_requests(self, shipment_alice, client_bob, access_request_ro_attributes, access_request_rw_attributes, user_bob_id): response = client_bob.post(self.list_url, access_request_ro_attributes) AssertionHelper.HTTP_201(response, entity_refs=AssertionHelper.EntityRef( resource='AccessRequest', attributes={ **{ 'requester_id': user_bob_id }, **access_request_ro_attributes })) response = client_bob.post(self.list_url, access_request_rw_attributes) AssertionHelper.HTTP_201(response, entity_refs=AssertionHelper.EntityRef( resource='AccessRequest', attributes={ **{ 'requester_id': user_bob_id }, **access_request_rw_attributes }))
def test_shipment_not_included(self, client_bob, new_access_request_bob): response = client_bob.get(self.list_url) AssertionHelper.HTTP_200(response, is_list=True, count=1) assert 'included' not in response.json() response = client_bob.get(self.shipment_list_url) AssertionHelper.HTTP_200(response, is_list=True, count=1) assert 'included' not in response.json()
def test_owner_can_create(self, client_alice): response = client_alice.post(self.url_permission_link_create, {'name': 'Permission Link Name'}) AssertionHelper.HTTP_201( response, entity_refs=AssertionHelper.EntityRef( resource='PermissionLink', attributes={'name': 'Permission Link Name'}, relationships=self.shipment_alice_relationships))
def test_meta_field_presence(self, shipment_alice, client_bob, approved_access_request_bob): response = client_bob.get(self.shipment_detail) AssertionHelper.HTTP_200( response, entity_refs=AssertionHelper.EntityRef( resource='Shipment', pk=shipment_alice.id, meta={'permission_derivation': 'AccessRequest'}))
def test_per_shipment_return(self, client_alice, entity_ref_document_shipment_alice, entity_ref_document_shipment_alice_two, entity_ref_document_shipment_two_alice): response = client_alice.get(self.shipment_alice_url) AssertionHelper.HTTP_200(response, is_list=True, entity_refs=[entity_ref_document_shipment_alice, entity_ref_document_shipment_alice_two], count=2) response = client_alice.get(self.shipment_alice_two_url) AssertionHelper.HTTP_200(response, entity_refs=[entity_ref_document_shipment_two_alice], count=1, is_list=True)
def test_ordering(self, client_alice, entity_ref_document_shipment_alice, entity_ref_document_shipment_alice_two): response = client_alice.get(f'{self.shipment_alice_url}?ordering=-created_at') AssertionHelper.HTTP_200(response, is_list=True, entity_refs=[entity_ref_document_shipment_alice_two, entity_ref_document_shipment_alice], check_ordering=True) response = client_alice.get(f'{self.shipment_alice_url}?ordering=-modified_at') AssertionHelper.HTTP_200(response, is_list=True, entity_refs=[entity_ref_document_shipment_alice_two, entity_ref_document_shipment_alice], check_ordering=True)
def test_with_attributes(self, client_alice, route_attributes): response = client_alice.post(self.url, route_attributes) AssertionHelper.HTTP_201( response, entity_refs=AssertionHelper.EntityRef( resource='Route', attributes={ 'name': route_attributes['name'], 'driver_id': route_attributes['driver_id'], }, ), )
def test_update_upload_status(self, client_alice, entity_ref_document_shipment_alice, mock_s3_buckets, mocker): response = client_alice.patch(self.document_alice_url, {'upload_status': UploadStatus.FAILED.name}) entity_ref_document_shipment_alice.attributes['upload_status'] = UploadStatus.FAILED.name AssertionHelper.HTTP_200(response, entity_ref_document_shipment_alice) assert isinstance(response.json()['data']['meta']['presigned_s3'], dict) mocker.patch('apps.documents.rpc.DocumentRPCClient.put_document_in_s3', return_value={'success': True}) response = client_alice.patch(self.document_alice_url, {'upload_status': UploadStatus.COMPLETE.name}) entity_ref_document_shipment_alice.attributes['description'] = UploadStatus.COMPLETE.name AssertionHelper.HTTP_200(response, entity_ref_document_shipment_alice) assert isinstance(response.json()['data']['meta']['presigned_s3'], str) assert isinstance(response.json()['data']['meta']['presigned_s3_thumbnail'], str)
def test_can_create_with_permission(self, client_bob, mock_successful_wallet_owner_calls): response = client_bob.post(self.url_permission_link_create, {'name': 'Permission Link Name'}) AssertionHelper.HTTP_201( response, entity_refs=AssertionHelper.EntityRef( resource='PermissionLink', attributes={'name': 'Permission Link Name'}, relationships=self.shipment_alice_relationships)) mock_successful_wallet_owner_calls.assert_calls( self.successful_wallet_owner_calls_assertions)
def test_geofence_creates(client_alice, mocked_iot_api, mocked_profiles, mocked_engine_rpc, profiles_ids, successful_shipment_create_profiles_assertions): url = reverse('shipment-list', kwargs={'version': 'v1'}) shipment_create_request = {"geofences": [GEOFENCE_3], **profiles_ids} response = client_alice.post(url, data=shipment_create_request) AssertionHelper.HTTP_202(response, entity_refs=AssertionHelper.EntityRef( resource='Shipment', attributes=shipment_create_request)) mocked_profiles.assert_calls( successful_shipment_create_profiles_assertions)
def test_creator_get(self, client_alice, new_route): response = client_alice.get(self.url_route) AssertionHelper.HTTP_200( response, entity_refs=AssertionHelper.EntityRef( resource='Route', pk=new_route.id, attributes={ 'name': new_route.name, 'driver_id': new_route.driver_id, } ) )
def test_org_shipment(self, client_alice, leg_attributes, second_shipment): leg_attributes['shipment_id'] = second_shipment.pk response = client_alice.post(self.url_route, data=leg_attributes) AssertionHelper.HTTP_201( response, entity_refs=AssertionHelper.EntityRef( resource='RouteLeg', attributes={ 'shipment_id': second_shipment.pk, }, ) )
def test_update_delete_shipment_note(client_alice, shipment, shipment_notes): url = reverse('shipment-notes-detail', kwargs={'version': 'v1', 'shipment_pk': shipment.id, 'pk': shipment_notes[0].id}) update_note_data = {'message': 'Update message!'} # A note object cannot be updated response = client_alice.patch(url, update_note_data) AssertionHelper.HTTP_405(response) # Similarly, a note object cannot be deleted response = client_alice.delete(url) AssertionHelper.HTTP_405(response)