test_rpc.py 24.5 KB
Newer Older
1
2
import re

3
from datetime import datetime
Kevin Morris's avatar
Kevin Morris committed
4
from http import HTTPStatus
5
from typing import List
Kevin Morris's avatar
Kevin Morris committed
6
7
from unittest import mock

8
import orjson
9
import pytest
10
11

from fastapi.testclient import TestClient
Kevin Morris's avatar
Kevin Morris committed
12
from redis.client import Pipeline
13

14
15
16
17
18
import aurweb.models.dependency_type as dt
import aurweb.models.relation_type as rt

from aurweb import asgi, config, db, scripts
from aurweb.models.account_type import USER_ID
19
20
21
22
23
24
25
26
27
from aurweb.models.license import License
from aurweb.models.package import Package
from aurweb.models.package_base import PackageBase
from aurweb.models.package_dependency import PackageDependency
from aurweb.models.package_keyword import PackageKeyword
from aurweb.models.package_license import PackageLicense
from aurweb.models.package_relation import PackageRelation
from aurweb.models.package_vote import PackageVote
from aurweb.models.user import User
Kevin Morris's avatar
Kevin Morris committed
28
from aurweb.redis import redis_connection
29
30


31
32
33
@pytest.fixture
def client() -> TestClient:
    yield TestClient(app=asgi.app)
34
35


36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
@pytest.fixture
def user(db_test) -> User:
    with db.begin():
        user = db.create(User, Username="test", Email="test@example.org",
                         RealName="Test User 1", Passwd=str(),
                         AccountTypeID=USER_ID)
    yield user


@pytest.fixture
def user2() -> User:
    with db.begin():
        user = db.create(User, Username="user2", Email="user2@example.org",
                         RealName="Test User 2", Passwd=str(),
                         AccountTypeID=USER_ID)
    yield user


@pytest.fixture
def user3() -> User:
    with db.begin():
        user = db.create(User, Username="user3", Email="user3@example.org",
                         RealName="Test User 3", Passwd=str(),
                         AccountTypeID=USER_ID)
    yield user


@pytest.fixture
def packages(user: User, user2: User, user3: User) -> List[Package]:
    output = []

    # Create package records used in our tests.
    with db.begin():
        pkgbase = db.create(PackageBase, Name="big-chungus",
                            Maintainer=user, Packager=user)
        pkg = db.create(Package, PackageBase=pkgbase, Name=pkgbase.Name,
                        Description="Bunny bunny around bunny",
                        URL="https://example.com/")
        output.append(pkg)

        pkgbase = db.create(PackageBase, Name="chungy-chungus",
                            Maintainer=user, Packager=user)
        pkg = db.create(Package, PackageBase=pkgbase, Name=pkgbase.Name,
                        Description="Wubby wubby on wobba wuubu",
                        URL="https://example.com/")
        output.append(pkg)

        pkgbase = db.create(PackageBase, Name="gluggly-chungus",
                            Maintainer=user, Packager=user)
        pkg = db.create(Package, PackageBase=pkgbase, Name=pkgbase.Name,
                        Description="glurrba glurrba gur globba",
                        URL="https://example.com/")
        output.append(pkg)

        pkgbase = db.create(PackageBase, Name="fugly-chungus",
                            Maintainer=user, Packager=user)
92
93

        desc = "A Package belonging to a PackageBase with another name."
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
        pkg = db.create(Package, PackageBase=pkgbase, Name="other-pkg",
                        Description=desc, URL="https://example.com")
        output.append(pkg)

        pkgbase = db.create(PackageBase, Name="woogly-chungus")
        pkg = db.create(Package, PackageBase=pkgbase, Name=pkgbase.Name,
                        Description="wuggla woblabeloop shemashmoop",
                        URL="https://example.com/")
        output.append(pkg)

    # Setup a few more related records on the first package:
    # a license, some keywords and some votes.
    with db.begin():
        lic = db.create(License, Name="GPL")
        db.create(PackageLicense, Package=output[0], License=lic)

        for keyword in ["big-chungus", "smol-chungus", "sizeable-chungus"]:
            db.create(PackageKeyword,
                      PackageBase=output[0].PackageBase,
                      Keyword=keyword)

        now = int(datetime.utcnow().timestamp())
        for user_ in [user, user2, user3]:
            db.create(PackageVote, User=user_,
                      PackageBase=output[0].PackageBase, VoteTS=now)
    scripts.popupdate.run_single(output[0].PackageBase)

    yield output


