Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] Add patch totals to the /pulls endpoint #1108

Open
wants to merge 10 commits into
base: main
Choose a base branch
from

Conversation

rohitvinnakota-codecov
Copy link
Contributor

@rohitvinnakota-codecov rohitvinnakota-codecov commented Jan 27, 2025

Purpose/Motivation

Addressing https://github.com/codecov/internal-issues/issues/1144

The diff field is deprecated due to its flaky responses so we are going to add a patch field for changes in the patch coverage of a pull request

Screenshot 2025-01-28 at 9 12 59 AM

Legal Boilerplate

Look, I get it. The entity doing business as "Sentry" was incorporated in the State of Delaware in 2015 as Functional Software, Inc. In 2022 this entity acquired Codecov and as result Sentry is going to need some rights from me in order to utilize my contributions in this PR. So here's the deal: I retain all rights, title and interest in and to my contributions, and by keeping this boilerplate intact I confirm that Sentry can use, modify, copy, and redistribute my contributions, under Sentry's choice of terms.

@codecov-notifications
Copy link

codecov-notifications bot commented Jan 27, 2025

Codecov Report

Attention: Patch coverage is 96.87500% with 1 line in your changes missing coverage. Please review.

✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
api/public/v2/pull/serializers.py 95.45% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@codecov-qa
Copy link

codecov-qa bot commented Jan 27, 2025

Codecov Report

Attention: Patch coverage is 96.87500% with 1 line in your changes missing coverage. Please review.

Project coverage is 95.98%. Comparing base (e06c5d8) to head (9ed8723).
Report is 5 commits behind head on main.

✅ All tests successful. No failed tests found.

Copy link

codecov-public-qa bot commented Jan 27, 2025

❌ 8 Tests Failed:

Tests completed Failed Passed Skipped
2718 8 2710 6
View the top 3 failed tests by shortest run time
api/public/v2/tests/test_api_pull_viewset.py::PullViewsetTests::test_retrieve_with_patch_coverag_no_branches
Stack Traces | 0.089s run time
.../local/lib/python3.12/unittest/mock.py:1393: in patched
    with self.decoration_helper(patched,
.../local/lib/python3.12/contextlib.py:137: in __enter__
    return next(self.gen)
.../local/lib/python3.12/unittest/mock.py:1375: in decoration_helper
    arg = exit_stack.enter_context(patching)
.../local/lib/python3.12/contextlib.py:526: in enter_context
    result = _enter(cm)
.../local/lib/python3.12/unittest/mock.py:1451: in __enter__
    self.target = self.getter()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

name = 'api.public.v2.pull.serializers.CommitComparison.objects'

    def resolve_name(name):
        """
        Resolve a name to an object.
    
        It is expected that `name` will be a string in one of the following
        formats, where W is shorthand for a valid Python identifier and dot stands
        for a literal period in these pseudo-regexes:
    
        W(.W)*
        W(.W)*:(W(.W)*)?
    
        The first form is intended for backward compatibility only. It assumes that
        some part of the dotted name is a package, and the rest is an object
        somewhere within that package, possibly nested inside other objects.
        Because the place where the package stops and the object hierarchy starts
        can't be inferred by inspection, repeated attempts to import must be done
        with this form.
    
        In the second form, the caller makes the division point clear through the
        provision of a single colon: the dotted name to the left of the colon is a
        package to be imported, and the dotted name to the right is the object
        hierarchy within that package. Only one import is needed in this form. If
        it ends with the colon, then a module object is returned.
    
        The function will return an object (which might be a module), or raise one
        of the following exceptions:
    
        ValueError - if `name` isn't in a recognised format
        ImportError - if an import failed when it shouldn't have
        AttributeError - if a failure occurred when traversing the object hierarchy
                         within the imported package to get to the desired object.
        """
        global _NAME_PATTERN
        if _NAME_PATTERN is None:
            # Lazy import to speedup Python startup time
            import re
            dotted_words = r'(?!\d)(\w+)(\.(?!\d)(\w+))*'
            _NAME_PATTERN = re.compile(f'^(?P<pkg>{dotted_words})'
                                       f'(?P<cln>:(?P<obj>{dotted_words})?)?$',
                                       re.UNICODE)
    
        m = _NAME_PATTERN.match(name)
        if not m:
            raise ValueError(f'invalid format: {name!r}')
        gd = m.groupdict()
        if gd.get('cln'):
            # there is a colon - a one-step import is all that's needed
            mod = importlib.import_module(gd['pkg'])
            parts = gd.get('obj')
            parts = parts.split('.') if parts else []
        else:
            # no colon - have to iterate to find the package boundary
            parts = name.split('.')
            modname = parts.pop(0)
            # first part *must* be a module/package.
            mod = importlib.import_module(modname)
            while parts:
                p = parts[0]
                s = f'{modname}.{p}'
                try:
                    mod = importlib.import_module(s)
                    parts.pop(0)
                    modname = s
                except ImportError:
                    break
        # if we reach this point, mod is the module, already imported, and
        # parts is the list of parts in the object hierarchy to be traversed, or
        # an empty list if just the module is wanted.
        result = mod
        for p in parts:
>           result = getattr(result, p)
E           AttributeError: module 'api.public.v2.pull.serializers' has no attribute 'CommitComparison'. Did you mean: 'CommitComparisonService'?

.../local/lib/python3.12/pkgutil.py:528: AttributeError
api/public/v2/tests/test_api_pull_viewset.py::PullViewsetTests::test_list_cursor_pagination
Stack Traces | 0.091s run time
self = <test_api_pull_viewset.PullViewsetTests testMethod=test_list_cursor_pagination>

    def test_list_cursor_pagination(self):
        url = reverse(
            "api-v2-pulls-list",
            kwargs={
                "service": self.org.service,
                "owner_username": self.org.username,
                "repo_name": self.repo.name,
            },
        )
        res = self.client.get(f"{url}?page_size=1&cursor=")
        assert res.status_code == 200
        data = res.json()
>       assert data["results"] == [
            {
                "pullid": self.pulls[1].pullid,
                "title": self.pulls[1].title,
                "base_totals": None,
                "head_totals": None,
                "updatestamp": "2023-01-01T00:00:00Z",
                "state": "open",
                "ci_passed": None,
                "author": None,
                "patch": self.no_patch_response,
            }
        ]
E       AssertionError: assert [{'author': N...': None, ...}] == [{'author': N...': None, ...}]
E         
E         At index 0 diff: {'pullid': 95, 'title': 'Officer will system though important nature clear.', 'base_totals': None, 'head_totals': None, 'updatestamp': '2023-01-01T00:00:00Z', 'state': 'open', 'ci_passed': None, 'author': None, 'patch': None} != {'pullid': 95, 'title': 'Officer will system though important nature clear.', 'base_totals': None, 'head_totals': None, 'updatestamp': '2023-01-01T00:00:00Z', 'state': 'open', 'ci_passed': None, 'author': None, 'patch': {'hits': 0, 'misses': 0, 'partials': 0, 'coverage': 0}}
E         Use -v to get more diff

.../v2/tests/test_api_pull_viewset.py:155: AssertionError
api/public/v2/tests/test_api_pull_viewset.py::PullViewsetTests::test_retrieve_with_patch_coverage
Stack Traces | 0.099s run time
.../local/lib/python3.12/unittest/mock.py:1393: in patched
    with self.decoration_helper(patched,
.../local/lib/python3.12/contextlib.py:137: in __enter__
    return next(self.gen)
.../local/lib/python3.12/unittest/mock.py:1375: in decoration_helper
    arg = exit_stack.enter_context(patching)
.../local/lib/python3.12/contextlib.py:526: in enter_context
    result = _enter(cm)
.../local/lib/python3.12/unittest/mock.py:1451: in __enter__
    self.target = self.getter()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

name = 'api.public.v2.pull.serializers.CommitComparison.objects'

    def resolve_name(name):
        """
        Resolve a name to an object.
    
        It is expected that `name` will be a string in one of the following
        formats, where W is shorthand for a valid Python identifier and dot stands
        for a literal period in these pseudo-regexes:
    
        W(.W)*
        W(.W)*:(W(.W)*)?
    
        The first form is intended for backward compatibility only. It assumes that
        some part of the dotted name is a package, and the rest is an object
        somewhere within that package, possibly nested inside other objects.
        Because the place where the package stops and the object hierarchy starts
        can't be inferred by inspection, repeated attempts to import must be done
        with this form.
    
        In the second form, the caller makes the division point clear through the
        provision of a single colon: the dotted name to the left of the colon is a
        package to be imported, and the dotted name to the right is the object
        hierarchy within that package. Only one import is needed in this form. If
        it ends with the colon, then a module object is returned.
    
        The function will return an object (which might be a module), or raise one
        of the following exceptions:
    
        ValueError - if `name` isn't in a recognised format
        ImportError - if an import failed when it shouldn't have
        AttributeError - if a failure occurred when traversing the object hierarchy
                         within the imported package to get to the desired object.
        """
        global _NAME_PATTERN
        if _NAME_PATTERN is None:
            # Lazy import to speedup Python startup time
            import re
            dotted_words = r'(?!\d)(\w+)(\.(?!\d)(\w+))*'
            _NAME_PATTERN = re.compile(f'^(?P<pkg>{dotted_words})'
                                       f'(?P<cln>:(?P<obj>{dotted_words})?)?$',
                                       re.UNICODE)
    
        m = _NAME_PATTERN.match(name)
        if not m:
            raise ValueError(f'invalid format: {name!r}')
        gd = m.groupdict()
        if gd.get('cln'):
            # there is a colon - a one-step import is all that's needed
            mod = importlib.import_module(gd['pkg'])
            parts = gd.get('obj')
            parts = parts.split('.') if parts else []
        else:
            # no colon - have to iterate to find the package boundary
            parts = name.split('.')
            modname = parts.pop(0)
            # first part *must* be a module/package.
            mod = importlib.import_module(modname)
            while parts:
                p = parts[0]
                s = f'{modname}.{p}'
                try:
                    mod = importlib.import_module(s)
                    parts.pop(0)
                    modname = s
                except ImportError:
                    break
        # if we reach this point, mod is the module, already imported, and
        # parts is the list of parts in the object hierarchy to be traversed, or
        # an empty list if just the module is wanted.
        result = mod
        for p in parts:
>           result = getattr(result, p)
E           AttributeError: module 'api.public.v2.pull.serializers' has no attribute 'CommitComparison'. Did you mean: 'CommitComparisonService'?

.../local/lib/python3.12/pkgutil.py:528: AttributeError

To view more test analytics, go to the Test Analytics Dashboard
📢 Thoughts on this report? Let us know!

Copy link
Contributor

github-actions bot commented Jan 27, 2025

✅ All tests successful. No failed tests were found.

📣 Thoughts on this report? Let Codecov know! | Powered by Codecov

Copy link

codecov bot commented Jan 28, 2025

Codecov Report

Attention: Patch coverage is 96.87500% with 1 line in your changes missing coverage. Please review.

Project coverage is 96.21%. Comparing base (e06c5d8) to head (9ed8723).
Report is 5 commits behind head on main.

✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
api/public/v2/pull/serializers.py 95.45% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1108      +/-   ##
==========================================
+ Coverage   96.11%   96.21%   +0.09%     
==========================================
  Files         835      837       +2     
  Lines       19572    20201     +629     
==========================================
+ Hits        18812    19436     +624     
- Misses        760      765       +5     
Flag Coverage Δ
unit 95.98% <96.87%> (-0.05%) ⬇️
unit-latest-uploader 95.98% <96.87%> (-0.05%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@rohitvinnakota-codecov rohitvinnakota-codecov marked this pull request as ready for review January 28, 2025 15:21
api/public/v2/pull/serializers.py Outdated Show resolved Hide resolved
api/public/v2/pull/serializers.py Outdated Show resolved Hide resolved
api/public/v2/pull/serializers.py Outdated Show resolved Hide resolved
api/public/v2/pull/serializers.py Outdated Show resolved Hide resolved
Copy link
Contributor

@JerrySentry JerrySentry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm, just missing a test for the uncovered line :P

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants