@gallopsystems/agent-skills 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +137 -0
  2. package/package.json +26 -0
  3. package/plugins/doctl/.claude-plugin/plugin.json +8 -0
  4. package/plugins/doctl/skills/doctl/SKILL.md +93 -0
  5. package/plugins/kysely-postgres/.claude-plugin/plugin.json +8 -0
  6. package/plugins/kysely-postgres/skills/kysely-postgres/SKILL.md +1101 -0
  7. package/plugins/kysely-postgres/skills/kysely-postgres/references/aggregations.ts +167 -0
  8. package/plugins/kysely-postgres/skills/kysely-postgres/references/ctes.ts +165 -0
  9. package/plugins/kysely-postgres/skills/kysely-postgres/references/expressions.ts +272 -0
  10. package/plugins/kysely-postgres/skills/kysely-postgres/references/joins.ts +206 -0
  11. package/plugins/kysely-postgres/skills/kysely-postgres/references/json-arrays.ts +398 -0
  12. package/plugins/kysely-postgres/skills/kysely-postgres/references/mutations.ts +199 -0
  13. package/plugins/kysely-postgres/skills/kysely-postgres/references/orderby-pagination.ts +117 -0
  14. package/plugins/kysely-postgres/skills/kysely-postgres/references/relations.ts +176 -0
  15. package/plugins/kysely-postgres/skills/kysely-postgres/references/select-where.ts +146 -0
  16. package/plugins/linear/.claude-plugin/plugin.json +8 -0
  17. package/plugins/linear/skills/linear/SKILL.md +1040 -0
  18. package/plugins/linear/skills/linear/bin/linear.mjs +1228 -0
  19. package/plugins/linear/skills/linear/tech-stack.md +273 -0
  20. package/plugins/nitro-testing/.claude-plugin/plugin.json +8 -0
  21. package/plugins/nitro-testing/skills/nitro-testing/SKILL.md +497 -0
  22. package/plugins/nitro-testing/skills/nitro-testing/async-testing.md +270 -0
  23. package/plugins/nitro-testing/skills/nitro-testing/ci-setup.md +226 -0
  24. package/plugins/nitro-testing/skills/nitro-testing/examples/global-setup.ts +90 -0
  25. package/plugins/nitro-testing/skills/nitro-testing/examples/handler.test.ts +167 -0
  26. package/plugins/nitro-testing/skills/nitro-testing/examples/setup.ts +29 -0
  27. package/plugins/nitro-testing/skills/nitro-testing/examples/test-utils-index.ts +297 -0
  28. package/plugins/nitro-testing/skills/nitro-testing/examples/vitest.config.ts +42 -0
  29. package/plugins/nitro-testing/skills/nitro-testing/factories.md +278 -0
  30. package/plugins/nitro-testing/skills/nitro-testing/frontend-testing.md +512 -0
  31. package/plugins/nitro-testing/skills/nitro-testing/test-utils.md +262 -0
  32. package/plugins/nitro-testing/skills/nitro-testing/transaction-rollback.md +183 -0
  33. package/plugins/nitro-testing/skills/nitro-testing/vitest-config.md +236 -0
  34. package/plugins/nuxt-nitro-api/.claude-plugin/plugin.json +8 -0
  35. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/SKILL.md +260 -0
  36. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/auth-patterns.md +228 -0
  37. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/composables-utils.md +174 -0
  38. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/deep-linking.md +190 -0
  39. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/examples/auth-middleware.ts +32 -0
  40. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/examples/auth-utils.ts +51 -0
  41. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/examples/deep-link-page.vue +61 -0
  42. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/examples/service-util.ts +63 -0
  43. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/examples/sse-endpoint.ts +59 -0
  44. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/examples/validation-endpoint.ts +38 -0
  45. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/fetch-patterns.md +178 -0
  46. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/nitro-tasks.md +243 -0
  47. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/page-structure.md +162 -0
  48. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/server-services.md +238 -0
  49. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/sse.md +221 -0
  50. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/ssr-client.md +166 -0
  51. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/validation.md +131 -0
  52. package/scripts/link-skills.mjs +252 -0
