Пример #1
0
    def test_vulnerabilities_renders(self, pyramid_config, db_request):
        project = ProjectFactory.create(has_docs=False)
        release = ReleaseFactory.create(project=project, version="0.1")
        VulnerabilityRecordFactory.create(
            id="PYSEC-001",
            source="the source",
            link="the link",
            aliases=["alias1", "alias2"],
            details="some details",
            fixed_in=["3.3.2"],
            releases=[release],
        )

        url = "/the/fake/url/"
        db_request.route_url = pretend.call_recorder(lambda *args, **kw: url)

        result = json.json_release(release, db_request)

        assert result["vulnerabilities"] == [
            {
                "id": "PYSEC-001",
                "source": "the source",
                "link": "the link",
                "aliases": ["alias1", "alias2"],
                "details": "some details",
                "fixed_in": ["3.3.2"],
            },
        ]
Пример #2
0
    def test_normalizing_redirects(self, db_request):
        project = ProjectFactory.create()
        release = ReleaseFactory.create(project=project, version="3.0")

        name = release.project.name.lower()
        if name == release.project.name:
            name = release.project.name.upper()

        db_request.matchdict = {"name": name}
        db_request.current_route_path = pretend.call_recorder(
            lambda name: "/project/the-redirect/3.0/")

        resp = json.json_release(release, db_request)

        assert isinstance(resp, HTTPMovedPermanently)
        assert resp.headers["Location"] == "/project/the-redirect/3.0/"
        assert db_request.current_route_path.calls == [
            pretend.call(name=release.project.name),
        ]
Пример #3
0
    def test_normalizing_redirects(self, db_request):
        project = ProjectFactory.create()
        release = ReleaseFactory.create(project=project, version="3.0")

        name = release.project.name.lower()
        if name == release.project.name:
            name = release.project.name.upper()

        db_request.matchdict = {"name": name}
        db_request.current_route_path = pretend.call_recorder(
            lambda name: "/project/the-redirect/3.0/"
        )

        resp = json.json_release(release, db_request)

        assert isinstance(resp, HTTPMovedPermanently)
        assert resp.headers["Location"] == "/project/the-redirect/3.0/"
        assert db_request.current_route_path.calls == [
            pretend.call(name=release.project.name),
        ]
Пример #4
0
    def test_minimal_renders(self, pyramid_config, db_request):
        project = ProjectFactory.create(has_docs=False)
        release = ReleaseFactory.create(project=project, version="0.1")
        file = FileFactory.create(
            release=release,
            filename="{}-{}.tar.gz".format(project.name, release.version),
            python_version="source",
            size=200,
            has_signature=True,
        )

        user = UserFactory.create()
        JournalEntryFactory.reset_sequence()
        je = JournalEntryFactory.create(name=project.name, submitted_by=user)

        url = "/the/fake/url/"
        db_request.route_url = pretend.call_recorder(lambda *args, **kw: url)

        result = json.json_release(release, db_request)

        assert set(db_request.route_url.calls) == {
            pretend.call("packaging.file", path=file.path),
            pretend.call("packaging.project", name=project.name),
            pretend.call("packaging.release",
                         name=project.name,
                         version=release.version),
        }

        _assert_has_cors_headers(db_request.response.headers)
        assert db_request.response.headers["X-PyPI-Last-Serial"] == str(je.id)

        assert result == {
            "info": {
                "author": None,
                "author_email": None,
                "bugtrack_url": None,
                "classifiers": [],
                "description_content_type": None,
                "description": None,
                "docs_url": None,
                "download_url": None,
                "downloads": {
                    "last_day": -1,
                    "last_week": -1,
                    "last_month": -1
                },
                "home_page": None,
                "keywords": None,
                "license": None,
                "maintainer": None,
                "maintainer_email": None,
                "name": project.name,
                "platform": None,
                "project_url": "/the/fake/url/",
                "package_url": "/the/fake/url/",
                "release_url": "/the/fake/url/",
                "requires_dist": None,
                "requires_python": None,
                "summary": None,
                "version": "0.1",
            },
            "releases": {
                "0.1": [{
                    "comment_text":
                    None,
                    "downloads":
                    -1,
                    "filename":
                    file.filename,
                    "has_sig":
                    True,
                    "md5_digest":
                    file.md5_digest,
                    "digests": {
                        "md5": file.md5_digest,
                        "sha256": file.sha256_digest,
                    },
                    "packagetype":
                    None,
                    "python_version":
                    "source",
                    "size":
                    200,
                    "upload_time":
                    file.upload_time.strftime("%Y-%m-%dT%H:%M:%S"),
                    "url":
                    "/the/fake/url/",
                }]
            },
            "urls": [{
                "comment_text":
                None,
                "downloads":
                -1,
                "filename":
                file.filename,
                "has_sig":
                True,
                "md5_digest":
                file.md5_digest,
                "digests": {
                    "md5": file.md5_digest,
                    "sha256": file.sha256_digest
                },
                "packagetype":
                None,
                "python_version":
                "source",
                "size":
                200,
                "upload_time":
                file.upload_time.strftime("%Y-%m-%dT%H:%M:%S"),
                "url":
                "/the/fake/url/",
            }],
            "last_serial":
            je.id,
        }
