示例#1
0
def test_case_citation_redirect(client, case_factory, elasticsearch):
    """Should allow various forms of citation, should redirect to normalized_cite"""
    case = case_factory(citations__cite='123 Mass. App. 456')
    citation = case.citations.first()
    url = api_reverse("cases-detail", args=[citation.normalized_cite])

    # should have received a redirect
    response = client.get(url)
    check_response(response, status_code=302)

    response = client.get(url, follow=True)
    check_response(response)
    content = response.json()['results']
    case = citation.case

    # should only have one case returned
    assert len(content) == 1
    assert content[0]['id'] == case.id
    # should only have one citation for this case
    citations_result = content[0]['citations']
    assert len(citations_result) == 1
    assert citations_result[0]['cite'] == citation.cite

    # allow user to enter real citation (not normalized)
    url = api_reverse("case-get-cite", args=[citation.cite])
    response = client.get(url, follow=True)

    check_response(response)
    content = response.json()['results']
    case = citation.case
    assert len(content) == 1
    assert content[0]['id'] == case.id
示例#2
0
def test_csv(transactional_db, client, auth_client, restricted_case,
             unrestricted_case, elasticsearch):
    """ Test ?format=csv on case detail and list API. """
    content_type = 'text/csv'
    case_text = "Opinion text"

    # unauthorized request can't fetch restricted TSV
    response = client.get(
        api_reverse("cases-detail", args=[restricted_case.id]), {
            "full_case": "true",
            "format": "csv"
        })
    check_response(response,
                   content_excludes=case_text,
                   content_type=content_type)

    # authorized request can fetch restricted TSV
    response = auth_client.get(
        api_reverse("cases-detail", args=[restricted_case.id]), {
            "full_case": "true",
            "format": "csv"
        })
    check_response(response,
                   content_includes=case_text,
                   content_type=content_type)

    # both can fetch unrestricted TSV
    response = client.get(
        api_reverse("cases-detail", args=[unrestricted_case.id]), {
            "full_case": "true",
            "format": "csv"
        })
    check_response(response,
                   content_includes=case_text,
                   content_type=content_type)
    response = auth_client.get(
        api_reverse("cases-detail", args=[unrestricted_case.id]), {
            "full_case": "true",
            "format": "csv"
        })
    check_response(response,
                   content_includes=case_text,
                   content_type=content_type)

    # ?format=csv works on list page
    response = auth_client.get(api_reverse("cases-list"), {
        "full_case": "true",
        "format": "csv",
        "page_size": "100"
    })
    # each row separated by '\n'
    decoded_response = ""
    for res in response.streaming_content:
        decoded_response += res.decode()

    # -1 for headers, -1 for last newline '\n'
    response_count = len(decoded_response.split('\n')) - 2
    assert response_count == CaseMetadata.objects.count()
    assert case_text in decoded_response
    check_response(response, content_type=content_type)
示例#3
0
def test_cases_endpoint(client, unrestricted_case, elasticsearch):
    # test list endpoint
    case_list_url = api_reverse("cases-list")
    response = client.get(case_list_url)
    assert response.json()['count'] == 1

    # test detail endpoint
    response = client.get(
        api_reverse("cases-detail", args=[unrestricted_case.id]))
    check_response(response)
    results = response.json()
    assert results['name'] == unrestricted_case.name
示例#4
0
def test_bulk_data_list(request, case_export, private_case_export, client_fixture, can_see_private):
    client = request.getfixturevalue(client_fixture)
    public_url = api_reverse('caseexport-download', args=[case_export.pk])
    private_url = api_reverse('caseexport-download', args=[private_case_export.pk])

    response = client.get(reverse('bulk-download'))
    check_response(response)
    content = response.content.decode()
    assert public_url in content
    if can_see_private:
        assert private_url in content
    else:
        assert private_url not in content