@@ -0,0 +1,270 @@
1
+ # Async & Automation Testing
2
+
3
+ > **Example:** [test-utils-index.ts](./examples/test-utils-index.ts)
4
+
5
+ Test Nitro tasks, background jobs, and automation systems that trigger asynchronously.
6
+
7
+ ## The Challenge
8
+
9
+ When handlers trigger background tasks, tests need to:
10
+ 1. Capture task handlers defined with `defineTask`
11
+ 2. Execute tasks when `runTask` is called
12
+ 3. Wait for all async operations to complete
13
+
14
+ ## Task Handler Registry
15
+
16
+ Capture task handlers at registration time:
17
+
18
+ ```typescript
19
+ // Track registered task handlers
20
+ const taskHandlers: Map<string, (opts: { payload: any }) => Promise<any>> = new Map();
21
+
22
+ // Track pending task executions
23
+ let pendingTasks: Promise<any>[] = [];
24
+
25
+ // Stub defineTask to capture handlers
26
+ vi.stubGlobal("defineTask", (config: {
27
+ meta: { name: string };
28
+ run: (opts: { payload: any }) => Promise<any>;
29
+ }) => {
30
+ taskHandlers.set(config.meta.name, config.run);
31
+ return config;
32
+ });
33
+
34
+ // Stub runTask to execute and track
35
+ vi.stubGlobal("runTask", (taskName: string, options: { payload: any }) => {
36
+ const handler = taskHandlers.get(taskName);
37
+ if (!handler) {
38
+ return Promise.reject(new Error(`Task handler not found: ${taskName}`));
39
+ }
40
+ const promise = handler(options);
41
+ pendingTasks.push(promise);
42
+ return promise;
43
+ });
44
+ ```
45
+
46
+ ## Waiting for Async Operations
47
+
48
+ ```typescript
49
+ /**
50
+ * Wait for all pending task executions to complete.
51
+ * Loops because tasks can spawn other tasks.
52
+ */
53
+ export async function waitForAutomations(): Promise<void> {
54
+ while (pendingTasks.length > 0) {
55
+ const tasksToWait = [...pendingTasks];
56
+ pendingTasks = [];
57
+ await Promise.allSettled(tasksToWait);
58
+ }
59
+ }
60
+ ```
61
+
62
+ ## Enabling Real Implementations
63
+
64
+ By default, stub async triggers as no-ops. Tests opt-in to real behavior:
65
+
66
+ ```typescript
67
+ // Default: no-op stub
68
+ vi.stubGlobal("triggerAutomation", () => {});
69
+
70
+ /**
71
+ * Enable automation triggers for tests that need them.
72
+ */
73
+ export async function enableAutomationTriggers() {
74
+ // Import the task handler to register it
75
+ await import("../tasks/execute-automation");
76
+
77
+ // Import the real trigger function
78
+ const { triggerAutomation: realTrigger } = await import("../utils/automation");
79
+
80
+ // Wrap to track promises
81
+ vi.stubGlobal("triggerAutomation", (...args: Parameters<typeof realTrigger>) => {
82
+ const promise = realTrigger(...args);
83
+ pendingTasks.push(promise);
84
+ return promise;
85
+ });
86
+ }
87
+ ```
88
+
89
+ ## Usage in Tests
90
+
91
+ ### Basic Automation Test
92
+
93
+ ```typescript
94
+ import {
95
+ describe,
96
+ test,
97
+ expect,
98
+ mockPost,
99
+ enableAutomationTriggers,
100
+ waitForAutomations,
101
+ beforeAll,
102
+ } from "~/server/test-utils";
103
+ import handler from "./index.post";
104
+
105
+ // Enable real automations for this file
106
+ beforeAll(async () => {
107
+ await enableAutomationTriggers();
108
+ });
109
+
110
+ describe("Job Creation Automations", () => {
111
+ test("automation creates task when job is created", async ({ factories, db }) => {
112
+ // Set up automation rule
113
+ const template = await factories.jobTemplate();
114
+ await factories.automation({
115
+ jobTemplateId: template.id,
116
+ triggerConfig: { subject: "job", event: "created" },
117
+ actionPayload: { task_type: "Review" },
118
+ });
119
+
120
+ // Create job (triggers automation)
121
+ const project = await factories.project();
122
+ const event = mockPost({}, {
123
+ projectId: project.id,
124
+ jobTemplateId: template.id,
125
+ jobType: "Test",
126
+ });
127
+ const result = await handler(event);
128
+
129
+ // Wait for automation to complete
130
+ await waitForAutomations();
131
+
132
+ // Verify automation created the task
133
+ const tasks = await db
134
+ .selectFrom("task")
135
+ .where("job_id", "=", result.job.id)
136
+ .selectAll()
137
+ .execute();
138
+
139
+ expect(tasks).toHaveLength(1);
140
+ expect(tasks[0].task_type).toBe("Review");
141
+ });
142
+ });
143
+ ```
144
+
145
+ ### Chained Automations
146
+
147
+ ```typescript
148
+ test("completing task triggers follow-up automation", async ({ factories, db }) => {
149
+ const template = await factories.jobTemplate();
150
+
151
+ // Automation: when any task completes, create follow-up
152
+ await factories.automation({
153
+ jobTemplateId: template.id,
154
+ triggerConfig: { subject: "task", event: "completed" },
155
+ actionPayload: { task_type: "Follow-up" },
156
+ });
157
+
158
+ // Create job with initial task
159
+ const project = await factories.project();
160
+ const job = await factories.job({ projectId: project.id, jobTemplateId: template.id });
161
+ const task = await factories.task({ jobId: job.id, status: "Active" });
162
+
163
+ // Complete the task
164
+ const event = mockPatch({}, {
165
+ tasks: [{ id: task.id, status: "Completed" }],
166
+ });
167
+ await taskPatchHandler(event);
168
+ await waitForAutomations();
169
+
170
+ // Should have 2 tasks: original + follow-up
171
+ const tasks = await db
172
+ .selectFrom("task")
173
+ .where("job_id", "=", job.id)
174
+ .selectAll()
175
+ .execute();
176
+
177
+ expect(tasks).toHaveLength(2);
178
+ expect(tasks.some(t => t.task_type === "Follow-up")).toBe(true);
179
+ });
180
+ ```
181
+
182
+ ### Testing Multiple Async Triggers
183
+
184
+ ```typescript
185
+ test("bulk update triggers multiple automations", async ({ factories, db }) => {
186
+ const template = await factories.jobTemplate();
187
+ await factories.automation({
188
+ jobTemplateId: template.id,
189
+ triggerConfig: { subject: "task", event: "completed" },
190
+ actionPayload: { task_type: "Follow-up" },
191
+ });
192
+
193
+ const job = await factories.job({ jobTemplateId: template.id });
194
+ const tasks = await Promise.all([
195
+ factories.task({ jobId: job.id, status: "Active" }),
196
+ factories.task({ jobId: job.id, status: "Active" }),
197
+ factories.task({ jobId: job.id, status: "Active" }),
198
+ ]);
199
+
200
+ // Complete all 3 tasks in one patch
201
+ const event = mockPatch({}, {
202
+ tasks: tasks.map(t => ({ id: t.id, status: "Completed" })),
203
+ });
204
+ await handler(event);
205
+ await waitForAutomations();
206
+
207
+ // Should have 6 tasks: 3 original + 3 follow-ups
208
+ const allTasks = await db
209
+ .selectFrom("task")
210
+ .where("job_id", "=", job.id)
211
+ .selectAll()
212
+ .execute();
213
+
214
+ expect(allTasks).toHaveLength(6);
215
+ expect(allTasks.filter(t => t.task_type === "Follow-up")).toHaveLength(3);
216
+ });
217
+ ```
218
+
219
+ ## Testing Without Automations
220
+
221
+ Most tests don't need real automations - the stub is a no-op:
222
+
223
+ ```typescript
224
+ // No beforeAll(enableAutomationTriggers) - uses stub
225
+ describe("Basic CRUD", () => {
226
+ test("creates job without triggering automations", async ({ factories }) => {
227
+ const project = await factories.project();
228
+ const event = mockPost({}, { projectId: project.id, jobType: "Test" });
229
+
230
+ const result = await handler(event);
231
+
232
+ expect(result.job.id).toBeDefined();
233
+ // No automations ran - triggerAutomation is a no-op
234
+ });
235
+ });
236
+ ```
237
+
238
+ ## Verifying Execution Records
239
+
240
+ If your system logs automation executions:
241
+
242
+ ```typescript
243
+ test("records automation execution", async ({ factories, db }) => {
244
+ const template = await factories.jobTemplate();
245
+ await factories.automation({
246
+ jobTemplateId: template.id,
247
+ triggerConfig: { subject: "job", event: "created" },
248
+ });
249
+
250
+ const job = await factories.job({ jobTemplateId: template.id });
251
+ await waitForAutomations();
252
+
253
+ const execution = await db
254
+ .selectFrom("automation_execution")
255
+ .where("job_id", "=", job.id)
256
+ .selectAll()
257
+ .executeTakeFirst();
258
+
259
+ expect(execution?.status).toBe("completed");
260
+ expect(execution?.affected_entities).toBeDefined();
261
+ });
262
+ ```
263
+
264
+ ## Key Patterns
265
+
266
+ 1. **Opt-in real behavior** - Default to stubs, `enableAutomationTriggers()` for tests that need it
267
+ 2. **Track all promises** - Both the trigger and the tasks it spawns
268
+ 3. **Wait in a loop** - Tasks can spawn more tasks
269
+ 4. **Use `Promise.allSettled`** - Don't fail fast, let all settle
270
+ 5. **Verify final state** - Check database after `waitForAutomations()`
@@ -0,0 +1,226 @@
1
+ # CI/CD Setup
2
+
3
+ Configure GitHub Actions to run tests with a real PostgreSQL database.
4
+
5
+ ## GitHub Actions Workflow
6
+
7
+ ```yaml
8
+ # .github/workflows/ci.yml
9
+ name: CI
10
+
11
+ on:
12
+ push:
13
+ branches: [main]
14
+ pull_request:
15
+ branches: [main]
16
+
17
+ jobs:
18
+ test:
19
+ runs-on: ubuntu-latest
20
+
21
+ services:
22
+ postgres:
23
+ image: postgres:15
24
+ env:
25
+ POSTGRES_USER: postgres
26
+ POSTGRES_PASSWORD: postgres
27
+ POSTGRES_DB: myapp-test
28
+ ports:
29
+ - 5432:5432
30
+ options: >-
31
+ --health-cmd pg_isready
32
+ --health-interval 10s
33
+ --health-timeout 5s
34
+ --health-retries 5
35
+
36
+ steps:
37
+ - uses: actions/checkout@v4
38
+
39
+ - name: Setup Node.js
40
+ uses: actions/setup-node@v4
41
+ with:
42
+ node-version: "22"
43
+ cache: "yarn"
44
+
45
+ - name: Install dependencies
46
+ run: yarn install --frozen-lockfile
47
+
48
+ - name: Run tests
49
+ run: yarn test --coverage
50
+ env:
51
+ TEST_POSTGRESQL_CONNECTION_STRING: postgresql://postgres:postgres@localhost:5432/myapp-test
52
+
53
+ - name: Upload coverage
54
+ uses: actions/upload-artifact@v4
55
+ with:
56
+ name: coverage-report
57
+ path: coverage/
58
+ retention-days: 7
59
+
60
+ typecheck:
61
+ runs-on: ubuntu-latest
62
+ steps:
63
+ - uses: actions/checkout@v4
64
+ - uses: actions/setup-node@v4
65
+ with:
66
+ node-version: "22"
67
+ cache: "yarn"
68
+ - run: yarn install --frozen-lockfile
69
+ - run: yarn typecheck
70
+ ```
71
+
72
+ ## Coverage Report on PRs
73
+
74
+ Add coverage reporting to pull requests:
75
+
76
+ ```yaml
77
+ # .github/workflows/ci.yml (add to test job steps)
78
+ - name: Coverage Report
79
+ if: github.event_name == 'pull_request'
80
+ uses: davelosert/vitest-coverage-report-action@v2
81
+ with:
82
+ vite-config-path: vitest.config.ts
83
+ json-summary-path: coverage/coverage-summary.json
84
+ json-final-path: coverage/coverage-final.json
85
+ ```
86
+
87
+ ## Key Configuration Points
88
+
89
+ ### PostgreSQL Service
90
+
91
+ ```yaml
92
+ services:
93
+ postgres:
94
+ image: postgres:15 # Match your production version
95
+ env:
96
+ POSTGRES_USER: postgres
97
+ POSTGRES_PASSWORD: postgres
98
+ POSTGRES_DB: myapp-test # Test database name
99
+ ports:
100
+ - 5432:5432
101
+ options: >-
102
+ --health-cmd pg_isready
103
+ --health-interval 10s
104
+ --health-timeout 5s
105
+ --health-retries 5
106
+ ```
107
+
108
+ The health check ensures PostgreSQL is ready before tests run.
109
+
110
+ ### Connection String
111
+
112
+ ```yaml
113
+ env:
114
+ TEST_POSTGRESQL_CONNECTION_STRING: postgresql://postgres:postgres@localhost:5432/myapp-test
115
+ ```
116
+
117
+ This environment variable is read by `global-setup.ts` and `setup.ts`.
118
+
119
+ ### Node.js Version
120
+
121
+ ```yaml
122
+ - uses: actions/setup-node@v4
123
+ with:
124
+ node-version: "22" # Match your project
125
+ cache: "yarn" # or "npm"
126
+ ```
127
+
128
+ ## Parallel Test Jobs
129
+
130
+ For faster CI, run tests in parallel (if you have many):
131
+
132
+ ```yaml
133
+ jobs:
134
+ test:
135
+ runs-on: ubuntu-latest
136
+ strategy:
137
+ matrix:
138
+ shard: [1, 2, 3, 4]
139
+
140
+ services:
141
+ postgres:
142
+ # ... same config
143
+
144
+ steps:
145
+ # ... setup steps
146
+
147
+ - name: Run tests (shard ${{ matrix.shard }})
148
+ run: yarn test --shard=${{ matrix.shard }}/${{ strategy.job-total }}
149
+ env:
150
+ TEST_POSTGRESQL_CONNECTION_STRING: postgresql://postgres:postgres@localhost:5432/myapp-test
151
+ ```
152
+
153
+ ## Caching Dependencies
154
+
155
+ The `cache: "yarn"` option caches `node_modules` based on `yarn.lock`:
156
+
157
+ ```yaml
158
+ - uses: actions/setup-node@v4
159
+ with:
160
+ node-version: "22"
161
+ cache: "yarn" # Automatically caches based on yarn.lock
162
+ ```
163
+
164
+ For npm:
165
+ ```yaml
166
+ cache: "npm" # Caches based on package-lock.json
167
+ ```
168
+
169
+ ## Branch Protection
170
+
171
+ Configure branch protection rules in GitHub:
172
+
173
+ 1. Go to **Settings → Branches → Add rule**
174
+ 2. Branch name pattern: `main`
175
+ 3. Enable:
176
+ - ✅ Require status checks to pass before merging
177
+ - ✅ Require branches to be up to date before merging
178
+ 4. Add required status checks:
179
+ - `test`
180
+ - `typecheck`
181
+
182
+ ## Local CI Simulation
183
+
184
+ Test the CI workflow locally using [act](https://github.com/nektos/act):
185
+
186
+ ```bash
187
+ # Install act
188
+ brew install act
189
+
190
+ # Run workflow
191
+ act push
192
+ ```
193
+
194
+ Or use Docker directly:
195
+
196
+ ```bash
197
+ # Start test database
198
+ docker run -d --name test-db \
199
+ -e POSTGRES_USER=postgres \
200
+ -e POSTGRES_PASSWORD=postgres \
201
+ -e POSTGRES_DB=myapp-test \
202
+ -p 5432:5432 \
203
+ postgres:15
204
+
205
+ # Run tests
206
+ TEST_POSTGRESQL_CONNECTION_STRING=postgresql://postgres:postgres@localhost:5432/myapp-test \
207
+ yarn test
208
+
209
+ # Cleanup
210
+ docker stop test-db && docker rm test-db
211
+ ```
212
+
213
+ ## Secrets and Environment Variables
214
+
215
+ For production-like test databases or external services:
216
+
217
+ ```yaml
218
+ steps:
219
+ - name: Run tests
220
+ run: yarn test
221
+ env:
222
+ TEST_POSTGRESQL_CONNECTION_STRING: ${{ secrets.TEST_DATABASE_URL }}
223
+ STRIPE_TEST_KEY: ${{ secrets.STRIPE_TEST_KEY }}
224
+ ```
225
+
226
+ Store secrets in **Settings → Secrets and variables → Actions**.
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Vitest Global Setup
3
+ *
4
+ * Runs ONCE before all tests to reset and migrate the test database.
5
+ */
6
+
7
+ import { Kysely, PostgresDialect, Migrator, FileMigrationProvider } from "kysely";
8
+ import { Pool } from "pg";
9
+ import path from "path";
10
+ import { promises as fs } from "fs";
11
+
12
+ function getTestConnectionString(): string {
13
+ if (process.env.TEST_POSTGRESQL_CONNECTION_STRING) {
14
+ return process.env.TEST_POSTGRESQL_CONNECTION_STRING;
15
+ }
16
+ return "postgresql://localhost/myapp-test";
17
+ }
18
+
19
+ export async function setup() {
20
+ const connectionString = getTestConnectionString();
21
+ const pool = new Pool({ connectionString });
22
+
23
+ // Check database exists
24
+ try {
25
+ await pool.query("SELECT 1");
26
+ } catch (err: unknown) {
27
+ const pgError = err as { code?: string };
28
+ if (pgError.code === "3D000") {
29
+ console.error(`
30
+ ╭─────────────────────────────────────────────────────╮
31
+ │ Error: Test database does not exist. │
32
+ │ │
33
+ │ Run: createdb myapp-test │
34
+ │ │
35
+ │ Then re-run your tests. │
36
+ ╰─────────────────────────────────────────────────────╯
37
+ `);
38
+ process.exit(1);
39
+ }
40
+ throw err;
41
+ }
42
+
43
+ // Drop all tables and custom types for clean slate
44
+ await pool.query(`
45
+ DO $$ DECLARE
46
+ r RECORD;
47
+ BEGIN
48
+ -- Drop all tables
49
+ FOR r IN (SELECT tablename FROM pg_tables WHERE schemaname = 'public') LOOP
50
+ EXECUTE 'DROP TABLE IF EXISTS ' || quote_ident(r.tablename) || ' CASCADE';
51
+ END LOOP;
52
+ -- Drop all custom enum types
53
+ FOR r IN (SELECT typname FROM pg_type t
54
+ JOIN pg_namespace n ON t.typnamespace = n.oid
55
+ WHERE n.nspname = 'public' AND t.typtype = 'e') LOOP
56
+ EXECUTE 'DROP TYPE IF EXISTS ' || quote_ident(r.typname) || ' CASCADE';
57
+ END LOOP;
58
+ END $$;
59
+ `);
60
+
61
+ await pool.end();
62
+
63
+ // Run migrations from scratch
64
+ const db = new Kysely({
65
+ dialect: new PostgresDialect({
66
+ pool: new Pool({ connectionString }),
67
+ }),
68
+ });
69
+
70
+ const migrator = new Migrator({
71
+ db,
72
+ provider: new FileMigrationProvider({
73
+ fs,
74
+ path,
75
+ migrationFolder: path.resolve(__dirname, "../db/migrations"),
76
+ }),
77
+ });
78
+
79
+ const { error, results } = await migrator.migrateToLatest();
80
+
81
+ if (error) {
82
+ console.error("Migration failed:", error);
83
+ throw error;
84
+ }
85
+
86
+ const applied = results?.filter((r) => r.status === "Success") ?? [];
87
+ console.log(`Test DB ready: ${applied.length} migrations applied`);
88
+
89
+ await db.destroy();
90
+ }