def test_create_delete_application(client):
    url = reverse("private:create_app")

    # test auth
    response = client.post(url)
    assert response.status_code == 401

    response = client.post(url, **auth_header("unknown_scope"))
    assert response.status_code == 403

    assert mds.authent.models.Application.objects.count() == 0

    app_owner = str(uuid.uuid4())
    data = {
        "app_name": "Test App",
        "scopes": ["admin", "others"],
        "app_owner": app_owner,
    }

    response = client.post(url,
                           data=data,
                           content_type="application/json",
                           **auth_header(SCOPE_PRV_API))
    assert response.status_code == 200
    assert mds.authent.models.Application.objects.count() == 1

    data = {"app_owner": app_owner, "delete": True}
    response = client.delete(url,
                             data=data,
                             content_type="application/json",
                             **auth_header(SCOPE_PRV_API))
    assert response.status_code == 200
    assert mds.authent.models.Application.objects.count() == 0
def test_polygon_delete(client):
    polygon_id = str(uuid.uuid4())
    Polygon(id=polygon_id, geom=str(MOCK_GEOJSON))

    response = client.delete(
        "{}{}/".format(POLY_BASE_URL, polygon_id), **auth_header(SCOPE_PRV_API)
    )
    assert response.status_code == 204

    response = client.get(
        "{}{}/".format(POLY_BASE_URL, polygon_id), **auth_header(SCOPE_PRV_API)
    )
    assert response.status_code == 404
def test_area_delete(client):
    area_id = str(uuid.uuid4())

    Area(id=area_id)

    response = client.delete(
        "{}{}/".format(AREA_BASE_URL, area_id), **auth_header(SCOPE_PRV_API)
    )
    assert response.status_code == 204

    response = client.get(
        "{}{}/".format(AREA_BASE_URL, area_id), **auth_header(SCOPE_PRV_API)
    )
    assert response.status_code == 404
示例#4
0
def test_device_register(client):
    provider = factories.Provider(
        id=uuid.UUID("aaaa0000-61fd-4cce-8113-81af1de90942"))
    device_id = uuid.UUID("bbbb0000-61fd-4cce-8113-81af1de90942")

    assert models.Device.objects.count() == 0

    data = {
        "device_id": str(device_id),
        "vehicle_id": "foo",
        "type": "scooter",
        "propulsion": ["electric"],
        "year": 2012,
        "mfgr": "Toto inc",
        "model": "IDFX 3000",
    }

    # Test auth
    response = client.post(reverse("agency:device-list"),
                           data=data,
                           content_type="application/json")
    assert response.status_code == 401

    response = client.post(
        reverse("agency:device-list"),
        data=data,
        content_type="application/json",
        **auth_header(SCOPE_AGENCY_API, provider_id=provider.id),
    )
    assert response.status_code == 201
    assert response.data == {}

    device = models.Device.objects.get()  # Also tests unicity
    assert device.provider == provider
示例#5
0
def test_areas_metadata(client):
    provider = factories.Provider(name="Test provider")
    response = client.options(
        reverse("agency-0.3:area-list"),
        **auth_header(SCOPE_AGENCY_API, provider_id=provider.id),
    )
    assert response.status_code == 200
    assert response._headers["allow"][1] == "GET, HEAD, OPTIONS"
def test_polygon_creation(client):
    response = client.post(
        POLY_BASE_URL,
        data={"label": "test", "geom": MOCK_GEOJSON, "areas": []},
        content_type="application/json",
        **auth_header(SCOPE_PRV_API),
    )
    assert response.status_code == 201
    assert models.Polygon.objects.count() == 1
def test_area_creation(client):

    response = client.post(
        AREA_BASE_URL,
        data={"color": "#FF00FF", "label": "test_area", "polygons": []},
        content_type="application/json",
        **auth_header(SCOPE_PRV_API),
    )
    assert response.status_code == 201

    assert models.Area.objects.count() == 1
