Skip to content

Commit ebe6aa6

Browse files
committed
Tests, plus new --dump option, refs #2, closes #3
1 parent 7ab475a commit ebe6aa6

3 files changed

Lines changed: 104 additions & 16 deletions

File tree

apple_notes_to_sqlite/cli.py

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import click
2+
import json
23
import secrets
34
import sqlite_utils
45
import subprocess
@@ -20,38 +21,54 @@
2021
set noteCreated to (noteCreatedDate as «class isot» as string)
2122
set noteUpdatedDate to the modification date of eachNote
2223
set noteUpdated to (noteUpdatedDate as «class isot» as string)
23-
log "{split}{split}" & "\n"
2424
log "{split}-id: " & noteId & "\n"
2525
log "{split}-created: " & noteCreated & "\n"
2626
log "{split}-updated: " & noteUpdated & "\n"
2727
log "{split}-title: " & noteTitle & "\n\n"
2828
log noteBody & "\n"
29+
log "{split}{split}" & "\n"
2930
end repeat
3031
end tell
31-
"""
32+
""".strip()
3233

3334

3435
@click.command()
3536
@click.version_option()
36-
@click.argument("db_path")
37+
@click.argument(
38+
"db_path",
39+
type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),
40+
required=False,
41+
)
3742
@click.option("--stop-after", type=int, help="Stop after this many notes")
38-
def cli(db_path, stop_after):
43+
@click.option("--dump", is_flag=True, help="Output notes to standard output")
44+
def cli(db_path, stop_after, dump):
3945
"Export Apple Notes to SQLite"
40-
db = sqlite_utils.Database(db_path)
46+
if not db_path and not dump:
47+
raise click.UsageError(
48+
"Please specify a path to a database file, or use --dump to see the output",
49+
)
4150
expected_count = stop_after
4251
if not expected_count:
4352
expected_count = count_notes()
4453
# Use click progressbar
4554
i = 0
46-
with click.progressbar(
47-
length=expected_count, label="Exporting notes", show_eta=True, show_pos=True
48-
) as bar:
55+
if dump:
4956
for note in extract_notes():
50-
db["notes"].insert(note, pk="id", replace=True)
51-
bar.update(1)
57+
click.echo(json.dumps(note))
5258
i += 1
5359
if stop_after and i >= stop_after:
5460
break
61+
else:
62+
db = sqlite_utils.Database(db_path)
63+
with click.progressbar(
64+
length=expected_count, label="Exporting notes", show_eta=True, show_pos=True
65+
) as bar:
66+
for note in extract_notes():
67+
db["notes"].insert(note, pk="id", replace=True)
68+
bar.update(1)
69+
i += 1
70+
if stop_after and i >= stop_after:
71+
break
5572

5673

5774
def count_notes():
@@ -80,10 +97,11 @@ def extract_notes():
8097
line = line.decode("mac_roman").strip()
8198
if line == f"{split}{split}":
8299
if note.get("id"):
83-
note["body"] = "\n".join(body)
100+
note["body"] = "\n".join(body).strip()
84101
yield note
85102
note = {}
86103
body = []
104+
continue
87105
found_key = False
88106
for key in ("id", "title", "created", "updated"):
89107
if line.startswith(f"{split}-{key}: "):

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,6 @@ def get_long_description():
3232
apple-notes-to-sqlite=apple_notes_to_sqlite.cli:cli
3333
""",
3434
install_requires=["click", "sqlite-utils"],
35-
extras_require={"test": ["pytest"]},
35+
extras_require={"test": ["pytest", "pytest-subprocess"]},
3636
python_requires=">=3.7",
3737
)
Lines changed: 74 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,80 @@
11
from click.testing import CliRunner
2-
from apple_notes_to_sqlite.cli import cli
2+
from apple_notes_to_sqlite.cli import cli, COUNT_SCRIPT
3+
import sqlite_utils
4+
import json
5+
import os
6+
from unittest.mock import patch
37

8+
FAKE_OUTPUT = b"""
9+
abcdefg-id: note-1
10+
abcdefg-created: 2023-03-08T16:36:41
11+
abcdefg-updated: 2023-03-08T15:36:41
12+
abcdefg-title: Title 1
413
5-
def test_version():
14+
This is the content of note 1
15+
abcdefgabcdefg
16+
abcdefg-id: note-2
17+
abcdefg-created: 2023-03-08T16:36:41
18+
abcdefg-updated: 2023-03-08T15:36:41
19+
abcdefg-title: Title 2
20+
21+
This is the content of note 2
22+
abcdefgabcdefg
23+
""".strip()
24+
25+
EXPECTED_NOTES = [
26+
{
27+
"id": "note-1",
28+
"created": "2023-03-08T16:36:41",
29+
"updated": "2023-03-08T15:36:41",
30+
"title": "Title 1",
31+
"body": "This is the content of note 1",
32+
},
33+
{
34+
"id": "note-2",
35+
"created": "2023-03-08T16:36:41",
36+
"updated": "2023-03-08T15:36:41",
37+
"title": "Title 2",
38+
"body": "This is the content of note 2",
39+
},
40+
]
41+
42+
43+
@patch("secrets.token_hex")
44+
def test_apple_notes_to_sqlite(mock_token_hex, fp):
45+
fp.register_subprocess(["osascript", "-e", COUNT_SCRIPT], stdout=b"2")
46+
fp.register_subprocess(["osascript", "-e", fp.any()], stdout=FAKE_OUTPUT)
47+
mock_token_hex.return_value = "abcdefg"
48+
runner = CliRunner()
49+
with runner.isolated_filesystem():
50+
assert not os.path.exists("notes.db")
51+
# Run the CLI
52+
result = runner.invoke(cli, ["notes.db"])
53+
assert result.exit_code == 0
54+
# Check that the database was created
55+
assert os.path.exists("notes.db")
56+
db = sqlite_utils.Database("notes.db")
57+
# Check that the notes table was created
58+
assert db.table_names() == ["notes"]
59+
# Check that the notes were inserted
60+
assert list(db["notes"].rows) == EXPECTED_NOTES
61+
62+
63+
@patch("secrets.token_hex")
64+
def test_apple_notes_to_sqlite_dump(mock_token_hex, fp):
65+
fp.register_subprocess(["osascript", "-e", COUNT_SCRIPT], stdout=b"2")
66+
fp.register_subprocess(["osascript", "-e", fp.any()], stdout=FAKE_OUTPUT)
67+
mock_token_hex.return_value = "abcdefg"
668
runner = CliRunner()
769
with runner.isolated_filesystem():
8-
result = runner.invoke(cli, ["--version"])
70+
assert not os.path.exists("notes.db")
71+
result = runner.invoke(cli, ["--dump"])
72+
# Check the output
973
assert result.exit_code == 0
10-
assert result.output.startswith("cli, version ")
74+
# Should still be no database
75+
assert not os.path.exists("notes.db")
76+
# Output should be newline-delimited JSON
77+
notes = []
78+
for line in result.output.splitlines():
79+
notes.append(json.loads(line))
80+
assert notes == EXPECTED_NOTES

0 commit comments

Comments
 (0)