示例#1
0
    def test_derive_duration_string_returns_minutes_seconds_repr_of_timedelta(
            self, minutes, seconds):
        duration = timedelta(minutes=minutes, seconds=seconds)

        ret_val = utils.derive_duration_string(duration)

        assert ret_val == f'{minutes:02}:{seconds:02}'
 def form_has_values_from_watering_station(self, watering_station):
     return all([
         self.status == watering_station.status,
         self.plant_type == watering_station.plant_type,
         self.watering_duration == derive_duration_string(
             watering_station.watering_duration),
         self.moisture_threshold == str(watering_station.moisture_threshold)
     ])
示例#3
0
 def form_fields(cls, **kwargs):
     keys = [
         'moisture_threshold', 'watering_duration', 'plant_type', 'status'
     ]
     data = super().json()
     form_data = {key: data[key] for key in keys}
     form_data['watering_duration'] = derive_duration_string(
         form_data['watering_duration'])
     return form_data
示例#4
0
    def test_POST_with_valid_data_creates_new_watering_station_with_supplied_data(
            self, auth_client, watering_station_form_fields):
        auth_client.post(self.url, data=watering_station_form_fields)

        self.garden.refresh_from_db()
        watering_station = self.garden.watering_stations.last()
        assert watering_station_form_fields.pop(
            'watering_duration') == derive_duration_string(
                watering_station.watering_duration)
        assertions.assert_model_fields_have_values(
            watering_station_form_fields, watering_station)
示例#5
0
    def test_POST_with_valid_data_updates_the_watering_station_with_given_data(
            self, auth_client, watering_station_form_fields):
        resp = auth_client.post(self.url, data=watering_station_form_fields)

        self.watering_station.refresh_from_db()
        assert resp.status_code == status.HTTP_200_OK
        assert self.watering_station.moisture_threshold == watering_station_form_fields[
            'moisture_threshold']
        assert derive_duration_string(
            self.watering_station.watering_duration
        ) == watering_station_form_fields['watering_duration']
        assert self.watering_station.plant_type == watering_station_form_fields[
            'plant_type']
示例#6
0
    def test_POST_doesnt_update_watering_station_is_accessed_by_user_who_doesnt_own_it(
            self, auth_client, watering_station, watering_station_form_fields):
        url = self.create_url(watering_station.garden.pk, watering_station.pk)

        auth_client.post(url, data=watering_station_form_fields)

        watering_station.refresh_from_db()
        assert any([
            watering_station.moisture_threshold !=
            watering_station_form_fields['moisture_threshold'],
            derive_duration_string(watering_station.watering_duration) !=
            watering_station_form_fields['watering_duration'],
            watering_station.plant_type !=
            watering_station_form_fields['plant_type']
        ])
示例#7
0
    def test_user_can_create_a_garden(self):
        # a user goes to the garden page
        self.driver.get(self.url)
        list_page = GardenListPage(self.driver)
        self.wait_for_page_to_be_loaded(list_page)

        # they see a list of registered gardens which is currently 0
        assert list_page.get_number_of_gardens() == 0

        # they also see an option to create a new garden, which they click and immediately see a modal that prompts
        # them for the garden name. They are also prompted for the number of watering stations that are going to be in
        # this garden, an image, and the update interval
        list_page.new_garden_button.click()
        self.wait_for_modal_to_be_visible(list_page.modal_id)
        assert list_page.new_garden_name == ''
        assert list_page.num_watering_stations == ''
        assert list_page.update_frequency == str(
            derive_duration_string(_default_update_frequency()))
        assert list_page.garden_image

        # they cancel out of the modal, but then re-enter again
        list_page.cancel_button.click()
        self.wait_for_model_to_disappear(list_page.modal_id)
        list_page.new_garden_button.click()
        self.wait_for_modal_to_be_visible(list_page.modal_id)

        # they enter a negative number for the number of watering stations and hit enter, and they see a error message
        # appear
        garden_name = 'My New Garden'
        list_page.new_garden_name = garden_name
        list_page.num_watering_stations = -1
        list_page.update_frequency = -1
        list_page.submit_button.click()
        self.wait_for_form_error('error_1_id_num_watering_stations')
        self.wait_for_form_error('error_1_id_update_frequency')

        # now they enter a garden name and valid number of watering stations and see a new garden appear in the list of
        # gardens
        num_watering_stations = 3
        garden_image = 'test_garden_image.png'
        update_frequency = '0:10:00'
        list_page.num_watering_stations = num_watering_stations
        list_page.update_frequency = update_frequency
        self.perform_image_crop(list_page, garden_image)
        list_page.submit_button.click()
        list_page.wait_for_garden_in_list(garden_name)
        assert_image_files_equal(list_page.get_garden_image(garden_name),
                                 garden_image)

        # they click on the garden and are taken to the associated garden page
        list_page.click_garden(garden_name)
        detail_page = GardenDetailPage(self.driver)
        self.wait_for_page_to_be_loaded(detail_page)

        # they see the watering stations that are part of the garden
        assert detail_page.get_number_watering_stations(
        ) == num_watering_stations

        # the user then clicks the AutoGarden tag in the nav bar to navigate home and is taken back to the garden
        # list page
        list_page.home_button.click()
        self.wait_for_page_to_be_loaded(list_page)