示例#5
0
def test_case_detail_pdf(transactional_db, client, auth_client,
                         restricted_case, unrestricted_case, elasticsearch):
    """ Test ?format=pdf on case detail API. """
    content_type = 'application/pdf'
    case_text = "REMEMBERED"
    CaseMetadata.objects.update(first_page_order=1, last_page_order=3)

    # unauthorized request can't fetch restricted PDF
    response = client.get(
        api_reverse("cases-detail", args=[restricted_case.id]), {
            "full_case": "true",
            "format": "pdf"
        })
    check_response(response, status_code=400)

    # authorized request can fetch restricted PDF
    response = auth_client.get(
        api_reverse("cases-detail", args=[restricted_case.id]), {
            "full_case": "true",
            "format": "pdf"
        })
    check_response(response,
                   content_includes=case_text,
                   content_type=content_type)

    # both can fetch unrestricted PDF
    response = client.get(
        api_reverse("cases-detail", args=[unrestricted_case.id]), {
            "full_case": "true",
            "format": "pdf"
        })
    check_response(response,
                   content_includes=case_text,
                   content_type=content_type)
    response = auth_client.get(
        api_reverse("cases-detail", args=[unrestricted_case.id]), {
            "full_case": "true",
            "format": "pdf"
        })
    check_response(response,
                   content_includes=case_text,
                   content_type=content_type)

    # ?format=pdf only works on detail page
    response = auth_client.get(
        api_reverse("cases-list", args=[unrestricted_case.id]), {
            "full_case": "true",
            "format": "pdf"
        })
    check_response(response, status_code=404)
示例#6
0
def test_ngrams_api(client, request):
    ngrammed_cases = request.getfixturevalue(
        'ngrammed_cases'
    )  # load fixture inside test so flaky() can catch errors

    # check result counts when not filtering by jurisdiction
    json = client.get(api_reverse('ngrams-list'), {'q': 'one two'}).json()
    assert json['results'] == {
        'one two': {
            'total': [{
                'year': '2000',
                'count': [2, 9],
                'doc_count': [2, 3]
            }]
        }
    }

    # check result counts when filtering by jurisdiction
    json = client.get(api_reverse('ngrams-list'), {
        'q': 'one two',
        'jurisdiction': ngrammed_cases[1].jurisdiction.slug
    }).json()
    assert json['results'] == {
        'one two': {
            'jur1': [{
                'year': '2000',
                'count': [1, 6],
                'doc_count': [1, 2]
            }]
        }
    }

    # check wildcard match
    json = client.get(api_reverse('ngrams-list'), {'q': 'three *'}).json()
    assert json['results'] == {
        'three four': {
            'total': [{
                'year': '2000',
                'count': [1, 9],
                'doc_count': [1, 3]
            }]
        },
        "three don't": {
            'total': [{
                'year': '2000',
                'count': [2, 9],
                'doc_count': [2, 3]
            }]
        }
    }
示例#7
0
def test_filter_court(client, court):
    # filtering court by jurisdiction
    jur_slug = court.jurisdiction.slug
    response = client.get(api_reverse("court-list"),
                          {"jurisdiction_slug": jur_slug})
    check_response(response)
    results = response.json()['results']
    assert court.name_abbreviation == results[0]['name_abbreviation']

    # filtering court by name substring
    court_name_str = court.name.split(' ')[1]
    response = client.get(api_reverse("court-list"), {"name": court_name_str})
    content = response.json()
    for result in content['results']:
        assert court_name_str in result['name']
示例#8
0
def test_ingest_courtlistener(client, elasticsearch):
    fabfile.ingest_courtlistener(
        Path(settings.BASE_DIR) / "test_data/courtlistener")
    response = client.get(api_reverse("resolve-list"), {'q': '254 P.3d 649'})
    check_response(response)
    assert response.json() == {
        '254 P.3d 649': [{
            'source':
            'cl',
            'source_id':
            891689,
            'citations': [{
                'cite': '254 P.3d 649',
                'normalized_cite': '254p3d649',
                'type': 3,
                'volume': 254,
                'reporter': 'P.3d',
                'page': '649'
            }],
            'name_short':
            'State v. Ramirez',
            'name_full':
            '',
            'decision_date':
            '2011-06-13',
            'frontend_url':
            'https://www.courtlistener.com/opinion/891689/state-v-ramirez/',
            'api_url':
            'https://www.courtlistener.com/api/rest/v3/clusters/891689/',
            'simhash':
            '1:b862eaa3efce3d01'
        }]
    }
