|
| 1 | +/* |
| 2 | + * Cross-runtime smoke test — runs on Node, Bun, and Deno. |
| 3 | + * |
| 4 | + * Exercises the critical path that defines "can you even use this SDK on this |
| 5 | + * runtime": construct a Client, do one unary write, query the row count back |
| 6 | + * via the GreptimeDB HTTP SQL API, close the client. Any runtime-level |
| 7 | + * incompatibility in @grpc/grpc-js shows up here as a connection/stream |
| 8 | + * failure before the write resolves. |
| 9 | + * |
| 10 | + * Intentionally uses no `node:*` imports — only the SDK plus the global |
| 11 | + * `fetch` (available on all three runtimes). |
| 12 | + * |
| 13 | + * Env: |
| 14 | + * GREPTIMEDB_ENDPOINT (default: localhost:4001) gRPC target |
| 15 | + * GREPTIMEDB_HTTP (default: http://127.0.0.1:4000) HTTP base URL |
| 16 | + * |
| 17 | + * Run: |
| 18 | + * bun run scripts/smoke.ts |
| 19 | + * deno run -A scripts/smoke.ts |
| 20 | + * pnpm tsx scripts/smoke.ts # Node path (via tsx) |
| 21 | + */ |
| 22 | + |
| 23 | +import { Client, DataType, Precision, Table } from '../src/index.js'; |
| 24 | + |
| 25 | +const ROW_COUNT = 5; |
| 26 | + |
| 27 | +async function main(): Promise<void> { |
| 28 | + const endpoint = process.env.GREPTIMEDB_ENDPOINT ?? 'localhost:4001'; |
| 29 | + const httpBase = process.env.GREPTIMEDB_HTTP ?? 'http://127.0.0.1:4000'; |
| 30 | + const tableName = `smoke_test_${Date.now()}`; |
| 31 | + |
| 32 | + const client = new Client(Client.create(endpoint).withDatabase('public').build()); |
| 33 | + |
| 34 | + try { |
| 35 | + const now = Date.now(); |
| 36 | + const table = Table.new(tableName) |
| 37 | + .addTagColumn('host', DataType.String) |
| 38 | + .addFieldColumn('usage', DataType.Float64) |
| 39 | + .addTimestampColumn('ts', Precision.Millisecond); |
| 40 | + |
| 41 | + for (let i = 0; i < ROW_COUNT; i++) { |
| 42 | + table.addRow([`server-${i}`, 10 + i, now + i]); |
| 43 | + } |
| 44 | + |
| 45 | + const result = await client.write(table); |
| 46 | + if (result.value !== ROW_COUNT) { |
| 47 | + throw new Error(`write returned ${result.value} rows, expected ${ROW_COUNT}`); |
| 48 | + } |
| 49 | + |
| 50 | + const observed = await queryCount(httpBase, tableName); |
| 51 | + if (observed !== ROW_COUNT) { |
| 52 | + throw new Error(`SELECT COUNT(*) returned ${observed}, expected ${ROW_COUNT}`); |
| 53 | + } |
| 54 | + |
| 55 | + console.log(`smoke ok: wrote ${ROW_COUNT} rows to ${tableName}, verified via HTTP SQL`); |
| 56 | + } finally { |
| 57 | + await client.close(); |
| 58 | + } |
| 59 | +} |
| 60 | + |
| 61 | +// GreptimeDB HTTP SQL API: POST /v1/sql?db=<db> with form-encoded `sql=...`. |
| 62 | +async function queryCount(httpBase: string, table: string): Promise<number> { |
| 63 | + const url = `${httpBase.replace(/\/$/, '')}/v1/sql?db=public`; |
| 64 | + const res = await fetch(url, { |
| 65 | + method: 'POST', |
| 66 | + headers: { 'content-type': 'application/x-www-form-urlencoded' }, |
| 67 | + body: new URLSearchParams({ sql: `SELECT COUNT(*) FROM ${table}` }), |
| 68 | + }); |
| 69 | + if (!res.ok) { |
| 70 | + throw new Error(`HTTP SQL ${res.status}: ${await res.text()}`); |
| 71 | + } |
| 72 | + const body = (await res.json()) as { |
| 73 | + output?: { records?: { rows?: unknown[][] } }[]; |
| 74 | + }; |
| 75 | + const cell = body.output?.[0]?.records?.rows?.[0]?.[0]; |
| 76 | + if (typeof cell !== 'number' && typeof cell !== 'bigint') { |
| 77 | + throw new Error(`unexpected SQL response shape: ${JSON.stringify(body)}`); |
| 78 | + } |
| 79 | + return Number(cell); |
| 80 | +} |
| 81 | + |
| 82 | +main().catch((err: unknown) => { |
| 83 | + console.error('smoke failed:', err); |
| 84 | + process.exit(1); |
| 85 | +}); |
0 commit comments