示例#8
0
def test_policy_list_basic(client, django_assert_num_queries):
    provider = factories.Provider(name="Test provider")
    other_provider = factories.Provider(name="Other provider")

    # One provider-specific policy
    provider_policy = factories.Policy(providers=[provider], published=True)
    # And one general-purpose policy
    general_policy = factories.Policy(published=True)

    # Test without auth
    n = BASE_NUM_QUERIES
    n += 1  # query on policies
    n += 1  # query on related providers
    n += 1  # query on related previous policies
    with django_assert_num_queries(n - 1):  # No token check
        response = client.get(reverse("agency-0.3:policy-list"))
    assert response.status_code == 200
    assert [p["policy_id"] for p in response.data] == [str(general_policy.id)]

    # Test with provider auth
    with django_assert_num_queries(n):
        response = client.get(
            reverse("agency-0.3:policy-list"),
            **auth_header(SCOPE_AGENCY_API, provider_id=provider.id),
        )
    assert response.status_code == 200
    assert [p["policy_id"] for p in response.data] == [
        str(provider_policy.id),
        str(general_policy.id),
    ]
    # We made this mistake before, don't serve a UUID object
    assert response.data[0]["provider_ids"] == [str(provider.id)]

    # Test with other provider auth
    with django_assert_num_queries(n):
        response = client.get(
            reverse("agency-0.3:policy-list"),
            **auth_header(SCOPE_AGENCY_API, provider_id=other_provider.id),
        )
    assert response.status_code == 200
    assert [p["policy_id"] for p in response.data] == [str(general_policy.id)]
def test_polygon_update(client):
    polygon_id = str(uuid.uuid4())
    poly = Polygon(id=polygon_id, label="test", properties={}, geom=str(MOCK_GEOJSON))

    response = client.put(
        "{}{}/".format(POLY_BASE_URL, polygon_id),
        data={"label": "test_polygon_foo", "properties": {"name": "foo"}},
        content_type="application/json",
        **auth_header(SCOPE_PRV_API),
    )
    assert response.status_code == 400

    response = client.put(
        "{}{}/".format(POLY_BASE_URL, polygon_id),
        data={"label": "test 2", "geom": MOCK_GEOJSON, "areas": []},
        content_type="application/json",
        **auth_header(SCOPE_PRV_API),
    )
    assert response.status_code == 200
    poly.refresh_from_db()
    assert poly.label == "test 2"
def test_area_patch(client):
    area_id = str(uuid.uuid4())
    area = Area(id=area_id, label="test_area")

    response = client.patch(
        "{}{}/".format(AREA_BASE_URL, area_id),
        data={"label": "test_area_foo"},
        content_type="application/json",
        **auth_header(SCOPE_PRV_API),
    )
    assert response.status_code == 200
    area.refresh_from_db()
    assert area.label == "test_area_foo"
def test_polygons_import_without_areas(client):
    new_polygon = {"label": "test import", "geom": MOCK_GEOJSON, "areas": []}

    response = client.post(
        "{}import/".format(POLY_BASE_URL),
        data={"polygons": [new_polygon]},
        content_type="application/json",
        **auth_header(SCOPE_PRV_API),
    )

    assert response.status_code == 200
    assert models.Polygon.objects.filter(label=new_polygon["label"]).count() == 1
    assert models.Area.objects.count() == 0
示例#12
0
def test_areas_detail(client, django_assert_num_queries):
    provider = factories.Provider(name="Test provider")
    area = factories.Area(providers=[provider])
    other_provider = factories.Provider(name="Test other provider")

    response = client.get(reverse("agency-0.3:area-detail", args=[area.pk]))
    assert response.status_code == 401

    response = client.get(
        reverse("agency-0.3:area-detail", args=["foobar"]),
        **auth_header(SCOPE_AGENCY_API, provider_id=provider.id),
    )
    assert response.status_code == 404  # Testing DRF?!

    response = client.get(
        reverse("agency-0.3:area-detail", args=[area.pk]),
        **auth_header(SCOPE_AGENCY_API, provider_id=other_provider.id),
    )
    assert response.status_code == 404

    n = BASE_NUM_QUERIES
    n += 1  # query on areas
    n += 1  # query on polygons
    with django_assert_num_queries(n):
        response = client.get(
            reverse("agency-0.3:area-detail", args=[area.pk]),
            **auth_header(SCOPE_AGENCY_API, provider_id=provider.id),
        )
    assert response.status_code == 200
    assert response.data == {
        "service_area_id": str(area.pk),
        "area": {
            "coordinates": [[[[[0.0, 0.0], [0.0, 50.0], [50.0, 50.0],
                               [50.0, 0.0], [0.0, 0.0]]]]],
            "type":
            "MultiPolygon",
        },
        "type": "unrestricted",
    }
