def _enable_use_docker_in_memory( working_database: VuforiaDatabase, inactive_database: VuforiaDatabase, monkeypatch: MonkeyPatch, ) -> Generator: # We set ``wsgi.input_terminated`` to ``True`` so that when going through # ``requests``, the Flask applications # have the given ``Content-Length`` headers and the given data in # ``request.headers`` and ``request.data``. # # We do not set these in the Flask application itself. # This is because when running the Flask application, if this is set, # reading ``request.data`` hangs. # # Therefore, when running the real Flask application, the behavior is not # the same as the real Vuforia. # This is documented as a difference in the documentation for this package. VWS_FLASK_APP.config['TERMINATE_WSGI_INPUT'] = True CLOUDRECO_FLASK_APP.config['TERMINATE_WSGI_INPUT'] = True target_manager_base_url = 'http://example.com' monkeypatch.setenv( name='TARGET_MANAGER_BASE_URL', value=target_manager_base_url, ) with requests_mock.Mocker(real_http=False) as mock: add_flask_app_to_mock( mock_obj=mock, flask_app=VWS_FLASK_APP, base_url='https://vws.vuforia.com', ) add_flask_app_to_mock( mock_obj=mock, flask_app=CLOUDRECO_FLASK_APP, base_url='https://cloudreco.vuforia.com', ) add_flask_app_to_mock( mock_obj=mock, flask_app=TARGET_MANAGER_FLASK_APP, base_url=target_manager_base_url, ) databases_url = target_manager_base_url + '/databases' databases = requests.get(url=databases_url).json() for database in databases: database_name = database['database_name'] requests.delete(url=databases_url + '/' + database_name) requests.post(url=databases_url, json=working_database.to_dict()) requests.post(url=databases_url, json=inactive_database.to_dict()) yield
def test_to_dict(self, high_quality_image: io.BytesIO) -> None: """ It is possible to dump a database to a dictionary and load it back. """ database = VuforiaDatabase() vws_client = VWS( server_access_key=database.server_access_key, server_secret_key=database.server_secret_key, ) # We test a database with a target added. with MockVWS() as mock: mock.add_database(database=database) vws_client.add_target( name='example', width=1, image=high_quality_image, active_flag=True, application_metadata=None, ) database_dict = database.to_dict() # The dictionary is JSON dump-able assert json.dumps(database_dict) new_database = VuforiaDatabase.from_dict(database_dict=database_dict) assert new_database == database
def test_custom( self, high_quality_image: io.BytesIO, monkeypatch: MonkeyPatch, ) -> None: """ It is possible to use set a custom amount of time that it takes for the Query API on the mock to process that a target has been deleted. """ # We choose a low time for a quick test. query_processes_deletion = 0.1 database = VuforiaDatabase() databases_url = _EXAMPLE_URL_FOR_TARGET_MANAGER + '/databases' requests.post(url=databases_url, json=database.to_dict()) monkeypatch.setenv( name='DELETION_PROCESSING_SECONDS', value=str(query_processes_deletion), ) time_taken = process_deletion_seconds( high_quality_image=high_quality_image, vuforia_database=database, ) expected = query_processes_deletion assert abs(expected - time_taken) < self.LEEWAY
def test_duplicate_keys(self) -> None: """ It is not possible to have multiple databases with matching keys. """ database = VuforiaDatabase( server_access_key='1', server_secret_key='2', client_access_key='3', client_secret_key='4', database_name='5', ) bad_server_access_key_db = VuforiaDatabase(server_access_key='1') bad_server_secret_key_db = VuforiaDatabase(server_secret_key='2') bad_client_access_key_db = VuforiaDatabase(client_access_key='3') bad_client_secret_key_db = VuforiaDatabase(client_secret_key='4') bad_database_name_db = VuforiaDatabase(database_name='5') server_access_key_conflict_error = ( 'All server access keys must be unique. ' 'There is already a database with the server access key "1".') server_secret_key_conflict_error = ( 'All server secret keys must be unique. ' 'There is already a database with the server secret key "2".') client_access_key_conflict_error = ( 'All client access keys must be unique. ' 'There is already a database with the client access key "3".') client_secret_key_conflict_error = ( 'All client secret keys must be unique. ' 'There is already a database with the client secret key "4".') database_name_conflict_error = ( 'All names must be unique. ' 'There is already a database with the name "5".') databases_url = _EXAMPLE_URL_FOR_TARGET_MANAGER + '/databases' requests.post(url=databases_url, json=database.to_dict()) for bad_database, expected_message in ( (bad_server_access_key_db, server_access_key_conflict_error), (bad_server_secret_key_db, server_secret_key_conflict_error), (bad_client_access_key_db, client_access_key_conflict_error), (bad_client_secret_key_db, client_secret_key_conflict_error), (bad_database_name_db, database_name_conflict_error), ): response = requests.post( url=databases_url, json=bad_database.to_dict(), ) assert response.status_code == HTTPStatus.CONFLICT assert response.text == expected_message
def test_default( self, image_file_failed_state: io.BytesIO, ) -> None: """ By default, targets in the mock take 0.5 seconds to be processed. """ database = VuforiaDatabase() databases_url = _EXAMPLE_URL_FOR_TARGET_MANAGER + '/databases' requests.post(url=databases_url, json=database.to_dict()) time_taken = processing_time_seconds( vuforia_database=database, image=image_file_failed_state, ) expected = 0.5 assert abs(expected - time_taken) < self.LEEWAY
def test_default( self, high_quality_image: io.BytesIO, ) -> None: """ By default it takes three seconds for the Query API on the mock to process that a target has been deleted. The real Query API takes between seven and thirty seconds. See ``test_query`` for more information. """ database = VuforiaDatabase() databases_url = _EXAMPLE_URL_FOR_TARGET_MANAGER + '/databases' requests.post(url=databases_url, json=database.to_dict()) time_taken = process_deletion_seconds( high_quality_image=high_quality_image, vuforia_database=database, ) expected = 3 assert abs(expected - time_taken) < self.LEEWAY
def test_custom( self, image_file_failed_state: io.BytesIO, monkeypatch: MonkeyPatch, ) -> None: """ It is possible to set a custom processing time. """ monkeypatch.setenv( name='PROCESSING_TIME_SECONDS', value='0.1', ) database = VuforiaDatabase() databases_url = _EXAMPLE_URL_FOR_TARGET_MANAGER + '/databases' requests.post(url=databases_url, json=database.to_dict()) time_taken = processing_time_seconds( vuforia_database=database, image=image_file_failed_state, ) expected = 0.1 assert abs(expected - time_taken) < self.LEEWAY
def test_build_and_run( high_quality_image: io.BytesIO, custom_bridge_network: Network, ) -> None: """ It is possible to build Docker images which combine to make a working mock application. """ repository_root = Path(__file__).parent.parent.parent client = docker.from_env() dockerfile_dir = repository_root / 'src/mock_vws/_flask_server/dockerfiles' target_manager_dockerfile = (dockerfile_dir / 'target_manager' / 'Dockerfile') vws_dockerfile = dockerfile_dir / 'vws' / 'Dockerfile' vwq_dockerfile = dockerfile_dir / 'vwq' / 'Dockerfile' random = uuid.uuid4().hex target_manager_tag = 'vws-mock-target-manager:latest-' + random vws_tag = 'vws-mock-vws:latest-' + random vwq_tag = 'vws-mock-vwq:latest-' + random try: target_manager_image, _ = client.images.build( path=str(repository_root), dockerfile=str(target_manager_dockerfile), tag=target_manager_tag, ) except docker.errors.BuildError as exc: full_log = '\n'.join( [item['stream'] for item in exc.build_log if 'stream' in item], ) # If this assertion fails, it may be useful to look at the other # properties of ``exc``. assert 'no matching manifest for windows/amd64' in exc.msg, full_log reason = 'We do not currently support using Windows containers.' pytest.skip(reason) vws_image, _ = client.images.build( path=str(repository_root), dockerfile=str(vws_dockerfile), tag=vws_tag, ) vwq_image, _ = client.images.build( path=str(repository_root), dockerfile=str(vwq_dockerfile), tag=vwq_tag, ) database = VuforiaDatabase() target_manager_container_name = 'vws-mock-target-manager-' + random target_manager_base_url = f'http://{target_manager_container_name}:5000' target_manager_container = client.containers.run( image=target_manager_image, detach=True, name=target_manager_container_name, publish_all_ports=True, network=custom_bridge_network.name, ) vws_container = client.containers.run( image=vws_image, detach=True, name='vws-mock-vws-' + random, publish_all_ports=True, network=custom_bridge_network.name, environment={'TARGET_MANAGER_BASE_URL': target_manager_base_url}, ) vwq_container = client.containers.run( image=vwq_image, detach=True, name='vws-mock-vwq-' + random, publish_all_ports=True, network=custom_bridge_network.name, environment={'TARGET_MANAGER_BASE_URL': target_manager_base_url}, ) target_manager_container.reload() target_manager_port_attrs = target_manager_container.attrs[ 'NetworkSettings']['Ports'] target_manager_host_ip = target_manager_port_attrs['5000/tcp'][0]['HostIp'] target_manager_host_port = target_manager_port_attrs['5000/tcp'][0][ 'HostPort'] vws_container.reload() vws_port_attrs = vws_container.attrs['NetworkSettings']['Ports'] vws_host_ip = vws_port_attrs['5000/tcp'][0]['HostIp'] vws_host_port = vws_port_attrs['5000/tcp'][0]['HostPort'] vwq_container.reload() vwq_port_attrs = vwq_container.attrs['NetworkSettings']['Ports'] vwq_host_ip = vwq_port_attrs['5000/tcp'][0]['HostIp'] vwq_host_port = vwq_port_attrs['5000/tcp'][0]['HostPort'] target_manager_host_url = ( f'http://{target_manager_host_ip}:{target_manager_host_port}') response = requests.post( url=f'{target_manager_host_url}/databases', json=database.to_dict(), ) assert response.status_code == HTTPStatus.CREATED vws_client = VWS( server_access_key=database.server_access_key, server_secret_key=database.server_secret_key, base_vws_url=f'http://{vws_host_ip}:{vws_host_port}', ) target_id = vws_client.add_target( name='example', width=1, image=high_quality_image, active_flag=True, application_metadata=None, ) vws_client.wait_for_target_processed(target_id=target_id) cloud_reco_client = CloudRecoService( client_access_key=database.client_access_key, client_secret_key=database.client_secret_key, base_vwq_url=f'http://{vwq_host_ip}:{vwq_host_port}', ) matching_targets = cloud_reco_client.query(image=high_quality_image) for container in (target_manager_container, vws_container, vwq_container): container.stop() container.remove() assert matching_targets[0].target_id == target_id