Skip to content

Commit

Permalink
fix
Browse files Browse the repository at this point in the history
  • Loading branch information
fhur committed Oct 10, 2024
1 parent f11d370 commit d6b76c8
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 122 deletions.
70 changes: 26 additions & 44 deletions packages/backend/src/execution/execute.test.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import {
col,
Query,
query,
} from '@synthql/queries';
import { col, Query, query } from '@synthql/queries';
import { describe, expect, test } from 'vitest';
import { collectLast } from '..';
import { QueryProvider } from '../QueryProvider';
import { DB, schema } from '../tests/generated';
import { PgCatalogInt4, PgCatalogText } from '../tests/generated/db';
import { execute } from './execute';
import { QueryProviderExecutor } from './executors/QueryProviderExecutor';
import { flattenDeferredQueryResult } from '../tests/util/flattenDeferredQueryResultsResults';
import { flattenDeferredQueryResult } from '../tests/util/flattenDeferredQueryResults';

interface DbWithVirtualTables extends DB {
film_rating: {
Expand Down Expand Up @@ -436,10 +432,9 @@ const storeProvider: StoreQueryProvider = {

describe('execute', () => {
test('1 level deep deferred queries with one include', async () => {
const filmLanguageQuery = from('language')
.where({
language_id: col('film.language_id'),
})
const filmLanguageQuery = from('language').where({
language_id: col('film.language_id'),
});
const query = createFilmQuery(filmLanguageQuery.one()).many();

const queryWithDefer = createFilmQuery(
Expand Down Expand Up @@ -470,16 +465,13 @@ describe('execute', () => {
});

test('1 level deep deferred queries with two includes', async () => {
const customerAddressQuery = from('address').where({
address_id: col('customer.address_id'),
});

const customerAddressQuery = from('address')
.where({
address_id: col('customer.address_id'),
})

const customerStoreQuery = from('store')
.where({
store_id: col('customer.store_id'),
})
const customerStoreQuery = from('store').where({
store_id: col('customer.store_id'),
});

const query = createCustomerQuery(
customerAddressQuery.one(),
Expand Down Expand Up @@ -526,20 +518,17 @@ describe('execute', () => {
});

test('1 level deep deferred queries with three includes', async () => {
const paymentStaffQuery = from('staff')
.where({
staff_id: col('payment.staff_id'),
});
const paymentStaffQuery = from('staff').where({
staff_id: col('payment.staff_id'),
});

const paymentRentalQuery = from('rental')
.where({
rental_id: col('payment.rental_id'),
});
const paymentRentalQuery = from('rental').where({
rental_id: col('payment.rental_id'),
});

const paymentCustomerQuery = from('customer')
.where({
customer_id: col('payment.customer_id'),
})
const paymentCustomerQuery = from('customer').where({
customer_id: col('payment.customer_id'),
});

const query = createPaymentQuery(
paymentCustomerQuery.one(),
Expand Down Expand Up @@ -591,15 +580,13 @@ describe('execute', () => {

// TODO: fix the underlying `setIn()` fn to get this to work
test.skip('2 level deep deferred queries', async () => {
const addressCityQuery = from('city')
.where({
city_id: col('address.city_id'),
});
const addressCityQuery = from('city').where({
city_id: col('address.city_id'),
});

const storeAddressQuery = from('address')
.where({
address_id: col('store.address_id'),
})
const storeAddressQuery = from('address').where({
address_id: col('store.address_id'),
});

const query = createCustomerQuery(
createCustomerAddressQuery(addressCityQuery.one()).one(),
Expand Down Expand Up @@ -729,8 +716,6 @@ describe('execute', () => {
});
});



function createCustomerQuery(
...queriesToInclude: Array<Query<DbWithVirtualTables>>
) {
Expand Down Expand Up @@ -774,6 +759,3 @@ function createPaymentQuery(
.where({})
.include(Object.fromEntries(queriesToInclude.entries()));
}



55 changes: 37 additions & 18 deletions packages/backend/src/tests/propertyBased/properties/defer.test.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,56 @@
import { describe } from "node:test";
import { ArbitraryQueryBuilder } from "../arbitraries/ArbitraryQueryBuilder";
import { test } from "@fast-check/vitest";
import { queryEngine } from "../../queryEngine";
import { expect } from "vitest";
import { queryHeight } from "../../util/queryHeight";
import { flattenDeferredQueryResult } from "../../util/flattenDeferredResults";
import { describe } from 'node:test';
import { ArbitraryQueryBuilder } from '../arbitraries/ArbitraryQueryBuilder';
import { test } from '@fast-check/vitest';
import { queryEngine } from '../../queryEngine';
import { expect } from 'vitest';
import { queryHeight } from '../../util/queryHeight';
import { flattenDeferredQueryResult } from '../../util/flattenDeferredQueryResults';
import { Query } from '@synthql/queries';
import { DB } from '../../generated';

const queryBuilder = ArbitraryQueryBuilder.fromPagila();

describe('property based tests for defer', () => {
const numRuns = 100;
const timeout = numRuns * 20;
const timeout = numRuns * 100;
const endOnFailure = true;


test.prop([queryBuilder.withCardinality('many').withSomeResults().build()], {
verbose: true,
numRuns,
timeout,
endOnFailure,
})(
'A query and a defer() query return essentially the same result',
test.prop(
[queryBuilder.withCardinality('many').withSomeResults().build()],
{
verbose: true,
numRuns,
endOnFailure,
},
)(
[
'A query and a deferred query return essentially the same QueryResult',
].join(''),
async (query) => {
// so far we're only generating queries of height 1
// this is here as a reminder when we start supporting deeper queries.
// The moment we start supporting deeper queries, this line will break,
// but the rest of the test should still pass.
expect(queryHeight(query)).toBe(1);
const queryResult = await queryEngine.executeAndWait(query);

// as a quick sanity check, make sure there are actually some results
expect(queryResult).not.toEqual([]);

// now another sanity check on the flattenDeferResultsRecursively
expect(queryResult).toEqual(flattenDeferredQueryResult(query));
expect(queryResult).toEqual(
flattenDeferredQueryResult(queryResult),
);

const deferredQuery: Query<DB> = { ...query, lazy: true };

const deferredQueryResult =
await queryEngine.executeAndWait(deferredQuery);

expect(queryResult).toEqual(
flattenDeferredQueryResult(deferredQueryResult),
);
},
timeout,
);
});

Original file line number Diff line number Diff line change
@@ -1,40 +1,42 @@
import { DeferredResult } from "@synthql/queries";
import { getObjectChildren, iterateTree } from "../../util/tree/iterateTree";
import { DeferredResult } from '@synthql/queries';
import { mapRecursive } from '../../util/tree/mapRecursive';

/**
* Function used for testing to flatten deferred results in a query object.
*
*
* Example:
*
*
* ```ts
* {user_id: string, friends: DeferredResult<{user_id: string, name: string}[]>}
* ```
*
*
* Becomes:
*
* ```ts
*
* ```ts
* {user_id: string, friends: {user_id: string, name: string}[]}
* ```
*/
export function flattenDeferredQueryResult(queryWithDeferResult: { [key: string]: any }) {
for (const { node, path, parent } of iterateTree(queryWithDeferResult, getObjectChildren)) {
if (isDeferredResult(node) && parent) {
const key = path[path.length - 1];
parent[key] = node.data;
export function flattenDeferredQueryResult(queryWithDeferResult: {
[key: string]: any;
}) {
return mapRecursive(queryWithDeferResult, (node) => {
if (isDeferredResult(node)) {
return node.data;
}
}
return node;
});
}

type DoneDeferredResult<T = unknown> = DeferredResult<T> & { status: 'done' };

// note that this cannot be made a general purpose function because it can very easily produce
// false positives. This is only intended to be used in tests.
// Maybe in the future we will force DeferredResult
// Maybe in the future we will force DeferredResult
function isDeferredResult<T = unknown>(x: any): x is DoneDeferredResult<T> {
return (
x !== null &&
x !== undefined &&
x.status === 'done' &&
x.data !== undefined
);
}
}
7 changes: 2 additions & 5 deletions packages/backend/src/tests/util/queryHeight.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AnyQuery } from "@synthql/queries";
import { AnyQuery } from '@synthql/queries';

/**
* Calculate the height of a query i.e. the maximum depth of the query tree.
Expand All @@ -10,8 +10,5 @@ export function queryHeight(query: AnyQuery): number {
return 1;
}

return 1 + Math.max(
...subQueries.map(subQuery => queryHeight(subQuery))
);
return 1 + Math.max(...subQueries.map((subQuery) => queryHeight(subQuery)));
}

38 changes: 0 additions & 38 deletions packages/backend/src/util/tree/iterateTree.ts

This file was deleted.

21 changes: 21 additions & 0 deletions packages/backend/src/util/tree/mapRecursive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export function mapRecursive<T>(
item: T,
visitor: (traversable: unknown) => T,
): T {
const result = visitor(item);

if (
result === null ||
typeof result !== 'object' ||
result instanceof Date
) {
return result;
}

for (const [key, value] of Object.entries(result)) {
// @ts-ignore this operation is safe because we know that the result is an object
result[key] = mapRecursive(value, visitor);
}

return result;
}
2 changes: 1 addition & 1 deletion packages/docs/static/reference/assets/navigation.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit d6b76c8

Please sign in to comment.