class AddressesAPI(API):
    """API methods for addresses."""

    API: API = API()
    URL: str = API.build_api_url(path=f'/ondemand/{AGENCY}/addresses')

    def create_address(self, address_data: dict, url: str) -> dict:
        """Add an address using the API, then surface the address ID for teardown.

        This method does not utilize the base self.URL attribute as it requires a url to be passed
        from the creation of the address dictionary. This is due to the separation between Web and
        Admin address creation.

        :param address_data: A dictionary of address data for API consumption.
        :param url: An application URL yielded from the Address Factory.
        """
        if 'url' in address_data:
            del address_data['url']

        return self.API.create_request(data=address_data, expected_response=[200], url=url)

    def delete_address(self, address: dict, rider_address: bool) -> None:
        """Add an address using the API, then surface the address ID for teardown.

        :param address: The address intended for deletion.
        :param rider_address: Boolean for whether the address is web or not.
        """
        address_id: int = address['address_id']
        if rider_address:
            self.URL = self.API.build_api_url('/me/rider/addresses')
        delete_url: str = f'{self.URL}/{address_id}'

        self.API.delete_request(expected_response=[200], url=delete_url)
Beispiel #2
0
class RidesAPI(API):
    """API methods for rides."""

    API: API = API()
    URL = API.build_api_url(path=f'/ondemand/{AGENCY}/rides')

    def cancel_ride(self, ride: dict) -> None:
        """Cancel a ride using the API.

        :param ride: A ride yielded by the API.
        """
        self.change_ride_status(ride, 'Canceled')

    def change_ride_status(self, ride: dict, status: str) -> None:
        """Manipulate a ride's status using the API.

        :param ride: A ride yielded from the API.
        :param status: A specified status.
        """
        groomed_status: str = status.lower().replace(' ', '_')
        ride_id: int = ride['ride_id']
        status_data: dict = {'status': f'{groomed_status}'}
        url: str = f'{self.URL}/{ride_id}'

        self.API.change_ride_status_request(data=status_data,
                                            expected_response=[200],
                                            url=url)

    def complete_ride(self, ride: dict) -> None:
        """Complete a ride using the API.

        :param ride: A ride yielded by the API.
        """
        self.change_ride_status(ride=ride, status='In Progress')
        self.change_ride_status(ride=ride, status='Complete')

    def create_ride(self, ride_data: dict) -> dict:
        """Add a ride using the API, then surface the ride ID for teardown.

        :param ride_data: A dictionary of ride data for API consumption.
        """
        return self.API.create_request(data=ride_data,
                                       expected_response=[200],
                                       url=self.URL)

    def set_no_show(self, ride: dict) -> None:
        """Set a ride to 'No Show' status.

        :param ride: A ride yielded by the API.
        """
        self.change_ride_status(ride=ride, status='No Show')

    def start_ride(self, ride: dict) -> None:
        """Start a ride using the API.

        :param ride: A ride yielded by the API.
        """
        self.change_ride_status(ride=ride, status='In Progress')
