* Build a mock Transaction that resolves zql builder queries against in-memory data. * * The `tx.run(query)` API passes an AST, but the builder stores filter info. * We intercept via Proxy so `.where()` chains build up predicates, and * `.one()` / the final run resolves them. * * This is a sim
(
store: TableStore,
opts: { location: 'server' | 'client' } = { location: 'server' }
)
| 149 | * zql.<table>.where(col, '=', val) |
| 150 | */ |
| 151 | function createMockTx( |
| 152 | store: TableStore, |
| 153 | opts: { location: 'server' | 'client' } = { location: 'server' } |
| 154 | ) { |
| 155 | // Track mutations for assertions |
| 156 | const mutations: Array<{ op: string; table: string; data: any }> = [] |
| 157 | |
| 158 | function getRows(table: keyof TableStore) { |
| 159 | return store[table] ?? [] |
| 160 | } |
| 161 | |
| 162 | function matchPk(table: keyof TableStore, row: any, data: any) { |
| 163 | return TABLE_PKS[table].every((pk) => row[pk] === data[pk]) |
| 164 | } |
| 165 | |
| 166 | // The mutate object provides insert/update/upsert/delete for each table |
| 167 | function makeTableMutator(tableName: keyof TableStore) { |
| 168 | return { |
| 169 | insert: async (data: any) => { |
| 170 | mutations.push({ op: 'insert', table: tableName, data }) |
| 171 | ;(store[tableName] as any[]).push({ ...data }) |
| 172 | }, |
| 173 | update: async (data: any) => { |
| 174 | mutations.push({ op: 'update', table: tableName, data }) |
| 175 | const rows = store[tableName] as any[] |
| 176 | const idx = rows.findIndex((r) => matchPk(tableName, r, data)) |
| 177 | if (idx >= 0) Object.assign(rows[idx], data) |
| 178 | }, |
| 179 | upsert: async (data: any) => { |
| 180 | mutations.push({ op: 'upsert', table: tableName, data }) |
| 181 | const rows = store[tableName] as any[] |
| 182 | const idx = rows.findIndex((r) => matchPk(tableName, r, data)) |
| 183 | if (idx >= 0) { |
| 184 | Object.assign(rows[idx], data) |
| 185 | } else { |
| 186 | rows.push({ ...data }) |
| 187 | } |
| 188 | }, |
| 189 | delete: async (data: any) => { |
| 190 | mutations.push({ op: 'delete', table: tableName, data }) |
| 191 | const rows = store[tableName] as any[] |
| 192 | const idx = rows.findIndex((r) => matchPk(tableName, r, data)) |
| 193 | if (idx >= 0) rows.splice(idx, 1) |
| 194 | }, |
| 195 | } |
| 196 | } |
| 197 | |
| 198 | const mutate: any = {} |
| 199 | for (const t of Object.keys(store) as (keyof TableStore)[]) { |
| 200 | mutate[t] = makeTableMutator(t) |
| 201 | } |
| 202 | |
| 203 | // tx.run(query) — the query is a builder that carries an AST. |
| 204 | // We need to resolve it against our store. |
| 205 | // The builder from createBuilder(schema) returns objects with .ast property. |
| 206 | // We parse the AST to figure out which table + where clauses + one(). |
| 207 | function resolveAst(ast: any): any[] { |
| 208 | const table = ast.table as keyof TableStore |
no test coverage detected
searching dependent graphs…