def test_all_areas_get(client):
    area = Area(label="test")

    response = client.get(AREA_BASE_URL)
    assert response.status_code == 401

    response = client.get(AREA_BASE_URL, **auth_header(SCOPE_PRV_API))
    assert response.status_code == 200

    db_areas = models.Area.objects.all()

    assert len(response.data) == len(db_areas)
    assert db_areas[0] == area
示例#14
0
def test_long_lived_token(mocked, client):
    app = _create_application("My application", owner=uuid.uuid4())
    response = client.post(reverse("mds_prv_api:long_lived_token"),
                           **auth_header(SCOPE_PRV_API))
    assert response.status_code == 400
    assert set(response.data.keys()) == {"app_owner", "token_duration"}

    data = {"app_owner": str(app.owner), "token_duration": 3600}
    response = client.post(
        reverse("mds_prv_api:long_lived_token"),
        data=data,
        content_type="application/json",
        **auth_header(SCOPE_PRV_API),
    )
    assert response.status_code == 200
    auth_mean = mocked.return_value
    data = response.data
    jwt_token = jwt.decode(data.pop("access_token"),
                           auth_mean.key,
                           algorithms=auth_mean.algorithm)
    assert data == {"expires_in": 3600, "token_type": "bearer"}
    assert mds.authent.models.AccessToken.objects.get(jti=jwt_token["jti"])
def test_polygon_get(client):
    polygon_id = str(uuid.uuid4())
    Polygon(id=polygon_id, label="test", properties={}, geom=str(MOCK_GEOJSON))

    response = client.get(
        "{}{}/".format(POLY_BASE_URL, polygon_id), **auth_header(SCOPE_PRV_API)
    )
    assert response.status_code == 200
    assert response.data == {
        "id": polygon_id,
        "areas": [],
        "label": "test",
        "geom": MOCK_GEOJSON,
    }
def test_area_update(client):
    area_id = str(uuid.uuid4())
    area = Area(id=area_id, label="test_area")

    response = client.put(
        "{}{}/".format(AREA_BASE_URL, area_id),
        data={"color": "#FF00FF", "label": "test_area_foo", "polygons": []},
        content_type="application/json",
        **auth_header(SCOPE_PRV_API),
    )
    assert response.status_code == 200
    area.refresh_from_db()
    assert area.label == "test_area_foo"
    assert area.color == "#FF00FF"
示例#17
0
def test_areas_list(client, django_assert_num_queries):
    provider = factories.Provider(name="Test provider")
    factories.Area.create_batch(5, providers=[provider])

    response = client.get(reverse("agency-0.3:area-list"))
    assert response.status_code == 401

    n = BASE_NUM_QUERIES
    n += 1  # query on areas
    n += 1  # query on polygons
    with django_assert_num_queries(n):
        response = client.get(
            reverse("agency-0.3:area-list"),
            **auth_header(SCOPE_AGENCY_API, provider_id=provider.id),
        )
    assert response.status_code == 200
    assert len(response.data) == 5
def test_polygon_areas_update(client):
    area_id = str(uuid.uuid4())
    area = Area(id=area_id)
    polygon_id = str(uuid.uuid4())
    poly = Polygon(id=polygon_id, label="test", geom=str(MOCK_GEOJSON))

    assert poly.areas.count() == 0

    response = client.patch(
        "{}{}/".format(POLY_BASE_URL, polygon_id),
        data={"areas": [area_id]},
        content_type="application/json",
        **auth_header(SCOPE_PRV_API),
    )
    assert response.status_code == 200
    poly.refresh_from_db()
    assert poly.areas.count() == 1
    assert str(poly.areas.first().id) == str(area.id)