Пример #5
0
    def test_minimal_renders(self, pyramid_config, db_request):
        project = ProjectFactory.create(has_docs=False)
        release = ReleaseFactory.create(project=project, version="0.1")
        file = FileFactory.create(
            release=release,
            filename="{}-{}.tar.gz".format(project.name, release.version),
            python_version="source",
            size=200,
            has_signature=True,
        )

        user = UserFactory.create()
        JournalEntryFactory.reset_sequence()
        je = JournalEntryFactory.create(name=project.name, submitted_by=user)

        url = "/the/fake/url/"
        db_request.route_url = pretend.call_recorder(lambda *args, **kw: url)

        result = json.json_release(release, db_request)

        assert set(db_request.route_url.calls) == {
            pretend.call("packaging.file", path=file.path),
            pretend.call("packaging.project", name=project.name),
            pretend.call(
                "packaging.release", name=project.name, version=release.version
            ),
        }

        _assert_has_cors_headers(db_request.response.headers)
        assert db_request.response.headers["X-PyPI-Last-Serial"] == str(je.id)

        assert result == {
            "info": {
                "author": None,
                "author_email": None,
                "bugtrack_url": None,
                "classifiers": [],
                "description_content_type": release.description.content_type,
                "description": release.description.raw,
                "docs_url": None,
                "download_url": None,
                "downloads": {"last_day": -1, "last_week": -1, "last_month": -1},
                "home_page": None,
                "keywords": None,
                "license": None,
                "maintainer": None,
                "maintainer_email": None,
                "name": project.name,
                "package_url": "/the/fake/url/",
                "platform": None,
                "project_url": "/the/fake/url/",
                "project_urls": None,
                "release_url": "/the/fake/url/",
                "requires_dist": None,
                "requires_python": None,
                "summary": None,
                "version": "0.1",
            },
            "releases": {
                "0.1": [
                    {
                        "comment_text": None,
                        "downloads": -1,
                        "filename": file.filename,
                        "has_sig": True,
                        "md5_digest": file.md5_digest,
                        "digests": {
                            "md5": file.md5_digest,
                            "sha256": file.sha256_digest,
                        },
                        "packagetype": None,
                        "python_version": "source",
                        "size": 200,
                        "upload_time": file.upload_time.strftime("%Y-%m-%dT%H:%M:%S"),
                        "url": "/the/fake/url/",
                        "requires_python": None,
                    }
                ]
            },
            "urls": [
                {
                    "comment_text": None,
                    "downloads": -1,
                    "filename": file.filename,
                    "has_sig": True,
                    "md5_digest": file.md5_digest,
                    "digests": {"md5": file.md5_digest, "sha256": file.sha256_digest},
                    "packagetype": None,
                    "python_version": "source",
                    "size": 200,
                    "upload_time": file.upload_time.strftime("%Y-%m-%dT%H:%M:%S"),
                    "url": "/the/fake/url/",
                    "requires_python": None,
                }
            ],
            "last_serial": je.id,
        }