Beispiel #3
0
class GroupsAPI(API):
    """API methods for groups."""

    API: API = API()
    URL: str = API.build_api_url(path=f'/ondemand/{AGENCY}/groups')

    def create_group(self, group_data: dict) -> dict:
        """Add a group using the API, then surface the group ID for teardown.

        :param group_data: A dictionary of group data for API consumption.
        """
        return self.API.create_request(data=group_data,
                                       expected_response=[200],
                                       url=self.URL)

    def delete_group(self, group: dict) -> None:
        """Delete a group which has been successfully created.

        :param group: A group generated by a factory.
        """
        group_id: str = group['group_id']
        delete_url: str = f'{self.URL}/{group_id}'

        self.API.delete_request(expected_response=[200], url=delete_url)

    def add_user_to_group(self, group: dict, user: dict) -> dict:
        """Associate a user with a group.

        Note- both group and user must already exist for association to take place.

        :param group: A group generated by a factory.
        :param user: A user generated by a factory.
        """
        group_id: int = group['group_id']
        username: str = user['username']
        url: str = f'{self.URL}/{group_id}/users/{username}'

        return self.API.create_request(data=dict(),
                                       expected_response=[200],
                                       url=url)

    def remove_user_from_group(self, group: dict, user: dict) -> None:
        """Disassociate a user with a group.

        :param group: A group generated by a factory.
        :param user: A user generated by a factory.
        """
        group_id: int = group['group_id']
        username: str = user['username']
        url: str = f'{self.URL}/{group_id}/users/{username}'

        self.API.delete_request(expected_response=[200], url=url)
 def test_create_address__failure__invalid_input(self) -> None:
     """Check that the create_address method fails with invalid input."""
     with pytest.raises(TypeError):
         self.addresses_api.create_address(
             address_data=1111111,
             url=API.build_api_url('/testing'),  # type: ignore
         )
Beispiel #5
0
    class Params:
        """Optional params which change factory output when True.

        :param rider_address: Generate an address for the OnDemand Web application.

        :example usage: AddressFactory.create(rider_address=True)
        """

        rider_address = Trait(url=API.build_api_url('/me/rider/addresses'))
Beispiel #6
0
class AddressFactory(Factory):
    """Create a new address for OnDemand testing.

    This is a factory which can be configured by passing in optional parameters for
    address customization via the nested class method. Multiple addresses may be created using the
    factory by calling a batch method.

    Optional parameters must match fields defined in the Address data class.

    :example usage:
        API: AddressFactory.create(name='Lake Park Plaza')
        API Web Address: AddressFactory.create(name='Lake Park Plaza', rider_address=True)
        Non-API: AddressFactory.build(name='Lake Park Plaza')
    """
    class Meta:
        model = Address

    class Params:
        """Optional params which change factory output when True.

        :param rider_address: Generate an address for the OnDemand Web application.

        :example usage: AddressFactory.create(rider_address=True)
        """

        rider_address = Trait(url=API.build_api_url('/me/rider/addresses'))

    _api: AddressesAPI = AddressesAPI()

    url: str = API.build_api_url(f'/ondemand/{AGENCY}/addresses')
    name: str = LazyAttribute(
        lambda _: f'{fake.sentence(nb_words=3)}-{process_time()}')

    @classmethod
    def _build(cls, model_class: Callable, **kwargs: Any) -> dict:
        """Override the default _build method to build a new Address.

        :param model_class: The factory model used for generation.
        :param kwargs: Additional arguments being passed for generation.
        """
        return model_class(**kwargs).__dict__

    @classmethod
    def _create(cls, model_class: Callable, **kwargs: Any) -> dict:
        """Override the default _create method to post against the Addresses API.

        :param app: The application under test.
        :param model_class: The factory model used for generation.
        :param kwargs: Additional arguments being passed for generation.
        """
        _address_dict: dict = model_class(**kwargs).__dict__

        return cls._api.create_address(address_data=_address_dict,
                                       url=_address_dict['url'])
    def test_create_address__remove_url_key(self) -> None:
        """Check that the create_address method removes the 'url' key."""
        address_data: dict = AddressFactory.build()
        url: str = API.build_api_url(path=f'/ondemand/{AGENCY}/addresses')

        address: dict = self.addresses_api.create_address(
            address_data=address_data, url=url)

        assert 'url' not in address

        self.addresses_api.delete_address(address=address, rider_address=False)
    def test_create_address__success(self) -> None:
        """Check that an Address may be created."""
        address_data: dict = AddressFactory.build()
        url: str = API.build_api_url(path=f'/ondemand/{AGENCY}/addresses')

        try:
            address: dict = self.addresses_api.create_address(
                address_data=address_data, url=url)
            self.addresses_api.delete_address(address=address,
                                              rider_address=False)
        except HTTPError:
            pytest.fail('Test failed due to HTTPError.', pytrace=True)
