Пример #1
0
    def get_color(self,
                  hex,
                  format='json',
                  expect_status=200,
                  verbose=True,
                  **kwargs):
        """
            Grab the information for one color as specified by the
            color's hexadecimal code.

            This endpoint returns a list of json dicts, even though there
            is only one color returned. The calling code will have to
            handle this list; for example:

            >>> res = self.color_endpoint.get_color('000000')
            >>> assert res.json()[0]['hex'] == '000000'

            :param hex: str, 6-character hex for color
            :param format: str, either `xml` or `json`; defaults to `json`
            :param verbose: Bool, whether to output additional logging information
            :param kwargs: dict of key-value pairs of additional parameters
            :return res: Requests response object
        """
        kwargs['format'] = format
        url = self.endpoint_url + hex
        logger.info('hex: "%s"; url will be "%s".' % (hex, url))
        logger.info('kwargs = %s' % kwargs)

        # pass to the base endpoints *requests* wrapper
        res = self.get(url, expect_status=expect_status, **kwargs)

        logger.info('Response status code is "%s:".' % res.status_code)
        if verbose:
            logger.info('Response json: \n%s' % plog(res.json()))
        return res
Пример #2
0
    def get_colors(self, format='json', verbose=True, **kwargs):
        """
            Grab the first n-results for the parameters passed
            to the colors endpoint.


            This endpoint returns a list of json dicts, even though there
            is only one color returned. The calling code will have to handle
            that. for example:

            >>> res = self.colors_endpoint.get_colors('000000')
            >>> assert res.json()[0]['hex'] == '000000'

            :param format: str, either `xml` or `json`; defaults to `json`
            :param verbose: Bool, whether to output additional logging information
            :param kwargs: dict of key-value pairs of additional parameters
            :return res: Request response object with a json list of dicts
        """
        kwargs['format'] = format
        logger.info('kwargs = %s' % kwargs)

        # pass to the base endpoints *requests* wrapper
        res = self.get(self.endpoint_url, **kwargs)

        logger.info('Response status code is "%s:".' % res.status_code)
        if verbose:
            logger.info('Response json: \n%s' % plog(res.json()))
        return res
Пример #3
0
    def test_get_single_name(self, genderizer, good_name):
        """
            For the supplied name and expected gender, verify that the API
            response contains the name and expected gender.

            :param good_name: tuple, str name and str gender
            :return: None
        """
        api = self.gender_endpoint
        name = good_name[0]
        expected_name = good_name[0]
        ex_gender = good_name[1]

        res = api.get_gender(name, verbose=True)

        # test point: verify the correct response for a correct api call
        assert res.status_code == 200
        logger.info(utils.plog(res.json()))

        # test point: verify that we get back the same name that we requested
        assert res.json()['name'] == expected_name, \
            'expected "%s", but got "%s"' \
            % (expected_name, res.json()['name'])

        # test point: verify that we got the expected gender assignment
        assert api.got_gender(res, ex_gender)

        # test point: verify that the json keys are correct
        assert api.verify_keys_in_response(res.json().keys())

        # test point: verify that the json schema is correct
        # using the new schema validator approach
        assert api.validate_schema(res.json(), verbose=True)
Пример #4
0
    def test_colordata(self, colourlovers, colors_data):
        """
            This is a parametrized test case using a data source consisting of a list of lists.
            Pytest will iterate over the data model and call this test method once for each top
            level item in that data model. For each call, we get a list consisting of a color
            name, its hex code, and its RGB values.

            The hex string is the second element in this data, so we get that by grabbing it by
            calling `colors_data[1]`. Then, we need to drop the prepended `#` by
            calling `colors_data[1][1:]`

            :param colors_data: list consisting of str color name, str hex code, str RGB values
            :return: None
        """
        logger.info('test data = "%s".' % colors_data)

        # setup: extract the hex string from the parametrized data input and make API request
        this_hex = colors_data[1][1:]
        res = self.color_endpoint.get_color(this_hex)

        # test point: verify the correct response for a correct api call
        assert res.status_code == 200
        logger.info(utils.plog(res.json()))

        # test point: verify that we get back the same hex that we requested
        assert res.json()[0]['hex'] == this_hex

        # test point: verify that the json keys are correct
        assert self.color_endpoint.verify_keys_in_response(res.json()[0].keys())

        # slow down the API calls so that we don't cause problems or exceed our welcome
        time.sleep(2)
