Skip to content

Commit b0276d4

Browse files
ignatzMentalGearsamwillis
authored
Add a wider range of subset queries for TrailBase integation: pagination, ordering, and some where. (#1101)
* Add partial on-demand support and e2e tests to trailbase-db-collection package Based on #1090 by @MentalGear. Co-authored-by: MentalGear <2837147+MentalGear@users.noreply.github.com> * Add broader partial fetch support including "order" and "where" clauses. * fix(trailbase-db-collection): inline UUID helpers for e2e Remove the TrailBase e2e test file's runtime dependency on uuid so the package test run can typecheck cleanly while keeping the ID conversions used by e2e coverage. Made-with: Cursor * chore(trailbase-db-collection): add subset support changeset Add the missing patch changeset for the TrailBase subset query work covering pagination, ordering, and where support. Made-with: Cursor --------- Co-authored-by: MentalGear <2837147+MentalGear@users.noreply.github.com> Co-authored-by: Sam Willis <sam.willis@gmail.com>
1 parent 9952921 commit b0276d4

11 files changed

Lines changed: 926 additions & 35 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@tanstack/trailbase-db-collection': patch
3+
---
4+
5+
Add a wider range of subset queries for TrailBase integration, including pagination, ordering, and basic `where` support.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
FROM trailbase/trailbase:latest
2+
3+
COPY traildepot /app/traildepot/
4+
EXPOSE 4000
5+
6+
CMD ["/app/trail", "--data-dir", "/app/traildepot", "run", "--address", "0.0.0.0:4000"]
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
import { execSync, spawn } from 'node:child_process'
2+
import { dirname } from 'node:path'
3+
import { fileURLToPath } from 'node:url'
4+
import type { ChildProcess } from 'node:child_process'
5+
import type { TestProject } from 'vitest/node'
6+
7+
const CONTAINER_NAME = 'trailbase-e2e-test'
8+
const TRAILBASE_PORT = process.env.TRAILBASE_PORT ?? '4047'
9+
const TRAILBASE_URL =
10+
process.env.TRAILBASE_URL ?? `http://localhost:${TRAILBASE_PORT}`
11+
12+
// Module augmentation for type-safe context injection
13+
declare module 'vitest' {
14+
export interface ProvidedContext {
15+
baseUrl: string
16+
}
17+
}
18+
19+
function isDockerAvailable(): boolean {
20+
try {
21+
execSync('docker --version', { stdio: 'pipe' })
22+
return true
23+
} catch {}
24+
25+
return false
26+
}
27+
28+
async function isTrailBaseRunning(url: string): Promise<boolean> {
29+
try {
30+
const res = await fetch(`${url}/api/healthcheck`)
31+
return res.ok
32+
} catch {}
33+
34+
return false
35+
}
36+
37+
function buildDockerImage(): void {
38+
const DOCKER_DIR = dirname(fileURLToPath(import.meta.url))
39+
40+
console.log('🔨 Building TrailBase Docker image...')
41+
execSync(`docker build -t trailbase-e2e ${DOCKER_DIR}`, {
42+
stdio: 'inherit',
43+
})
44+
console.log('✓ Docker image built')
45+
}
46+
47+
function cleanupExistingContainer(): void {
48+
try {
49+
execSync(`docker stop ${CONTAINER_NAME}`, {
50+
stdio: 'pipe',
51+
})
52+
execSync(`docker rm ${CONTAINER_NAME}`, {
53+
stdio: 'pipe',
54+
})
55+
} catch {
56+
// Ignore errors - container might not exist
57+
}
58+
}
59+
60+
function startDockerContainer(): ChildProcess {
61+
console.log('🚀 Starting TrailBase container...')
62+
63+
const proc = spawn(
64+
'docker',
65+
[
66+
'run',
67+
'--rm',
68+
'--name',
69+
CONTAINER_NAME,
70+
'-p',
71+
`${TRAILBASE_PORT}:4000`,
72+
'trailbase-e2e',
73+
],
74+
{
75+
stdio: ['ignore', 'pipe', 'pipe'],
76+
},
77+
)
78+
79+
proc.stdout.on('data', (data) => {
80+
console.log(`[trailbase] ${data.toString().trim()}`)
81+
})
82+
83+
proc.stderr.on('data', (data) => {
84+
console.error(`[trailbase] ${data.toString().trim()}`)
85+
})
86+
87+
proc.on('error', (error) => {
88+
console.error('Failed to start TrailBase container:', error)
89+
})
90+
91+
return proc
92+
}
93+
94+
async function waitForTrailBase(url: string): Promise<void> {
95+
return new Promise<void>((resolve, reject) => {
96+
const timeout = setTimeout(() => {
97+
reject(
98+
new Error(`Timed out waiting for TrailBase to be active at ${url}`),
99+
)
100+
}, 60000) // 60 seconds timeout for startup
101+
102+
const check = async (): Promise<void> => {
103+
try {
104+
// Try the healthz endpoint first, then fall back to root
105+
const res = await fetch(`${url}/api/healthcheck`)
106+
if (res.ok) {
107+
clearTimeout(timeout)
108+
return resolve()
109+
}
110+
} catch {}
111+
112+
setTimeout(() => void check(), 500)
113+
}
114+
115+
void check()
116+
})
117+
}
118+
119+
/**
120+
* Global setup for TrailBase e2e test suite
121+
*/
122+
export default async function ({ provide }: TestProject) {
123+
let serverProcess: ChildProcess | null = null
124+
125+
// Check if TrailBase is already running
126+
if (await isTrailBaseRunning(TRAILBASE_URL)) {
127+
console.log(`✓ TrailBase already running at ${TRAILBASE_URL}`)
128+
} else {
129+
if (!isDockerAvailable()) {
130+
throw new Error(
131+
`TrailBase is not running at ${TRAILBASE_URL} and no startup method is available.\n` +
132+
`Please either:\n` +
133+
` 1. Start TrailBase manually at ${TRAILBASE_URL}\n` +
134+
` 2. Install Docker and run the tests again\n`,
135+
)
136+
}
137+
138+
// Clean up any existing container
139+
cleanupExistingContainer()
140+
// Build Docker image
141+
buildDockerImage()
142+
// Start container
143+
serverProcess = startDockerContainer()
144+
}
145+
146+
// Wait for TrailBase server to be ready
147+
console.log(`⏳ Waiting for TrailBase at ${TRAILBASE_URL}...`)
148+
await waitForTrailBase(TRAILBASE_URL)
149+
console.log('✓ TrailBase is ready')
150+
151+
// Provide context values to all tests
152+
provide('baseUrl', TRAILBASE_URL)
153+
154+
console.log('✓ Global setup complete\n')
155+
156+
// Return cleanup function (runs once after all tests)
157+
return () => {
158+
console.log('\n🧹 Running global teardown...')
159+
if (serverProcess !== null) {
160+
cleanupExistingContainer()
161+
serverProcess.kill()
162+
serverProcess = null
163+
}
164+
console.log('✅ Global teardown complete')
165+
}
166+
}

0 commit comments

Comments
 (0)