Пример #6
0
    def test_detail_renders(self, pyramid_config, db_request, db_session):
        project = ProjectFactory.create(has_docs=True)
        description_content_type = "text/x-rst"
        url = "/the/fake/url/"
        project_urls = [
            "url," + url,
            "Homepage,https://example.com/home2/",
            "Source Code,https://example.com/source-code/",
            "uri,http://[email protected]:123/forum/questions/?tag=networking&order=newest#top",  # noqa: E501
            "ldap,ldap://[2001:db8::7]/c=GB?objectClass?one",
            "tel,tel:+1-816-555-1212",
            "telnet,telnet://192.0.2.16:80/",
            "urn,urn:oasis:names:specification:docbook:dtd:xml:4.1.2",
            "reservedchars,http://example.com?&$+/:;=@#",  # Commas don't work!
            r"unsafechars,http://example.com <>[]{}|\^%",
        ]
        expected_urls = []
        for project_url in reversed(project_urls):
            expected_urls.append(tuple(project_url.split(",")))
        expected_urls = OrderedDict(tuple(expected_urls))

        releases = [
            ReleaseFactory.create(project=project, version=v)
            for v in ["0.1", "1.0", "2.0"]
        ]
        releases += [
            ReleaseFactory.create(
                project=project,
                version="3.0",
                description=DescriptionFactory.create(
                    content_type=description_content_type
                ),
            )
        ]

        for urlspec in project_urls:
            db_session.add(
                Dependency(
                    release=releases[3],
                    kind=DependencyKind.project_url.value,
                    specifier=urlspec,
                )
            )

        files = [
            FileFactory.create(
                release=r,
                filename="{}-{}.tar.gz".format(project.name, r.version),
                python_version="source",
                size=200,
                has_signature=True,
            )
            for r in releases[1:]
        ]
        user = UserFactory.create()
        JournalEntryFactory.reset_sequence()
        je = JournalEntryFactory.create(name=project.name, submitted_by=user)

        db_request.route_url = pretend.call_recorder(lambda *args, **kw: url)

        result = json.json_release(releases[3], db_request)

        assert set(db_request.route_url.calls) == {
            pretend.call("packaging.file", path=files[0].path),
            pretend.call("packaging.file", path=files[1].path),
            pretend.call("packaging.file", path=files[2].path),
            pretend.call("packaging.project", name=project.name),
            pretend.call(
                "packaging.release", name=project.name, version=releases[3].version
            ),
            pretend.call("legacy.docs", project=project.name),
        }

        _assert_has_cors_headers(db_request.response.headers)
        assert db_request.response.headers["X-PyPI-Last-Serial"] == str(je.id)

        assert result == {
            "info": {
                "author": None,
                "author_email": None,
                "bugtrack_url": None,
                "classifiers": [],
                "description_content_type": description_content_type,
                "description": releases[-1].description.raw,
                "docs_url": "/the/fake/url/",
                "download_url": None,
                "downloads": {"last_day": -1, "last_week": -1, "last_month": -1},
                "home_page": None,
                "keywords": None,
                "license": None,
                "maintainer": None,
                "maintainer_email": None,
                "name": project.name,
                "package_url": "/the/fake/url/",
                "platform": None,
                "project_url": "/the/fake/url/",
                "project_urls": expected_urls,
                "release_url": "/the/fake/url/",
                "requires_dist": None,
                "requires_python": None,
                "summary": None,
                "version": "3.0",
            },
            "releases": {
                "0.1": [],
                "1.0": [
                    {
                        "comment_text": None,
                        "downloads": -1,
                        "filename": files[0].filename,
                        "has_sig": True,
                        "md5_digest": files[0].md5_digest,
                        "digests": {
                            "md5": files[0].md5_digest,
                            "sha256": files[0].sha256_digest,
                        },
                        "packagetype": None,
                        "python_version": "source",
                        "size": 200,
                        "upload_time": files[0].upload_time.strftime(
                            "%Y-%m-%dT%H:%M:%S"
                        ),
                        "url": "/the/fake/url/",
                        "requires_python": None,
                    }
                ],
                "2.0": [
                    {
                        "comment_text": None,
                        "downloads": -1,
                        "filename": files[1].filename,
                        "has_sig": True,
                        "md5_digest": files[1].md5_digest,
                        "digests": {
                            "md5": files[1].md5_digest,
                            "sha256": files[1].sha256_digest,
                        },
                        "packagetype": None,
                        "python_version": "source",
                        "size": 200,
                        "upload_time": files[1].upload_time.strftime(
                            "%Y-%m-%dT%H:%M:%S"
                        ),
                        "url": "/the/fake/url/",
                        "requires_python": None,
                    }
                ],
                "3.0": [
                    {
                        "comment_text": None,
                        "downloads": -1,
                        "filename": files[2].filename,
                        "has_sig": True,
                        "md5_digest": files[2].md5_digest,
                        "digests": {
                            "md5": files[2].md5_digest,
                            "sha256": files[2].sha256_digest,
                        },
                        "packagetype": None,
                        "python_version": "source",
                        "size": 200,
                        "upload_time": files[2].upload_time.strftime(
                            "%Y-%m-%dT%H:%M:%S"
                        ),
                        "url": "/the/fake/url/",
                        "requires_python": None,
                    }
                ],
            },
            "urls": [
                {
                    "comment_text": None,
                    "downloads": -1,
                    "filename": files[2].filename,
                    "has_sig": True,
                    "md5_digest": files[2].md5_digest,
                    "digests": {
                        "md5": files[2].md5_digest,
                        "sha256": files[2].sha256_digest,
                    },
                    "packagetype": None,
                    "python_version": "source",
                    "size": 200,
                    "upload_time": files[2].upload_time.strftime("%Y-%m-%dT%H:%M:%S"),
                    "url": "/the/fake/url/",
                    "requires_python": None,
                }
            ],
            "last_serial": je.id,
        }