示例#8
0
    def test_microcontroller_interaction_with_server(self):
        # the microcontroller PATCHs data to the garden api to update the garden instance
        garden_data = {
            'water_level': Garden.LOW,
            'connection_strength': random.randint(-100, 0)
        }
        self.send_patch_request_to_garden_api(self.garden, garden_data)

        # afterwards a user visits the garden detail page where they see that the garden info has updated
        self.driver.get(self.url)
        detail_gpage = GardenDetailPage(self.driver)
        self.wait_for_page_to_be_loaded(detail_gpage)
        self.garden.refresh_from_db()
        assert detail_gpage.is_displaying_info_for_garden(self.garden)

        # the MC sends a GET request to retrieve the watering station configs from the server
        self.send_get_request_to_watering_station_api(self.garden)

        # immediately after the MC sends a GET request to retrieve the garden update interval duration
        self.send_get_request_to_garden_api(self.garden)

        # the MC then performs its operations and POSTs the data from its watering station sensors to the server
        data = []
        for i in range(self.num_watering_stations):
            data.append({'moisture_level': random.uniform(0, 100)})
        self.send_post_request_to_watering_station_api(self.garden, data)

        # the microcontroller then crashes and misses an update. The user refreshes the page and sees the updated
        # connection info, where the connection status is not disconnected
        sleep(self.update_frequency)
        self.driver.get(self.url)
        self.wait_for_page_to_be_loaded(detail_gpage)
        self.garden.refresh_from_db()
        assert detail_gpage.is_displaying_info_for_garden(self.garden)
        assert self.garden.is_connected == False

        # the user then change the garden configs
        detail_gpage.edit_button.click()
        update_gpage = GardenUpdatePage(self.driver)
        self.wait_for_page_to_be_loaded(update_gpage)
        update_frequency = timedelta(minutes=7, seconds=20)
        update_gpage.update_garden(
            update_frequency=derive_duration_string(update_frequency))
        self.wait_for_page_to_be_loaded(update_gpage)
        update_gpage.garden_detail_nav_button.click()
        self.wait_for_page_to_be_loaded(detail_gpage)

        # the user also changes the configs for a watering station
        selected_watering_station = 1
        detail_gpage.watering_station = selected_watering_station
        detail_ws_page = WateringStationDetailPage(self.driver)
        detail_ws_page.edit_button.click()
        update_ws_page = WateringStationUpdatePage(self.driver)
        self.wait_for_page_to_be_loaded(update_ws_page)
        ws_status = not list(self.garden.watering_stations.all()
                             )[selected_watering_station].status
        moisture_threshold = 80
        watering_duration = timedelta(minutes=3, seconds=2)
        update_ws_page.status = ws_status
        update_ws_page.moisture_threshold = moisture_threshold
        update_ws_page.watering_duration = derive_duration_string(
            watering_duration)
        update_ws_page.submit_button.click()

        # when the MC sends another GET request to the watering station api, it recieves the new updated configs
        # self.send_get_request_to_watering_station_api(self.garden)
        resp = self.api_client.get(
            self.get_watering_station_api_url(self.garden))
        assert resp.status_code == status.HTTP_200_OK
        for i, ws_config in enumerate(resp.data, start=1):
            if i == selected_watering_station:
                assert ws_config['status'] == ws_status
                assert ws_config['moisture_threshold'] == moisture_threshold
                assert ws_config[
                    'watering_duration'] == watering_duration.total_seconds()
            else:
                assert ws_config['status'] == _default_status()
                assert ws_config[
                    'moisture_threshold'] == _default_moisture_threshold()
                assert ws_config[
                    'watering_duration'] == _default_watering_duration(
                    ).total_seconds()

        # similarly when it sends a GET request to the garden api, it recieves the new updated configs
        resp = self.api_client.get(self.get_garden_api_url(self.garden))
        assert resp.status_code == status.HTTP_200_OK
        assert resp.data['update_frequency'] == update_frequency.total_seconds(
        )

        # the user then goes to the update garden page
        update_ws_page.garden_detail_nav_button.click()
        self.wait_for_page_to_be_loaded(detail_gpage)
        detail_gpage.edit_button.click()
        self.wait_for_page_to_be_loaded(update_gpage)

        # they then click the reset api key button and the api key resets
        update_gpage.reset_api_key_button.click()
        assert wait_for(lambda: '*' not in str(update_gpage.api_key))
        assert update_gpage.api_key != self.token_uuid

        # the microcontroller then tries to access the api, but the operation is not allowed
        resp = self.api_client.get(
            self.get_watering_station_api_url(self.garden))
        assert resp.status_code == status.HTTP_403_FORBIDDEN

        resp = self.api_client.get(self.get_garden_api_url(self.garden))
        assert resp.status_code == status.HTTP_403_FORBIDDEN

        resp = self.api_client.post(
            self.get_watering_station_api_url(self.garden))
        assert resp.status_code == status.HTTP_403_FORBIDDEN

        resp = self.api_client.patch(self.get_garden_api_url(self.garden))
        assert resp.status_code == status.HTTP_403_FORBIDDEN