class ServicesAPI(API):
    """API methods for services."""

    API: API = API()
    URL = API.build_api_url(path=f'/ondemand/{AGENCY}/services')

    def archive_service(self, service: dict) -> None:
        """Archive an expired service which has been successfully created.

        This endpoint calls True when both a 200 or 503 have been received. The reasoning for this
        is to ensure that services have been archived correctly (503 response) and if not, to
        archive them (200 response).

        :param service: The service intended for archive.
        """
        service_id: str = service['service_id']
        archive_url: str = f'{self.URL}/{service_id}'

        self.API.delete_request(expected_response=[200, 503], url=archive_url)

    def create_service(self, service_data: dict) -> dict:
        """Create a service using the API, then surface the service ID for teardown.

        :param service_data: A dictionary of service data for API consumption.
        """
        return self.API.create_request(data=service_data,
                                       expected_response=[200],
                                       url=self.URL)

    def delete_service(self, service: dict) -> None:
        """Delete a service which has been successfully created.

        :param service: The service intended for deletion.
        """
        service_id: int = service['service_id']
        destroy_url: str = f'{self.URL}/{service_id}/destroy'

        self.API.delete_request(expected_response=[200], url=destroy_url)
class VehiclesAPI(API):
    """API methods for vehicles."""

    API: API = API()
    URL = API.build_api_url(path=f'/ondemand/{AGENCY}/vehicles')

    def create_vehicle(self, vehicle_data: dict) -> dict:
        """Add a vehicle using the API, then surface the vehicle ID for teardown.

        :param vehicle_data: A dictionary of vehicle data for API consumption.
        """
        return self.API.create_request(data=vehicle_data,
                                       expected_response=[200],
                                       url=self.URL)

    def delete_vehicle(self, vehicle: dict) -> None:
        """Delete a vehicle which has been successfully created.

        :param vehicle: The unique vehicle generated when a vehicle is created.
        """
        vehicle_id: int = vehicle['vehicle_id']
        delete_url: str = f'{self.URL}/{vehicle_id}'

        self.API.delete_request(expected_response=[200], url=delete_url)
Beispiel #11
0
    def change_ride_status(self, ride: dict, status: str) -> None:
        """Manipulate a recurring ride's status using the API.

        :param ride: A recurring ride yielded by the API.
        :param status: A specified status.
        """
        groomed_status: str = status.lower().replace(' ', '_')
        ride_id: int = ride['ride']['ride_id']
        status_data: dict = {'status': f'{groomed_status}'}
        url: str = API.build_api_url(
            path=f'/ondemand/{AGENCY}/rides/{ride_id}')

        self.API.change_ride_status_request(data=status_data,
                                            expected_response=[200],
                                            url=url)
Beispiel #12
0
class RegionsAPI(API):
    """API methods for regions."""

    API: API = API()
    URL = API.build_api_url(path=f'/ondemand/{AGENCY}/regions')

    def create_region(self, region_data: dict) -> dict:
        """Add a region using the API, then surface the region ID for teardown.

        :param region_data: A dictionary of region data for API consumption.
        """
        return self.API.create_request(data=region_data,
                                       expected_response=[200],
                                       url=self.URL)

    def delete_region(self, region: dict) -> None:
        """Delete a region which has been successfully created.

        :param region: The unique region ID generated when a region is created.
        """
        region_id: int = region['region_id']
        delete_url: str = f'{self.URL}/{region_id}'

        self.API.delete_request(expected_response=[200], url=delete_url)
    def valid_address(self) -> Address:
        """Create a valid Address."""
        url: str = API.build_api_url(f'/ondemand/test_agency/addresses')
        address: Address = Address(name='Test Address', url=url)

        yield address
