500 lines
18 KiB
JavaScript
500 lines
18 KiB
JavaScript
var _ = require('underscore');
|
|
var $ = require('jquery');
|
|
var Backbone = require('backbone');
|
|
var SQL = require('../../../src/api/sql');
|
|
|
|
describe('api/sql', function () {
|
|
var USER = 'cartojs-test';
|
|
var sql;
|
|
var ajax;
|
|
var ajaxParams;
|
|
var TEST_DATA = { test: 'good' };
|
|
var NO_BOUNDS = { 'rows': [
|
|
{ 'maxx': null }
|
|
]};
|
|
var throwError;
|
|
var abort = jasmine.createSpy('abort');
|
|
|
|
beforeEach(function () {
|
|
jasmine.clock().install();
|
|
|
|
ajaxParams = null;
|
|
ajax = function (params) {
|
|
ajaxParams = params;
|
|
_.defer(function () {
|
|
params.complete && params.complete();
|
|
if (!throwError && params.success) params.success(TEST_DATA, 200);
|
|
throwError && params.error && params.error({
|
|
responseText: JSON.stringify({
|
|
error: ['jaja']
|
|
})
|
|
});
|
|
});
|
|
|
|
return {
|
|
abort: abort
|
|
};
|
|
};
|
|
|
|
spyOn($, 'ajax').and.callFake(ajax);
|
|
|
|
sql = new SQL({
|
|
user: USER,
|
|
protocol: 'https'
|
|
});
|
|
});
|
|
|
|
afterEach(function () {
|
|
jasmine.clock().uninstall();
|
|
});
|
|
|
|
it('should compile the url if not completeDomain passed', function () {
|
|
expect(sql._host()).toEqual('https://cartojs-test.carto.com/api/v2/sql');
|
|
});
|
|
|
|
it('should compile the url if completeDomain passed', function () {
|
|
var sqlBis = new SQL({
|
|
user: USER,
|
|
protocol: 'https',
|
|
completeDomain: 'http://troloroloro.com'
|
|
});
|
|
|
|
expect(sqlBis._host()).toEqual('http://troloroloro.com/api/v2/sql');
|
|
});
|
|
|
|
it('should execute a query', function () {
|
|
sql.execute('select * from table');
|
|
expect(ajaxParams.url).toEqual(
|
|
'https://' + USER + '.carto.com/api/v2/sql?q=' + encodeURIComponent('select * from table')
|
|
);
|
|
expect(ajaxParams.type).toEqual('get');
|
|
expect(ajaxParams.dataType).toEqual('json');
|
|
expect(ajaxParams.crossDomain).toEqual(true);
|
|
});
|
|
|
|
it('should be abortable', function () {
|
|
sql = new SQL({
|
|
user: USER,
|
|
protocol: 'https',
|
|
abortable: true
|
|
});
|
|
|
|
sql.execute('select * from table');
|
|
expect(abort).not.toHaveBeenCalled();
|
|
|
|
sql.execute('select * from table limit 10');
|
|
expect(abort).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should parse template', function () {
|
|
sql.execute('select * from {{table}}', {
|
|
table: 'cartojs-test'
|
|
});
|
|
expect(ajaxParams.url).toEqual(
|
|
'https://' + USER + '.carto.com/api/v2/sql?q=' + encodeURIComponent('select * from cartojs-test')
|
|
);
|
|
});
|
|
|
|
it('should execute a long query', function () {
|
|
// Generating a giant query
|
|
var longSQL = [];
|
|
var i = 2000;
|
|
while (--i) longSQL.push('10000');
|
|
var longQuery = 'SELECT * ' + longSQL;
|
|
|
|
// required to have jquery as transport, is checked in the execute method
|
|
sql.execute(longQuery);
|
|
|
|
expect(ajaxParams.url).toEqual(
|
|
'https://' + USER + '.carto.com/api/v2/sql'
|
|
);
|
|
|
|
expect(ajaxParams.data.q).toEqual(longQuery);
|
|
expect(ajaxParams.type).toEqual('post');
|
|
expect(ajaxParams.dataType).toEqual('json');
|
|
expect(ajaxParams.crossDomain).toEqual(true);
|
|
});
|
|
|
|
it('should execute a long query with params', function () {
|
|
var s = new SQL({
|
|
user: 'cartojs-test',
|
|
format: 'geojson',
|
|
protocol: 'http',
|
|
host: 'charlies.com',
|
|
api_key: 'testkey',
|
|
'cartojs-test': 'test'
|
|
});
|
|
|
|
// Generating a giant query
|
|
var longSQL = [];
|
|
var i = 2000;
|
|
while (--i) longSQL.push('10000');
|
|
var longQuery = 'SELECT * ' + longSQL;
|
|
|
|
s.execute(longQuery, null, {
|
|
dp: 0
|
|
});
|
|
|
|
expect(ajaxParams.url.indexOf('http://')).not.toEqual(-1);
|
|
expect(ajaxParams.url.indexOf('cartojs-test.charlies.com')).not.toEqual(-1);
|
|
// Check that we don't have params in the URI
|
|
expect(ajaxParams.url.indexOf('&format=geojson')).toEqual(-1);
|
|
expect(ajaxParams.url.indexOf('&api_key=testkey')).toEqual(-1);
|
|
expect(ajaxParams.url.indexOf('&dp=2')).toEqual(-1);
|
|
expect(ajaxParams.url.indexOf('&cartojs-test')).toEqual(-1);
|
|
// Check that we have the params in the body
|
|
expect(ajaxParams.data.q).toEqual(longQuery);
|
|
expect(ajaxParams.data.format).toEqual('geojson');
|
|
expect(ajaxParams.data.api_key).toEqual('testkey');
|
|
expect(ajaxParams.data.dp).toEqual(0);
|
|
expect(ajaxParams['cartojs-test']).toEqual('test');
|
|
});
|
|
|
|
it('should substitute mapnik tokens', function () {
|
|
sql.execute('select !pixel_width! as w, !pixel_height! as h, !bbox! as b from {{table}}', {
|
|
table: 't'
|
|
});
|
|
|
|
var earthCircumference = 40075017;
|
|
var tileSize = 256;
|
|
var srid = 3857;
|
|
var fullResolution = earthCircumference / tileSize;
|
|
var shift = earthCircumference / 2.0;
|
|
|
|
var pw = fullResolution;
|
|
var ph = pw;
|
|
var bbox = 'ST_MakeEnvelope(' + (-shift) + ',' + (-shift) + ',' +
|
|
shift + ',' + shift + ',' + srid + ')';
|
|
|
|
expect(ajaxParams.url).toEqual(
|
|
'https://' + USER + '.carto.com/api/v2/sql?q=' + encodeURIComponent(
|
|
'select ' + pw + ' as w, ' + ph + ' as h, ' +
|
|
bbox + ' as b from t')
|
|
);
|
|
});
|
|
|
|
it('should call promise', function () {
|
|
var data;
|
|
var dataCallback;
|
|
|
|
sql.execute('select * from bla', function (e, data) {
|
|
dataCallback = data;
|
|
}).done(function (d) {
|
|
data = d;
|
|
});
|
|
|
|
jasmine.clock().tick(100);
|
|
|
|
expect(data).toEqual(TEST_DATA);
|
|
expect(dataCallback).toEqual(TEST_DATA);
|
|
});
|
|
|
|
it('should call promise on error', function () {
|
|
throwError = true;
|
|
var err = false;
|
|
|
|
sql.execute('select * from bla').error(function () {
|
|
err = true;
|
|
});
|
|
|
|
jasmine.clock().tick(10);
|
|
expect(err).toEqual(true);
|
|
});
|
|
|
|
it('should include url params', function () {
|
|
var s = new SQL({
|
|
user: 'cartojs-test',
|
|
format: 'geojson',
|
|
protocol: 'http',
|
|
host: 'charlies.com',
|
|
api_key: 'testkey',
|
|
'cartojs-test': 'test'
|
|
});
|
|
s.execute('select * from cartojs-test', null, {
|
|
dp: 2
|
|
});
|
|
expect(ajaxParams.url.indexOf('http://')).not.toEqual(-1);
|
|
expect(ajaxParams.url.indexOf('cartojs-test.charlies.com')).not.toEqual(-1);
|
|
expect(ajaxParams.url.indexOf('&format=geojson')).not.toEqual(-1);
|
|
expect(ajaxParams.url.indexOf('&api_key=testkey')).not.toEqual(-1);
|
|
expect(ajaxParams.url.indexOf('&dp=2')).not.toEqual(-1);
|
|
expect(ajaxParams.url.indexOf('&cartojs-test')).toEqual(-1);
|
|
});
|
|
|
|
it('should include extra url params', function () {
|
|
var s = new SQL({
|
|
user: 'cartojs-test',
|
|
format: 'geojson',
|
|
protocol: 'http',
|
|
host: 'charlies.com',
|
|
api_key: 'testkey',
|
|
'cartojs-test': 'test',
|
|
extra_params: ['cartojs-test']
|
|
});
|
|
s.execute('select * from cartojs-test', null, {
|
|
dp: 2
|
|
});
|
|
expect(ajaxParams.url.indexOf('http://')).not.toEqual(-1);
|
|
expect(ajaxParams.url.indexOf('cartojs-test.charlies.com')).not.toEqual(-1);
|
|
expect(ajaxParams.url.indexOf('&format=geojson')).not.toEqual(-1);
|
|
expect(ajaxParams.url.indexOf('&api_key=testkey')).not.toEqual(-1);
|
|
expect(ajaxParams.url.indexOf('&dp=2')).not.toEqual(-1);
|
|
expect(ajaxParams.url.indexOf('&cartojs-test=test')).not.toEqual(-1);
|
|
|
|
s.execute('select * from cartojs-test', null, {
|
|
dp: 2,
|
|
'cartojs-test': 'test2'
|
|
});
|
|
expect(ajaxParams.url.indexOf('&cartojs-test=test2')).not.toEqual(-1);
|
|
});
|
|
|
|
it('should use jsonp if browser does not support cors', function () {
|
|
var corsPrev = $.support.cors;
|
|
$.support.cors = false;
|
|
var s = new SQL({ user: 'jaja' });
|
|
expect(s.options.jsonp).toEqual(true);
|
|
s.execute('select * from cartojs-test', null, {
|
|
dp: 2,
|
|
jsonpCallback: 'test_callback',
|
|
cache: false
|
|
});
|
|
expect(ajaxParams.dataType).toEqual('jsonp');
|
|
expect(ajaxParams.crossDomain).toEqual(undefined);
|
|
expect(ajaxParams.jsonp).toEqual(undefined);
|
|
expect(ajaxParams.jsonpCallback).toEqual('test_callback');
|
|
expect(ajaxParams.cache).toEqual(false);
|
|
$.support.cors = corsPrev;
|
|
});
|
|
|
|
describe('.getBounds', function () {
|
|
it('should get bounds for query', function () {
|
|
var sql = 'SELECT ST_XMin(ST_Extent(the_geom)) as minx,' +
|
|
' ST_YMin(ST_Extent(the_geom)) as miny,' +
|
|
' ST_XMax(ST_Extent(the_geom)) as maxx,' +
|
|
' ST_YMax(ST_Extent(the_geom)) as maxy' +
|
|
' from (select * from cartojs-test where id=2) as subq';
|
|
var s = new SQL({ user: 'jaja' });
|
|
s.getBounds('select * from cartojs-test where id={{id}}', {id: 2});
|
|
expect(ajaxParams.url.indexOf(encodeURIComponent(sql))).not.toEqual(-1);
|
|
});
|
|
|
|
it('should get bounds for query with appostrophes', function () {
|
|
var s = new SQL({ user: 'jaja' });
|
|
s.getBounds('select * from country where name={{ name }}', {name: "'Spain'"});
|
|
expect(ajaxParams.url.indexOf('%26amp%3B%2339%3B')).toEqual(-1);
|
|
});
|
|
|
|
it('should resolve promise as error in case there are no bounds', function () {
|
|
var prevTestData = TEST_DATA;
|
|
var actualErrors = null;
|
|
TEST_DATA = NO_BOUNDS;
|
|
throwError = false;
|
|
|
|
var s = new SQL({ user: 'jaja' });
|
|
s.getBounds('SELECT * FROM somewhere')
|
|
.error(function (err) {
|
|
actualErrors = err;
|
|
});
|
|
|
|
jasmine.clock().tick(10);
|
|
expect(actualErrors).not.toBeNull();
|
|
expect(actualErrors.length).toBe(1);
|
|
expect(actualErrors[0]).toEqual('No bounds');
|
|
|
|
// Cleaning
|
|
TEST_DATA = prevTestData;
|
|
});
|
|
|
|
it('should trigger the error callback in case there are no bounds', function () {
|
|
var prevTestData = TEST_DATA;
|
|
var actualErrors = null;
|
|
TEST_DATA = NO_BOUNDS;
|
|
throwError = false;
|
|
|
|
function cb (err) {
|
|
actualErrors = err;
|
|
}
|
|
|
|
var s = new SQL({ user: 'jaja' });
|
|
s.getBounds('SELECT * FROM somewhere', null, null, cb);
|
|
|
|
jasmine.clock().tick(10);
|
|
expect(actualErrors).not.toBeNull();
|
|
expect(actualErrors.length).toBe(1);
|
|
expect(actualErrors[0]).toEqual('No bounds');
|
|
|
|
// Cleaning
|
|
TEST_DATA = prevTestData;
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('api/sql.table', function () {
|
|
var USER = 'cartojs-test';
|
|
var sql;
|
|
|
|
beforeEach(function () {
|
|
sql = new SQL({
|
|
user: USER,
|
|
protocol: 'https'
|
|
});
|
|
});
|
|
|
|
it('sql', function () {
|
|
var s = sql.table('test');
|
|
expect(s.sql()).toEqual('select * from test');
|
|
s.columns(['age', 'jeta']);
|
|
expect(s.sql()).toEqual('select age,jeta from test');
|
|
s.filter('age < 10');
|
|
expect(s.sql()).toEqual('select age,jeta from test where age < 10');
|
|
s.limit(15);
|
|
expect(s.sql()).toEqual('select age,jeta from test where age < 10 limit 15');
|
|
s.order_by('age');
|
|
expect(s.sql()).toEqual('select age,jeta from test where age < 10 limit 15 order by age');
|
|
});
|
|
});
|
|
|
|
describe('api/sql column descriptions', function () {
|
|
var USER = 'manolo';
|
|
var sql;
|
|
|
|
beforeAll(function () {
|
|
this.colDate = new Backbone.Model(JSON.parse('{"name":"object_postedtime","type":"date","geometry_type":"point","bbox":[[-28.92163128242129,-201.09375],[75.84516854027044,196.875]],"analyzed":true,"success":true,"stats":{"type":"date","start_time":"2015-02-19T15:13:16.000Z","end_time":"2015-02-22T04:34:05.000Z","range":220849000,"steps":1024,"null_ratio":0,"column":"object_postedtime"}}'));
|
|
this.colFloat = new Backbone.Model(JSON.parse('{"name":"asdfd","type":"number","geometry_type":"point"}'));
|
|
this.colString = new Backbone.Model(JSON.parse('{"name":"asdfd","type":"string","geometry_type":"point"}'));
|
|
this.colGeom = new Backbone.Model(JSON.parse('{"name":"asdfd","type":"geometry","geometry_type":"point"}'));
|
|
this.colBoolean = new Backbone.Model(JSON.parse('{"name":"asdfd","type":"boolean","geometry_type":"point"}'));
|
|
this.query = 'SELECT * FROM whatevs';
|
|
|
|
sql = new SQL({
|
|
user: USER,
|
|
protocol: 'https'
|
|
});
|
|
sql.execute = function (sql, callback) {
|
|
callback(null, {});
|
|
};
|
|
});
|
|
|
|
it('should deduct correct describe method', function () {
|
|
spyOn(sql, 'describeDate');
|
|
sql.describe(this.query, this.colDate, {type: this.colDate.get('type')}, function () {});
|
|
expect(sql.describeDate).toHaveBeenCalled();
|
|
|
|
spyOn(sql, 'describeFloat');
|
|
sql.describe(this.query, this.colFloat, {type: this.colFloat.get('type')}, function () {});
|
|
expect(sql.describeFloat).toHaveBeenCalled();
|
|
|
|
spyOn(sql, 'describeString');
|
|
sql.describe(this.query, this.colString, {type: this.colString.get('type')}, function () {});
|
|
expect(sql.describeString).toHaveBeenCalled();
|
|
|
|
spyOn(sql, 'describeGeom');
|
|
sql.describe(this.query, this.colGeom, {type: this.colGeom.get('type')}, function () {});
|
|
expect(sql.describeGeom).toHaveBeenCalled();
|
|
|
|
spyOn(sql, 'describeBoolean');
|
|
sql.describe(this.query, this.colBoolean, {type: this.colBoolean.get('type')}, function () {});
|
|
expect(sql.describeBoolean).toHaveBeenCalled();
|
|
});
|
|
|
|
describe('string describer', function () {
|
|
var description;
|
|
beforeAll(function (done) {
|
|
sql.execute = function (sql, callback) {
|
|
var data = JSON.parse('{"rows":[{"uniq":462,"cnt":487,"null_count":1,"null_ratio":0.002053388090349076,"skew":0.043121149897330596,"array_agg":""}],"time":0.01,"fields":{"uniq":{"type":"number"},"cnt":{"type":"number"},"null_count":{"type":"number"},"null_ratio":{"type":"number"},"skew":{"type":"number"},"array_agg":{"type":"unknown(2287)"}},"total_rows":1}');
|
|
callback(null, data);
|
|
};
|
|
var callback = function (e, stuff) {
|
|
description = stuff;
|
|
done();
|
|
};
|
|
sql.describeString(sql, this.colString, callback); // THE COLS DON'T MATCH!!!
|
|
});
|
|
|
|
it('should return correct properties', function () {
|
|
expect(description.hist.constructor).toEqual(Array); // Right now it's an empty array because JSON.parse doesn't like our way of notating histograms
|
|
expect(description.type).toEqual('string');
|
|
expect(typeof description.null_count).toEqual('number');
|
|
expect(typeof description.distinct).toEqual('number');
|
|
expect(typeof description.null_ratio).toEqual('number');
|
|
expect(typeof description.skew).toEqual('number');
|
|
expect(typeof description.weight).toEqual('number');
|
|
});
|
|
});
|
|
|
|
describe('geometry describer', function () {
|
|
var description;
|
|
beforeAll(function (done) {
|
|
sql.execute = function (sql, callback) {
|
|
var data = {'rows': [{'geometry_type': 'ST_Point'}], 'time': 0.035, 'fields': {'geometry_type': {'type': 'string'}}, 'total_rows': 1};
|
|
callback(null, data);
|
|
};
|
|
var callback = function (e, stuff) {
|
|
description = stuff;
|
|
done();
|
|
};
|
|
sql.describeGeom(sql, this.colGeom, callback);
|
|
});
|
|
it('should return correct properties', function () {
|
|
expect(description.type).toEqual('geom');
|
|
expect(['ST_Point', 'ST_Line', 'ST_Polygon'].indexOf(description.geometry_type) > -1).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('number describer', function () {
|
|
var description;
|
|
beforeAll(function (done) {
|
|
sql.execute = function (sql, callback) {
|
|
var data = JSON.parse('{"rows":[{"hist":"{\\"(1,empty,69368)\\",\\"(25,empty,11063)\\"}","min":0,"max":4,"avg":0.3745819397993311,"cnt":89401,"uniq":5,"null_ratio":0,"stddev":0.000009057366328792043,"stddevmean":2.1617223091836313,"dist_type":"U","quantiles":[0,1,2,2,3,4,4],"equalint":[0,0,0,0,0,0,0],"jenks":[0,1,2,3,4],"headtails":[0,1,2,3,4],"cat_hist":"{\\"(1,empty,69368)\\",\\"(25,empty,11063)\\"}"}],"time":1.442,"fields":{"hist":{"type":"unknown(2287)"},"min":{"type":"number"},"max":{"type":"number"},"avg":{"type":"number"},"cnt":{"type":"number"},"uniq":{"type":"number"},"null_ratio":{"type":"number"},"stddev":{"type":"number"},"stddevmean":{"type":"number"},"dist_type":{"type":"string"},"quantiles":{"type":"number[]"},"equalint":{"type":"number[]"},"jenks":{"type":"number[]"},"headtails":{"type":"number[]"},"cat_hist":{"type":"unknown(2287)"}},"total_rows":1}');
|
|
callback(null, data);
|
|
};
|
|
var callback = function (e, stuff) {
|
|
description = stuff;
|
|
done();
|
|
};
|
|
sql.describeFloat(sql, this.colGeom, callback);
|
|
});
|
|
it('should return correct properties', function () {
|
|
expect(description.type).toEqual('number');
|
|
expect(['A', 'U', 'F', 'J'].indexOf(description.dist_type) > -1).toBe(true);
|
|
var i;
|
|
var numTypes = ['avg', 'max', 'min', 'stddevmean', 'weight', 'stddev', 'null_ratio', 'count'];
|
|
for (i = 0; i < numTypes.length; i++) {
|
|
expect(typeof description[numTypes[i]]).toEqual('number');
|
|
}
|
|
var arrayTypes = ['quantiles', 'equalint', 'jenks', 'headtails', 'cat_hist', 'hist'];
|
|
for (i = 0; i < arrayTypes.length; i++) {
|
|
expect(description[arrayTypes[i]].constructor).toEqual(Array);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('boolean describer', function () {
|
|
var description;
|
|
beforeAll(function (done) {
|
|
sql.execute = function (sql, callback) {
|
|
var data = {'rows': [
|
|
{'true_ratio': 0.3377926421404682, 'null_ratio': 0, 'uniq': 2, 'cnt': 89401}
|
|
],
|
|
'time': 0.251,
|
|
'fields': {'true_ratio': {'type': 'number'}, 'null_ratio': {'type': 'number'}, 'uniq': {'type': 'number'}, 'cnt': {'type': 'number'}},
|
|
'total_rows': 1
|
|
};
|
|
callback(null, data);
|
|
};
|
|
var callback = function (e, stuff) {
|
|
description = stuff;
|
|
done();
|
|
};
|
|
sql.describeBoolean(sql, this.colGeom, callback);
|
|
});
|
|
it('should return correct properties', function () {
|
|
expect(description.type).toEqual('boolean');
|
|
expect(typeof description.true_ratio).toEqual('number');
|
|
expect(typeof description.distinct).toEqual('number');
|
|
expect(typeof description.count).toEqual('number');
|
|
expect(typeof description.null_ratio).toEqual('number');
|
|
});
|
|
});
|
|
});
|