def test_zone_list_get_with_sensor_filter(self, api_root): excluded_zone = Zone( name='Test Zone Excluded', notes='Zone notes.', key=uuid4() ) excluded_zone.save() excluded_sensor = Sensor( guid=uuid4(), name='Test Sensor Excluded', notes='Sensor notes.', zone=excluded_zone, min_value=20, max_value=25, min_value_operator='>=', max_value_operator='<=', state=True, state_last_change_date=datetime.utcnow() ) excluded_sensor.save() suffix = 'zones/?sensor_ids={}&format=json'.format(self.sensor.id) response = requests.get(api_root + suffix) data = response.json() assert len(data) > 0 excluded_zone_json = [x for x in data if x['id'] == str(excluded_zone.id)] assert len(excluded_zone_json) == 0 excluded_sensor.delete() excluded_zone.delete() zone_json = [x for x in data if int(x['id']) == self.zone.id][0] self._compare_zone_json(self.zone, zone_json)
def register_sensor(request): if request.method == 'POST': email = request.POST.get('email') if isinstance(email, list): email = email[0] sensor_name = request.POST.get('sensor_name') lat = request.POST.get('lat') lon = request.POST.get('lon') address = request.POST.get('address') serial = request.POST.get('serial') description = request.POST.get('description') error = verify_email(email) if error: return error if not sensor_name: sensor_name = random_name.generate_name() # If a sensor with the email already exists issue an error try: user = User.objects.get(email=email) return Response({"error": "The email %s is already registered with a sensor" % email}, status=status.HTTP_400_BAD_REQUEST) except User.DoesNotExist: # Creat the new user with transaction.atomic(): username = User.objects.make_random_password(length=10) password = User.objects.make_random_password(length=10) user = User(username=username, password=password, email=email, is_active=False) user.save() # Add sensor information sensor = Sensor(account=user, sensor_name=sensor_name, lat=lat, lon=lon, address=address, serial=serial, description=description) sensor.save() # Send Verification email verification_code = User.objects.make_random_password(length=40) verify = SensorVerification(account=user, verification_code=verification_code) verify.save() mail = VerificaitonEmail(user.email, verification_code) mail.send() return Response({ 'email': user.email, 'username': user.username, 'id': user.id, 'verified': verify.verified, 'message': 'New sensor is registered. A verification email is sent to the email provided. ' + 'Please verify the sensor.', }, status=status.HTTP_201_CREATED)
def test_sensors_list_get_with_order_by(self, api_root): second_sensor = Sensor( guid=uuid4(), name=self.sensor.name + '_', # Just something to make it sort lower notes='Sensor notes.', zone=self.zone, min_value=20, max_value=25, min_value_operator='>=', max_value_operator='<=', state=True, state_last_change_date=datetime.utcnow()) second_sensor.save() # Sort by name, ascending response = requests.get(api_root + 'sensors/?order_by=name&format=json') data = response.json() assert len(data) > 1 first_guid = str(self.sensor.guid) second_guid = str(second_sensor.guid) matches = [ x for x in data if (x['guid'] == first_guid or x['guid'] == second_guid) ] assert matches[0]['name'] == self.sensor.name assert matches[1]['name'] == second_sensor.name # Sort by name, descending response = requests.get(api_root + 'sensors/?order_by=-name&format=json') data = response.json() assert len(data) > 1 matches = [ x for x in data if (x['guid'] == first_guid or x['guid'] == second_guid) ] assert matches[0]['name'] == second_sensor.name assert matches[1]['name'] == self.sensor.name # Default sorting (id) response = requests.get(api_root + 'sensors/?format=json') data = response.json() assert len(data) > 1 matches = [ x for x in data if (x['guid'] == first_guid or x['guid'] == second_guid) ] assert matches[0]['name'] == self.sensor.name assert matches[1]['name'] == second_sensor.name
def test_sensors_list_get_with_order_by(self, api_root): second_sensor = Sensor( guid=uuid4(), name=self.sensor.name + '_', # Just something to make it sort lower notes='Sensor notes.', zone=self.zone, min_value=20, max_value=25, min_value_operator='>=', max_value_operator='<=', state=True, state_last_change_date=datetime.utcnow() ) second_sensor.save() # Sort by name, ascending response = requests.get(api_root + 'sensors/?order_by=name&format=json') data = response.json() assert len(data) > 1 first_guid = str(self.sensor.guid) second_guid = str(second_sensor.guid) matches = [x for x in data if ( x['guid'] == first_guid or x['guid'] == second_guid)] assert matches[0]['name'] == self.sensor.name assert matches[1]['name'] == second_sensor.name # Sort by name, descending response = requests.get(api_root + 'sensors/?order_by=-name&format=json') data = response.json() assert len(data) > 1 matches = [x for x in data if ( x['guid'] == first_guid or x['guid'] == second_guid)] assert matches[0]['name'] == second_sensor.name assert matches[1]['name'] == self.sensor.name # Default sorting (id) response = requests.get(api_root + 'sensors/?format=json') data = response.json() assert len(data) > 1 matches = [x for x in data if ( x['guid'] == first_guid or x['guid'] == second_guid)] assert matches[0]['name'] == self.sensor.name assert matches[1]['name'] == second_sensor.name
def create_data(self, request): def finalizer(): if self.data is not None: for data in self.data: data.delete() self.data = None if self.sensor is not None: self.sensor.delete() self.sensor = None if self.zone is not None: self.zone.delete() self.zone = None self.zone = Zone(name='Test Zone', notes='Zone notes.', key=uuid4()) self.zone.save() self.sensor = Sensor(guid=uuid4(), name='Test Sensor', notes='Sensor notes.', zone=self.zone, min_value=20, max_value=25, min_value_operator='>=', max_value_operator='<=', state=True, state_last_change_date=datetime.utcnow()) self.sensor.save() self.data = [] for i in range(3): datum = SensorData(sensor=self.sensor, datetime=datetime.utcnow(), value=self.sensor.min_value + i, state=True, state_changed=False) datum.save() self.data.append(datum) request.addfinalizer(finalizer)
def test_zone_list_get_with_sensor_filter(self, api_root): excluded_zone = Zone(name='Test Zone Excluded', notes='Zone notes.', key=uuid4()) excluded_zone.save() excluded_sensor = Sensor(guid=uuid4(), name='Test Sensor Excluded', notes='Sensor notes.', zone=excluded_zone, min_value=20, max_value=25, min_value_operator='>=', max_value_operator='<=', state=True, state_last_change_date=datetime.utcnow()) excluded_sensor.save() suffix = 'zones/?sensor_ids={}&format=json'.format(self.sensor.id) response = requests.get(api_root + suffix) data = response.json() assert len(data) > 0 excluded_zone_json = [ x for x in data if x['id'] == str(excluded_zone.id) ] assert len(excluded_zone_json) == 0 excluded_sensor.delete() excluded_zone.delete() zone_json = [x for x in data if int(x['id']) == self.zone.id][0] self._compare_zone_json(self.zone, zone_json)
def test_sensor_data_list_get_with_sensor_filter(self, api_root): excluded_sensor = Sensor( guid=uuid4(), name='Test Sensor Excluded', notes='Sensor notes.', zone=self.zone, min_value=20, max_value=25, min_value_operator='>=', max_value_operator='<=', state=True, state_last_change_date=datetime.utcnow() ) excluded_sensor.save() excluded_data = SensorData( sensor=excluded_sensor, datetime=datetime.utcnow(), value=22, state=True, state_changed=False ) excluded_data.save() suffix = 'data/?sensor_ids={}&format=json'.format(self.sensor.id) response = requests.get(api_root + suffix) data = response.json() assert len(data) > 0 excluded_data_json = [x for x in data if x['id'] == str(excluded_data.id)] assert len(excluded_data_json) == 0 excluded_data.delete() excluded_sensor.delete() for i in range(3): data_json = [x for x in data if int(x['id']) == self.data[i].id][0] self._compare_sensor_data_json(self.data[i], data_json)
def create_data(self, request): def finalizer(): if self.data is not None: for data in self.data: data.delete() self.data = None if self.sensor is not None: self.sensor.delete() self.sensor = None if self.zone is not None: self.zone.delete() self.zone = None self.zone = Zone( name='Test Zone', notes='Zone notes.', key=uuid4() ) self.zone.save() self.sensor = Sensor( guid=uuid4(), name='Test Sensor', notes='Sensor notes.', zone=self.zone, min_value=20, max_value=25, min_value_operator='>=', max_value_operator='<=', state=True, state_last_change_date=datetime.utcnow() ) self.sensor.save() self.data = [] for i in range(3): datum = SensorData( sensor=self.sensor, datetime=datetime.utcnow(), value=self.sensor.min_value + i, state=True, state_changed=False ) datum.save() self.data.append(datum) request.addfinalizer(finalizer)
def create(self, request): """Create a new `SensorData` object for the data in `request`. The `state` and `state_changed` values will be set automatically based on the associated `Sensor`'s values. If necessary, the `Sensor`'s `state` and `state_last_change_date` values will be updated accordingly too. You can optionally send along `guid` with the request, and it will automatically be associated with the matching `Sensor`. If a `Sensor` does not exist, it will be created. Arguments: request -- The API request containing the data for the new object. Returns: A new `SensorData` object containing the data in `request`. """ zone = _get_zone(request) # `sensor` sent. Associate data with this `Sensor`. if "sensor" in request.data: sensor_url = request.data['sensor'] sensor_id = int([x for x in sensor_url.split('/') if x][-1]) sensor = Sensor.objects.get(pk=sensor_id) if sensor.zone.id != zone.id: _raise_zone_key_auth_failed() # `guid` sent. Automatically associate with that particular `Sensor`. # Create a `Sensor` if a matching `guid` does not already exist. elif "guid" in request.data: try: sensor = Sensor.objects.get(zone=zone, guid=request.data['guid']) except Sensor.DoesNotExist: sensor = Sensor() sensor.name = request.data['guid'] sensor.guid = request.data['guid'] sensor.zone = zone sensor.save() request.data['sensor'] = reverse('sensor-detail', args=[sensor.id], request=request) return super(SensorDataViewSet, self).create(request)
def add_plant(request): if request.method == 'POST': plant = Plant() if (not request.POST.get('alias')) or len( Plant.objects.filter(alias=request.POST.get('alias'))) != 0: return HttpResponseRedirect(reverse('plants:dashboard')) plant.alias = request.POST.get('alias') plant.parent = request.user plant.save() sensor1 = Sensor() sensor1.sensor_type = 'GPS Module' sensor1.parent = plant sensor1.save() actuator = Actuator() actuator.parent = plant actuator.name = request.POST.get('alias') actuator.state = 0 actuator.save() return HttpResponseRedirect(reverse('plants:dashboard')) return HttpResponseRedirect(reverse('plants:dashboard'))
def test_sensor_data_list_get_with_sensor_filter(self, api_root): excluded_sensor = Sensor(guid=uuid4(), name='Test Sensor Excluded', notes='Sensor notes.', zone=self.zone, min_value=20, max_value=25, min_value_operator='>=', max_value_operator='<=', state=True, state_last_change_date=datetime.utcnow()) excluded_sensor.save() excluded_data = SensorData(sensor=excluded_sensor, datetime=datetime.utcnow(), value=22, state=True, state_changed=False) excluded_data.save() suffix = 'data/?sensor_ids={}&format=json'.format(self.sensor.id) response = requests.get(api_root + suffix) data = response.json() assert len(data) > 0 excluded_data_json = [ x for x in data if x['id'] == str(excluded_data.id) ] assert len(excluded_data_json) == 0 excluded_data.delete() excluded_sensor.delete() for i in range(3): data_json = [x for x in data if int(x['id']) == self.data[i].id][0] self._compare_sensor_data_json(self.data[i], data_json)
class TestSensorsApi(object): zone = None sensor = None data = None @pytest.fixture(autouse=True) def api_root(self, request, live_server): api_root = '{}{}api/v1/'.format( live_server.url, '/' if not live_server.url.endswith('/') else '') return api_root @pytest.fixture(autouse=True) def create_data(self, request): def finalizer(): if self.data is not None: for data in self.data: data.delete() self.data = None if self.sensor is not None: self.sensor.delete() self.sensor = None if self.zone is not None: self.zone.delete() self.zone = None self.zone = Zone(name='Test Zone', notes='Zone notes.', key=uuid4()) self.zone.save() self.sensor = Sensor(guid=uuid4(), name='Test Sensor', notes='Sensor notes.', zone=self.zone, min_value=20, max_value=25, min_value_operator='>=', max_value_operator='<=', state=True, state_last_change_date=datetime.utcnow()) self.sensor.save() self.data = [] for i in range(3): datum = SensorData(sensor=self.sensor, datetime=datetime.utcnow(), value=self.sensor.min_value + i, state=True, state_changed=False) datum.save() self.data.append(datum) request.addfinalizer(finalizer) def _compare_zone_json(self, zone, zone_json): assert zone.id == zone_json['id'] assert zone.name == zone_json['name'] assert zone.notes == zone_json['notes'] def _compare_sensor_json(self, sensor, sensor_json): assert sensor.id == sensor_json['id'] assert str(sensor.guid) == sensor_json['guid'] assert sensor.name == sensor_json['name'] assert sensor.notes == sensor_json['notes'] assert sensor.latest_value == sensor_json['latest_value'] assert sensor.min_value == Decimal(sensor_json['min_value']) assert sensor.max_value == Decimal(sensor_json['max_value']) assert sensor.min_value_operator == sensor_json['min_value_operator'] assert sensor.max_value_operator == sensor_json['max_value_operator'] assert sensor.state == sensor_json['state'] assert sensor.state_last_change_date.strftime('%Y-%m-%dT%H:%M:%S.%f') == \ sensor_json['state_last_change_date'][:26] m = re.match(r'.*zones/([0-9]+)/?', sensor_json['zone']) assert m is not None assert sensor.zone.id == int(m.group(1)) def _compare_sensor_data_json(self, sensor_data, sensor_data_json): assert sensor_data.datetime.strftime('%Y-%m-%dT%H:%M:%S.%f') == \ sensor_data_json['datetime'][:26] assert sensor_data.value == Decimal(sensor_data_json['value']) assert sensor_data.state == sensor_data_json['state'] assert sensor_data.state_changed == sensor_data_json['state_changed'] m = re.match(r'.*sensors/([0-9]+)/?', sensor_data_json['sensor']) assert m is not None assert sensor_data.sensor.id == int(m.group(1)) def test_api_root_get(self, api_root): response = requests.get(api_root) data = response.json() assert 'data' in data assert 'sensors' in data assert 'zones' in data def test_api_root_post(self, api_root): response = requests.post(api_root) assert response.status_code == 405 def test_api_root_put(self, api_root): response = requests.put(api_root) assert response.status_code == 405 def test_api_root_patch(self, api_root): response = requests.patch(api_root) assert response.status_code == 405 def test_api_root_delete(self, api_root): response = requests.delete(api_root) assert response.status_code == 405 def test_sensors_list_get_no_filters(self, api_root): response = requests.get(api_root + 'sensors/?format=json') data = response.json() assert len(data) > 0 sensor_json = [x for x in data if x['guid'] == str(self.sensor.guid)][0] self._compare_sensor_json(self.sensor, sensor_json) def test_sensors_list_get_with_limit_and_offset(self, api_root): response = requests.get(api_root + 'sensors/?limit=10&offset=0&format=json') data = response.json() assert 'count' in data assert 'next' in data assert 'previous' in data assert 'results' in data # It's more work than it's worth to check that we can find self.sensor # in this test--there's no telling how many pages would have to be # checked, and the json is already being checked elsewhere anyway. -ajm def test_sensors_list_get_with_sensor_filter(self, api_root): excluded_sensor = Sensor(guid=uuid4(), name='Test Sensor Excluded', notes='Sensor notes.', zone=self.zone, min_value=20, max_value=25, min_value_operator='>=', max_value_operator='<=', state=True, state_last_change_date=datetime.utcnow()) excluded_sensor.save() suffix = 'sensors/?sensor_ids={}&format=json'.format(self.sensor.id) response = requests.get(api_root + suffix) data = response.json() assert len(data) > 0 excluded_sensor_json = [ x for x in data if x['guid'] == str(excluded_sensor.guid) ] assert len(excluded_sensor_json) == 0 excluded_sensor.delete() sensor_json = [x for x in data if x['guid'] == str(self.sensor.guid)][0] self._compare_sensor_json(self.sensor, sensor_json) def test_sensors_list_get_with_zone_filter(self, api_root): excluded_zone = Zone(name='Test Zone Excluded', notes='Zone notes.', key=uuid4()) excluded_zone.save() excluded_sensor = Sensor(guid=uuid4(), name='Test Sensor Excluded', notes='Sensor notes.', zone=excluded_zone, min_value=20, max_value=25, min_value_operator='>=', max_value_operator='<=', state=True, state_last_change_date=datetime.utcnow()) excluded_sensor.save() suffix = 'sensors/?zone_ids={}&format=json'.format(self.sensor.zone.id) response = requests.get(api_root + suffix) data = response.json() assert len(data) > 0 excluded_sensor_json = [ x for x in data if x['guid'] == str(excluded_sensor.guid) ] assert len(excluded_sensor_json) == 0 excluded_sensor.delete() excluded_zone.delete() sensor_json = [x for x in data if x['guid'] == str(self.sensor.guid)][0] self._compare_sensor_json(self.sensor, sensor_json) def test_sensors_list_get_with_order_by(self, api_root): second_sensor = Sensor( guid=uuid4(), name=self.sensor.name + '_', # Just something to make it sort lower notes='Sensor notes.', zone=self.zone, min_value=20, max_value=25, min_value_operator='>=', max_value_operator='<=', state=True, state_last_change_date=datetime.utcnow()) second_sensor.save() # Sort by name, ascending response = requests.get(api_root + 'sensors/?order_by=name&format=json') data = response.json() assert len(data) > 1 first_guid = str(self.sensor.guid) second_guid = str(second_sensor.guid) matches = [ x for x in data if (x['guid'] == first_guid or x['guid'] == second_guid) ] assert matches[0]['name'] == self.sensor.name assert matches[1]['name'] == second_sensor.name # Sort by name, descending response = requests.get(api_root + 'sensors/?order_by=-name&format=json') data = response.json() assert len(data) > 1 matches = [ x for x in data if (x['guid'] == first_guid or x['guid'] == second_guid) ] assert matches[0]['name'] == second_sensor.name assert matches[1]['name'] == self.sensor.name # Default sorting (id) response = requests.get(api_root + 'sensors/?format=json') data = response.json() assert len(data) > 1 matches = [ x for x in data if (x['guid'] == first_guid or x['guid'] == second_guid) ] assert matches[0]['name'] == self.sensor.name assert matches[1]['name'] == second_sensor.name def test_sensors_list_post(self, api_root): sensor_dict = { 'guid': str(uuid4()), 'name': 'POST Sensor', 'notes': 'POST notes.', 'min_value': 20, 'max_value': 25, 'min_value_operator': '>=', 'max_value_operator': '<=', 'key': str(self.zone.key) } headers = {'Content-type': 'application/json'} response = requests.post(api_root + 'sensors/', data=json.dumps(sensor_dict), headers=headers) data = response.json() assert 'id' in data id = int(data['id']) assert id > 0 sensor = Sensor.objects.get(id=id) self._compare_sensor_json(sensor, data) sensor.delete() def test_sensors_list_post_with_invalid_data(self, api_root): sensor_dict = { 'guid': None, 'name': None, 'notes': 'POST notes.', 'min_value': 20, 'max_value': 25, 'min_value_operator': '>=', 'max_value_operator': '<=', 'key': str(self.zone.key) } headers = {'Content-type': 'application/json'} response = requests.post(api_root + 'sensors/', data=json.dumps(sensor_dict), headers=headers) # Attempt to remove the object if it's erroneously created. try: data = response.json() if 'id' in data: Sensor.objects.get(id=int(data['id'])).delete() except: pass assert response.status_code == 400 def test_sensors_list_post_without_zone_key(self, api_root): sensor_dict = { 'guid': str(uuid4()), 'name': 'POST Sensor', 'notes': 'POST notes.', 'min_value': 20, 'max_value': 25, 'min_value_operator': '>=', 'max_value_operator': '<=' } headers = {'Content-type': 'application/json'} response = requests.post(api_root + 'sensors/', data=json.dumps(sensor_dict), headers=headers) # Attempt to remove the object if it's erroneously created. try: data = response.json() if 'id' in data: Sensor.objects.get(id=int(data['id'])).delete() except: pass assert response.status_code == 403 def test_sensors_list_put(self, api_root): response = requests.put(api_root + 'sensors/') assert response.status_code == 405 def test_sensors_list_patch(self, api_root): response = requests.patch(api_root + 'sensors/') assert response.status_code == 405 def test_sensors_list_delete(self, api_root): response = requests.delete(api_root + 'sensors/') assert response.status_code == 405 def test_sensors_detail_get(self, api_root): suffix = 'sensors/{}/?format=json'.format(self.sensor.id) response = requests.get(api_root + suffix) data = response.json() self._compare_sensor_json(self.sensor, data) def test_sensors_detail_post(self, api_root): suffix = 'sensors/{}/?format=json'.format(self.sensor.id) response = requests.post(api_root + suffix) assert response.status_code == 405 def test_sensors_detail_put(self, api_root): suffix = 'sensors/{}/'.format(self.sensor.id) sensor_dict = { 'guid': str(uuid4()), 'name': 'PUT Sensor', 'notes': 'PUT notes.', 'zone': api_root + 'zones/{}/'.format(self.zone.id), 'min_value': 200, 'max_value': 250, 'min_value_operator': '<=', 'max_value_operator': '>=', 'key': str(self.zone.key) } headers = {'Content-type': 'application/json'} response = requests.put(api_root + suffix, data=json.dumps(sensor_dict), headers=headers) assert response.status_code == 200 data = response.json() assert data['guid'] == sensor_dict['guid'] assert data['name'] == sensor_dict['name'] assert data['notes'] == sensor_dict['notes'] assert data['zone'] == sensor_dict['zone'] assert Decimal(data['min_value']) == Decimal(sensor_dict['min_value']) assert Decimal(data['max_value']) == Decimal(sensor_dict['max_value']) assert data['min_value_operator'] == sensor_dict['min_value_operator'] assert data['max_value_operator'] == sensor_dict['max_value_operator'] def test_sensors_detail_put_without_zone_key(self, api_root): suffix = 'sensors/{}/'.format(self.sensor.id) sensor_dict = { 'guid': str(uuid4()), 'name': 'PUT Sensor', 'notes': 'PUT notes.', 'zone': api_root + 'zones/{}/'.format(self.zone.id), 'min_value': 200, 'max_value': 250, 'min_value_operator': '<=', 'max_value_operator': '>=' } headers = {'Content-type': 'application/json'} response = requests.put(api_root + suffix, data=json.dumps(sensor_dict), headers=headers) assert response.status_code == 403 def test_sensors_detail_put_with_invalid_data(self, api_root): suffix = 'sensors/{}/'.format(self.sensor.id) sensor_dict = { 'guid': None, 'name': None, 'notes': 'PUT notes.', 'zone': api_root + 'zones/{}/'.format(self.zone.id), 'min_value': 200, 'max_value': 250, 'min_value_operator': '<=', 'max_value_operator': '>=', 'key': str(self.zone.key) } headers = {'Content-type': 'application/json'} response = requests.put(api_root + suffix, data=json.dumps(sensor_dict), headers=headers) assert response.status_code == 400 def test_sensors_detail_patch(self, api_root): suffix = 'sensors/{}/'.format(self.sensor.id) sensor_dict = { 'guid': str(self.sensor.guid), 'name': 'PATCH Sensor', 'notes': 'PATCH notes.', 'min_value': self.sensor.min_value, 'max_value': self.sensor.max_value, 'key': str(self.zone.key) } headers = {'Content-type': 'application/json'} response = requests.patch(api_root + suffix, data=json.dumps(sensor_dict), headers=headers) print(response.text) assert response.status_code == 200 data = response.json() assert data['guid'] == sensor_dict['guid'] assert data['name'] == sensor_dict['name'] assert data['notes'] == sensor_dict['notes'] assert Decimal(data['min_value']) == Decimal(sensor_dict['min_value']) assert Decimal(data['max_value']) == Decimal(sensor_dict['max_value']) def test_sensors_detail_patch_without_zone_key(self, api_root): suffix = 'sensors/{}/'.format(self.sensor.id) sensor_dict = { 'guid': str(self.sensor.guid), 'name': 'PATCH Sensor', 'notes': 'PATCH notes.', 'min_value': self.sensor.min_value, 'max_value': self.sensor.max_value } headers = {'Content-type': 'application/json'} response = requests.patch(api_root + suffix, data=json.dumps(sensor_dict), headers=headers) assert response.status_code == 403 def test_sensors_detail_patch_with_invalid_data(self, api_root): suffix = 'sensors/{}/'.format(self.sensor.id) sensor_dict = { 'guid': None, 'name': None, 'notes': 'PATCH notes.', 'min_value': None, 'max_value': None, 'key': str(self.zone.key) } headers = {'Content-type': 'application/json'} response = requests.patch(api_root + suffix, data=json.dumps(sensor_dict), headers=headers) print(response.text) assert response.status_code == 400 def test_sensors_detail_delete(self, api_root): data = {'key': str(self.zone.key)} headers = {'Content-type': 'application/json'} suffix = 'sensors/{}/'.format(self.sensor.id) response = requests.delete(api_root + suffix, data=json.dumps(data), headers=headers) assert response.status_code == 204 def test_sensors_detail_delete_without_zone_key(self, api_root): suffix = 'sensors/{}/'.format(self.sensor.id) response = requests.delete(api_root + suffix) assert response.status_code == 403 def test_sensor_data_list_get_no_filters(self, api_root): response = requests.get(api_root + 'data/?format=json') data = response.json() assert len(data) > 2 for i in range(3): data_json = [x for x in data if int(x['id']) == self.data[i].id][0] self._compare_sensor_data_json(self.data[i], data_json) def test_sensor_data_list_get_with_limit_and_offset(self, api_root): response = requests.get(api_root + 'data/?limit=10&offset=0&format=json') data = response.json() assert 'count' in data assert 'next' in data assert 'previous' in data assert 'results' in data def test_sensor_data_list_get_with_sensor_filter(self, api_root): excluded_sensor = Sensor(guid=uuid4(), name='Test Sensor Excluded', notes='Sensor notes.', zone=self.zone, min_value=20, max_value=25, min_value_operator='>=', max_value_operator='<=', state=True, state_last_change_date=datetime.utcnow()) excluded_sensor.save() excluded_data = SensorData(sensor=excluded_sensor, datetime=datetime.utcnow(), value=22, state=True, state_changed=False) excluded_data.save() suffix = 'data/?sensor_ids={}&format=json'.format(self.sensor.id) response = requests.get(api_root + suffix) data = response.json() assert len(data) > 0 excluded_data_json = [ x for x in data if x['id'] == str(excluded_data.id) ] assert len(excluded_data_json) == 0 excluded_data.delete() excluded_sensor.delete() for i in range(3): data_json = [x for x in data if int(x['id']) == self.data[i].id][0] self._compare_sensor_data_json(self.data[i], data_json) def test_sensor_data_list_get_with_zone_filter(self, api_root): excluded_zone = Zone(name='Test Zone Excluded', notes='Zone notes.', key=uuid4()) excluded_zone.save() excluded_sensor = Sensor(guid=uuid4(), name='Test Sensor Excluded', notes='Sensor notes.', zone=excluded_zone, min_value=20, max_value=25, min_value_operator='>=', max_value_operator='<=', state=True, state_last_change_date=datetime.utcnow()) excluded_sensor.save() excluded_data = SensorData(sensor=excluded_sensor, datetime=datetime.utcnow(), value=22, state=True, state_changed=False) excluded_data.save() suffix = 'data/?zone_ids={}&format=json'.format(self.zone.id) response = requests.get(api_root + suffix) data = response.json() assert len(data) > 0 excluded_data_json = [ x for x in data if x['id'] == str(excluded_data.id) ] assert len(excluded_data_json) == 0 excluded_data.delete() excluded_sensor.delete() excluded_zone.delete() for i in range(3): data_json = [x for x in data if int(x['id']) == self.data[i].id][0] self._compare_sensor_data_json(self.data[i], data_json) def test_sensor_data_list_get_with_date_filters(self, api_root): suffix = 'data/?start_date={}&end_date={}&format=json'.format( self.data[0].datetime.strftime('%Y-%m-%dT%H:%M:%S.%fZ'), self.data[0].datetime.strftime('%Y-%m-%dT%H:%M:%S.%fZ')) response = requests.get(api_root + suffix) data = response.json() assert len(data) > 0 excluded_data_json = [ x for x in data if x['id'] == str(self.data[1].id) ] assert len(excluded_data_json) == 0 data_json = [x for x in data if int(x['id']) == self.data[0].id][0] self._compare_sensor_data_json(self.data[0], data_json) def test_sensor_data_list_get_with_value_filters(self, api_root): suffix = 'data/?min_value={}&max_value={}&format=json'.format( self.data[0].value, self.data[0].value) response = requests.get(api_root + suffix) data = response.json() assert len(data) > 0 excluded_data_json = [ x for x in data if x['id'] == str(self.data[1].id) ] assert len(excluded_data_json) == 0 data_json = [x for x in data if int(x['id']) == self.data[0].id][0] self._compare_sensor_data_json(self.data[0], data_json) def test_sensor_data_list_get_csv(self, api_root): suffix = 'data/?zone_ids={}&format=csv'.format(self.zone.id) response = requests.get(api_root + suffix) reader = csv.reader(StringIO(response.text)) num_lines = 0 for line in reader: num_lines = num_lines + 1 # All lines should contain 4 fields # datetime,sensor_name,state,value assert len(line) == 4 # Should expect 3 lines of data and 1 header line == 4 assert num_lines == 4 def test_sensor_data_list_post(self, api_root): data_dict = { 'sensor': api_root + 'sensors/{}/'.format(self.sensor.id), 'datetime': datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%fZ'), 'value': 22.5, 'key': str(self.zone.key) } headers = {'Content-type': 'application/json'} response = requests.post(api_root + 'data/', data=json.dumps(data_dict), headers=headers) data = response.json() assert 'id' in data id = int(data['id']) assert id > 0 sensor_data = SensorData.objects.get(id=id) self._compare_sensor_data_json(sensor_data, data) sensor_data.delete() def test_sensor_data_list_post_without_zone_key(self, api_root): data_dict = { 'sensor': api_root + 'sensors/{}/'.format(self.sensor.id), 'datetime': datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%fZ'), 'value': 22.5 } headers = {'Content-type': 'application/json'} response = requests.post(api_root + 'data/', data=json.dumps(data_dict), headers=headers) assert response.status_code == 403 def test_sensor_data_list_post_in_range_maintains_state(self, api_root): prev_state_last_change_date = self.sensor.state_last_change_date data_dict = { 'sensor': api_root + 'sensors/{}/'.format(self.sensor.id), 'datetime': datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%fZ'), 'value': 22.5, 'key': str(self.zone.key) } headers = {'Content-type': 'application/json'} response = requests.post(api_root + 'data/', data=json.dumps(data_dict), headers=headers) data = response.json() assert 'id' in data id = int(data['id']) assert id > 0 sensor_data = SensorData.objects.get(id=id) self._compare_sensor_data_json(sensor_data, data) sensor = Sensor.objects.get(pk=self.sensor.id) assert sensor.state is True assert sensor.state_last_change_date.strftime('%Y-%m-%dT%H:%M:%S.%fZ') == \ prev_state_last_change_date.strftime('%Y-%m-%dT%H:%M:%S.%fZ') sensor_data.delete() def test_sensor_data_list_post_outside_range_updates_state(self, api_root): prev_state_last_change_date = self.sensor.state_last_change_date data_dict = { 'sensor': api_root + 'sensors/{}/'.format(self.sensor.id), 'datetime': datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%fZ'), 'value': self.sensor.max_value + 1, 'key': str(self.zone.key) } headers = {'Content-type': 'application/json'} response = requests.post(api_root + 'data/', data=json.dumps(data_dict), headers=headers) data = response.json() assert 'id' in data id = int(data['id']) assert id > 0 sensor_data = SensorData.objects.get(id=id) self._compare_sensor_data_json(sensor_data, data) sensor = Sensor.objects.get(pk=self.sensor.id) assert not sensor.state assert sensor.state_last_change_date.strftime('%Y-%m-%dT%H:%M:%S.%fZ') != \ prev_state_last_change_date.strftime('%Y-%m-%dT%H:%M:%S.%fZ') sensor_data.delete() def test_sensor_data_list_put(self, api_root): response = requests.put(api_root + 'data/') assert response.status_code == 405 def test_sensor_data_list_patch(self, api_root): response = requests.patch(api_root + 'data/') assert response.status_code == 405 def test_sensor_data_list_delete(self, api_root): response = requests.delete(api_root + 'data/') assert response.status_code == 405 def test_sensor_data_detail_get(self, api_root): suffix = 'data/{}/?format=json'.format(self.data[0].id) response = requests.get(api_root + suffix) data = response.json() self._compare_sensor_data_json(self.data[0], data) def test_sensor_data_detail_post(self, api_root): response = requests.post(api_root + 'data/{}/'.format(self.data[0].id)) assert response.status_code == 405 def test_sensor_data_detail_post_guid(self, api_root): """ Test adding sensor data with only supplying a Sensor guid, sensor data value and a zone key. If the sensor doesn't exist, this will create it. """ zone = Zone(name='Test Zone', key=uuid4()) zone.save() guid = 'DEADBEEF00000012345TEST' value_first = 15.0 value_second = 25.0 num_sensors_before_add = Sensor.objects.all().count() data_dict = { 'guid': guid, 'value': value_first, 'key': str(zone.key), } headers = {'Content-type': 'application/json'} response = requests.post(api_root + 'data/', data=json.dumps(data_dict), headers=headers) response_json_first = response.json() num_sensors_after_add = Sensor.objects.all().count() assert response.status_code == 201 assert num_sensors_before_add + 1 == num_sensors_after_add data = SensorData.objects.get(pk=response_json_first["id"]) data.delete() data_dict = { 'guid': guid, 'value': value_second, 'key': str(zone.key), } headers = {'Content-type': 'application/json'} response = requests.post(api_root + 'data/', data=json.dumps(data_dict), headers=headers) response_json_second = response.json() num_sensors_after_second_request = Sensor.objects.all().count() # Ensure we didn't add another sensor assert response.status_code == 201 assert num_sensors_after_add == num_sensors_after_second_request assert response_json_first["sensor"] == response_json_second["sensor"] data = SensorData.objects.get(pk=response_json_second["id"]) sensor = Sensor.objects.get(pk=data.sensor.id) data.delete() sensor.delete() def test_sensor_data_detail_put(self, api_root): response = requests.put(api_root + 'data/{}/'.format(self.data[0].id)) assert response.status_code == 405 def test_sensor_data_detail_patch(self, api_root): response = requests.patch(api_root + 'data/{}/'.format(self.data[0].id)) assert response.status_code == 405 def test_sensor_data_detail_delete(self, api_root): response = requests.delete(api_root + 'data/{}/'.format(self.data[0].id)) assert response.status_code == 405 def test_zone_list_get_no_filters(self, api_root): response = requests.get(api_root + 'zones/?format=json') data = response.json() assert len(data) > 0 zone_json = [x for x in data if int(x['id']) == self.zone.id][0] self._compare_zone_json(self.zone, zone_json) def test_zone_list_get_with_limit_and_offset(self, api_root): response = requests.get(api_root + 'zones/?limit=10&offset=0&format=json') data = response.json() assert 'count' in data assert 'next' in data assert 'previous' in data assert 'results' in data def test_zone_list_get_with_sensor_filter(self, api_root): excluded_zone = Zone(name='Test Zone Excluded', notes='Zone notes.', key=uuid4()) excluded_zone.save() excluded_sensor = Sensor(guid=uuid4(), name='Test Sensor Excluded', notes='Sensor notes.', zone=excluded_zone, min_value=20, max_value=25, min_value_operator='>=', max_value_operator='<=', state=True, state_last_change_date=datetime.utcnow()) excluded_sensor.save() suffix = 'zones/?sensor_ids={}&format=json'.format(self.sensor.id) response = requests.get(api_root + suffix) data = response.json() assert len(data) > 0 excluded_zone_json = [ x for x in data if x['id'] == str(excluded_zone.id) ] assert len(excluded_zone_json) == 0 excluded_sensor.delete() excluded_zone.delete() zone_json = [x for x in data if int(x['id']) == self.zone.id][0] self._compare_zone_json(self.zone, zone_json) def test_zone_list_get_with_zone_filter(self, api_root): excluded_zone = Zone(name='Test Zone Excluded', notes='Zone notes.', key=uuid4()) excluded_zone.save() suffix = 'zones/?zone_ids={}&format=json'.format(self.zone.id) response = requests.get(api_root + suffix) data = response.json() assert len(data) > 0 excluded_zone_json = [ x for x in data if x['id'] == str(excluded_zone.id) ] assert len(excluded_zone_json) == 0 excluded_zone.delete() zone_json = [x for x in data if int(x['id']) == self.zone.id][0] self._compare_zone_json(self.zone, zone_json) def test_zone_list_post(self, api_root): response = requests.put(api_root + 'zones/') assert response.status_code == 405 def test_zone_list_put(self, api_root): response = requests.put(api_root + 'zones/') assert response.status_code == 405 def test_zone_list_patch(self, api_root): response = requests.patch(api_root + 'zones/') assert response.status_code == 405 def test_zone_list_delete(self, api_root): response = requests.delete(api_root + 'zones/') assert response.status_code == 405 def test_zone_detail_get(self, api_root): response = requests.get(api_root + 'zones/{}/'.format(self.zone.id)) data = response.json() self._compare_zone_json(self.zone, data) def test_zone_detail_post(self, api_root): response = requests.post(api_root + 'zones/{}/'.format(self.zone.id)) assert response.status_code == 405 def test_zone_detail_put(self, api_root): zone_dict = { 'name': 'PUT Zone', 'notes': 'PUT notes.', 'key': str(self.zone.key) } headers = {'Content-type': 'application/json'} response = requests.put(api_root + 'zones/{}/'.format(self.zone.id), data=json.dumps(zone_dict), headers=headers) assert response.status_code == 200 data = response.json() assert data['name'] == zone_dict['name'] assert data['notes'] == zone_dict['notes'] def test_zone_detail_put_without_zone_key(self, api_root): zone_dict = {'name': 'PUT Zone', 'notes': 'PUT notes.'} headers = {'Content-type': 'application/json'} response = requests.put(api_root + 'zones/{}/'.format(self.zone.id), data=json.dumps(zone_dict), headers=headers) assert response.status_code == 403 def test_zone_detail_put_with_invalid_data(self, api_root): zone_dict = { 'name': None, 'notes': 'PUT notes.', 'key': str(self.zone.key) } headers = {'Content-type': 'application/json'} response = requests.put(api_root + 'zones/{}/'.format(self.zone.id), data=json.dumps(zone_dict), headers=headers) assert response.status_code == 400 def test_zone_detail_patch(self, api_root): zone_dict = { 'name': 'PATCH Zone', 'notes': 'PATCH notes.', 'key': str(self.zone.key) } headers = {'Content-type': 'application/json'} response = requests.patch(api_root + 'zones/{}/'.format(self.zone.id), data=json.dumps(zone_dict), headers=headers) assert response.status_code == 200 data = response.json() assert data['name'] == zone_dict['name'] assert data['notes'] == zone_dict['notes'] def test_zone_detail_patch_without_zone_key(self, api_root): zone_dict = {'name': 'PATCH Zone', 'notes': 'PATCH notes.'} headers = {'Content-type': 'application/json'} response = requests.patch(api_root + 'zones/{}/'.format(self.zone.id), data=json.dumps(zone_dict), headers=headers) assert response.status_code == 403 def test_zone_detail_patch_with_invalid_data(self, api_root): zone_dict = { 'name': None, 'notes': 'PATCH notes.', 'key': str(self.zone.key) } headers = {'Content-type': 'application/json'} response = requests.patch(api_root + 'zones/{}/'.format(self.zone.id), data=json.dumps(zone_dict), headers=headers) assert response.status_code == 400 def test_zone_detail_delete(self, api_root): response = requests.delete(api_root + 'zones/{}/'.format(self.zone.id)) assert response.status_code == 405
def add_plant(request): if request.method == 'POST': plant = Plant() if (not request.POST.get('alias')) or len(Plant.objects.filter(alias=request.POST.get('alias')))!=0 : return HttpResponseRedirect(reverse('plants:dashboard')) plant.alias = request.POST.get('alias') plant.parent = request.user plant.save() sensor1 = Sensor() sensor1.sensor_type = 'Temperature' sensor1.parent = plant sensor1.save() sensor2 = Sensor() sensor2.sensor_type = 'Humidity' sensor2.parent = plant sensor2.save() sensor3 = Sensor() sensor3.sensor_type = 'Soil Moisture' sensor3.parent = plant sensor3.save() sensor4 = Sensor() sensor4.sensor_type = 'Water Level' sensor4.parent = plant sensor4.save() sensor5 = Sensor() sensor5.parent = plant sensor5.sensor_type = 'RainSensor' sensor5.save() actuator = Actuator() actuator.parent = plant actuator.name = request.POST.get('alias') actuator.state = 0 actuator.save() return HttpResponseRedirect(reverse('plants:dashboard')) return HttpResponseRedirect(reverse('plants:dashboard'))
def create_data(self, request): def finalizer(): if self.alerts is not None: for alert in self.alerts: alert.delete() self.alerts = None if self.data is not None: for data in self.data: data.delete() self.data = None if self.sensor is not None: self.sensor.delete() self.sensor = None if self.zone is not None: self.zone.delete() self.zone = None if self.alert_group is not None: self.alert_group.delete() self.alert_group = None if self.user is not None: self.user.delete() username = str(uuid4())[:30] self.user = User(username=username, email='{}@example.net'.format(username)) self.user.save() self.alert_group = SensorAlertGroup(name='Test Group') self.alert_group.save() self.alert_group.users.add(self.user) self.zone = Zone(name='Test Zone', notes='Zone notes.', key=uuid4()) self.zone.save() self.sensor = Sensor(guid=uuid4(), name='Test Sensor', notes='Sensor notes.', zone=self.zone, min_value=60, max_value=85, min_value_operator='>=', max_value_operator='<=', state=True, state_last_change_date=datetime.utcnow()) self.sensor.save() self.sensor.alert_groups.add(self.alert_group) self.data = [] # Inbox is empty assert 0 == len(mail.outbox) # Create in-range data point; no notification. datum = SensorData(sensor=self.sensor, datetime=datetime.utcnow(), value=self.sensor.min_value + 1, state=True, state_changed=False) datum.save() self.data.append(datum) # Inbox still empty assert 0 == len(mail.outbox) # Create out-of-range notification; sends "alert." datum = SensorData(sensor=self.sensor, datetime=datetime.utcnow(), value=self.sensor.min_value - 1, state=True, state_changed=False) datum.save() self.data.append(datum) # Inbox received alert assert 1 == len(mail.outbox) # Create another in-range data point; sends "recovered." datum = SensorData(sensor=self.sensor, datetime=datetime.utcnow(), value=self.sensor.min_value + 1, state=True, state_changed=False) datum.save() self.data.append(datum) # Inbox received recovery email assert 2 == len(mail.outbox) self.alerts = SensorAlert.objects.all().order_by('id') request.addfinalizer(finalizer)
class TestSensorsApi(object): user = None alert_group = None zone = None sensor = None data = None alerts = None @pytest.fixture(autouse=True) def api_root(self, request, live_server): api_root = '{}{}api/v1/'.format( live_server.url, '/' if not live_server.url.endswith('/') else '') return api_root @pytest.fixture(autouse=True) def create_data(self, request): def finalizer(): if self.alerts is not None: for alert in self.alerts: alert.delete() self.alerts = None if self.data is not None: for data in self.data: data.delete() self.data = None if self.sensor is not None: self.sensor.delete() self.sensor = None if self.zone is not None: self.zone.delete() self.zone = None if self.alert_group is not None: self.alert_group.delete() self.alert_group = None if self.user is not None: self.user.delete() username = str(uuid4())[:30] self.user = User(username=username, email='{}@example.net'.format(username)) self.user.save() self.alert_group = SensorAlertGroup(name='Test Group') self.alert_group.save() self.alert_group.users.add(self.user) self.zone = Zone(name='Test Zone', notes='Zone notes.', key=uuid4()) self.zone.save() self.sensor = Sensor(guid=uuid4(), name='Test Sensor', notes='Sensor notes.', zone=self.zone, min_value=60, max_value=85, min_value_operator='>=', max_value_operator='<=', state=True, state_last_change_date=datetime.utcnow()) self.sensor.save() self.sensor.alert_groups.add(self.alert_group) self.data = [] # Inbox is empty assert 0 == len(mail.outbox) # Create in-range data point; no notification. datum = SensorData(sensor=self.sensor, datetime=datetime.utcnow(), value=self.sensor.min_value + 1, state=True, state_changed=False) datum.save() self.data.append(datum) # Inbox still empty assert 0 == len(mail.outbox) # Create out-of-range notification; sends "alert." datum = SensorData(sensor=self.sensor, datetime=datetime.utcnow(), value=self.sensor.min_value - 1, state=True, state_changed=False) datum.save() self.data.append(datum) # Inbox received alert assert 1 == len(mail.outbox) # Create another in-range data point; sends "recovered." datum = SensorData(sensor=self.sensor, datetime=datetime.utcnow(), value=self.sensor.min_value + 1, state=True, state_changed=False) datum.save() self.data.append(datum) # Inbox received recovery email assert 2 == len(mail.outbox) self.alerts = SensorAlert.objects.all().order_by('id') request.addfinalizer(finalizer) def _compare_sensor_alert_json(self, alert, alert_json): assert alert.id == alert_json['id'] assert alert.alert_type == alert_json['alert_type'] assert alert.alert_class == alert_json['alert_class'] assert alert.recipients == alert_json['recipients'] assert alert.message == alert_json['message'] assert alert.date.strftime( '%Y-%m-%dT%H:%M:%S.%f') == alert_json['date'][:26] m = re.match(r'.*sensors/([0-9]+)/?', alert_json['sensor']) assert m is not None assert alert.sensor.id == int(m.group(1)) m = re.match(r'.*data/([0-9]+)/?', alert_json['data_point']) assert m is not None assert alert.data_point.id == int(m.group(1)) def test_alerts_list_no_filters(self, api_root): response = requests.get(api_root + 'alerts/?format=json') data = response.json() assert len(data) > 1 for alert in self.alerts: alert_json = [x for x in data if int(x['id']) == alert.id][0] self._compare_sensor_alert_json(alert, alert_json) def test_alerts_list_get_with_limit_and_offset(self, api_root): response = requests.get(api_root + 'alerts/?limit=10&offset=0&format=json') data = response.json() assert 'count' in data assert 'next' in data assert 'previous' in data assert 'results' in data def test_alerts_list_get_with_alert_filter(self, api_root): included_alert = self.alerts[0] excluded_alert = self.alerts[1] suffix = 'alerts/?alert_ids={}&format=json'.format(included_alert.id) response = requests.get(api_root + suffix) data = response.json() assert len(data) > 0 excluded_alert_json = [ x for x in data if int(x['id']) == excluded_alert.id ] assert len(excluded_alert_json) == 0 included_alert_json = [ x for x in data if int(x['id']) == included_alert.id ][0] self._compare_sensor_alert_json(included_alert, included_alert_json) def test_alerts_list_get_with_zone_filters(self, api_root): alert = self.alerts[0] # Ensure this alert is *not* included if its zone isn't specified. suffix = 'alerts/?zone_ids={}&format=json'.format( alert.sensor.zone.id + 1) response = requests.get(api_root + suffix) data = response.json() alerts_json = [x for x in data if int(x['id']) == alert.id] assert len(alerts_json) == 0 # Ensure this alert *is* included if its zone is specified. suffix = 'alerts/?zone_ids={}&format=json'.format(alert.sensor.zone.id) response = requests.get(api_root + suffix) data = response.json() alert_json = [x for x in data if int(x['id']) == alert.id][0] self._compare_sensor_alert_json(alert, alert_json) def test_alerts_list_get_with_sensor_filters(self, api_root): alert = self.alerts[0] # Ensure this alert is *not* included if its zone isn't specified. suffix = 'alerts/?sensor_ids={}&format=json'.format(alert.sensor.id + 1) response = requests.get(api_root + suffix) data = response.json() alerts_json = [x for x in data if int(x['id']) == alert.id] assert len(alerts_json) == 0 # Ensure this alert *is* included if its zone is specified. suffix = 'alerts/?sensor_ids={}&format=json'.format(alert.sensor.id) response = requests.get(api_root + suffix) data = response.json() alert_json = [x for x in data if int(x['id']) == alert.id][0] self._compare_sensor_alert_json(alert, alert_json) def test_alerts_list_get_with_order_by(self, api_root): response = requests.get(api_root + 'alerts/?order_by=-id&format=json') data = response.json() assert len(data) > 1 assert int(data[0]['id']) == self.alerts[1].id assert int(data[1]['id']) == self.alerts[0].id def test_alerts_list_post(self, api_root): response = requests.post(api_root + 'alerts/?format=json') assert response.status_code == 405 def test_alerts_list_patch(self, api_root): response = requests.patch(api_root + 'alerts/?format=json') assert response.status_code == 405 def test_alerts_list_delete(self, api_root): response = requests.delete(api_root + 'alerts/?format=json') assert response.status_code == 405 def test_alerts_detail_get(self, api_root): alert = self.alerts[0] suffix = 'alerts/{}/?format=json'.format(alert.id) response = requests.get(api_root + suffix) data = response.json() self._compare_sensor_alert_json(alert, data) def test_alerts_detail_post(self, api_root): alert = self.alerts[0] suffix = 'alerts/{0}/?format=json'.format(alert.id) response = requests.post(api_root + suffix) assert response.status_code == 405 def test_alerts_detail_patch(self, api_root): alert = self.alerts[0] suffix = 'alerts/{0}/?format=json'.format(alert.id) response = requests.patch(api_root + suffix) assert response.status_code == 405 def test_alerts_detail_delete(self, api_root): alert = self.alerts[0] suffix = 'alerts/{0}/?format=json'.format(alert.id) response = requests.delete(api_root + suffix) assert response.status_code == 405
def create_data(self, request): def finalizer(): if self.alerts is not None: for alert in self.alerts: alert.delete() self.alerts = None if self.data is not None: for data in self.data: data.delete() self.data = None if self.sensor is not None: self.sensor.delete() self.sensor = None if self.zone is not None: self.zone.delete() self.zone = None if self.alert_group is not None: self.alert_group.delete() self.alert_group = None if self.user is not None: self.user.delete() username = str(uuid4())[:30] self.user = User( username=username, email='{}@example.net'.format(username) ) self.user.save() self.alert_group = SensorAlertGroup(name='Test Group') self.alert_group.save() self.alert_group.users.add(self.user) self.zone = Zone( name='Test Zone', notes='Zone notes.', key=uuid4() ) self.zone.save() self.sensor = Sensor( guid=uuid4(), name='Test Sensor', notes='Sensor notes.', zone=self.zone, min_value=60, max_value=85, min_value_operator='>=', max_value_operator='<=', state=True, state_last_change_date=datetime.utcnow() ) self.sensor.save() self.sensor.alert_groups.add(self.alert_group) self.data = [] # Inbox is empty assert 0 == len(mail.outbox) # Create in-range data point; no notification. datum = SensorData( sensor=self.sensor, datetime=datetime.utcnow(), value=self.sensor.min_value + 1, state=True, state_changed=False ) datum.save() self.data.append(datum) # Inbox still empty assert 0 == len(mail.outbox) # Create out-of-range notification; sends "alert." datum = SensorData( sensor=self.sensor, datetime=datetime.utcnow(), value=self.sensor.min_value - 1, state=True, state_changed=False ) datum.save() self.data.append(datum) # Inbox received alert assert 1 == len(mail.outbox) # Create another in-range data point; sends "recovered." datum = SensorData( sensor=self.sensor, datetime=datetime.utcnow(), value=self.sensor.min_value + 1, state=True, state_changed=False ) datum.save() self.data.append(datum) # Inbox received recovery email assert 2 == len(mail.outbox) self.alerts = SensorAlert.objects.all().order_by('id') request.addfinalizer(finalizer)
class TestSensorsApi(object): zone = None sensor = None data = None @pytest.fixture(autouse=True) def api_root(self, request, live_server): api_root = '{}{}api/v1/'.format( live_server.url, '/' if not live_server.url.endswith('/') else '') return api_root @pytest.fixture(autouse=True) def create_data(self, request): def finalizer(): if self.data is not None: for data in self.data: data.delete() self.data = None if self.sensor is not None: self.sensor.delete() self.sensor = None if self.zone is not None: self.zone.delete() self.zone = None self.zone = Zone( name='Test Zone', notes='Zone notes.', key=uuid4() ) self.zone.save() self.sensor = Sensor( guid=uuid4(), name='Test Sensor', notes='Sensor notes.', zone=self.zone, min_value=20, max_value=25, min_value_operator='>=', max_value_operator='<=', state=True, state_last_change_date=datetime.utcnow() ) self.sensor.save() self.data = [] for i in range(3): datum = SensorData( sensor=self.sensor, datetime=datetime.utcnow(), value=self.sensor.min_value + i, state=True, state_changed=False ) datum.save() self.data.append(datum) request.addfinalizer(finalizer) def _compare_zone_json(self, zone, zone_json): assert zone.id == zone_json['id'] assert zone.name == zone_json['name'] assert zone.notes == zone_json['notes'] def _compare_sensor_json(self, sensor, sensor_json): assert sensor.id == sensor_json['id'] assert str(sensor.guid) == sensor_json['guid'] assert sensor.name == sensor_json['name'] assert sensor.notes == sensor_json['notes'] assert sensor.latest_value == sensor_json['latest_value'] assert sensor.min_value == Decimal(sensor_json['min_value']) assert sensor.max_value == Decimal(sensor_json['max_value']) assert sensor.min_value_operator == sensor_json['min_value_operator'] assert sensor.max_value_operator == sensor_json['max_value_operator'] assert sensor.state == sensor_json['state'] assert sensor.state_last_change_date.strftime('%Y-%m-%dT%H:%M:%S.%f') == \ sensor_json['state_last_change_date'][:26] m = re.match(r'.*zones/([0-9]+)/?', sensor_json['zone']) assert m is not None assert sensor.zone.id == int(m.group(1)) def _compare_sensor_data_json(self, sensor_data, sensor_data_json): assert sensor_data.datetime.strftime('%Y-%m-%dT%H:%M:%S.%f') == \ sensor_data_json['datetime'][:26] assert sensor_data.value == Decimal(sensor_data_json['value']) assert sensor_data.state == sensor_data_json['state'] assert sensor_data.state_changed == sensor_data_json['state_changed'] m = re.match(r'.*sensors/([0-9]+)/?', sensor_data_json['sensor']) assert m is not None assert sensor_data.sensor.id == int(m.group(1)) def test_api_root_get(self, api_root): response = requests.get(api_root) data = response.json() assert 'data' in data assert 'sensors' in data assert 'zones' in data def test_api_root_post(self, api_root): response = requests.post(api_root) assert response.status_code == 405 def test_api_root_put(self, api_root): response = requests.put(api_root) assert response.status_code == 405 def test_api_root_patch(self, api_root): response = requests.patch(api_root) assert response.status_code == 405 def test_api_root_delete(self, api_root): response = requests.delete(api_root) assert response.status_code == 405 def test_sensors_list_get_no_filters(self, api_root): response = requests.get(api_root + 'sensors/?format=json') data = response.json() assert len(data) > 0 sensor_json = [x for x in data if x['guid'] == str(self.sensor.guid)][0] self._compare_sensor_json(self.sensor, sensor_json) def test_sensors_list_get_with_limit_and_offset(self, api_root): response = requests.get(api_root + 'sensors/?limit=10&offset=0&format=json') data = response.json() assert 'count' in data assert 'next' in data assert 'previous' in data assert 'results' in data # It's more work than it's worth to check that we can find self.sensor # in this test--there's no telling how many pages would have to be # checked, and the json is already being checked elsewhere anyway. -ajm def test_sensors_list_get_with_sensor_filter(self, api_root): excluded_sensor = Sensor( guid=uuid4(), name='Test Sensor Excluded', notes='Sensor notes.', zone=self.zone, min_value=20, max_value=25, min_value_operator='>=', max_value_operator='<=', state=True, state_last_change_date=datetime.utcnow() ) excluded_sensor.save() suffix = 'sensors/?sensor_ids={}&format=json'.format(self.sensor.id) response = requests.get(api_root + suffix) data = response.json() assert len(data) > 0 excluded_sensor_json = [x for x in data if x['guid'] == str(excluded_sensor.guid)] assert len(excluded_sensor_json) == 0 excluded_sensor.delete() sensor_json = [x for x in data if x['guid'] == str(self.sensor.guid)][0] self._compare_sensor_json(self.sensor, sensor_json) def test_sensors_list_get_with_zone_filter(self, api_root): excluded_zone = Zone( name='Test Zone Excluded', notes='Zone notes.', key=uuid4() ) excluded_zone.save() excluded_sensor = Sensor( guid=uuid4(), name='Test Sensor Excluded', notes='Sensor notes.', zone=excluded_zone, min_value=20, max_value=25, min_value_operator='>=', max_value_operator='<=', state=True, state_last_change_date=datetime.utcnow() ) excluded_sensor.save() suffix = 'sensors/?zone_ids={}&format=json'.format(self.sensor.zone.id) response = requests.get(api_root + suffix) data = response.json() assert len(data) > 0 excluded_sensor_json = [x for x in data if x['guid'] == str(excluded_sensor.guid)] assert len(excluded_sensor_json) == 0 excluded_sensor.delete() excluded_zone.delete() sensor_json = [x for x in data if x['guid'] == str(self.sensor.guid)][0] self._compare_sensor_json(self.sensor, sensor_json) def test_sensors_list_get_with_order_by(self, api_root): second_sensor = Sensor( guid=uuid4(), name=self.sensor.name + '_', # Just something to make it sort lower notes='Sensor notes.', zone=self.zone, min_value=20, max_value=25, min_value_operator='>=', max_value_operator='<=', state=True, state_last_change_date=datetime.utcnow() ) second_sensor.save() # Sort by name, ascending response = requests.get(api_root + 'sensors/?order_by=name&format=json') data = response.json() assert len(data) > 1 first_guid = str(self.sensor.guid) second_guid = str(second_sensor.guid) matches = [x for x in data if ( x['guid'] == first_guid or x['guid'] == second_guid)] assert matches[0]['name'] == self.sensor.name assert matches[1]['name'] == second_sensor.name # Sort by name, descending response = requests.get(api_root + 'sensors/?order_by=-name&format=json') data = response.json() assert len(data) > 1 matches = [x for x in data if ( x['guid'] == first_guid or x['guid'] == second_guid)] assert matches[0]['name'] == second_sensor.name assert matches[1]['name'] == self.sensor.name # Default sorting (id) response = requests.get(api_root + 'sensors/?format=json') data = response.json() assert len(data) > 1 matches = [x for x in data if ( x['guid'] == first_guid or x['guid'] == second_guid)] assert matches[0]['name'] == self.sensor.name assert matches[1]['name'] == second_sensor.name def test_sensors_list_post(self, api_root): sensor_dict = { 'guid': str(uuid4()), 'name': 'POST Sensor', 'notes': 'POST notes.', 'min_value': 20, 'max_value': 25, 'min_value_operator': '>=', 'max_value_operator': '<=', 'key': str(self.zone.key) } headers = {'Content-type': 'application/json'} response = requests.post( api_root + 'sensors/', data=json.dumps(sensor_dict), headers=headers ) data = response.json() assert 'id' in data id = int(data['id']) assert id > 0 sensor = Sensor.objects.get(id=id) self._compare_sensor_json(sensor, data) sensor.delete() def test_sensors_list_post_with_invalid_data(self, api_root): sensor_dict = { 'guid': None, 'name': None, 'notes': 'POST notes.', 'min_value': 20, 'max_value': 25, 'min_value_operator': '>=', 'max_value_operator': '<=', 'key': str(self.zone.key) } headers = {'Content-type': 'application/json'} response = requests.post( api_root + 'sensors/', data=json.dumps(sensor_dict), headers=headers ) # Attempt to remove the object if it's erroneously created. try: data = response.json() if 'id' in data: Sensor.objects.get(id=int(data['id'])).delete() except: pass assert response.status_code == 400 def test_sensors_list_post_without_zone_key(self, api_root): sensor_dict = { 'guid': str(uuid4()), 'name': 'POST Sensor', 'notes': 'POST notes.', 'min_value': 20, 'max_value': 25, 'min_value_operator': '>=', 'max_value_operator': '<=' } headers = {'Content-type': 'application/json'} response = requests.post( api_root + 'sensors/', data=json.dumps(sensor_dict), headers=headers ) # Attempt to remove the object if it's erroneously created. try: data = response.json() if 'id' in data: Sensor.objects.get(id=int(data['id'])).delete() except: pass assert response.status_code == 403 def test_sensors_list_put(self, api_root): response = requests.put(api_root + 'sensors/') assert response.status_code == 405 def test_sensors_list_patch(self, api_root): response = requests.patch(api_root + 'sensors/') assert response.status_code == 405 def test_sensors_list_delete(self, api_root): response = requests.delete(api_root + 'sensors/') assert response.status_code == 405 def test_sensors_detail_get(self, api_root): suffix = 'sensors/{}/?format=json'.format(self.sensor.id) response = requests.get(api_root + suffix) data = response.json() self._compare_sensor_json(self.sensor, data) def test_sensors_detail_post(self, api_root): suffix = 'sensors/{}/?format=json'.format(self.sensor.id) response = requests.post(api_root + suffix) assert response.status_code == 405 def test_sensors_detail_put(self, api_root): suffix = 'sensors/{}/'.format(self.sensor.id) sensor_dict = { 'guid': str(uuid4()), 'name': 'PUT Sensor', 'notes': 'PUT notes.', 'zone': api_root + 'zones/{}/'.format(self.zone.id), 'min_value': 200, 'max_value': 250, 'min_value_operator': '<=', 'max_value_operator': '>=', 'key': str(self.zone.key) } headers = {'Content-type': 'application/json'} response = requests.put( api_root + suffix, data=json.dumps(sensor_dict), headers=headers ) assert response.status_code == 200 data = response.json() assert data['guid'] == sensor_dict['guid'] assert data['name'] == sensor_dict['name'] assert data['notes'] == sensor_dict['notes'] assert data['zone'] == sensor_dict['zone'] assert Decimal(data['min_value']) == Decimal(sensor_dict['min_value']) assert Decimal(data['max_value']) == Decimal(sensor_dict['max_value']) assert data['min_value_operator'] == sensor_dict['min_value_operator'] assert data['max_value_operator'] == sensor_dict['max_value_operator'] def test_sensors_detail_put_without_zone_key(self, api_root): suffix = 'sensors/{}/'.format(self.sensor.id) sensor_dict = { 'guid': str(uuid4()), 'name': 'PUT Sensor', 'notes': 'PUT notes.', 'zone': api_root + 'zones/{}/'.format(self.zone.id), 'min_value': 200, 'max_value': 250, 'min_value_operator': '<=', 'max_value_operator': '>=' } headers = {'Content-type': 'application/json'} response = requests.put( api_root + suffix, data=json.dumps(sensor_dict), headers=headers ) assert response.status_code == 403 def test_sensors_detail_put_with_invalid_data(self, api_root): suffix = 'sensors/{}/'.format(self.sensor.id) sensor_dict = { 'guid': None, 'name': None, 'notes': 'PUT notes.', 'zone': api_root + 'zones/{}/'.format(self.zone.id), 'min_value': 200, 'max_value': 250, 'min_value_operator': '<=', 'max_value_operator': '>=', 'key': str(self.zone.key) } headers = {'Content-type': 'application/json'} response = requests.put( api_root + suffix, data=json.dumps(sensor_dict), headers=headers ) assert response.status_code == 400 def test_sensors_detail_patch(self, api_root): suffix = 'sensors/{}/'.format(self.sensor.id) sensor_dict = { 'guid': str(self.sensor.guid), 'name': 'PATCH Sensor', 'notes': 'PATCH notes.', 'min_value': self.sensor.min_value, 'max_value': self.sensor.max_value, 'key': str(self.zone.key) } headers = {'Content-type': 'application/json'} response = requests.patch( api_root + suffix, data=json.dumps(sensor_dict), headers=headers ) print(response.text) assert response.status_code == 200 data = response.json() assert data['guid'] == sensor_dict['guid'] assert data['name'] == sensor_dict['name'] assert data['notes'] == sensor_dict['notes'] assert Decimal(data['min_value']) == Decimal(sensor_dict['min_value']) assert Decimal(data['max_value']) == Decimal(sensor_dict['max_value']) def test_sensors_detail_patch_without_zone_key(self, api_root): suffix = 'sensors/{}/'.format(self.sensor.id) sensor_dict = { 'guid': str(self.sensor.guid), 'name': 'PATCH Sensor', 'notes': 'PATCH notes.', 'min_value': self.sensor.min_value, 'max_value': self.sensor.max_value } headers = {'Content-type': 'application/json'} response = requests.patch( api_root + suffix, data=json.dumps(sensor_dict), headers=headers ) assert response.status_code == 403 def test_sensors_detail_patch_with_invalid_data(self, api_root): suffix = 'sensors/{}/'.format(self.sensor.id) sensor_dict = { 'guid': None, 'name': None, 'notes': 'PATCH notes.', 'min_value': None, 'max_value': None, 'key': str(self.zone.key) } headers = {'Content-type': 'application/json'} response = requests.patch( api_root + suffix, data=json.dumps(sensor_dict), headers=headers ) print(response.text) assert response.status_code == 400 def test_sensors_detail_delete(self, api_root): data = {'key': str(self.zone.key)} headers = {'Content-type': 'application/json'} suffix = 'sensors/{}/'.format(self.sensor.id) response = requests.delete( api_root + suffix, data=json.dumps(data), headers=headers ) assert response.status_code == 204 def test_sensors_detail_delete_without_zone_key(self, api_root): suffix = 'sensors/{}/'.format(self.sensor.id) response = requests.delete(api_root + suffix) assert response.status_code == 403 def test_sensor_data_list_get_no_filters(self, api_root): response = requests.get(api_root + 'data/?format=json') data = response.json() assert len(data) > 2 for i in range(3): data_json = [x for x in data if int(x['id']) == self.data[i].id][0] self._compare_sensor_data_json(self.data[i], data_json) def test_sensor_data_list_get_with_limit_and_offset(self, api_root): response = requests.get(api_root + 'data/?limit=10&offset=0&format=json') data = response.json() assert 'count' in data assert 'next' in data assert 'previous' in data assert 'results' in data def test_sensor_data_list_get_with_sensor_filter(self, api_root): excluded_sensor = Sensor( guid=uuid4(), name='Test Sensor Excluded', notes='Sensor notes.', zone=self.zone, min_value=20, max_value=25, min_value_operator='>=', max_value_operator='<=', state=True, state_last_change_date=datetime.utcnow() ) excluded_sensor.save() excluded_data = SensorData( sensor=excluded_sensor, datetime=datetime.utcnow(), value=22, state=True, state_changed=False ) excluded_data.save() suffix = 'data/?sensor_ids={}&format=json'.format(self.sensor.id) response = requests.get(api_root + suffix) data = response.json() assert len(data) > 0 excluded_data_json = [x for x in data if x['id'] == str(excluded_data.id)] assert len(excluded_data_json) == 0 excluded_data.delete() excluded_sensor.delete() for i in range(3): data_json = [x for x in data if int(x['id']) == self.data[i].id][0] self._compare_sensor_data_json(self.data[i], data_json) def test_sensor_data_list_get_with_zone_filter(self, api_root): excluded_zone = Zone( name='Test Zone Excluded', notes='Zone notes.', key=uuid4() ) excluded_zone.save() excluded_sensor = Sensor( guid=uuid4(), name='Test Sensor Excluded', notes='Sensor notes.', zone=excluded_zone, min_value=20, max_value=25, min_value_operator='>=', max_value_operator='<=', state=True, state_last_change_date=datetime.utcnow() ) excluded_sensor.save() excluded_data = SensorData( sensor=excluded_sensor, datetime=datetime.utcnow(), value=22, state=True, state_changed=False ) excluded_data.save() suffix = 'data/?zone_ids={}&format=json'.format(self.zone.id) response = requests.get(api_root + suffix) data = response.json() assert len(data) > 0 excluded_data_json = [x for x in data if x['id'] == str(excluded_data.id)] assert len(excluded_data_json) == 0 excluded_data.delete() excluded_sensor.delete() excluded_zone.delete() for i in range(3): data_json = [x for x in data if int(x['id']) == self.data[i].id][0] self._compare_sensor_data_json(self.data[i], data_json) def test_sensor_data_list_get_with_date_filters(self, api_root): suffix = 'data/?start_date={}&end_date={}&format=json'.format( self.data[0].datetime.strftime('%Y-%m-%dT%H:%M:%S.%fZ'), self.data[0].datetime.strftime('%Y-%m-%dT%H:%M:%S.%fZ')) response = requests.get(api_root + suffix) data = response.json() assert len(data) > 0 excluded_data_json = [x for x in data if x['id'] == str(self.data[1].id)] assert len(excluded_data_json) == 0 data_json = [x for x in data if int(x['id']) == self.data[0].id][0] self._compare_sensor_data_json(self.data[0], data_json) def test_sensor_data_list_get_with_value_filters(self, api_root): suffix = 'data/?min_value={}&max_value={}&format=json'.format( self.data[0].value, self.data[0].value) response = requests.get(api_root + suffix) data = response.json() assert len(data) > 0 excluded_data_json = [x for x in data if x['id'] == str(self.data[1].id)] assert len(excluded_data_json) == 0 data_json = [x for x in data if int(x['id']) == self.data[0].id][0] self._compare_sensor_data_json(self.data[0], data_json) def test_sensor_data_list_get_csv(self, api_root): suffix = 'data/?zone_ids={}&format=csv'.format( self.zone.id ) response = requests.get(api_root + suffix) reader = csv.reader(StringIO(response.text)) num_lines = 0 for line in reader: num_lines = num_lines + 1 # All lines should contain 4 fields # datetime,sensor_name,state,value assert len(line) == 4 # Should expect 3 lines of data and 1 header line == 4 assert num_lines == 4 def test_sensor_data_list_post(self, api_root): data_dict = { 'sensor': api_root + 'sensors/{}/'.format(self.sensor.id), 'datetime': datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%fZ'), 'value': 22.5, 'key': str(self.zone.key) } headers = {'Content-type': 'application/json'} response = requests.post( api_root + 'data/', data=json.dumps(data_dict), headers=headers ) data = response.json() assert 'id' in data id = int(data['id']) assert id > 0 sensor_data = SensorData.objects.get(id=id) self._compare_sensor_data_json(sensor_data, data) sensor_data.delete() def test_sensor_data_list_post_without_zone_key(self, api_root): data_dict = { 'sensor': api_root + 'sensors/{}/'.format(self.sensor.id), 'datetime': datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%fZ'), 'value': 22.5 } headers = {'Content-type': 'application/json'} response = requests.post( api_root + 'data/', data=json.dumps(data_dict), headers=headers ) assert response.status_code == 403 def test_sensor_data_list_post_in_range_maintains_state(self, api_root): prev_state_last_change_date = self.sensor.state_last_change_date data_dict = { 'sensor': api_root + 'sensors/{}/'.format(self.sensor.id), 'datetime': datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%fZ'), 'value': 22.5, 'key': str(self.zone.key) } headers = {'Content-type': 'application/json'} response = requests.post( api_root + 'data/', data=json.dumps(data_dict), headers=headers ) data = response.json() assert 'id' in data id = int(data['id']) assert id > 0 sensor_data = SensorData.objects.get(id=id) self._compare_sensor_data_json(sensor_data, data) sensor = Sensor.objects.get(pk=self.sensor.id) assert sensor.state is True assert sensor.state_last_change_date.strftime('%Y-%m-%dT%H:%M:%S.%fZ') == \ prev_state_last_change_date.strftime('%Y-%m-%dT%H:%M:%S.%fZ') sensor_data.delete() def test_sensor_data_list_post_outside_range_updates_state(self, api_root): prev_state_last_change_date = self.sensor.state_last_change_date data_dict = { 'sensor': api_root + 'sensors/{}/'.format(self.sensor.id), 'datetime': datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%fZ'), 'value': self.sensor.max_value + 1, 'key': str(self.zone.key) } headers = {'Content-type': 'application/json'} response = requests.post( api_root + 'data/', data=json.dumps(data_dict), headers=headers ) data = response.json() assert 'id' in data id = int(data['id']) assert id > 0 sensor_data = SensorData.objects.get(id=id) self._compare_sensor_data_json(sensor_data, data) sensor = Sensor.objects.get(pk=self.sensor.id) assert not sensor.state assert sensor.state_last_change_date.strftime('%Y-%m-%dT%H:%M:%S.%fZ') != \ prev_state_last_change_date.strftime('%Y-%m-%dT%H:%M:%S.%fZ') sensor_data.delete() def test_sensor_data_list_put(self, api_root): response = requests.put(api_root + 'data/') assert response.status_code == 405 def test_sensor_data_list_patch(self, api_root): response = requests.patch(api_root + 'data/') assert response.status_code == 405 def test_sensor_data_list_delete(self, api_root): response = requests.delete(api_root + 'data/') assert response.status_code == 405 def test_sensor_data_detail_get(self, api_root): suffix = 'data/{}/?format=json'.format(self.data[0].id) response = requests.get(api_root + suffix) data = response.json() self._compare_sensor_data_json(self.data[0], data) def test_sensor_data_detail_post(self, api_root): response = requests.post(api_root + 'data/{}/'.format(self.data[0].id)) assert response.status_code == 405 def test_sensor_data_detail_post_guid(self, api_root): """ Test adding sensor data with only supplying a Sensor guid, sensor data value and a zone key. If the sensor doesn't exist, this will create it. """ zone = Zone(name='Test Zone', key=uuid4()) zone.save() guid = 'DEADBEEF00000012345TEST' value_first = 15.0 value_second = 25.0 num_sensors_before_add = Sensor.objects.all().count() data_dict = { 'guid': guid, 'value': value_first, 'key': str(zone.key), } headers = {'Content-type': 'application/json'} response = requests.post( api_root + 'data/', data=json.dumps(data_dict), headers=headers ) response_json_first = response.json() num_sensors_after_add = Sensor.objects.all().count() assert response.status_code == 201 assert num_sensors_before_add + 1 == num_sensors_after_add data = SensorData.objects.get(pk=response_json_first["id"]) data.delete() data_dict = { 'guid': guid, 'value': value_second, 'key': str(zone.key), } headers = {'Content-type': 'application/json'} response = requests.post( api_root + 'data/', data=json.dumps(data_dict), headers=headers ) response_json_second = response.json() num_sensors_after_second_request = Sensor.objects.all().count() # Ensure we didn't add another sensor assert response.status_code == 201 assert num_sensors_after_add == num_sensors_after_second_request assert response_json_first["sensor"] == response_json_second["sensor"] data = SensorData.objects.get(pk=response_json_second["id"]) sensor = Sensor.objects.get(pk=data.sensor.id) data.delete() sensor.delete() def test_sensor_data_detail_put(self, api_root): response = requests.put(api_root + 'data/{}/'.format(self.data[0].id)) assert response.status_code == 405 def test_sensor_data_detail_patch(self, api_root): response = requests.patch(api_root + 'data/{}/'.format(self.data[0].id)) assert response.status_code == 405 def test_sensor_data_detail_delete(self, api_root): response = requests.delete(api_root + 'data/{}/'.format(self.data[0].id)) assert response.status_code == 405 def test_zone_list_get_no_filters(self, api_root): response = requests.get(api_root + 'zones/?format=json') data = response.json() assert len(data) > 0 zone_json = [x for x in data if int(x['id']) == self.zone.id][0] self._compare_zone_json(self.zone, zone_json) def test_zone_list_get_with_limit_and_offset(self, api_root): response = requests.get(api_root + 'zones/?limit=10&offset=0&format=json') data = response.json() assert 'count' in data assert 'next' in data assert 'previous' in data assert 'results' in data def test_zone_list_get_with_sensor_filter(self, api_root): excluded_zone = Zone( name='Test Zone Excluded', notes='Zone notes.', key=uuid4() ) excluded_zone.save() excluded_sensor = Sensor( guid=uuid4(), name='Test Sensor Excluded', notes='Sensor notes.', zone=excluded_zone, min_value=20, max_value=25, min_value_operator='>=', max_value_operator='<=', state=True, state_last_change_date=datetime.utcnow() ) excluded_sensor.save() suffix = 'zones/?sensor_ids={}&format=json'.format(self.sensor.id) response = requests.get(api_root + suffix) data = response.json() assert len(data) > 0 excluded_zone_json = [x for x in data if x['id'] == str(excluded_zone.id)] assert len(excluded_zone_json) == 0 excluded_sensor.delete() excluded_zone.delete() zone_json = [x for x in data if int(x['id']) == self.zone.id][0] self._compare_zone_json(self.zone, zone_json) def test_zone_list_get_with_zone_filter(self, api_root): excluded_zone = Zone( name='Test Zone Excluded', notes='Zone notes.', key=uuid4() ) excluded_zone.save() suffix = 'zones/?zone_ids={}&format=json'.format(self.zone.id) response = requests.get(api_root + suffix) data = response.json() assert len(data) > 0 excluded_zone_json = [x for x in data if x['id'] == str(excluded_zone.id)] assert len(excluded_zone_json) == 0 excluded_zone.delete() zone_json = [x for x in data if int(x['id']) == self.zone.id][0] self._compare_zone_json(self.zone, zone_json) def test_zone_list_post(self, api_root): response = requests.put(api_root + 'zones/') assert response.status_code == 405 def test_zone_list_put(self, api_root): response = requests.put(api_root + 'zones/') assert response.status_code == 405 def test_zone_list_patch(self, api_root): response = requests.patch(api_root + 'zones/') assert response.status_code == 405 def test_zone_list_delete(self, api_root): response = requests.delete(api_root + 'zones/') assert response.status_code == 405 def test_zone_detail_get(self, api_root): response = requests.get(api_root + 'zones/{}/'.format(self.zone.id)) data = response.json() self._compare_zone_json(self.zone, data) def test_zone_detail_post(self, api_root): response = requests.post(api_root + 'zones/{}/'.format(self.zone.id)) assert response.status_code == 405 def test_zone_detail_put(self, api_root): zone_dict = { 'name': 'PUT Zone', 'notes': 'PUT notes.', 'key': str(self.zone.key) } headers = {'Content-type': 'application/json'} response = requests.put( api_root + 'zones/{}/'.format(self.zone.id), data=json.dumps(zone_dict), headers=headers ) assert response.status_code == 200 data = response.json() assert data['name'] == zone_dict['name'] assert data['notes'] == zone_dict['notes'] def test_zone_detail_put_without_zone_key(self, api_root): zone_dict = { 'name': 'PUT Zone', 'notes': 'PUT notes.' } headers = {'Content-type': 'application/json'} response = requests.put( api_root + 'zones/{}/'.format(self.zone.id), data=json.dumps(zone_dict), headers=headers ) assert response.status_code == 403 def test_zone_detail_put_with_invalid_data(self, api_root): zone_dict = { 'name': None, 'notes': 'PUT notes.', 'key': str(self.zone.key) } headers = {'Content-type': 'application/json'} response = requests.put( api_root + 'zones/{}/'.format(self.zone.id), data=json.dumps(zone_dict), headers=headers ) assert response.status_code == 400 def test_zone_detail_patch(self, api_root): zone_dict = { 'name': 'PATCH Zone', 'notes': 'PATCH notes.', 'key': str(self.zone.key) } headers = {'Content-type': 'application/json'} response = requests.patch( api_root + 'zones/{}/'.format(self.zone.id), data=json.dumps(zone_dict), headers=headers ) assert response.status_code == 200 data = response.json() assert data['name'] == zone_dict['name'] assert data['notes'] == zone_dict['notes'] def test_zone_detail_patch_without_zone_key(self, api_root): zone_dict = { 'name': 'PATCH Zone', 'notes': 'PATCH notes.' } headers = {'Content-type': 'application/json'} response = requests.patch( api_root + 'zones/{}/'.format(self.zone.id), data=json.dumps(zone_dict), headers=headers ) assert response.status_code == 403 def test_zone_detail_patch_with_invalid_data(self, api_root): zone_dict = { 'name': None, 'notes': 'PATCH notes.', 'key': str(self.zone.key) } headers = {'Content-type': 'application/json'} response = requests.patch( api_root + 'zones/{}/'.format(self.zone.id), data=json.dumps(zone_dict), headers=headers ) assert response.status_code == 400 def test_zone_detail_delete(self, api_root): response = requests.delete(api_root + 'zones/{}/'.format(self.zone.id)) assert response.status_code == 405
class TestSensorsApi(object): user = None alert_group = None zone = None sensor = None data = None alerts = None @pytest.fixture(autouse=True) def api_root(self, request, live_server): api_root = '{}{}api/v1/'.format( live_server.url, '/' if not live_server.url.endswith('/') else '') return api_root @pytest.fixture(autouse=True) def create_data(self, request): def finalizer(): if self.alerts is not None: for alert in self.alerts: alert.delete() self.alerts = None if self.data is not None: for data in self.data: data.delete() self.data = None if self.sensor is not None: self.sensor.delete() self.sensor = None if self.zone is not None: self.zone.delete() self.zone = None if self.alert_group is not None: self.alert_group.delete() self.alert_group = None if self.user is not None: self.user.delete() username = str(uuid4())[:30] self.user = User( username=username, email='{}@example.net'.format(username) ) self.user.save() self.alert_group = SensorAlertGroup(name='Test Group') self.alert_group.save() self.alert_group.users.add(self.user) self.zone = Zone( name='Test Zone', notes='Zone notes.', key=uuid4() ) self.zone.save() self.sensor = Sensor( guid=uuid4(), name='Test Sensor', notes='Sensor notes.', zone=self.zone, min_value=60, max_value=85, min_value_operator='>=', max_value_operator='<=', state=True, state_last_change_date=datetime.utcnow() ) self.sensor.save() self.sensor.alert_groups.add(self.alert_group) self.data = [] # Inbox is empty assert 0 == len(mail.outbox) # Create in-range data point; no notification. datum = SensorData( sensor=self.sensor, datetime=datetime.utcnow(), value=self.sensor.min_value + 1, state=True, state_changed=False ) datum.save() self.data.append(datum) # Inbox still empty assert 0 == len(mail.outbox) # Create out-of-range notification; sends "alert." datum = SensorData( sensor=self.sensor, datetime=datetime.utcnow(), value=self.sensor.min_value - 1, state=True, state_changed=False ) datum.save() self.data.append(datum) # Inbox received alert assert 1 == len(mail.outbox) # Create another in-range data point; sends "recovered." datum = SensorData( sensor=self.sensor, datetime=datetime.utcnow(), value=self.sensor.min_value + 1, state=True, state_changed=False ) datum.save() self.data.append(datum) # Inbox received recovery email assert 2 == len(mail.outbox) self.alerts = SensorAlert.objects.all().order_by('id') request.addfinalizer(finalizer) def _compare_sensor_alert_json(self, alert, alert_json): assert alert.id == alert_json['id'] assert alert.alert_type == alert_json['alert_type'] assert alert.alert_class == alert_json['alert_class'] assert alert.recipients == alert_json['recipients'] assert alert.message == alert_json['message'] assert alert.date.strftime('%Y-%m-%dT%H:%M:%S.%f') == alert_json['date'][:26] m = re.match(r'.*sensors/([0-9]+)/?', alert_json['sensor']) assert m is not None assert alert.sensor.id == int(m.group(1)) m = re.match(r'.*data/([0-9]+)/?', alert_json['data_point']) assert m is not None assert alert.data_point.id == int(m.group(1)) def test_alerts_list_no_filters(self, api_root): response = requests.get(api_root + 'alerts/?format=json') data = response.json() assert len(data) > 1 for alert in self.alerts: alert_json = [x for x in data if int(x['id']) == alert.id][0] self._compare_sensor_alert_json(alert, alert_json) def test_alerts_list_get_with_limit_and_offset(self, api_root): response = requests.get(api_root + 'alerts/?limit=10&offset=0&format=json') data = response.json() assert 'count' in data assert 'next' in data assert 'previous' in data assert 'results' in data def test_alerts_list_get_with_alert_filter(self, api_root): included_alert = self.alerts[0] excluded_alert = self.alerts[1] suffix = 'alerts/?alert_ids={}&format=json'.format(included_alert.id) response = requests.get(api_root + suffix) data = response.json() assert len(data) > 0 excluded_alert_json = [x for x in data if int(x['id']) == excluded_alert.id] assert len(excluded_alert_json) == 0 included_alert_json = [x for x in data if int(x['id']) == included_alert.id][0] self._compare_sensor_alert_json(included_alert, included_alert_json) def test_alerts_list_get_with_zone_filters(self, api_root): alert = self.alerts[0] # Ensure this alert is *not* included if its zone isn't specified. suffix = 'alerts/?zone_ids={}&format=json'.format(alert.sensor.zone.id + 1) response = requests.get(api_root + suffix) data = response.json() alerts_json = [x for x in data if int(x['id']) == alert.id] assert len(alerts_json) == 0 # Ensure this alert *is* included if its zone is specified. suffix = 'alerts/?zone_ids={}&format=json'.format(alert.sensor.zone.id) response = requests.get(api_root + suffix) data = response.json() alert_json = [x for x in data if int(x['id']) == alert.id][0] self._compare_sensor_alert_json(alert, alert_json) def test_alerts_list_get_with_sensor_filters(self, api_root): alert = self.alerts[0] # Ensure this alert is *not* included if its zone isn't specified. suffix = 'alerts/?sensor_ids={}&format=json'.format(alert.sensor.id + 1) response = requests.get(api_root + suffix) data = response.json() alerts_json = [x for x in data if int(x['id']) == alert.id] assert len(alerts_json) == 0 # Ensure this alert *is* included if its zone is specified. suffix = 'alerts/?sensor_ids={}&format=json'.format(alert.sensor.id) response = requests.get(api_root + suffix) data = response.json() alert_json = [x for x in data if int(x['id']) == alert.id][0] self._compare_sensor_alert_json(alert, alert_json) def test_alerts_list_get_with_order_by(self, api_root): response = requests.get(api_root + 'alerts/?order_by=-id&format=json') data = response.json() assert len(data) > 1 assert int(data[0]['id']) == self.alerts[1].id assert int(data[1]['id']) == self.alerts[0].id def test_alerts_list_post(self, api_root): response = requests.post(api_root + 'alerts/?format=json') assert response.status_code == 405 def test_alerts_list_patch(self, api_root): response = requests.patch(api_root + 'alerts/?format=json') assert response.status_code == 405 def test_alerts_list_delete(self, api_root): response = requests.delete(api_root + 'alerts/?format=json') assert response.status_code == 405 def test_alerts_detail_get(self, api_root): alert = self.alerts[0] suffix = 'alerts/{}/?format=json'.format(alert.id) response = requests.get(api_root + suffix) data = response.json() self._compare_sensor_alert_json(alert, data) def test_alerts_detail_post(self, api_root): alert = self.alerts[0] suffix = 'alerts/{0}/?format=json'.format(alert.id) response = requests.post(api_root + suffix) assert response.status_code == 405 def test_alerts_detail_patch(self, api_root): alert = self.alerts[0] suffix = 'alerts/{0}/?format=json'.format(alert.id) response = requests.patch(api_root + suffix) assert response.status_code == 405 def test_alerts_detail_delete(self, api_root): alert = self.alerts[0] suffix = 'alerts/{0}/?format=json'.format(alert.id) response = requests.delete(api_root + suffix) assert response.status_code == 405