def login(request: fixture, selenium: fixture) -> None:
    """Login via submitting a login form or auth token.

    Runs a check to determine if an auth token exists. If one exists, the framework will add the
    auth token as a cookie. If not, the framework will input valid login credentials, then capture
    the auth token for later tests.

    :param request: Fixture for requesting Pytest configuration data.
    :param selenium: An instance of Selenium Web Driver.
    """
    login_api = API()
    login_page = Login(selenium)

    if 'no_auth' not in request.keywords:

        #
        # Check for specific pytest marks for permissions, then login accordingly
        #
        if 'role_agent' in request.keywords:
            login_api.capture_token(username=USERS.Agent.USERNAME,
                                    superuser=False)
        elif 'role_driver' in request.keywords:
            login_api.capture_token(username=USERS.Driver.USERNAME,
                                    superuser=False)
        elif 'role_dispatcher' in request.keywords:
            login_api.capture_token(username=USERS.Dispatcher.USERNAME,
                                    superuser=False)
        elif 'role_rider' in request.keywords:
            login_api.capture_token(username=USERS.Rider.USERNAME,
                                    superuser=False)
        else:
            login_api.capture_token(username=USERS.Admin.USERNAME,
                                    superuser=False)

        login_page.visit()
        login_page.add_auth_token()
Beispiel #15
0
class RecurringRidesAPI(API):
    """API methods for recurring rides."""

    API: API = API()
    URL: str = API.build_api_url(
        path=f'/ondemand/{AGENCY}/rides/subscriptions')

    def cancel_booked_ride(self, ride: dict) -> None:
        """Cancel a booked portion of a recurring ride using the API.

        :param ride: A recurring ride yielded by the API.
        """
        self.change_ride_status(ride, 'Canceled')

    def cancel_recurring_ride(self, recurring_ride: dict) -> None:
        """Cancel a recurring ride using the API.

        Use this method for cancelling an entire subscription rather than a single booked ride.

        :param recurring_ride: A recurring ride yielded by the API.
        """
        tries: int = 10
        url: str = f'{self.URL}/{recurring_ride["ride_subscription_id"]}/cancel'

        while True:
            tries -= 1

            sleep(random())
            response: Response = requests.put(
                url=url,
                headers=self.API.build_auth_headers(),
            )
            if response.status_code == 200:
                break
            elif tries == 0:
                raise HTTPError(
                    f'Requests failed with response: {response.json()}')

    def change_ride_status(self, ride: dict, status: str) -> None:
        """Manipulate a recurring ride's status using the API.

        :param ride: A recurring ride yielded by the API.
        :param status: A specified status.
        """
        groomed_status: str = status.lower().replace(' ', '_')
        ride_id: int = ride['ride']['ride_id']
        status_data: dict = {'status': f'{groomed_status}'}
        url: str = API.build_api_url(
            path=f'/ondemand/{AGENCY}/rides/{ride_id}')

        self.API.change_ride_status_request(data=status_data,
                                            expected_response=[200],
                                            url=url)

    def complete_ride(self, ride: dict) -> None:
        """Complete a ride using the API.

        :param ride: A recurring ride yielded by the API.
        """
        self.change_ride_status(ride=ride, status='In Progress')
        self.change_ride_status(ride=ride, status='Complete')

    def create_recurring_ride(self, ride_data: dict) -> dict:
        """Add a recurring ride using the API, then surface the ride ID for teardown.

        :param ride_data: A dictionary of recurring ride data for API consumption.
        """
        return self.API.create_request(data=ride_data,
                                       expected_response=[200],
                                       url=self.URL)

    def set_no_show(self, ride: dict) -> None:
        """Set a ride to 'No Show' status.

        :param ride: A recurring ride yielded by the API.
        """
        self.change_ride_status(ride=ride, status='No Show')

    def start_ride(self, ride: dict) -> None:
        """Start a ride using the API.

        :param ride: A recurring ride yielded by the API.
        """
        self.change_ride_status(ride=ride, status='In Progress')
 def set_api(self) -> None:
     """Instantiate all APIs for API helper testing."""
     self.api: API = API()