Пример #5
0
    def test_get_multiple_good_names(self, genderizer, multiple_good_names):
        """
            For the supplied list of names and expected genders, verify that
            the API response contains the names and expected genders.

            :param multiple_good_names: tuple, list of str names and list of str genders
            :return: None
        """
        api = self.gender_endpoint
        names = list(multiple_good_names[0])

        res = api.get_gender(names, verbose=True)

        # test point: verify the correct response for a correct api call
        assert res.status_code == 200
        logger.info(utils.plog(res.json()))

        # convert the param from list of names + list of genders
        # to list of name + gender pairs
        expected = list(zip(multiple_good_names[0], multiple_good_names[1]))
        logger.info('expected: %s' % expected)

        # convert the returned json into a list of name + gender pairs
        actual = [(n['name'], n['gender']) for n in res.json()]
        logger.info('actual: %s' % actual)

        # test point: verify that the json keys are correct for the first item
        assert api.verify_keys_in_response(res.json()[0].keys())

        # test point: verify that the json schema is correct
        # using the new schema validator approach
        assert api.validate_schema(res.json()[0], verbose=True)

        # test point: verify a results item for each name passed to the API
        assert len(expected) == len(actual), \
            'FAIL: got %s results but expected %s.' \
            % (len(actual), len(expected))

        # loop over the results and validate each name/gender pair
        for i, item in enumerate(actual):

            # test point: verify that we get back the same name that we requested
            assert item[0] == expected[i][0], \
                'expected "%s", but got "%s"' % (expected[i][0], item[0])

            # test point: verify the expected gender for the name
            assert item[1] == expected[i][1], \
                'expected "%s", but got "%s"' % (expected[i][1], item[1])
Пример #6
0
    def test_exceed_multiple_request_limit(self, genderizer,
                                           more_than_11_names):
        """
            Request 11 names in one API call; the limit is 10 so any
            name after the tenth gets ignored.

            :param more_than_11_names: tuple, list of str names and
            list of str genders
            :return: None
        """
        api = self.gender_endpoint
        name = list(more_than_11_names[0])  # needs to be a list
        logger.info(f"\n----> name: {name}")

        res = api.get_gender(name, verbose=True)

        # test point: verify the correct response for a correct api call
        assert res.status_code == 200
        logger.info(utils.plog(res.json()))

        # convert the param from list of names + list of genders
        # to list of name + gender pairs
        expected = list(zip(more_than_11_names[0],
                            more_than_11_names[1]))[:10]  # keep list to ten
        logger.info('expected: %s' % expected)

        # convert the returned json into a list of name + gender pairs
        actual = [(n['name'], n['gender']) for n in res.json()]
        logger.info('actual: %s' % actual)

        # test point: verify that the json keys are correct for the first item
        assert api.verify_keys_in_response(res.json()[0].keys())

        # test point: verify a results item for each name passed to the API
        assert len(expected) == len(actual), \
            'FAIL: got %s results but expected %s.' \
            % (len(actual), len(expected))

        # loop over the results and validate each name/gender pair
        for i, item in enumerate(actual):

            # test point: verify that we get back the same name that we requested
            assert item[0] == expected[i][0], \
                'expected "%s", but got "%s"' % (expected[i][0], item[0])

            # test point: verify the expected gender for the name
            assert item[1] == expected[i][1], \
                'expected "%s", but got "%s"' % (expected[i][1], item[1])
Пример #7
0
def write_cookies_to_file(cookies, url, fname=''):
    """
        Save cookies as json to a file.

        :param cookies: list of dicts
        :param url: str, url for the current page
        :param fname: str, first part of filename, will be appended with
                           timestamp; defaults to empty string
        :return: None
    """
    filename = f"/{time.strftime('%H%M%S')}_{utils.path_proof_name(fname)}.txt"
    path = pytest.custom_namespace['testrun_cookies_output'] + filename
    with open(path, 'w') as f:
        f.write(f"{url}\n")  # write the url as the first line
        f.write(utils.plog(cookies))
    logger.info(f"\nSaved cookies: {path}.")
Пример #8
0
    def test_get_colors(self, colourlovers):
        """
            Simple test for the "colors" endpoint, using no data. This just
            looks at the structure of the returned json.

            :return: None
        """
        # setup: make API request
        res = self.colors_endpoint.get_colors()

        # test point: verify the correct response for a correct api call
        assert res.status_code == 200
        logger.info(utils.plog(res.json()))

        # test point: verify that the json keys are correct
        assert self.color_endpoint.verify_keys_in_response(res.json()[0].keys())