def test_area_get(client):
    area_id = uuid.uuid4()
    area = Area(id=area_id, label="test_area")

    response = client.get(
        "{}{}/".format(AREA_BASE_URL, str(area_id)), **auth_header(SCOPE_PRV_API)
    )
    assert response.status_code == 200
    data = dict(response.data)
    poly = list(map(dict, data.pop("polygons")))
    assert data == {"id": str(area_id), "label": "test_area", "color": "#FFFFFF"}
    assert poly == [
        {
            "id": str(area.polygons.get().id),
            "areas": [area_id],
            "label": "",
            "geom": {
                "coordinates": [
                    [[0.0, 0.0], [0.0, 50.0], [50.0, 50.0], [50.0, 0.0], [0.0, 0.0]]
                ],
                "type": "Polygon",
            },
        }
    ]
示例#20
0
            "geometry": {"type": "Point", "coordinates": [-118.48153, 33.996_339]},
        },
        "associated_trip": None,
    }
    # test auth
    response = client.get("/prv/provider_api/status_changes")
    assert response.status_code == 401

    start_time = utils.to_mds_timestamp(now - datetime.timedelta(minutes=30))
    n = BASE_NUM_QUERIES
    n += 1  # query on events
    n += 1  # count on events
    with django_assert_num_queries(n):
        response = client.get(
            "/prv/provider_api/status_changes?start_time=%s" % start_time,
            **auth_header(SCOPE_PRV_API, provider_id=provider.id),
        )
    assert response.status_code == 200

    data = response.data["data"]["status_changes"]
    assert len(data) == 2

    assert expected_event_device1 in data
    assert expected_event_device2 in data

    # Test pagination: retrieve only given number of events
    response = client.get(
        "/prv/provider_api/status_changes?start_time=%s&take=1" % start_time,
        **auth_header(SCOPE_PRV_API, provider_id=provider.id),
    )
    data = response.data["data"]["status_changes"]
def test_all_polygons_get(client):
    response = client.get(POLY_BASE_URL)
    assert response.status_code == 401
    response = client.get(POLY_BASE_URL, **auth_header(SCOPE_PRV_API))
    assert response.status_code == 200