示例#9
0
def test_filter_reporter(client, reporter):
    # filtering reporter by name substring
    reporter_name_str = reporter.full_name.split(' ')[1]
    response = client.get(api_reverse("reporter-list"),
                          {"full_name": reporter_name_str})
    content = response.json()
    for result in content['results']:
        assert reporter_name_str in result['full_name']
示例#10
0
def test_case_export_download(request, client_fixture, export_fixture, status_code):
    client = request.getfixturevalue(client_fixture)
    export = request.getfixturevalue(export_fixture)
    response = client.get(api_reverse('caseexport-download', args=[export.pk]))
    if status_code == 200:
        check_zip_response(response)
    else:
        check_response(response, status_code=status_code)
示例#11
0
def get_casebody_data_with_format(client, case_id, body_format):
    response = client.get(api_reverse('cases-detail', args=[case_id]), {
        "full_case": "true",
        "body_format": body_format
    })
    check_response(response)
    content = response.json()
    casebody = content["casebody"]
    assert casebody['status'] == "ok"
    return casebody['data']
示例#12
0
def test_unauthorized_request(cap_user, client, restricted_case,
                              elasticsearch):
    assert cap_user.case_allowance_remaining == cap_user.total_case_allowance
    client.credentials(HTTP_AUTHORIZATION='Token fake')
    response = client.get(
        api_reverse("cases-detail", args=[restricted_case.id]),
        {"full_case": "true"})
    check_response(response, status_code=401)

    cap_user.refresh_from_db()
    assert cap_user.case_allowance_remaining == cap_user.total_case_allowance
示例#13
0
def test_model_endpoint(request, client, fixture_name, detail_attr,
                        comparison_attr):
    """ Generic test to kick the tires on -list and -detail for model endpoints. """
    instance = request.getfixturevalue(fixture_name)
    model = instance.__class__
    resource_name = model.__name__.lower()

    # test list endpoint
    response = client.get(api_reverse("%s-list" % resource_name))
    check_response(response)
    results = response.json()['results']
    assert results
    assert len(results) == model.objects.count()

    # test detail endpoint
    response = client.get(
        api_reverse("%s-detail" % resource_name,
                    args=[getattr(instance, detail_attr)]))
    check_response(response)
    results = response.json()
    assert results[comparison_attr] == getattr(instance, comparison_attr)
示例#14
0
def test_full_text_search(client, case_factory, elasticsearch):
    case1 = case_factory(name_abbreviation="111 222 333 555666")
    case2 = case_factory(name_abbreviation="111 stop 222 444 555777")
    case_factory(name_abbreviation="nothing matching")

    # AND queries
    response = client.get(api_reverse("cases-list"), {"search": "111 222"})
    content = response.json()
    assert {case1.id,
            case2.id} == set(result['id'] for result in content['results'])

    # OR queries
    response = client.get(api_reverse("cases-list"), {"search": "333 | 444"})
    content = response.json()
    assert {case1.id,
            case2.id} == set(result['id'] for result in content['results'])

    # phrase search
    response = client.get(api_reverse("cases-list"), {"search": '"111 222"'})
    content = response.json()
    assert {case1.id} == set(result['id'] for result in content['results'])

    # prefix search
    response = client.get(api_reverse("cases-list"), {"search": '555*'})
    content = response.json()
    assert {case1.id,
            case2.id} == set(result['id'] for result in content['results'])

    # empty search
    response = client.get(api_reverse("cases-list"),
                          {"search": "Some other search that doesn't work"})
    content = response.json()
    assert content == {
        "count": 0,
        "next": None,
        "previous": None,
        "results": []
    }