Пример #7
0
    def test_detail_renders(self, pyramid_config, db_request):
        project = ProjectFactory.create(has_docs=True)
        releases = [
            ReleaseFactory.create(project=project, version=v)
            for v in ["1.0", "2.0", "3.0"]
        ]
        files = [
            FileFactory.create(
                release=r,
                filename="{}-{}.tar.gz".format(project.name, r.version),
                python_version="source",
                size=200,
                has_signature=True,
            )
            for r in releases[:-1]
        ]
        user = UserFactory.create()
        je = JournalEntryFactory.create(
            name=project.name,
            submitted_by=user.username,
        )

        daily_stats = pretend.stub()
        weekly_stats = pretend.stub()
        monthly_stats = pretend.stub()

        db_request.find_service = lambda x: pretend.stub(
            get_daily_stats=lambda p: daily_stats,
            get_weekly_stats=lambda p: weekly_stats,
            get_monthly_stats=lambda p: monthly_stats,
        )

        url = "/the/fake/url/"
        db_request.route_url = pretend.call_recorder(lambda *args, **kw: url)

        result = json.json_release(releases[1], db_request)

        assert set(db_request.route_url.calls) == {
            pretend.call("packaging.file", path=files[0].path),
            pretend.call("packaging.file", path=files[1].path),
            pretend.call("packaging.project", name=project.name),
            pretend.call(
                "packaging.release",
                name=project.name,
                version=releases[1].version,
            ),
            pretend.call("legacy.docs", project=project.name),
        }

        headers = db_request.response.headers
        assert headers["Access-Control-Allow-Origin"] == "*"
        assert headers["X-PyPI-Last-Serial"] == je.id

        assert result == {
            "info": {
                "author": None,
                "author_email": None,
                "bugtrack_url": None,
                "classifiers": [],
                "description": None,
                "docs_url": "/the/fake/url/",
                "download_url": None,
                "downloads": {
                    "last_day": daily_stats,
                    "last_week": weekly_stats,
                    "last_month": monthly_stats,
                },
                "home_page": None,
                "keywords": None,
                "license": None,
                "maintainer": None,
                "maintainer_email": None,
                "name": project.name,
                "platform": None,
                "project_url": "/the/fake/url/",
                "release_url": "/the/fake/url/",
                "requires_python": None,
                "summary": None,
                "version": "2.0",
            },
            "releases": {
                "1.0": [
                    {
                        "comment_text": None,
                        "downloads": 0,
                        "filename": files[0].filename,
                        "has_sig": True,
                        "md5_digest": files[0].md5_digest,
                        "packagetype": None,
                        "python_version": "source",
                        "size": 200,
                        "upload_time": files[0].upload_time.strftime(
                            "%Y-%m-%dT%H:%M:%S",
                        ),
                        "url": "/the/fake/url/",
                    },
                ],
                "2.0": [
                    {
                        "comment_text": None,
                        "downloads": 0,
                        "filename": files[1].filename,
                        "has_sig": True,
                        "md5_digest": files[1].md5_digest,
                        "packagetype": None,
                        "python_version": "source",
                        "size": 200,
                        "upload_time": files[1].upload_time.strftime(
                            "%Y-%m-%dT%H:%M:%S",
                        ),
                        "url": "/the/fake/url/",
                    },
                ],
                "3.0": [],
            },
            "urls": [
                {
                    "comment_text": None,
                    "downloads": 0,
                    "filename": files[1].filename,
                    "has_sig": True,
                    "md5_digest": files[1].md5_digest,
                    "packagetype": None,
                    "python_version": "source",
                    "size": 200,
                    "upload_time": files[1].upload_time.strftime(
                        "%Y-%m-%dT%H:%M:%S",
                    ),
                    "url": "/the/fake/url/",
                },
            ],
        }
