Skip to content

Fixes #3354 - Handles FileNotFoundError for assets #3355

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

Merged
merged 4 commits into from
Jun 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
221 changes: 133 additions & 88 deletions tests/unit/test_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,104 +6,149 @@

"""Tests for webcompat/templates/__init__.py."""

import unittest
import pytest

import webcompat
from webcompat.templates import bust_cache
from webcompat.templates import cache_dict
from webcompat.templates import format_date
from webcompat.templates import format_milestone_class
from webcompat.templates import format_milestone_title
from webcompat.templates import format_title
from webcompat.templates import get_description
from webcompat.templates import get_checksum
from webcompat.templates import get_domain
from webcompat.templates import md5_checksum
from webcompat.templates import STATIC_PATH


class TestTemplates(unittest.TestCase):
"""Class for testing template filters and helpers."""

def setUp(self):
"""Set up."""
pass

def tearDown(self):
"""Tear down."""
pass

def test_format_date(self):
"""Test date formatting.

We trust what GitHub gives us, so just test length.
input: 2014-05-01T02:26:28Z
ouput: 2014-05-01
"""
input = '2014-05-01T02:26:28Z'
output = format_date(input)
assert len(output) == 10
assert 'T' not in output
assert output == '2014-05-01'

def test_get_domain(self):
"""Test domain name extraction from title.

(This really means give us the first pile of strings before
the first whitespace).
"""
title = 'staging.webcompat.com - site is not usable'
assert get_domain(title) == 'staging.webcompat.com'
title = '😎 😓'
assert get_domain(title) == '😎'

def test_get_description(self):
"""Test that we can pull out the first part of the description."""
body_html_frag = """
def test_format_date():
"""Test date formatting.

We trust what GitHub gives us, so just test length.
input: 2014-05-01T02:26:28Z
ouput: 2014-05-01
"""
input = '2014-05-01T02:26:28Z'
output = format_date(input)
assert len(output) == 10
assert 'T' not in output
assert output == '2014-05-01'


def test_get_domain():
"""Test domain name extraction from title.