示例#15
0
def test_track_history(auth_user, auth_client, restricted_case, elasticsearch):
    # initial fetch
    url = api_reverse("cases-detail", args=[restricted_case.id])
    kwargs = {"full_case": "true"}
    response = auth_client.get(url, kwargs)
    check_response(response)
    result = response.json()
    assert result['casebody']['status'] == 'ok'

    # make sure the auth_user's case download number has gone down by 1
    auth_user.refresh_from_db()
    assert auth_user.case_allowance_remaining == auth_user.total_case_allowance - 1

    # make sure no history is tracked
    assert UserHistory.objects.count() == 0

    # fetch with history tracking
    auth_user.track_history = True
    auth_user.save()
    response = auth_client.get(url, kwargs)
    result = response.json()
    assert result['casebody']['status'] == 'ok'

    # make sure the auth_user's case download number has gone down by 1
    auth_user.refresh_from_db()
    assert auth_user.case_allowance_remaining == auth_user.total_case_allowance - 2

    # history object now exists
    assert [(h.case_id, h.user_id) for h in UserHistory.objects.all()
            ] == [(restricted_case.id, auth_user.id)]

    # fetch again with history tracking
    auth_user.track_history = True
    auth_user.save()
    response = auth_client.get(url, kwargs)
    result = response.json()
    assert result['casebody']['status'] == 'ok'

    # download number has not gone down after second fetch with history tracking
    auth_user.refresh_from_db()
    assert auth_user.case_allowance_remaining == auth_user.total_case_allowance - 2

    # user can still fetch this case even when quota exhausted
    auth_user.case_allowance_remaining = 0
    auth_user.save()
    response = auth_client.get(url, kwargs)
    result = response.json()
    assert result['casebody']['status'] == 'ok'
    auth_user.refresh_from_db()
    assert auth_user.case_allowance_remaining == 0
示例#16
0
def test_authenticated_full_case_blacklisted(auth_user, auth_client,
                                             restricted_case, elasticsearch):
    ### blacklisted jurisdiction cases should be counted against the user

    response = auth_client.get(
        api_reverse("cases-detail", args=[restricted_case.id]),
        {"full_case": "true"})
    check_response(response)
    result = response.json()
    assert result['casebody']['status'] == 'ok'

    # make sure the auth_user's case download number has gone down by 1
    auth_user.refresh_from_db()
    assert auth_user.case_allowance_remaining == auth_user.total_case_allowance - 1
示例#17
0
def test_unauthenticated_full_case(unrestricted_case, restricted_case, client,
                                   elasticsearch):
    """
    we should allow users to get full case without authentication
    if case is whitelisted
    we should allow users to see why they couldn't get full case
    if case is blacklisted
    """
    case_url = api_reverse("cases-detail", args=[unrestricted_case.id])

    response = client.get(case_url, {"full_case": "true"})
    check_response(response)
    content = response.json()
    assert "casebody" in content
    assert type(content['casebody']['data']) is dict

    case_url = api_reverse("cases-detail", args=[restricted_case.id])
    response = client.get(case_url, {"full_case": "true"})
    check_response(response)
    content = response.json()
    casebody = content['casebody']
    assert 'error_' in casebody['status']
    assert not casebody['data']
示例#18
0
def test_authenticated_multiple_full_cases(auth_user, auth_client,
                                           case_factory, elasticsearch):
    ### mixed requests should be counted only for blacklisted cases

    [case_factory(jurisdiction__whitelisted=False) for i in range(2)]
    [case_factory(jurisdiction__whitelisted=True) for i in range(1)]

    response = auth_client.get(api_reverse("cases-list"),
                               {"full_case": "true"})
    check_response(response)
    assert response.json()['count'] == 3

    # make sure the auth_user's case download number has gone down by 2
    auth_user.refresh_from_db()
    assert auth_user.case_allowance_remaining == auth_user.total_case_allowance - 2
示例#19
0
def test_authenticated_full_case_whitelisted(auth_user, auth_client,
                                             unrestricted_case, elasticsearch):
    ### whitelisted jurisdiction should not be counted against the user

    response = auth_client.get(
        api_reverse("cases-detail", args=[unrestricted_case.id]),
        {"full_case": "true"})
    check_response(response)
    result = response.json()
    casebody = result['casebody']
    assert casebody['status'] == 'ok'
    assert type(casebody['data']) is dict

    # make sure the user's case download number has remained the same
    auth_user.refresh_from_db()
    assert auth_user.case_allowance_remaining == auth_user.total_case_allowance
示例#20
0
def test_filter_case_cite_by(client, extracted_citation_factory, case_factory,
                             elasticsearch):
    search_url = api_reverse("cases-list")
    cases = [case_factory() for _ in range(4)]
    case_cited = case_factory(citations__cite='1 Mass. 1')
    for c in cases[:-1]:
        extracted_citation_factory(cited_by=c, cite='1 Mass. 1')
    update_elasticsearch_from_queue()

    # get cases by cites_to=citation
    content = client.get(search_url, {"cites_to": '1 Mass. 1'}).json()
    assert set(case['id']
               for case in content['results']) == set(c.id for c in cases[:-1])

    # get cases by cites_to=id
    content = client.get(search_url, {"cites_to": case_cited.id}).json()
    assert set(case['id']
               for case in content['results']) == set(c.id for c in cases[:-1])
示例#21
0
def test_flow(client, unrestricted_case, elasticsearch):
    """user should be able to click through to get to different tables"""
    # start with case
    response = client.get(
        api_reverse("cases-detail", args=[unrestricted_case.id]))
    check_response(response)
    content = response.json()
    # onwards to court
    court_url = content.get("court")["url"]
    assert court_url
    response = client.get(court_url)
    check_response(response)
    # onwards to jurisdiction
    jurisdiction_url = content.get("jurisdiction")["url"]
    assert jurisdiction_url
    response = client.get(jurisdiction_url)
    check_response(response)
    content = response.json()
    assert content.get("name") == unrestricted_case.jurisdiction.name
示例#22
0
def test_pagination(client, case_factory, elasticsearch):
    cases = [case_factory() for _ in range(3)]

    ids = []

    response = client.get(api_reverse("cases-list"), {"page_size": 1})
    content = response.json()
    assert len(content['results']) == 1
    ids.append(content['results'][0]['id'])

    response = client.get(content['next'])
    content = response.json()
    assert len(content['results']) == 1
    ids.append(content['results'][0]['id'])

    response = client.get(content['next'])
    content = response.json()
    assert len(content['results']) == 1
    ids.append(content['results'][0]['id'])
    assert content['next'] is None

    assert set(ids) == set(case.id for case in cases)
示例#23
0
def test_harvard_access(request, restricted_case, client_fixture_name,
                        elasticsearch):
    ### user with harvard access can download from harvard IPs, even without case allowance
    client = request.getfixturevalue(client_fixture_name)
    user = client.auth_user
    user.harvard_access = True
    user.case_allowance_remaining = 1
    user.save()
    case_url = api_reverse("cases-detail", args=[restricted_case.id])

    # request works when IP address provided
    response = client.get(case_url, {"full_case": "true"},
                          HTTP_CF_CONNECTING_IP='128.103.1.1')
    check_response(response)
    result = response.json()
    assert result['casebody']['status'] == 'ok'

    # no case allowance used
    user.refresh_from_db()
    assert user.case_allowance_remaining == 1

    # request succeeds when IP address is wrong, using case allowance
    response = client.get(case_url, {"full_case": "true"},
                          HTTP_CF_CONNECTING_IP='1.1.1.1')
    check_response(response)
    result = response.json()
    assert result['casebody']['status'] == 'ok'

    # case allowance used
    user.refresh_from_db()
    assert user.case_allowance_remaining == 0

    # request fails when case allowance exhausted
    response = client.get(case_url, {"full_case": "true"},
                          HTTP_CF_CONNECTING_IP='1.1.1.1')
    check_response(response)
    result = response.json()
    assert result['casebody']['status'] != 'ok'
示例#24
0
def test_unlimited_access(auth_user, auth_client, restricted_case,
                          elasticsearch):
    ### user with unlimited access should not have blacklisted cases count against them
    auth_user.total_case_allowance = settings.API_CASE_DAILY_ALLOWANCE
    auth_user.unlimited_access = True
    auth_user.unlimited_access_until = timedelta(hours=24) + timezone.now()
    auth_user.save()
    case_url = api_reverse("cases-detail", args=[restricted_case.id])

    response = auth_client.get(case_url, {"full_case": "true"})
    check_response(response)
    auth_user.refresh_from_db()
    assert auth_user.case_allowance_remaining == auth_user.total_case_allowance

    # total_case_allowance shouldn't matter if unlimited access is in effect
    auth_user.total_case_allowance = 0
    auth_user.case_allowance_remaining = 0
    auth_user.unlimited_access_until = None
    auth_user.save()
    response = auth_client.get(case_url, {"full_case": "true"})
    check_response(response)
    result = response.json()
    casebody = result['casebody']
    assert casebody['status'] == 'ok'
    assert type(casebody['data']) is dict

    # don't allow user to download blacklisted case if unlimited access has expired
    # and they don't have enough case allowance
    auth_user.total_case_allowance = 0
    auth_user.case_allowance_remaining = 0
    auth_user.unlimited_access_until = timezone.now() - timedelta(hours=1)
    auth_user.save()
    response = auth_client.get(case_url, {"full_case": "true"})
    check_response(response)
    result = response.json()
    assert result['casebody']['status'] != 'ok'
示例#25
0
def api_url(url_name, *args, **kwargs):
    """ Like the {% url %} tag, but output includes the full domain. """
    return api_reverse(url_name, args=args, kwargs=kwargs)
示例#26
0
def test_redoc(client):
    response = client.get(api_reverse("schema-redoc"))
    check_response(response, content_type="text/html")
示例#27
0
            }]
        },
        "three don't": {
            'total': [{
                'year': '2000',
                'count': [2, 9],
                'doc_count': [2, 3]
            }]
        }
    }


# API SPECIFICATION ENDPOINTS
@pytest.mark.django_db
@pytest.mark.parametrize("url, content_type", [
    (api_reverse("schema-swagger-ui"), 'text/html'),
    (api_reverse("schema-json", args=['.json']), 'application/json'),
    (api_reverse("schema-json", args=['.yaml']), 'application/yaml'),
])
def test_swagger(client, url, content_type):
    response = client.get(url)
    check_response(response, content_type=content_type)


@pytest.mark.django_db
def test_redoc(client):
    response = client.get(api_reverse("schema-redoc"))
    check_response(response, content_type="text/html")