Пример #8
0
    def test_detail_renders(self, pyramid_config, db_request, db_session):
        project = ProjectFactory.create(has_docs=True)
        description_content_type = "text/x-rst"
        url = "/the/fake/url/"
        project_urls = [
            "url," + url,
            "Homepage,https://example.com/home2/",
            "Source Code,https://example.com/source-code/",
            "uri,http://[email protected]:123/forum/questions/?tag=networking&order=newest#top",  # noqa: E501
            "ldap,ldap://[2001:db8::7]/c=GB?objectClass?one",
            "tel,tel:+1-816-555-1212",
            "telnet,telnet://192.0.2.16:80/",
            "urn,urn:oasis:names:specification:docbook:dtd:xml:4.1.2",
            "reservedchars,http://example.com?&$+/:;=@#",  # Commas don't work!
            r"unsafechars,http://example.com <>[]{}|\^%",
        ]
        expected_urls = []
        for project_url in reversed(project_urls):
            expected_urls.append(tuple(project_url.split(",")))
        expected_urls = OrderedDict(tuple(expected_urls))

        releases = [
            ReleaseFactory.create(project=project, version=v)
            for v in ["0.1", "1.0", "2.0"]
        ]
        releases += [
            ReleaseFactory.create(
                project=project,
                version="3.0",
                description_content_type=description_content_type,
            )
        ]

        for urlspec in project_urls:
            db_session.add(
                Dependency(
                    release=releases[3],
                    kind=DependencyKind.project_url.value,
                    specifier=urlspec,
                ))

        files = [
            FileFactory.create(
                release=r,
                filename="{}-{}.tar.gz".format(project.name, r.version),
                python_version="source",
                size=200,
                has_signature=True,
            ) for r in releases[1:]
        ]
        user = UserFactory.create()
        JournalEntryFactory.reset_sequence()
        je = JournalEntryFactory.create(name=project.name, submitted_by=user)

        db_request.route_url = pretend.call_recorder(lambda *args, **kw: url)

        result = json.json_release(releases[3], db_request)

        assert set(db_request.route_url.calls) == {
            pretend.call("packaging.file", path=files[0].path),
            pretend.call("packaging.file", path=files[1].path),
            pretend.call("packaging.file", path=files[2].path),
            pretend.call("packaging.project", name=project.name),
            pretend.call("packaging.release",
                         name=project.name,
                         version=releases[3].version),
            pretend.call("legacy.docs", project=project.name),
        }

        _assert_has_cors_headers(db_request.response.headers)
        assert db_request.response.headers["X-PyPI-Last-Serial"] == str(je.id)

        assert result == {
            "info": {
                "author": None,
                "author_email": None,
                "bugtrack_url": None,
                "classifiers": [],
                "description_content_type": description_content_type,
                "description": None,
                "docs_url": "/the/fake/url/",
                "download_url": None,
                "downloads": {
                    "last_day": -1,
                    "last_week": -1,
                    "last_month": -1
                },
                "home_page": None,
                "keywords": None,
                "license": None,
                "maintainer": None,
                "maintainer_email": None,
                "name": project.name,
                "package_url": "/the/fake/url/",
                "platform": None,
                "project_url": "/the/fake/url/",
                "project_urls": expected_urls,
                "release_url": "/the/fake/url/",
                "requires_dist": None,
                "requires_python": None,
                "summary": None,
                "version": "3.0",
            },
            "releases": {
                "0.1": [],
                "1.0": [{
                    "comment_text":
                    None,
                    "downloads":
                    -1,
                    "filename":
                    files[0].filename,
                    "has_sig":
                    True,
                    "md5_digest":
                    files[0].md5_digest,
                    "digests": {
                        "md5": files[0].md5_digest,
                        "sha256": files[0].sha256_digest,
                    },
                    "packagetype":
                    None,
                    "python_version":
                    "source",
                    "size":
                    200,
                    "upload_time":
                    files[0].upload_time.strftime("%Y-%m-%dT%H:%M:%S"),
                    "url":
                    "/the/fake/url/",
                    "requires_python":
                    None,
                }],
                "2.0": [{
                    "comment_text":
                    None,
                    "downloads":
                    -1,
                    "filename":
                    files[1].filename,
                    "has_sig":
                    True,
                    "md5_digest":
                    files[1].md5_digest,
                    "digests": {
                        "md5": files[1].md5_digest,
                        "sha256": files[1].sha256_digest,
                    },
                    "packagetype":
                    None,
                    "python_version":
                    "source",
                    "size":
                    200,
                    "upload_time":
                    files[1].upload_time.strftime("%Y-%m-%dT%H:%M:%S"),
                    "url":
                    "/the/fake/url/",
                    "requires_python":
                    None,
                }],
                "3.0": [{
                    "comment_text":
                    None,
                    "downloads":
                    -1,
                    "filename":
                    files[2].filename,
                    "has_sig":
                    True,
                    "md5_digest":
                    files[2].md5_digest,
                    "digests": {
                        "md5": files[2].md5_digest,
                        "sha256": files[2].sha256_digest,
                    },
                    "packagetype":
                    None,
                    "python_version":
                    "source",
                    "size":
                    200,
                    "upload_time":
                    files[2].upload_time.strftime("%Y-%m-%dT%H:%M:%S"),
                    "url":
                    "/the/fake/url/",
                    "requires_python":
                    None,
                }],
            },
            "urls": [{
                "comment_text":
                None,
                "downloads":
                -1,
                "filename":
                files[2].filename,
                "has_sig":
                True,
                "md5_digest":
                files[2].md5_digest,
                "digests": {
                    "md5": files[2].md5_digest,
                    "sha256": files[2].sha256_digest,
                },
                "packagetype":
                None,
                "python_version":
                "source",
                "size":
                200,
                "upload_time":
                files[2].upload_time.strftime("%Y-%m-%dT%H:%M:%S"),
                "url":
                "/the/fake/url/",
                "requires_python":
                None,
            }],
            "last_serial":
            je.id,
        }