def test_provider_basic(client, django_assert_num_queries):
    provider = factories.Provider(
        id=uuid.UUID("aaaaaaa0-1342-413b-8e89-db802b2f83f6"),
        name="Test provider")
    factories.Device(provider=provider, category="car")
    factories.Device(
        provider=factories.Provider(
            id=uuid.UUID("bbbbbbb0-1342-413b-8e89-db802b2f83f6"),
            name="Test provider 2"),
        category="scooter",
    )
    factories.Device(
        provider=factories.Provider(
            id=uuid.UUID("ccccccc0-1342-413b-8e89-db802b2f83f6"),
            name="Test provider 3"),
        category="bicycle",
    )

    response = client.get("/prv/providers/")
    assert response.status_code == 401

    response = client.get(
        "/prv/providers/",
        **auth_header(SCOPE_AGENCY_API, provider_id=provider.id))
    assert response.status_code == 403

    n = BASE_NUM_QUERIES
    n += 1  # query on providers
    with django_assert_num_queries(n):
        response = client.get("/prv/providers/", **auth_header(SCOPE_PRV_API))
    assert response.status_code == 200
    assert len(response.data) == 3
    assert {
        "id": "aaaaaaa0-1342-413b-8e89-db802b2f83f6",
        "name": "Test provider",
        "logo_b64": None,
        "base_api_url": "http://provider",
        "api_configuration": {
            "trailing_slash": False
        },
        "api_authentication": {
            "type": "none"
        },
        "agency_api_authentication": {
            "type": "none"
        },
        "colors": {},
        "device_categories": {
            "bicycle": 0,
            "scooter": 0,
            "car": 1
        },
    } in response.data
    assert {
        "id": "bbbbbbb0-1342-413b-8e89-db802b2f83f6",
        "name": "Test provider 2",
        "logo_b64": None,
        "base_api_url": "http://provider",
        "api_configuration": {
            "trailing_slash": False
        },
        "api_authentication": {
            "type": "none"
        },
        "agency_api_authentication": {
            "type": "none"
        },
        "colors": {},
        "device_categories": {
            "bicycle": 0,
            "scooter": 1,
            "car": 0
        },
    } in response.data
    assert {
        "id": "ccccccc0-1342-413b-8e89-db802b2f83f6",
        "name": "Test provider 3",
        "logo_b64": None,
        "base_api_url": "http://provider",
        "api_configuration": {
            "trailing_slash": False
        },
        "api_authentication": {
            "type": "none"
        },
        "agency_api_authentication": {
            "type": "none"
        },
        "colors": {},
        "device_categories": {
            "bicycle": 1,
            "scooter": 0,
            "car": 0
        },
    } in response.data

    response = client.get(
        "/prv/providers/%s/" % "aaaaaaa0-1342-413b-8e89-db802b2f83f6",
        **auth_header(SCOPE_PRV_API))
    assert response.status_code == 200
    assert response.data == {
        "id": "aaaaaaa0-1342-413b-8e89-db802b2f83f6",
        "name": "Test provider",
        "logo_b64": None,
        "base_api_url": "http://provider",
        "api_configuration": {
            "trailing_slash": False
        },
        "api_authentication": {
            "type": "none"
        },
        "agency_api_authentication": {
            "type": "none"
        },
        "colors": {},
        "device_categories": {
            "bicycle": 0,
            "scooter": 0,
            "car": 1
        },
    }
def test_device_list_filters(client, django_assert_num_queries):
    factories.Device.create_batch(3, category="car", dn_status="available")
    factories.Device.create_batch(3,
                                  category="scooter",
                                  dn_status="unavailable")
    factories.Device.create_batch(3,
                                  category="bicycle",
                                  dn_status="unavailable")

    response = client.get("/prv/vehicles/?category=car",
                          **auth_header(SCOPE_PRV_API))
    assert response.status_code == 200
    assert len(response.data["results"]) == 3

    response = client.get("/prv/vehicles/?category=car,scooter",
                          **auth_header(SCOPE_PRV_API))
    assert response.status_code == 200
    assert len(response.data["results"]) == 6

    response = client.get("/prv/vehicles/?status=available",
                          **auth_header(SCOPE_PRV_API))
    assert response.status_code == 200
    assert len(response.data["results"]) == 3

    response = client.get("/prv/vehicles/?status=available,unavailable",
                          **auth_header(SCOPE_PRV_API))
    assert response.status_code == 200
    assert len(response.data["results"]) == 9

    response = client.get(
        "/prv/vehicles/?status=available,unavailable&category=car",
        **auth_header(SCOPE_PRV_API),
    )
    assert response.status_code == 200
    assert len(response.data["results"]) == 3

    provider = str(
        models.Device.objects.filter(category="car").first().provider_id)
    response = client.get(
        "/prv/vehicles/?provider=%s,%s" % (provider, str(uuid.uuid4())),
        **auth_header(SCOPE_PRV_API),
    )
    assert response.status_code == 200
    assert (len(response.data["results"]) == models.Device.objects.filter(
        provider_id=provider).count())

    response = client.get(
        "/prv/vehicles/?category=car&status=available&provider=%s,%s" %
        (provider, str(uuid.uuid4())),
        **auth_header(SCOPE_PRV_API),
    )
    assert response.status_code == 200
    assert (len(response.data["results"]) == models.Device.objects.filter(
        category="car", dn_status="available", provider_id=provider).count())

    response = client.get(
        "/prv/vehicles/?category=car&status=unavailable&provider=%s,%s" %
        (provider, str(uuid.uuid4())),
        **auth_header(SCOPE_PRV_API),
    )
    assert response.status_code == 200
    assert len(response.data["results"]) == 0
