#!/usr/bin/env python3 """ `wiki:` is a domain field, and the parser now agrees with the format. python3 -m unittest discover -s tests -v The bug: `references/format.md` put `wiki:` in the table of domain fields, between `depends` and `origin`, and `issue.py` had never heard of it. The field fell into `extra` and rendered with the foreign keys — sorted in beside `branch`, `gitea`, `synced`, `url`, i.e. AFTER the sync fields, which the same document forbids one line further down. A list written without brackets parsed as a single string, and nothing could set the field but a text editor. These tests pin the resolution: implemented in the domain, rendered among the domain fields, parsed as a list in both forms, and reachable from the command line. The layer rule rides along — a title is a name for a document, so the field carries titles and this layer never resolves one. """ import os import subprocess import sys import tempfile import unittest REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) ISSUE_SCRIPTS = os.path.join(REPO, "skills", "issue", "scripts") sys.path.insert(0, ISSUE_SCRIPTS) import issue # noqa: E402 TITLES = ["Simple Chains/Ideas/Chain core", "Simple Chains/Ideas/Transport"] SYNCED = """\ --- id: wire-sqlc-appclick state: open labels: [type/task] assignees: [] milestone: none depends: [migrate-schema] wiki: [Simple Chains/Ideas/Chain core] origin: gitea branch: feat/wire-sqlc gitea: claude-skills/tea#42 synced: 2026-08-09T18:40:00Z url: https://git.noodles.cam/claude-skills/tea/issues/42 --- # Wire sqlc into the appclick repo layer ## Summary Тело роли не играет. """ class TestTheFieldIsInTheDomain(unittest.TestCase): def test_it_is_a_domain_key_and_a_list_key(self): self.assertIn("wiki", issue.DOMAIN_KEYS) self.assertIn("wiki", issue.LIST_KEYS) def test_it_renders_between_depends_and_origin(self): """`format.md` states the order and says domain fields render first. The old behavior put it after the sync fields.""" order = issue.DOMAIN_KEYS self.assertEqual(order[order.index("depends") + 1], "wiki") self.assertEqual(order[order.index("wiki") + 1], "origin") def test_it_survives_a_round_trip_among_the_domain_fields(self): iss = issue.Issue.from_text(SYNCED, id="wire-sqlc-appclick") self.assertEqual(iss.wiki, ["Simple Chains/Ideas/Chain core"]) self.assertNotIn("wiki", iss.extra) text = iss.to_text() keys = [line.split(":", 1)[0] for line in text.splitlines()[1:] if line != "---" and ":" in line] keys = keys[:keys.index("origin") + 1] self.assertEqual(keys[-3:], ["depends", "wiki", "origin"]) self.assertLess(keys.index("wiki"), keys.index("origin")) again = issue.Issue.from_text(text, id="wire-sqlc-appclick") self.assertEqual(again.wiki, iss.wiki) def test_a_bracketless_list_is_still_a_list(self): """Without membership in LIST_KEYS this parsed as one string — `wiki: A, B` became the single title "A, B".""" text = SYNCED.replace("wiki: [Simple Chains/Ideas/Chain core]", "wiki: %s" % ", ".join(TITLES)) self.assertEqual(issue.Issue.from_text(text).wiki, TITLES) def test_the_bracketed_form_parses_the_same_way(self): text = SYNCED.replace("wiki: [Simple Chains/Ideas/Chain core]", "wiki: [%s]" % ", ".join(TITLES)) self.assertEqual(issue.Issue.from_text(text).wiki, TITLES) def test_an_absent_field_is_an_empty_list_and_renders_as_one(self): text = "\n".join(l for l in SYNCED.splitlines() if not l.startswith("wiki:")) iss = issue.Issue.from_text(text) self.assertEqual(iss.wiki, []) self.assertIn("wiki: []", iss.to_text()) def test_the_titles_are_carried_verbatim(self): """A title with a slash in it is one title — the slash is hierarchy inside the name, not a path this layer walks.""" iss = issue.Issue(id="x", title="X", wiki=TITLES) self.assertIn("wiki: [%s]" % ", ".join(TITLES), iss.to_text()) def test_the_domain_still_knows_nothing_about_a_wiki_it_could_reach(self): """The layer rule: titles only. No page path, no sub_url, no HTTP.""" with open(os.path.join(ISSUE_SCRIPTS, "issue.py")) as f: body = f.read() for banned in ("sub_url", "content_base64", "urllib"): self.assertNotIn(banned, body) imports = [l for l in body.splitlines() if l.startswith("import ") or l.startswith("from ")] self.assertNotIn("import subprocess", imports) class TestIssueNewCanSetIt(unittest.TestCase): """The script run for real, in a throwaway store — never the developer's.""" def setUp(self): self._tmp = tempfile.TemporaryDirectory(prefix="tea-wiki-field-") self.out = os.path.join(os.path.realpath(self._tmp.name), "issues") self.addCleanup(self._tmp.cleanup) def new(self, *args): p = subprocess.run( [sys.executable, os.path.join(ISSUE_SCRIPTS, "issue_new.py"), "--type", "task", "--title", "Write the chain core up", "--out", self.out] + list(args), capture_output=True, text=True) self.assertEqual(p.returncode, 0, p.stdout + p.stderr) with open(os.path.join(self.out, "write-the-chain-core-up.md")) as f: return f.read() def test_the_flag_repeats_into_a_list(self): text = self.new("--wiki", TITLES[0], "--wiki", TITLES[1]) self.assertIn("wiki: [%s]" % ", ".join(TITLES), text) self.assertEqual(issue.Issue.from_text(text).wiki, TITLES) def test_without_the_flag_the_field_is_present_and_empty(self): self.assertIn("wiki: []", self.new()) if __name__ == "__main__": unittest.main()