@pytest.fixture
def depends(packages: List[Package]) -> List[PackageDependency]:
    output = []

    with db.begin():
        dep = db.create(PackageDependency,
                        Package=packages[0],
                        DepTypeID=dt.DEPENDS_ID,
                        DepName="chungus-depends")
        output.append(dep)

        dep = db.create(PackageDependency,
                        Package=packages[1],
                        DepTypeID=dt.DEPENDS_ID,
                        DepName="chungy-depends")
        output.append(dep)

        dep = db.create(PackageDependency,
                        Package=packages[0],
                        DepTypeID=dt.OPTDEPENDS_ID,
                        DepName="chungus-optdepends",
                        DepCondition="=50")
        output.append(dep)

        dep = db.create(PackageDependency,
                        Package=packages[0],
                        DepTypeID=dt.MAKEDEPENDS_ID,
                        DepName="chungus-makedepends")
        output.append(dep)

        dep = db.create(PackageDependency,
                        Package=packages[0],
                        DepTypeID=dt.CHECKDEPENDS_ID,
                        DepName="chungus-checkdepends")
        output.append(dep)

    yield output


@pytest.fixture
def relations(user: User, packages: List[Package]) -> List[PackageRelation]:
    output = []

    with db.begin():
        rel = db.create(PackageRelation,
                        Package=packages[0],
                        RelTypeID=rt.CONFLICTS_ID,
                        RelName="chungus-conflicts")
        output.append(rel)

        rel = db.create(PackageRelation,
                        Package=packages[1],
                        RelTypeID=rt.CONFLICTS_ID,
                        RelName="chungy-conflicts")
        output.append(rel)

        rel = db.create(PackageRelation,
                        Package=packages[0],
                        RelTypeID=rt.PROVIDES_ID,
                        RelName="chungus-provides",
                        RelCondition="<=200")
        output.append(rel)

        rel = db.create(PackageRelation,
                        Package=packages[0],
                        RelTypeID=rt.REPLACES_ID,
                        RelName="chungus-replaces",
                        RelCondition="<=200")
        output.append(rel)

    # Finally, yield the packages.
    yield output


@pytest.fixture(autouse=True)
def setup(db_test):
    # Create some extra package relationships.
    pass
202

203

Kevin Morris's avatar
Kevin Morris committed
204
205
206
207
208
@pytest.fixture
def pipeline():
    redis = redis_connection()
    pipeline = redis.pipeline()

209
210
    # The 'testclient' host is used when requesting the app
    # via fastapi.testclient.TestClient.
Kevin Morris's avatar
Kevin Morris committed
211
212
    pipeline.delete("ratelimit-ws:testclient")
    pipeline.delete("ratelimit:testclient")
213
    pipeline.execute()
Kevin Morris's avatar
Kevin Morris committed
214
215
216
217

    yield pipeline


218
219
220
221
222
def test_rpc_singular_info(client: TestClient,
                           user: User,
                           packages: List[Package],
                           depends: List[PackageDependency],
                           relations: List[PackageRelation]):
223
    # Define expected response.
224
    pkg = packages[0]
225
    expected_data = {
226
227
        "version": 5,
        "results": [{
228
229
230
231
232
233
234
            "Name": pkg.Name,
            "Version": pkg.Version,
            "Description": pkg.Description,
            "URL": pkg.URL,
            "PackageBase": pkg.PackageBase.Name,
            "NumVotes": pkg.PackageBase.NumVotes,
            "Popularity": float(pkg.PackageBase.Popularity),
235
            "OutOfDate": None,
236
237
            "Maintainer": user.Username,
            "URLPath": f"/cgit/aur.git/snapshot/{pkg.Name}.tar.gz",
238
239
240
241
242
243
244
            "Depends": ["chungus-depends"],
            "OptDepends": ["chungus-optdepends=50"],
            "MakeDepends": ["chungus-makedepends"],
            "CheckDepends": ["chungus-checkdepends"],
            "Conflicts": ["chungus-conflicts"],
            "Provides": ["chungus-provides<=200"],
            "Replaces": ["chungus-replaces<=200"],
245
            "License": [pkg.package_licenses.first().License.Name],
246
247
248
249
250
251
252
253
254
            "Keywords": [
                "big-chungus",
                "sizeable-chungus",
                "smol-chungus"
            ]
        }],
        "resultcount": 1,
        "type": "multiinfo"
    }
255
256

    # Make dummy request.
257
    with client as request:
258
259
260
261
262
        resp = request.get("/rpc", params={
            "v": 5,
            "type": "info",
            "arg": ["chungy-chungus", "big-chungus"],
        })
263
264

    # Load  request response into Python dictionary.
265
    response_data = orjson.loads(resp.text)
266
267
268
269
270

    # Remove the FirstSubmitted LastModified, ID and PackageBaseID keys from
    # reponse, as the key's values aren't guaranteed to match between the two
    # (the keys are already removed from 'expected_data').
    for i in ["FirstSubmitted", "LastModified", "ID", "PackageBaseID"]:
271
        response_data["results"][0].pop(i)
272
273

    # Validate that the new dictionaries are the same.
274
    assert response_data == expected_data
275
276


277
def test_rpc_nonexistent_package(client: TestClient):
278
    # Make dummy request.
279
280
    with client as request:
        response = request.get("/rpc/?v=5&type=info&arg=nonexistent-package")
281
282
283
284
285
286
287
288

    # Load request response into Python dictionary.
    response_data = orjson.loads(response.content.decode())

    # Validate data.
    assert response_data["resultcount"] == 0


289
def test_rpc_multiinfo(client: TestClient, packages: List[Package]):
290
291
    # Make dummy request.
    request_packages = ["big-chungus", "chungy-chungus"]
292
    with client as request:
293
294
295
        response = request.get("/rpc", params={
            "v": 5, "type": "info", "arg[]": request_packages
        })
296
297
298
299
300
301
302
303
304
305
306

    # Load request response into Python dictionary.
    response_data = orjson.loads(response.content.decode())

    # Validate data.
    for i in response_data["results"]:
        request_packages.remove(i["Name"])

    assert request_packages == []


307
def test_rpc_mixedargs(client: TestClient, packages: List[Package]):
308
309
310
311
    # Make dummy request.
    response1_packages = ["gluggly-chungus"]
    response2_packages = ["gluggly-chungus", "chungy-chungus"]

312
    with client as request:
313
        # Supply all of the args in the url to enforce ordering.
314
315
316
317
318
319
        response1 = request.get(
            "/rpc?v=5&arg[]=big-chungus&arg=gluggly-chungus&type=info")
    assert response1.status_code == int(HTTPStatus.OK)

    with client as request:
        response2 = request.get(
320
321
            "/rpc?v=5&arg=big-chungus&arg[]=gluggly-chungus"
            "&type=info&arg[]=chungy-chungus")
322
    assert response1.status_code == int(HTTPStatus.OK)
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338

    # Load request response into Python dictionary.
    response1_data = orjson.loads(response1.content.decode())
    response2_data = orjson.loads(response2.content.decode())

    # Validate data.
    for i in response1_data["results"]:
        response1_packages.remove(i["Name"])

    for i in response2_data["results"]:
        response2_packages.remove(i["Name"])

    for i in [response1_packages, response2_packages]:
        assert i == []


339
340
341
342
343
344
345
346
347
def test_rpc_no_dependencies_omits_key(client: TestClient, user: User,
                                       packages: List[Package],
                                       depends: List[PackageDependency],
                                       relations: List[PackageRelation]):
    """
    This makes sure things like 'MakeDepends' get removed from JSON strings
    when they don't have set values.
    """
    pkg = packages[1]
348
    expected_response = {
349
350
        'version': 5,
        'results': [{
351
352
353
354
355
356
357
            'Name': pkg.Name,
            'Version': pkg.Version,
            'Description': pkg.Description,
            'URL': pkg.URL,
            'PackageBase': pkg.PackageBase.Name,
            'NumVotes': pkg.PackageBase.NumVotes,
            'Popularity': int(pkg.PackageBase.Popularity),
358
            'OutOfDate': None,
359
            'Maintainer': user.Username,
360
361
362
363
364
365
366
367
368
            'URLPath': '/cgit/aur.git/snapshot/chungy-chungus.tar.gz',
            'Depends': ['chungy-depends'],
            'Conflicts': ['chungy-conflicts'],
            'License': [],
            'Keywords': []
        }],
        'resultcount': 1,
        'type': 'multiinfo'
    }
369
370

    # Make dummy request.
371
    with client as request:
372
373
374
        response = request.get("/rpc", params={
            "v": 5, "type": "info", "arg": "chungy-chungus"
        })