(This really means give us the first pile of strings before
the first whitespace).
"""
title = 'staging.webcompat.com - site is not usable'
assert get_domain(title) == 'staging.webcompat.com'
title = '😎 😓'
assert get_domain(title) == '😎'


def test_get_description():
"""Test that we can pull out the first part of the description."""
body_html_frag = """
<p><strong>Problem type</strong>: Site is not usable<br>
<strong>Description</strong>: Missing items<br>
<strong>Steps to Reproduce</strong>:<br>
"""
body_html_frag2 = '😎'
assert get_description(body_html_frag) == 'Missing items'
assert get_description(body_html_frag2) is None

def test_format_title(self):
"""Test putting issue titles together."""
# title comes from body description
issue_data = {
'body_html': '\n'
'<p><strong>Problem type</strong>: Site is not usable<br>\n'
'<strong>Description</strong>: Desc from Description<br>\n'
'<strong>Steps to Reproduce</strong>:<br>\n',
'title': 'staging.webcompat.com - desc from title'}
expected = 'staging.webcompat.com - Desc from Description'
assert format_title(issue_data) == expected
# title comes from issue title
issue_data = {
'body_html': 'fix my bug',
'title': 'staging.webcompat.com - desc from title'}
expected = 'staging.webcompat.com - desc from title'
assert format_title(issue_data) == expected

def test_format_milestone_title(self):
"""Test that we return a properly formatted milestone title."""
# missing milestone title
issue_data = {'milestone': {'title': ''}, 'state': 'open'}
expected = 'Missing Milestone!'
assert format_milestone_title(issue_data) == expected
# milestone has title
issue_data = {'milestone': {'title': 'sitewait'}, 'state': 'open'}
expected = 'Site Contacted'
assert format_milestone_title(issue_data) == expected
# milestone has title in a closed state
issue_data = {'milestone': {'title': 'sitewait'}, 'state': 'closed'}
expected = 'Closed: Site Contacted'
assert format_milestone_title(issue_data) == expected

def test_format_milestone_class(self):
"""Test that we return the correct class string."""
# open state
issue_data = {
'milestone': {'title': 'needsdiagnosis'},
'state': 'open'}
assert format_milestone_class(issue_data) == 'needsdiagnosis'
# closed state
issue_data = {
'milestone': {'title': 'needsdiagnosis'},
'state': 'closed'}
assert format_milestone_class(issue_data) == 'closed'
body_html_frag2 = '😎'
assert get_description(body_html_frag) == 'Missing items'
assert get_description(body_html_frag2) is None


def test_format_title():
"""Test putting issue titles together."""
# title comes from body description
issue_data = {
'body_html': '\n'
'<p><strong>Problem type</strong>: Site is not usable<br>\n'
'<strong>Description</strong>: Desc from Description<br>\n'
'<strong>Steps to Reproduce</strong>:<br>\n',
'title': 'staging.webcompat.com - desc from title'}
expected = 'staging.webcompat.com - Desc from Description'
assert format_title(issue_data) == expected
# title comes from issue title
issue_data = {
'body_html': 'fix my bug',
'title': 'staging.webcompat.com - desc from title'}
expected = 'staging.webcompat.com - desc from title'
assert format_title(issue_data) == expected


def test_format_milestone_title():
"""Test that we return a properly formatted milestone title."""
# missing milestone title
issue_data = {'milestone': {'title': ''}, 'state': 'open'}
expected = 'Missing Milestone!'
assert format_milestone_title(issue_data) == expected
# milestone has title
issue_data = {'milestone': {'title': 'sitewait'}, 'state': 'open'}
expected = 'Site Contacted'
assert format_milestone_title(issue_data) == expected
# milestone has title in a closed state
issue_data = {'milestone': {'title': 'sitewait'}, 'state': 'closed'}
expected = 'Closed: Site Contacted'
assert format_milestone_title(issue_data) == expected


def test_format_milestone_class():
"""Test that we return the correct class string."""
# open state
issue_data = {
'milestone': {'title': 'needsdiagnosis'},
'state': 'open'}
assert format_milestone_class(issue_data) == 'needsdiagnosis'
# closed state
issue_data = {
'milestone': {'title': 'needsdiagnosis'},
'state': 'closed'}
assert format_milestone_class(issue_data) == 'closed'


def test_md5_checksum_file_missing():
"""Test checksum computation."""
assert md5_checksum('/punkcat/nowhere') == 'missing_file'


def test_md5_checksum_file_exists(tmp_path):
"""Test checksum computation."""
file_path = tmp_path / 'space.js'
file_path.write_text('punkcat')
assert md5_checksum(file_path) == '501753e94c8bfbbbd53c792c9688c8b5'


def test_get_checksum_not_in_cache(tmp_path):
"""Test the checksum cache_dict."""
assert cache_dict == {}
file_path = tmp_path / 'space.js'
file_path.write_text('punkcat')
get_checksum(file_path)
assert str(file_path) in cache_dict


def test_bust_cache_localhost():
"""Test for LOCALHOST the path is not modified."""
expected = '/dist/vendor.js'
webcompat.app.config['LOCALHOST'] = True
assert bust_cache('/dist/vendor.js') == expected


def test_bust_cache_production_missing_file():
"""Test for cache_bust the path is missing."""
expected = '/punkcat/nowhere?missing_file'
webcompat.app.config['LOCALHOST'] = None
assert bust_cache('/punkcat/nowhere') == expected


def test_bust_cache_production_file_exists(tmpdir):
"""Test cache_bust with the right checksum."""
webcompat.app.config['LOCALHOST'] = None
webcompat.templates.STATIC_PATH = tmpdir
file_path = tmpdir.join('space.js')
file_path.write_text('punkcat', encoding='utf-8')
expected = 'space.js' + '?501753e94c8bfbbbd53c792c9688c8b5'
assert bust_cache('space.js') == expected
46 changes: 27 additions & 19 deletions webcompat/templates/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import hashlib
import os
import os.path
import re

from flask import Markup
Expand All @@ -27,32 +28,39 @@ def bust_cache(file_path):
is restarted (which is when file changes would have been deployed).
Doesn't return hash on development
"""

def get_checksum(file_path):
try:
checksum = cache_dict[file_path]
except KeyError:
checksum = md5_checksum(file_path)
cache_dict[file_path] = checksum
return checksum

if app.config['LOCALHOST']:
return file_path
absolute_path = os.path.join(STATIC_PATH, file_path)
return file_path + '?' + get_checksum(absolute_path)

return file_path + '?' + get_checksum(STATIC_PATH + file_path)

def get_checksum(file_path):
print('GET_CHECKSUM', cache_dict)
try:
checksum = cache_dict[file_path]
except KeyError:
checksum = md5_checksum(file_path)
cache_dict[str(file_path)] = checksum
return checksum


def md5_checksum(file_path):
"""Return the md5 checksum for a given file path."""
with open(file_path, 'rb') as fh:
m = hashlib.md5()
while True:
# only read in 8k of the file at a time
data = fh.read(8192)
if not data:
break
m.update(data)
return m.hexdigest()
try:
with open(file_path, 'rb') as fh:
m = hashlib.md5()
while True:
# only read in 8k of the file at a time
data = fh.read(8192)
if not data:
break
m.update(data)
checksum = m.hexdigest()
except FileNotFoundError:
# if file doesn't exist we want to be able to show it in the URI
checksum = 'missing_file'
finally:
return checksum


@app.template_filter('format_date')
Expand Down