We tried mocking fetch, mocking the database, and contract-testing in isolation. None of them caught the bugs we cared about.
The setup that finally worked
Real Postgres in CI. Real Server Action invocations through a thin contract layer. Tests that read like the user story. Slow? A little. Catches real bugs? Every release.
If you are tempted to mock the DB to keep tests fast — measure first. We were faster the moment we stopped fighting the integration boundary.