browser-sqlite-playground / source /teaql-ts /tests /numeric-partition-trace.test.ts
philipgreat's picture
Deploy traceable order management playground
aa89cb5 verified
Raw History Blame Contribute Delete
7.89 kB
import { OrderBy, SelectQuery } from '../src/core/ast';
import { UserContext } from '../src/core/context';
import { PLAINTEXT_LOG_ENV } from '../src/core/log-privacy';
import {
AbstractSQLTeaQLClient, EntitySchema, SQLExecutionEvidenceStore,
SQLExecutionMetadata, SqlQueryResult,
} from '../src/sql/core';
import { SQLiteDriver } from '../src/sql/sqlite';
// TC-SQL-10 native subset: public numeric grouping and relation-derived windows.
// SelectQuery has no HAVING or public root partitionBy API; do not invent one.
const SECRET = 'PRIVATE-NUMERIC-PARTITION';
const integer = (columnName: string) => ({ columnName, logicalType: 'integer' as const,
decode: 'number' as const, logPolicy: 'plain' as const });
const identity = { id: integer('id'), version: integer('version') };
const schemas: Record<string, EntitySchema> = {
GroupRoot: { table: 'group_root', columns: identity, relations: {
owners: { targetEntity: 'GroupOwner', localKey: 'id', foreignKey: 'rootId', many: true },
} },
GroupOwner: { table: 'group_owner', columns: { ...identity, rootId: integer('root_id') }, relations: {
samples: { targetEntity: 'MetricSample', localKey: 'id', foreignKey: 'ownerId', many: true },
} },
MetricSample: { table: 'metric_sample', auditMaskFields: ['name'], columns: {
...identity, ownerId: integer('owner_id'), bucket: integer('bucket'),
name: { columnName: 'name', logicalType: 'text', decode: 'string', logPolicy: 'plain' },
} },
};
class Driver extends SQLiteDriver {
readonly reads: Array<{ sql: string; values: unknown[] }> = [];
override async query(sql: string, values: any[] = []): Promise<SqlQueryResult> {
if (sql.startsWith('SELECT')) this.reads.push({ sql, values: [...values] });
return super.query(sql, values);
}
}
class Client extends AbstractSQLTeaQLClient {
constructor(readonly transport = new Driver(':memory:')) { super(transport, schemas); }
}
async function fixture(logging: boolean) {
const client = new Client().setDiagnosticSQLLogSink(undefined);
await new UserContext().insertResource('dataService', client).ensureSchema();
await client.executeMutation({ entity: 'GroupRoot', action: 'Create', id: 1, payload: {}, comment: 'seed group root' });
for (const id of [1, 2, 3]) await client.executeMutation({ entity: 'GroupOwner', action: 'Create', id,
payload: { rootId: 1 }, comment: 'seed numeric group owner' });
// Different per-owner counts, a filtered row, an orphan, and an empty owner.
const samples = [[1, 20], [1, 10], [1, 10], [1, 20], [2, 20], [2, 20], [2, 10], [null, 30]];
for (const [index, [ownerId, bucket]] of samples.entries()) await client.executeMutation({
entity: 'MetricSample', action: 'Create', id: index + 1,
payload: { ownerId, bucket, name: index === 3 ? 'excluded' : SECRET }, comment: 'seed numeric sample',
});
const evidence = new SQLExecutionEvidenceStore();
const diagnostics: SQLExecutionMetadata[] = [];
client.setRuntimeTelemetrySink(evidence).setQueryLoggingEnabled(logging)
.setDiagnosticSQLLogSink({ write: entry => { diagnostics.push(entry); } });
client.transport.reads.length = 0;
return { client, evidence, diagnostics };
}
const previousPlaintext = process.env[PLAINTEXT_LOG_ENV];
beforeEach(() => { delete process.env[PLAINTEXT_LOG_ENV]; });
afterEach(() => {
if (previousPlaintext === undefined) delete process.env[PLAINTEXT_LOG_ENV];
else process.env[PLAINTEXT_LOG_ENV] = previousPlaintext;
});
const cases = ['root', 'window', 'probe'].flatMap(shape =>
[true, false].flatMap(logging => [1, 10].map(limit => ({ shape, logging, limit }))));
test.each(cases)('numeric grouping $shape logging=$logging limit=$limit', async ({ shape, logging, limit }) => {
const f = await fixture(logging);
try {
expect(schemas.MetricSample.relations?.bucket).toBeUndefined();
const grouped = new SelectQuery('MetricSample').groupBy('ownerId').groupBy('bucket')
.aggregate('Count', 'id', 'n').filter({ name: { $eq: SECRET }, ownerId: { $isNull: false } })
.order(OrderBy.desc('bucket')).limit(limit);
const root = shape === 'root' ? 'MetricSample' : 'GroupRoot';
const query = (shape === 'root' ? grouped.order(OrderBy.asc('ownerId'))
: new SelectQuery('GroupRoot').limit(1).relationQuery('owners',
new SelectQuery('GroupOwner').limit(10).order(OrderBy.asc('id'))
.relationQuery('samples', grouped.topNProbeParentThreshold(shape === 'window' ? 0 : 32))))
.comment(`count numeric groups ${SECRET}`).purpose(`render numeric groups ${SECRET}`);
const original = JSON.stringify(query);
const rows = await f.client.executeQuery<any>(query);
if (shape === 'root') {
expect(rows.map(row => [row.ownerId, row.bucket, row.n]))
.toEqual([[1, 20, 1], [2, 20, 2], [1, 10, 2], [2, 10, 1]].slice(0, limit));
} else {
expect(rows).toHaveLength(1);
expect(rows[0].owners.map((owner: any) => [owner.id,
owner.samples.map((row: any) => [row.bucket, row.n])])).toEqual([
[1, [[20, 1], [10, 2]].slice(0, limit)],
[2, [[20, 2], [10, 1]].slice(0, limit)], [3, []],
]);
for (const owner of rows[0].owners) for (const sample of owner.samples) {
expect(sample).not.toHaveProperty('__teaql_partition_rank');
}
}
expect(JSON.stringify(query)).toBe(original);
const entries = f.evidence.snapshot();
const routes = shape === 'root' ? [[]] : [[], ['GroupRoot.owners'],
...Array.from({ length: shape === 'window' ? 1 : 3 }, () => ['GroupRoot.owners', 'GroupOwner.samples'])];
expect(entries).toHaveLength(routes.length);
expect(f.client.transport.reads).toHaveLength(routes.length);
expect(f.diagnostics).toHaveLength(logging ? routes.length : 0);
entries.forEach((entry, index) => {
expect(entry.tracePath.map(node => [node.kind, node.name, node.detail ?? ''])).toEqual([
['operation', root, 'query'], ['request', root, ''],
...routes[index].map(field => ['relation', field.split('.').pop(), field]),
['provider', 'sqlite', ''], ['sql', 'select', ''],
]);
expect(entry.executionOutcome).toBe('success');
expect(entry.comment).toContain('count numeric groups');
expect(entry.purpose).toContain('render numeric groups');
console.log(`NUMERIC_SQL shape=${shape} logging=${logging} limit=${limit} ` +
JSON.stringify({ sql: entry.parameterizedSQL, path: entry.tracePath }));
});
// The future-only child operand must protect the very first root statement.
expect(JSON.stringify(entries)).not.toContain(SECRET);
expect(JSON.stringify(f.diagnostics)).not.toContain(SECRET);
if (shape !== 'root') expect(f.client.transport.reads[0].values).not.toContain(SECRET);
const groupedReads = f.client.transport.reads.filter(read => read.sql.includes('COUNT('));
expect(groupedReads).toHaveLength(shape === 'probe' ? 3 : 1);
for (const read of groupedReads) {
expect(read.values).toContain(SECRET);
expect(read.sql).toContain('GROUP BY "owner_id", "bucket"');
expect(read.sql.includes('ROW_NUMBER() OVER')).toBe(shape === 'window');
expect(read.sql).toContain('ORDER BY "bucket" DESC, "owner_id" ASC');
expect(read.sql).not.toContain(', "id" ASC');
}
f.evidence.enableAll();
f.diagnostics.length = 0;
await f.client.executeQuery(new SelectQuery('GroupRoot').limit(1)
.comment(SECRET).purpose('independent numeric query'));
expect(f.evidence.snapshot()[0].comment).toBe(SECRET);
expect(f.evidence.snapshot()[0].tracePath.map(node => node.name))
.toEqual(['GroupRoot', 'GroupRoot', 'sqlite', 'select']);
expect(f.diagnostics).toHaveLength(logging ? 1 : 0);
console.log(`NUMERIC_RESULT shape=${shape} logging=${logging} limit=${limit} SQL=${routes.length} counts=verified`);
} finally { await f.client.close(); }
});