/** * * Requires the database and tables setup in config/environments/test.js to exist * Ensure the user is present in the pgbouncer auth file too * TODO: Add OAuth tests. * * To run this test, ensure that cartodb_test_user_1_db metadata exists * in Redis for the vizzuality.cartodb.com domain * * SELECT 5 * HSET rails:users:vizzuality id 1 * HSET rails:users:vizzuality database_name cartodb_test_user_1_db * */ require('../helper'); require('../support/assert'); var app = require(global.settings.app_root + '/app/controllers/app')() , assert = require('assert') , querystring = require('querystring') , _ = require('underscore') , zipfile = require('zipfile') , fs = require('fs') , libxmljs = require('libxmljs') , Step = require('step') ; suite('app.test', function() { var expected_cache_control = 'no-cache,max-age=31536000,must-revalidate,public'; var expected_rw_cache_control = 'no-cache,max-age=0,must-revalidate,public'; var expected_cache_control_persist = 'public,max-age=31536000'; test('GET /api/v1/version', function(done){ assert.response(app, { url: '/api/v1/version', method: 'GET' },{}, function(res) { assert.equal(res.statusCode, 200); var parsed = JSON.parse(res.body); var sqlapi_version = require(__dirname + '/../../package.json').version; assert.ok(parsed.hasOwnProperty('cartodb_sql_api'), "No 'cartodb_sql_api' version in " + parsed); assert.equal(parsed.cartodb_sql_api, sqlapi_version); done(); }); }); test('GET /api/v1/sql', function(done){ assert.response(app, { url: '/api/v1/sql', method: 'GET' },{ status: 400 }, function(res) { assert.deepEqual(res.headers['content-type'], 'application/json; charset=utf-8'); assert.deepEqual(res.headers['content-disposition'], 'inline'); assert.deepEqual(JSON.parse(res.body), {"error":["You must indicate a sql query"]}); done(); }); }); // Test base_url setting test('GET /api/whatever/sql', function(done){ assert.response(app, { url: '/api/whatever/sql?q=SELECT%201', headers: {host: 'vizzuality.cartodb.com'}, method: 'GET' },{ }, function(res) { assert.equal(res.statusCode, 200, res.body); done(); }); }); // Test CORS headers with GET test('GET /api/whatever/sql', function(done){ assert.response(app, { url: '/api/whatever/sql?q=SELECT%201', headers: {host: 'vizzuality.cartodb.com'}, method: 'GET' },{ }, function(res) { assert.equal(res.statusCode, 200, res.body); assert.equal(res.headers['access-control-allow-headers'], 'X-Requested-With, X-Prototype-Version, X-CSRF-Token'); assert.equal(res.headers['access-control-allow-origin'], '*'); done(); }); }); // Test that OPTIONS does not run queries test('OPTIONS /api/x/sql', function(done){ assert.response(app, { url: '/api/x/sql?q=syntax%20error', headers: {host: 'vizzuality.cartodb.com'}, method: 'OPTIONS' },{}, function(res) { assert.equal(res.statusCode, 200, res.body); assert.equal(res.body, ''); assert.equal(res.headers['access-control-allow-headers'], 'X-Requested-With, X-Prototype-Version, X-CSRF-Token'); assert.equal(res.headers['access-control-allow-origin'], '*'); done(); }); }); test('GET /api/v1/sql with SQL parameter on SELECT only. No oAuth included ', function(done){ assert.response(app, { url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4&database=cartodb_test_user_1_db', headers: {host: 'vizzuality.cartodb.com'}, method: 'GET' },{ }, function(res) { assert.equal(res.statusCode, 200, res.body); // Check cache headers assert.equal(res.headers['x-cache-channel'], 'cartodb_test_user_1_db:public.untitle_table_4'); assert.equal(res.headers['cache-control'], expected_cache_control); done(); }); }); test('cache_policy=persist', function(done){ assert.response(app, { url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4&database=cartodb_test_user_1_db&cache_policy=persist', headers: {host: 'vizzuality.cartodb.com'}, method: 'GET' },{ }, function(res) { assert.equal(res.statusCode, 200, res.body); // Check cache headers assert.ok(res.headers.hasOwnProperty('x-cache-channel')); // See https://github.com/CartoDB/CartoDB-SQL-API/issues/105 assert.equal(res.headers['x-cache-channel'], 'cartodb_test_user_1_db:public.untitle_table_4'); assert.equal(res.headers['cache-control'], expected_cache_control_persist); done(); }); }); test('GET /api/v1/sql with SQL parameter on SELECT only. no database param, just id using headers', function(done){ assert.response(app, { url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4', headers: {host: 'vizzuality.cartodb.com'}, method: 'GET' },{ }, function(res) { assert.equal(res.statusCode, 200, res.body); done(); }); }); // See https://github.com/CartoDB/CartoDB-SQL-API/issues/121 test('SELECT from user-specific database', function(done){ var backupDBHost = global.settings.db_host; global.settings.db_host = '6.6.6.6'; assert.response(app, { url: '/api/v1/sql?q=SELECT+2+as+n', headers: {host: 'cartodb250user.cartodb.com'}, method: 'GET' },{}, function(res) { global.settings.db_host = backupDBHost; var err = null; try { assert.equal(res.statusCode, 200, res.statusCode + ": " + res.body); var parsed = JSON.parse(res.body); assert.equal(parsed.rows.length, 1); assert.equal(parsed.rows[0].n, 2); } catch (e) { err = e; } done(err); }); }); // See https://github.com/CartoDB/CartoDB-SQL-API/issues/120 test('SELECT with user-specific password', function(done){ var backupDBUserPass = global.settings.db_user_pass; global.settings.db_user_pass = '<%= user_password %>'; assert.response(app, { url: '/api/v1/sql?q=SELECT+2+as+n&api_key=1234', headers: {host: 'cartodb250user.cartodb.com'}, method: 'GET' },{}, function(res) { global.settings.db_user_pass = backupDBUserPass; var err = null; try { assert.equal(res.statusCode, 200, res.statusCode + ": " + res.body); var parsed = JSON.parse(res.body); assert.equal(parsed.rows.length, 1); assert.equal(parsed.rows[0].n, 2); } catch (e) { err = e; } done(err); }); }); test('GET /api/v1/sql with SQL parameter on SELECT only. no database param, just id using headers. Authenticated.', function(done){ assert.response(app, { url: '/api/v1/sql?q=SELECT%20cartodb_id*2%20FROM%20untitle_table_4&api_key=1234', headers: {host: 'vizzuality.cartodb.com'}, method: 'GET' },{ }, function(res) { assert.equal(res.statusCode, 200, res.body); // Check cache headers assert.equal(res.headers['x-cache-channel'], 'cartodb_test_user_1_db:public.untitle_table_4'); assert.equal(res.headers['cache-control'], expected_cache_control); done(); }); }); // Test for https://github.com/Vizzuality/CartoDB-SQL-API/issues/85 test("paging doesn't break x-cache-channel", function(done){ assert.response(app, { url: '/api/v1/sql?' + querystring.stringify({ // note: select casing intentionally mixed q: 'selECT cartodb_id*3 FROM untitle_table_4', api_key: '1234', rows_per_page: 1, page: 2 }), headers: {host: 'vizzuality.cartodb.com'}, method: 'GET' },{ }, function(res) { assert.equal(res.statusCode, 200, res.body); assert.equal(res.headers['x-cache-channel'], 'cartodb_test_user_1_db:public.untitle_table_4'); var parsed = JSON.parse(res.body); assert.equal(parsed.rows.length, 1); done(); }); }); // Test page and rows_per_page params test("paging", function(done){ var sql = 'SELECT * FROM (VALUES(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(v)'; var pr = [ [2,3], [0,4] ]; // page and rows var methods = [ 'GET', 'POST' ]; var authorized = 0; var testing = 0; var method = 0; var testNext = function() { if ( testing >= pr.length ) { if ( method+1 >= methods.length ) { if ( authorized ) { done(); return; } else { authorized = 1; method = 0; testing = 0; } } else { testing = 0; ++method; } } var prcur = pr[testing++]; console.log("Test " + testing + "/" + pr.length + " method " + methods[method] + " " + ( authorized ? "authenticated" : "" ) ); var page = prcur[0]; var nrows = prcur[1]; var data_obj = { q: sql, rows_per_page: nrows, page: page }; if ( authorized ) data_obj['api_key'] = '1234'; var data = querystring.stringify(data_obj); var req = { url: '/api/v1/sql', headers: {host: 'vizzuality.cartodb.com'} }; if ( methods[method] == 'GET' ) { req.method = 'GET'; req.url += '?' + data; } else { req.method = 'POST'; req.headers['Content-Type'] = 'application/x-www-form-urlencoded'; req.data = data; } assert.response(app, req, {}, function(res) { assert.equal(res.statusCode, 200, res.body); var parsed = JSON.parse(res.body); assert.equal(parsed.rows.length, nrows); for (var i=0; i