Repository: ietf-tools/datatracker

Issue:
Submission status display is sometimes misleading about checker results
### Describe the issue

The submission status page shows any passed/failed checker results (i.e., `SubmissionCheck` instances) for the submission. If there are no results or if all the instances are passing results, this is shown as a green box with the statement `Your draft has been verified to pass the submission checks.` If any are not passing, the box is red and the statement indicates that a check failed.

If there are no check results, this is still shown with the message indicating that the draft has been verified to pass the submission checks. This is misleading. It would be better to indicate that the validations have not been run.

### Code of Conduct

- [X] I agree to follow the [IETF's Code of Conduct](https://github.com/ietf-tools/.github/blob/main/CODE_OF_CONDUCT.md)


Discussion:
Note that on the dev branch for asynchronous submission, the checker results are suppressed for drafts in the "validating" state. This avoids the worst of the effect, but if such a draft is canceled before the checkers are complete, the misleading impression becomes visible.

Resolving patch:
diff --git a/ietf/submit/models.py b/ietf/submit/models.py
--- a/ietf/submit/models.py
+++ b/ietf/submit/models.py
@@ -79,11 +79,18 @@ def existing_document(self):
         return Document.objects.filter(name=self.name).first()
 
     def latest_checks(self):
-        checks = [ self.checks.filter(checker=c).latest('time') for c in self.checks.values_list('checker', flat=True).distinct() ]
-        return checks
+        """Latest check of each type, excluding any that did not apply
+        
+        Ignores any checks where passed is None
+        """
+        latest_for_each_checker = [
+            self.checks.filter(checker=c).latest("time")
+            for c in self.checks.values_list("checker", flat=True).distinct()
+        ]
+        return [check for check in latest_for_each_checker if check.passed is not None]
 
     def has_yang(self):
-        return any ( [ c.checker=='yang validation' and c.passed is not None for c in self.latest_checks()] )
+        return any(c.checker == "yang validation" for c in self.latest_checks())
 
     @property
     def replaces_names(self):
diff --git a/ietf/submit/views.py b/ietf/submit/views.py
--- a/ietf/submit/views.py
+++ b/ietf/submit/views.py
@@ -321,7 +321,9 @@ def submission_status(request, submission_id, access_token=None):
         errors = {}
     else:
         errors = validate_submission(submission)
-    passes_checks = all([ c.passed!=False for c in submission.checks.all() ])
+    latest_checks = submission.latest_checks()
+    applied_any_checks = len(latest_checks) > 0
+    passes_checks = applied_any_checks and all(c.passed for c in latest_checks)
 
     is_secretariat = has_role(request.user, "Secretariat")
     is_chair = submission.group and submission.group.has_role(request.user, "chair")
@@ -547,6 +549,7 @@ def submission_status(request, submission_id, access_token=None):
         'selected': 'status',
         'submission': submission,
         'errors': errors,
+        'applied_any_checks': applied_any_checks,
         'passes_checks': passes_checks,
         'submitter_form': submitter_form,
         'replaces_form': replaces_form,


Test patch:
diff --git a/ietf/submit/tests.py b/ietf/submit/tests.py
--- a/ietf/submit/tests.py
+++ b/ietf/submit/tests.py
@@ -3808,3 +3808,54 @@ def test_submit_case_conflited_name_fails(self):
         files["xml"], _ = submission_file("draft-something-hascapitalletters-00", "draft-something-hascapitalletters-00.xml", None, "test_submission.xml")
         r = self.post_to_upload_submission(url, files)
         self.assertContains(r,"Case-conflicting draft name found",status_code=200)
+
+
+class SubmissionStatusTests(BaseSubmitTestCase):
+    """Tests of the submission_status view
+
+    Many tests are interspersed in the monolithic tests above. We can aspire to break these
+    out more modularly, though.
+    """
+
+    def test_submission_checks(self):
+        for state_slug in ("uploaded", "cancel", "posted"):
+            submission = SubmissionFactory(state_id=state_slug)
+            url = urlreverse(
+                "ietf.submit.views.submission_status",
+                kwargs={"submission_id": submission.pk},
+            )
+            # No checks
+            r = self.client.get(url)
+            self.assertContains(
+                r,
+                "No submission checks were applied to your Internet-Draft.",
+                status_code=200,
+            )
+            # Inapplicable check
+            submission.checks.create(
+                checker="yang validation", passed=None, message="Yang message"
+            )
+            r = self.client.get(url)
+            self.assertContains(
+                r,
+                "No submission checks were applied to your Internet-Draft.",
+                status_code=200,
+            )
+            # Passed check
+            submission.checks.create(
+                checker="idnits check", passed=True, message="idnits ok"
+            )
+            r = self.client.get(url)
+            self.assertContains(
+                r,
+                "Your Internet-Draft has been verified to pass the submission checks.",
+                status_code=200,
+            )
+            # Failed check + passed check
+            submission.checks.filter(checker="yang validation").update(passed=False)
+            r = self.client.get(url)
+            self.assertContains(
+                r,
+                "Your Internet-Draft failed at least one submission check.",
+                status_code=200,
+            )