375
376
377
378
379
380
381
382
383
    response_data = orjson.loads(response.content.decode())

    # Remove inconsistent keys.
    for i in ["ID", "PackageBaseID", "FirstSubmitted", "LastModified"]:
        response_data["results"][0].pop(i)

    assert response_data == expected_response


384
def test_rpc_bad_type(client: TestClient):
385
386
    # Define expected response.
    expected_data = {
387
388
389
390
391
392
        'version': 5,
        'results': [],
        'resultcount': 0,
        'type': 'error',
        'error': 'Incorrect request type specified.'
    }
393
394

    # Make dummy request.
395
    with client as request:
396
397
398
        response = request.get("/rpc", params={
            "v": 5, "type": "invalid-type", "arg": "big-chungus"
        })
399
400
401
402
403
404
405
406

    # Load  request response into Python dictionary.
    response_data = orjson.loads(response.content.decode())

    # Validate data.
    assert expected_data == response_data


407
def test_rpc_bad_version(client: TestClient):
408
    # Define expected response.
409
410
411
412
413
414
415
    expected_data = {
        'version': 0,
        'resultcount': 0,
        'results': [],
        'type': 'error',
        'error': 'Invalid version specified.'
    }
416
417

    # Make dummy request.
418
    with client as request:
419
420
421
        response = request.get("/rpc", params={
            "v": 0, "type": "info", "arg": "big-chungus"
        })
422
423
424
425
426
427
428
429

    # Load  request response into Python dictionary.
    response_data = orjson.loads(response.content.decode())

    # Validate data.
    assert expected_data == response_data


430
def test_rpc_no_version(client: TestClient):
431
    # Define expected response.
432
433
434
435
436
437
438
    expected_data = {
        'version': None,
        'resultcount': 0,
        'results': [],
        'type': 'error',
        'error': 'Please specify an API version.'
    }
439
440

    # Make dummy request.
441
    with client as request:
442
443
444
445
        response = request.get("/rpc", params={
            "type": "info",
            "arg": "big-chungus"
        })
446
447
448
449
450
451
452
453

    # Load  request response into Python dictionary.
    response_data = orjson.loads(response.content.decode())

    # Validate data.
    assert expected_data == response_data


454
def test_rpc_no_type(client: TestClient):
455
456
    # Define expected response.
    expected_data = {
457
458
459
460
461
462
        'version': 5,
        'results': [],
        'resultcount': 0,
        'type': 'error',
        'error': 'No request type/data specified.'
    }
463
464

    # Make dummy request.
465
    with client as request:
466
        response = request.get("/rpc", params={"v": 5, "arg": "big-chungus"})
467
468
469
470
471
472
473
474

    # Load  request response into Python dictionary.
    response_data = orjson.loads(response.content.decode())

    # Validate data.
    assert expected_data == response_data


475
def test_rpc_no_args(client: TestClient):
476
477
    # Define expected response.
    expected_data = {
478
479
480
481
482
483
        'version': 5,
        'results': [],
        'resultcount': 0,
        'type': 'error',
        'error': 'No request type/data specified.'
    }
484
485

    # Make dummy request.
486
    with client as request:
487
        response = request.get("/rpc", params={"v": 5, "type": "info"})
488
489
490
491
492
493
494
495

    # Load  request response into Python dictionary.
    response_data = orjson.loads(response.content.decode())

    # Validate data.
    assert expected_data == response_data


496
def test_rpc_no_maintainer(client: TestClient, packages: List[Package]):
497
    # Make dummy request.
498
    with client as request:
499
500
501
        response = request.get("/rpc", params={
            "v": 5, "type": "info", "arg": "woogly-chungus"
        })
502
503
504
505
506
507

    # Load  request response into Python dictionary.
    response_data = orjson.loads(response.content.decode())

    # Validate data.
    assert response_data["results"][0]["Maintainer"] is None
508
509


510
511
def test_rpc_suggest_pkgbase(client: TestClient, packages: List[Package]):
    params = {"v": 5, "type": "suggest-pkgbase", "arg": "big"}
512
    with client as request:
513
        response = request.get("/rpc", params=params)
514
515
516
    data = response.json()
    assert data == ["big-chungus"]

517
    params["arg"] = "chungy"
518
    with client as request:
519
        response = request.get("/rpc", params=params)
520
521
    data = response.json()
    assert data == ["chungy-chungus"]
522

523
    # Test no arg supplied.
524
    del params["arg"]
525
    with client as request:
526
        response = request.get("/rpc", params=params)
527
528
529
    data = response.json()
    assert data == []

530

531
532
def test_rpc_suggest(client: TestClient, packages: List[Package]):
    params = {"v": 5, "type": "suggest", "arg": "other"}
533
    with client as request:
534
        response = request.get("/rpc", params=params)
535
536
537
538
    data = response.json()
    assert data == ["other-pkg"]

    # Test non-existent Package.
539
    params["arg"] = "nonexistent"
540
    with client as request:
541
        response = request.get("/rpc", params=params)
542
543
544
    data = response.json()
    assert data == []

545
    # Test no arg supplied.
546
    del params["arg"]
547
    with client as request:
548
        response = request.get("/rpc", params=params)
549
550
551
    data = response.json()
    assert data == []

552

Kevin Morris's avatar
Kevin Morris committed
553
554
555
556
557
558
559
560
561
def mock_config_getint(section: str, key: str):
    if key == "request_limit":
        return 4
    elif key == "window_length":
        return 100
    return config.getint(section, key)


@mock.patch("aurweb.config.getint", side_effect=mock_config_getint)
562
def test_rpc_ratelimit(getint: mock.MagicMock, client: TestClient,
563
564
565
                       pipeline: Pipeline, packages: List[Package]):
    params = {"v": 5, "type": "suggest-pkgbase", "arg": "big"}

Kevin Morris's avatar
Kevin Morris committed
566
567
    for i in range(4):
        # The first 4 requests should be good.
568
        with client as request:
569
            response = request.get("/rpc", params=params)
Kevin Morris's avatar
Kevin Morris committed
570
571
572
        assert response.status_code == int(HTTPStatus.OK)

    # The fifth request should be banned.
573
    with client as request:
574
        response = request.get("/rpc", params=params)
Kevin Morris's avatar
Kevin Morris committed
575
576
577
578
579
580
581
582
583
    assert response.status_code == int(HTTPStatus.TOO_MANY_REQUESTS)

    # Delete the cached records.
    pipeline.delete("ratelimit-ws:testclient")
    pipeline.delete("ratelimit:testclient")
    one, two = pipeline.execute()
    assert one and two

    # The new first request should be good.
584
    with client as request:
585
        response = request.get("/rpc", params=params)
Kevin Morris's avatar
Kevin Morris committed
586
    assert response.status_code == int(HTTPStatus.OK)
587
588


589
590
def test_rpc_etag(client: TestClient, packages: List[Package]):
    params = {"v": 5, "type": "suggest-pkgbase", "arg": "big"}
591
592

    with client as request:
593
594
595
596
        response1 = request.get("/rpc", params=params)
    with client as request:
        response2 = request.get("/rpc", params=params)

597
598
599
    assert response1.headers.get("ETag") is not None
    assert response1.headers.get("ETag") != str()
    assert response1.headers.get("ETag") == response2.headers.get("ETag")
600
601


602
def test_rpc_search_arg_too_small(client: TestClient):
603
    params = {"v": 5, "type": "search", "arg": "b"}
604
    with client as request:
605
        response = request.get("/rpc", params=params)
606
607
608
609
    assert response.status_code == int(HTTPStatus.OK)
    assert response.json().get("error") == "Query arg too small."


610
611
def test_rpc_search(client: TestClient, packages: List[Package]):
    params = {"v": 5, "type": "search", "arg": "big"}
612
    with client as request:
613
        response = request.get("/rpc", params=params)
614
615
616
617
618
619
    assert response.status_code == int(HTTPStatus.OK)

    data = response.json()
    assert data.get("resultcount") == 1

    result = data.get("results")[0]
620
    assert result.get("Name") == packages[0].Name
621

622
623
624
    # Test the If-None-Match headers.
    etag = response.headers.get("ETag").strip('"')
    headers = {"If-None-Match": etag}
625
    response = request.get("/rpc", params=params, headers=headers)
626
627
628
    assert response.status_code == int(HTTPStatus.NOT_MODIFIED)
    assert response.content == b''

629
    # No args on non-m by types return an error.
630
631
632
    del params["arg"]
    with client as request:
        response = request.get("/rpc", params=params)
633
634
635
    assert response.json().get("error") == "No request type/data specified."


636
637
def test_rpc_msearch(client: TestClient, user: User, packages: List[Package]):
    params = {"v": 5, "type": "msearch", "arg": user.Username}
638
    with client as request:
639
        response = request.get("/rpc", params=params)
640
641
642
643
644
    data = response.json()

    # user1 maintains 4 packages; assert that we got them all.
    assert data.get("resultcount") == 4
    names = list(sorted(r.get("Name") for r in data.get("results")))
645
    expected_results = [
646
647
648
649
        "big-chungus",
        "chungy-chungus",
        "gluggly-chungus",
        "other-pkg"
650
    ]
651
652
653
    assert names == expected_results

    # Search for a non-existent maintainer, giving us zero packages.
654
655
    params["arg"] = "blah-blah"
    response = request.get("/rpc", params=params)
656
657
658
    data = response.json()
    assert data.get("resultcount") == 0

659
660
661
    with db.begin():
        packages[0].PackageBase.Maintainer = None

662
663
    # A missing arg still succeeds, but it returns all orphans.
    # Just verify that we receive no error and the orphaned result.
664
665
    params.pop("arg")
    response = request.get("/rpc", params=params)
666
667
668
    data = response.json()
    assert data.get("resultcount") == 1
    result = data.get("results")[0]
669
    assert result.get("Name") == "big-chungus"
670
671


672
673
674
675
676
def test_rpc_search_depends(client: TestClient, packages: List[Package],
                            depends: List[PackageDependency]):
    params = {
        "v": 5, "type": "search", "by": "depends", "arg": "chungus-depends"
    }
677
    with client as request:
678
        response = request.get("/rpc", params=params)
679
680
681
    data = response.json()
    assert data.get("resultcount") == 1
    result = data.get("results")[0]
682
    assert result.get("Name") == packages[0].Name
683
684


685
686
687
688
689
690
691
692
def test_rpc_search_makedepends(client: TestClient, packages: List[Package],
                                depends: List[PackageDependency]):
    params = {
        "v": 5,
        "type": "search",
        "by": "makedepends",
        "arg": "chungus-makedepends"
    }
693
    with client as request:
694
        response = request.get("/rpc", params=params)
695
696
697
    data = response.json()
    assert data.get("resultcount") == 1
    result = data.get("results")[0]
698
    assert result.get("Name") == packages[0].Name
699
700


701
702
703
704
705
706
707
708
def test_rpc_search_optdepends(client: TestClient, packages: List[Package],
                               depends: List[PackageDependency]):
    params = {
        "v": 5,
        "type": "search",
        "by": "optdepends",
        "arg": "chungus-optdepends"
    }
709
    with client as request:
710
        response = request.get("/rpc", params=params)
711
712
713
    data = response.json()
    assert data.get("resultcount") == 1
    result = data.get("results")[0]
714
    assert result.get("Name") == packages[0].Name
715
716


717
718
719
720
721
722
723
724
def test_rpc_search_checkdepends(client: TestClient, packages: List[Package],
                                 depends: List[PackageDependency]):
    params = {
        "v": 5,
        "type": "search",
        "by": "checkdepends",
        "arg": "chungus-checkdepends"
    }
725
    with client as request:
726
        response = request.get("/rpc", params=params)
727
728
729
    data = response.json()
    assert data.get("resultcount") == 1
    result = data.get("results")[0]
730
    assert result.get("Name") == packages[0].Name
731
732


733
def test_rpc_incorrect_by(client: TestClient):
734
    params = {"v": 5, "type": "search", "by": "fake", "arg": "big"}
735
    with client as request:
736
        response = request.get("/rpc", params=params)
737
    assert response.json().get("error") == "Incorrect by field specified."
738
739


740
def test_rpc_jsonp_callback(client: TestClient):
741
742
743
744
745
    """ Test the callback parameter.

    For end-to-end verification, the `examples/jsonp.html` file can be
    used to submit jsonp callback requests to the RPC.
    """
746
747
748
749
750
751
    params = {
        "v": 5,
        "type": "search",
        "arg": "big",
        "callback": "jsonCallback"
    }
752
    with client as request:
753
        response = request.get("/rpc", params=params)
754
755
    assert response.headers.get("content-type") == "text/javascript"
    assert re.search(r'^/\*\*/jsonCallback\(.*\)$', response.text) is not None
756
757

    # Test an invalid callback name; we get an application/json error.
758
    params["callback"] = "jsonCallback!"
759
    with client as request:
760
        response = request.get("/rpc", params=params)
761
762
    assert response.headers.get("content-type") == "application/json"
    assert response.json().get("error") == "Invalid callback name."