# PAGINATION
示例#28
0
def test_filter_case(client, case_factory, elasticsearch):
    cases = [case_factory() for _ in range(3)]
    search_url = api_reverse("cases-list")

    # filtering case by name_abbreviation
    case_to_test = cases[0]
    response = client.get(
        search_url, {"name_abbreviation": case_to_test.name_abbreviation})
    content = response.json()
    assert [case_to_test.id] == [result['id'] for result in content['results']]

    # filtering case by name_abbreviation lowercased substring
    response = client.get(
        search_url,
        {"name_abbreviation": case_to_test.name_abbreviation.lower()})
    content = response.json()
    assert [case_to_test.id] == [result['id'] for result in content['results']]

    # filtering case by court slug
    case_to_test = cases[2]
    response = client.get(search_url, {"court": case_to_test.court.slug})
    content = response.json()
    assert [case_to_test.id] == [result['id'] for result in content['results']]

    # filtering case by court id
    response = client.get(search_url, {"court_id": case_to_test.court.id})
    content = response.json()
    assert len(content['results']) == 1
    assert [case_to_test.id] == [result['id'] for result in content['results']]

    # filtering case by reporter
    reporter = case_to_test.reporter.id
    response = client.get(search_url, {"reporter": reporter})
    content = response.json()
    assert [case_to_test.id] == [result['id'] for result in content['results']]

    # filtering by decision_date
    # make sure that we can filter by decision_date's datefield
    # but we get decision_date_original string in response
    case_to_test = cases[0]
    decision_date = case_to_test.decision_date_original
    response = client.get(search_url, {
        "decision_date_min": decision_date,
        "decision_date_max": decision_date
    })
    content = response.json()
    result = content['results'][0]
    assert case_to_test.id == result['id']

    # by jurisdiction
    response = client.get(search_url,
                          {"jurisdiction": case_to_test.jurisdiction.slug},
                          follow=True)
    content = response.json()
    assert content['count'] == 1
    jurisdictions = set(
        [result['jurisdiction']['slug'] for result in content['results']])
    assert jurisdictions == {case_to_test.jurisdiction.slug}

    # by docket_number
    case_to_test = cases[0]
    response = client.get(search_url,
                          {"docket_number": case_to_test.docket_number})
    content = response.json()
    result = content['results'][0]
    assert case_to_test.docket_number == result['docket_number']
示例#29
0
def test_registration_flow(client, restricted_case, elasticsearch, email_blocklist_factory):

    # can't register without agreeing to TOS
    email = '*****@*****.**'
    register_kwargs = {
        'email': email,
        'first_name': 'First',
        'last_name': 'Last',
        'password1': 'Password2',
        'password2': 'Password2',
        'agreed_to_tos': '',
    }
    response = client.post(reverse('register'), register_kwargs)
    check_response(response, content_includes="This field is required.")

    # can't register with blocked email
    email_blocklist_factory(domain='blocked.com')
    register_kwargs['agreed_to_tos'] = 'on'
    register_kwargs['email'] = '*****@*****.**'
    response = client.post(reverse('register'), register_kwargs)
    check_response(response, content_includes="This email address is invalid.")

    # can register
    register_kwargs['email'] = email
    response = client.post(reverse('register'), register_kwargs)
    check_response(response)
    user = CapUser.objects.get(email=email)
    assert user.first_name == "First"
    assert user.last_name == "Last"
    assert user.check_password("Password2")
    assert user.total_case_allowance == 0

    # new user doesn't have a token yet
    with pytest.raises(Token.DoesNotExist):
        assert user.auth_token

    # can't login without verifying email address
    response = client.post(reverse('login'), {
        'username': user.email,
        'password': '******'
    })
    check_response(response, content_includes="This email is registered but not yet verified")

    # can verify email address
    verify_email = mail.outbox[0].body
    verify_url = re.findall(r'https://\S+', verify_email)[0]
    response = client.get(verify_url)
    check_response(response, content_includes="We've verified your email address.")
    user.refresh_from_db()
    assert user.email_verified
    assert user.auth_token
    assert user.total_case_allowance == settings.API_CASE_DAILY_ALLOWANCE

    # can login with verified email address
    response = client.post(reverse('login'), {
        'username': user.email,
        'password': '******'
    })
    check_response(response, status_code=302)

    # can fetch blacklisted case
    response = client.get(api_reverse('cases-detail', kwargs={'id': restricted_case.id}), {'full_case':'true'})
    check_response(response, content_includes="ok")

    # logout to attempt new registration
    client.logout()

    # can't register with similar email addresses
    response = client.post(reverse('register'), {
        'email': email.replace('new_user', 'new_user+stuff'),
        'first_name': 'First',
        'last_name': 'Last',
        'password1': 'Password2',
        'password2': 'Password2',
    })
    check_response(response, content_includes="A user with the same email address has already registered.")