def test_device_list_basic(client, django_assert_num_queries):
    today = datetime.datetime(2012, 1, 1, tzinfo=datetime.timezone.utc)

    uuid1 = "aaaaaaa1-1342-413b-8e89-db802b2f83f6"
    uuid2 = "ccccccc3-1342-413b-8e89-db802b2f83f6"

    provider = factories.Provider(name="Test provider")
    provider2 = factories.Provider(name="Test another provider")

    device = factories.Device(
        id=uuid1,
        provider=provider,
        identification_number="1AAAAA",
        model="Testa_Model_S",
        category="car",
        propulsion=["combustion"],
        registration_date=today,
        dn_status="available",
        dn_gps_point="Point(40 15.0)",
        dn_gps_timestamp=today,
        dn_battery_pct=0.5,
    )
    factories.Device(
        id=uuid2,
        provider=provider2,
        identification_number="3CCCCC",
        model="Testa_Model_X",
        category="scooter",
        propulsion=["electric"],
        registration_date=today,
        dn_status="unavailable",
        dn_gps_point=None,
        dn_gps_timestamp=None,
        dn_battery_pct=None,
    )

    # Add some telemetries on the first device
    factories.EventRecord(device=device, saved_at=today, event_type="reserve")
    factories.EventRecord.create_batch(3,
                                       device=device,
                                       saved_at=today -
                                       datetime.timedelta(seconds=10))

    expected_device = {
        "id": uuid1,
        "provider_id": str(provider.id),
        "provider_name": "Test provider",
        "identification_number": "1AAAAA",
        "model": "Testa_Model_S",
        "status": "available",
        "category": "car",
        "propulsion": ["combustion"],
        "position": {
            "type": "Point",
            "coordinates": [40, 15.0]
        },
        "last_telemetry_date": "2012-01-01T00:00:00Z",
        "registration_date": "2012-01-01T00:00:00Z",
        "battery": 0.5,
    }
    expected_device2 = {
        "id": uuid2,
        "provider_id": str(provider2.id),
        "provider_name": "Test another provider",
        "identification_number": "3CCCCC",
        "model": "Testa_Model_X",
        "status": "unavailable",
        "category": "scooter",
        "propulsion": ["electric"],
        "last_telemetry_date": None,
        "position": None,
        "registration_date": "2012-01-01T00:00:00Z",
        "battery": None,
    }
    # test auth
    response = client.get("/prv/vehicles/")
    assert response.status_code == 401

    n = BASE_NUM_QUERIES
    n += 1  # query on devices
    n += 1  # count on devices
    with django_assert_num_queries(n):
        response = client.get(
            "/prv/vehicles/",
            **auth_header(SCOPE_PRV_API, provider_id=provider.id))
    assert response.status_code == 200
    data = response.data["results"]
    assert len(data) == 2

    assert expected_device in data
    assert expected_device2 in data

    # test auth
    response = client.get("/prv/vehicles/%s/" % device.id)
    assert response.status_code == 401

    n = BASE_NUM_QUERIES
    n += 1  # query on devices
    n += 1  # query to get areas of device
    expected_device["areas"] = []
    expected_device["provider_logo"] = None

    with django_assert_num_queries(n):
        response = client.get(
            "/prv/vehicles/%s/" % device.id,
            **auth_header(SCOPE_PRV_API, provider_id=provider.id),
        )
    assert response.status_code == 200
    assert response.data == expected_device
示例#25
0
    # test auth
    assert device.event_records.all().count() == 0
    response = client.post(
        reverse("agency:device-event", args=[device_id]),
        data=data,
        content_type="application/json",
    )
    assert response.status_code == 401

    # test nominal
    response = client.post(
        reverse("agency:device-event", args=[device_id]),
        data=data,
        content_type="application/json",
        **auth_header(SCOPE_AGENCY_API, provider_id=provider.id),
    )
    assert response.status_code == 201
    assert response.data == {"device_id": str(device_id), "status": "removed"}
    assert device.event_records.all().count() == 1


