Example #1
0
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
Example #2
0
    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