test_rpc.py 25.2 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
import aurweb.models.dependency_type as dt
import aurweb.models.relation_type as rt

17
from aurweb import asgi, config, db, rpc, scripts
18
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
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
def test_rpc_documentation(client: TestClient):
    with client as request:
        resp = request.get("/rpc")
    assert resp.status_code == int(HTTPStatus.OK)
    assert "aurweb RPC Interface" in resp.text


def test_rpc_documentation_missing():
    config_get = config.get

    def mock_get(section: str, key: str) -> str:
        if section == "options" and key == "aurwebdir":
            return "/missing"
        return config_get(section, key)

    with mock.patch("aurweb.config.get", side_effect=mock_get):
        config.rehash()
        expr = r"^doc/rpc\.html could not be read$"
        with pytest.raises(OSError, match=expr):
            rpc.documentation()
    config.rehash()


241
242
243
244
245
def test_rpc_singular_info(client: TestClient,
                           user: User,
                           packages: List[Package],
                           depends: List[PackageDependency],
                           relations: List[PackageRelation]):
246
    # Define expected response.
247
    pkg = packages[0]
248
    expected_data = {
249
250
        "version": 5,
        "results": [{
251
252
253
254
255
256
257
            "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),
258
            "OutOfDate": None,
259
260
            "Maintainer": user.Username,
            "URLPath": f"/cgit/aur.git/snapshot/{pkg.Name}.tar.gz",
261
262
263
264
265
266
267
            "Depends": ["chungus-depends"],
            "OptDepends": ["chungus-optdepends=50"],
            "MakeDepends": ["chungus-makedepends"],
            "CheckDepends": ["chungus-checkdepends"],
            "Conflicts": ["chungus-conflicts"],
            "Provides": ["chungus-provides<=200"],
            "Replaces": ["chungus-replaces<=200"],
268
            "License": [pkg.package_licenses.first().License.Name],
269
270
271
272
273
274
275
276
277
            "Keywords": [
                "big-chungus",
                "sizeable-chungus",
                "smol-chungus"
            ]
        }],
        "resultcount": 1,
        "type": "multiinfo"
    }
278
279

    # Make dummy request.
280
    with client as request:
281
282
283
284
285
        resp = request.get("/rpc", params={
            "v": 5,
            "type": "info",
            "arg": ["chungy-chungus", "big-chungus"],
        })
286
287

    # Load  request response into Python dictionary.
288
    response_data = orjson.loads(resp.text)
289
290
291
292
293

    # 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"]:
294
        response_data["results"][0].pop(i)
295
296

    # Validate that the new dictionaries are the same.
297
    assert response_data == expected_data
298
299


300
def test_rpc_nonexistent_package(client: TestClient):
301
    # Make dummy request.
302
303
    with client as request:
        response = request.get("/rpc/?v=5&type=info&arg=nonexistent-package")
304
305
306
307
308
309
310
311

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

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


312
def test_rpc_multiinfo(client: TestClient, packages: List[Package]):
313
314
    # Make dummy request.
    request_packages = ["big-chungus", "chungy-chungus"]
315
    with client as request:
316
317
318
        response = request.get("/rpc", params={
            "v": 5, "type": "info", "arg[]": request_packages
        })
319
320
321
322
323
324
325
326
327
328
329

    # 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 == []


330
def test_rpc_mixedargs(client: TestClient, packages: List[Package]):
331
332
333
334
    # Make dummy request.
    response1_packages = ["gluggly-chungus"]
    response2_packages = ["gluggly-chungus", "chungy-chungus"]

335
    with client as request:
336
        # Supply all of the args in the url to enforce ordering.
337
338
339
340
341
342
        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(
343
344
            "/rpc?v=5&arg=big-chungus&arg[]=gluggly-chungus"
            "&type=info&arg[]=chungy-chungus")
345
    assert response1.status_code == int(HTTPStatus.OK)
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361

    # 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 == []


362
363
364
365
366
367
368
369
370
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]
371
    expected_response = {
372
373
        'version': 5,
        'results': [{
374
375
376
377
378
379
380
            '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),
381
            'OutOfDate': None,
382
            'Maintainer': user.Username,
383
384
385
386
387
388
389
390
391
            'URLPath': '/cgit/aur.git/snapshot/chungy-chungus.tar.gz',
            'Depends': ['chungy-depends'],
            'Conflicts': ['chungy-conflicts'],
            'License': [],
            'Keywords': []
        }],
        'resultcount': 1,
        'type': 'multiinfo'
    }
392
393

    # Make dummy request.
394
    with client as request:
395
396
397
        response = request.get("/rpc", params={
            "v": 5, "type": "info", "arg": "chungy-chungus"
        })
398
399
400
401
402
403
404
405
406
    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


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

    # Make dummy request.
418
    with client as request:
419
420
421
        response = request.get("/rpc", params={
            "v": 5, "type": "invalid-type", "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_bad_version(client: TestClient):
431
    # Define expected response.
432
433
434
435
436
437
438
    expected_data = {
        'version': 0,
        'resultcount': 0,
        'results': [],
        'type': 'error',
        'error': 'Invalid version specified.'
    }
439
440

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

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

    # Validate data.
    assert expected_data == response_data


453
def test_rpc_no_version(client: TestClient):
454
    # Define expected response.
455
456
457
458
459
460
461
    expected_data = {
        'version': None,
        'resultcount': 0,
        'results': [],
        'type': 'error',
        'error': 'Please specify an API version.'
    }
462
463

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

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

    # Validate data.
    assert expected_data == response_data


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

    # Make dummy request.
488
    with client as request:
489
        response = request.get("/rpc", params={"v": 5, "arg": "big-chungus"})
490
491
492
493
494
495
496
497

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

    # Validate data.
    assert expected_data == response_data


498
def test_rpc_no_args(client: TestClient):
499
500
    # Define expected response.
    expected_data = {
501
502
503
504
505
506
        'version': 5,
        'results': [],
        'resultcount': 0,
        'type': 'error',
        'error': 'No request type/data specified.'
    }
507
508

    # Make dummy request.
509
    with client as request:
510
        response = request.get("/rpc", params={"v": 5, "type": "info"})
511
512
513
514
515
516
517
518

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

    # Validate data.
    assert expected_data == response_data


519
def test_rpc_no_maintainer(client: TestClient, packages: List[Package]):
520
    # Make dummy request.
521
    with client as request:
522
523
524
        response = request.get("/rpc", params={
            "v": 5, "type": "info", "arg": "woogly-chungus"
        })
525
526
527
528
529
530

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

    # Validate data.
    assert response_data["results"][0]["Maintainer"] is None
531
532


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

540
    params["arg"] = "chungy"
541
    with client as request:
542
        response = request.get("/rpc", params=params)
543
544
    data = response.json()
    assert data == ["chungy-chungus"]
545

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

553

554
555
def test_rpc_suggest(client: TestClient, packages: List[Package]):
    params = {"v": 5, "type": "suggest", "arg": "other"}
556
    with client as request:
557
        response = request.get("/rpc", params=params)
558
559
560
561
    data = response.json()
    assert data == ["other-pkg"]

    # Test non-existent Package.
562
    params["arg"] = "nonexistent"
563
    with client as request:
564
        response = request.get("/rpc", params=params)
565
566
567
    data = response.json()
    assert data == []

568
    # Test no arg supplied.
569
    del params["arg"]
570
    with client as request:
571
        response = request.get("/rpc", params=params)
572
573
574
    data = response.json()
    assert data == []

575

Kevin Morris's avatar
Kevin Morris committed
576
577
578
579
580
581
582
583
584
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)
585
def test_rpc_ratelimit(getint: mock.MagicMock, client: TestClient,
586
587
588
                       pipeline: Pipeline, packages: List[Package]):
    params = {"v": 5, "type": "suggest-pkgbase", "arg": "big"}

Kevin Morris's avatar
Kevin Morris committed
589
590
    for i in range(4):
        # The first 4 requests should be good.
591
        with client as request:
592
            response = request.get("/rpc", params=params)
Kevin Morris's avatar
Kevin Morris committed
593
594
595
        assert response.status_code == int(HTTPStatus.OK)

    # The fifth request should be banned.
596
    with client as request:
597
        response = request.get("/rpc", params=params)
Kevin Morris's avatar
Kevin Morris committed
598
599
600
601
602
603
604
605
606
    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.
607
    with client as request:
608
        response = request.get("/rpc", params=params)
Kevin Morris's avatar
Kevin Morris committed
609
    assert response.status_code == int(HTTPStatus.OK)
610
611


612
613
def test_rpc_etag(client: TestClient, packages: List[Package]):
    params = {"v": 5, "type": "suggest-pkgbase", "arg": "big"}
614
615

    with client as request:
616
617
618
619
        response1 = request.get("/rpc", params=params)
    with client as request:
        response2 = request.get("/rpc", params=params)

620
621
622
    assert response1.headers.get("ETag") is not None
    assert response1.headers.get("ETag") != str()
    assert response1.headers.get("ETag") == response2.headers.get("ETag")
623
624


625
def test_rpc_search_arg_too_small(client: TestClient):
626
    params = {"v": 5, "type": "search", "arg": "b"}
627
    with client as request:
628
        response = request.get("/rpc", params=params)
629
630
631
632
    assert response.status_code == int(HTTPStatus.OK)
    assert response.json().get("error") == "Query arg too small."


633
634
def test_rpc_search(client: TestClient, packages: List[Package]):
    params = {"v": 5, "type": "search", "arg": "big"}
635
    with client as request:
636
        response = request.get("/rpc", params=params)
637
638
639
640
641
642
    assert response.status_code == int(HTTPStatus.OK)

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

    result = data.get("results")[0]
643
    assert result.get("Name") == packages[0].Name
644

645
646
647
    # Test the If-None-Match headers.
    etag = response.headers.get("ETag").strip('"')
    headers = {"If-None-Match": etag}
648
    response = request.get("/rpc", params=params, headers=headers)
649
650
651
    assert response.status_code == int(HTTPStatus.NOT_MODIFIED)
    assert response.content == b''

652
    # No args on non-m by types return an error.
653
654
655
    del params["arg"]
    with client as request:
        response = request.get("/rpc", params=params)
656
657
658
    assert response.json().get("error") == "No request type/data specified."


659
660
def test_rpc_msearch(client: TestClient, user: User, packages: List[Package]):
    params = {"v": 5, "type": "msearch", "arg": user.Username}
661
    with client as request:
662
        response = request.get("/rpc", params=params)
663
664
665
666
667
    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")))
668
    expected_results = [
669
670
671
672
        "big-chungus",
        "chungy-chungus",
        "gluggly-chungus",
        "other-pkg"
673
    ]
674
675
676
    assert names == expected_results

    # Search for a non-existent maintainer, giving us zero packages.
677
678
    params["arg"] = "blah-blah"
    response = request.get("/rpc", params=params)
679
680
681
    data = response.json()
    assert data.get("resultcount") == 0

682
683
684
    with db.begin():
        packages[0].PackageBase.Maintainer = None

685
686
    # A missing arg still succeeds, but it returns all orphans.
    # Just verify that we receive no error and the orphaned result.
687
688
    params.pop("arg")
    response = request.get("/rpc", params=params)
689
690
691
    data = response.json()
    assert data.get("resultcount") == 1
    result = data.get("results")[0]
692
    assert result.get("Name") == "big-chungus"
693
694


695
696
697
698
699
def test_rpc_search_depends(client: TestClient, packages: List[Package],
                            depends: List[PackageDependency]):
    params = {
        "v": 5, "type": "search", "by": "depends", "arg": "chungus-depends"
    }
700
    with client as request:
701
        response = request.get("/rpc", params=params)
702
703
704
    data = response.json()
    assert data.get("resultcount") == 1
    result = data.get("results")[0]
705
    assert result.get("Name") == packages[0].Name
706
707


708
709
710
711
712
713
714
715
def test_rpc_search_makedepends(client: TestClient, packages: List[Package],
                                depends: List[PackageDependency]):
    params = {
        "v": 5,
        "type": "search",
        "by": "makedepends",
        "arg": "chungus-makedepends"
    }
716
    with client as request:
717
        response = request.get("/rpc", params=params)
718
719
720
    data = response.json()
    assert data.get("resultcount") == 1
    result = data.get("results")[0]
721
    assert result.get("Name") == packages[0].Name
722
723


724
725
726
727
728
729
730
731
def test_rpc_search_optdepends(client: TestClient, packages: List[Package],
                               depends: List[PackageDependency]):
    params = {
        "v": 5,
        "type": "search",
        "by": "optdepends",
        "arg": "chungus-optdepends"
    }
732
    with client as request:
733
        response = request.get("/rpc", params=params)
734
735
736
    data = response.json()
    assert data.get("resultcount") == 1
    result = data.get("results")[0]
737
    assert result.get("Name") == packages[0].Name
738
739


740
741
742
743
744
745
746
747
def test_rpc_search_checkdepends(client: TestClient, packages: List[Package],
                                 depends: List[PackageDependency]):
    params = {
        "v": 5,
        "type": "search",
        "by": "checkdepends",
        "arg": "chungus-checkdepends"
    }
748
    with client as request:
749
        response = request.get("/rpc", params=params)
750
751
752
    data = response.json()
    assert data.get("resultcount") == 1
    result = data.get("results")[0]
753
    assert result.get("Name") == packages[0].Name
754
755


756
def test_rpc_incorrect_by(client: TestClient):
757
    params = {"v": 5, "type": "search", "by": "fake", "arg": "big"}
758
    with client as request:
759
        response = request.get("/rpc", params=params)
760
    assert response.json().get("error") == "Incorrect by field specified."
761
762


763
def test_rpc_jsonp_callback(client: TestClient):
764
765
766
767
768
    """ 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.
    """
769
770
771
772
773
774
    params = {
        "v": 5,
        "type": "search",
        "arg": "big",
        "callback": "jsonCallback"
    }
775
    with client as request:
776
        response = request.get("/rpc", params=params)
777
778
    assert response.headers.get("content-type") == "text/javascript"
    assert re.search(r'^/\*\*/jsonCallback\(.*\)$', response.text) is not None
779
780

    # Test an invalid callback name; we get an application/json error.
781
    params["callback"] = "jsonCallback!"
782
    with client as request:
783
        response = request.get("/rpc", params=params)
784
785
    assert response.headers.get("content-type") == "application/json"
    assert response.json().get("error") == "Invalid callback name."