def matches(self, recorded: RecordedHTTPRequest) -> Union[bool, MismatchReason]:
        """
        Test if an http request meets the expectations of self
        Args:
            recorded: a recorded http request

        Returns:
            True if the request matches, or a MismatchReason object with the reason why otherwise.
        """
        reasons = list(self.scope_mismatch_reasons(recorded))

        if self.method and self.method != recorded.method:
            reasons.append(reason_is_ne('body', self.method, recorded.method))

        if self.body_decode is not None:
            try:
                body = self.body_decode(recorded.content)
            except Exception as e:
                reasons.append(f'failed to parse content: {e!r}')
            else:
                if self.data != body:
                    reasons.append(reason_is_ne('content', self.data, body))

        if reasons:
            return MismatchReason(', '.join(reasons))
        return True
    def matches(self,
                message: RecordedWSMessage) -> Union[bool, MismatchReason]:
        """
        Checks whether a recorded message matches the expectation
        Args:
            message: the recorded message

        Returns:
            True if the message matches, or a mismatch reason otherwise.
        """
        if self.sender != message.sender:
            return MismatchReason(
                reason_is_ne('sender', self.sender, message.sender))

        if self.data is ...:
            return True
        elif isinstance(self.data, Pattern):
            try:
                match = self.data.fullmatch(
                    message.data)  # type:ignore[arg-type]
            except TypeError:  # this would happen when we try to use byte patterns on strs or vice-versa
                match = None
            if not match:
                return MismatchReason(
                    f'expected data to match pattern {self.data.pattern!r}, got {message.data!r}'
                )
            return True
        else:
            if self.data != message.data:
                return MismatchReason(
                    reason_is_ne('data', self.data, message.data))
        return True
    def matches(self, recorded: RecordedWSTranscript):
        """
        Checks whether a recorded transcript matches the expectation
        Args:
            recorded: the recorded transcript

        Returns:
            True if the transcript matches, or a mismatch reason otherwise.
        """
        if recorded.close is None:
            raise RuntimeError('the transcript is not yet done')

        reasons = list(self.scope_mismatch_reasons(recorded))

        if self.close is not None and recorded.close != self.close:
            reasons.append(
                reason_is_ne('close_code', self.close, recorded.close))
        if self.accepted is not None and recorded.accepted != self.accepted:
            reasons.append(
                reason_is_ne('accepted', self.accepted, recorded.accepted))

        if not self.any_start and not self.any_end and len(recorded) != len(
                self.expected_messages):
            reasons.append(
                f'expected exactly {len(self.expected_messages)} messages, found {len(recorded)}'
            )
        if len(recorded) < len(self.expected_messages):
            reasons.append(
                f'expected at least {len(self.expected_messages)} messages, found only {len(recorded)}'
            )
        if reasons:
            return MismatchReason(', '.join(reasons))
        # in order to account for any_start, any_end, we check every subsequence of the recorded transcript
        # we store a list of candidate indices for the subsequence start
        if not self.any_start:
            # the case of (not any_start and not any_end) is covered by the first condition
            indices: Iterable[int] = (0, )
        elif not self.any_end:
            indices = (len(recorded) - len(self.expected_messages), )
        else:
            indices = range(0, len(recorded) - len(self.expected_messages) + 1)

        whynots = []
        for start_index in indices:
            for i, expected_message in enumerate(self.expected_messages):
                rec = recorded[start_index + i]
                match = expected_message.matches(rec)
                if not match:
                    whynots.append(
                        f'{rec} did not match {expected_message}: {match}')
                    break
            else:
                return True

        return MismatchReason('could not find expected calls' +
                              ''.join('\n\t' + wn for wn in whynots))
Example #4
0
    def scope_mismatch_reasons(self, recorded) -> Iterator[str]:
        header_match = _matches_expected_multimap('header', self.headers,
                                                  recorded.headers)
        if header_match:
            yield header_match

        if (self.path_pattern is not None
                and self.path_pattern.fullmatch(str(recorded.path)) is None):
            yield reason_is_ne('path', self.path_pattern.pattern,
                               recorded.path)

        path_params_match = _matches_expected_map('path_params',
                                                  self.path_params,
                                                  recorded.path_params)
        if path_params_match:
            yield path_params_match

        query_params_match = _matches_expected_multimap(
            'query_params', self.query_params, recorded.query_params)
        if query_params_match:
            yield query_params_match
Example #5
0
def _matches_expected_map(name: str, expected: Optional[Tuple[Mapping[K, V], bool]], recorded: Mapping[K, V]) \
        -> Optional[str]:
    """
    Matches a map against the return value of _to_expected_map
    Args:
        name: the name of the field being matched
        expected: the expected value, as returned by _to_expected_map
        recorded: the recorded value to match

    Returns:
        True if the condition matches, or a MismatchReason otherwise.
    """
    if expected is None:
        return None
    expected_value, is_subset = expected
    if is_subset:
        submap_match = _is_submap_of(expected_value, recorded)
        if not submap_match:
            return f'{name} mismatch: {submap_match}'
    else:
        if expected_value != recorded:
            return reason_is_ne(name, expected_value, recorded)
    return None