Index: ow/tests/models/test_workout.py
===================================================================
--- ow/tests/models/test_workout.py	(revision 8c2b0949ae91540ee5c8101b9d5ff39003773da3)
+++ ow/tests/models/test_workout.py	(revision 5ec3a0b5cf5640304a0953320daef2a28bbca9a5)
@@ -3,15 +3,12 @@
 from datetime import datetime, timedelta, timezone
 from decimal import Decimal
-from unittest.mock import patch, Mock
+from unittest.mock import patch
 
 import pytest
-from pyramid.security import Allow, Everyone, Deny, ALL_PERMISSIONS
+from pyramid.security import Allow, Everyone
 
 from ow.models.workout import Workout
 from ow.models.user import User
 from ow.models.root import OpenWorkouts
-from ow.utilities import create_blob
-
-from ow.tests.helpers import join
 
 
@@ -33,17 +30,13 @@
     def test__acl__(self, root):
         # First check permissions for a workout without parent
-        workout = Workout()
-        with pytest.raises(AttributeError):
-            workout.__acl__()
+        permissions = [(Allow, Everyone, 'view'),
+                       (Allow, 'group:admins', 'edit')]
+        workout = Workout()
+        assert workout.__acl__() == permissions
+
         # Now permissions on a workout that has been added to a user
         uid = str(root['john'].uid)
-        workout = root['john']['1']
-        permissions = [
-            (Allow, uid, 'view'),
-            (Allow, uid, 'edit'),
-            (Allow, uid, 'delete'),
-            (Deny, Everyone, ALL_PERMISSIONS)
-        ]
-        assert workout.__acl__() == permissions
+        permissions = [(Allow, uid, 'view'), (Allow, uid, 'edit')]
+        assert root['john']['1'].__acl__() == permissions
 
     def test_runthrough(self, root):
@@ -64,13 +57,10 @@
         assert 250 == joe['1'].distance * 1000
 
+    def test_add_user(self, root):
+        root.add_user('fred', firstname=u'Fred', lastname=u'Flintstone')
+        assert u'Fred Flintstone' == root['fred'].fullname
+
     def test_workout_id(self, root):
         assert root['john']['1'].workout_id == '1'
-
-    def test_owner(self, root):
-        # workout with owner
-        assert root['john']['1'].owner == root['john']
-        # workout without owner
-        w = Workout()
-        assert w.owner is None
 
     def test_end(self, root):
@@ -91,21 +81,4 @@
         workout = Workout(start=start_date)
         assert workout.start_time == start_date.strftime('%H:%M')
-
-    def test_start_in_timezone(self):
-        start_date = datetime.now(tz=timezone.utc)
-        str_start_date = start_date.strftime('%d/%m/%Y %H:%M (%Z)')
-        workout = Workout(start=start_date)
-        assert workout.start_in_timezone('UTC') == str_start_date
-        assert workout.start_in_timezone('Europe/Madrid') != str_start_date
-        assert workout.start_in_timezone('America/Vancouver') != str_start_date
-
-    def test_end_in_timezone(self):
-        start_date = datetime.now(tz=timezone.utc)
-        end_date = start_date + timedelta(minutes=60)
-        str_end_date = end_date.strftime('%d/%m/%Y %H:%M (%Z)')
-        workout = Workout(start=start_date, duration=timedelta(minutes=60))
-        assert workout.end_in_timezone('UTC') == str_end_date
-        assert workout.end_in_timezone('Europe/Madrid') != str_end_date
-        assert workout.end_in_timezone('America/Vancouver') != str_end_date
 
     def test_split_duration(self):
@@ -128,11 +101,4 @@
         assert workout.duration_seconds == '15'
 
-    def test__duration(self):
-        # covering the property that shows the duration of a workout properly
-        # formatted in hours:minutes:seconds
-        duration = timedelta(hours=1, minutes=30, seconds=15)
-        workout = Workout(duration=duration)
-        assert workout._duration == '01:30:15'
-
     def test_rounded_distance_no_value(self):
         workout = Workout()
@@ -142,44 +108,5 @@
         workout = Workout()
         workout.distance = 44.44444444
-        assert workout.rounded_distance == 44.44
-
-    def test_hashed(self, root):
-        # first test a workout that is attached to a user
-        workout = root['john']['1']
-        assert workout.hashed == (
-            str(workout.owner.uid) +
-            workout.start.strftime('%Y%m%d%H%M%S') +
-            str(workout.duration.seconds) +
-            str(workout.distance)
-        )
-        # now a workout that is not (no owner info)
-        workout = Workout(
-            sport='running',
-            start_time=datetime.now(timezone.utc),
-            duration=timedelta(seconds=3600),
-            distance=Decimal(30)
-        )
-        assert workout.hashed == (
-            workout.start.strftime('%Y%m%d%H%M%S') +
-            str(workout.duration.seconds) +
-            str(workout.distance)
-        )
-        # now an empty workout...
-        workout = Workout()
-        assert workout.hashed == (
-            workout.start.strftime('%Y%m%d%H%M%S')
-        )
-
-    def test_trimmed_notes(self):
-        workout = Workout()
-        assert workout.notes == ''
-        assert workout.trimmed_notes == ''
-        workout.notes = 'very short notes'
-        assert workout.notes == 'very short notes'
-        assert workout.trimmed_notes == 'very short notes'
-        workout.notes = 'long notes now, repeated' * 1000
-        assert len(workout.notes) == 24000
-        assert len(workout.trimmed_notes) == 224
-        assert workout.trimmed_notes.endswith(' ...')
+        assert workout.rounded_distance == 44.4
 
     def test_has_hr(self):
@@ -249,30 +176,4 @@
         assert workout.atemp['avg'] == 5
 
-    def test_tracking_file_path(self):
-        workout = Workout()
-        # no tracking file, path is None
-        assert workout.tracking_file_path is None
-        # workout still not saved to the db
-        workout.tracking_file = Mock()
-        workout.tracking_file._uncommitted.return_value = '/tmp/blobtempfile'
-        workout.tracking_file.committed.return_value = None
-        assert workout.tracking_file_path == '/tmp/blobtempfile'
-        workout.tracking_file._uncommitted.return_value = None
-        workout.tracking_file.committed.return_value = '/var/db/blobs/blobfile'
-        assert workout.tracking_file_path == '/var/db/blobs/blobfile'
-
-    def test_fit_file_path(self):
-        workout = Workout()
-        # no tracking file, path is None
-        assert workout.fit_file_path is None
-        # workout still not saved to the db
-        workout.fit_file = Mock()
-        workout.fit_file._uncommitted.return_value = '/tmp/blobtempfile'
-        workout.fit_file.committed.return_value = None
-        assert workout.fit_file_path == '/tmp/blobtempfile'
-        workout.fit_file._uncommitted.return_value = None
-        workout.fit_file.committed.return_value = '/var/db/blobs/blobfile'
-        assert workout.fit_file_path == '/var/db/blobs/blobfile'
-
     def test_load_from_file_invalid(self):
         workout = Workout()
@@ -288,11 +189,4 @@
             workout.load_from_file()
             assert lfg.called
-
-    def test_load_from_file_fit(self):
-        workout = Workout()
-        workout.tracking_filetype = 'fit'
-        with patch.object(workout, 'load_from_fit') as lff:
-            workout.load_from_file()
-            assert lff.called
 
     gpx_params = (
@@ -445,207 +339,24 @@
         assert res == {}
 
-    fit_params = (
-        # complete fit file from a garmin 520 device
-        ('fixtures/20181230_101115.fit', {
-            'start': datetime(2018, 12, 30, 9, 11, 15, tzinfo=timezone.utc),
-            'duration': timedelta(0, 13872, 45000),
-            'distance': Decimal('103.4981999999999970896169543'),
-            'title': 'Synapse cycling',
-            'hr': {'min': 93, 'max': 170, 'avg': 144},
-            'cad': {'min': 0, 'max': 121, 'avg': 87},
-            'atemp': {'min': -4, 'max': 15, 'avg': 2},
-            'gpx_file': 'fixtures/20181230_101115.gpx'}),
-        # fit file from a garmin 520 without heart rate or cadence data
-        ('fixtures/20181231_110728.fit', {
-            'start': datetime(2018, 12, 31, 10, 7, 28, tzinfo=timezone.utc),
-            'duration': timedelta(0, 2373, 142000),
-            'distance': Decimal('6.094909999999999854480847716'),
-            'title': 'Synapse cycling',
-            'hr': None,
-            'cad': None,
-            'atemp': {'min': -1, 'max': 11, 'avg': 1},
-            'gpx_file': 'fixtures/20181231_110728.gpx'}),
-    )
-
-    @pytest.mark.parametrize(('filename', 'expected'), fit_params)
-    def test_load_from_fit(self, filename, expected):
-        """
-        Load a fit file located in tests/fixtures using the load_from_fit()
-        method of the Workout model, then check that certain attrs on the
-        workout are updated correctly.
-
-        Ensure also that the proper gpx file is created automatically from
-        the fit file, with the proper contents (we have a matching gpx file
-        in tests/fixtures for each fit file)
-        """
-        # expected values
-        start = expected['start']
-        duration = expected['duration']
-        distance = expected['distance']
-        title = expected['title']
-        hr = expected['hr']
-        cad = expected['cad']
-        atemp = expected['atemp']
-        # gpx_file = expected['gpx_file']
-
-        workout = Workout()
-
-        # Check the values are different by default
-        assert workout.start != start
-        assert workout.duration != duration
-        assert workout.distance != distance
-
-        # by default no tracking file and no fit file are associated with this
-        # workout.
-        assert workout.tracking_file is None
-        assert workout.fit_file is None
-        assert not workout.has_tracking_file
-        assert not workout.has_gpx
-        assert not workout.has_fit
-
-        fit_file_path = os.path.join(
-            os.path.dirname(os.path.dirname(__file__)), filename)
-
-        # gpx_file_path = os.path.join(
-        #     os.path.dirname(os.path.dirname(__file__)), gpx_file)
-
-        # add the fit file as a blob to tracking_file
-        with open(fit_file_path, 'rb') as fit_file:
-            fit_blob = create_blob(fit_file.read(), file_extension='fit',
-                                   binary=True)
-        workout.tracking_file = fit_blob
-        workout.tracking_filetype = 'fit'
-
-        res = workout.load_from_fit()
-
-        assert res is True
-        assert workout.start == start
-        assert workout.duration == duration
-        assert isinstance(workout.distance, Decimal)
-        assert round(workout.distance) == round(distance)
-        # The title of the workout is taken from the gpx file
-        assert workout.title == title
-
-        if hr is not None:
-            for k in hr.keys():
-                # We use 'fail' as the fallback in the getattr call
-                # because None is one of the posible values, and we
-                # want to be sure those attrs are there
-                value = getattr(workout, 'hr_' + k, 'fail')
-                # Use round() to avoid problems when comparing long
-                # Decimal objects
-                assert round(hr[k]) == round(value)
-
-        if cad is not None:
-            for k in cad.keys():
-                value = getattr(workout, 'cad_' + k, 'fail')
-                assert round(cad[k]) == round(value)
-
-        if atemp is not None:
-            for k in atemp.keys():
-                value = getattr(workout, 'atemp_' + k, 'fail')
-                assert round(atemp[k]) == round(value)
-
-        assert workout.tracking_file is not None
-        # the tracking file type is set back to gpx, as we have
-        # automatically generated the gpx version
-        assert workout.tracking_filetype == 'gpx'
-        assert workout.fit_file is not None
-        assert workout.has_tracking_file
-        assert workout.has_gpx
-        assert workout.has_fit
-
     def test_has_tracking_file(self, root):
         workout = root['john']['1']
         # without tracking file
-        assert not workout.has_tracking_file
+        assert workout.has_tracking_file is False
         # with tracking file
         workout.tracking_file = 'faked tracking file'
-        assert workout.has_tracking_file
+        assert workout.has_tracking_file is True
 
     def test_has_gpx(self, root):
         workout = root['john']['1']
         # without tracking file
-        assert not workout.has_gpx
+        assert workout.has_gpx is False
         workout.tracking_filetype = 'fit'
-        assert not workout.has_gpx
+        assert workout.has_gpx is False
         # with non-gpx tracking file
         workout.tracking_file = 'faked tracking file'
         workout.tracking_filetype = 'fit'
-        assert not workout.has_gpx
+        assert workout.has_gpx is False
         # with gpx tracking file
         workout.tracking_file = 'faked tracking file'
         workout.tracking_filetype = 'gpx'
-        assert workout.has_gpx
-
-    def test_has_fit(self, root):
-        workout = root['john']['1']
-        # without tracking file
-        assert not workout.has_fit
-        # tracking_file is a fit, this should not happen, as uploading a fit
-        # puts the fit file into .fit_file and generates a gpx for
-        # .tracking_file
-        workout.tracking_file = 'faked tracking file'
-        workout.tracking_filetype = 'fit'
-        assert not workout.has_fit
-        # now, having a fit file returns true
-        workout.fit_file = 'faked fit file'
-        assert workout.has_fit
-        # no matter what we have in tracking_file
-        workout.tracking_filetype = 'gpx'
-        assert workout.has_fit
-        workout.tracking_file = None
-        workout.tracking_filetype = None
-        assert workout.has_fit
-
-    def test_map_screenshot_name(self, root):
-        workout = root['john']['1']
-        assert workout.map_screenshot_name == (
-            str(root['john'].uid) + '/' + str(workout.workout_id) + '.png')
-
-    def test_map_screenshot_path(self, root):
-        workout = root['john']['1']
-        assert workout.map_screenshot_path.endswith(
-            'static/maps/' + workout.map_screenshot_name)
-
-    @patch('ow.models.workout.os')
-    def test_map_screenshot_no_gpx(self, os, root):
-        workout = root['john']['1']
-        assert workout.map_screenshot is None
-        assert not os.path.join.called
-        assert not os.path.exists.called
-
-    @patch('ow.models.workout.os')
-    def test_map_screenshot_no_shot(self, os, root):
-        """
-        A workout with a tracking file has no map screenshot
-        """
-        # This says "no screenshot found"
-        os.path.exists.return_value = False
-
-        workout = root['john']['1']
-        workout.tracking_file = 'faked gpx file'
-        workout.tracking_filetype = 'gpx'
-
-        assert workout.map_screenshot is None
-        assert os.path.join.called
-        os.path.exists.assert_called_once_with(workout.map_screenshot_path)
-
-    @patch('ow.models.workout.os')
-    def test_map_screenshot_has_shot(self, os, root):
-        """
-        A workout with a tracking file has a map screenshot, the path to that
-        is returned without doing anything else
-        """
-        # This says "yeah, we have a screenshot"
-        os.path.exists.return_value = True
-        os.path.abspath.return_value = '/'
-        os.path.join.side_effect = join
-
-        workout = root['john']['1']
-        workout.tracking_file = 'faked gpx file'
-        workout.tracking_filetype = 'gpx'
-        uid = str(root['john'].uid)
-        assert workout.map_screenshot == 'ow:/static/maps/' + uid + '/1.png'
-        os.path.exists.assert_called_once_with(workout.map_screenshot_path)
-        assert os.path.join.called
+        assert workout.has_gpx is True