Пример #9
0
    def test_int_as_name(self, genderizer):
        """
            Integers are converted to strings by the API.
        """
        api = self.gender_endpoint
        name = 42
        expected_name = '42'

        res = api.get_gender(name, verbose=True)

        # test point: verify the correct response for a correct api call
        assert res.status_code == 200
        logger.info(utils.plog(res.json()))

        # test point: verify that we get back the same name that we requested
        actual_name = res.json()['name']
        assert actual_name == expected_name, \
            f"expected '{expected_name}', but got '{actual_name}'."
Пример #10
0
def write_sdk_response_to_file(response, sdk_app, fname=''):
    """
        Save the response from an SDK. Unlike typical API calls, the
        SDK calls don't necessarily have the standard http request data
        patterns.

        These SDKs will be wrappers in the welkin/integrations folder.

        :param response: SDK response, probably json or a dict
        :param sdk_app: str name of the SDK
        :param fname: str, first part of filename, will be appended with
                           timestamp; defaults to empty string
        :return: None
    """
    filename = f"/{time.strftime('%H%M%S')}_{utils.path_proof_name(fname)}.json"
    path = pytest.custom_namespace['testrun_integrations_log_folder'] + filename
    with open(path, 'a') as f:
        f.write(utils.plog(response))
Пример #11
0
def write_console_log_to_file(log, url, fname=''):
    """
        Write the chrome devtools console logs to a file.

        :param log: dict
        :param url: str, url for the current page
        :param fname: str, first part of filename, will be appended with
                           timestamp; defaults to empty string
        :return: None
    """
    filename = f"/{time.strftime('%H%M%S')}_{utils.path_proof_name(fname)}.json"
    path = pytest.custom_namespace['testrun_console_log_folder'] + filename

    # add key/value for the page url
    log.update({'_page': url})

    with open(path, 'a') as f:
        f.write(utils.plog(log))
    logger.info(f"\nSaved console logs (and bad headers): {path}.")
Пример #12
0
    def test_get_color(self, colourlovers):
        """
            Simple test for the "color" endpoint, using hardcoded data.

            :return: None
        """

        # setup: make API request
        data = '000000'
        res = self.color_endpoint.get_color(data)

        # test point: verify the correct response for a correct api call
        assert res.status_code == 200
        logger.info(utils.plog(res.json()))

        # test point: verify that we get back the same hex that we requested
        assert res.json()[0]['hex'] == data

        # test point: verify that the json keys are correct
        assert self.color_endpoint.verify_keys_in_response(res.json()[0].keys())
Пример #13
0
def write_network_log_to_file(log, url, fname=''):
    """
        Save the browser network log as json to a file.

        :param log:
        :param url: str, url for the current page
        :param fname: str, first part of filename, will be appended with
                           timestamp; defaults to empty string
        :return: None
    """
    # note: json files don't allow comments, so we'd not be able
    # to write the url to the file
    filename = f"/{time.strftime('%H%M%S')}_{utils.path_proof_name(fname)}.json"
    path = pytest.custom_namespace['testrun_network_log_folder'] + filename

    wrapper = {}
    wrapper['_page'] = url
    wrapper['chrome network logs'] = log
    with open(path, 'a') as f:
        f.write(utils.plog(wrapper))
    logger.info(f"\nSaved browser network log: {path}.")
Пример #14
0
    def get_password_from_aws(self, aws_session, decrypt=False, verbose=False):
        """
            Make an AWS call to retrieve the password for this user.

            :param aws_session: AWS session object
            :param decrypt: bool, True to decrypt the AWS param value
            :param verbose: bool, True to output additional information
            :return: None
        """
        resource = 'ssm'  # AWS System manager is used for parameters
        client = AWSClient(aws_session, resource_name=resource)

        # make the AWS get parameter call
        res = client.get_parameter_data(aws_key_name=self.password_key,
                                        decrypt=decrypt)
        if verbose:
            logging_data = utils.plog(utils.strip_password_for_reporting(res))
            msg1 = f"AWS response:\n{logging_data}"
            msg2 = f"\n\n\n{'#-' * 50}\nNOTE: Don't log passwords!" \
                   f"\n{msg1}\n{'#-' * 50}\n\n\n"
            logger.info(msg2)
        self.password = res['Parameter']['Value']
