|
| 1 | +import io |
1 | 2 | import unittest |
| 3 | +from contextlib import redirect_stdout |
2 | 4 | from unittest import mock |
3 | 5 |
|
4 | 6 | from cleo.testers.command_tester import CommandTester |
5 | 7 |
|
6 | 8 | from fastapi_startkit.masoniteorm.commands.DBSeedCommand import DBSeedCommand |
7 | 9 |
|
| 10 | +from .fixtures.databases.seeds.recorder import CALLS |
8 | 11 |
|
9 | | -class FakeSeeder: |
10 | | - """Records constructor args and awaited methods, mocking DB side effects.""" |
11 | | - |
12 | | - instances = [] |
13 | | - |
14 | | - def __init__(self, seed_path="databases/seeds", connection=None): |
15 | | - self.seed_path = seed_path |
16 | | - self.connection = connection |
17 | | - self.calls = [] |
18 | | - FakeSeeder.instances.append(self) |
19 | | - |
20 | | - async def run_database_seed(self): |
21 | | - self.calls.append(("run_database_seed", None)) |
22 | | - |
23 | | - async def run_specific_seed(self, seed): |
24 | | - self.calls.append(("run_specific_seed", seed)) |
| 12 | +FIXTURE_SEED_PATH = "tests.masoniteorm.commands.fixtures.databases.seeds" |
25 | 13 |
|
26 | 14 |
|
27 | 15 | class TestDBSeedCommand(unittest.TestCase): |
28 | 16 | def setUp(self): |
29 | | - FakeSeeder.instances = [] |
30 | | - patcher = mock.patch( |
31 | | - "fastapi_startkit.masoniteorm.seeds.Seeder", |
32 | | - FakeSeeder, |
33 | | - ) |
34 | | - patcher.start() |
35 | | - self.addCleanup(patcher.stop) |
| 17 | + from .fixtures.app import create_app |
| 18 | + |
| 19 | + self.app = create_app() |
| 20 | + CALLS.clear() |
| 21 | + |
| 22 | + def tearDown(self): |
| 23 | + CALLS.clear() |
36 | 24 |
|
37 | 25 | def _run(self, args=""): |
38 | 26 | tester = CommandTester(DBSeedCommand()) |
39 | 27 | tester.execute(args) |
40 | 28 | return tester.io.fetch_output() |
41 | 29 |
|
| 30 | + def _run_app(self, args=""): |
| 31 | + buffer = io.StringIO() |
| 32 | + with redirect_stdout(buffer): |
| 33 | + self.app.run("db:seed", args) |
| 34 | + return buffer.getvalue() |
| 35 | + |
| 36 | + # -- option/argument resolution, exercised against a mocked Seeder -- |
| 37 | + |
42 | 38 | def test_runs_database_seeder_by_default(self): |
43 | | - output = self._run("") |
| 39 | + with mock.patch("fastapi_startkit.masoniteorm.seeds.Seeder", autospec=True) as MockSeeder: |
| 40 | + output = self._run("") |
44 | 41 |
|
45 | 42 | self.assertIn("Database Seeder seeded!", output) |
46 | | - seeder = FakeSeeder.instances[-1] |
47 | | - self.assertEqual(seeder.calls, [("run_database_seed", None)]) |
48 | | - self.assertEqual(seeder.seed_path, "databases/seeds") |
49 | | - self.assertEqual(seeder.connection, "default") |
| 43 | + MockSeeder.assert_called_once_with(seed_path="databases/seeds", connection="default") |
| 44 | + MockSeeder.return_value.run_database_seed.assert_awaited_once_with() |
| 45 | + MockSeeder.return_value.run_specific_seed.assert_not_awaited() |
50 | 46 |
|
51 | 47 | def test_seeds_specific_table_from_argument(self): |
52 | | - output = self._run("posts") |
| 48 | + with mock.patch("fastapi_startkit.masoniteorm.seeds.Seeder", autospec=True) as MockSeeder: |
| 49 | + output = self._run("posts") |
53 | 50 |
|
54 | 51 | self.assertIn("PostsTableSeeder seeded!", output) |
55 | | - seeder = FakeSeeder.instances[-1] |
56 | | - self.assertEqual( |
57 | | - seeder.calls, |
58 | | - [("run_specific_seed", "posts_table_seeder.PostsTableSeeder")], |
59 | | - ) |
| 52 | + MockSeeder.return_value.run_specific_seed.assert_awaited_once_with("posts_table_seeder.PostsTableSeeder") |
60 | 53 |
|
61 | 54 | def test_class_option_resolves_plain_class_name(self): |
62 | | - output = self._run("--class PostSeeder") |
| 55 | + with mock.patch("fastapi_startkit.masoniteorm.seeds.Seeder", autospec=True) as MockSeeder: |
| 56 | + output = self._run("--class PostSeeder") |
63 | 57 |
|
64 | 58 | self.assertIn("PostSeeder seeded!", output) |
65 | | - seeder = FakeSeeder.instances[-1] |
66 | | - self.assertEqual( |
67 | | - seeder.calls, |
68 | | - [("run_specific_seed", "post_seeder.PostSeeder")], |
69 | | - ) |
| 59 | + MockSeeder.return_value.run_specific_seed.assert_awaited_once_with("post_seeder.PostSeeder") |
70 | 60 |
|
71 | 61 | def test_class_option_resolves_table_seeder_suffix(self): |
72 | | - output = self._run("--class PostTableSeeder") |
| 62 | + with mock.patch("fastapi_startkit.masoniteorm.seeds.Seeder", autospec=True) as MockSeeder: |
| 63 | + output = self._run("--class PostTableSeeder") |
73 | 64 |
|
74 | 65 | self.assertIn("PostTableSeeder seeded!", output) |
75 | | - seeder = FakeSeeder.instances[-1] |
76 | | - self.assertEqual( |
77 | | - seeder.calls, |
78 | | - [("run_specific_seed", "post_table_seeder.PostTableSeeder")], |
79 | | - ) |
| 66 | + MockSeeder.return_value.run_specific_seed.assert_awaited_once_with("post_table_seeder.PostTableSeeder") |
80 | 67 |
|
81 | 68 | def test_class_option_accepts_dotted_path(self): |
82 | | - output = self._run("--class custom.MySeeder") |
| 69 | + with mock.patch("fastapi_startkit.masoniteorm.seeds.Seeder", autospec=True) as MockSeeder: |
| 70 | + output = self._run("--class custom.MySeeder") |
83 | 71 |
|
84 | 72 | self.assertIn("MySeeder seeded!", output) |
85 | | - seeder = FakeSeeder.instances[-1] |
86 | | - self.assertEqual(seeder.calls, [("run_specific_seed", "custom.MySeeder")]) |
| 73 | + MockSeeder.return_value.run_specific_seed.assert_awaited_once_with("custom.MySeeder") |
87 | 74 |
|
88 | 75 | def test_connection_and_directory_options_are_forwarded(self): |
89 | | - self._run("--connection sqlite --directory db/seeds") |
| 76 | + with mock.patch("fastapi_startkit.masoniteorm.seeds.Seeder", autospec=True) as MockSeeder: |
| 77 | + self._run("--connection sqlite --directory db/seeds") |
| 78 | + |
| 79 | + MockSeeder.assert_called_once_with(seed_path="db/seeds", connection="sqlite") |
| 80 | + |
| 81 | + # -- end-to-end, driven through the registered console app against real fixture seeders -- |
| 82 | + |
| 83 | + def test_runs_database_seeder_by_default_via_app(self): |
| 84 | + output = self._run_app(f"--directory {FIXTURE_SEED_PATH} --connection sqlite") |
| 85 | + |
| 86 | + self.assertIn("Database Seeder seeded!", output) |
| 87 | + self.assertEqual(CALLS, [("database", "sqlite")]) |
| 88 | + |
| 89 | + def test_runs_seeder_for_table_argument_via_app(self): |
| 90 | + output = self._run_app(f"user --directory {FIXTURE_SEED_PATH} --connection sqlite") |
| 91 | + |
| 92 | + self.assertIn("UserTableSeeder seeded!", output) |
| 93 | + self.assertEqual(CALLS, [("user_table", "sqlite")]) |
| 94 | + |
| 95 | + def test_runs_seeder_for_class_option_without_table_suffix_via_app(self): |
| 96 | + output = self._run_app(f"--directory {FIXTURE_SEED_PATH} --class SampleSeeder --connection sqlite") |
| 97 | + |
| 98 | + self.assertIn("SampleSeeder seeded!", output) |
| 99 | + self.assertEqual(CALLS, [("sample", "sqlite")]) |
| 100 | + |
| 101 | + def test_runs_seeder_for_class_option_with_table_suffix_via_app(self): |
| 102 | + output = self._run_app(f"--directory {FIXTURE_SEED_PATH} --class UserTableSeeder --connection sqlite") |
| 103 | + |
| 104 | + self.assertIn("UserTableSeeder seeded!", output) |
| 105 | + self.assertEqual(CALLS, [("user_table", "sqlite")]) |
| 106 | + |
| 107 | + def test_runs_seeder_for_fully_qualified_class_option_via_app(self): |
| 108 | + output = self._run_app( |
| 109 | + f"--directory {FIXTURE_SEED_PATH} --class special_seeder.SpecialSeeder --connection sqlite" |
| 110 | + ) |
| 111 | + |
| 112 | + self.assertIn("SpecialSeeder seeded!", output) |
| 113 | + self.assertEqual(CALLS, [("special", "sqlite")]) |
| 114 | + |
| 115 | + def test_class_option_takes_precedence_over_table_argument_via_app(self): |
| 116 | + output = self._run_app(f"user --directory {FIXTURE_SEED_PATH} --class SampleSeeder --connection sqlite") |
| 117 | + |
| 118 | + self.assertIn("SampleSeeder seeded!", output) |
| 119 | + self.assertEqual(CALLS, [("sample", "sqlite")]) |
| 120 | + |
| 121 | + def test_uses_default_connection_option_via_app(self): |
| 122 | + self._run_app(f"--directory {FIXTURE_SEED_PATH}") |
| 123 | + |
| 124 | + self.assertEqual(CALLS, [("database", "default")]) |
| 125 | + |
| 126 | + # -- error paths: driven directly through CommandTester, since the console |
| 127 | + # application catches command exceptions instead of propagating them -- |
| 128 | + |
| 129 | + def test_raises_when_seeder_class_cannot_be_found(self): |
| 130 | + with self.assertRaises(ValueError): |
| 131 | + self._run(f"--directory {FIXTURE_SEED_PATH} --class does_not_exist.NopeSeeder --connection sqlite") |
90 | 132 |
|
91 | | - seeder = FakeSeeder.instances[-1] |
92 | | - self.assertEqual(seeder.seed_path, "db/seeds") |
93 | | - self.assertEqual(seeder.connection, "sqlite") |
| 133 | + def test_raises_when_database_seeder_missing_from_directory(self): |
| 134 | + with self.assertRaises(ValueError): |
| 135 | + self._run("--directory tests.masoniteorm.commands.fixtures.databases.migrations --connection sqlite") |
94 | 136 |
|
95 | 137 |
|
96 | 138 | if __name__ == "__main__": |
|
0 commit comments