Repository: conbench/conbench (language: python)

Issue:
benchmark result page: error when commit is None
When accessing `<conbench>/benchmark-results/<id>/`:
```
unexpected exception, please report this:
Traceback (most recent call last):
  File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 1823, in full_dispatch_request
    rv = self.dispatch_request()
         ^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 1799, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/flask/views.py", line 107, in view
    return current_app.ensure_sync(self.dispatch_request)(**kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/flask/views.py", line 188, in dispatch_request
    return current_app.ensure_sync(meth)(**kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/conbench/app/_endpoint.py", line 28, in wrapper
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/app/conbench/app/results.py", line 292, in get
    return self.page(
           ^^^^^^^^^^
  File "/app/conbench/app/results.py", line 263, in page
    return self.render_template(
           ^^^^^^^^^^^^^^^^^^^^^
  File "/app/conbench/app/_endpoint.py", line 135, in render_template
    return f.render_template(template, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/flask/templating.py", line 147, in render_template
    return _render(app, template, context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/flask/templating.py", line 130, in _render
    rv = template.render(context)
         ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/jinja2/environment.py", line 1301, in render
    self.environment.handle_exception()
  File "/usr/local/lib/python3.11/site-packages/jinja2/environment.py", line 936, in handle_exception
    raise rewrite_traceback_stack(source=source)
  File "/app/conbench/templates/benchmark-result.html", line 2, in top-level template code
    {% import 'bootstrap/wtf.html' as wtf %}
    ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/conbench/templates/app.html", line 1, in top-level template code
    {% block doc -%}
  File "/app/conbench/templates/app.html", line 4, in block 'doc'
    {%- block html %}
  File "/app/conbench/templates/app.html", line 39, in block 'html'
    {% block body -%}
  File "/app/conbench/templates/app.html", line 105, in block 'body'
    {% block content %}
  File "/app/conbench/templates/app.html", line 121, in block 'content'
    {% block app_content %}{% endblock %}
    ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/conbench/templates/benchmark-result.html", line 39, in block 'app_content'
    ({{ result.run.commit.timestamp.strftime("%Y-%m-%d") }})
    ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/jinja2/environment.py", line 485, in getattr
    return getattr(obj, attribute)
           ^^^^^^^^^^^^^^^^^^^^^^^
jinja2.exceptions.UndefinedError: 'None' has no attribute 'timestamp'
```

The associated Run of the benchmark result in question has `"commit": null`.


Discussion / hints:

Fix patch:
diff --git a/conbench/templates/benchmark-result.html b/conbench/templates/benchmark-result.html
index 40566f6ff..f0f058d6d 100644
--- a/conbench/templates/benchmark-result.html
+++ b/conbench/templates/benchmark-result.html
@@ -33,11 +33,15 @@ <h4>summary</h4>
         <div class="cb-bmr-summary-item">
           <strong>benchmarked code</strong>
           <br>
-          <code>
-            <a href="{{ result.run.commit.repo_url }}">{{ result.run.commit.repo_url }}</a>,
-            commit {{ result.ui_commit_url_anchor() |safe }}
-            ({{ result.run.commit.timestamp.strftime("%Y-%m-%d") }})
-          </code>
+          {% if result.run.commit is not none %}
+            <code>
+              <a href="{{ result.run.commit.repo_url }}">{{ result.run.commit.repo_url }}</a>,
+              commit {{ result.ui_commit_url_anchor() |safe }}
+              ({{ result.run.commit.timestamp.strftime("%Y-%m-%d") }})
+            </code>
+          {% else %}
+            commit info unavailable
+          {% endif %}
         </div>
         <div class="cb-bmr-summary-item">
           <strong>hardware name</strong>


Test patch:
diff --git a/conbench/tests/app/test_results.py b/conbench/tests/app/test_results.py
index 76b55f12a..fb990140f 100644
--- a/conbench/tests/app/test_results.py
+++ b/conbench/tests/app/test_results.py
@@ -76,6 +76,23 @@ def test_display_result_no_mean_in_history(self, client):
         resp = client.get(f"benchmark-results/{bmr_id}/")
         assert resp.status_code == 200, resp.text
 
+    def test_get_result_without_commit(self, client):
+        self.authenticate(client)
+
+        # Post a benchmark without a commit
+        payload = _fixtures.VALID_RESULT_PAYLOAD.copy()
+        del payload["github"]
+        post_response = client.post("/api/benchmarks/", json=payload)
+        assert post_response.status_code == 201
+
+        # Ensure the run doesn't have a commit
+        run_response = client.get(f'/api/runs/{payload["run_id"]}/')
+        assert run_response.status_code == 200
+        assert run_response.json["commit"] is None
+
+        # Ensure the result page looks as expected
+        self._assert_view(client, post_response.json["id"])
+
 
 class TestBenchmarkResultDelete(_asserts.DeleteEnforcer):
     def test_authenticated(self, client):