From 53280ee87b9c373219bca39f1d34b0c0a73b71cc Mon Sep 17 00:00:00 2001 From: "Felix Eckstein (Ext.)" Date: Thu, 25 Jun 2026 11:53:53 +0200 Subject: [PATCH 1/5] no more error when running in a worktree --- Makefile | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index fe4e0056..55c2db11 100644 --- a/Makefile +++ b/Makefile @@ -111,10 +111,9 @@ test-int: test-all .PHONY: test-all test-all: install - TEST_INTEGRATION=true poetry run pytest $(PACKAGES) $(PYTEST_OPTIONS) + TEST_INTEGRATION=true poetry run pytest $(PACKAGES) $(PYTEST_OPTIONS) ifndef DISABLE_COVERAGE - @ echo - poetry run coveragespace update overall + @ poetry run coveragespace update overall || echo "Coverage upload skipped: no remote origin configured" endif .PHONY: test-cover From dd0a7dd2b761a1ef894e9f1d4edd2f2495f3f353 Mon Sep 17 00:00:00 2001 From: "Felix Eckstein (Ext.)" Date: Thu, 25 Jun 2026 11:55:22 +0200 Subject: [PATCH 2/5] - added additional test case: REQ007 with a requirement of "Heading"-type - updated the creation of the golden master files --- .../publishers/tests/test_publisher_html.py | 9 +- .../tests/test_publisher_markdown.py | 7 +- doorstop/core/tests/__init__.py | 13 +++ doorstop/core/tests/files/REQ007.yml | 10 +++ doorstop/core/tests/test_all.py | 50 ++++++++--- doorstop/core/tests/test_document.py | 85 ++++++++++++++++--- doorstop/core/tests/test_importer.py | 17 +++- doorstop/core/tests/test_tree.py | 17 +++- 8 files changed, 167 insertions(+), 41 deletions(-) create mode 100644 doorstop/core/tests/files/REQ007.yml diff --git a/doorstop/core/publishers/tests/test_publisher_html.py b/doorstop/core/publishers/tests/test_publisher_html.py index 58a389f0..954fd591 100644 --- a/doorstop/core/publishers/tests/test_publisher_html.py +++ b/doorstop/core/publishers/tests/test_publisher_html.py @@ -325,6 +325,7 @@ def test_toc_no_links_or_heading_levels(self): {"depth": 2, "text": "1.6 Hello, world! (REQ004)", "uid": ""}, {"depth": 2, "text": "2.1 Plantuml (REQ002)", "uid": ""}, {"depth": 2, "text": "2.1 Hello, world! (REQ2-001)", "uid": ""}, + {'depth': 1, 'text': '3.0 My Heading', 'uid': ""} ] html_publisher = publisher.check(".html", self.document) toc = html_publisher.table_of_contents(linkify=None, obj=self.document) @@ -335,16 +336,13 @@ def test_toc_no_links(self): """Verify the table of contents is generated without heading levels""" expected = [ {"depth": 0, "text": "Table of Contents", "uid": "toc"}, - { - "depth": 3, - "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod (REQ001)", - "uid": "", - }, + {"depth": 3, "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod (REQ001)", "uid": ""}, {"depth": 2, "text": "Unicode: -40° ±1% (REQ003)", "uid": ""}, {"depth": 2, "text": "Hello, world! (REQ006)", "uid": ""}, {"depth": 2, "text": "Hello, world! (REQ004)", "uid": ""}, {"depth": 2, "text": "Plantuml (REQ002)", "uid": ""}, {"depth": 2, "text": "Hello, world! (REQ2-001)", "uid": ""}, + {"depth": 1, "text": "My Heading", "uid": ""}, ] html_publisher = publisher.check(".html", self.document) @@ -373,6 +371,7 @@ def test_toc(self): "text": "2.1 Hello, world! (REQ2-001)", "uid": UID("REQ2-001"), }, + {"depth": 1, "text": "3.0 My Heading", "uid": UID("REQ007")}, ] html_publisher = publisher.check(".html", self.document) toc = html_publisher.table_of_contents(linkify=True, obj=self.document) diff --git a/doorstop/core/publishers/tests/test_publisher_markdown.py b/doorstop/core/publishers/tests/test_publisher_markdown.py index e10bcead..3d2ab791 100644 --- a/doorstop/core/publishers/tests/test_publisher_markdown.py +++ b/doorstop/core/publishers/tests/test_publisher_markdown.py @@ -230,7 +230,8 @@ def test_toc_no_links_or_heading_levels(self): * 1.5 Hello, world! (REQ006) * 1.6 Hello, world! (REQ004) * 2.1 Plantuml (REQ002) - * 2.1 Hello, world! (REQ2-001)\n""" + * 2.1 Hello, world! (REQ2-001) + * 3.0 My Heading\n""" md_publisher = publisher.check(".md", self.document) toc = md_publisher.table_of_contents(linkify=None, obj=self.document) self.assertEqual(expected, toc) @@ -246,6 +247,7 @@ def test_toc_no_links(self): * Hello, world! (REQ004) * Plantuml (REQ002) * Hello, world! (REQ2-001) + * My Heading """ md_publisher = publisher.check(".md", self.document) toc = md_publisher.table_of_contents(linkify=None, obj=self.document) @@ -260,7 +262,8 @@ def test_toc(self): * [1.5 Hello, world! (REQ006)](#15-req006-req006) * [1.6 Hello, world! (REQ004)](#16-req004-req004) * [2.1 Plantuml (REQ002)](#21-plantuml-req002-req002) - * [2.1 Hello, world! (REQ2-001)](#21-req2-001-req2-001)\n""" + * [2.1 Hello, world! (REQ2-001)](#21-req2-001-req2-001) + * [3.0 My Heading](#30-my-heading-req007)\n""" self.maxDiff = None md_publisher = publisher.check(".md", self.document) toc = md_publisher.table_of_contents(linkify=True, obj=self.document) diff --git a/doorstop/core/tests/__init__.py b/doorstop/core/tests/__init__.py index aca01c4b..22857d8a 100644 --- a/doorstop/core/tests/__init__.py +++ b/doorstop/core/tests/__init__.py @@ -17,6 +17,19 @@ TESTS_ROOT = os.path.dirname(__file__) FILES = os.path.join(os.path.dirname(__file__), "files") +# Files that use the golden master pattern and are intentionally +# updated by tests - these should not be reset after each test run +GOLDEN_MASTER_FILES = { + os.path.join(FILES, "exported.yml"), + os.path.join(FILES, "exported.csv"), + os.path.join(FILES, "exported.tsv"), + os.path.join(FILES, "published.html"), + os.path.join(FILES, "published.md"), + os.path.join(FILES, "published.txt"), + os.path.join(FILES, "published2.html"), + os.path.join(FILES, "published2.md"), + os.path.join(FILES, "published2.txt"), +} FILES_MD = os.path.join(os.path.dirname(__file__), "files_md") SYS = os.path.join(FILES, "parent") TST = os.path.join(FILES, "child") diff --git a/doorstop/core/tests/files/REQ007.yml b/doorstop/core/tests/files/REQ007.yml new file mode 100644 index 00000000..25fea3bf --- /dev/null +++ b/doorstop/core/tests/files/REQ007.yml @@ -0,0 +1,10 @@ +active: true +derived: false +header: | + My Heading +level: 3.0 +links: [] +normative: false +ref: '' +reviewed: null +text: '' diff --git a/doorstop/core/tests/test_all.py b/doorstop/core/tests/test_all.py index add9c84b..23638e66 100644 --- a/doorstop/core/tests/test_all.py +++ b/doorstop/core/tests/test_all.py @@ -27,6 +27,7 @@ EMPTY, ENV, FILES, + GOLDEN_MASTER_FILES, FILES_MD, REASON, ROOT, @@ -77,6 +78,15 @@ def cleanup_test_yml_files(): if result.returncode == 0 and result.stdout.strip(): yaml_files = result.stdout.strip().split("\n") + # Exclude golden master files from reset + yaml_files = [ + f for f in yaml_files + if os.path.abspath(os.path.join(repo_root, f)) not in GOLDEN_MASTER_FILES + ] + + if not yaml_files: + continue + # Reset only these YAML files subprocess.run( ["git", "-c", "core.autocrlf=false", "checkout", "--"] + yaml_files, @@ -98,7 +108,6 @@ def cleanup_test_yml_files(): # Ignore errors in cleanup to prevent test failures pass - class TestItem(unittest.TestCase): """Integration tests for the Item class.""" @@ -215,7 +224,7 @@ def test_load(self): self.assertEqual("REQ", doc.prefix) self.assertEqual("yaml", doc.itemformat) self.assertEqual(2, doc.digits) - self.assertEqual(6, len(doc.items)) + self.assertEqual(7, len(doc.items)) def test_new(self): """Verify a new document can be created.""" @@ -238,7 +247,7 @@ def test_issues_count(self): issues = self.document.issues for issue in self.document.issues: logging.info(repr(issue)) - self.assertEqual(13, len(issues)) + self.assertEqual(15, len(issues)) @patch("doorstop.settings.REORDER", False) @patch("doorstop.settings.REVIEW_NEW_ITEMS", False) @@ -394,7 +403,7 @@ def test_issues_count(self): issues = self.tree.issues for issue in self.tree.issues: logging.info(repr(issue)) - self.assertEqual(15, len(issues)) + self.assertEqual(17, len(issues)) @patch("doorstop.settings.REORDER", False) @patch("doorstop.settings.REVIEW_NEW_ITEMS", False) @@ -600,8 +609,11 @@ def test_export_yml(self): # Assert self.assertIs(temp, path2) actual = read_yml(temp) - self.assertEqual(expected, actual) + # Assert + if actual != expected: + common.log.error(f"Published content changed: {path}") move_file(temp, path) + self.assertEqual(expected, actual) def test_export_csv(self): """Verify a document can be exported as a CSV file.""" @@ -613,8 +625,11 @@ def test_export_csv(self): # Assert self.assertIs(temp, path2) actual = read_csv(temp) - self.assertEqual(expected, actual) + # Assert + if actual != expected: + common.log.error(f"Published content changed: {path}") move_file(temp, path) + self.assertEqual(expected, actual) @patch("doorstop.settings.REVIEW_NEW_ITEMS", False) def test_export_tsv(self): @@ -627,9 +642,11 @@ def test_export_tsv(self): # Assert self.assertIs(temp, path2) actual = read_csv(temp, delimiter="\t") - self.assertEqual(expected, actual) + # Assert + if actual != expected: + common.log.error(f"Published content changed: {path}") move_file(temp, path) - + self.assertEqual(expected, actual) class TestPublisher(unittest.TestCase): """Integration tests for the doorstop.core.publisher module.""" @@ -684,8 +701,10 @@ def test_lines_text_document(self): lines = core.publisher.publish_lines(self.document, ".txt") text = "".join(line + "\n" for line in lines) # Assert + if text != expected: + common.log.error(f"Published content changed: {path}") + common.write_text(text, path) self.assertEqual(expected, text) - common.write_text(text, path) @patch("doorstop.settings.PUBLISH_CHILD_LINKS", False) def test_lines_text_document_without_child_links(self): @@ -696,8 +715,11 @@ def test_lines_text_document_without_child_links(self): lines = core.publisher.publish_lines(self.document, ".txt") text = "".join(line + "\n" for line in lines) # Assert + if text != expected: + common.log.error(f"Published content changed: {path}") + common.write_text(text, path) self.assertEqual(expected, text) - common.write_text(text, path) + def test_lines_markdown_document(self): """Verify Markdown can be published from a document.""" @@ -709,7 +731,7 @@ def test_lines_markdown_document(self): # Assert if text != expected: common.log.error(f"Published content changed: {path}") - common.write_text(text, path) + common.write_text(text, path) self.assertEqual(expected, text) @patch("doorstop.settings.PUBLISH_CHILD_LINKS", False) @@ -723,7 +745,7 @@ def test_lines_markdown_document_without_child_links(self): # Assert if text != expected: common.log.error(f"Published content changed: {path}") - common.write_text(text, path) + common.write_text(text, path) self.assertEqual(expected, text) @patch("plantuml_markdown.PlantUMLPreprocessor.run") @@ -751,7 +773,7 @@ def run(lines: List[str]) -> List[str]: # Assert if actual != expected: common.log.error(f"Published content changed: {path}") - common.write_text(actual, path) + common.write_text(actual, path) self.assertEqual(expected, actual) @patch("plantuml_markdown.PlantUMLPreprocessor.run") @@ -776,7 +798,7 @@ def run(lines: List[str]) -> List[str]: # Assert if actual != expected: common.log.error(f"Published content changed: {path}") - common.write_text(actual, path) + common.write_text(actual, path) self.assertEqual(expected, actual) diff --git a/doorstop/core/tests/test_document.py b/doorstop/core/tests/test_document.py index ec321537..27b4436b 100644 --- a/doorstop/core/tests/test_document.py +++ b/doorstop/core/tests/test_document.py @@ -297,12 +297,12 @@ def test_hash(self): def test_len(self): """Verify a document has a length.""" - self.assertEqual(6, len(self.document)) + self.assertEqual(7, len(self.document)) def test_items(self): """Verify the items in a document can be accessed.""" items = self.document.items - self.assertEqual(6, len(items)) + self.assertEqual(7, len(items)) for item in self.document: logging.debug("item: {}".format(item)) self.assertIs(self.document, item.document) @@ -313,7 +313,7 @@ def test_items_cache(self): self.document.tree = Mock() self.document.tree._item_cache = {} print(self.document.items) - self.assertEqual(7, len(self.document.tree._item_cache)) + self.assertEqual(8, len(self.document.tree._item_cache)) @patch("doorstop.core.document.Document", MockDocument) def test_new(self): @@ -381,7 +381,7 @@ def test_depth(self): def test_next_number(self): """Verify the next item number can be determined.""" - self.assertEqual(7, self.document.next_number) + self.assertEqual(8, self.document.next_number) def test_next_number_server(self): """Verify the next item number can be determined with a server.""" @@ -407,8 +407,9 @@ def test_index_set(self, mock_write_lines): " - REQ003: # Unicode: -40° ±1%", " - REQ006: # Hello, world!", " - REQ004: # Hello, world!", - " - REQ002: # Hello, world!", + " - REQ002: # Plantuml", " - REQ2-001: # Hello, world!", + " - REQ007: # My Heading", ] # Act self.document.index = True # create index @@ -450,6 +451,62 @@ def test_read_index(self, mock_write_lines): # Assert self.assertEqual(expected, actual) + @patch("doorstop.common.write_lines") + @patch("doorstop.settings.MAX_LINE_LENGTH", 40) + def test_read_index(self, mock_write_lines): + """Verify a document index can be read.""" + lines = """initial: 1.2.3 +outline: + - REQ001: # Lorem ipsum d... + - REQ003: # Unicode: -40° ±1% + - REQ004: # Hello, world! !['.. + - REQ002: # Hello, world! !["... + - REQ2-001: # Hello, world! + - REQ005: # My Heading""" + + expected = { + "initial": "1.2.3", + "outline": [ + {"REQ001": [{"text": "Lorem ipsum d..."}]}, + {"REQ003": [{"text": "Unicode: -40° ±1%"}]}, + {"REQ004": [{"text": "Hello, world! !['.."}]}, + {"REQ002": [{"text": 'Hello, world! !["...'}]}, + {"REQ2-001": [{"text": "Hello, world!"}]}, + {"REQ005": [{"text": "My Heading"}]}, + ], + } + # Act + with patch("builtins.open") as mock_open: + mock_open.side_effect = lambda *args, **kw: mock.mock_open( + read_data=lines + ).return_value + actual = self.document._read_index("mock_path") + # Assert + self.assertEqual(expected, actual) + + @patch("doorstop.common.write_lines") + @patch("doorstop.settings.MAX_LINE_LENGTH", 40) + def test_index_set(self, mock_write_lines): + """Verify an document's index can be created.""" + lines = [ + "initial: 1.2.3", + "outline:", + " - REQ001: # Lorem ipsum d...", + " - REQ003: # Unicode: -40° ±1%", + " - REQ006: # Hello, world!", + " - REQ004: # Hello, world!", + " - REQ002: # Hello, world!", + " - REQ2-001: # Hello, world!", + " - REQ007: # ", + ] + # Act + self.document.index = True # create index + # Assert + gen, path = mock_write_lines.call_args[0] + lines2 = list(gen)[8:] # skip lines of info comments + self.assertListEqual(lines, lines2) + self.assertEqual(os.path.join(FILES, "index.yml"), path) + @patch("doorstop.common.delete") def test_index_del(self, mock_delete): """Verify a document's index can be deleted.""" @@ -463,7 +520,7 @@ def test_add_item(self, mock_new, mock_reorder): with patch("doorstop.settings.REORDER", True): self.document.add_item() mock_new.assert_called_once_with( - None, self.document, FILES, ROOT, "REQ007", level=Level("2.2") + None, self.document, FILES, ROOT, "REQ008", level=Level("3.1") ) self.assertEqual(0, mock_reorder.call_count) @@ -474,7 +531,7 @@ def test_add_item_with_level(self, mock_new, mock_reorder): with patch("doorstop.settings.REORDER", True): item = self.document.add_item(level="4.2") mock_new.assert_called_once_with( - None, self.document, FILES, ROOT, "REQ007", level="4.2" + None, self.document, FILES, ROOT, "REQ008", level="4.2" ) mock_reorder.assert_called_once_with(keep=item) @@ -483,7 +540,7 @@ def test_add_item_with_number(self, mock_new): """Verify an item can be added to a document with a number.""" self.document.add_item(number=999) mock_new.assert_called_once_with( - None, self.document, FILES, ROOT, "REQ999", level=Level("2.2") + None, self.document, FILES, ROOT, "REQ999", level=Level("3.1") ) def test_add_item_with_no_sep(self): @@ -515,7 +572,7 @@ def test_add_item_with_name(self, mock_new): self.document.sep = "-" self.document.add_item(name="ABC") mock_new.assert_called_once_with( - None, self.document, FILES, ROOT, "REQ-ABC", level=Level("2.2") + None, self.document, FILES, ROOT, "REQ-ABC", level=Level("3.1") ) @patch("doorstop.core.item.Item.new") @@ -524,7 +581,7 @@ def test_add_item_with_number_name(self, mock_new): self.document.sep = "-" self.document.add_item(name="99") mock_new.assert_called_once_with( - None, self.document, FILES, ROOT, "REQ-099", level=Level("2.2") + None, self.document, FILES, ROOT, "REQ-099", level=Level("3.1") ) @patch("doorstop.core.item.Item.set_attributes") @@ -729,7 +786,7 @@ def test_validate(self, mock_reorder, mock_get_issues): with patch("doorstop.settings.REORDER", True): self.assertTrue(self.document.validate()) mock_reorder.assert_called_once_with(_items=self.document.items) - self.assertEqual(6, mock_get_issues.call_count) + self.assertEqual(7, mock_get_issues.call_count) @patch( "doorstop.core.validators.item_validator.ItemValidator.get_issues", @@ -753,14 +810,14 @@ def test_validate_hook(self): """Verify an item hook can be called.""" mock_hook = MagicMock() self.document.validate(item_hook=mock_hook) - self.assertEqual(6, mock_hook.call_count) + self.assertEqual(7, mock_hook.call_count) @patch("doorstop.core.item.Item.delete") @patch("os.rmdir") def test_delete(self, mock_delete, mock_item_delete): """Verify a document can be deleted.""" self.document.delete() - self.assertEqual(7, mock_item_delete.call_count) + self.assertEqual(8, mock_item_delete.call_count) self.assertEqual(1, mock_delete.call_count) self.document.delete() # ensure a second delete is ignored @@ -770,7 +827,7 @@ def test_delete_with_assets(self, mock_delete, mock_item_delete): """Verify a document's assets aren't deleted.""" mock_delete.side_effect = OSError self.document.delete() - self.assertEqual(7, mock_item_delete.call_count) + self.assertEqual(8, mock_item_delete.call_count) self.assertEqual(1, mock_delete.call_count) self.document.delete() # ensure a second delete is ignored diff --git a/doorstop/core/tests/test_importer.py b/doorstop/core/tests/test_importer.py index 1df09bf8..63015f64 100644 --- a/doorstop/core/tests/test_importer.py +++ b/doorstop/core/tests/test_importer.py @@ -90,7 +90,7 @@ def test_file_yml(self, mock_add_item): # Act importer._file_yml(path, mock_document) # Assert - self.assertEqual(6, mock_add_item.call_count) + self.assertEqual(7, mock_add_item.call_count) @patch("doorstop.core.importer.add_item") def test_file_yml_duplicates(self, mock_add_item): @@ -100,7 +100,7 @@ def test_file_yml_duplicates(self, mock_add_item): # Act importer._file_yml(path, mock_document) # Assert - self.assertEqual(6, mock_add_item.call_count) + self.assertEqual(7, mock_add_item.call_count) def test_file_yml_bad_format(self): """Verify YAML file import can handle bad data.""" @@ -202,6 +202,19 @@ def test_file_csv(self, mock_itemize): True, "", ], + [ + "REQ007", + "3.0", + "", + "", + "", + "", + True, + False, + "My Heading", + False, + "", + ], ] self.assertEqual(expected_data, data) self.assertIs(mock_document, document) diff --git a/doorstop/core/tests/test_tree.py b/doorstop/core/tests/test_tree.py index 4f21c0d6..34cec2a0 100644 --- a/doorstop/core/tests/test_tree.py +++ b/doorstop/core/tests/test_tree.py @@ -17,7 +17,7 @@ from doorstop.common import DoorstopError, DoorstopInfo, DoorstopWarning from doorstop.core.builder import build from doorstop.core.document import Document -from doorstop.core.tests import EMPTY, FILES, SYS, MockDocumentSkip +from doorstop.core.tests import EMPTY, FILES, GOLDEN_MASTER_FILES, SYS, MockDocumentSkip from doorstop.core.tree import Tree @@ -30,7 +30,7 @@ def reset_fixture_files(): try: test_dir = os.path.dirname(__file__) - # get all git-tracked YAML-files in files/ + # Get all git-tracked YAML files in files/ result = subprocess.run( ["git", "ls-files", "files/*.yml", "files/*.yaml"], capture_output=True, @@ -43,7 +43,17 @@ def reset_fixture_files(): if result.returncode == 0 and result.stdout.strip(): yaml_files = result.stdout.strip().split("\n") - # reset those files + # Exclude golden master files from reset as they are + # intentionally updated by tests using the golden master pattern + yaml_files = [ + f for f in yaml_files + if os.path.abspath(os.path.join(test_dir, f)) not in GOLDEN_MASTER_FILES + ] + + if not yaml_files: + return + + # Reset those files subprocess.run( ["git", "checkout", "--"] + yaml_files, capture_output=True, @@ -54,7 +64,6 @@ def reset_fixture_files(): except Exception: pass - @patch("doorstop.core.document.Document", MockDocumentSkip) class TestTreeStrings(unittest.TestCase): """Unit tests for the Tree class using strings.""" From e856d363c6f26bebe2df5f93099d3a0261ca3615 Mon Sep 17 00:00:00 2001 From: "Felix Eckstein (Ext.)" Date: Thu, 25 Jun 2026 11:55:49 +0200 Subject: [PATCH 3/5] - updated the golden master files to include the additional REQ007 test case --- doorstop/core/tests/files/exported.csv | 1 + doorstop/core/tests/files/exported.tsv | 1 + doorstop/core/tests/files/exported.yml | 12 ++++++++++++ doorstop/core/tests/files/published.html | 10 +++++++++- doorstop/core/tests/files/published.md | 2 ++ doorstop/core/tests/files/published.txt | 2 ++ doorstop/core/tests/files/published2.html | 10 +++++++++- doorstop/core/tests/files/published2.md | 2 ++ doorstop/core/tests/files/published2.txt | 2 ++ 9 files changed, 40 insertions(+), 2 deletions(-) diff --git a/doorstop/core/tests/files/exported.csv b/doorstop/core/tests/files/exported.csv index 2254cb6b..ab14fef3 100644 --- a/doorstop/core/tests/files/exported.csv +++ b/doorstop/core/tests/files/exported.csv @@ -38,3 +38,4 @@ Test Math Expressions in Latex Style: Inline Style 1: $a \ne 0$ Inline Style 2: \(ax^2 + bx + c = 0\) Multiline: $$x = {-b \pm \sqrt{b^2-4ac} \over 2a}.$$",,,REQ001,True,False,,True, +REQ007,3.0,,,,,True,False,My Heading,False, diff --git a/doorstop/core/tests/files/exported.tsv b/doorstop/core/tests/files/exported.tsv index 11b64e22..e4b4fea5 100644 --- a/doorstop/core/tests/files/exported.tsv +++ b/doorstop/core/tests/files/exported.tsv @@ -38,3 +38,4 @@ Test Math Expressions in Latex Style: Inline Style 1: $a \ne 0$ Inline Style 2: \(ax^2 + bx + c = 0\) Multiline: $$x = {-b \pm \sqrt{b^2-4ac} \over 2a}.$$" REQ001 True False True +REQ007 3.0 True False My Heading False diff --git a/doorstop/core/tests/files/exported.yml b/doorstop/core/tests/files/exported.yml index 94bb89c3..9a967913 100644 --- a/doorstop/core/tests/files/exported.yml +++ b/doorstop/core/tests/files/exported.yml @@ -113,3 +113,15 @@ REQ2-001: Inline Style 2: \(ax^2 + bx + c = 0\) Multiline: $$x = {-b \pm \sqrt{b^2-4ac} \over 2a}.$$ +REQ007: + active: true + derived: false + header: | + My Heading + level: 3.0 + links: [] + normative: false + ref: '' + reviewed: null + text: '' + diff --git a/doorstop/core/tests/files/published.html b/doorstop/core/tests/files/published.html index 33a8394e..3f533ad9 100644 --- a/doorstop/core/tests/files/published.html +++ b/doorstop/core/tests/files/published.html @@ -90,7 +90,14 @@ data-bs-placement="left" title="REQ2-001">2.1 Hello, world! (REQ2-001) - + +
  • + 3.0 My Heading +
  • @@ -186,6 +193,7 @@

    2.1 REQ2-001

    Multiline: $$x = {-b \pm \sqrt{b^2-4ac} \over 2a}.$$

    Parent links: REQ001

    Child links: TST001

    +

    3.0 My Heading

    diff --git a/doorstop/core/tests/files/published.md b/doorstop/core/tests/files/published.md index d4f595fa..cc556968 100644 --- a/doorstop/core/tests/files/published.md +++ b/doorstop/core/tests/files/published.md @@ -70,3 +70,5 @@ Multiline: $$x = {-b \pm \sqrt{b^2-4ac} \over 2a}.$$ *Child links: TST001* +# 3.0 My Heading {#REQ007} + diff --git a/doorstop/core/tests/files/published.txt b/doorstop/core/tests/files/published.txt index 2a6c6880..f713b9e7 100644 --- a/doorstop/core/tests/files/published.txt +++ b/doorstop/core/tests/files/published.txt @@ -73,3 +73,5 @@ Child links: TST001 +3.0 My Heading + diff --git a/doorstop/core/tests/files/published2.html b/doorstop/core/tests/files/published2.html index cf6e6d39..e54321a9 100644 --- a/doorstop/core/tests/files/published2.html +++ b/doorstop/core/tests/files/published2.html @@ -90,7 +90,14 @@ data-bs-placement="left" title="REQ2-001">2.1 Hello, world! (REQ2-001) - + +
  • + 3.0 My Heading +
  • @@ -184,6 +191,7 @@

    2.1 REQ2-001

    Inline Style 2: (ax^2 + bx + c = 0) Multiline: $$x = {-b \pm \sqrt{b^2-4ac} \over 2a}.$$

    Links: REQ001

    +

    3.0 My Heading

    diff --git a/doorstop/core/tests/files/published2.md b/doorstop/core/tests/files/published2.md index cf6447b6..c397ceb7 100644 --- a/doorstop/core/tests/files/published2.md +++ b/doorstop/core/tests/files/published2.md @@ -66,3 +66,5 @@ Multiline: $$x = {-b \pm \sqrt{b^2-4ac} \over 2a}.$$ *Links: REQ001* +# 3.0 My Heading {#REQ007} + diff --git a/doorstop/core/tests/files/published2.txt b/doorstop/core/tests/files/published2.txt index 1a8ff99a..ff2b7b66 100644 --- a/doorstop/core/tests/files/published2.txt +++ b/doorstop/core/tests/files/published2.txt @@ -69,3 +69,5 @@ Links: REQ001 +3.0 My Heading + From 8c602b00c6518b3c2687e5c3ac1c114da7fea884 Mon Sep 17 00:00:00 2001 From: "Felix Eckstein (Ext.)" Date: Thu, 25 Jun 2026 16:07:05 +0200 Subject: [PATCH 4/5] running checks and repaired makefile --- Makefile | 4 +- .../publishers/tests/test_publisher_html.py | 8 ++- doorstop/core/tests/test_all.py | 23 +++++--- doorstop/core/tests/test_document.py | 56 ------------------- doorstop/core/tests/test_tree.py | 4 +- 5 files changed, 27 insertions(+), 68 deletions(-) diff --git a/Makefile b/Makefile index 55c2db11..10e3d782 100644 --- a/Makefile +++ b/Makefile @@ -111,9 +111,9 @@ test-int: test-all .PHONY: test-all test-all: install - TEST_INTEGRATION=true poetry run pytest $(PACKAGES) $(PYTEST_OPTIONS) + TEST_INTEGRATION=true poetry run pytest $(PACKAGES) $(PYTEST_OPTIONS) ifndef DISABLE_COVERAGE - @ poetry run coveragespace update overall || echo "Coverage upload skipped: no remote origin configured" + @ poetry run coveragespace update overall || echo "Coverage upload skipped: no remote origin configured" endif .PHONY: test-cover diff --git a/doorstop/core/publishers/tests/test_publisher_html.py b/doorstop/core/publishers/tests/test_publisher_html.py index 954fd591..5c6406a1 100644 --- a/doorstop/core/publishers/tests/test_publisher_html.py +++ b/doorstop/core/publishers/tests/test_publisher_html.py @@ -325,7 +325,7 @@ def test_toc_no_links_or_heading_levels(self): {"depth": 2, "text": "1.6 Hello, world! (REQ004)", "uid": ""}, {"depth": 2, "text": "2.1 Plantuml (REQ002)", "uid": ""}, {"depth": 2, "text": "2.1 Hello, world! (REQ2-001)", "uid": ""}, - {'depth': 1, 'text': '3.0 My Heading', 'uid': ""} + {"depth": 1, "text": "3.0 My Heading", "uid": ""}, ] html_publisher = publisher.check(".html", self.document) toc = html_publisher.table_of_contents(linkify=None, obj=self.document) @@ -336,7 +336,11 @@ def test_toc_no_links(self): """Verify the table of contents is generated without heading levels""" expected = [ {"depth": 0, "text": "Table of Contents", "uid": "toc"}, - {"depth": 3, "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod (REQ001)", "uid": ""}, + { + "depth": 3, + "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod (REQ001)", + "uid": "", + }, {"depth": 2, "text": "Unicode: -40° ±1% (REQ003)", "uid": ""}, {"depth": 2, "text": "Hello, world! (REQ006)", "uid": ""}, {"depth": 2, "text": "Hello, world! (REQ004)", "uid": ""}, diff --git a/doorstop/core/tests/test_all.py b/doorstop/core/tests/test_all.py index 23638e66..6cd1a8e1 100644 --- a/doorstop/core/tests/test_all.py +++ b/doorstop/core/tests/test_all.py @@ -27,8 +27,8 @@ EMPTY, ENV, FILES, - GOLDEN_MASTER_FILES, FILES_MD, + GOLDEN_MASTER_FILES, REASON, ROOT, SYS, @@ -80,8 +80,10 @@ def cleanup_test_yml_files(): # Exclude golden master files from reset yaml_files = [ - f for f in yaml_files - if os.path.abspath(os.path.join(repo_root, f)) not in GOLDEN_MASTER_FILES + f + for f in yaml_files + if os.path.abspath(os.path.join(repo_root, f)) + not in GOLDEN_MASTER_FILES ] if not yaml_files: @@ -108,6 +110,7 @@ def cleanup_test_yml_files(): # Ignore errors in cleanup to prevent test failures pass + class TestItem(unittest.TestCase): """Integration tests for the Item class.""" @@ -612,7 +615,9 @@ def test_export_yml(self): # Assert if actual != expected: common.log.error(f"Published content changed: {path}") - move_file(temp, path) + move_file(temp, path) + else: + common.delete(temp) # clean up in case the file didn't change self.assertEqual(expected, actual) def test_export_csv(self): @@ -628,7 +633,9 @@ def test_export_csv(self): # Assert if actual != expected: common.log.error(f"Published content changed: {path}") - move_file(temp, path) + move_file(temp, path) + else: + common.delete(temp) # clean up in case the file didn't change self.assertEqual(expected, actual) @patch("doorstop.settings.REVIEW_NEW_ITEMS", False) @@ -645,9 +652,12 @@ def test_export_tsv(self): # Assert if actual != expected: common.log.error(f"Published content changed: {path}") - move_file(temp, path) + move_file(temp, path) + else: + common.delete(temp) # clean up in case the file didn't change self.assertEqual(expected, actual) + class TestPublisher(unittest.TestCase): """Integration tests for the doorstop.core.publisher module.""" @@ -720,7 +730,6 @@ def test_lines_text_document_without_child_links(self): common.write_text(text, path) self.assertEqual(expected, text) - def test_lines_markdown_document(self): """Verify Markdown can be published from a document.""" path = os.path.join(FILES, "published.md") diff --git a/doorstop/core/tests/test_document.py b/doorstop/core/tests/test_document.py index 27b4436b..253f2f77 100644 --- a/doorstop/core/tests/test_document.py +++ b/doorstop/core/tests/test_document.py @@ -396,29 +396,6 @@ def test_index_get(self): path = os.path.join(self.document.path, self.document.INDEX) self.assertEqual(path, self.document.index) - @patch("doorstop.common.write_lines") - @patch("doorstop.settings.MAX_LINE_LENGTH", 40) - def test_index_set(self, mock_write_lines): - """Verify an document's index can be created.""" - lines = [ - "initial: 1.2.3", - "outline:", - " - REQ001: # Lorem ipsum d...", - " - REQ003: # Unicode: -40° ±1%", - " - REQ006: # Hello, world!", - " - REQ004: # Hello, world!", - " - REQ002: # Plantuml", - " - REQ2-001: # Hello, world!", - " - REQ007: # My Heading", - ] - # Act - self.document.index = True # create index - # Assert - gen, path = mock_write_lines.call_args[0] - lines2 = list(gen)[8:] # skip lines of info comments - self.assertListEqual(lines, lines2) - self.assertEqual(os.path.join(FILES, "index.yml"), path) - @patch("doorstop.common.write_lines") @patch("doorstop.settings.MAX_LINE_LENGTH", 40) def test_read_index(self, mock_write_lines): @@ -451,39 +428,6 @@ def test_read_index(self, mock_write_lines): # Assert self.assertEqual(expected, actual) - @patch("doorstop.common.write_lines") - @patch("doorstop.settings.MAX_LINE_LENGTH", 40) - def test_read_index(self, mock_write_lines): - """Verify a document index can be read.""" - lines = """initial: 1.2.3 -outline: - - REQ001: # Lorem ipsum d... - - REQ003: # Unicode: -40° ±1% - - REQ004: # Hello, world! !['.. - - REQ002: # Hello, world! !["... - - REQ2-001: # Hello, world! - - REQ005: # My Heading""" - - expected = { - "initial": "1.2.3", - "outline": [ - {"REQ001": [{"text": "Lorem ipsum d..."}]}, - {"REQ003": [{"text": "Unicode: -40° ±1%"}]}, - {"REQ004": [{"text": "Hello, world! !['.."}]}, - {"REQ002": [{"text": 'Hello, world! !["...'}]}, - {"REQ2-001": [{"text": "Hello, world!"}]}, - {"REQ005": [{"text": "My Heading"}]}, - ], - } - # Act - with patch("builtins.open") as mock_open: - mock_open.side_effect = lambda *args, **kw: mock.mock_open( - read_data=lines - ).return_value - actual = self.document._read_index("mock_path") - # Assert - self.assertEqual(expected, actual) - @patch("doorstop.common.write_lines") @patch("doorstop.settings.MAX_LINE_LENGTH", 40) def test_index_set(self, mock_write_lines): diff --git a/doorstop/core/tests/test_tree.py b/doorstop/core/tests/test_tree.py index 34cec2a0..27745ab1 100644 --- a/doorstop/core/tests/test_tree.py +++ b/doorstop/core/tests/test_tree.py @@ -46,7 +46,8 @@ def reset_fixture_files(): # Exclude golden master files from reset as they are # intentionally updated by tests using the golden master pattern yaml_files = [ - f for f in yaml_files + f + for f in yaml_files if os.path.abspath(os.path.join(test_dir, f)) not in GOLDEN_MASTER_FILES ] @@ -64,6 +65,7 @@ def reset_fixture_files(): except Exception: pass + @patch("doorstop.core.document.Document", MockDocumentSkip) class TestTreeStrings(unittest.TestCase): """Unit tests for the Tree class using strings.""" From eda7c321f61db8b548a5f27b40ca14d58a9ab53e Mon Sep 17 00:00:00 2001 From: "Felix Eckstein (Ext.)" Date: Fri, 26 Jun 2026 08:10:33 +0200 Subject: [PATCH 5/5] - undo makefile changes as it's out of scope of issue --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 10e3d782..fe4e0056 100644 --- a/Makefile +++ b/Makefile @@ -113,7 +113,8 @@ test-int: test-all test-all: install TEST_INTEGRATION=true poetry run pytest $(PACKAGES) $(PYTEST_OPTIONS) ifndef DISABLE_COVERAGE - @ poetry run coveragespace update overall || echo "Coverage upload skipped: no remote origin configured" + @ echo + poetry run coveragespace update overall endif .PHONY: test-cover