aboutsummaryrefslogtreecommitdiffziptar.gz
path: root/tests/test_migrate.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_migrate.py')
-rw-r--r--tests/test_migrate.py160
1 files changed, 160 insertions, 0 deletions
diff --git a/tests/test_migrate.py b/tests/test_migrate.py
new file mode 100644
index 0000000..bd02f92
--- /dev/null
+++ b/tests/test_migrate.py
@@ -0,0 +1,160 @@
+import sys
+import unittest
+from pathlib import Path
+
+sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
+from wflib import migrate as M
+from wflib import tasks as T
+
+OLD = """\
+# Tasks — demo
+
+Conventions: CLAUDE.md.
+
+## Awaiting your decision
+
+- **Smoke screenshots (need you present)**: windows open hidden,
+ so screenshots are black.
+- Online mod updates (spec step 4) not built.
+
+## Pending
+
+1. **[P1] Terrain: cave seams** (Effort: <1h) — close the slit beside the lintel.
+ - Tests (red first): no edge left open.
+ - After: Terrain 6/6: cave rendering + sound
+ Reference: [DESIGN.md#terrain](DESIGN.md#terrain)
+
+2. N. **[P2] Reputation factions** (Effort: 5h, interactive) (in progress: master; slices 1–2/3 done (see plan)) — make factions friendly.
+ - Steps: use the table
+ - nested
+ - After: Terrain: cave seams
+ Reference: docs/notes/exe-map.md (ai.c rows), [spec](docs/spec.md#rep).
+
+3. **[P2] Spirit Powers 3/5: technique keys** (Effort: 1h) — keys 1/2/3.
+
+4. **[P3] Spirit Powers 4/5: passives. And more** (Effort: 1h)
+ - After: Spirit Powers 3/5: technique keys
+
+## Needs human
+
+Only what a script can't judge.
+
+1. **[P1] Play-test feel** (Effort: <1h) — last pass 2026-09-13.
+ - [ ] keyboard works
+ - [ ] motion smooth
+
+## Deferred (explicitly out of scope, tracked for later)
+
+**Final art**: later.
+"""
+
+NEW = """\
+# Tasks — demo
+
+Conventions: CLAUDE.md.
+
+## Awaiting your decision
+
+- **a-smoke-screenshots**: Smoke screenshots (need you present). windows open hidden,
+ so screenshots are black.
+
+- **a-online-mod-updates-not-built**: Online mod updates (spec step 4) not built.
+
+## Pending
+
+- **t-terrain-cave-seams** [P1] (<1h): Terrain: cave seams. close the slit beside the lintel.
+ - Tests (red first): no edge left open.
+ Ref: DESIGN.md#terrain
+
+- **t-reputation-factions** [P2] (5h, interactive) (in progress: master; slices 1–2/3 done [see plan]): Reputation factions. make factions friendly.
+ - Steps: use the table
+ - nested
+ - After: [[t-terrain-cave-seams]]
+ Ref: docs/notes/exe-map.md (ai.c rows), docs/spec.md#rep
+
+- **t-spirit-powers-3** [P2] (1h): Spirit Powers 3/5: technique keys. keys 1/2/3.
+
+- **t-spirit-powers-4** [P3] (1h): Spirit Powers 4/5: passives, And more.
+ - After: [[t-spirit-powers-3]]
+
+## Needs human
+
+Only what a script can't judge.
+
+- **t-play-test-feel** [P1] (<1h): Play-test feel. last pass 2026-09-13.
+ - [ ] keyboard works
+ - [ ] motion smooth
+
+## Deferred
+
+(explicitly out of scope, tracked for later)
+
+**Final art**: later.
+"""
+
+
+class MigrateTest(unittest.TestCase):
+ def test_converts_all_oddities(self):
+ new, report = M.migrate(OLD)
+ self.assertEqual(new, NEW)
+ self.assertEqual(report.ids, {
+ "Smoke screenshots (need you present)": "a-smoke-screenshots",
+ "Online mod updates (spec step 4) not built": "a-online-mod-updates-not-built",
+ "Terrain: cave seams": "t-terrain-cave-seams",
+ "Reputation factions": "t-reputation-factions",
+ "Spirit Powers 3/5: technique keys": "t-spirit-powers-3",
+ "Spirit Powers 4/5: passives. And more": "t-spirit-powers-4",
+ "Play-test feel": "t-play-test-feel",
+ })
+ self.assertEqual(report.notes, [
+ "t-terrain-cave-seams: dropped 'After: Terrain 6/6: cave rendering + sound' (no open task with that title: done)"])
+
+ def test_result_parses_clean(self):
+ doc = T.parse(M.migrate(OLD)[0])
+ self.assertEqual([i.error for i in doc.all_items()], [None] * 7)
+ self.assertEqual(T.render(doc), NEW)
+
+ def test_idempotent(self):
+ new, report = M.migrate(NEW)
+ self.assertEqual(new, NEW)
+ self.assertEqual((report.ids, report.notes), ({}, []))
+
+ def test_missing_sections_are_added(self):
+ new, _ = M.migrate("# T\n\n## Pending\n\n1. **[P1] A** (Effort: 1h) — a.\n\n## Notes\n\nprose\n")
+ self.assertEqual(new, "# T\n\n## Pending\n\n- **t-a** [P1] (1h): A. a.\n\n## Notes\n\nprose\n\n"
+ "## Awaiting your decision\n\n## Needs human\n\n## Deferred\n")
+
+ def test_same_title_twice_gets_distinct_ids(self):
+ new, _ = M.migrate("## Pending\n\n1. **[P1] A** (Effort: 1h) — x.\n\n2. **[P1] A** (Effort: 1h) — y.\n")
+ self.assertIn("- **t-a** [P1] (1h): A. x.", new)
+ self.assertIn("- **t-a-2** [P1] (1h): A. y.", new)
+
+ def test_ids_avoid_the_archive(self):
+ new, _ = M.migrate("## Pending\n\n1. **[P1] A** (Effort: 1h) — x.\n", taken={"t-a"})
+ self.assertIn("- **t-a-2** [P1] (1h): A. x.", new)
+
+ def test_one_column_deeper_under_a_colon_line_is_a_child(self):
+ new, _ = M.migrate("## Pending\n\n1. **[P1] A** (Effort: 1h) — x.\n - Seed Qs:\n - first?\n - second?\n"
+ " - [ ] box\n - [ ] sibling typo\n")
+ self.assertIn("- **t-a** [P1] (1h): A. x.\n - Seed Qs:\n - first?\n - second?\n"
+ " - [ ] box\n - [ ] sibling typo\n", new)
+
+ def test_reference_words_that_are_no_path_become_a_note(self):
+ new, _ = M.migrate("## Pending\n\n1. **[P1] A** (Effort: 1h) — x.\n"
+ " Reference: docs/a.md (fate rows), skill row notes, src/X.cs, DESIGN.md#terrain\n\n"
+ "2. **[P1] B** (Effort: 1h) — y.\n Reference: main spec §5\n")
+ self.assertIn(" Ref: docs/a.md (fate rows; skill row notes), src/X.cs, DESIGN.md#terrain\n", new)
+ self.assertIn("- **t-b** [P1] (1h): B. y.\n - Reference: main spec §5\n", new)
+
+ def test_reference_note_keeps_the_body_indent(self):
+ new, _ = M.migrate("## Pending\n\n1. **[P1] A** (Effort: 1h) — x.\n - Steps: s\n Reference: main spec §5\n")
+ self.assertIn("- **t-a** [P1] (1h): A. x.\n - Steps: s\n - Reference: main spec §5\n", new)
+
+ def test_unreadable_numbered_item_is_reported_and_kept(self):
+ new, report = M.migrate("## Pending\n\n1. something without the pattern\n body\n")
+ self.assertIn("1. something without the pattern\n body\n", new)
+ self.assertEqual(report.notes, ["line 3: numbered item not understood, left as it is: 1. something without the pattern"])
+
+
+if __name__ == "__main__":
+ unittest.main()