Пример #15
0
def _write_local_to_file(data, event, pageobject_name, source_url, output_url):
    """
        Write the local storage to a json file in the webstorage folder.

        Note: several sweord key-value pairs will be inserted into that data.

        :param data: dict of local storage log pulled from the browser
        :param event: str, descriptor for an interaction with the React app
        :param pageobject_name: str, name of pageobject
        :param source_url: str, full url of the page that generated the logs
        :param output_url: str, full local path for the output file
        :return: None
    """
    # add key/value for the page url
    data.update({'_storage type': 'local'})
    data.update({'_page': source_url})
    data.update({'_page object name': pageobject_name})
    data.update({'_precipitating event': event})

    with open(output_url, 'a') as f:
        f.write(utils.plog(data))
    logger.info(f"Saved local storage log: {output_url}.")
Пример #16
0
    def test_gender_not_resolved(self, genderizer, bad_name):
        """
            For the supplied problematic name, verify that the API response
            contains the name and null gender.

            :param bad_name: str name
            :return: None
        """
        api = self.gender_endpoint
        name = bad_name
        ex_gender = None

        res = api.get_gender(name, verbose=True)

        # test point: verify the correct response for a correct api call
        assert res.status_code == 200
        logger.info(utils.plog(res.json()))

        # test point: verify that we get back the same name that we requested
        assert res.json()['name'] == bad_name, \
            'expected "%s", but got "%s"' % (bad_name, res.json()['name'])

        # verify that we got gender assignment of None
        assert self.gender_endpoint.got_gender(res, ex_gender)
Пример #17
0
def write_request_to_file(response, url, fname=''):
    """
        Save the request and response headers and payload.

        Note that the request info is extracted from the response content.

        :param response: requests Response object
        :param url: str, url for the current page
        :param fname: str, first part of filename, will be appended with
                           timestamp; defaults to empty string
        :return: None
    """
    filename = f"/{time.strftime('%H%M%S')}_{utils.path_proof_name(fname)}.txt"
    path = pytest.custom_namespace['testrun_requests_log_folder'] + filename
    boundary = None
    with open(path, 'a') as f:
        f.write(f"{url}\n\n")  # write the url as the first line
        f.write("###### REQUEST ####### \n")
        f.write("HEADERS\n")
        try:
            # the request header MUST have the Content-Type key-value pair
            # if not, raise an exception and kill the test
            content_type = response.request.headers['Content-Type']
        except KeyError:
            msg = f"Missing header content-type field in request to {url}"
            logger.error(msg)
            raise ValueError(msg)
        if 'boundary' in content_type:
            # this is a POST, it must have the multi-part content-type, so
            # extract the boundary str used between binary attachments
            boundary = content_type[content_type.index('boundary=') + 9:]
        f.write(utils.plog(response.request.headers))

        if response.request.body:
            # it will be as BODY (None) in file because content_type is not found in headers
            f.write(f"\n\nBODY ({content_type})\n")
            if isinstance(response.request.body, bytes):
                # this was a binary upload, so decode it as a
                # unicode str in order to write it to the file
                logger.warning("response.request.body cast as string.")
                f.write("--decoded from bytes--\n")
                if boundary:
                    # this is a multi-part form body
                    # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition
                    body = response.request.body.decode(
                        'utf-8', 'backslashreplace')
                    cleaned_body = body.replace('\r\n\r\n\n\r\n', '\n\n')
                    f.write(cleaned_body)
                else:
                    f.write(
                        "don't know what happened with this request body;\n"
                        "probably a POST with bytes.")

            else:
                try:
                    f.write(utils.plog(json.loads(response.request.body)))
                except UnicodeDecodeError:
                    logger.warning("got UnicodeDecodeError.")
                    f.write("-- byte string snipped --")
                except json.decoder.JSONDecodeError:
                    logger.warning("got json.decoder.JSONDecodeError.")
                    f.write(utils.plog(response.request.body))

        f.write("\n\n###### RESPONSE ####### \n")
        f.write("HEADERS\n")
        f.write(utils.plog(response.headers))
        f.write("\n\nRESPONSE STATUS CODE\n")
        f.write(f"response server status: {response.status_code}")
        f.write("\n\nPAYLOAD\n")
        try:
            f.write(utils.plog(response.json()))
        except json.decoder.JSONDecodeError:
            # this could be an xml byte response
            f.write(utils.plog(response.content))

        f.write(f"\n\n{'~' * 45}\n\n")

    logger.info(f"Saved headers: {path}")