postgis
Version:
A Node.js class for interacting with PostGIS-enabled PostgreSQL databases.
1,024 lines (830 loc) • 40.7 kB
JavaScript
// __tests__/postgis.test.js
const Postgis = require('../index'); // Adjust the path if necessary
describe('Postgis', () => {
let client;
let postgis;
beforeEach(() => {
client = {
query: jest.fn()
};
postgis = new Postgis(client);
});
describe('constructor', () => {
it('should throw an error if client is not provided', () => {
expect(() => new Postgis()).toThrow('A valid pg.Client instance is required.');
});
it('should not throw an error if a valid client is provided', () => {
expect(() => new Postgis(client)).not.toThrow();
});
});
describe('list_tables', () => {
it('should execute the correct query', async () => {
client.query.mockResolvedValue({ rows: [] });
const filter = 'srid = 4326';
await postgis.list_tables({ filter });
expect(client.query).toHaveBeenCalledWith(expect.stringMatching(/SELECT\s+i\.table_name,\s+i\.table_type,\s+g\.f_geometry_column\s+as\s+geometry_column,\s+g\.coord_dimension,\s+g\.srid,\s+g\.type\s+FROM\s+information_schema\.tables\s+i\s+LEFT\s+JOIN\s+geometry_columns\s+g\s+ON\s+i\.table_name\s+=\s+g\.f_table_name\s+INNER\s+JOIN\s+information_schema\.table_privileges\s+p\s+ON\s+i\.table_name\s+=\s+p\.table_name\s+AND\s+p\.grantee\s+in\s+\(current_user,\s+'PUBLIC'\)\s+AND\s+p\.privilege_type\s+=\s+'SELECT'\s+WHERE\s+i\.table_schema\s+not\s+in\s+\('pg_catalog',\s+'information_schema'\)\s+--\s+Optional\s+where\s+filter\s+and\s+srid\s+=\s+4326\s+ORDER\s+BY\s+table_name/));
});
it('should execute the correct query without filter', async () => {
client.query.mockResolvedValue({ rows: [] });
const expectedQuery = `
SELECT
i.table_name,
i.table_type,
g.f_geometry_column as geometry_column,
g.coord_dimension,
g.srid,
g.type
FROM
information_schema.tables i
LEFT JOIN geometry_columns g
ON i.table_name = g.f_table_name
INNER JOIN information_schema.table_privileges p
ON i.table_name = p.table_name
AND p.grantee in (current_user, 'PUBLIC')
AND p.privilege_type = 'SELECT'
WHERE
i.table_schema not in ('pg_catalog', 'information_schema')
-- Optional where filter
ORDER BY table_name
`.replace(/\s+/g, ' ').trim();
await postgis.list_tables();
const receivedQuery = client.query.mock.calls[0][0].replace(/\s+/g, ' ').trim();
// Expect the received query to match the expected pattern
expect(receivedQuery).toMatch(expectedQuery);
});
});
describe('list_columns', () => {
it('should execute the correct query', async () => {
// Mock the client.query method to resolve with an empty rows array
client.query.mockResolvedValue({ rows: [] });
// Define the table name for the test
const table = 'table_name';
// Call the function to execute
await postgis.list_columns(table);
// Expect the client.query method to be called with the correct SQL query
expect(client.query).toHaveBeenCalledWith(expect.stringMatching(
new RegExp(
`SELECT\\s+attname\\s+as\\s+field_name,\\s+typname\\s+as\\s+field_type\\s+FROM\\s+pg_namespace,\\s+pg_attribute,\\s+pg_type,\\s+pg_class\\s+WHERE\\s+pg_type\\.oid\\s+=\\s+atttypid\\s+AND\\s+pg_class\\.oid\\s+=\\s+attrelid\\s+AND\\s+relnamespace\\s+=\\s+pg_namespace\\.oid\\s+AND\\s+attnum\\s+>=\\s+1\\s+AND\\s+relname\\s+=\\s+'${table}'`
)
));
});
});
describe('query_table', () => {
it('should execute the correct query with parameters', async () => {
// Mock the client.query method to resolve with an empty rows array
client.query.mockResolvedValue({ rows: [] });
// Define the table and options for the test
const table = 'table_name';
const options = { columns: 'name', filter: `"state" = 'GOA'`, group: 'name', sort: 'name', limit: 10 };
// Call the function to execute
await postgis.query_table(table, options);
// Define the expected regular expression for the query
const expectedQuery = new RegExp(
`^SELECT\\s+${options.columns}\\s+FROM\\s+${table}\\s*` +
`${options.filter ? `WHERE\\s+${options.filter}\\s*` : ''}` +
`${options.group ? `GROUP\\s+BY\\s+${options.group}\\s*` : ''}` +
`${options.sort ? `ORDER\\s+BY\\s+${options.sort}\\s*` : ''}` +
`${options.limit ? `LIMIT\\s+${options.limit}\\s*` : ''}$`
);
// Expect the client.query method to be called with the correct SQL query
expect(client.query).toHaveBeenCalledWith(expect.stringMatching(expectedQuery));
});
it('should execute the query without parameters', async () => {
// Mock the client.query method to resolve with an empty rows array
client.query.mockResolvedValue({ rows: [] });
// Define the table and options for the test
const table = 'table_name';
// Call the function to execute
await postgis.query_table(table);
// Define the expected regular expression for the query
const expectedQuery = `
SELECT * FROM ${table}
`.replace(/\s+/g, ' ').trim();
const receivedQuery = client.query.mock.calls[0][0].replace(/\s+/g, ' ').trim();
// Expect the received query to match the expected pattern
expect(receivedQuery).toMatch(expectedQuery);
});
it('should execute the query with limit null', async () => {
// Mock the client.query method to resolve with an empty rows array
client.query.mockResolvedValue({ rows: [] });
// Define the table and options for the test
const table = 'table_name';
const options = { limit: null };
// Call the function to execute
await postgis.query_table(table, options);
// Define the expected regular expression for the query
const expectedQuery = `
SELECT * FROM ${table}
`.replace(/\s+/g, ' ').trim();
const receivedQuery = client.query.mock.calls[0][0].replace(/\s+/g, ' ').trim();
// Expect the received query to match the expected pattern
expect(receivedQuery).toMatch(expectedQuery);
});
});
describe('bbox', () => {
const queryFun = (table, geom_column = 'geom', srid = '4326', filter) => {
return `
SELECT
ST_Extent(ST_Transform(${geom_column}, ${srid})) as bbox
FROM
${table}
${filter ? `WHERE ${filter}` : ''}
`
}
it('should execute the correct query with default parameters', async () => {
client.query.mockResolvedValue({ rows: [] });
const table = 'table_name';
const geom_column = 'geom';
const srid = '4326';
const filter = '';
await postgis.bbox(table, { geom_column, srid, filter });
// Define the expected SQL query string
const expectedQuery = queryFun(table, geom_column, srid, filter).replace(/\s+/g, ' ').trim(); // Normalize to single line
// Normalize the received query to single line for comparison
const receivedQuery = client.query.mock.calls[0][0].replace(/\s+/g, ' ').trim();
expect(receivedQuery).toMatch(expectedQuery);
});
it('should execute the query without parameters', async () => {
client.query.mockResolvedValue({ rows: [] });
const table = 'table_name';
await postgis.bbox(table);
// Define the expected SQL query string
const expectedQuery = queryFun(table).replace(/\s+/g, ' ').trim(); // Normalize to single line
// Normalize the received query to single line for comparison
const receivedQuery = client.query.mock.calls[0][0].replace(/\s+/g, ' ').trim();
expect(receivedQuery).toMatch(expectedQuery);
});
it('should execute the query without filter', async () => {
client.query.mockResolvedValue({ rows: [] });
const table = 'table_name';
const geom_column = 'geom';
const srid = '4326';
const filter = 'column_name = "value"';
await postgis.bbox(table, { geom_column, srid, filter });
// Define the expected SQL query string
const expectedQuery = queryFun(table, geom_column, srid, filter).replace(/\s+/g, ' ').trim(); // Normalize to single line
// Normalize the received query to single line for comparison
const receivedQuery = client.query.mock.calls[0][0].replace(/\s+/g, ' ').trim();
expect(receivedQuery).toMatch(expectedQuery);
});
});
describe('centroid', () => {
const queryFun = (table, force_on_surface = false, geom_column = 'geom', srid = '4326', filter) => {
return `
SELECT
ST_X(
ST_Transform(
${force_on_surface ? 'ST_PointOnSurface' : 'ST_Centroid'}(
${geom_column}
), ${srid})
) as x,
ST_Y(
ST_Transform(
${force_on_surface ? 'ST_PointOnSurface' : 'ST_Centroid'}(
${geom_column}
), ${srid})
) as y
FROM
${table}
${filter ? `WHERE ${filter}` : ''}
`
}
it('should execute the correct query with default parameters', async () => {
client.query.mockResolvedValue({ rows: [] });
const table = 'table_name';
const force_on_surface = false;
const geom_column = 'geom';
const srid = '4326';
const filter = '';
// Call the function with default parameters
await postgis.centroid(table, { force_on_surface, geom_column, srid, filter });
// Define the expected SQL query
const expectedQuery = queryFun(table, force_on_surface, geom_column, srid, filter).replace(/\s+/g, ' ').trim(); // Normalize to single line
// Normalize the received query to single line for comparison
const receivedQuery = client.query.mock.calls[0][0].replace(/\s+/g, ' ').trim();
// Expect the received query to match the expected pattern
expect(receivedQuery).toMatch(expectedQuery);
});
it('should execute the query without parameters', async () => {
client.query.mockResolvedValue({ rows: [] });
const table = 'table_name';
// Call the function with default parameters
await postgis.centroid(table);
// Define the expected SQL query
const expectedQuery = queryFun(table).replace(/\s+/g, ' ').trim(); // Normalize to single line
// Normalize the received query to single line for comparison
const receivedQuery = client.query.mock.calls[0][0].replace(/\s+/g, ' ').trim();
// Expect the received query to match the expected pattern
expect(receivedQuery).toMatch(expectedQuery);
});
it('should execute the query with filter', async () => {
client.query.mockResolvedValue({ rows: [] });
const table = 'table_name';
const force_on_surface = true;
const geom_column = 'geom';
const srid = '4326';
const filter = 'column_name = "value"';
// Call the function with default parameters
await postgis.centroid(table, { force_on_surface, geom_column, srid, filter });
// Define the expected SQL query
const expectedQuery = queryFun(table, force_on_surface, geom_column, srid, filter).replace(/\s+/g, ' ').trim(); // Normalize to single line
// Normalize the received query to single line for comparison
const receivedQuery = client.query.mock.calls[0][0].replace(/\s+/g, ' ').trim();
// Expect the received query to match the expected pattern
expect(receivedQuery).toMatch(expectedQuery);
});
});
describe('intersect_feature', () => {
const queryFun = (table_from, table_to, columns = '*', distance = '0', geom_column_from = 'geom', geom_column_to = 'geom', filter, sort, limit) => {
return `
SELECT
${columns}
FROM
${table_from},
${table_to}
WHERE
ST_DWithin(
${table_from}.${geom_column_from},
${table_to}.${geom_column_to},
${distance}
)
${filter ? `AND ${filter}` : ''}
${sort ? `ORDER BY ${sort}` : ''}
${limit ? `LIMIT ${limit}` : ''}
`
}
it('should execute the correct query with default parameters', async () => {
client.query.mockResolvedValue({ rows: [] });
const table_from = 'table_name';
const table_to = 'other_table';
const columns = '*'
const distance = '0'
const geom_column_from = 'geom'
const geom_column_to = 'geom'
const filter = ''
const sort = 'some_column ASC'
const limit = 10
// Call the function with default parameters
await postgis.intersect_feature(table_from, table_to, { columns, distance, geom_column_from, geom_column_to, filter, sort, limit });
// Define the expected SQL query
const expectedQuery = queryFun(table_from, table_to, columns, distance, geom_column_from, geom_column_to, filter, sort, limit).replace(/\s+/g, ' ').trim(); // Normalize to single line
// Normalize the received query to single line for comparison
const receivedQuery = client.query.mock.calls[0][0].replace(/\s+/g, ' ').trim();
// Expect the received query to match the expected pattern
expect(receivedQuery).toMatch(expectedQuery);
});
it('should execute the query without parameters', async () => {
client.query.mockResolvedValue({ rows: [] });
const table_from = 'table_name';
const table_to = 'other_table';
// Call the function with default parameters
await postgis.intersect_feature(table_from, table_to);
// Define the expected SQL query
const expectedQuery = queryFun(table_from, table_to).replace(/\s+/g, ' ').trim(); // Normalize to single line
// Normalize the received query to single line for comparison
const receivedQuery = client.query.mock.calls[0][0].replace(/\s+/g, ' ').trim();
// Expect the received query to match the expected pattern
expect(receivedQuery).toMatch(expectedQuery);
});
it('should execute the query with filter', async () => {
client.query.mockResolvedValue({ rows: [] });
const table_from = 'table_name';
const table_to = 'other_table';
const columns = '*'
const distance = '0'
const geom_column_from = 'geom'
const geom_column_to = 'geom'
const filter = 'column_name = "value"'
const sort = 'some_column ASC'
const limit = 10
// Call the function with default parameters
await postgis.intersect_feature(table_from, table_to, { columns, distance, geom_column_from, geom_column_to, filter, sort, limit });
// Define the expected SQL query
const expectedQuery = queryFun(table_from, table_to, columns, distance, geom_column_from, geom_column_to, filter, sort, limit).replace(/\s+/g, ' ').trim(); // Normalize to single line
// Normalize the received query to single line for comparison
const receivedQuery = client.query.mock.calls[0][0].replace(/\s+/g, ' ').trim();
// Expect the received query to match the expected pattern
expect(receivedQuery).toMatch(expectedQuery);
});
});
describe('intersect_point', () => {
const queryFun = (table, point, columns = '*', distance = '0', geom_column = 'geom', filter, sort, limit) => {
const [x, y, srid] = point.match(/^(-?\d+\.?\d+),(-?\d+\.?\d+),([0-9]{4})$/).slice(1);
return `
SELECT
${columns}
FROM
${table}
WHERE
ST_DWithin(
${geom_column},
ST_Transform(
st_setsrid(
st_makepoint(${x}, ${y}),
${srid}
),
(SELECT ST_SRID(${geom_column}) FROM ${table} LIMIT 1)
),
${distance}
)
${filter ? `AND ${filter}` : ''}
${sort ? `ORDER BY ${sort}` : ''}
${limit ? `LIMIT ${limit}` : ''}
`
}
it('should execute the correct query with default parameters', async () => {
client.query.mockResolvedValue({ rows: [] });
const table = 'table_name';
const point = '73.70534,14.94202,4326'; // Ensure this is in the correct format
const columns = '*'
const distance = '0'
const geom_column = 'geom'
const filter = ''
const sort = 'some_column ASC'
const limit = 10
// Call the function with default parameters
await postgis.intersect_point(table, point, { columns, distance, geom_column, filter, sort, limit });
// Define the expected SQL query with lowercase PostGIS functions
const expectedQuery = queryFun(table, point, columns, distance, geom_column, filter, sort, limit).replace(/\s+/g, ' ').trim(); // Normalize to single line
// Normalize the received query to single line for comparison
const receivedQuery = client.query.mock.calls[0][0].replace(/\s+/g, ' ').trim();
// Expect the received query to match the expected pattern
expect(receivedQuery).toMatch(expectedQuery);
});
it('should execute the query without parameters', async () => {
client.query.mockResolvedValue({ rows: [] });
const table = 'table_name';
const point = '73.70534,14.94202,4326'; // Ensure this is in the correct format
// Call the function with default parameters
await postgis.intersect_point(table, point);
// Define the expected SQL query with lowercase PostGIS functions
const expectedQuery = queryFun(table, point).replace(/\s+/g, ' ').trim(); // Normalize to single line
// Normalize the received query to single line for comparison
const receivedQuery = client.query.mock.calls[0][0].replace(/\s+/g, ' ').trim();
// Expect the received query to match the expected pattern
expect(receivedQuery).toMatch(expectedQuery);
});
it('should execute the query with filter', async () => {
client.query.mockResolvedValue({ rows: [] });
const table = 'table_name';
const point = '73.70534,14.94202,4326'; // Ensure this is in the correct format
const columns = '*'
const distance = '0'
const geom_column = 'geom'
const filter = 'column_name ="value"'
const sort = 'some_column ASC'
const limit = 10
// Call the function with default parameters
await postgis.intersect_point(table, point, { columns, distance, geom_column, filter, sort, limit });
// Define the expected SQL query with lowercase PostGIS functions
const expectedQuery = queryFun(table, point, columns, distance, geom_column, filter, sort, limit).replace(/\s+/g, ' ').trim(); // Normalize to single line
// Normalize the received query to single line for comparison
const receivedQuery = client.query.mock.calls[0][0].replace(/\s+/g, ' ').trim();
// Expect the received query to match the expected pattern
expect(receivedQuery).toMatch(expectedQuery);
});
it('should execute the query without limit', async () => {
client.query.mockResolvedValue({ rows: [] });
const table = 'table_name';
const point = '73.70534,14.94202,4326'; // Ensure this is in the correct format
const columns = '*'
const distance = '0'
const geom_column = 'geom'
const filter = 'column_name ="value"'
const sort = 'some_column ASC'
const limit = null
// Call the function with default parameters
await postgis.intersect_point(table, point, { columns, distance, geom_column, filter, sort, limit });
// Define the expected SQL query with lowercase PostGIS functions
const expectedQuery = queryFun(table, point, columns, distance, geom_column, filter, sort, limit).replace(/\s+/g, ' ').trim(); // Normalize to single line
// Normalize the received query to single line for comparison
const receivedQuery = client.query.mock.calls[0][0].replace(/\s+/g, ' ').trim();
// Expect the received query to match the expected pattern
expect(receivedQuery).toMatch(expectedQuery);
});
});
describe('geojson', () => {
const queryFun = (table, bounds, id_column, precision = 9, geom_column = 'geom', columns, filter) => {
let bounds_value = bounds ? bounds.split(',').map(Number) : null;
return `SELECT
jsonb_build_object(
'type', 'Feature',
${id_column ? `'id', ${id_column},` : ''
}
'geometry', ST_AsGeoJSON(geom, ${parseInt(precision, 10)})::jsonb,
'properties', to_jsonb( subq.* ) - 'geom' ${id_column ? `- '${id_column}'` : ''}
) AS geojson
FROM (
SELECT
ST_Transform(${geom_column}, 4326) as geom
${columns ? `, ${columns}` : ''}
${id_column ? `, ${id_column}` : ''}
FROM
${table},
(SELECT ST_SRID(${geom_column}) AS srid FROM ${table} WHERE ${geom_column} IS NOT NULL LIMIT 1) a
${filter || bounds_value ? 'WHERE' : ''}
${filter ? `${filter}` : ''}
${filter && bounds_value ? 'AND' : ''}
${bounds_value && bounds_value.length === 4 ?
`${geom_column} &&
ST_Transform(
ST_MakeEnvelope(${bounds_value.join()}, 4326),
srid
)
`
: ''
}
${bounds_value && bounds_value.length === 3 ?
`${geom_column} &&
ST_Transform(
ST_TileEnvelope(${bounds_value.join()}),
srid
)
`
: ''
}
) as subq`
}
it('should return geojson object', async () => {
client.query.mockResolvedValue({ rows: [{ geojson: {} }] });
const table = 'table_name';
const result = await postgis.geojson(table, {});
expect(result).toEqual({ type: 'FeatureCollection', features: [{}] });
});
it('should execute the correct query with default parameters', async () => {
client.query.mockResolvedValue({ rows: [{ geojson: {} }] });
const table = 'table_name';
const columns = '*'; // Default or adjust as needed
const geom_column = 'geom'; // Default or adjust as needed
const id_column = 'id'; // Default or adjust as needed
const precision = 9; // Default or adjust as needed
const filter = ''; // Default or adjust as needed
const bounds = '1,2'
await postgis.geojson(table, { bounds, id_column, precision, geom_column, columns, filter });
// Define the expected SQL query with normalized whitespace
const expectedQuery = queryFun(table, bounds, id_column, precision, geom_column, columns, filter).replace(/\s+/g, ' ').trim(); // Normalize to single line
// Normalize the received query to single line for comparison
const receivedQuery = client.query.mock.calls[0][0].replace(/\s+/g, ' ').trim();
// Expect the received query to match the expected pattern
expect(receivedQuery).toMatch(expectedQuery);
});
it('should execute the query without parameters', async () => {
client.query.mockResolvedValue({ rows: [{ geojson: {} }] });
const table = 'table_name';
await postgis.geojson(table);
// Define the expected SQL query with normalized whitespace
const expectedQuery = queryFun(table).replace(/\s+/g, ' ').trim(); // Normalize to single line
// Normalize the received query to single line for comparison
const receivedQuery = client.query.mock.calls[0][0].replace(/\s+/g, ' ').trim();
// Expect the received query to match the expected pattern
expect(receivedQuery).toMatch(expectedQuery);
});
it('should execute the query with filter and bounds length 3', async () => {
client.query.mockResolvedValue({ rows: [{ geojson: {} }] });
const table = 'table_name';
const columns = '*'; // Default or adjust as needed
const geom_column = 'geom'; // Default or adjust as needed
const id_column = 'id'; // Default or adjust as needed
const precision = 9; // Default or adjust as needed
const filter = 'column_name = "value"'; // Default or adjust as needed
const bounds = '1,2,3'
await postgis.geojson(table, { bounds, id_column, precision, geom_column, columns, filter });
// Define the expected SQL query with normalized whitespace
const expectedQuery = queryFun(table, bounds, id_column, precision, geom_column, columns, filter).replace(/\s+/g, ' ').trim(); // Normalize to single line
// Normalize the received query to single line for comparison
const receivedQuery = client.query.mock.calls[0][0].replace(/\s+/g, ' ').trim();
// Expect the received query to match the expected pattern
expect(receivedQuery).toMatch(expectedQuery);
});
it('should execute the query with bounds length 4', async () => {
client.query.mockResolvedValue({ rows: [{ geojson: {} }] });
const table = 'table_name';
const columns = '*'; // Default or adjust as needed
const geom_column = 'geom'; // Default or adjust as needed
const id_column = 'id'; // Default or adjust as needed
const precision = 9; // Default or adjust as needed
const filter = 'column_name = "value"'; // Default or adjust as needed
const bounds = '1,2,3,4'
await postgis.geojson(table, { bounds, id_column, precision, geom_column, columns, filter });
// Define the expected SQL query with normalized whitespace
const expectedQuery = queryFun(table, bounds, id_column, precision, geom_column, columns, filter).replace(/\s+/g, ' ').trim(); // Normalize to single line
// Normalize the received query to single line for comparison
const receivedQuery = client.query.mock.calls[0][0].replace(/\s+/g, ' ').trim();
// Expect the received query to match the expected pattern
expect(receivedQuery).toMatch(expectedQuery);
});
});
describe('geobuf', () => {
const queryFun = (table, bounds, geom_column = 'geom', columns, filter) => {
let bounds_value = bounds ? bounds.split(',').map(Number) : null;
return `SELECT
ST_AsGeobuf(q, 'geom')
FROM
(
SELECT
ST_Transform(${geom_column}, 4326) as geom
${columns ? `, ${columns}` : ''}
FROM
${table}
${bounds_value
? `, (SELECT ST_SRID(${geom_column}) AS srid FROM ${table} WHERE ${geom_column} IS NOT NULL LIMIT 1) sq`
: ''
}
${filter || bounds_value ? 'WHERE' : ''}
${filter ? `${filter}` : ''}
${filter && bounds_value ? 'AND' : ''}
${bounds_value && bounds_value.length === 4 ?
`${geom_column} &&
ST_Transform(
ST_MakeEnvelope(${bounds_value.join()}, 4326),
srid
)
`
: ''
}
${bounds_value && bounds_value.length === 3 ?
`${geom_column} &&
ST_Transform(
ST_TileEnvelope(${bounds_value.join()}),
srid
)
`
: ''
}
) as q;
`
}
it('should return geobuf data', async () => {
client.query.mockResolvedValue({ rows: [{ st_asgeobuf: Buffer.from('') }] });
const table = 'table_name';
const result = await postgis.geobuf(table, {});
expect(result).toBeInstanceOf(Buffer);
});
it('should execute the correct query with default parameters', async () => {
client.query.mockResolvedValue({ rows: [{ st_asgeobuf: Buffer.from('') }] });
const table = 'table_name';
const columns = '*'; // Default or adjust as needed
const geom_column = 'geom'; // Default or adjust as needed
const filter = ''; // Default or adjust as needed
const bounds = '1,2'
await postgis.geobuf(table, { bounds, geom_column, columns, filter });
// Define the expected SQL query with normalized whitespace
const expectedQuery = queryFun(table, bounds, geom_column, columns, filter).replace(/\s+/g, ' ').trim(); // Normalize to single line
// Normalize the received query to single line for comparison
const receivedQuery = client.query.mock.calls[0][0].replace(/\s+/g, ' ').trim();
// Expect the received query to match the expected pattern
expect(receivedQuery).toMatch(expectedQuery);
});
it('should execute the query without parameters', async () => {
client.query.mockResolvedValue({ rows: [{ st_asgeobuf: Buffer.from('') }] });
const table = 'table_name';
await postgis.geobuf(table);
// Define the expected SQL query with normalized whitespace
const expectedQuery = queryFun(table).replace(/\s+/g, ' ').trim(); // Normalize to single line
// Normalize the received query to single line for comparison
const receivedQuery = client.query.mock.calls[0][0].replace(/\s+/g, ' ').trim();
// Expect the received query to match the expected pattern
expect(receivedQuery).toMatch(expectedQuery);
});
it('should execute the query with filter and bounds length 3', async () => {
client.query.mockResolvedValue({ rows: [{ st_asgeobuf: Buffer.from('') }] });
const table = 'table_name';
const columns = '*'; // Default or adjust as needed
const geom_column = 'geom'; // Default or adjust as needed
const filter = 'column_name = "value"'; // Default or adjust as needed
const bounds = '1,2,3'
await postgis.geobuf(table, { bounds, geom_column, columns, filter });
// Define the expected SQL query with normalized whitespace
const expectedQuery = queryFun(table, bounds, geom_column, columns, filter).replace(/\s+/g, ' ').trim(); // Normalize to single line
// Normalize the received query to single line for comparison
const receivedQuery = client.query.mock.calls[0][0].replace(/\s+/g, ' ').trim();
// Expect the received query to match the expected pattern
expect(receivedQuery).toMatch(expectedQuery);
});
it('should execute the query with bounds length 4', async () => {
client.query.mockResolvedValue({ rows: [{ st_asgeobuf: Buffer.from('') }] });
const table = 'table_name';
const columns = '*'; // Default or adjust as needed
const geom_column = 'geom'; // Default or adjust as needed
const filter = 'column_name = "value"'; // Default or adjust as needed
const bounds = '1,2,3,4'
await postgis.geobuf(table, { bounds, geom_column, columns, filter });
// Define the expected SQL query with normalized whitespace
const expectedQuery = queryFun(table, bounds, geom_column, columns, filter).replace(/\s+/g, ' ').trim(); // Normalize to single line
// Normalize the received query to single line for comparison
const receivedQuery = client.query.mock.calls[0][0].replace(/\s+/g, ' ').trim();
// Expect the received query to match the expected pattern
expect(receivedQuery).toMatch(expectedQuery);
});
});
describe('mvt', () => {
const queryFun = (table, x, y, z, columns, id_column, geom_column = 'geom', filter) => {
return `
WITH mvtgeom as (
SELECT
ST_AsMVTGeom (
ST_Transform(${geom_column}, 3857),
ST_TileEnvelope(${z}, ${x}, ${y})
) as geom
${columns ? `, ${columns}` : ''}
${id_column ? `, ${id_column}` : ''}
FROM
${table},
(SELECT ST_SRID(${geom_column}) AS srid FROM ${table} WHERE ${geom_column} IS NOT NULL LIMIT 1) a
WHERE
ST_Intersects(
${geom_column},
ST_Transform(
ST_TileEnvelope(${z}, ${x}, ${y}),
srid
)
)
${filter ? ` AND ${filter}` : ''}
)
SELECT ST_AsMVT(mvtgeom.*, '${table}', 4096, 'geom' ${id_column ? `, '${id_column}'` : ''
}) AS mvt from mvtgeom;
`
}
it('should execute the correct query with default parameters', async () => {
client.query.mockResolvedValue({ rows: [] });
const table = 'table_name';
const x = 0, y = 0, z = 0;
const columns = '*'; // Default or adjust as needed
const id_column = 'id'; // Default or adjust as needed
const geom_column = 'geom'; // Default or adjust as needed
const filter = ''; // Default or adjust as needed
await postgis.mvt(table, x, y, z, { columns, id_column });
// Define the expected SQL query with normalized whitespace
const expectedQuery = queryFun(table, x, y, z, columns, id_column, geom_column, filter).replace(/\s+/g, ' ').trim(); // Normalize to single line
// Normalize the received query to single line for comparison
const receivedQuery = client.query.mock.calls[0][0].replace(/\s+/g, ' ').trim();
// Expect the received query to match the expected pattern
expect(receivedQuery).toMatch(expectedQuery);
});
it('should execute the query without parameters', async () => {
client.query.mockResolvedValue({ rows: [] });
const table = 'table_name';
const x = 0, y = 0, z = 0;
await postgis.mvt(table, x, y, z);
// Define the expected SQL query with normalized whitespace
const expectedQuery = queryFun(table, x, y, z).replace(/\s+/g, ' ').trim(); // Normalize to single line
// Normalize the received query to single line for comparison
const receivedQuery = client.query.mock.calls[0][0].replace(/\s+/g, ' ').trim();
// Expect the received query to match the expected pattern
expect(receivedQuery).toMatch(expectedQuery);
});
it('should execute the query with filter', async () => {
client.query.mockResolvedValue({ rows: [] });
const table = 'table_name';
const x = 0, y = 0, z = 0;
const columns = null; // Default or adjust as needed
const id_column = null; // Default or adjust as needed
const geom_column = 'geom'; // Default or adjust as needed
const filter = 'column_name = "value"'; // Default or adjust as needed
await postgis.mvt(table, x, y, z, { filter });
// Define the expected SQL query with normalized whitespace
const expectedQuery = queryFun(table, x, y, z, columns, id_column, geom_column, filter).replace(/\s+/g, ' ').trim(); // Normalize to single line
// Normalize the received query to single line for comparison
const receivedQuery = client.query.mock.calls[0][0].replace(/\s+/g, ' ').trim();
// Expect the received query to match the expected pattern
expect(receivedQuery).toMatch(expectedQuery);
});
});
describe('nearest', () => {
const queryFun = (table, point, columns = '*', geom_column = 'geom', filter, limit = 10) => {
const [x, y, srid] = point.match(/^((-?\d+\.?\d+)(,-?\d+\.?\d+)(,[0-9]{4}))/)[0].split(',')
return `
SELECT
${columns},
ST_Distance(
ST_Transform(
st_setsrid( st_makepoint(${x}, ${y}), ${srid} ),
(SELECT ST_SRID(${geom_column}) FROM ${table} LIMIT 1)
),
${geom_column}
) as distance
FROM
${table}
${filter ? `WHERE ${filter}` : ''}
ORDER BY
${geom_column} <-> ST_Transform(
st_setsrid( st_makepoint(${x}, ${y}), ${srid} ),
(SELECT ST_SRID(${geom_column}) FROM ${table} LIMIT 1)
)
LIMIT ${limit}
`
}
it('should execute the correct query with default parameters', async () => {
client.query.mockResolvedValue({ rows: [] });
const table = 'table_name';
const point = '73.70534,14.94202,4326';
const columns = '*'; // Default or adjust as needed
const geom_column = 'geom'; // Default or adjust as needed
const filter = ''; // Adjust as needed
const limit = 10; // Default or adjust as needed
await postgis.nearest(table, point, { columns, geom_column, filter, limit });
// Define the expected SQL query with normalized whitespace
const expectedQuery = queryFun(table, point, columns, geom_column, filter, limit).replace(/\s+/g, ' ').trim(); // Normalize to single line
// Normalize the received query to single line for comparison
const receivedQuery = client.query.mock.calls[0][0].replace(/\s+/g, ' ').trim();
// Expect the received query to match the expected pattern
expect(receivedQuery).toMatch(expectedQuery);
});
it('should execute the query without parameters', async () => {
client.query.mockResolvedValue({ rows: [] });
const table = 'table_name';
const point = '73.70534,14.94202,4326';
await postgis.nearest(table, point);
// Define the expected SQL query with normalized whitespace
const expectedQuery = queryFun(table, point).replace(/\s+/g, ' ').trim(); // Normalize to single line
// Normalize the received query to single line for comparison
const receivedQuery = client.query.mock.calls[0][0].replace(/\s+/g, ' ').trim();
// Expect the received query to match the expected pattern
expect(receivedQuery).toMatch(expectedQuery);
});
it('should execute the query with filter parameters', async () => {
client.query.mockResolvedValue({ rows: [] });
const table = 'table_name';
const point = '73.70534,14.94202,4326';
const columns = '*'; // Default or adjust as needed
const geom_column = 'geom'; // Default or adjust as needed
const filter = 'column_name = "value"'; // Adjust as needed
const limit = 10; // Default or adjust as needed
await postgis.nearest(table, point, { columns, geom_column, filter, limit });
const expectedQuery = queryFun(table, point, columns, geom_column, filter, limit).replace(/\s+/g, ' ').trim(); // Normalize to single line
// Normalize the received query to single line for comparison
const receivedQuery = client.query.mock.calls[0][0].replace(/\s+/g, ' ').trim();
// Expect the received query to match the expected pattern
expect(receivedQuery).toMatch(expectedQuery);
});
});
describe('transform_point', () => {
const queryFun = (point, srid) => {
const [x, y, srid1] = point.match(/^(-?\d+\.?\d+),(-?\d+\.?\d+),([0-9]{4})$/).slice(1);
return ` SELECT
ST_X(
ST_Transform(
ST_SetSRID(
ST_MakePoint(${x}, ${y}),
${srid1}
),
${srid}
)
) as x,
ST_Y(
ST_Transform(
ST_SetSRID(
ST_MakePoint(${x}, ${y}),
${srid1}
),
${srid}
)
) as y`
}
it('should execute the correct query with default parameters', async () => {
client.query.mockResolvedValue({ rows: [] });
const point = '73.70534,14.94202,4326';
const srid = '3857'; // Set the SRID you want to transform to
await postgis.transform_point(point, { srid: srid });
// Define the expected SQL query with normalized whitespace
const expectedQuery = queryFun(point, srid).replace(/\s+/g, ' ').trim(); // Normalize to single line
// Normalize the received query to single line for comparison
const receivedQuery = client.query.mock.calls[0][0].replace(/\s+/g, ' ').trim();
// Expect the received query to match the expected pattern
expect(receivedQuery).toMatch(expectedQuery);
});
it('should execute the query without parameters', async () => {
client.query.mockResolvedValue({ rows: [] });
const point = '73.70534,14.94202,4326';
const srid = '4326'; // Set the SRID you want to transform to
await postgis.transform_point(point);
const expectedQuery = queryFun(point, srid).replace(/\s+/g, ' ').trim(); // Normalize to single line
// Normalize the received query to single line for comparison
const receivedQuery = client.query.mock.calls[0][0].replace(/\s+/g, ' ').trim();
// Expect the received query to match the expected pattern
expect(receivedQuery).toMatch(expectedQuery);
});
it('should throw an error for invalid point format', async () => {
const invalidPoint = 'invalid,point,format';
await expect(postgis.transform_point(invalidPoint)).rejects.toThrow('Invalid point format');
});
it('should throw an error when query execution fails', async () => {
const errorMessage = 'Query failed';
client.query.mockRejectedValue(new Error(errorMessage));
const point = '73.70534,14.94202,4326';
const srid = 3857;
await expect(postgis.transform_point(point, { srid }))
.rejects
.toThrow(`Query execution failed: ${errorMessage}`);
});
});
describe('_executeQuery', () => {
it('should throw an error when query execution fails', async () => {
const errorMessage = 'Query failed';
client.query.mockRejectedValueOnce(new Error(errorMessage)); // Use mockRejectedValueOnce to ensure it's used once
await expect(postgis._executeQuery('SELECT * FROM some_table'))
.rejects
.toThrow(`Query execution failed: ${errorMessage}`);
});
});
});