mirror of
https://github.com/natelandau/obsidian-metadata.git
synced 2025-11-18 01:43:39 -05:00
feat(application): add new metadata to frontmatter (#9)
* feat(frontmatter): frontmatter method to add key, values * build: add pysnooper to aid in debugging * feat(application): add new frontmatter * build: clean up dev container * fix(notes): diff now pretty prints in a table * docs(readme): update usage information * docs(readme): fix markdown lists
This commit is contained in:
@@ -3,14 +3,16 @@
|
||||
|
||||
How mocking works in this test suite:
|
||||
|
||||
1. The main_app() method is mocked using a side effect iterable. This allows us to pass a value in the first run, and then a KeyError in the second run to exit the loop.
|
||||
1. The application_main() method is mocked using a side effect iterable. This allows us to pass a value in the first run, and then a KeyError in the second run to exit the loop.
|
||||
2. All questions are mocked using return_value. This allows us to pass in a value to the question and then the method will return that value. This is useful for testing questionary prompts without user input.
|
||||
"""
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from obsidian_metadata.models.enums import MetadataType
|
||||
from tests.helpers import Regex
|
||||
|
||||
|
||||
@@ -31,273 +33,335 @@ def test_abort(test_application, mocker, capsys) -> None:
|
||||
app = test_application
|
||||
app.load_vault()
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_main_application",
|
||||
"obsidian_metadata.models.application.Questions.ask_application_main",
|
||||
return_value="abort",
|
||||
)
|
||||
|
||||
app.main_app()
|
||||
app.application_main()
|
||||
captured = capsys.readouterr()
|
||||
assert "Vault Info" in captured.out
|
||||
assert "Done!" in captured.out
|
||||
|
||||
|
||||
def test_list_notes(test_application, mocker, capsys) -> None:
|
||||
"""Test renaming a key."""
|
||||
def test_add_metadata_frontmatter_success(test_application, mocker, capsys) -> None:
|
||||
"""Test adding new metadata to the vault."""
|
||||
app = test_application
|
||||
app.load_vault()
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_main_application",
|
||||
side_effect=["list_notes", KeyError],
|
||||
"obsidian_metadata.models.application.Questions.ask_application_main",
|
||||
side_effect=["add_metadata", KeyError],
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_area",
|
||||
return_value=MetadataType.FRONTMATTER,
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_new_key",
|
||||
return_value="new_key",
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_new_value",
|
||||
return_value="new_key_value",
|
||||
)
|
||||
|
||||
with pytest.raises(KeyError):
|
||||
app.main_app()
|
||||
app.application_main()
|
||||
captured = capsys.readouterr()
|
||||
assert "04 no metadata/no_metadata_1.md" in captured.out
|
||||
assert "02 inline/inline 2.md" in captured.out
|
||||
assert "+inbox/Untitled.md" in captured.out
|
||||
assert "00 meta/templates/data sample.md" in captured.out
|
||||
assert captured.out == Regex(r"SUCCESS +\| Added metadata to.*\d+.*notes", re.DOTALL)
|
||||
|
||||
|
||||
def test_all_metadata(test_application, mocker, capsys) -> None:
|
||||
"""Test renaming a key."""
|
||||
app = test_application
|
||||
app.load_vault()
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_main_application",
|
||||
side_effect=["all_metadata", KeyError],
|
||||
)
|
||||
|
||||
with pytest.raises(KeyError):
|
||||
app.main_app()
|
||||
captured = capsys.readouterr()
|
||||
expected = re.escape("┃ Keys ┃ Values")
|
||||
assert captured.out == Regex(expected)
|
||||
expected = re.escape("Inline Tags │ breakfast")
|
||||
assert captured.out == Regex(expected)
|
||||
|
||||
|
||||
def test_filter_notes(test_application, mocker, capsys) -> None:
|
||||
"""Test renaming a key."""
|
||||
app = test_application
|
||||
app.load_vault()
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_main_application",
|
||||
side_effect=["filter_notes", "list_notes", KeyError],
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_for_filter_path",
|
||||
return_value="inline",
|
||||
)
|
||||
|
||||
with pytest.raises(KeyError):
|
||||
app.main_app()
|
||||
captured = capsys.readouterr()
|
||||
assert "04 no metadata/no_metadata_1.md" not in captured.out
|
||||
assert "02 inline/inline 1.md" in captured.out
|
||||
assert "02 inline/inline 2.md" in captured.out
|
||||
assert "+inbox/Untitled.md" not in captured.out
|
||||
assert "00 meta/templates/data sample.md" not in captured.out
|
||||
|
||||
|
||||
def test_rename_key_success(test_application, mocker, capsys) -> None:
|
||||
"""Test renaming a key."""
|
||||
app = test_application
|
||||
app.load_vault()
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_main_application",
|
||||
side_effect=["rename_key", KeyError],
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_for_existing_key",
|
||||
return_value="tags",
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_for_new_key",
|
||||
return_value="new_tags",
|
||||
)
|
||||
|
||||
with pytest.raises(KeyError):
|
||||
app.main_app()
|
||||
captured = capsys.readouterr()
|
||||
assert captured.out == Regex(r"Renamed.*tags.*to.*new_tags.*in.*\d+.*notes", re.DOTALL)
|
||||
|
||||
|
||||
def test_rename_key_fail(test_application, mocker, capsys) -> None:
|
||||
"""Test renaming a key."""
|
||||
app = test_application
|
||||
app.load_vault()
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_main_application",
|
||||
side_effect=["rename_key", KeyError],
|
||||
)
|
||||
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_for_existing_key",
|
||||
return_value="tag",
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_for_new_key",
|
||||
return_value="new_tags",
|
||||
)
|
||||
|
||||
with pytest.raises(KeyError):
|
||||
app.main_app()
|
||||
captured = capsys.readouterr()
|
||||
assert "WARNING | No notes were changed" in captured.out
|
||||
|
||||
|
||||
def test_rename_inline_tag_success(test_application, mocker, capsys) -> None:
|
||||
def test_delete_inline_tag(test_application, mocker, capsys) -> None:
|
||||
"""Test renaming an inline tag."""
|
||||
app = test_application
|
||||
app.load_vault()
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_main_application",
|
||||
side_effect=["rename_inline_tag", KeyError],
|
||||
"obsidian_metadata.models.application.Questions.ask_application_main",
|
||||
side_effect=["delete_metadata", KeyError],
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_for_existing_inline_tag",
|
||||
return_value="breakfast",
|
||||
"obsidian_metadata.models.application.Questions.ask_selection",
|
||||
side_effect=["delete_inline_tag", "back"],
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_for_new_tag",
|
||||
return_value="new_tag",
|
||||
)
|
||||
|
||||
with pytest.raises(KeyError):
|
||||
app.main_app()
|
||||
captured = capsys.readouterr()
|
||||
assert captured.out == Regex(r"Renamed.*breakfast.*to.*new_tag.*in.*\d+.*notes", re.DOTALL)
|
||||
|
||||
|
||||
def test_rename_inline_tag_fail(test_application, mocker, capsys) -> None:
|
||||
"""Test renaming an inline tag."""
|
||||
app = test_application
|
||||
app.load_vault()
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_main_application",
|
||||
side_effect=["rename_inline_tag", KeyError],
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_for_existing_inline_tag",
|
||||
return_value="not_a_tag",
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_for_new_tag",
|
||||
return_value="new_tag",
|
||||
)
|
||||
|
||||
with pytest.raises(KeyError):
|
||||
app.main_app()
|
||||
captured = capsys.readouterr()
|
||||
assert captured.out == Regex(r"WARNING +\| No notes were changed", re.DOTALL)
|
||||
|
||||
|
||||
def test_delete_inline_tag_success(test_application, mocker, capsys) -> None:
|
||||
"""Test renaming an inline tag."""
|
||||
app = test_application
|
||||
app.load_vault()
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_main_application",
|
||||
side_effect=["delete_inline_tag", KeyError],
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_for_existing_inline_tag",
|
||||
return_value="breakfast",
|
||||
)
|
||||
|
||||
with pytest.raises(KeyError):
|
||||
app.main_app()
|
||||
captured = capsys.readouterr()
|
||||
assert captured.out == Regex(r"SUCCESS +\| Deleted.*\d+.*notes", re.DOTALL)
|
||||
|
||||
|
||||
def test_delete_inline_tag_fail(test_application, mocker, capsys) -> None:
|
||||
"""Test renaming an inline tag."""
|
||||
app = test_application
|
||||
app.load_vault()
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_main_application",
|
||||
side_effect=["delete_inline_tag", KeyError],
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_for_existing_inline_tag",
|
||||
"obsidian_metadata.models.application.Questions.ask_existing_inline_tag",
|
||||
return_value="not_a_tag_in_vault",
|
||||
)
|
||||
|
||||
with pytest.raises(KeyError):
|
||||
app.main_app()
|
||||
app.application_main()
|
||||
captured = capsys.readouterr()
|
||||
assert captured.out == Regex(r"WARNING +\| No notes were changed", re.DOTALL)
|
||||
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_application_main",
|
||||
side_effect=["delete_metadata", KeyError],
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_selection",
|
||||
side_effect=["delete_inline_tag", "back"],
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_existing_inline_tag",
|
||||
return_value="breakfast",
|
||||
)
|
||||
|
||||
def test_delete_key_success(test_application, mocker, capsys) -> None:
|
||||
with pytest.raises(KeyError):
|
||||
app.application_main()
|
||||
captured = capsys.readouterr()
|
||||
assert captured.out == Regex(r"SUCCESS +\| Deleted.*\d+.*notes", re.DOTALL)
|
||||
|
||||
|
||||
def test_delete_key(test_application, mocker, capsys) -> None:
|
||||
"""Test renaming an inline tag."""
|
||||
app = test_application
|
||||
app.load_vault()
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_main_application",
|
||||
side_effect=["delete_key", KeyError],
|
||||
"obsidian_metadata.models.application.Questions.ask_application_main",
|
||||
side_effect=["delete_metadata", KeyError],
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_for_existing_keys_regex",
|
||||
"obsidian_metadata.models.application.Questions.ask_selection",
|
||||
side_effect=["delete_key", "back"],
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_existing_keys_regex",
|
||||
return_value=r"\d{7}",
|
||||
)
|
||||
|
||||
with pytest.raises(KeyError):
|
||||
app.application_main()
|
||||
captured = capsys.readouterr()
|
||||
assert captured.out == Regex(r"WARNING +\| No notes found with a.*key.*matching", re.DOTALL)
|
||||
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_application_main",
|
||||
side_effect=["delete_metadata", KeyError],
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_selection",
|
||||
side_effect=["delete_key", "back"],
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_existing_keys_regex",
|
||||
return_value=r"d\w+",
|
||||
)
|
||||
|
||||
with pytest.raises(KeyError):
|
||||
app.main_app()
|
||||
app.application_main()
|
||||
captured = capsys.readouterr()
|
||||
assert captured.out == Regex(
|
||||
r"SUCCESS +\|.*Deleted.*keys.*matching:.*d\\w\+.*from.*10", re.DOTALL
|
||||
)
|
||||
|
||||
|
||||
def test_delete_key_fail(test_application, mocker, capsys) -> None:
|
||||
def test_delete_value(test_application, mocker, capsys) -> None:
|
||||
"""Test renaming an inline tag."""
|
||||
app = test_application
|
||||
app.load_vault()
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_main_application",
|
||||
side_effect=["delete_key", KeyError],
|
||||
"obsidian_metadata.models.application.Questions.ask_application_main",
|
||||
side_effect=["delete_metadata", KeyError],
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_for_existing_keys_regex",
|
||||
return_value=r"\d{7}",
|
||||
)
|
||||
|
||||
with pytest.raises(KeyError):
|
||||
app.main_app()
|
||||
captured = capsys.readouterr()
|
||||
assert captured.out == Regex(r"WARNING +\| No notes found with a.*key.*matching", re.DOTALL)
|
||||
|
||||
|
||||
def test_rename_value_success(test_application, mocker, capsys) -> None:
|
||||
"""Test renaming an inline tag."""
|
||||
app = test_application
|
||||
app.load_vault()
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_main_application",
|
||||
side_effect=["rename_value", KeyError],
|
||||
"obsidian_metadata.models.application.Questions.ask_selection",
|
||||
side_effect=["delete_value", "back"],
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_for_existing_key",
|
||||
"obsidian_metadata.models.application.Questions.ask_existing_key",
|
||||
return_value="area",
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_for_existing_value",
|
||||
return_value="frontmatter",
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_for_new_value",
|
||||
return_value="new_key",
|
||||
"obsidian_metadata.models.application.Questions.ask_existing_value_regex",
|
||||
return_value=r"\d{7}",
|
||||
)
|
||||
with pytest.raises(KeyError):
|
||||
app.main_app()
|
||||
app.application_main()
|
||||
captured = capsys.readouterr()
|
||||
assert captured.out == Regex(r"WARNING +\| No notes found matching:", re.DOTALL)
|
||||
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_application_main",
|
||||
side_effect=["delete_metadata", KeyError],
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_selection",
|
||||
side_effect=["delete_value", "back"],
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_existing_key",
|
||||
return_value="area",
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_existing_value_regex",
|
||||
return_value=r"^front\w+$",
|
||||
)
|
||||
with pytest.raises(KeyError):
|
||||
app.application_main()
|
||||
captured = capsys.readouterr()
|
||||
assert captured.out == Regex(
|
||||
r"SUCCESS | Renamed 'area:frontmatter' to 'area:new_key'", re.DOTALL
|
||||
r"SUCCESS +\| Deleted value.*\^front\\w\+\$.*from.*key.*area.*in.*\d+.*notes", re.DOTALL
|
||||
)
|
||||
assert captured.out == Regex(r".*in.*\d+.*notes.*", re.DOTALL)
|
||||
|
||||
|
||||
def test_filter_notes_filter(test_application, mocker, capsys) -> None:
|
||||
"""Test renaming a key."""
|
||||
app = test_application
|
||||
app.load_vault()
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_application_main",
|
||||
side_effect=["filter_notes", KeyError],
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_selection",
|
||||
side_effect=["apply_filter", "list_notes", "back"],
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_filter_path",
|
||||
return_value="inline",
|
||||
)
|
||||
|
||||
with pytest.raises(KeyError):
|
||||
app.application_main()
|
||||
captured = capsys.readouterr()
|
||||
assert captured.out == Regex(r"SUCCESS +\| Loaded.*\d+.*notes from.*\d+.*total", re.DOTALL)
|
||||
assert "02 inline/inline 2.md" in captured.out
|
||||
assert "03 mixed/mixed 1.md" not in captured.out
|
||||
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_application_main",
|
||||
side_effect=["filter_notes", KeyError],
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_selection",
|
||||
side_effect=["apply_filter", "list_notes", "back"],
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_filter_path",
|
||||
return_value="",
|
||||
)
|
||||
|
||||
with pytest.raises(KeyError):
|
||||
app.application_main()
|
||||
captured = capsys.readouterr()
|
||||
assert captured.out == Regex(r"SUCCESS +\| Loaded all.*\d+.*total notes", re.DOTALL)
|
||||
assert "02 inline/inline 2.md" in captured.out
|
||||
assert "03 mixed/mixed 1.md" in captured.out
|
||||
|
||||
|
||||
def test_inspect_metadata_all(test_application, mocker, capsys) -> None:
|
||||
"""Test backing up a vault."""
|
||||
app = test_application
|
||||
app.load_vault()
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_application_main",
|
||||
side_effect=["inspect_metadata", KeyError],
|
||||
)
|
||||
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_selection",
|
||||
side_effect=["all_metadata", "back"],
|
||||
)
|
||||
with pytest.raises(KeyError):
|
||||
app.application_main()
|
||||
captured = capsys.readouterr()
|
||||
assert captured.out == Regex(r"type +│ article", re.DOTALL)
|
||||
|
||||
|
||||
def test_rename_inline_tag(test_application, mocker, capsys) -> None:
|
||||
"""Test renaming an inline tag."""
|
||||
app = test_application
|
||||
app.load_vault()
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_application_main",
|
||||
side_effect=["rename_metadata", KeyError],
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_selection",
|
||||
side_effect=["rename_inline_tag", "back"],
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_existing_inline_tag",
|
||||
return_value="not_a_tag",
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_new_tag",
|
||||
return_value="new_tag",
|
||||
)
|
||||
|
||||
with pytest.raises(KeyError):
|
||||
app.application_main()
|
||||
captured = capsys.readouterr()
|
||||
assert captured.out == Regex(r"WARNING +\| No notes were changed", re.DOTALL)
|
||||
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_application_main",
|
||||
side_effect=["rename_metadata", KeyError],
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_selection",
|
||||
side_effect=["rename_inline_tag", "back"],
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_existing_inline_tag",
|
||||
return_value="breakfast",
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_new_tag",
|
||||
return_value="new_tag",
|
||||
)
|
||||
|
||||
with pytest.raises(KeyError):
|
||||
app.application_main()
|
||||
captured = capsys.readouterr()
|
||||
assert captured.out == Regex(r"Renamed.*breakfast.*to.*new_tag.*in.*\d+.*notes", re.DOTALL)
|
||||
|
||||
|
||||
def test_rename_key(test_application, mocker, capsys) -> None:
|
||||
"""Test renaming a key."""
|
||||
app = test_application
|
||||
app.load_vault()
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_application_main",
|
||||
side_effect=["rename_metadata", KeyError],
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_selection",
|
||||
side_effect=["rename_key", "back"],
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_existing_key",
|
||||
return_value="tag",
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_new_key",
|
||||
return_value="new_tags",
|
||||
)
|
||||
|
||||
with pytest.raises(KeyError):
|
||||
app.application_main()
|
||||
captured = capsys.readouterr()
|
||||
assert "WARNING | No notes were changed" in captured.out
|
||||
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_application_main",
|
||||
side_effect=["rename_metadata", KeyError],
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_selection",
|
||||
side_effect=["rename_key", "back"],
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_existing_key",
|
||||
return_value="tags",
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_new_key",
|
||||
return_value="new_tags",
|
||||
)
|
||||
|
||||
with pytest.raises(KeyError):
|
||||
app.application_main()
|
||||
captured = capsys.readouterr()
|
||||
assert captured.out == Regex(r"Renamed.*tags.*to.*new_tags.*in.*\d+.*notes", re.DOTALL)
|
||||
|
||||
|
||||
def test_rename_value_fail(test_application, mocker, capsys) -> None:
|
||||
@@ -305,71 +369,57 @@ def test_rename_value_fail(test_application, mocker, capsys) -> None:
|
||||
app = test_application
|
||||
app.load_vault()
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_main_application",
|
||||
side_effect=["rename_value", KeyError],
|
||||
"obsidian_metadata.models.application.Questions.ask_application_main",
|
||||
side_effect=["rename_metadata", KeyError],
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_for_existing_key",
|
||||
"obsidian_metadata.models.application.Questions.ask_selection",
|
||||
side_effect=["rename_value", "back"],
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_existing_key",
|
||||
return_value="area",
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_for_existing_value",
|
||||
"obsidian_metadata.models.application.Questions.ask_existing_value",
|
||||
return_value="not_exists",
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_for_new_value",
|
||||
"obsidian_metadata.models.application.Questions.ask_new_value",
|
||||
return_value="new_key",
|
||||
)
|
||||
with pytest.raises(KeyError):
|
||||
app.main_app()
|
||||
app.application_main()
|
||||
captured = capsys.readouterr()
|
||||
assert captured.out == Regex(r"WARNING +\| No notes were changed", re.DOTALL)
|
||||
|
||||
|
||||
def test_delete_value_success(test_application, mocker, capsys) -> None:
|
||||
"""Test renaming an inline tag."""
|
||||
app = test_application
|
||||
app.load_vault()
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_main_application",
|
||||
side_effect=["delete_value", KeyError],
|
||||
"obsidian_metadata.models.application.Questions.ask_application_main",
|
||||
side_effect=["rename_metadata", KeyError],
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_for_existing_key",
|
||||
"obsidian_metadata.models.application.Questions.ask_selection",
|
||||
side_effect=["rename_value", "back"],
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_existing_key",
|
||||
return_value="area",
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_for_existing_value_regex",
|
||||
return_value=r"^front\w+$",
|
||||
"obsidian_metadata.models.application.Questions.ask_existing_value",
|
||||
return_value="frontmatter",
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_new_value",
|
||||
return_value="new_key",
|
||||
)
|
||||
with pytest.raises(KeyError):
|
||||
app.main_app()
|
||||
app.application_main()
|
||||
captured = capsys.readouterr()
|
||||
assert captured.out == Regex(
|
||||
r"SUCCESS +\| Deleted value.*\^front\\w\+\$.*from.*key.*area.*in.*\d+.*notes", re.DOTALL
|
||||
r"SUCCESS +\| Renamed.*'area:frontmatter'.*to.*'area:new_key'", re.DOTALL
|
||||
)
|
||||
|
||||
|
||||
def test_delete_value_fail(test_application, mocker, capsys) -> None:
|
||||
"""Test renaming an inline tag."""
|
||||
app = test_application
|
||||
app.load_vault()
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_main_application",
|
||||
side_effect=["delete_value", KeyError],
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_for_existing_key",
|
||||
return_value="area",
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_for_existing_value_regex",
|
||||
return_value=r"\d{7}",
|
||||
)
|
||||
with pytest.raises(KeyError):
|
||||
app.main_app()
|
||||
captured = capsys.readouterr()
|
||||
assert captured.out == Regex(r"WARNING +\| No notes found matching:", re.DOTALL)
|
||||
assert captured.out == Regex(r".*in.*\d+.*notes.*", re.DOTALL)
|
||||
|
||||
|
||||
def test_review_no_changes(test_application, mocker, capsys) -> None:
|
||||
@@ -377,11 +427,11 @@ def test_review_no_changes(test_application, mocker, capsys) -> None:
|
||||
app = test_application
|
||||
app.load_vault()
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_main_application",
|
||||
"obsidian_metadata.models.application.Questions.ask_application_main",
|
||||
side_effect=["review_changes", KeyError],
|
||||
)
|
||||
with pytest.raises(KeyError):
|
||||
app.main_app()
|
||||
app.application_main()
|
||||
captured = capsys.readouterr()
|
||||
assert captured.out == Regex(r"INFO +\| No changes to review", re.DOTALL)
|
||||
|
||||
@@ -391,24 +441,68 @@ def test_review_changes(test_application, mocker, capsys) -> None:
|
||||
app = test_application
|
||||
app.load_vault()
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_main_application",
|
||||
side_effect=["delete_key", "review_changes", KeyError],
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_for_existing_keys_regex",
|
||||
return_value=r"d\w+",
|
||||
"obsidian_metadata.models.application.Questions.ask_application_main",
|
||||
side_effect=["rename_metadata", "review_changes", KeyError],
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_confirm",
|
||||
return_value=True,
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_for_selection",
|
||||
side_effect=[1, "return"],
|
||||
"obsidian_metadata.models.application.Questions.ask_existing_key",
|
||||
return_value="tags",
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_new_key",
|
||||
return_value="new_tags",
|
||||
)
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_selection",
|
||||
side_effect=["rename_key", 1, "return"],
|
||||
)
|
||||
with pytest.raises(KeyError):
|
||||
app.main_app()
|
||||
app.application_main()
|
||||
captured = capsys.readouterr()
|
||||
assert captured.out == Regex(r".*Found.*\d+.*changed notes in the vault.*", re.DOTALL)
|
||||
assert "- date_created: 2022-12-22" in captured.out
|
||||
assert "+ - breakfast" in captured.out
|
||||
assert "- tags:" in captured.out
|
||||
assert "+ new_tags:" in captured.out
|
||||
|
||||
|
||||
def test_vault_backup(test_application, mocker, capsys) -> None:
|
||||
"""Test backing up a vault."""
|
||||
app = test_application
|
||||
app.load_vault()
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_application_main",
|
||||
side_effect=["vault_actions", KeyError],
|
||||
)
|
||||
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_selection",
|
||||
side_effect=["backup_vault", "back"],
|
||||
)
|
||||
with pytest.raises(KeyError):
|
||||
app.application_main()
|
||||
captured = capsys.readouterr()
|
||||
assert captured.out == Regex(r"SUCCESS +\|.*application\.bak", re.DOTALL)
|
||||
|
||||
|
||||
def test_vault_delete(test_application, mocker, capsys, tmp_path) -> None:
|
||||
"""Test backing up a vault."""
|
||||
app = test_application
|
||||
backup_path = Path(tmp_path / "application.bak")
|
||||
backup_path.mkdir()
|
||||
app.load_vault()
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_application_main",
|
||||
side_effect=["vault_actions", KeyError],
|
||||
)
|
||||
|
||||
mocker.patch(
|
||||
"obsidian_metadata.models.application.Questions.ask_selection",
|
||||
side_effect=["delete_backup", "back"],
|
||||
)
|
||||
with pytest.raises(KeyError):
|
||||
app.application_main()
|
||||
captured = capsys.readouterr()
|
||||
assert captured.out == Regex(r"SUCCESS +\| Backup deleted", re.DOTALL)
|
||||
|
||||
@@ -222,6 +222,71 @@ def test_frontmatter_contains() -> None:
|
||||
assert frontmatter.contains("key", r"\w\d_", is_regex=True) is True
|
||||
|
||||
|
||||
def test_frontmatter_add() -> None:
|
||||
"""Test frontmatter add."""
|
||||
frontmatter = Frontmatter(FRONTMATTER_CONTENT)
|
||||
|
||||
assert frontmatter.add("frontmatter_Key1") is False
|
||||
assert frontmatter.add("added_key") is True
|
||||
assert frontmatter.dict == {
|
||||
"added_key": [],
|
||||
"frontmatter_Key1": ["frontmatter_Key1_value"],
|
||||
"frontmatter_Key2": ["article", "note"],
|
||||
"shared_key1": ["shared_key1_value"],
|
||||
"tags": ["tag_1", "tag_2", "📅/tag_3"],
|
||||
}
|
||||
|
||||
assert frontmatter.add("added_key", "added_value") is True
|
||||
assert frontmatter.dict == {
|
||||
"added_key": ["added_value"],
|
||||
"frontmatter_Key1": ["frontmatter_Key1_value"],
|
||||
"frontmatter_Key2": ["article", "note"],
|
||||
"shared_key1": ["shared_key1_value"],
|
||||
"tags": ["tag_1", "tag_2", "📅/tag_3"],
|
||||
}
|
||||
|
||||
assert frontmatter.add("added_key", "added_value_2") is True
|
||||
assert frontmatter.dict == {
|
||||
"added_key": ["added_value", "added_value_2"],
|
||||
"frontmatter_Key1": ["frontmatter_Key1_value"],
|
||||
"frontmatter_Key2": ["article", "note"],
|
||||
"shared_key1": ["shared_key1_value"],
|
||||
"tags": ["tag_1", "tag_2", "📅/tag_3"],
|
||||
}
|
||||
|
||||
assert frontmatter.add("added_key", ["added_value_3", "added_value_4"]) is True
|
||||
assert frontmatter.dict == {
|
||||
"added_key": ["added_value", "added_value_2", "added_value_3", "added_value_4"],
|
||||
"frontmatter_Key1": ["frontmatter_Key1_value"],
|
||||
"frontmatter_Key2": ["article", "note"],
|
||||
"shared_key1": ["shared_key1_value"],
|
||||
"tags": ["tag_1", "tag_2", "📅/tag_3"],
|
||||
}
|
||||
|
||||
assert frontmatter.add("added_key2", ["added_value_1", "added_value_2"]) is True
|
||||
assert frontmatter.dict == {
|
||||
"added_key": ["added_value", "added_value_2", "added_value_3", "added_value_4"],
|
||||
"added_key2": ["added_value_1", "added_value_2"],
|
||||
"frontmatter_Key1": ["frontmatter_Key1_value"],
|
||||
"frontmatter_Key2": ["article", "note"],
|
||||
"shared_key1": ["shared_key1_value"],
|
||||
"tags": ["tag_1", "tag_2", "📅/tag_3"],
|
||||
}
|
||||
|
||||
assert frontmatter.add("added_key3", "added_value_1") is True
|
||||
assert frontmatter.dict == {
|
||||
"added_key": ["added_value", "added_value_2", "added_value_3", "added_value_4"],
|
||||
"added_key2": ["added_value_1", "added_value_2"],
|
||||
"added_key3": ["added_value_1"],
|
||||
"frontmatter_Key1": ["frontmatter_Key1_value"],
|
||||
"frontmatter_Key2": ["article", "note"],
|
||||
"shared_key1": ["shared_key1_value"],
|
||||
"tags": ["tag_1", "tag_2", "📅/tag_3"],
|
||||
}
|
||||
|
||||
assert frontmatter.add("added_key3", "added_value_1") is False
|
||||
|
||||
|
||||
def test_frontmatter_rename() -> None:
|
||||
"""Test frontmatter rename."""
|
||||
frontmatter = Frontmatter(FRONTMATTER_CONTENT)
|
||||
|
||||
@@ -7,6 +7,7 @@ from pathlib import Path
|
||||
import pytest
|
||||
import typer
|
||||
|
||||
from obsidian_metadata.models.enums import MetadataType
|
||||
from obsidian_metadata.models.notes import Note
|
||||
from tests.helpers import Regex
|
||||
|
||||
@@ -102,6 +103,65 @@ def test_append(sample_note) -> None:
|
||||
assert len(re.findall(re.escape(string2), note.file_content)) == 2
|
||||
|
||||
|
||||
def test_add_metadata(sample_note) -> None:
|
||||
"""Test adding metadata."""
|
||||
note = Note(note_path=sample_note)
|
||||
assert note.add_metadata(MetadataType.FRONTMATTER, "frontmatter_Key1") is False
|
||||
assert note.add_metadata(MetadataType.FRONTMATTER, "shared_key1", "shared_key1_value") is False
|
||||
assert note.add_metadata(MetadataType.FRONTMATTER, "new_key1") is True
|
||||
assert note.frontmatter.dict == {
|
||||
"date_created": ["2022-12-22"],
|
||||
"frontmatter_Key1": ["author name"],
|
||||
"frontmatter_Key2": ["article", "note"],
|
||||
"new_key1": [],
|
||||
"shared_key1": ["shared_key1_value"],
|
||||
"shared_key2": ["shared_key2_value1"],
|
||||
"tags": [
|
||||
"frontmatter_tag1",
|
||||
"frontmatter_tag2",
|
||||
"shared_tag",
|
||||
"📅/frontmatter_tag3",
|
||||
],
|
||||
}
|
||||
assert note.add_metadata(MetadataType.FRONTMATTER, "new_key2", "new_key2_value") is True
|
||||
assert note.frontmatter.dict == {
|
||||
"date_created": ["2022-12-22"],
|
||||
"frontmatter_Key1": ["author name"],
|
||||
"frontmatter_Key2": ["article", "note"],
|
||||
"new_key1": [],
|
||||
"new_key2": ["new_key2_value"],
|
||||
"shared_key1": ["shared_key1_value"],
|
||||
"shared_key2": ["shared_key2_value1"],
|
||||
"tags": [
|
||||
"frontmatter_tag1",
|
||||
"frontmatter_tag2",
|
||||
"shared_tag",
|
||||
"📅/frontmatter_tag3",
|
||||
],
|
||||
}
|
||||
assert (
|
||||
note.add_metadata(
|
||||
MetadataType.FRONTMATTER, "new_key2", ["new_key2_value2", "new_key2_value3"]
|
||||
)
|
||||
is True
|
||||
)
|
||||
assert note.frontmatter.dict == {
|
||||
"date_created": ["2022-12-22"],
|
||||
"frontmatter_Key1": ["author name"],
|
||||
"frontmatter_Key2": ["article", "note"],
|
||||
"new_key1": [],
|
||||
"new_key2": ["new_key2_value", "new_key2_value2", "new_key2_value3"],
|
||||
"shared_key1": ["shared_key1_value"],
|
||||
"shared_key2": ["shared_key2_value1"],
|
||||
"tags": [
|
||||
"frontmatter_tag1",
|
||||
"frontmatter_tag2",
|
||||
"shared_tag",
|
||||
"📅/frontmatter_tag3",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def test_contains_inline_tag(sample_note) -> None:
|
||||
"""Test contains inline tag."""
|
||||
note = Note(note_path=sample_note)
|
||||
@@ -212,9 +272,6 @@ def test_print_note(sample_note, capsys) -> None:
|
||||
def test_print_diff(sample_note, capsys) -> None:
|
||||
"""Test printing diff."""
|
||||
note = Note(note_path=sample_note)
|
||||
note.print_diff()
|
||||
captured = capsys.readouterr()
|
||||
assert captured.out == ""
|
||||
|
||||
note.append("This is a test string.")
|
||||
note.print_diff()
|
||||
|
||||
@@ -26,7 +26,6 @@ def test_validate_valid_regex() -> None:
|
||||
questions = Questions(vault=VAULT)
|
||||
assert questions._validate_valid_vault_regex(r".*\.md") is True
|
||||
assert "Invalid regex" in questions._validate_valid_vault_regex("[")
|
||||
assert "Regex cannot be empty" in questions._validate_valid_vault_regex("")
|
||||
assert "Regex does not match paths" in questions._validate_valid_vault_regex(r"\d\d\d\w\d")
|
||||
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ from pathlib import Path
|
||||
|
||||
from obsidian_metadata._config import Config
|
||||
from obsidian_metadata.models import Vault
|
||||
from obsidian_metadata.models.enums import MetadataType
|
||||
from tests.helpers import Regex
|
||||
|
||||
|
||||
@@ -155,7 +156,7 @@ def test_info(test_vault, capsys):
|
||||
|
||||
captured = capsys.readouterr()
|
||||
assert captured.out == Regex(r"Vault +\│ /[\d\w]+")
|
||||
assert captured.out == Regex(r"Notes being edited +\│ \d+")
|
||||
assert captured.out == Regex(r"Notes in scope +\│ \d+")
|
||||
assert captured.out == Regex(r"Backup +\│ None")
|
||||
|
||||
|
||||
@@ -170,6 +171,90 @@ def test_contains_inline_tag(test_vault) -> None:
|
||||
assert vault.contains_inline_tag("intext_tag2") is True
|
||||
|
||||
|
||||
def test_add_metadata(test_vault) -> None:
|
||||
"""Test adding metadata to the vault."""
|
||||
vault_path = test_vault
|
||||
config = Config(config_path="tests/fixtures/test_vault_config.toml", vault_path=vault_path)
|
||||
vault_config = config.vaults[0]
|
||||
vault = Vault(config=vault_config)
|
||||
|
||||
assert vault.add_metadata(MetadataType.FRONTMATTER, "new_key") == 3
|
||||
assert vault.metadata.dict == {
|
||||
"Inline Tags": [
|
||||
"ignored_file_tag2",
|
||||
"inline_tag_bottom1",
|
||||
"inline_tag_bottom2",
|
||||
"inline_tag_top1",
|
||||
"inline_tag_top2",
|
||||
"intext_tag1",
|
||||
"intext_tag2",
|
||||
"shared_tag",
|
||||
],
|
||||
"author": ["author name"],
|
||||
"bottom_key1": ["bottom_key1_value"],
|
||||
"bottom_key2": ["bottom_key2_value"],
|
||||
"date_created": ["2022-12-22"],
|
||||
"emoji_📅_key": ["emoji_📅_key_value"],
|
||||
"frontmatter_Key1": ["author name"],
|
||||
"frontmatter_Key2": ["article", "note"],
|
||||
"ignored_frontmatter": ["ignore_me"],
|
||||
"intext_key": ["intext_value"],
|
||||
"new_key": [],
|
||||
"shared_key1": ["shared_key1_value"],
|
||||
"shared_key2": ["shared_key2_value1", "shared_key2_value2"],
|
||||
"tags": [
|
||||
"frontmatter_tag1",
|
||||
"frontmatter_tag2",
|
||||
"frontmatter_tag3",
|
||||
"ignored_file_tag1",
|
||||
"shared_tag",
|
||||
"📅/frontmatter_tag3",
|
||||
],
|
||||
"top_key1": ["top_key1_value"],
|
||||
"top_key2": ["top_key2_value"],
|
||||
"top_key3": ["top_key3_value_as_link"],
|
||||
"type": ["article", "note"],
|
||||
}
|
||||
assert vault.add_metadata(MetadataType.FRONTMATTER, "new_key2", "new_key2_value") == 3
|
||||
assert vault.metadata.dict == {
|
||||
"Inline Tags": [
|
||||
"ignored_file_tag2",
|
||||
"inline_tag_bottom1",
|
||||
"inline_tag_bottom2",
|
||||
"inline_tag_top1",
|
||||
"inline_tag_top2",
|
||||
"intext_tag1",
|
||||
"intext_tag2",
|
||||
"shared_tag",
|
||||
],
|
||||
"author": ["author name"],
|
||||
"bottom_key1": ["bottom_key1_value"],
|
||||
"bottom_key2": ["bottom_key2_value"],
|
||||
"date_created": ["2022-12-22"],
|
||||
"emoji_📅_key": ["emoji_📅_key_value"],
|
||||
"frontmatter_Key1": ["author name"],
|
||||
"frontmatter_Key2": ["article", "note"],
|
||||
"ignored_frontmatter": ["ignore_me"],
|
||||
"intext_key": ["intext_value"],
|
||||
"new_key": [],
|
||||
"new_key2": ["new_key2_value"],
|
||||
"shared_key1": ["shared_key1_value"],
|
||||
"shared_key2": ["shared_key2_value1", "shared_key2_value2"],
|
||||
"tags": [
|
||||
"frontmatter_tag1",
|
||||
"frontmatter_tag2",
|
||||
"frontmatter_tag3",
|
||||
"ignored_file_tag1",
|
||||
"shared_tag",
|
||||
"📅/frontmatter_tag3",
|
||||
],
|
||||
"top_key1": ["top_key1_value"],
|
||||
"top_key2": ["top_key2_value"],
|
||||
"top_key3": ["top_key3_value_as_link"],
|
||||
"type": ["article", "note"],
|
||||
}
|
||||
|
||||
|
||||
def test_contains_metadata(test_vault) -> None:
|
||||
"""Test if the vault contains a metadata key."""
|
||||
vault_path = test_vault
|
||||
|
||||
Reference in New Issue
Block a user