Skip to content

Commit

Permalink
Fix updating todo content in Markdown format
Browse files Browse the repository at this point in the history
When user first created a todo content and then updated it
in Markdown format, the data was not parsed correctly. This
was easily visible when no changes were made to the content.
Previously the data was changed and for example the digest
checksum was updated due to incorrect data parsing.

The problem was that the optional time part in the todo
was not read correctly when the todo item checksum was
added. The Checksum is not present when user adds a todo
content for the verify first time. But the tool calculates
a checksum for each todo item to directly access a specific
item. This checksum caused a parsing error when a todo
content was updated.

Signed-off-by: Heikki Laaksonen <laaksonen.heikki.j@gmail.com>
  • Loading branch information
heilaaks committed Sep 9, 2020
1 parent 4d6481c commit 117f287
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 14 deletions.
2 changes: 1 addition & 1 deletion TODO.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## WORKING
- [ ] Fix identical todo tasks that produce the same checksum.
- [ ] Fix storing of txt and mkdn templates in todo content.
- [ ] Add todo tests that updates without changes. Reveals problems well.
- [ ] Allow python runner update -d 04653b397d64a736 --done 4ec86f
- [ ] Snippet # Comment alignment does not work properly with unicode characters. Test importing tldr/pages.zh.
- [ ] Do not let coveralls to fail build if coverage decreases.
Expand Down
4 changes: 2 additions & 2 deletions snippy/content/parsers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class ContentParserBase(object):
Today | # Match timeline special string.
Tomorrow | # Match timeline special string.
\d{4}-\d{2}-\d{2} # Match simplified ISO8601 date.
(?:
(?: # Match optional time.
T # Match simplified ISO8601 date and time separator.
\d{2}\:\d{2}\:\d{2} # Match Simplified ISO8601 time.
(?:
Expand All @@ -48,7 +48,7 @@ class ContentParserBase(object):
)
|
$
)
)?
"""

RE_MATCH_TODO_TIMELINE = re.compile(r'''
Expand Down
14 changes: 14 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@
# Todos
CREATE_DEFMKD = (Todo.DEFMKD_CREATED,)*1
CREATE_DEPLOY = (Todo.DEPLOY_CREATED,)*1
UPDATE_DEPLOY = (Todo.DEPLOY_CREATED,)*2

# Templates
EXPORT_TEMPLATE = Helper.EXPORT_TEMPLATE
Expand Down Expand Up @@ -937,12 +938,25 @@ def create_defmkd_time_mock(mocker):

mocker.patch.object(Config, 'utcnow', side_effect=CREATE_DEFMKD)

@pytest.fixture(scope='function', name='import-deploy')
def import_deploy_todo(mocker, snippy):
"""Import 'deploy' todo for testing purposes."""

contents = [Todo.DEPLOY]
_import_resources(snippy, mocker, contents)

@pytest.fixture(scope='function', name='create-deploy-utc')
def create_deploy_time_mock(mocker):
"""Mock timestamps to create 'deploy' todo."""

mocker.patch.object(Config, 'utcnow', side_effect=CREATE_DEPLOY)

@pytest.fixture(scope='function', name='update-deploy-utc')
def update_deploy_time_mock(mocker):
"""Mock timestamps to update 'deploy' todo."""

mocker.patch.object(Config, 'utcnow', side_effect=UPDATE_DEPLOY)

## Templates

@pytest.fixture(scope='function', name='template-utc')
Expand Down
1 change: 0 additions & 1 deletion tests/lib/snippet.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,6 @@ class Snippet(object): # pylint: disable=too-few-public-methods
UMOUNT_UUID = _DEFAULTS[_UMOUNT]['uuid']
INTERP_UUID = _DEFAULTS[_INTERP]['uuid']


REMOVE = _DEFAULTS[_REMOVE]
FORCED = _DEFAULTS[_FORCED]
EXITED = _DEFAULTS[_EXITED]
Expand Down
33 changes: 25 additions & 8 deletions tests/lib/todo.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,31 +50,48 @@ class Todo(object): # pylint: disable=too-few-public-methods
'digest': 'fb0544e8817b654994a1295dc47a46af616350c35c468a3084699f8579d1546e'
}, {
'category': 'todo',
'data': ('docker rm --volumes $(docker ps --all --quiet)',),
'brief': 'Remove all docker containers with volumes',
'data': (
'[ ] Add testing # No Timeline [94c789]',
'[ ] Add testing # No Timeline [94c789]',
'[ ] Add testing # Today [259876]',
'[ ] Add testing # Today [259876]',
'[x] Add tests # 2020-06-30 [3038c4]',
'[ ] Add tests # 2020-06-30 [c8f811]',
'[ ] Add tests # 2020-06-30 [c8f811]',
'[ ] Add tests # 2020-06-30 [c8f811]',
'[x] Add tests 9 # 2020-06-30T11:04:55Z [202a96]',
'[x] Add tests 10 # 2020-07-30T11:04:55+00:00 [f40c77]',
'[x] Add tests 11 # 2020-07-30T11:04:55+00:00 [38f409]',
),
'brief': 'Test deploy',
'description': '',
'name': '',
'groups': ('testing',),
'tags': ('todo', 'testing'),
'groups': ('snippy',),
'tags': ('deploy', 'testing', 'todo'),
'links': (),
'source': '',
'versions': (),
'languages': (),
'filename': '',
'created': DEFAULT_TIME,
'updated': DEFAULT_TIME,
'uuid': '11cd5827-b6ef-4067-b5ac-3ceac07dde9f',
'digest': '54e41e9b52a02b631b5c65a6a053fcbabc77ccd42b02c64fdfbc76efdb18e319'
'uuid': 'a1cd5827-b6ef-4067-b5ac-3ceac07dde9f',
'digest': 'f3fa4d98677f117121a2c6ab8a416b28afae0030db1d630c246f18cecc206edf'
})

DEFMKD_CREATED = _DEFAULTS[_DEFMKD]['created']
DEFMKD_UPDATED = _DEFAULTS[_DEPLOY]['updated']
DEPLOY_CREATED = _DEFAULTS[_DEFMKD]['created']
DEFMKD_UPDATED = _DEFAULTS[_DEFMKD]['updated']
DEPLOY_CREATED = _DEFAULTS[_DEPLOY]['created']
DEPLOY_UPDATED = _DEFAULTS[_DEPLOY]['updated']

if not DEFAULT_TIME == DEFMKD_CREATED == DEFMKD_UPDATED == DEPLOY_CREATED == DEPLOY_UPDATED:
raise Exception('default content timestamps must be same - see \'Test case layouts and data structures\'')

DEFMKD_DIGEST = _DEFAULTS[_DEFMKD]['digest']
DEPLOY_DIGEST = _DEFAULTS[_DEPLOY]['digest']
DEFMKD_UUID = _DEFAULTS[_DEFMKD]['uuid']
DEPLOY_UUID = _DEFAULTS[_DEPLOY]['uuid']

DEFMKD = _DEFAULTS[_DEFMKD]
DEPLOY = _DEFAULTS[_DEPLOY]

Expand Down
83 changes: 83 additions & 0 deletions tests/test_cli_update_todo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# -*- coding: utf-8 -*-
#
# SPDX-License-Identifier: AGPL-3.0-or-later
#
# snippy - software development and maintenance notes manager.
# Copyright 2017-2020 Heikki J. Laaksonen <laaksonen.heikki.j@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

"""test_cli_update_todo: Test workflows for updating todos."""

import pytest

from snippy.cause import Cause
from snippy.constants import Constants as Const
from tests.lib.content import Content
from tests.lib.todo import Todo


class TestCliUpdateTodo(object):
"""Test workflows for updating todos."""

@staticmethod
@pytest.mark.usefixtures('import-deploy', 'update-deploy-utc')
def test_cli_update_todo_001(snippy, editor_data, capsys):
"""Update todo with editor in Markdown format.
Update a todo without doing any changes. The updated todo must not be
changed in the process. In this case the Markdown format was used.
"""

output = (
'1. Test deploy @snippy [f3fa4d98677f1171]',
'',
' ! [ ] Add testing # No Timeline [94c789]',
' ! [ ] Add testing # No Timeline [94c789]',
' ! [ ] Add testing # Today [259876]',
' ! [ ] Add testing # Today [259876]',
' ! [x] Add tests # 2020-06-30 [3038c4]',
' ! [ ] Add tests # 2020-06-30 [c8f811]',
' ! [ ] Add tests # 2020-06-30 [c8f811]',
' ! [ ] Add tests # 2020-06-30 [c8f811]',
' ! [x] Add tests 9 # 2020-06-30T11:04:55Z [202a96]',
' ! [x] Add tests 10 # 2020-07-30T11:04:55+00:00 [f40c77]',
' ! [x] Add tests 11 # 2020-07-30T11:04:55+00:00 [38f409]',
'',
' # deploy,testing,todo',
' >',
'',
'OK',
''
)
content = {
'data': [
Content.deepcopy(Todo.DEPLOY)
]
}
editor_data.return_value = Content.dump_mkdn(content['data'][0])
cause = snippy.run(['snippy', 'update', '-d', 'f3fa4d98677f1171', '--scat', 'todo'])
out, err = capsys.readouterr() # Clear old capture.
assert cause == Cause.ALL_OK
Content.assert_storage(content)
cause = snippy.run(['snippy', 'search', '-d', 'f3fa4d98677f1171', '--no-ansi'])
out, err = capsys.readouterr()
assert out == Const.NEWLINE.join(output)
assert not err

@classmethod
def teardown_class(cls):
"""Teardown class."""

Content.delete()
82 changes: 80 additions & 2 deletions tests/test_ut_content_parser_mkdn.py
Original file line number Diff line number Diff line change
Expand Up @@ -2094,9 +2094,10 @@ def test_parser_todo_003(self):
def test_parser_todo_004(self):
"""Test parsing todo.
Test case verifies that parsing TODO from filled template is parsed
Test case verifies that parsing todo from filled template is parsed
correct. The timeline in the comment must not be part of the todo
item.
item. This case verifies the case where user fills the content for
the very first time. In this case there are no checksums.
"""

text = Const.NEWLINE.join((
Expand Down Expand Up @@ -2166,3 +2167,80 @@ def test_parser_todo_004(self):
assert resource.updated == '2020-07-01T11:23:50.244185+00:00'
assert resource.uuid == '361f7a5c-4863-4ee9-af1c-4f911fe864d1'
assert resource.digest == '5d359df5af9b34e0b3e2094c556321f1774b70e7a400a4bc15e79584db1cf62d'

def test_parser_todo_005(self):
"""Test parsing todo.
Test case verifies that parsing updated todo with checksum is parsed
correctly. In this case the todo item was created which adds the todo
item checksum. After this, the todo item is updated. All timestamps
must be read correctly.
"""

text = Const.NEWLINE.join((
'# Test todo @snippy',
'',
'>',
'',
'## Todo',
'',
'- [ ] Add testing # No Timeline [94c789]',
'- [ ] Add testing # No Timeline [94c789]',
'- [ ] Add testing # Today [259876]',
'- [ ] Add testing # Today [259876]',
'- [x] Add tests # 2020-06-30 [3038c4]',
'- [ ] Add tests # 2020-06-30 [c8f811]',
'- [ ] Add tests # 2020-06-30 [c8f811]',
'- [ ] Add tests # 2020-06-30 [c8f811]',
'- [x] Add tests 9 # 2020-06-30T11:04:55Z [202a96]',
'- [x] Add tests 10 # 2020-07-30T11:04:55+00:00 [f40c77]',
'- [x] Add tests 11 # 2020-07-30T11:04:55+00:00 [38f409]',
'',
'## Whiteboard',
'',
'## Meta',
'',
'> category : todo',
'created : 2020-07-01T11:17:34.512824+00:00',
'digest : 9d212abf8b48c8753738eac00eeed79c3f7a3bbb9b094b2aa8ee554195d320d8',
'filename :',
'languages :',
'name :',
'source :',
'tags :',
'updated : 2020-07-01T11:23:50.244185+00:00',
'uuid : 361f7a5c-4863-4ee9-af1c-4f911fe864d1',
'versions :',
'', ))
data = (
'[ ] Add testing # No Timeline [94c789]',
'[ ] Add testing # No Timeline [94c789]',
'[ ] Add testing # Today [259876]',
'[ ] Add testing # Today [259876]',
'[x] Add tests # 2020-06-30 [3038c4]',
'[ ] Add tests # 2020-06-30 [c8f811]',
'[ ] Add tests # 2020-06-30 [c8f811]',
'[ ] Add tests # 2020-06-30 [c8f811]',
'[x] Add tests 9 # 2020-06-30T11:04:55Z [202a96]',
'[x] Add tests 10 # 2020-07-30T11:04:55+00:00 [f40c77]',
'[x] Add tests 11 # 2020-07-30T11:04:55+00:00 [38f409]',
)
collection = Collection()
Parser(self.TIMESTAMP, text, collection).read_collection()
resource = next(collection.resources())
assert resource.category == Const.TODO
assert resource.data == data
assert resource.brief == 'Test todo'
assert resource.description == ''
assert resource.name == ''
assert resource.groups == ('snippy',)
assert resource.tags == ()
assert resource.links == ()
assert resource.source == ''
assert resource.versions == ()
assert resource.languages == ()
assert resource.filename == ''
assert resource.created == '2020-07-01T11:17:34.512824+00:00'
assert resource.updated == '2020-07-01T11:23:50.244185+00:00'
assert resource.uuid == '361f7a5c-4863-4ee9-af1c-4f911fe864d1'
assert resource.digest == '3ce16c3e6a98e64a248a93814b31e792dbdef31f3c04c211007d670ddf1211f3'

0 comments on commit 117f287

Please sign in to comment.