Пример #9
0
    def test_detail_renders(self, pyramid_config, db_request):
        project = ProjectFactory.create(has_docs=True)
        releases = [
            ReleaseFactory.create(project=project, version=v)
            for v in ["1.0", "2.0", "3.0"]
        ]
        files = [
            FileFactory.create(
                release=r,
                filename="{}-{}.tar.gz".format(project.name, r.version),
                python_version="source",
                size=200,
                has_signature=True,
            ) for r in releases[:-1]
        ]
        user = UserFactory.create()
        JournalEntryFactory.reset_sequence()
        je = JournalEntryFactory.create(
            name=project.name,
            submitted_by=user,
        )

        daily_stats = pretend.stub()
        weekly_stats = pretend.stub()
        monthly_stats = pretend.stub()

        db_request.find_service = lambda x: pretend.stub(
            get_daily_stats=lambda p: daily_stats,
            get_weekly_stats=lambda p: weekly_stats,
            get_monthly_stats=lambda p: monthly_stats,
        )

        url = "/the/fake/url/"
        db_request.route_url = pretend.call_recorder(lambda *args, **kw: url)

        result = json.json_release(releases[1], db_request)

        assert set(db_request.route_url.calls) == {
            pretend.call("packaging.file", path=files[0].path),
            pretend.call("packaging.file", path=files[1].path),
            pretend.call("packaging.project", name=project.name),
            pretend.call(
                "packaging.release",
                name=project.name,
                version=releases[1].version,
            ),
            pretend.call("legacy.docs", project=project.name),
        }

        headers = db_request.response.headers
        assert headers["Access-Control-Allow-Origin"] == "*"
        assert headers["Access-Control-Allow-Headers"] == (
            "Content-Type, If-Match, If-Modified-Since, If-None-Match, "
            "If-Unmodified-Since")
        assert headers["Access-Control-Allow-Methods"] == "GET"
        assert headers["Access-Control-Max-Age"] == "86400"
        assert headers["Access-Control-Expose-Headers"] == "X-PyPI-Last-Serial"
        assert headers["X-PyPI-Last-Serial"] == str(je.id)

        assert result == {
            "info": {
                "author": None,
                "author_email": None,
                "bugtrack_url": None,
                "classifiers": [],
                "description": None,
                "docs_url": "/the/fake/url/",
                "download_url": None,
                "downloads": {
                    "last_day": daily_stats,
                    "last_week": weekly_stats,
                    "last_month": monthly_stats,
                },
                "home_page": None,
                "keywords": None,
                "license": None,
                "maintainer": None,
                "maintainer_email": None,
                "name": project.name,
                "platform": None,
                "project_url": "/the/fake/url/",
                "release_url": "/the/fake/url/",
                "requires_dist": [],
                "requires_python": None,
                "summary": None,
                "version": "2.0",
            },
            "releases": {
                "1.0": [
                    {
                        "comment_text":
                        None,
                        "downloads":
                        -1,
                        "filename":
                        files[0].filename,
                        "has_sig":
                        True,
                        "md5_digest":
                        files[0].md5_digest,
                        "digests": {
                            "md5": files[0].md5_digest,
                            "sha256": files[0].sha256_digest,
                        },
                        "packagetype":
                        None,
                        "python_version":
                        "source",
                        "size":
                        200,
                        "upload_time":
                        files[0].upload_time.strftime("%Y-%m-%dT%H:%M:%S", ),
                        "url":
                        "/the/fake/url/",
                    },
                ],
                "2.0": [
                    {
                        "comment_text":
                        None,
                        "downloads":
                        -1,
                        "filename":
                        files[1].filename,
                        "has_sig":
                        True,
                        "md5_digest":
                        files[1].md5_digest,
                        "digests": {
                            "md5": files[1].md5_digest,
                            "sha256": files[1].sha256_digest,
                        },
                        "packagetype":
                        None,
                        "python_version":
                        "source",
                        "size":
                        200,
                        "upload_time":
                        files[1].upload_time.strftime("%Y-%m-%dT%H:%M:%S", ),
                        "url":
                        "/the/fake/url/",
                    },
                ],
                "3.0": [],
            },
            "urls": [
                {
                    "comment_text":
                    None,
                    "downloads":
                    -1,
                    "filename":
                    files[1].filename,
                    "has_sig":
                    True,
                    "md5_digest":
                    files[1].md5_digest,
                    "digests": {
                        "md5": files[1].md5_digest,
                        "sha256": files[1].sha256_digest,
                    },
                    "packagetype":
                    None,
                    "python_version":
                    "source",
                    "size":
                    200,
                    "upload_time":
                    files[1].upload_time.strftime("%Y-%m-%dT%H:%M:%S", ),
                    "url":
                    "/the/fake/url/",
                },
            ],
        }