@pytest.mark.django_db
def test_device_event_inverted_coordinates(client):
    provider = factories.Provider(
        id=uuid.UUID("aaaa0000-61fd-4cce-8113-81af1de90942"))
    device_id = uuid.UUID("bbbb0000-61fd-4cce-8113-81af1de90942")
    device = factories.Device(id=device_id, provider=provider)

    data = {
        "event_type": "service_end",
示例#26
0
def test_device_list_basic(client, django_assert_num_queries):
    today = datetime.datetime(2012, 1, 1, tzinfo=datetime.timezone.utc)
    uuid1 = uuid.UUID("aaaaaaa1-1342-413b-8e89-db802b2f83f6")
    uuid2 = uuid.UUID("ccccccc3-1342-413b-8e89-db802b2f83f6")

    provider = factories.Provider(name="Test provider")
    provider2 = factories.Provider(name="Test another provider")

    device = factories.Device(
        id=uuid1,
        provider=provider,
        identification_number="1AAAAA",
        model="Testa_Model_S",
        category="car",
        propulsion=["combustion"],
        registration_date=today,
        dn_status="available",
    )
    factories.Device(
        id=uuid2,
        provider=provider,
        identification_number="3CCCCC",
        model="Testa_Model_X",
        category="scooter",
        propulsion=["electric"],
        registration_date=today,
        dn_status="available",
    )
    other_device = factories.Device(provider=provider2)

    # Add some telemetries on the first device
    factories.EventRecord(
        device=device,
        saved_at=today - datetime.timedelta(seconds=10),
        timestamp=today - datetime.timedelta(seconds=10),
    )
    # Last event
    factories.EventRecord(
        device=device,
        saved_at=today,
        event_type=enums.EVENT_TYPE.reserve.name,
        timestamp=today,
    )
    # timestamp predates second record, but it was saved afterwards
    factories.EventRecord(
        device=device,
        saved_at=today + datetime.timedelta(seconds=10),
        event_type=enums.EVENT_TYPE.maintenance_drop_off.name,
        timestamp=today - datetime.timedelta(seconds=5),
    )

    expected_device = {
        "device_id": str(uuid1),
        "provider_id": str(provider.id),
        "vehicle_id": "1AAAAA",
        "model": "Testa_Model_S",
        "type": "car",
        "propulsion": ["combustion"],
        "mfgr": "",
        "year": None,
        "status": "available",
        "prev_event": "reserve",
        "updated": 1_325_376_000_000,
    }

    expected_device2 = {
        "device_id": str(uuid2),
        "provider_id": str(provider.id),
        "vehicle_id": "3CCCCC",
        "model": "Testa_Model_X",
        "type": "scooter",
        "propulsion": ["electric"],
        "mfgr": "",
        "year": None,
        "status": "available",
        "prev_event": None,
        "updated": None,
    }

    # test auth
    response = client.get(reverse("agency:device-list"))
    assert response.status_code == 401

    n = BASE_NUM_QUERIES
    n += 1  # query on devices
    n += 1  # query on last telemetry
    with django_assert_num_queries(n):
        response = client.get(
            reverse("agency:device-list"),
            **auth_header(SCOPE_AGENCY_API, provider_id=provider.id),
        )
    assert response.status_code == 200
    assert len(response.data) == 2

    assert expected_device in response.data
    assert expected_device2 in response.data

    # test auth
    response = client.get(reverse("agency:device-detail", args=[device.id]))
    assert response.status_code == 401

    n = BASE_NUM_QUERIES
    n += 1  # query on devices
    n += 1  # query on last telemetry
    with django_assert_num_queries(n):
        response = client.get(
            reverse("agency:device-detail", args=[device.id]),
            **auth_header(SCOPE_AGENCY_API, provider_id=provider.id),
        )
    assert response.status_code == 200
    assert response.data == expected_device

    # cannot access other providers data
    response = client.get(
        reverse("agency:device-detail", args=[other_device.id]),
        **auth_header(SCOPE_AGENCY_API, provider_id=provider.id),
    )
    assert response.status_code == 404