raw · 6292 bytes
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 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() |