diff --git a/config.json.example b/config.json.example
index 3e586b5684bc2749e8eee38f4c4630f10fa62a8b..ea9bd2f3a7c826ed36999cb360b748801d1445ef 100644
--- a/config.json.example
+++ b/config.json.example
@@ -5,7 +5,7 @@
         "ip": "127.0.0.1",
         "debug" : true,
         "monetdb": {
-            "host": "simcaqdb1",
+            "host": "simcaqdb3.c3sl.ufpr.br",
             "port": 50000,
             "dbname": "simcaq_dev",
             "user": "monetdb",
@@ -28,7 +28,7 @@
         "ip": "127.0.0.1",
         "debug" : true,
         "monetdb": {
-            "host": "simcaqdb1",
+            "host": "simcaqdb3.c3sl.ufpr.br",
             "port": 50000,
             "dbname": "simcaq_dev",
             "user": "monetdb",
@@ -51,7 +51,7 @@
         "ip": "127.0.0.1",
         "debug" : false,
         "monetdb": {
-            "host": "simcaqdb1",
+            "host": "simcaqdb3.c3sl.ufpr.br",
             "port": 50000,
             "dbname": "simcaq_dev",
             "user": "monetdb",
diff --git a/gulpfile.babel.js b/gulpfile.babel.js
index e732dc6b630cb7da241d00fdf1ba451fc6124d62..2ff461ffc69e431cf02358587d59f2f80391c95f 100644
--- a/gulpfile.babel.js
+++ b/gulpfile.babel.js
@@ -74,12 +74,17 @@ gulp.task('pre-test', () => {
 
 gulp.task('test', ['pre-test'], () => {
     process.chdir('build');
-    gulp.src('test/test.js', {read: false})
+    gulp.src(['test/**/*.js'], {read: false})
     .pipe(mocha({timeout: 15000}))
     .pipe(istanbul.writeReports())
     .pipe(istanbul.enforceThresholds({
         thresholds: {
-            global: 80
+            global: {
+                statements: 80,
+                branches: 75,
+                lines: 80,
+                functions: 80
+            }
         }
     }))
     .on('error', () => {
diff --git a/package.json b/package.json
index 97d929e45e68049d786b667187fa26c2c2f44b2f..53a11f68ddd60f4021bab4c507e07f1c1f32c683 100644
--- a/package.json
+++ b/package.json
@@ -50,11 +50,11 @@
     "browserify": "^13.1.0",
     "chai-xml": "^0.3.1",
     "docdash": "^0.4.0",
-    "eslint": "^3.3.1",
+    "eslint": "^3.7.1",
     "eslint-config-airbnb": "^13.0.0",
     "eslint-plugin-import": "^2.2.0",
     "eslint-plugin-jsx-a11y": "^2.2.3",
-    "eslint-plugin-react": "^6.1.1",
+    "eslint-plugin-react": "^6.4.0",
     "gulp": "^3.9.1",
     "gulp-babel": "^6.1.2",
     "gulp-cli": "^1.2.2",
diff --git a/src/libs/models/user.js b/src/libs/models/user.js
index 83315b8238e97ce11daf02a8234824ff660f3d7b..554fabf05b5816c0a13ecc825a682dcb141e803c 100644
--- a/src/libs/models/user.js
+++ b/src/libs/models/user.js
@@ -64,7 +64,7 @@ UserSchema.pre('save', function (next) {
             if (err) {
                 return next(err);
             }
-            bcrypt.hash(user.password, salt, function (err, hash) {
+            bcrypt.hash(user.password, salt, null, function (err, hash) {
                 if (err) {
                     return next(err);
                 }
diff --git a/src/libs/routes/api.js b/src/libs/routes/api.js
index 422dd97b9f003658f08c5ae8d91161ed8dac38a3..c916a093a4d70656580998dbe060fd014705ac31 100644
--- a/src/libs/routes/api.js
+++ b/src/libs/routes/api.js
@@ -18,6 +18,8 @@ const city = require('./city');
 
 const school = require('./school');
 
+const location = require('./location');
+
 const simulation = require('./simulation');
 
 const user = require('./user');
@@ -27,7 +29,6 @@ api.get('/', (req, res) => {
 });
 
 // mount API routes
-
 api.use('/user', user);
 api.use('/simulation', simulation);
 api.use('/enrollment', enrollment);
@@ -35,5 +36,6 @@ api.use('/state', cache('15 day'), state);
 api.use('/region', cache('15 day'), region);
 api.use('/city', cache('15 day'), city);
 api.use('/school', cache('15 day'), school);
+api.use('/location', cache('1 day'), location);
 
 module.exports = api;
diff --git a/src/libs/routes/enrollment.js b/src/libs/routes/enrollment.js
index 554eb3836341a7fd1576c41fa5e4979f6ced7985..93f5d75d1d054152c63f15b932dfd6c50a6819aa 100644
--- a/src/libs/routes/enrollment.js
+++ b/src/libs/routes/enrollment.js
@@ -26,6 +26,14 @@ enrollmentApp.get('/year_range', (req, res, next) => {
     next();
 }, query, response('range'));
 
+enrollmentApp.get('/location', (req, res, next) => {
+    req.sql = squel.select()
+        .field('pk_localizacao_id', 'location_id')
+        .field('descricao', 'description')
+        .from('localizacao');
+    next();
+}, query, response('location'));
+
 // Returns all educational levels avaible
 enrollmentApp.get('/education_level', (req, res, next) => {
     req.sql.from('etapa_ensino')
diff --git a/src/libs/routes/location.js b/src/libs/routes/location.js
new file mode 100644
index 0000000000000000000000000000000000000000..f5476f5ea451876aa2e4c6ad0d4fe67a5b214879
--- /dev/null
+++ b/src/libs/routes/location.js
@@ -0,0 +1,1279 @@
+const express = require('express');
+
+const libs = `${process.cwd()}/libs`;
+
+const squel = require('squel');
+
+const log = require(`${libs}/log`)(module);
+
+const query = require(`${libs}/middlewares/query`);
+
+const sqlQuery = require(`${libs}/db/query_exec`);
+
+const response = require(`${libs}/middlewares/response`);
+
+const locationApp = express();
+
+function locationIdToStr(locationId) {
+    let locationStr = 'Total';
+    switch(locationId) {
+        case 1:
+            locationStr = 'Urbana';                         break;
+        case 2:
+            locationStr = 'Rural';                          break;
+        case 3:
+            locationStr = 'Área de assentamento';           break;
+        case 4:
+            locationStr = 'Terra indígena';                 break;
+        case 5:
+            locationStr = 'Área remanescente de quilombos'; break;
+        case 6:
+            locationStr = 'Unidade de uso sustentável';     break;
+
+    }
+    return locationStr;
+}
+
+function schoolYearIdToStr(schoolYearId)
+{
+    let schoolYearStr;
+    switch(schoolYearId) {
+        case 11:
+            schoolYearStr = 'Creche';
+            break;
+        case 21:
+            schoolYearStr = 'Pré-escola';
+            break;
+        case 31:
+            schoolYearStr = '1 Ano';
+            break;
+        case 32:
+            schoolYearStr = '2 Ano - 1 Serie';
+            break;
+        case 33:
+            schoolYearStr = '3 Ano - 2 Serie';
+            break;
+        case 34:
+            schoolYearStr = '4 Ano - 3 Serie';
+            break;
+        case 35:
+            schoolYearStr = '5 Ano - 4 Serie';
+            break;
+        case 41:
+            schoolYearStr = '6 Ano - 5 Serie';
+            break;
+        case 42:
+            schoolYearStr = '7 Ano - 6 Serie';
+            break;
+        case 43:
+            schoolYearStr = '8 Ano - 7 Serie';
+            break;
+        case 44:
+            schoolYearStr = '9 Ano - 8 Serie';
+            break;
+        case 51:
+            schoolYearStr = '1 Ano'; // equivalent to 'EM 1 Série'
+            break;
+        case 52:
+            schoolYearStr = '2 Ano'; // equivalent to 'EM 2 Série'
+            break;
+        case 53:
+            schoolYearStr = '3 Ano'; // equivalent to 'EM 3 Série'
+            break;
+        case 54:
+            schoolYearStr = '4 Ano'; // equivalent to 'EM 4 Série'
+            break;
+        case 61:
+            schoolYearStr = 'EJA AI';
+            break;
+        case 62:
+            schoolYearStr = 'EJA AF';
+            break;
+        case 63:
+            schoolYearStr = 'EJA EM';
+            break;
+        case 64:
+            schoolYearStr = 'EJA semi-presencial';
+            break;
+        case 71:
+            schoolYearStr = 'EP';
+            break;
+        case 81:
+            schoolYearStr = 'Atividades complementares e AEE';
+            break;
+        default:
+            schoolYearStr = 'Não classificado';
+    }
+    return schoolYearStr;
+}
+
+function processResultSet(querySet, querySetLabels = ["result"], singleResult = false) {
+    const resultMap = new Map();
+    let resultIdx = 0;
+    // loop relies on the fact that Promise.all maintains the order of the original iterable
+    for(let result of querySet) {
+        const resultLbl = querySetLabels[resultIdx];
+        resultMap[resultLbl] = [];
+        if (singleResult) {
+            resultMap[resultLbl] = result[0];
+        } else {
+            for(let row of result) {
+                log.debug(row);
+                resultMap[resultLbl].push(row);
+            }
+        }
+        resultIdx++;
+    }
+    log.debug(resultMap);
+    return resultMap;
+}
+
+function dbExecAll(querySet = []) {
+    // Issue all queries concurrently to the database, for every query object in the iterable
+    // NOTE: Array.map() returns a copy of the original array with each object 'mapped'.
+    return querySet.map((qry) => { return sqlQuery(qry.toString()); });
+}
+
+locationApp.get('/sociodemographic', (req, res, next) => {
+    const populationYearQry = squel.select()
+        .field('MAX(ibge_populacao.ano_censo)')
+        .from('ibge_populacao');
+
+    const populationQry = squel.select()
+        .field('\'Brasil\'', 'name')
+        .field('SUM(populacao)', 'population')
+        .field('ibge_populacao.ano_censo', 'census_year')
+        .from('ibge_populacao')
+        .where(`ibge_populacao.ano_censo IN (${populationYearQry.toString()})`)
+        .group('ibge_populacao.ano_censo');
+
+    const pibYearQry = squel.select()
+        .field('MAX(ibge_pib.ano_censo)')
+        .from('ibge_pib');
+
+    const pibQry = squel.select()
+        .field('\'Brasil\'', 'name')
+        .field('AVG(ibge_pib.pib_per_capita)', 'gdp_per_capita')
+        .field('ibge_pib.ano_censo', 'census_year')
+        .from('ibge_pib')
+        .where(`ibge_pib.ano_censo IN (${pibYearQry.toString()})`)
+        .group('ibge_pib.ano_censo');
+
+    const idhYearQry = squel.select()
+        .field('MAX(adh_idh.ano_censo)')
+        .from('adh_idh');
+
+    const idhQry = squel.select()
+        .field('\'Brasil\'', 'name')
+        .field('AVG(idhm)', 'idhm')
+        .field('adh_idh.ano_censo', 'census_year')
+        .from('adh_idh')
+        .where(`adh_idh.ano_censo IN (${idhYearQry.toString()})`)
+        .group('adh_idh.ano_censo');
+
+    const analfabYearQry = squel.select()
+        .field('MAX(adh_analfabetismo.ano_censo)')
+        .from('adh_analfabetismo');
+
+    const analfabQry = squel.select()
+        .field('\'Brasil\'', 'name')
+        .field('AVG(t_analf15m)', 'analfabetism')
+        .field('adh_analfabetismo.ano_censo', 'census_year')
+        .from('adh_analfabetismo')
+        .where(`adh_analfabetismo.ano_censo IN (${analfabYearQry.toString()})`)
+        .group('adh_analfabetismo.ano_censo');
+
+    const giniYearQry = squel.select()
+        .field('MAX(adh_gini.ano_censo)')
+        .from('adh_gini');
+
+    const giniQry = squel.select()
+        .field('\'Brasil\'', 'name')
+        .field('AVG(gini)', 'gini')
+        .field('adh_gini.ano_censo', 'census_year')
+        .from('adh_gini')
+        .where(`adh_gini.ano_censo IN (${giniYearQry.toString()})`)
+        .group('adh_gini.ano_censo');
+
+    // map query objects to their respective response labels
+    const queryLabels = [ "population", "gdp", "idh", "analfab", "gini" ];
+    const querySet = [ populationQry, pibQry, idhQry, analfabQry, giniQry ];
+    // wait until all queries finish or one of them fail
+    Promise.all(dbExecAll(querySet)).then((queryResults) => {
+        req.result = processResultSet(queryResults, queryLabels, true);
+        next();
+    }).catch((error) => {
+        log.error(`[SQL query error] ${error}`);
+        next(error);
+    });
+}, response('location'));
+
+locationApp.get('/sociodemographic/region/:id', (req, res, next) => {
+    const regionId = parseInt(req.params.id, 10);
+
+    const populationYearQry = squel.select()
+        .field('MAX(ibge_populacao.ano_censo)')
+        .from('ibge_populacao');
+
+    const populationQry = squel.select()
+        .field('regiao.nome', 'name')
+        .field('SUM(populacao)', 'population')
+        .field('ano_censo', 'census_year')
+        .from('ibge_populacao')
+        .from('municipio')
+        .from('estado')
+        .from('regiao')
+        .where(`regiao.pk_regiao_id = ${regionId}`)
+        .where(`ibge_populacao.ano_censo IN (${populationYearQry.toString()})`)
+        .where('ibge_populacao.fk_municipio_id = municipio.pk_cod_ibge')
+        .where('municipio.fk_estado_id = estado.pk_estado_id')
+        .where('estado.fk_regiao_id = regiao.pk_regiao_id')
+        .group('regiao.nome')
+        .group('ibge_populacao.ano_censo')
+        .order('regiao.nome');
+
+    const pibYearQry = squel.select()
+        .field('MAX(ibge_pib.ano_censo)')
+        .from('ibge_pib');
+
+    const pibQry = squel.select()
+        .field('regiao.nome', 'name')
+        .field('AVG(ibge_pib.pib_per_capita)', 'gdp_per_capita')
+        .field('ano_censo', 'census_year')
+        .from('ibge_pib')
+        .from('municipio')
+        .from('estado')
+        .from('regiao')
+        .where(`regiao.pk_regiao_id = ${regionId}`)
+        .where(`ibge_pib.ano_censo IN (${pibYearQry.toString()})`)
+        .where('ibge_pib.fk_municipio_id = municipio.pk_cod_ibge')
+        .where('municipio.fk_estado_id = estado.pk_estado_id')
+        .where('estado.fk_regiao_id = regiao.pk_regiao_id')
+        .group('regiao.nome')
+        .group('ibge_pib.ano_censo')
+        .order('regiao.nome');
+
+    const idhYearQry = squel.select()
+        .field('MAX(adh_idh.ano_censo)')
+        .from('adh_idh');
+
+    const idhQry = squel.select()
+        .field('regiao.nome', 'name')
+        .field('AVG(idhm)', 'idhm')
+        .field('ano_censo', 'census_year')
+        .from('adh_idh')
+        .from('municipio')
+        .from('estado')
+        .from('regiao')
+        .where(`regiao.pk_regiao_id = ${regionId}`)
+        .where(`adh_idh.ano_censo IN (${idhYearQry.toString()})`)
+        .where('adh_idh.fk_municipio_id = municipio.pk_cod_ibge')
+        .where('municipio.fk_estado_id = estado.pk_estado_id')
+        .where('estado.fk_regiao_id = regiao.pk_regiao_id')
+        .group('regiao.nome')
+        .group('adh_idh.ano_censo')
+        .order('regiao.nome');
+
+    const analfabYearQry = squel.select()
+        .field('MAX(adh_analfabetismo.ano_censo)')
+        .from('adh_analfabetismo');
+
+    const analfabQry = squel.select()
+        .field('regiao.nome', 'name')
+        .field('AVG(t_analf15m)', 'analfabetism')
+        .field('ano_censo', 'census_year')
+        .from('adh_analfabetismo')
+        .from('municipio')
+        .from('estado')
+        .from('regiao')
+        .where(`regiao.pk_regiao_id = ${regionId}`)
+        .where(`adh_analfabetismo.ano_censo IN (${analfabYearQry.toString()})`)
+        .where('adh_analfabetismo.fk_municipio_id = municipio.pk_cod_ibge')
+        .where('municipio.fk_estado_id = estado.pk_estado_id')
+        .where('estado.fk_regiao_id = regiao.pk_regiao_id')
+        .group('regiao.nome')
+        .group('adh_analfabetismo.ano_censo')
+        .order('regiao.nome');
+
+    const giniYearQry = squel.select()
+        .field('MAX(adh_gini.ano_censo)')
+        .from('adh_gini');
+
+    const giniQry = squel.select()
+        .field('regiao.nome', 'name')
+        .field('AVG(gini)', 'gini')
+        .field('ano_censo', 'census_year')
+        .from('adh_gini')
+        .from('municipio')
+        .from('estado')
+        .from('regiao')
+        .where(`regiao.pk_regiao_id = ${regionId}`)
+        .where(`adh_gini.ano_censo IN (${giniYearQry.toString()})`)
+        .where('adh_gini.fk_municipio_id = municipio.pk_cod_ibge')
+        .where('municipio.fk_estado_id = estado.pk_estado_id')
+        .where('estado.fk_regiao_id = regiao.pk_regiao_id')
+        .group('regiao.nome')
+        .group('adh_gini.ano_censo')
+        .order('regiao.nome');
+    // map query objects to their respective response labels
+    const queryLabels = [ "population", "gdp", "idh", "analfab", "gini" ];
+    const querySet = [ populationQry, pibQry, idhQry, analfabQry, giniQry ];
+    // wait until all queries finish or one of them fail
+    Promise.all(dbExecAll(querySet)).then((queryResults) => {
+        req.result = processResultSet(queryResults, queryLabels, true);
+        next();
+    }).catch((error) => {
+        log.error(`[SQL query error] ${error}`);
+        next(error);
+    });
+}, response('location'));
+
+locationApp.get('/sociodemographic/state/:id', (req, res, next) => {
+    const stateId = parseInt(req.params.id, 10);
+
+    const populationYearQry = squel.select()
+        .field('MAX(ibge_populacao.ano_censo)')
+        .from('ibge_populacao');
+    // load all cities by state and compute the sociodemographic and educational information
+    const populationQry = squel.select()
+        .field('estado.nome', 'name')
+        .field('SUM(populacao)', 'population')
+        .field('ibge_populacao.ano_censo', 'census_year')
+        .from('ibge_populacao')
+        .from('municipio')
+        .from('estado')
+        .where(`estado.pk_estado_id = ${stateId}`)
+        .where(`ibge_populacao.ano_censo IN (${populationYearQry.toString()})`)
+        .where('ibge_populacao.fk_municipio_id = municipio.pk_cod_ibge')
+        .where('municipio.fk_estado_id = estado.pk_estado_id')
+        .group('estado.nome')
+        .group('ibge_populacao.ano_censo')
+        .order('estado.nome');
+
+    const pibYearQry = squel.select()
+        .field('MAX(ibge_pib.ano_censo)')
+        .from('ibge_pib');
+
+    const pibQry = squel.select()
+        .field('estado.nome', 'name')
+        .field('AVG(ibge_pib.pib_per_capita)', 'gdp_per_capita')
+        .field('ibge_pib.ano_censo', 'census_year')
+        .from('ibge_pib')
+        .from('municipio')
+        .from('estado')
+        .where(`estado.pk_estado_id = ${stateId}`)
+        .where(`ibge_pib.ano_censo IN (${pibYearQry.toString()})`)
+        .where('ibge_pib.fk_municipio_id = municipio.pk_cod_ibge')
+        .where('municipio.fk_estado_id = estado.pk_estado_id')
+        .group('estado.nome')
+        .group('ibge_pib.ano_censo')
+        .order('estado.nome');
+
+    const idhYearQry = squel.select()
+        .field('MAX(adh_idh.ano_censo)')
+        .from('adh_idh');
+
+    const idhQry = squel.select()
+        .field('estado.nome', 'name')
+        .field('AVG(idhm)', 'idhm')
+        .field('adh_idh.ano_censo', 'census_year')
+        .from('adh_idh')
+        .from('municipio')
+        .from('estado')
+        .where(`estado.pk_estado_id = ${stateId}`)
+        .where(`adh_idh.ano_censo IN (${idhYearQry.toString()})`)
+        .where('adh_idh.fk_municipio_id = municipio.pk_cod_ibge')
+        .where('municipio.fk_estado_id = estado.pk_estado_id')
+        .group('estado.nome')
+        .group('adh_idh.ano_censo')
+        .order('estado.nome');
+
+    const analfabYearQry = squel.select()
+        .field('MAX(adh_analfabetismo.ano_censo)')
+        .from('adh_analfabetismo');
+
+    const analfabQry = squel.select()
+        .field('estado.nome', 'name')
+        .field('AVG(t_analf15m)', 'analfabetism')
+        .field('adh_analfabetismo.ano_censo', 'census_year')
+        .from('adh_analfabetismo')
+        .from('municipio')
+        .from('estado')
+        .where(`estado.pk_estado_id = ${stateId}`)
+        .where(`adh_analfabetismo.ano_censo IN (${analfabYearQry.toString()})`)
+        .where('adh_analfabetismo.fk_municipio_id = municipio.pk_cod_ibge')
+        .where('municipio.fk_estado_id = estado.pk_estado_id')
+        .group('estado.nome')
+        .group('adh_analfabetismo.ano_censo')
+        .order('estado.nome');
+
+    const giniYearQry = squel.select()
+        .field('MAX(adh_gini.ano_censo)')
+        .from('adh_gini');
+
+    const giniQry = squel.select()
+        .field('estado.nome', 'name')
+        .field('AVG(gini)', 'gini')
+        .field('adh_gini.ano_censo', 'census_year')
+        .from('adh_gini')
+        .from('municipio')
+        .from('estado')
+        .where(`estado.pk_estado_id = ${stateId}`)
+        .where(`adh_gini.ano_censo IN (${giniYearQry.toString()})`)
+        .where('adh_gini.fk_municipio_id = municipio.pk_cod_ibge')
+        .where('municipio.fk_estado_id = estado.pk_estado_id')
+        .group('estado.nome')
+        .group('adh_gini.ano_censo')
+        .order('estado.nome');
+    // map query objects to their respective response labels
+    const queryLabels = [ "population", "gdp", "idh", "analfab", "gini" ];
+    const querySet = [ populationQry, pibQry, idhQry, analfabQry, giniQry ];
+    // wait until all queries finish or one of them fail
+    Promise.all(dbExecAll(querySet)).then((queryResults) => {
+        req.result = processResultSet(queryResults, queryLabels, true);
+        next();
+    }).catch((error) => {
+        log.error(`[SQL query error] ${error}`);
+        next(error);
+    });
+}, response('location'));
+
+locationApp.get('/sociodemographic/city/:id', (req, res, next) => {
+    const cityId = parseInt(req.params.id, 10);
+
+    const populationYearQry = squel.select()
+        .field('MAX(ibge_populacao.ano_censo)')
+        .from('ibge_populacao');
+    // load all cities by state and compute the sociodemographic and educational information
+    const populationQry = squel.select()
+        .field('municipio.nome', 'name')
+        .field('SUM(populacao)', 'population')
+        .field('ibge_populacao.ano_censo', 'census_year')
+        .from('ibge_populacao')
+        .from('municipio')
+        .where(`municipio.pk_cod_ibge = ${cityId}`)
+        .where(`ibge_populacao.ano_censo IN (${populationYearQry.toString()})`)
+        .where('ibge_populacao.fk_municipio_id = municipio.pk_cod_ibge')
+        .group('municipio.nome')
+        .group('ibge_populacao.ano_censo')
+        .order('municipio.nome');
+
+    const pibYearQry = squel.select()
+        .field('MAX(ibge_pib.ano_censo)')
+        .from('ibge_pib');
+
+    const pibQry = squel.select()
+        .field('municipio.nome', 'name')
+        .field('AVG(ibge_pib.pib_per_capita)', 'gdp_per_capita')
+        .field('ibge_pib.ano_censo', 'census_year')
+        .from('ibge_pib')
+        .from('municipio')
+        .where(`municipio.pk_cod_ibge = ${cityId}`)
+        .where(`ibge_pib.ano_censo IN (${pibYearQry.toString()})`)
+        .where('ibge_pib.fk_municipio_id = municipio.pk_cod_ibge')
+        .group('municipio.nome')
+        .group('ibge_pib.ano_censo')
+        .order('municipio.nome');
+
+    const idhYearQry = squel.select()
+        .field('MAX(adh_idh.ano_censo)')
+        .from('adh_idh');
+
+    const idhQry = squel.select()
+        .field('municipio.nome', 'name')
+        .field('AVG(idhm)', 'idhm')
+        .field('adh_idh.ano_censo', 'census_year')
+        .from('adh_idh')
+        .from('municipio')
+        .where(`municipio.pk_cod_ibge = ${cityId}`)
+        .where(`adh_idh.ano_censo IN (${idhYearQry.toString()})`)
+        .where('adh_idh.fk_municipio_id = municipio.pk_cod_ibge')
+        .group('municipio.nome')
+        .group('adh_idh.ano_censo')
+        .order('municipio.nome');
+
+    const analfabYearQry = squel.select()
+        .field('MAX(adh_analfabetismo.ano_censo)')
+        .from('adh_analfabetismo');
+
+    const analfabQry = squel.select()
+        .field('municipio.nome', 'name')
+        .field('AVG(t_analf15m)', 'analfabetism')
+        .field('adh_analfabetismo.ano_censo', 'census_year')
+        .from('adh_analfabetismo')
+        .from('municipio')
+        .where(`municipio.pk_cod_ibge = ${cityId}`)
+        .where(`adh_analfabetismo.ano_censo IN (${analfabYearQry.toString()})`)
+        .where('adh_analfabetismo.fk_municipio_id = municipio.pk_cod_ibge')
+        .group('municipio.nome')
+        .group('adh_analfabetismo.ano_censo')
+        .order('municipio.nome');
+
+    const giniYearQry = squel.select()
+        .field('MAX(adh_gini.ano_censo)')
+        .from('adh_gini');
+
+    const giniQry = squel.select()
+        .field('municipio.nome', 'name')
+        .field('AVG(gini)', 'gini')
+        .field('adh_gini.ano_censo', 'census_year')
+        .from('adh_gini')
+        .from('municipio')
+        .where(`municipio.pk_cod_ibge = ${cityId}`)
+        .where(`adh_gini.ano_censo IN (${giniYearQry.toString()})`)
+        .where('adh_gini.fk_municipio_id = municipio.pk_cod_ibge')
+        .group('municipio.nome')
+        .group('adh_gini.ano_censo')
+        .order('municipio.nome');
+    // map query objects to their respective response labels
+    const queryLabels = [ "population", "gdp", "idh", "analfab", "gini" ];
+    const querySet = [ populationQry, pibQry, idhQry, analfabQry, giniQry ];
+    // wait until all queries finish or one of them fail
+    Promise.all(dbExecAll(querySet)).then((queryResults) => {
+        req.result = processResultSet(queryResults, queryLabels, true);
+        next();
+    }).catch((error) => {
+        log.error(`[SQL query error] ${error}`);
+        next(error);
+    });
+}, response('location'));
+
+locationApp.get('/educational', (req, res, next) => {
+    const censusYearQry = squel.select()
+        .field('MAX(escola.ano_censo)', 'ano_censo')
+        .from('escola')
+        .toString();
+
+    const totalSchoolsQry = squel.select()
+        .field('\'Brasil\'', 'name')
+        .field('0', 'location')
+        .field('COUNT(DISTINCT(escola.cod_entidade))', 'total')
+        .field('escola.ano_censo', 'census_year')
+        .from('escola')
+        .where(`escola.ano_censo IN (${censusYearQry})`)
+        .where('escola.id_tipo_turma = 0')
+        .group('escola.ano_censo');
+
+    const schoolsPerLocationQry = squel.select()
+        .field('\'Brasil\'', 'name')
+        .field('escola.tipo_localizacao', 'location')
+        .field('COUNT(DISTINCT(escola.cod_entidade))', 'total')
+        .field('escola.ano_censo', 'census_year')
+        .from('escola')
+        .where(`escola.ano_censo IN (${censusYearQry})`)
+        .where('escola.id_tipo_turma = 0')
+        .group('escola.tipo_localizacao')
+        .group('escola.ano_censo')
+        .order('escola.tipo_localizacao');
+
+    const schoolClassYearQry = squel.select()
+        .field('MAX(turma.ano_censo)')
+        .from('turma')
+        .toString();
+
+    const enrollmentsQry = squel.select()
+        .field('\'Brasil\'', 'name')
+        .field('COALESCE(SUM(turma.num_matriculas), 0)', 'total')
+        .field('turma.ano_censo', 'census_year')
+        .from('turma')
+        .where(`turma.ano_censo IN (${schoolClassYearQry})`)
+        .where('turma.fk_tipo_turma_id <= 3')
+        .group('turma.ano_censo');
+
+    const enrollmentsPerAdmDepQry = squel.select()
+        .field('\'Brasil\'', 'name')
+        .field('COALESCE(SUM(turma.num_matriculas), 0)', 'total')
+        .field('turma.ano_censo', 'census_year')
+        .field('dependencia_adm.nome', 'adm_dependency')
+        .from('turma')
+        .from('dependencia_adm')
+        .where('turma.fk_dependencia_adm_id = dependencia_adm.pk_dependencia_adm_id')
+        .where('turma.fk_tipo_turma_id <= 3')
+        .where(`turma.ano_censo IN (${schoolClassYearQry})`)
+        .group('turma.ano_censo')
+        .group('dependencia_adm.nome')
+        .order('dependencia_adm.nome');
+
+    const enrollmentsPerSchoolLevelQry = squel.select()
+        .field('\'Brasil\'', 'name')
+        .field('COALESCE(SUM(turma.num_matriculas), 0)', 'total')
+        .field('turma.ano_censo', 'census_year')
+        .field('etapa_ensino.desc_etapa', 'school_level')
+        .from('turma')
+        .from('etapa_ensino')
+        .where('turma.fk_etapa_ensino_id = etapa_ensino.pk_etapa_ensino_id')
+        .where('turma.fk_tipo_turma_id <= 3')
+        .where(`turma.ano_censo IN (${schoolClassYearQry})`)
+        .group('turma.ano_censo')
+        .group('etapa_ensino.desc_etapa')
+        .group('etapa_ensino.pk_etapa_ensino_id')
+        .order('etapa_ensino.pk_etapa_ensino_id');
+
+    const queryLabels = [ "school", "school_per_location", "enrollment", "enrollment_per_adm_dep",
+        "enrollment_per_school_level" ];
+    const querySet = [ totalSchoolsQry, schoolsPerLocationQry, enrollmentsQry,
+        enrollmentsPerAdmDepQry, enrollmentsPerSchoolLevelQry];
+    // wait until all queries finish or one of them fail
+    Promise.all(dbExecAll(querySet)).then((queryResults) => {
+        req.result = processResultSet(queryResults, queryLabels);
+        for(let label in req.result) {
+            for(let row of req.result[label]) {
+                if (row.hasOwnProperty('location')) {
+                    row.location = locationIdToStr(row.location);
+                }
+            }
+        }
+        next();
+    }).catch((error) => {
+        log.error(`[SQL query error] ${error}`);
+        next(error);
+    });
+}, response('location'));
+
+locationApp.get('/educational/region/:id', (req, res, next) => {
+    const regionId = parseInt(req.params.id, 10);
+
+    const censusYearQry = squel.select()
+        .field('MAX(escola.ano_censo)', 'ano_censo')
+        .from('escola')
+        .toString();
+
+    const totalSchoolsQry = squel.select()
+        .field('regiao.nome', 'name')
+        .field('0', 'location')
+        .field('COUNT(DISTINCT(escola.cod_entidade))', 'total')
+        .field('escola.ano_censo', 'census_year')
+        .from('escola')
+        .from('estado')
+        .from('regiao')
+        .where('escola.fk_estado_id = estado.pk_estado_id')
+        .where('estado.fk_regiao_id = regiao.pk_regiao_id')
+        .where(`regiao.pk_regiao_id = ${regionId}`)
+        .where(`escola.ano_censo IN (${censusYearQry})`)
+        .where('escola.id_tipo_turma = 0')
+        .group('regiao.nome')
+        .group('escola.ano_censo')
+        .order('regiao.nome');
+
+    const schoolsPerLocationQry = squel.select()
+        .field('regiao.nome', 'name')
+        .field('escola.tipo_localizacao', 'location')
+        .field('COUNT(DISTINCT(escola.cod_entidade))', 'total')
+        .field('escola.ano_censo', 'census_year')
+        .from('escola')
+        .from('estado')
+        .from('regiao')
+        .where('escola.fk_estado_id = estado.pk_estado_id')
+        .where('estado.fk_regiao_id = regiao.pk_regiao_id')
+        .where(`regiao.pk_regiao_id = ${regionId}`)
+        .where(`escola.ano_censo IN (${censusYearQry})`)
+        .where('escola.id_tipo_turma = 0')
+        .group('regiao.nome')
+        .group('escola.tipo_localizacao')
+        .group('escola.ano_censo')
+        .order('regiao.nome')
+        .order('escola.tipo_localizacao');
+
+    const schoolClassYearQry = squel.select()
+        .field('MAX(turma.ano_censo)')
+        .from('turma')
+        .toString();
+
+    const enrollmentsQry = squel.select()
+        .field('regiao.nome', 'name')
+        .field('COALESCE(SUM(turma.num_matriculas), 0)', 'total')
+        .field('turma.ano_censo', 'census_year')
+        .from('turma')
+        .from('regiao')
+        .where('turma.fk_regiao_id = regiao.pk_regiao_id')
+        .where(`turma.fk_regiao_id = ${regionId}`)
+        .where(`turma.ano_censo IN (${schoolClassYearQry})`)
+        .where('turma.fk_tipo_turma_id <= 3')
+        .group('turma.ano_censo')
+        .group('regiao.nome')
+        .order('regiao.nome');
+
+    const enrollmentsPerAdmDepQry = squel.select()
+        .field('regiao.nome', 'name')
+        .field('COALESCE(SUM(turma.num_matriculas), 0)', 'total')
+        .field('turma.ano_censo', 'census_year')
+        .field('dependencia_adm.nome', 'adm_dependency')
+        .from('turma')
+        .from('dependencia_adm')
+        .from('regiao')
+        .where('turma.fk_regiao_id = regiao.pk_regiao_id')
+        .where(`turma.fk_regiao_id = ${regionId}`)
+        .where('turma.fk_dependencia_adm_id = dependencia_adm.pk_dependencia_adm_id')
+        .where('turma.fk_tipo_turma_id <= 3')
+        .where(`turma.ano_censo IN (${schoolClassYearQry})`)
+        .group('turma.ano_censo')
+        .group('dependencia_adm.nome')
+        .group('regiao.nome')
+        .order('regiao.nome')
+        .order('dependencia_adm.nome');
+
+    const enrollmentsPerSchoolLevelQry = squel.select()
+        .field('regiao.nome', 'name')
+        .field('COALESCE(SUM(turma.num_matriculas), 0)', 'total')
+        .field('turma.ano_censo', 'census_year')
+        .field('etapa_ensino.desc_etapa', 'school_level')
+        .from('turma')
+        .from('etapa_ensino')
+        .from('regiao')
+        .where('turma.fk_regiao_id = regiao.pk_regiao_id')
+        .where(`turma.fk_regiao_id = ${regionId}`)
+        .where('turma.fk_etapa_ensino_id = etapa_ensino.pk_etapa_ensino_id')
+        .where('turma.fk_tipo_turma_id <= 3')
+        .where(`turma.ano_censo IN (${schoolClassYearQry})`)
+        .group('turma.ano_censo')
+        .group('etapa_ensino.desc_etapa')
+        .group('regiao.nome')
+        .group('etapa_ensino.pk_etapa_ensino_id')
+        .order('regiao.nome')
+        .order('etapa_ensino.pk_etapa_ensino_id');
+
+    const queryLabels = [ "school", "school_per_location", "enrollment", "enrollment_per_adm_dep",
+        "enrollment_per_school_level" ];
+    const querySet = [ totalSchoolsQry, schoolsPerLocationQry, enrollmentsQry,
+        enrollmentsPerAdmDepQry, enrollmentsPerSchoolLevelQry];
+    // wait until all queries finish or one of them fail
+    Promise.all(dbExecAll(querySet)).then((queryResults) => {
+        req.result = processResultSet(queryResults, queryLabels);
+        for(let label in req.result) {
+            for(let row of req.result[label]) {
+                if (row.hasOwnProperty('location')) {
+                    row.location = locationIdToStr(row.location);
+                }
+            }
+        }
+        next();
+    }).catch((error) => {
+        log.error(`[SQL query error] ${error}`);
+        next(error);
+    });
+}, response('location'));
+
+locationApp.get('/educational/state/:id', (req, res, next) => {
+    const stateId = parseInt(req.params.id, 10);
+
+    const censusYearQry = squel.select()
+        .field('MAX(escola.ano_censo)', 'ano_censo')
+        .from('escola')
+        .toString();
+
+    const totalSchoolsQry = squel.select()
+        .field('estado.nome', 'name')
+        .field('0', 'location')
+        .field('COUNT(DISTINCT(escola.cod_entidade))', 'total')
+        .field('escola.ano_censo', 'census_year')
+        .from('escola')
+        .from('estado')
+        .where('escola.fk_estado_id = estado.pk_estado_id')
+        .where(`escola.fk_estado_id = ${stateId}`)
+        .where(`escola.ano_censo IN (${censusYearQry})`)
+        .where('escola.id_tipo_turma = 0')
+        .group('estado.nome')
+        .group('escola.ano_censo')
+        .order('estado.nome');
+
+    const schoolsPerLocationQry = squel.select()
+        .field('estado.nome', 'name')
+        .field('escola.tipo_localizacao', 'location')
+        .field('COUNT(DISTINCT(escola.cod_entidade))', 'total')
+        .field('escola.ano_censo', 'census_year')
+        .from('escola')
+        .from('estado')
+        .where('escola.fk_estado_id = estado.pk_estado_id')
+        .where(`escola.fk_estado_id = ${stateId}`)
+        .where(`escola.ano_censo IN (${censusYearQry})`)
+        .where('escola.id_tipo_turma = 0')
+        .group('estado.nome')
+        .group('escola.tipo_localizacao')
+        .group('escola.ano_censo')
+        .order('estado.nome')
+        .order('escola.tipo_localizacao');
+
+    const schoolClassYearQry = squel.select()
+        .field('MAX(turma.ano_censo)')
+        .from('turma')
+        .toString();
+
+    const enrollmentsQry = squel.select()
+        .field('estado.nome', 'name')
+        .field('COALESCE(SUM(turma.num_matriculas), 0)', 'total')
+        .field('turma.ano_censo', 'census_year')
+        .from('turma')
+        .from('estado')
+        .where('turma.fk_estado_id = estado.pk_estado_id')
+        .where(`turma.fk_estado_id = ${stateId}`)
+        .where(`turma.ano_censo IN (${schoolClassYearQry})`)
+        .where('turma.fk_tipo_turma_id <= 3')
+        .group('turma.ano_censo')
+        .group('estado.nome')
+        .order('estado.nome');
+
+    const enrollmentsPerAdmDepQry = squel.select()
+        .field('estado.nome', 'name')
+        .field('COALESCE(SUM(turma.num_matriculas), 0)', 'total')
+        .field('turma.ano_censo', 'census_year')
+        .field('dependencia_adm.nome', 'adm_dependency')
+        .from('turma')
+        .from('dependencia_adm')
+        .from('estado')
+        .where('turma.fk_estado_id = estado.pk_estado_id')
+        .where(`turma.fk_estado_id = ${stateId}`)
+        .where('turma.fk_dependencia_adm_id = dependencia_adm.pk_dependencia_adm_id')
+        .where('turma.fk_tipo_turma_id <= 3')
+        .where(`turma.ano_censo IN (${schoolClassYearQry})`)
+        .group('turma.ano_censo')
+        .group('dependencia_adm.nome')
+        .group('estado.nome')
+        .order('estado.nome')
+        .order('dependencia_adm.nome');
+
+    const enrollmentsPerSchoolLevelQry = squel.select()
+        .field('estado.nome', 'name')
+        .field('COALESCE(SUM(turma.num_matriculas), 0)', 'total')
+        .field('turma.ano_censo', 'census_year')
+        .field('etapa_ensino.desc_etapa', 'school_level')
+        .from('turma')
+        .from('etapa_ensino')
+        .from('estado')
+        .where('turma.fk_estado_id = estado.pk_estado_id')
+        .where(`turma.fk_estado_id = ${stateId}`)
+        .where('turma.fk_etapa_ensino_id = etapa_ensino.pk_etapa_ensino_id')
+        .where('turma.fk_tipo_turma_id <= 3')
+        .where(`turma.ano_censo IN (${schoolClassYearQry})`)
+        .group('turma.ano_censo')
+        .group('etapa_ensino.desc_etapa')
+        .group('etapa_ensino.pk_etapa_ensino_id')
+        .group('estado.nome')
+        .order('estado.nome')
+        .order('etapa_ensino.pk_etapa_ensino_id');
+
+    const queryLabels = [ "school", "school_per_location", "enrollment", "enrollment_per_adm_dep",
+        "enrollment_per_school_level" ];
+    const querySet = [ totalSchoolsQry, schoolsPerLocationQry, enrollmentsQry,
+        enrollmentsPerAdmDepQry, enrollmentsPerSchoolLevelQry];
+    // wait until all queries finish or one of them fail
+    Promise.all(dbExecAll(querySet)).then((queryResults) => {
+        req.result = processResultSet(queryResults, queryLabels);
+        for(let label in req.result) {
+            for(let row of req.result[label]) {
+                if (row.hasOwnProperty('location')) {
+                    row.location = locationIdToStr(row.location);
+                }
+            }
+        }
+        next();
+    }).catch((error) => {
+        log.error(`[SQL query error] ${error}`);
+        next(error);
+    });
+}, response('location'));
+
+locationApp.get('/educational/city/:id', (req, res, next) => {
+    const cityId = parseInt(req.params.id, 10);
+
+    const censusYearQry = squel.select()
+        .field('MAX(escola.ano_censo)', 'ano_censo')
+        .from('escola')
+        .toString();
+
+    const totalSchoolsQry = squel.select()
+        .field('municipio.nome', 'name')
+        .field('0', 'location')
+        .field('COUNT(DISTINCT(escola.cod_entidade))', 'total')
+        .field('escola.ano_censo', 'census_year')
+        .from('escola')
+        .from('municipio')
+        .where('escola.fk_municipio_id = municipio.pk_cod_ibge')
+        .where(`escola.fk_municipio_id = ${cityId}`)
+        .where(`escola.ano_censo IN (${censusYearQry})`)
+        .where('escola.id_tipo_turma = 0')
+        .group('municipio.nome')
+        .group('escola.ano_censo')
+        .order('municipio.nome');
+
+    const schoolsPerLocationQry = squel.select()
+        .field('municipio.nome', 'name')
+        .field('escola.tipo_localizacao', 'location')
+        .field('COUNT(DISTINCT(escola.cod_entidade))', 'total')
+        .field('escola.ano_censo', 'census_year')
+        .from('escola')
+        .from('municipio')
+        .where('escola.fk_municipio_id = municipio.pk_cod_ibge')
+        .where(`escola.fk_municipio_id = ${cityId}`)
+        .where(`escola.ano_censo IN (${censusYearQry})`)
+        .where('escola.id_tipo_turma = 0')
+        .group('municipio.nome')
+        .group('escola.tipo_localizacao')
+        .group('escola.ano_censo')
+        .order('municipio.nome')
+        .order('escola.tipo_localizacao');
+
+    const schoolClassYearQry = squel.select()
+        .field('MAX(turma.ano_censo)')
+        .from('turma')
+        .toString();
+
+    const enrollmentsQry = squel.select()
+        .field('municipio.nome', 'name')
+        .field('COALESCE(SUM(turma.num_matriculas), 0)', 'total')
+        .field('turma.ano_censo', 'census_year')
+        .from('turma')
+        .from('municipio')
+        .where('turma.fk_municipio_id = municipio.pk_cod_ibge')
+        .where(`turma.fk_municipio_id = ${cityId}`)
+        .where(`turma.ano_censo IN (${schoolClassYearQry})`)
+        .where('turma.fk_tipo_turma_id <= 3')
+        .group('turma.ano_censo')
+        .group('municipio.nome')
+        .order('municipio.nome');
+
+    const enrollmentsPerAdmDepQry = squel.select()
+        .field('municipio.nome', 'name')
+        .field('COALESCE(SUM(turma.num_matriculas), 0)', 'total')
+        .field('turma.ano_censo', 'census_year')
+        .field('dependencia_adm.nome', 'adm_dependency')
+        .from('turma')
+        .from('dependencia_adm')
+        .from('municipio')
+        .where('turma.fk_municipio_id = municipio.pk_cod_ibge')
+        .where(`turma.fk_municipio_id = ${cityId}`)
+        .where('turma.fk_dependencia_adm_id = dependencia_adm.pk_dependencia_adm_id')
+        .where('turma.fk_tipo_turma_id <= 3')
+        .where(`turma.ano_censo IN (${schoolClassYearQry})`)
+        .group('turma.ano_censo')
+        .group('dependencia_adm.nome')
+        .group('municipio.nome')
+        .order('municipio.nome')
+        .order('dependencia_adm.nome');
+
+    const enrollmentsPerSchoolLevelQry = squel.select()
+        .field('municipio.nome', 'name')
+        .field('COALESCE(SUM(turma.num_matriculas), 0)', 'total')
+        .field('turma.ano_censo', 'census_year')
+        .field('etapa_ensino.desc_etapa', 'school_level')
+        .from('turma')
+        .from('etapa_ensino')
+        .from('municipio')
+        .where('turma.fk_municipio_id = municipio.pk_cod_ibge')
+        .where(`turma.fk_municipio_id = ${cityId}`)
+        .where('turma.fk_etapa_ensino_id = etapa_ensino.pk_etapa_ensino_id')
+        .where('turma.fk_tipo_turma_id <= 3')
+        .where(`turma.ano_censo IN (${schoolClassYearQry})`)
+        .group('turma.ano_censo')
+        .group('etapa_ensino.desc_etapa')
+        .group('etapa_ensino.pk_etapa_ensino_id')
+        .group('municipio.nome')
+        .order('municipio.nome')
+        .order('etapa_ensino.pk_etapa_ensino_id');
+
+    const queryLabels = [ "school", "school_per_location", "enrollment", "enrollment_per_adm_dep",
+        "enrollment_per_school_level" ];
+    const querySet = [ totalSchoolsQry, schoolsPerLocationQry, enrollmentsQry,
+        enrollmentsPerAdmDepQry, enrollmentsPerSchoolLevelQry];
+    // wait until all queries finish or one of them fail
+    Promise.all(dbExecAll(querySet)).then((queryResults) => {
+        req.result = processResultSet(queryResults, queryLabels);
+        for(let label in req.result) {
+            for(let row of req.result[label]) {
+                if (row.hasOwnProperty('location')) {
+                    row.location = locationIdToStr(row.location);
+                }
+            }
+        }
+        next();
+    }).catch((error) => {
+        log.error(`[SQL query error] ${error}`);
+        next(error);
+    });
+}, response('location'));
+
+locationApp.get('/educational/school_level', (req, res, next) => {
+    const enrollmentsPerSchoolLevelYearQry = squel.select()
+        .field('MAX(turma.ano_censo)', 'census_year')
+        .from('turma');
+
+    const enrollmentsPerSchoolLevelQry = squel.select()
+        .field('COALESCE(SUM(turma.num_matriculas), 0)', 'total')
+        .field('turma.ano_censo', 'census_year')
+        .field('turma.serie_ano', 'school_year')
+        .field('etapa_ensino.desc_etapa', 'school_level')
+        .from('turma')
+        .from('etapa_ensino')
+        .where(`turma.ano_censo IN (${enrollmentsPerSchoolLevelYearQry.toString()})`)
+        .where('turma.fk_etapa_ensino_id = etapa_ensino.pk_etapa_ensino_id')
+        .where('turma.fk_tipo_turma_id <= 3')
+        .group('etapa_ensino.desc_etapa')
+        .group('etapa_ensino.pk_etapa_ensino_id')
+        .group('turma.serie_ano')
+        .group('turma.ano_censo')
+        .order('etapa_ensino.pk_etapa_ensino_id')
+        .order('turma.serie_ano')
+        .order('turma.ano_censo');
+
+    const queryLabels = [ 'enrollment_per_school_level', 'enrollment_census_year' ];
+    const querySet = [ enrollmentsPerSchoolLevelQry, enrollmentsPerSchoolLevelYearQry ];
+    // wait until all queries finish or one of them fail
+    Promise.all(dbExecAll(querySet, enrollmentsPerSchoolLevelYearQry)).then((queryResults) => {
+        const result = queryResults[0];
+        const censusYear = queryResults[1][0]['census_year'];
+
+        let school_levels = {};
+        for(let i = 0; i < result.length; ++i) {
+            const school_year  = schoolYearIdToStr(result[i].school_year);
+            const school_level = result[i].school_level;
+            const census_year = result[i].census_year;
+            if (typeof school_levels[school_level] === 'undefined') {
+                school_levels[school_level] = {};
+            }
+            school_levels[school_level][school_year] = parseInt(result[i].total, 10);
+        }
+
+        let response = [];
+        for(let level in school_levels) {
+            if (school_levels.hasOwnProperty(level)) {
+                let sclevel = {};
+                sclevel["degree"] = level;
+                sclevel["census_year"] = parseInt(censusYear, 10);
+                sclevel["table"] = [];
+                for(let school_year in school_levels[level]) {
+                    if (school_levels[level].hasOwnProperty(school_year)) {
+                        let enrollment = { 'title' : school_year,
+                                           'value' : school_levels[level][school_year] };
+                        sclevel["table"].push(enrollment);
+                    }
+                }
+                response.push(sclevel);
+            }
+        }
+        req.result = response;
+        next();
+    }).catch((error) => {
+        log.error(`[SQL query error] ${error}`);
+        next(error);
+    });
+}, response('location'));
+
+locationApp.get('/educational/school_level/region/:id', (req, res, next) => {
+    const regionId = parseInt(req.params.id, 10);
+
+    const enrollmentsPerSchoolLevelYearQry = squel.select()
+        .field('MAX(turma.ano_censo)', 'census_year')
+        .from('turma');
+
+    const enrollmentsPerSchoolLevelQry = squel.select()
+        .field('COALESCE(SUM(turma.num_matriculas), 0)', 'total')
+        .field('turma.ano_censo', 'census_year')
+        .field('turma.serie_ano', 'school_year')
+        .field('etapa_ensino.desc_etapa', 'school_level')
+        .from('turma')
+        .from('etapa_ensino')
+        .from('regiao')
+        .where(`turma.fk_regiao_id = ${regionId}`)
+        .where('turma.fk_regiao_id = regiao.pk_regiao_id')
+        .where(`turma.ano_censo IN (${enrollmentsPerSchoolLevelYearQry.toString()})`)
+        .where('turma.fk_etapa_ensino_id = etapa_ensino.pk_etapa_ensino_id')
+        .where('turma.fk_tipo_turma_id <= 3')
+        .group('regiao.nome')
+        .group('etapa_ensino.desc_etapa')
+        .group('etapa_ensino.pk_etapa_ensino_id')
+        .group('turma.serie_ano')
+        .group('turma.ano_censo')
+        .order('regiao.nome')
+        .order('etapa_ensino.pk_etapa_ensino_id')
+        .order('turma.serie_ano')
+        .order('turma.ano_censo');
+
+    const queryLabels = [ 'enrollment_per_school_level', 'enrollment_census_year' ];
+    const querySet = [ enrollmentsPerSchoolLevelQry, enrollmentsPerSchoolLevelYearQry ];
+    // wait until all queries finish or one of them fail
+    Promise.all(dbExecAll(querySet, enrollmentsPerSchoolLevelYearQry)).then((queryResults) => {
+        const result = queryResults[0];
+        const censusYear = queryResults[1][0]['census_year'];
+
+        let school_levels = {};
+        for(let i = 0; i < result.length; ++i) {
+            const school_year  = schoolYearIdToStr(result[i].school_year);
+            const school_level = result[i].school_level;
+            const census_year = result[i].census_year;
+            if (typeof school_levels[school_level] === 'undefined') {
+                school_levels[school_level] = {};
+            }
+            school_levels[school_level][school_year] = parseInt(result[i].total, 10);
+        }
+
+        let response = [];
+        for(let level in school_levels) {
+            if (school_levels.hasOwnProperty(level)) {
+                let sclevel = {};
+                sclevel["degree"] = level;
+                sclevel["census_year"] = parseInt(censusYear, 10);
+                sclevel["table"] = [];
+                for(let school_year in school_levels[level]) {
+                    if (school_levels[level].hasOwnProperty(school_year)) {
+                        let enrollment = { 'title' : school_year,
+                                           'value' : school_levels[level][school_year] };
+                        sclevel["table"].push(enrollment);
+                    }
+                }
+                response.push(sclevel);
+            }
+        }
+        req.result = response;
+        next();
+    }).catch((error) => {
+        log.error(`[SQL query error] ${error}`);
+        next(error);
+    });
+}, response('location'));
+
+locationApp.get('/educational/school_level/state/:id', (req, res, next) => {
+    const stateId = parseInt(req.params.id, 10);
+
+    const enrollmentsPerSchoolLevelYearQry = squel.select()
+        .field('MAX(turma.ano_censo)', 'census_year')
+        .from('turma');
+
+    const enrollmentsPerSchoolLevelQry = squel.select()
+        .field('COALESCE(SUM(turma.num_matriculas), 0)', 'total')
+        .field('turma.ano_censo', 'census_year')
+        .field('turma.serie_ano', 'school_year')
+        .field('etapa_ensino.desc_etapa', 'school_level')
+        .from('turma')
+        .from('etapa_ensino')
+        .from('estado')
+        .where(`turma.fk_estado_id = ${stateId}`)
+        .where('turma.fk_estado_id = estado.pk_estado_id')
+        .where(`turma.ano_censo IN (${enrollmentsPerSchoolLevelYearQry.toString()})`)
+        .where('turma.fk_etapa_ensino_id = etapa_ensino.pk_etapa_ensino_id')
+        .where('turma.fk_tipo_turma_id <= 3')
+        .group('estado.nome')
+        .group('etapa_ensino.desc_etapa')
+        .group('etapa_ensino.pk_etapa_ensino_id')
+        .group('turma.serie_ano')
+        .group('turma.ano_censo')
+        .order('estado.nome')
+        .order('etapa_ensino.pk_etapa_ensino_id')
+        .order('turma.serie_ano')
+        .order('turma.ano_censo');
+
+    const queryLabels = [ 'enrollment_per_school_level', 'enrollment_census_year' ];
+    const querySet = [ enrollmentsPerSchoolLevelQry, enrollmentsPerSchoolLevelYearQry ];
+    // wait until all queries finish or one of them fail
+    Promise.all(dbExecAll(querySet, enrollmentsPerSchoolLevelYearQry)).then((queryResults) => {
+        const result = queryResults[0];
+        const censusYear = queryResults[1][0]['census_year'];
+
+        let school_levels = {};
+        for(let i = 0; i < result.length; ++i) {
+            const school_year  = schoolYearIdToStr(result[i].school_year);
+            const school_level = result[i].school_level;
+            const census_year = result[i].census_year;
+            if (typeof school_levels[school_level] === 'undefined') {
+                school_levels[school_level] = {};
+            }
+            school_levels[school_level][school_year] = parseInt(result[i].total, 10);
+        }
+
+        let response = [];
+        for(let level in school_levels) {
+            if (school_levels.hasOwnProperty(level)) {
+                let sclevel = {};
+                sclevel["degree"] = level;
+                sclevel["census_year"] = parseInt(censusYear, 10);
+                sclevel["table"] = [];
+                for(let school_year in school_levels[level]) {
+                    if (school_levels[level].hasOwnProperty(school_year)) {
+                        let enrollment = { 'title' : school_year,
+                                           'value' : school_levels[level][school_year] };
+                        sclevel["table"].push(enrollment);
+                    }
+                }
+                response.push(sclevel);
+            }
+        }
+        req.result = response;
+        next();
+    }).catch((error) => {
+        log.error(`[SQL query error] ${error}`);
+        next(error);
+    });
+}, response('location'));
+
+locationApp.get('/educational/school_level/city/:id', (req, res, next) => {
+    const cityId = parseInt(req.params.id, 10);
+
+    const enrollmentsPerSchoolLevelYearQry = squel.select()
+        .field('MAX(turma.ano_censo)', 'census_year')
+        .from('turma');
+
+    const enrollmentsPerSchoolLevelQry = squel.select()
+        .field('COALESCE(SUM(turma.num_matriculas), 0)', 'total')
+        .field('turma.ano_censo', 'census_year')
+        .field('turma.serie_ano', 'school_year')
+        .field('etapa_ensino.desc_etapa', 'school_level')
+        .from('turma')
+        .from('etapa_ensino')
+        .from('municipio')
+        .where(`turma.fk_municipio_id = ${cityId}`)
+        .where('turma.fk_municipio_id = municipio.pk_cod_ibge')
+        .where(`turma.ano_censo IN (${enrollmentsPerSchoolLevelYearQry.toString()})`)
+        .where('turma.fk_etapa_ensino_id = etapa_ensino.pk_etapa_ensino_id')
+        .where('turma.fk_tipo_turma_id <= 3')
+        .group('municipio.nome')
+        .group('etapa_ensino.desc_etapa')
+        .group('etapa_ensino.pk_etapa_ensino_id')
+        .group('turma.serie_ano')
+        .group('turma.ano_censo')
+        .order('municipio.nome')
+        .order('etapa_ensino.pk_etapa_ensino_id')
+        .order('turma.serie_ano')
+        .order('turma.ano_censo');
+
+    const queryLabels = [ 'enrollment_per_school_level', 'enrollment_census_year' ];
+    const querySet = [ enrollmentsPerSchoolLevelQry, enrollmentsPerSchoolLevelYearQry ];
+    // wait until all queries finish or one of them fail
+    Promise.all(dbExecAll(querySet, enrollmentsPerSchoolLevelYearQry)).then((queryResults) => {
+        const result = queryResults[0];
+        const censusYear = queryResults[1][0]['census_year'];
+
+        let school_levels = {};
+        for(let i = 0; i < result.length; ++i) {
+            const school_year  = schoolYearIdToStr(result[i].school_year);
+            const school_level = result[i].school_level;
+            const census_year = result[i].census_year;
+            if (typeof school_levels[school_level] === 'undefined') {
+                school_levels[school_level] = {};
+            }
+            school_levels[school_level][school_year] = parseInt(result[i].total, 10);
+        }
+
+        let response = [];
+        for(let level in school_levels) {
+            if (school_levels.hasOwnProperty(level)) {
+                let sclevel = {};
+                sclevel["degree"] = level;
+                sclevel["census_year"] = parseInt(censusYear, 10);
+                sclevel["table"] = [];
+                for(let school_year in school_levels[level]) {
+                    if (school_levels[level].hasOwnProperty(school_year)) {
+                        let enrollment = { 'title' : school_year,
+                                           'value' : school_levels[level][school_year] };
+                        sclevel["table"].push(enrollment);
+                    }
+                }
+                response.push(sclevel);
+            }
+        }
+        req.result = response;
+        next();
+    }).catch((error) => {
+        log.error(`[SQL query error] ${error}`);
+        next(error);
+    });
+}, response('location'));
+
+module.exports = locationApp;
diff --git a/src/libs/routes/simulation.js b/src/libs/routes/simulation.js
index 16c7dcf848e0851c71a5c5015d554087a6f4e1b6..6469d4d6b3df70077b9f38fca96c6ae479133022 100644
--- a/src/libs/routes/simulation.js
+++ b/src/libs/routes/simulation.js
@@ -4,7 +4,6 @@ const simulationApp = express();
 
 const libs = `${process.cwd()}/libs`;
 
-
 const log = require(`${libs}/log`)(module);
 
 const squel = require('squel');
diff --git a/src/server.js b/src/server.js
index aaf9309ef94b0e30a9683cc9fcc854e4205b8e7b..ccc5e884702641fca04e0fc0480860a4b72300ef 100644
--- a/src/server.js
+++ b/src/server.js
@@ -18,9 +18,6 @@ if (!compatVersion()) {
 // Set default port: first environment variable PORT, then configuration and last 3000
 app.set('port', process.env.PORT || config.port || 3000);
 process.env.NODE_ENV = process.env.NODE_ENV || 'development';
-
-app.set('port', process.env.PORT || config.port || 3000);
-
 // Set default ip: first environment variable IOP, then configuration and last '127.0.0.1'
 app.set('ip', process.env.IP || config.ip || '127.0.0.1');
 
diff --git a/src/test/api.js b/src/test/api.js
new file mode 100644
index 0000000000000000000000000000000000000000..b5476ea6352d086cb8d43f8e1f091686bc4f91c4
--- /dev/null
+++ b/src/test/api.js
@@ -0,0 +1,49 @@
+process.env.NODE_ENV = 'test';
+
+const chai = require('chai');
+
+const dirtyChai = require('dirty-chai');
+
+chai.use(dirtyChai);
+
+const chaiXml = require('chai-xml');
+
+chai.use(chaiXml);
+
+const chaiHttp = require('chai-http');
+
+const assert = chai.assert;
+
+const expect = chai.expect;
+
+const should = chai.should(); // actually call the function
+
+const libs = `${process.cwd()}/libs`;
+
+const server = require(`${libs}/app`);
+
+chai.use(chaiHttp);
+
+describe('API is running', () => {
+    it('should respond it\'s running', (done) => {
+        chai.request(server)
+            .get('/api/v1')
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.json;
+                res.body.should.have.property('msg');
+                done();
+            })
+    });
+
+    it('should respond with 404 error', (done) => {
+        chai.request(server)
+            .get('/api/v1/thisrouteshouldgivea404')
+            .end((err, res) => {
+                res.should.have.status(404);
+                res.should.be.json;
+                res.body.should.have.property('error');
+                done();
+            })
+    });
+});
diff --git a/src/test/city.js b/src/test/city.js
new file mode 100644
index 0000000000000000000000000000000000000000..761bcb0eeb21e55cd73d4aaac4dbd2db87c6cb90
--- /dev/null
+++ b/src/test/city.js
@@ -0,0 +1,73 @@
+process.env.NODE_ENV = 'test';
+
+const chai = require('chai');
+
+const dirtyChai = require('dirty-chai');
+
+chai.use(dirtyChai);
+
+const chaiXml = require('chai-xml');
+
+chai.use(chaiXml);
+
+const chaiHttp = require('chai-http');
+
+const assert = chai.assert;
+
+const expect = chai.expect;
+
+const should = chai.should(); // actually call the function
+
+const libs = `${process.cwd()}/libs`;
+
+const server = require(`${libs}/app`);
+
+chai.use(chaiHttp);
+
+
+describe('request cities', () => {
+    it('should list all cities', (done) => {
+        chai.request(server)
+            .get('/api/v1/city')
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.json;
+                res.body.should.have.property('result');
+                res.body.result.should.be.a('array');
+                res.body.result[0].should.have.property('pk_cod_ibge');
+                res.body.result[0].should.have.property('fk_estado_id');
+                res.body.result[0].should.have.property('nome');
+                done();
+            });
+    });
+
+    it('should list a city by id', (done) => {
+        chai.request(server)
+            .get('/api/v1/city?filter=id:4106902')
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.json;
+                res.body.should.have.property('result');
+                res.body.result.should.be.a('array');
+                res.body.result[0].should.have.property('pk_cod_ibge');
+                res.body.result[0].should.have.property('fk_estado_id');
+                res.body.result[0].should.have.property('nome');
+                done();
+            });
+    });
+
+    it('should list all cities from a state', (done) => {
+        chai.request(server)
+            .get('/api/v1/city?filter=state:41')
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.json;
+                res.body.should.have.property('result');
+                res.body.result.should.be.a('array');
+                res.body.result[0].should.have.property('pk_cod_ibge');
+                res.body.result[0].should.have.property('fk_estado_id');
+                res.body.result[0].should.have.property('nome');
+                done();
+            })
+    })
+});
diff --git a/src/test/enrollment.js b/src/test/enrollment.js
new file mode 100644
index 0000000000000000000000000000000000000000..9d76718b49b59f74915ba5f9262ac399b220cb12
--- /dev/null
+++ b/src/test/enrollment.js
@@ -0,0 +1,174 @@
+process.env.NODE_ENV = 'test';
+
+const chai = require('chai');
+
+const dirtyChai = require('dirty-chai');
+
+chai.use(dirtyChai);
+
+const chaiXml = require('chai-xml');
+
+chai.use(chaiXml);
+
+const chaiHttp = require('chai-http');
+
+const assert = chai.assert;
+
+const expect = chai.expect;
+
+const should = chai.should(); // actually call the function
+
+const libs = `${process.cwd()}/libs`;
+
+const server = require(`${libs}/app`);
+
+chai.use(chaiHttp);
+describe('request enrollments', () => {
+    it('should list the year range', (done) => {
+        chai.request(server)
+            .get('/api/v1/enrollment/year_range')
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.json;
+                res.body.should.have.property('result');
+                res.body.result.should.be.a('array');
+                res.body.result[0].should.have.property('start_year');
+                res.body.result[0].should.have.property('end_year');
+                done();
+            });
+    });
+
+    it('should list the locations', (done) => {
+        chai.request(server)
+            .get('/api/v1/enrollment/location')
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.json;
+                res.body.should.have.property('result');
+                res.body.result.should.be.a('array');
+                res.body.result[0].should.have.property('location_id');
+                res.body.result[0].should.have.property('description');
+                done();
+            });
+    });
+
+    it('should list the education level', (done) => {
+        chai.request(server)
+            .get('/api/v1/enrollment/education_level')
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.json;
+                res.body.should.have.property('result');
+                res.body.result.should.be.a('array');
+                res.body.result[0].should.have.property('id');
+                res.body.result[0].should.have.property('name');
+                done();
+            });
+    });
+
+    it('should list the administrative dependencies', (done) => {
+        chai.request(server)
+            .get('/api/v1/enrollment/adm_dependency ')
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.json;
+                res.body.should.have.property('result');
+                res.body.result.should.be.a('array');
+                res.body.result[0].should.have.property('id');
+                res.body.result[0].should.have.property('name');
+                done();
+            });
+    });
+
+    it('should list enrollments', (done) => {
+        chai.request(server)
+            .get('/api/v1/enrollment')
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.json;
+                res.body.should.have.property('result');
+                res.body.result.should.be.a('array');
+                res.body.result[0].should.have.property('name');
+                res.body.result[0].should.have.property('total');
+                done();
+            });
+    });
+
+    it('should list enrollments with valid filters', (done) => {
+        chai.request(server)
+            .get('/api/v1/enrollment?filter=min_year:2014,state:41')
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.json;
+                res.body.should.have.property('result');
+                res.body.result.should.be.a('array');
+                res.body.result[0].should.have.property('name');
+                res.body.result[0].should.have.property('total');
+                done();
+            });
+    });
+
+    it('should list enrollments with invalid filters', (done) => {
+        chai.request(server)
+            .get('/api/v1/enrollment?filter=foo:2010,bar:41')
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.json;
+                res.body.should.have.property('result');
+                res.body.result.should.be.a('array');
+                res.body.result[0].should.have.property('name');
+                res.body.result[0].should.have.property('total');
+                done();
+            });
+    });
+
+    it('should list enrollments with valid dimensions', (done) => {
+        chai.request(server)
+            .get('/api/v1/enrollment?dims=region,state,adm_dependency,location&filter=min_year:2014,region:4')
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.json;
+                res.body.should.have.property('result');
+                res.body.result.should.be.a('array');
+                res.body.result[0].should.have.property('region_name');
+                res.body.result[0].should.have.property('state_name');
+                res.body.result[0].should.have.property('adm_dependency_name');
+                res.body.result[0].should.have.property('location_name');
+                res.body.result[0].should.have.property('total');
+                done();
+            });
+    });
+
+    it('should list enrollments with invalid dimensions', (done) => {
+        chai.request(server)
+            .get('/api/v1/enrollment?dims=foo,bar')
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.json;
+                res.body.should.have.property('result');
+                res.body.result.should.be.a('array');
+                res.body.result[0].should.have.property('name');
+                res.body.result[0].should.have.property('total');
+                done();
+            });
+    });
+
+    it('should list enrollments with valid dimensions and filters', (done) => {
+        chai.request(server)
+            .get('/api/v1/enrollment?dims=region,state,education_level,school&filter=min_year:2013,max_year:2014,city:4106902')
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.json;
+                res.body.should.have.property('result');
+                res.body.result.should.be.a('array');
+                res.body.result[0].should.have.property('region_name');
+                res.body.result[0].should.have.property('state_name');
+                //res.body.result[0].should.have.property('school_name');
+                res.body.result[0].should.have.property('education_level');
+                res.body.result[0].should.have.property('total');
+                res.body.result[0].should.have.property('year');
+                done();
+            });
+    });
+
+});
diff --git a/src/test/location.js b/src/test/location.js
new file mode 100644
index 0000000000000000000000000000000000000000..42761a8dadf84c63b1c4202fe27e17b9fa2aa920
--- /dev/null
+++ b/src/test/location.js
@@ -0,0 +1,565 @@
+process.env.NODE_ENV = 'test';
+
+const chai = require('chai');
+
+const dirtyChai = require('dirty-chai');
+
+chai.use(dirtyChai);
+
+const chaiXml = require('chai-xml');
+
+chai.use(chaiXml);
+
+const chaiHttp = require('chai-http');
+
+const assert = chai.assert;
+
+const expect = chai.expect;
+
+const should = chai.should(); // actually call the function
+
+const libs = `${process.cwd()}/libs`;
+
+const server = require(`${libs}/app`);
+
+chai.use(chaiHttp);
+
+const testTimeout = 5000;
+
+describe('test location', () => {
+    it('should return the expected response format for sociodemographic data for the whole country', (done) => {
+        chai.request(server)
+            .get('/api/v1/location/sociodemographic')
+            .end((err, res) => {
+                res.should.have.status(200);
+                // test response format
+                res.should.be.json;
+                // test for result attribute in the response
+                res.body.should.have.property('result');
+                // test result type
+                res.body.result.should.be.a('object');
+                res.body.result.should.have.property('population');
+                res.body.result.should.have.property('gdp');
+                res.body.result.should.have.property('idh');
+                res.body.result.should.have.property('analfab');
+                res.body.result.should.have.property('gini');
+                // test response attributes for population
+                res.body.result.population.should.have.property('name');
+                res.body.result.population.should.have.property('population');
+                res.body.result.population.should.have.property('census_year');
+                // test response attributes for gdp
+                res.body.result.gdp.should.have.property('name');
+                res.body.result.gdp.should.have.property('gdp_per_capita');
+                res.body.result.gdp.should.have.property('census_year');
+                // test response attributes for idh
+                res.body.result.idh.should.have.property('name');
+                res.body.result.idh.should.have.property('idhm');
+                res.body.result.idh.should.have.property('census_year');
+                // test response attributes for analfab
+                res.body.result.analfab.should.have.property('name');
+                res.body.result.analfab.should.have.property('analfabetism');
+                res.body.result.analfab.should.have.property('census_year');
+                // test response attributes for gini
+                res.body.result.gini.should.have.property('name');
+                res.body.result.gini.should.have.property('gini');
+                res.body.result.gini.should.have.property('census_year');
+                done();
+            });
+    }).timeout(testTimeout);
+
+    it('should return the expected response format for sociodemographic data for a region', (done) => {
+        chai.request(server)
+            .get('/api/v1/location/sociodemographic/region/1')
+            .end((err, res) => {
+                res.should.have.status(200);
+                // test response format
+                res.should.be.json;
+                // test for result attribute in the response
+                res.body.should.have.property('result');
+                // test result type
+                res.body.result.should.be.a('object');
+                res.body.result.should.have.property('population');
+                res.body.result.should.have.property('gdp');
+                res.body.result.should.have.property('idh');
+                res.body.result.should.have.property('analfab');
+                res.body.result.should.have.property('gini');
+                // test response attributes for population
+                res.body.result.population.should.have.property('name');
+                res.body.result.population.should.have.property('population');
+                res.body.result.population.should.have.property('census_year');
+                // test response attributes for gdp
+                res.body.result.gdp.should.have.property('name');
+                res.body.result.gdp.should.have.property('gdp_per_capita');
+                res.body.result.gdp.should.have.property('census_year');
+                // test response attributes for idh
+                res.body.result.idh.should.have.property('name');
+                res.body.result.idh.should.have.property('idhm');
+                res.body.result.idh.should.have.property('census_year');
+                // test response attributes for analfab
+                res.body.result.analfab.should.have.property('name');
+                res.body.result.analfab.should.have.property('analfabetism');
+                res.body.result.analfab.should.have.property('census_year');
+                // test response attributes for gini
+                res.body.result.gini.should.have.property('name');
+                res.body.result.gini.should.have.property('gini');
+                res.body.result.gini.should.have.property('census_year');
+                done();
+            });
+    }).timeout(testTimeout);
+
+    it('should return the expected response format for sociodemographic data for a state', (done) => {
+        chai.request(server)
+            .get('/api/v1/location/sociodemographic/state/42')
+            .end((err, res) => {
+                res.should.have.status(200);
+                // test response format
+                res.should.be.json;
+                // test for result attribute in the response
+                res.body.should.have.property('result');
+                // test result type
+                res.body.result.should.be.a('object');
+                res.body.result.should.have.property('population');
+                res.body.result.should.have.property('gdp');
+                res.body.result.should.have.property('idh');
+                res.body.result.should.have.property('analfab');
+                res.body.result.should.have.property('gini');
+                // test response attributes for population
+                res.body.result.population.should.have.property('name');
+                res.body.result.population.should.have.property('population');
+                res.body.result.population.should.have.property('census_year');
+                // test response attributes for gdp
+                res.body.result.gdp.should.have.property('name');
+                res.body.result.gdp.should.have.property('gdp_per_capita');
+                res.body.result.gdp.should.have.property('census_year');
+                // test response attributes for idh
+                res.body.result.idh.should.have.property('name');
+                res.body.result.idh.should.have.property('idhm');
+                res.body.result.idh.should.have.property('census_year');
+                // test response attributes for analfab
+                res.body.result.analfab.should.have.property('name');
+                res.body.result.analfab.should.have.property('analfabetism');
+                res.body.result.analfab.should.have.property('census_year');
+                // test response attributes for gini
+                res.body.result.gini.should.have.property('name');
+                res.body.result.gini.should.have.property('gini');
+                res.body.result.gini.should.have.property('census_year');
+                done();
+            });
+    }).timeout(testTimeout);
+
+    it('should return the expected response format for sociodemographic data for a city', (done) => {
+        chai.request(server)
+            .get('/api/v1/location/sociodemographic/city/4106902')
+            .end((err, res) => {
+                res.should.have.status(200);
+                // test response format
+                res.should.be.json;
+                // test for result attribute in the response
+                res.body.should.have.property('result');
+                // test result type
+                res.body.result.should.be.a('object');
+                res.body.result.should.have.property('population');
+                res.body.result.should.have.property('gdp');
+                res.body.result.should.have.property('idh');
+                res.body.result.should.have.property('analfab');
+                res.body.result.should.have.property('gini');
+                // test response attributes for population
+                res.body.result.population.should.have.property('name');
+                res.body.result.population.should.have.property('population');
+                res.body.result.population.should.have.property('census_year');
+                // test response attributes for gdp
+                res.body.result.gdp.should.have.property('name');
+                res.body.result.gdp.should.have.property('gdp_per_capita');
+                res.body.result.gdp.should.have.property('census_year');
+                // test response attributes for idh
+                res.body.result.idh.should.have.property('name');
+                res.body.result.idh.should.have.property('idhm');
+                res.body.result.idh.should.have.property('census_year');
+                // test response attributes for analfab
+                res.body.result.analfab.should.have.property('name');
+                res.body.result.analfab.should.have.property('analfabetism');
+                res.body.result.analfab.should.have.property('census_year');
+                // test response attributes for gini
+                res.body.result.gini.should.have.property('name');
+                res.body.result.gini.should.have.property('gini');
+                res.body.result.gini.should.have.property('census_year');
+                done();
+            });
+    }).timeout(testTimeout);
+
+    it('should return the expected response format for educational data for the whole country', (done) => {
+        chai.request(server)
+            .get('/api/v1/location/educational')
+            .end((err, res) => {
+                res.should.have.status(200);
+                // test response format
+                res.should.be.json;
+                // test for result attribute in the response
+                res.body.should.have.property('result');
+                // test result type
+                res.body.result.should.be.a('object');
+                res.body.result.should.have.property('school');
+                res.body.result.school.should.be.a('array');
+                res.body.result.should.have.property('school_per_location');
+                res.body.result.school_per_location.should.be.a('array');
+                res.body.result.should.have.property('enrollment');
+                res.body.result.enrollment.should.be.a('array');
+                res.body.result.should.have.property('enrollment_per_adm_dep');
+                res.body.result.enrollment_per_adm_dep.should.be.a('array');
+                res.body.result.should.have.property('enrollment_per_school_level');
+                res.body.result.enrollment_per_school_level.should.be.a('array');
+                // test response attributes for school
+                res.body.result.school.should.a('array');
+                res.body.result.school.forEach((row) => {
+                    row.should.be.a('object');
+                    row.should.have.property('name');
+                    row.should.have.property('location');
+                    row.should.have.property('total');
+                    row.should.have.property('census_year');
+                });
+                // test response attributes for school_per_location
+                res.body.result.school_per_location.forEach((row) => {
+                    row.should.be.a('object');
+                    row.should.have.property('name');
+                    row.should.have.property('location');
+                    row.should.have.property('total');
+                    row.should.have.property('census_year');
+                });
+                // test response attributes for enrollment
+                res.body.result.enrollment.forEach((row) => {
+                    row.should.be.a('object');
+                    row.should.have.property('name');
+                    row.should.have.property('total');
+                    row.should.have.property('census_year');
+                });
+                // test response attributes for enrollment_per_adm_dep
+                res.body.result.enrollment_per_adm_dep.forEach((row) => {
+                    row.should.be.a('object');
+                    row.should.have.property('name');
+                    row.should.have.property('total');
+                    row.should.have.property('census_year');
+                    row.should.have.property('adm_dependency');
+                });
+                // test response attributes for enrollment_per_school_level
+                res.body.result.enrollment_per_school_level.forEach((row) => {
+                    row.should.be.a('object');
+                    row.should.have.property('name');
+                    row.should.have.property('total');
+                    row.should.have.property('census_year');
+                    row.should.have.property('school_level');
+                });
+                done();
+            });
+    }).timeout(testTimeout);
+
+    it('should return the expected response format for educational data for a country region', (done) => {
+        chai.request(server)
+            .get('/api/v1/location/educational/region/1')
+            .end((err, res) => {
+                res.should.have.status(200);
+                // test response format
+                res.should.be.json;
+                // test for result attribute in the response
+                res.body.should.have.property('result');
+                // test result type
+                res.body.result.should.be.a('object');
+                res.body.result.should.have.property('school');
+                res.body.result.school.should.be.a('array');
+                res.body.result.should.have.property('school_per_location');
+                res.body.result.school_per_location.should.be.a('array');
+                res.body.result.should.have.property('enrollment');
+                res.body.result.enrollment.should.be.a('array');
+                res.body.result.should.have.property('enrollment_per_adm_dep');
+                res.body.result.enrollment_per_adm_dep.should.be.a('array');
+                res.body.result.should.have.property('enrollment_per_school_level');
+                res.body.result.enrollment_per_school_level.should.be.a('array');
+                // test response attributes for school
+                res.body.result.school.should.a('array');
+                res.body.result.school.forEach((row) => {
+                    row.should.be.a('object');
+                    row.should.have.property('name');
+                    row.should.have.property('location');
+                    row.should.have.property('total');
+                    row.should.have.property('census_year');
+                });
+                // test response attributes for school_per_location
+                res.body.result.school_per_location.forEach((row) => {
+                    row.should.be.a('object');
+                    row.should.have.property('name');
+                    row.should.have.property('location');
+                    row.should.have.property('total');
+                    row.should.have.property('census_year');
+                });
+                // test response attributes for enrollment
+                res.body.result.enrollment.forEach((row) => {
+                    row.should.be.a('object');
+                    row.should.have.property('name');
+                    row.should.have.property('total');
+                    row.should.have.property('census_year');
+                });
+                // test response attributes for enrollment_per_adm_dep
+                res.body.result.enrollment_per_adm_dep.forEach((row) => {
+                    row.should.be.a('object');
+                    row.should.have.property('name');
+                    row.should.have.property('total');
+                    row.should.have.property('census_year');
+                    row.should.have.property('adm_dependency');
+                });
+                // test response attributes for enrollment_per_school_level
+                res.body.result.enrollment_per_school_level.forEach((row) => {
+                    row.should.be.a('object');
+                    row.should.have.property('name');
+                    row.should.have.property('total');
+                    row.should.have.property('census_year');
+                    row.should.have.property('school_level');
+                });
+                done();
+            });
+    }).timeout(testTimeout);
+
+    it('should return the expected response format for educational data for a country state', (done) => {
+        chai.request(server)
+            .get('/api/v1/location/educational/state/42')
+            .end((err, res) => {
+                res.should.have.status(200);
+                // test response format
+                res.should.be.json;
+                // test for result attribute in the response
+                res.body.should.have.property('result');
+                // test result type
+                res.body.result.should.be.a('object');
+                res.body.result.should.have.property('school');
+                res.body.result.school.should.be.a('array');
+                res.body.result.should.have.property('school_per_location');
+                res.body.result.school_per_location.should.be.a('array');
+                res.body.result.should.have.property('enrollment');
+                res.body.result.enrollment.should.be.a('array');
+                res.body.result.should.have.property('enrollment_per_adm_dep');
+                res.body.result.enrollment_per_adm_dep.should.be.a('array');
+                res.body.result.should.have.property('enrollment_per_school_level');
+                res.body.result.enrollment_per_school_level.should.be.a('array');
+                // test response attributes for school
+                res.body.result.school.should.a('array');
+                res.body.result.school.forEach((row) => {
+                    row.should.be.a('object');
+                    row.should.have.property('name');
+                    row.should.have.property('location');
+                    row.should.have.property('total');
+                    row.should.have.property('census_year');
+                });
+                // test response attributes for school_per_location
+                res.body.result.school_per_location.forEach((row) => {
+                    row.should.be.a('object');
+                    row.should.have.property('name');
+                    row.should.have.property('location');
+                    row.should.have.property('total');
+                    row.should.have.property('census_year');
+                });
+                // test response attributes for enrollment
+                res.body.result.enrollment.forEach((row) => {
+                    row.should.be.a('object');
+                    row.should.have.property('name');
+                    row.should.have.property('total');
+                    row.should.have.property('census_year');
+                });
+                // test response attributes for enrollment_per_adm_dep
+                res.body.result.enrollment_per_adm_dep.forEach((row) => {
+                    row.should.be.a('object');
+                    row.should.have.property('name');
+                    row.should.have.property('total');
+                    row.should.have.property('census_year');
+                    row.should.have.property('adm_dependency');
+                });
+                // test response attributes for enrollment_per_school_level
+                res.body.result.enrollment_per_school_level.forEach((row) => {
+                    row.should.be.a('object');
+                    row.should.have.property('name');
+                    row.should.have.property('total');
+                    row.should.have.property('census_year');
+                    row.should.have.property('school_level');
+                });
+                done();
+            });
+    }).timeout(testTimeout);
+
+    it('should return the expected response format for educational data for a country city', (done) => {
+        chai.request(server)
+            .get('/api/v1/location/educational/city/4106902')
+            .end((err, res) => {
+                res.should.have.status(200);
+                // test response format
+                res.should.be.json;
+                // test for result attribute in the response
+                res.body.should.have.property('result');
+                // test result type
+                res.body.result.should.be.a('object');
+                res.body.result.should.have.property('school');
+                res.body.result.school.should.be.a('array');
+                res.body.result.should.have.property('school_per_location');
+                res.body.result.school_per_location.should.be.a('array');
+                res.body.result.should.have.property('enrollment');
+                res.body.result.enrollment.should.be.a('array');
+                res.body.result.should.have.property('enrollment_per_adm_dep');
+                res.body.result.enrollment_per_adm_dep.should.be.a('array');
+                res.body.result.should.have.property('enrollment_per_school_level');
+                res.body.result.enrollment_per_school_level.should.be.a('array');
+                // test response attributes for school
+                res.body.result.school.should.a('array');
+                res.body.result.school.forEach((row) => {
+                    row.should.be.a('object');
+                    row.should.have.property('name');
+                    row.should.have.property('location');
+                    row.should.have.property('total');
+                    row.should.have.property('census_year');
+                });
+                // test response attributes for school_per_location
+                res.body.result.school_per_location.forEach((row) => {
+                    row.should.be.a('object');
+                    row.should.have.property('name');
+                    row.should.have.property('location');
+                    row.should.have.property('total');
+                    row.should.have.property('census_year');
+                });
+                // test response attributes for enrollment
+                res.body.result.enrollment.forEach((row) => {
+                    row.should.be.a('object');
+                    row.should.have.property('name');
+                    row.should.have.property('total');
+                    row.should.have.property('census_year');
+                });
+                // test response attributes for enrollment_per_adm_dep
+                res.body.result.enrollment_per_adm_dep.forEach((row) => {
+                    row.should.be.a('object');
+                    row.should.have.property('name');
+                    row.should.have.property('total');
+                    row.should.have.property('census_year');
+                    row.should.have.property('adm_dependency');
+                });
+                // test response attributes for enrollment_per_school_level
+                res.body.result.enrollment_per_school_level.forEach((row) => {
+                    row.should.be.a('object');
+                    row.should.have.property('name');
+                    row.should.have.property('total');
+                    row.should.have.property('census_year');
+                    row.should.have.property('school_level');
+                });
+                done();
+            });
+    }).timeout(testTimeout);
+
+    it('should return the correct format of enrollments per school level', (done) => {
+        chai.request(server)
+            .get('/api/v1/location/educational/school_level')
+            .end((err, res) => {
+                res.should.have.status(200);
+                // test response format
+                res.should.be.json;
+                // test for result attribute in the response
+                res.body.should.have.property('result');
+                res.body.result.should.be.a('array');
+                // test response attributes for school
+                res.body.result.forEach((row) => {
+                    row.should.be.a('object');
+                    row.should.have.property('degree');
+                    row.should.have.property('census_year');
+                    row.should.have.property('table');
+                    row.table.should.be.a('array');
+                    row.table.forEach((tableRow) => {
+                        tableRow.should.be.a('object');
+                        tableRow.should.have.property('title');
+                        tableRow.should.have.property('value');
+                        tableRow.title.should.be.a('String');
+                        tableRow.value.should.be.a('Number');
+                    });
+                });
+                done();
+            });
+    }).timeout(testTimeout);
+
+    it('should return the correct format of enrollments per school level for a region', (done) => {
+        chai.request(server)
+            .get('/api/v1/location/educational/school_level/region/1')
+            .end((err, res) => {
+                res.should.have.status(200);
+                // test response format
+                res.should.be.json;
+                // test for result attribute in the response
+                res.body.should.have.property('result');
+                res.body.result.should.be.a('array');
+                // test response attributes for school
+                res.body.result.forEach((row) => {
+                    row.should.be.a('object');
+                    row.should.have.property('degree');
+                    row.should.have.property('census_year');
+                    row.should.have.property('table');
+                    row.table.should.be.a('array');
+                    row.table.forEach((tableRow) => {
+                        tableRow.should.be.a('object');
+                        tableRow.should.have.property('title');
+                        tableRow.should.have.property('value');
+                        tableRow.title.should.be.a('String');
+                        tableRow.value.should.be.a('Number');
+                    });
+                });
+                done();
+            });
+    }).timeout(testTimeout);
+
+    it('should return the correct format of enrollments per school level for a state', (done) => {
+        chai.request(server)
+            .get('/api/v1/location/educational/school_level/state/42')
+            .end((err, res) => {
+                res.should.have.status(200);
+                // test response format
+                res.should.be.json;
+                // test for result attribute in the response
+                res.body.should.have.property('result');
+                res.body.result.should.be.a('array');
+                // test response attributes for school
+                res.body.result.forEach((row) => {
+                    row.should.be.a('object');
+                    row.should.have.property('degree');
+                    row.should.have.property('census_year');
+                    row.should.have.property('table');
+                    row.table.should.be.a('array');
+                    row.table.forEach((tableRow) => {
+                        tableRow.should.be.a('object');
+                        tableRow.should.have.property('title');
+                        tableRow.should.have.property('value');
+                        tableRow.title.should.be.a('String');
+                        tableRow.value.should.be.a('Number');
+                    });
+                });
+                done();
+            });
+    }).timeout(testTimeout);
+
+    it('should return the correct format of enrollments per school level for a city', (done) => {
+        chai.request(server)
+            .get('/api/v1/location/educational/school_level/state/4106902')
+            .end((err, res) => {
+                res.should.have.status(200);
+                // test response format
+                res.should.be.json;
+                // test for result attribute in the response
+                res.body.should.have.property('result');
+                res.body.result.should.be.a('array');
+                // test response attributes for school
+                res.body.result.forEach((row) => {
+                    row.should.be.a('object');
+                    row.should.have.property('degree');
+                    row.should.have.property('census_year');
+                    row.should.have.property('table');
+                    row.table.should.be.a('array');
+                    row.table.forEach((tableRow) => {
+                        tableRow.should.be.a('object');
+                        tableRow.should.have.property('title');
+                        tableRow.should.have.property('value');
+                        tableRow.title.should.be.a('String');
+                        tableRow.value.should.be.a('Number');
+                    });
+                });
+                done();
+            });
+    }).timeout(testTimeout);
+});
diff --git a/src/test/region.js b/src/test/region.js
new file mode 100644
index 0000000000000000000000000000000000000000..12cf3d09fa82ce3b2f030d26d4997cf820aa2932
--- /dev/null
+++ b/src/test/region.js
@@ -0,0 +1,56 @@
+process.env.NODE_ENV = 'test';
+
+const chai = require('chai');
+
+const dirtyChai = require('dirty-chai');
+
+chai.use(dirtyChai);
+
+const chaiXml = require('chai-xml');
+
+chai.use(chaiXml);
+
+const chaiHttp = require('chai-http');
+
+const assert = chai.assert;
+
+const expect = chai.expect;
+
+const should = chai.should(); // actually call the function
+
+const libs = `${process.cwd()}/libs`;
+
+const server = require(`${libs}/app`);
+
+chai.use(chaiHttp);
+
+describe('request regions', () => {
+    it('should list all regions', (done) => {
+        chai.request(server)
+            .get('/api/v1/region')
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.json;
+                res.body.should.have.property('result');
+                res.body.result.should.be.a('array');
+                res.body.result[0].should.have.property('pk_regiao_id');
+                res.body.result[0].should.have.property('nome');
+                done();
+            });
+    });
+
+    it('should list region by id', (done) => {
+        chai.request(server)
+            .get('/api/v1/region?filter=id:1')
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.json;
+                res.body.should.have.property('result');
+                res.body.result.should.be.a('array');
+                res.body.result.should.have.length(1);
+                res.body.result[0].should.have.property('pk_regiao_id');
+                res.body.result[0].should.have.property('nome');
+                done();
+            });
+    });
+});
diff --git a/src/test/response.js b/src/test/response.js
new file mode 100644
index 0000000000000000000000000000000000000000..bdafc9d89eefa01966bc4171ebcbd373924479cf
--- /dev/null
+++ b/src/test/response.js
@@ -0,0 +1,55 @@
+process.env.NODE_ENV = 'test';
+
+const chai = require('chai');
+
+const dirtyChai = require('dirty-chai');
+
+chai.use(dirtyChai);
+
+const chaiXml = require('chai-xml');
+
+chai.use(chaiXml);
+
+const chaiHttp = require('chai-http');
+
+const assert = chai.assert;
+
+const expect = chai.expect;
+
+const should = chai.should(); // actually call the function
+
+const libs = `${process.cwd()}/libs`;
+
+const server = require(`${libs}/app`);
+
+chai.use(chaiHttp);
+
+
+describe('test response', () => {
+    it('should list all regions in json', (done) => {
+        chai.request(server)
+            .get('/api/v1/region')
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.json;
+                done();
+            });
+    });
+    it('should list all regions in xml', (done) => {
+        chai.request(server)
+            .get('/api/v1/region?format=xml')
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.xml;
+                done();
+            });
+    });
+    it('should list all regions in csv', (done) => {
+        chai.request(server)
+            .get('/api/v1/region?format=csv')
+            .end((err, res) => {
+                res.should.have.status(200);
+                done();
+            });
+    });
+});
diff --git a/src/test/school.js b/src/test/school.js
new file mode 100644
index 0000000000000000000000000000000000000000..56b736752d87791dc1be8266389c58806b50d3db
--- /dev/null
+++ b/src/test/school.js
@@ -0,0 +1,72 @@
+process.env.NODE_ENV = 'test';
+
+const chai = require('chai');
+
+const dirtyChai = require('dirty-chai');
+
+chai.use(dirtyChai);
+
+const chaiXml = require('chai-xml');
+
+chai.use(chaiXml);
+
+const chaiHttp = require('chai-http');
+
+const assert = chai.assert;
+
+const expect = chai.expect;
+
+const should = chai.should(); // actually call the function
+
+const libs = `${process.cwd()}/libs`;
+
+const server = require(`${libs}/app`);
+
+chai.use(chaiHttp);
+
+describe('request schools', () => {
+    it('should list a school by id', (done) => {
+        chai.request(server)
+            .get('/api/v1/school?filter=id:11000023')
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.json;
+                res.body.should.have.property('result');
+                res.body.result.should.be.a('array');
+                res.body.result[0].should.have.property('cod_entidade');
+                res.body.result[0].should.have.property('year');
+                //res.body.result[0].should.have.property('nome_entidade');
+                done();
+            });
+    });
+
+    it('should list all schools from a state', (done) => {
+        chai.request(server)
+            .get('/api/v1/school?filter=state:41')
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.json;
+                res.body.should.have.property('result');
+                res.body.result.should.be.a('array');
+                res.body.result[0].should.have.property('cod_entidade');
+                res.body.result[0].should.have.property('year');
+                //res.body.result[0].should.have.property('nome_entidade');
+                done();
+            });
+    });
+
+    it('should list all schools from a city', (done) => {
+        chai.request(server)
+            .get('/api/v1/school?filter=city:4102802')
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.json;
+                res.body.should.have.property('result');
+                res.body.result.should.be.a('array');
+                res.body.result[0].should.have.property('cod_entidade');
+                res.body.result[0].should.have.property('year');
+                //res.body.result[0].should.have.property('nome_entidade');
+                done();
+            })
+    })
+});
diff --git a/src/test/test.js b/src/test/simulation.js
similarity index 61%
rename from src/test/test.js
rename to src/test/simulation.js
index 91f78b86d1900e6aa7d6639add7a9061f3c055d8..a9349256b29bc20068eb74b0172e15d0dbe28937 100644
--- a/src/test/test.js
+++ b/src/test/simulation.js
@@ -27,378 +27,6 @@ const Simulation = require('../libs/models/simulation');
 const User = require('../libs/models/user');
 
 chai.use(chaiHttp);
-
-describe('API is running', () => {
-    it('should respond it\'s running', (done) => {
-        chai.request(server)
-            .get('/api/v1')
-            .end((err, res) => {
-                res.should.have.status(200);
-                res.should.be.json;
-                res.body.should.have.property('msg');
-                done();
-            })
-    });
-});
-
-describe('request enrollments', () => {
-    it('should list the year range', (done) => {
-        chai.request(server)
-            .get('/api/v1/enrollment/year_range')
-            .end((err, res) => {
-                res.should.have.status(200);
-                res.should.be.json;
-                res.body.should.have.property('result');
-                res.body.result.should.be.a('array');
-                res.body.result[0].should.have.property('start_year');
-                res.body.result[0].should.have.property('end_year');
-                done();
-            });
-    });
-
-    it('should list the education level', (done) => {
-        chai.request(server)
-            .get('/api/v1/enrollment/education_level')
-            .end((err, res) => {
-                res.should.have.status(200);
-                res.should.be.json;
-                res.body.should.have.property('result');
-                res.body.result.should.be.a('array');
-                res.body.result[0].should.have.property('id');
-                res.body.result[0].should.have.property('name');
-                done();
-            });
-    });
-
-    it('should list the administrative dependencies', (done) => {
-        chai.request(server)
-            .get('/api/v1/enrollment/adm_dependency ')
-            .end((err, res) => {
-                res.should.have.status(200);
-                res.should.be.json;
-                res.body.should.have.property('result');
-                res.body.result.should.be.a('array');
-                res.body.result[0].should.have.property('id');
-                res.body.result[0].should.have.property('name');
-                done();
-            });
-    });
-
-    it('should list enrollments', (done) => {
-        chai.request(server)
-            .get('/api/v1/enrollment')
-            .end((err, res) => {
-                res.should.have.status(200);
-                res.should.be.json;
-                res.body.should.have.property('result');
-                res.body.result.should.be.a('array');
-                res.body.result[0].should.have.property('name');
-                res.body.result[0].should.have.property('total');
-                done();
-            });
-    });
-
-    it('should list enrollments with valid filters', (done) => {
-        chai.request(server)
-            .get('/api/v1/enrollment?filter=min_year:2010,state:41')
-            .end((err, res) => {
-                res.should.have.status(200);
-                res.should.be.json;
-                res.body.should.have.property('result');
-                res.body.result.should.be.a('array');
-                res.body.result[0].should.have.property('name');
-                res.body.result[0].should.have.property('total');
-                done();
-            });
-    });
-
-    it('should list enrollments with invalid filters', (done) => {
-        chai.request(server)
-            .get('/api/v1/enrollment?filter=foo:2010,bar:41')
-            .end((err, res) => {
-                res.should.have.status(200);
-                res.should.be.json;
-                res.body.should.have.property('result');
-                res.body.result.should.be.a('array');
-                res.body.result[0].should.have.property('name');
-                res.body.result[0].should.have.property('total');
-                done();
-            });
-    });
-
-    it('should list enrollments with valid dimensions', (done) => {
-        chai.request(server)
-            .get('/api/v1/enrollment?dims=region,state,adm_dependency,location&filter=min_year:2014,region:4')
-            .end((err, res) => {
-                res.should.have.status(200);
-                res.should.be.json;
-                res.body.should.have.property('result');
-                res.body.result.should.be.a('array');
-                res.body.result[0].should.have.property('region_name');
-                res.body.result[0].should.have.property('state_name');
-                res.body.result[0].should.have.property('adm_dependency_name');
-                res.body.result[0].should.have.property('location_name');
-                res.body.result[0].should.have.property('total');
-                done();
-            });
-    });
-
-    it('should list enrollments with invalid dimensions', (done) => {
-        chai.request(server)
-            .get('/api/v1/enrollment?dims=foo,bar')
-            .end((err, res) => {
-                res.should.have.status(200);
-                res.should.be.json;
-                res.body.should.have.property('result');
-                res.body.result.should.be.a('array');
-                res.body.result[0].should.have.property('name');
-                res.body.result[0].should.have.property('total');
-                done();
-            });
-    });
-
-    it('should list enrollments with valid dimensions and filters', (done) => {
-        chai.request(server)
-            .get('/api/v1/enrollment?dims=region,state,education_level,school&filter=min_year:2013,max_year:2014,city:4106902')
-            .end((err, res) => {
-                res.should.have.status(200);
-                res.should.be.json;
-                res.body.should.have.property('result');
-                res.body.result.should.be.a('array');
-                res.body.result[0].should.have.property('region_name');
-                res.body.result[0].should.have.property('state_name');
-                res.body.result[0].should.have.property('school_name');
-                res.body.result[0].should.have.property('education_level');
-                res.body.result[0].should.have.property('total');
-                res.body.result[0].should.have.property('year');
-                done();
-            });
-    });
-
-    // it('should list enrollments using all dimensions and filters', (done) => {
-    //     chai.request(server)
-    //         .get('/api/v1/enrollment?dims=region,state,city,education_level,school,adm_dependency,location&filter=min_year:2013,max_year:2014,city:4106902,adm_dependency:3,location:1,education_level:99')
-    //         .end((err, res) => {
-    //             res.should.have.status(200);
-    //             res.should.be.json;
-    //             res.body.should.have.property('result');
-    //             res.body.result.should.be.a('array');
-    //             res.body.result[0].should.have.property('region_name');
-    //             res.body.result[0].should.have.property('state_name');
-    //             res.body.result[0].should.have.property('school_name');
-    //             res.body.result[0].should.have.property('education_level');
-    //             res.body.result[0].should.have.property('location_name');
-    //             res.body.result[0].should.have.property('adm_dependency_name');
-    //             res.body.result[0].should.have.property('total');
-    //             res.body.result[0].should.have.property('year');
-    //             done();
-    //         });
-    // });
-
-
-});
-
-describe('request regions', () => {
-    it('should list all regions', (done) => {
-        chai.request(server)
-            .get('/api/v1/region')
-            .end((err, res) => {
-                res.should.have.status(200);
-                res.should.be.json;
-                res.body.should.have.property('result');
-                res.body.result.should.be.a('array');
-                res.body.result[0].should.have.property('pk_regiao_id');
-                res.body.result[0].should.have.property('nome');
-                done();
-            });
-    });
-
-    it('should list region by id', (done) => {
-        chai.request(server)
-            .get('/api/v1/region?filter=id:1')
-            .end((err, res) => {
-                res.should.have.status(200);
-                res.should.be.json;
-                res.body.should.have.property('result');
-                res.body.result.should.be.a('array');
-                res.body.result.should.have.length(1);
-                res.body.result[0].should.have.property('pk_regiao_id');
-                res.body.result[0].should.have.property('nome');
-                done();
-            });
-    });
-});
-
-describe('request states', () => {
-    it('should list all states', (done) => {
-        chai.request(server)
-            .get('/api/v1/state')
-            .end((err, res) => {
-                res.should.have.status(200);
-                res.should.be.json;
-                res.body.should.have.property('result');
-                res.body.result.should.be.a('array');
-                res.body.result[0].should.have.property('pk_estado_id');
-                res.body.result[0].should.have.property('fk_regiao_id');
-                res.body.result[0].should.have.property('nome');
-                done();
-            });
-    });
-
-    it('should list a state by id', (done) => {
-        chai.request(server)
-            .get('/api/v1/state?filter=id:11')
-            .end((err, res) => {
-                res.should.have.status(200);
-                res.should.be.json;
-                res.body.should.have.property('result');
-                res.body.result.should.be.a('array');
-                res.body.result.should.have.length(1);
-                res.body.result[0].should.have.property('pk_estado_id');
-                res.body.result[0].should.have.property('fk_regiao_id');
-                res.body.result[0].should.have.property('nome');
-                done();
-            });
-    });
-
-    it('should list states by region id', (done) => {
-        chai.request(server)
-            .get('/api/v1/state?filter=region:2')
-            .end((err, res) => {
-                res.should.have.status(200);
-                res.should.be.json;
-                res.body.should.have.property('result');
-                res.body.result.should.be.a('array');
-                res.body.result[0].should.have.property('pk_estado_id');
-                res.body.result[0].should.have.property('fk_regiao_id');
-                res.body.result[0].should.have.property('nome');
-                done();
-            });
-    });
-});
-
-describe('request cities', () => {
-    it('should list all cities', (done) => {
-        chai.request(server)
-            .get('/api/v1/city')
-            .end((err, res) => {
-                res.should.have.status(200);
-                res.should.be.json;
-                res.body.should.have.property('result');
-                res.body.result.should.be.a('array');
-                res.body.result[0].should.have.property('pk_cod_ibge');
-                res.body.result[0].should.have.property('nome');
-                res.body.result[0].should.have.property('fk_estado_id');
-                done();
-            });
-    });
-
-    it('should list a city by id', (done) => {
-        chai.request(server)
-            .get('/api/v1/city?filter=id:4106902')
-            .end((err, res) => {
-                res.should.have.status(200);
-                res.should.be.json;
-                res.body.should.have.property('result');
-                res.body.result.should.be.a('array');
-                res.body.result[0].should.have.property('pk_cod_ibge');
-                res.body.result[0].should.have.property('fk_estado_id');
-                res.body.result[0].should.have.property('nome');
-                done();
-            });
-    });
-
-    it('should list all cities from a state', (done) => {
-        chai.request(server)
-            .get('/api/v1/city?filter=state:41')
-            .end((err, res) => {
-                res.should.have.status(200);
-                res.should.be.json;
-                res.body.should.have.property('result');
-                res.body.result.should.be.a('array');
-                res.body.result[0].should.have.property('pk_cod_ibge');
-                res.body.result[0].should.have.property('fk_estado_id');
-                res.body.result[0].should.have.property('nome');
-                done();
-            })
-    })
-});
-
-describe('request schools', () => {
-    it('should list a school by id', (done) => {
-        chai.request(server)
-            .get('/api/v1/school?filter=id:41000021')
-            .end((err, res) => {
-                res.should.have.status(200);
-                res.should.be.json;
-                res.body.should.have.property('result');
-                res.body.result.should.be.a('array');
-                res.body.result[0].should.have.property('year');
-                res.body.result[0].should.have.property('cod_entidade');
-                done();
-            });
-    });
-
-    it('should list all schools from a state', (done) => {
-        chai.request(server)
-            .get('/api/v1/school?filter=state:41')
-            .end((err, res) => {
-                res.should.have.status(200);
-                res.should.be.json;
-                res.body.should.have.property('result');
-                res.body.result.should.be.a('array');
-                res.body.result[0].should.have.property('cod_entidade');
-                res.body.result[0].should.have.property('year');
-                done();
-            });
-    });
-
-    it('should list all schools from a city', (done) => {
-        chai.request(server)
-            .get('/api/v1/school?filter=city:4106902')
-            .end((err, res) => {
-                res.should.have.status(200);
-                res.should.be.json;
-                res.body.should.have.property('result');
-                res.body.result.should.be.a('array');
-                res.body.result[0].should.have.property('cod_entidade');
-                res.body.result[0].should.have.property('year');
-                done();
-            })
-    })
-});
-
-describe('test response', () => {
-    it('should list all regions in json', (done) => {
-        chai.request(server)
-            .get('/api/v1/region')
-            .end((err, res) => {
-                res.should.have.status(200);
-                res.should.be.json;
-                done();
-            });
-    });
-
-    it('should list all regions in xml', (done) => {
-        chai.request(server)
-            .get('/api/v1/region?format=xml')
-            .end((err, res) => {
-                res.should.have.status(200);
-                res.should.be.xml;
-                done();
-            });
-    });
-
-    it('should list all regions in csv', (done) => {
-        chai.request(server)
-            .get('/api/v1/region?format=csv')
-            .end((err, res) => {
-                res.should.have.status(200);
-                done();
-            });
-    });
-});
-
 describe('Requires a simulation', () => {
     let newSimulation;
 
@@ -782,9 +410,6 @@ describe('Saves a user', () => {
 
     beforeEach(() => {
         User.remove({}, (err) => {
-            if(err) {
-                console.log('Error while purging: ' + err);
-            }
             console.log('Test collection purged');
         });
     });
@@ -1242,203 +867,3 @@ describe('Saves a user', () => {
     });
 
 });
-
-describe('Authenticates a user', () => {
-
-    beforeEach(() => {
-        User.remove({}, (err) => {
-            console.log('Test collection purged');
-        });
-    });
-
-    it('should authenticate a user', (done) => {
-        let newUser = new User();
-
-        newUser.email = 'lorem@ipsum.com';
-        newUser.password = '123mudar';
-        newUser.name = 'Gute';
-        newUser.cpf = '08236017907';
-        newUser.schooling = 'Doutorado';
-        newUser.course = 'Ciência da Computação';
-        newUser.segment = 'Comunidade acadêmica';
-        newUser.role = 'Pesquisador';
-        newUser.institution_name = 'UFPR';
-        newUser.state = 'PR';
-        newUser.city = 'Curitiba';
-
-        newUser.save((err) => {
-            if (err) {
-                console.log('MongoDB error:' + err);
-            }
-        }).then(function(newuser){
-            chai.request(server)
-                .post('/api/v1/user/authenticate')
-                .set('content-type', 'application/x-www-form-urlencoded')
-                .set('x-apicache-bypass', 'true')
-                .send({email: 'lorem@ipsum.com',
-                       password: '123mudar'})
-                .end((err, res) => {
-                    let token;
-
-                    res.should.have.status(200);
-                    res.should.be.json;
-                    res.body.should.have.property('success');
-                    res.body.success.should.equal(true);
-                    res.body.should.have.property('token');
-                    token = res.body.token;
-                    token.substr(0, 3).should.equal('JWT');
-                    done();
-                });
-            });
-    });
-
-    it('should not authenticate a user with wrong password', (done) => {
-        let newUser = new User();
-
-        newUser.email = 'lorem@ipsum.com';
-        newUser.password = '123mudar';
-        newUser.name = 'Gute';
-        newUser.cpf = '08236017907';
-        newUser.schooling = 'Doutorado';
-        newUser.course = 'Ciência da Computação';
-        newUser.segment = 'Comunidade acadêmica';
-        newUser.role = 'Pesquisador';
-        newUser.institution_name = 'UFPR';
-        newUser.state = 'PR';
-        newUser.city = 'Curitiba';
-
-        newUser.save((err) => {
-            if (err) {
-                console.log('MongoDB error:' + err);
-            }
-        }).then(function(newuser){
-            chai.request(server)
-                .post('/api/v1/user/authenticate')
-                .set('content-type', 'application/x-www-form-urlencoded')
-                .set('x-apicache-bypass', 'true')
-                .send({email: 'lorem@ipsum.com',
-                       password: 'umasenhaerrada'})
-                .end((err, res) => {
-                    res.should.have.status(200);
-                    res.should.be.json;
-                    res.body.should.have.property('success');
-                    res.body.success.should.equal(false);
-                    res.body.should.have.property('msg');
-                    res.body.msg.should.equal('A Senha informada é inválida.')
-                    done();
-                });
-            });
-    });
-
-    it('should not authenticate a user with wrong email', (done) => {
-        let newUser = new User();
-
-        newUser.email = 'lorem@ipsum.com';
-        newUser.password = '123mudar';
-        newUser.name = 'Gute';
-        newUser.cpf = '08236017907';
-        newUser.schooling = 'Doutorado';
-        newUser.course = 'Ciência da Computação';
-        newUser.segment = 'Comunidade acadêmica';
-        newUser.role = 'Pesquisador';
-        newUser.institution_name = 'UFPR';
-        newUser.state = 'PR';
-        newUser.city = 'Curitiba';
-
-        newUser.save((err) => {
-            if (err) {
-                console.log('MongoDB error:' + err);
-            }
-        }).then(function(newuser){
-            chai.request(server)
-                .post('/api/v1/user/authenticate')
-                .set('content-type', 'application/x-www-form-urlencoded')
-                .set('x-apicache-bypass', 'true')
-                .send({email: 'dolor@ipsum.com',
-                       password: '123mudar'})
-                .end((err, res) => {
-                    res.should.have.status(200);
-                    res.should.be.json;
-                    res.body.should.have.property('success');
-                    res.body.success.should.equal(false);
-                    res.body.should.have.property('msg');
-                    res.body.msg.should.equal('O Email informado não está cadastrado.')
-                    done();
-                });
-            });
-    });
-
-    it('should not authenticate a user with missing email', (done) => {
-        let newUser = new User();
-
-        newUser.email = 'lorem@ipsum.com';
-        newUser.password = '123mudar';
-        newUser.name = 'Gute';
-        newUser.cpf = '08236017907';
-        newUser.schooling = 'Doutorado';
-        newUser.course = 'Ciência da Computação';
-        newUser.segment = 'Comunidade acadêmica';
-        newUser.role = 'Pesquisador';
-        newUser.institution_name = 'UFPR';
-        newUser.state = 'PR';
-        newUser.city = 'Curitiba';
-
-        newUser.save((err) => {
-            if (err) {
-                console.log('MongoDB error:' + err);
-            }
-        }).then(function(newuser){
-            chai.request(server)
-                .post('/api/v1/user/authenticate')
-                .set('content-type', 'application/x-www-form-urlencoded')
-                .set('x-apicache-bypass', 'true')
-                .send({password: '123mudar'})
-                .end((err, res) => {
-                    res.should.have.status(200);
-                    res.should.be.json;
-                    res.body.should.have.property('success');
-                    res.body.success.should.equal(false);
-                    res.body.should.have.property('msg');
-                    res.body.msg.should.equal('O campo Email é obrigatório.')
-                    done();
-                });
-            });
-    });
-
-    it('should not authenticate a user with missing password', (done) => {
-        let newUser = new User();
-
-        newUser.email = 'lorem@ipsum.com';
-        newUser.password = '123mudar';
-        newUser.name = 'Gute';
-        newUser.cpf = '08236017907';
-        newUser.schooling = 'Doutorado';
-        newUser.course = 'Ciência da Computação';
-        newUser.segment = 'Comunidade acadêmica';
-        newUser.role = 'Pesquisador';
-        newUser.institution_name = 'UFPR';
-        newUser.state = 'PR';
-        newUser.city = 'Curitiba';
-
-        newUser.save((err) => {
-            if (err) {
-                console.log('MongoDB error:' + err);
-            }
-        }).then(function(newuser){
-            chai.request(server)
-                .post('/api/v1/user/authenticate')
-                .set('content-type', 'application/x-www-form-urlencoded')
-                .set('x-apicache-bypass', 'true')
-                .send({email:'lorem@ipsum.com'})
-                .end((err, res) => {
-                    res.should.have.status(200);
-                    res.should.be.json;
-                    res.body.should.have.property('success');
-                    res.body.success.should.equal(false);
-                    res.body.should.have.property('msg');
-                    res.body.msg.should.equal('O campo Senha é obrigatório.')
-                    done();
-                });
-            });
-    });
-});
diff --git a/src/test/state.js b/src/test/state.js
new file mode 100644
index 0000000000000000000000000000000000000000..d3794f98df89b6a2f410c93107daa7033b019792
--- /dev/null
+++ b/src/test/state.js
@@ -0,0 +1,74 @@
+process.env.NODE_ENV = 'test';
+
+const chai = require('chai');
+
+const dirtyChai = require('dirty-chai');
+
+chai.use(dirtyChai);
+
+const chaiXml = require('chai-xml');
+
+chai.use(chaiXml);
+
+const chaiHttp = require('chai-http');
+
+const assert = chai.assert;
+
+const expect = chai.expect;
+
+const should = chai.should(); // actually call the function
+
+const libs = `${process.cwd()}/libs`;
+
+const server = require(`${libs}/app`);
+
+chai.use(chaiHttp);
+
+
+describe('request states', () => {
+    it('should list all states', (done) => {
+        chai.request(server)
+            .get('/api/v1/state')
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.json;
+                res.body.should.have.property('result');
+                res.body.result.should.be.a('array');
+                res.body.result[0].should.have.property('pk_estado_id');
+                res.body.result[0].should.have.property('fk_regiao_id');
+                res.body.result[0].should.have.property('nome');
+                done();
+            });
+    });
+
+    it('should list a state by id', (done) => {
+        chai.request(server)
+            .get('/api/v1/state?filter=id:11')
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.json;
+                res.body.should.have.property('result');
+                res.body.result.should.be.a('array');
+                res.body.result.should.have.length(1);
+                res.body.result[0].should.have.property('pk_estado_id');
+                res.body.result[0].should.have.property('fk_regiao_id');
+                res.body.result[0].should.have.property('nome');
+                done();
+            });
+    });
+
+    it('should list states by region id', (done) => {
+        chai.request(server)
+            .get('/api/v1/state?filter=region:1')
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.json;
+                res.body.should.have.property('result');
+                res.body.result.should.be.a('array');
+                res.body.result[0].should.have.property('pk_estado_id');
+                res.body.result[0].should.have.property('fk_regiao_id');
+                res.body.result[0].should.have.property('nome');
+                done();
+            });
+    });
+});
diff --git a/src/test/user.js b/src/test/user.js
new file mode 100644
index 0000000000000000000000000000000000000000..04ab8e9e58a521ff2b555c891fa53a919066c2d9
--- /dev/null
+++ b/src/test/user.js
@@ -0,0 +1,626 @@
+process.env.NODE_ENV = 'test';
+
+const chai = require('chai');
+
+const dirtyChai = require('dirty-chai');
+
+chai.use(dirtyChai);
+
+const chaiXml = require('chai-xml');
+
+chai.use(chaiXml);
+
+const chaiHttp = require('chai-http');
+
+const assert = chai.assert;
+
+const expect = chai.expect;
+
+const should = chai.should(); // actually call the function
+
+const libs = `${process.cwd()}/libs`;
+
+const server = require(`${libs}/app`);
+
+const mongoose = require('../libs/db/mongoose');
+//const Simulation = require('../libs/models/simulation');
+const User = require('../libs/models/user');
+
+chai.use(chaiHttp);
+
+describe('Saves a user', () => {
+    beforeEach(() => {
+        User.remove({}, (err) => {
+            console.log('Test collection purged')
+        });
+    });
+
+    it('should save a user', (done) => {
+        let newUser = {};
+        newUser.email = 'lorem@ipsum.com';
+        newUser.password = '123mudar';
+        newUser.name = 'Gute';
+        newUser.cpf = '08236017907';
+        newUser.schooling = 'Doutorado';
+        newUser.course = 'Ciência da Computação';
+        newUser.segment = 'Comunidade acadêmica';
+        newUser.role = 'Pesquisador';
+        newUser.institution_name = 'UFPR';
+        newUser.state = 'PR';
+        newUser.city = 'Curitiba';
+
+        chai.request(server)
+        .post('/api/v1/user/')
+        .set('content-type', 'application/x-www-form-urlencoded')
+        .set('x-apicache-bypass', 'true')
+        .send(newUser)
+        .end((err, res) => {
+            res.should.have.status(200);
+            res.should.be.json;
+            res.body.should.have.property('success');
+            res.body.success.should.equal(true);
+            res.body.should.have.property('msg');
+            res.body.msg.should.be.equal('Usuário cadastrado com sucesso!');
+            done();
+        });
+    });
+
+    it('should not save a user without email', (done) => {
+        let newUser = {};
+        newUser.email = null;
+        newUser.password = '123mudar';
+        newUser.name = 'Gute';
+        newUser.cpf = '08236017907';
+        newUser.schooling = 'Doutorado';
+        newUser.course = 'Ciência da Computação';
+        newUser.segment = 'Comunidade acadêmica';
+        newUser.role = 'Pesquisador';
+        newUser.institution_name = 'UFPR';
+        newUser.state = 'PR';
+        newUser.city = 'Curitiba';
+
+        chai.request(server)
+        .post('/api/v1/user/')
+        .set('content-type', 'application/x-www-form-urlencoded')
+        .set('x-apicache-bypass', 'true')
+        .send(newUser)
+        .end((err, res) => {
+            res.should.have.status(200);
+            res.should.be.json;
+            res.body.should.have.property('success');
+            res.body.success.should.equal(false);
+            res.body.should.have.property('msg');
+            res.body.msg.should.be.equal('O campo Email é obrigatório.');
+            done();
+        });
+    });
+
+    it('should not save a user without password', (done) => {
+        let newUser = {};
+        newUser.email = 'lorem@ipsum.com';
+        newUser.name = 'Gute';
+        newUser.cpf = '08236017907';
+        newUser.schooling = 'Doutorado';
+        newUser.course = 'Ciência da Computação';
+        newUser.segment = 'Comunidade acadêmica';
+        newUser.role = 'Pesquisador';
+        newUser.institution_name = 'UFPR';
+        newUser.state = 'PR';
+        newUser.city = 'Curitiba';
+
+        chai.request(server)
+        .post('/api/v1/user/')
+        .set('content-type', 'application/x-www-form-urlencoded')
+        .set('x-apicache-bypass', 'true')
+        .send(newUser)
+        .end((err, res) => {
+            res.should.have.status(200);
+            res.should.be.json;
+            res.body.should.have.property('success');
+            res.body.success.should.equal(false);
+            res.body.should.have.property('msg');
+            res.body.msg.should.be.equal('O campo Senha é obrigatório.');
+            done();
+        });
+    });
+
+    it('should not save a user with invalid email', (done) => {
+        let newUser = {};
+        newUser.email = 'invalid email';
+        newUser.password = '123mudar';
+        newUser.name = 'Gute';
+        newUser.cpf = '08236017907';
+        newUser.schooling = 'Doutorado';
+        newUser.course = 'Ciência da Computação';
+        newUser.segment = 'Comunidade acadêmica';
+        newUser.role = 'Pesquisador';
+        newUser.institution_name = 'UFPR';
+        newUser.state = 'PR';
+        newUser.city = 'Curitiba';
+
+        chai.request(server)
+        .post('/api/v1/user/')
+        .set('content-type', 'application/x-www-form-urlencoded')
+        .set('x-apicache-bypass', 'true')
+        .send(newUser)
+        .end((err, res) => {
+            res.should.have.status(200);
+            res.should.be.json;
+            res.body.should.have.property('success');
+            res.body.success.should.equal(false);
+            res.body.should.have.property('msg');
+            res.body.msg.should.be.equal('O email informado é inválido.');
+            done();
+        });
+    });
+
+    it('should not save a user without a name', (done) => {
+        let newUser = {};
+        newUser.email = 'lorem@ipsum.com';
+        newUser.password = '123mudar';
+        newUser.cpf = '08236017907';
+        newUser.schooling = 'Doutorado';
+        newUser.course = 'Ciência da Computação';
+        newUser.segment = 'Comunidade acadêmica';
+        newUser.role = 'Pesquisador';
+        newUser.institution_name = 'UFPR';
+        newUser.state = 'PR';
+        newUser.city = 'Curitiba';
+
+        chai.request(server)
+        .post('/api/v1/user/')
+        .set('content-type', 'application/x-www-form-urlencoded')
+        .set('x-apicache-bypass', 'true')
+        .send(newUser)
+        .end((err, res) => {
+            res.should.have.status(200);
+            res.should.be.json;
+            res.body.should.have.property('success');
+            res.body.success.should.equal(false);
+            res.body.should.have.property('msg');
+            res.body.msg.should.be.equal('O campo Nome é obrigatório.');
+            done();
+        });
+    });
+
+    it('should not save a user without CPF', (done) => {
+        let newUser = {};
+        newUser.email = 'lorem@ipsum.com';
+        newUser.password = '123mudar';
+        newUser.name = 'Gute';
+        newUser.schooling = 'Doutorado';
+        newUser.course = 'Ciência da Computação';
+        newUser.segment = 'Comunidade acadêmica';
+        newUser.role = 'Pesquisador';
+        newUser.institution_name = 'UFPR';
+        newUser.state = 'PR';
+        newUser.city = 'Curitiba';
+
+        chai.request(server)
+        .post('/api/v1/user/')
+        .set('content-type', 'application/x-www-form-urlencoded')
+        .set('x-apicache-bypass', 'true')
+        .send(newUser)
+        .end((err, res) => {
+            res.should.have.status(200);
+            res.should.be.json;
+            res.body.should.have.property('success');
+            res.body.success.should.equal(false);
+            res.body.should.have.property('msg');
+            res.body.msg.should.be.equal('O campo CPF é obrigatório.');
+            done();
+        });
+    });
+
+    it('should not save a user without schooling', (done) => {
+        let newUser = {};
+        newUser.email = 'lorem@ipsum.com';
+        newUser.password = '123mudar';
+        newUser.name = 'Gute';
+        newUser.cpf = '08236017907';
+        newUser.course = 'Ciência da Computação';
+        newUser.segment = 'Comunidade acadêmica';
+        newUser.role = 'Pesquisador';
+        newUser.institution_name = 'UFPR';
+        newUser.state = 'PR';
+        newUser.city = 'Curitiba';
+
+        chai.request(server)
+        .post('/api/v1/user/')
+        .set('content-type', 'application/x-www-form-urlencoded')
+        .set('x-apicache-bypass', 'true')
+        .send(newUser)
+        .end((err, res) => {
+            res.should.have.status(200);
+            res.should.be.json;
+            res.body.should.have.property('success');
+            res.body.success.should.equal(false);
+            res.body.should.have.property('msg');
+            res.body.msg.should.be.equal('O campo Escolaridade é obrigatório.');
+            done();
+        });
+    });
+
+    it('should not save a user without segment', (done) => {
+        let newUser = {};
+        newUser.email = 'lorem@ipsum.com';
+        newUser.password = '123mudar';
+        newUser.name = 'Gute';
+        newUser.cpf = '08236017907';
+        newUser.schooling = 'Doutorado';
+        newUser.course = 'Ciência da Computação';
+        newUser.role = 'Pesquisador';
+        newUser.institution_name = 'UFPR';
+        newUser.state = 'PR';
+        newUser.city = 'Curitiba';
+
+        chai.request(server)
+        .post('/api/v1/user/')
+        .set('content-type', 'application/x-www-form-urlencoded')
+        .set('x-apicache-bypass', 'true')
+        .send(newUser)
+        .end((err, res) => {
+            res.should.have.status(200);
+            res.should.be.json;
+            res.body.should.have.property('success');
+            res.body.success.should.equal(false);
+            res.body.should.have.property('msg');
+            res.body.msg.should.be.equal('O campo Segmento é obrigatório.');
+            done();
+        });
+    });
+
+    it('should not save a user without role', (done) => {
+        let newUser = {};
+        newUser.email = 'lorem@ipsum.com';
+        newUser.password = '123mudar';
+        newUser.name = 'Gute';
+        newUser.cpf = '08236017907';
+        newUser.schooling = 'Doutorado';
+        newUser.course = 'Ciência da Computação';
+        newUser.segment = 'Comunidade acadêmica';
+        newUser.institution_name = 'UFPR';
+        newUser.state = 'PR';
+        newUser.city = 'Curitiba';
+
+        chai.request(server)
+        .post('/api/v1/user/')
+        .set('content-type', 'application/x-www-form-urlencoded')
+        .set('x-apicache-bypass', 'true')
+        .send(newUser)
+        .end((err, res) => {
+            res.should.have.status(200);
+            res.should.be.json;
+            res.body.should.have.property('success');
+            res.body.success.should.equal(false);
+            res.body.should.have.property('msg');
+            res.body.msg.should.be.equal('O campo Função é obrigatório.');
+            done();
+        });
+    });
+
+    it('should not save a user without institution_name', (done) => {
+        let newUser = {};
+        newUser.email = 'lorem@ipsum.com';
+        newUser.password = '123mudar';
+        newUser.name = 'Gute';
+        newUser.cpf = '08236017907';
+        newUser.schooling = 'Doutorado';
+        newUser.course = 'Ciência da Computação';
+        newUser.segment = 'Comunidade acadêmica';
+        newUser.role = 'Pesquisador';
+        newUser.state = 'PR';
+        newUser.city = 'Curitiba';
+
+        chai.request(server)
+        .post('/api/v1/user/')
+        .set('content-type', 'application/x-www-form-urlencoded')
+        .set('x-apicache-bypass', 'true')
+        .send(newUser)
+        .end((err, res) => {
+            res.should.have.status(200);
+            res.should.be.json;
+            res.body.should.have.property('success');
+            res.body.success.should.equal(false);
+            res.body.should.have.property('msg');
+            res.body.msg.should.be.equal('O campo Instituição em que trabalha é obrigatório.');
+            done();
+        });
+    });
+
+    it('should not save a user without state', (done) => {
+        let newUser = {};
+        newUser.email = 'lorem@ipsum.com';
+        newUser.password = '123mudar';
+        newUser.name = 'Gute';
+        newUser.cpf = '08236017907';
+        newUser.schooling = 'Doutorado';
+        newUser.course = 'Ciência da Computação';
+        newUser.segment = 'Comunidade acadêmica';
+        newUser.role = 'Pesquisador';
+        newUser.institution_name = 'UFPR';
+        newUser.city = 'Curitiba';
+
+        chai.request(server)
+        .post('/api/v1/user/')
+        .set('content-type', 'application/x-www-form-urlencoded')
+        .set('x-apicache-bypass', 'true')
+        .send(newUser)
+        .end((err, res) => {
+            res.should.have.status(200);
+            res.should.be.json;
+            res.body.should.have.property('success');
+            res.body.success.should.equal(false);
+            res.body.should.have.property('msg');
+            res.body.msg.should.be.equal('O campo Estado é obrigatório.');
+            done();
+        });
+    });
+
+    it('should not save a user without city', (done) => {
+        let newUser = {};
+        newUser.email = 'lorem@ipsum.com';
+        newUser.password = '123mudar';
+        newUser.name = 'Gute';
+        newUser.cpf = '08236017907';
+        newUser.schooling = 'Doutorado';
+        newUser.course = 'Ciência da Computação';
+        newUser.segment = 'Comunidade acadêmica';
+        newUser.role = 'Pesquisador';
+        newUser.institution_name = 'UFPR';
+        newUser.state = 'Paraná';
+
+        chai.request(server)
+        .post('/api/v1/user/')
+        .set('content-type', 'application/x-www-form-urlencoded')
+        .set('x-apicache-bypass', 'true')
+        .send(newUser)
+        .end((err, res) => {
+            res.should.have.status(200);
+            res.should.be.json;
+            res.body.should.have.property('success');
+            res.body.success.should.equal(false);
+            res.body.should.have.property('msg');
+            res.body.msg.should.be.equal('O campo Cidade é obrigatório.');
+            done();
+        });
+    });
+
+})
+
+describe('Authenticates a user', () => {
+
+    beforeEach(() => {
+        User.remove({}, (err) => {
+            console.log('Test collection purged');
+        });
+    });
+
+    it('should authenticate a user', (done) => {
+        let newUser = new User();
+
+        newUser.email = 'lorem@ipsum.com';
+        newUser.password = '123mudar';
+        newUser.name = 'Gute';
+        newUser.cpf = '08236017907';
+        newUser.schooling = 'Doutorado';
+        newUser.course = 'Ciência da Computação';
+        newUser.segment = 'Comunidade acadêmica';
+        newUser.role = 'Pesquisador';
+        newUser.institution_name = 'UFPR';
+        newUser.state = 'PR';
+        newUser.city = 'Curitiba';
+
+        newUser.save((err) => {
+            if (err) {
+                console.log('MongoDB error:' + err);
+            }
+        }).then(function(newuser){
+            chai.request(server)
+            .post('/api/v1/user/authenticate')
+            .set('content-type', 'application/x-www-form-urlencoded')
+            .set('x-apicache-bypass', 'true')
+            .send({email: 'lorem@ipsum.com',
+            password: '123mudar'})
+            .end((err, res) => {
+                let token;
+
+                res.should.have.status(200);
+                res.should.be.json;
+                res.body.should.have.property('success');
+                res.body.success.should.equal(true);
+                res.body.should.have.property('token');
+                token = res.body.token;
+                token.substr(0, 3).should.equal('JWT');
+                done();
+            });
+        });
+    });
+
+    it('should not authenticate a user with wrong password', (done) => {
+        let newUser = new User();
+
+        newUser.email = 'lorem@ipsum.com';
+        newUser.password = '123mudar';
+        newUser.name = 'Gute';
+        newUser.cpf = '08236017907';
+        newUser.schooling = 'Doutorado';
+        newUser.course = 'Ciência da Computação';
+        newUser.segment = 'Comunidade acadêmica';
+        newUser.role = 'Pesquisador';
+        newUser.institution_name = 'UFPR';
+        newUser.state = 'PR';
+        newUser.city = 'Curitiba';
+
+        newUser.save((err) => {
+            if (err) {
+                console.log('MongoDB error:' + err);
+            }
+        }).then(function(newuser){
+            chai.request(server)
+            .post('/api/v1/user/authenticate')
+            .set('content-type', 'application/x-www-form-urlencoded')
+            .set('x-apicache-bypass', 'true')
+            .send({email: 'lorem@ipsum.com',
+            password: 'umasenhaerrada'})
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.json;
+                res.body.should.have.property('success');
+                res.body.success.should.equal(false);
+                res.body.should.have.property('msg');
+                res.body.msg.should.equal('A Senha informada é inválida.')
+                done();
+            });
+        });
+    });
+
+    it('should not authenticate a user with wrong email', (done) => {
+        let newUser = new User();
+
+        newUser.email = 'lorem@ipsum.com';
+        newUser.password = '123mudar';
+        newUser.name = 'Gute';
+        newUser.cpf = '08236017907';
+        newUser.schooling = 'Doutorado';
+        newUser.course = 'Ciência da Computação';
+        newUser.segment = 'Comunidade acadêmica';
+        newUser.role = 'Pesquisador';
+        newUser.institution_name = 'UFPR';
+        newUser.state = 'PR';
+        newUser.city = 'Curitiba';
+
+        newUser.save((err) => {
+            if (err) {
+                console.log('MongoDB error:' + err);
+            }
+        }).then(function(newuser){
+            chai.request(server)
+            .post('/api/v1/user/authenticate')
+            .set('content-type', 'application/x-www-form-urlencoded')
+            .set('x-apicache-bypass', 'true')
+            .send({email: 'dolor@ipsum.com',
+            password: '123mudar'})
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.json;
+                res.body.should.have.property('success');
+                res.body.success.should.equal(false);
+                res.body.should.have.property('msg');
+                res.body.msg.should.equal('O Email informado não está cadastrado.')
+                done();
+            });
+        });
+    });
+
+    it('should not authenticate a user with missing email', (done) => {
+        let newUser = new User();
+
+        newUser.email = 'lorem@ipsum.com';
+        newUser.password = '123mudar';
+        newUser.name = 'Gute';
+        newUser.cpf = '08236017907';
+        newUser.schooling = 'Doutorado';
+        newUser.course = 'Ciência da Computação';
+        newUser.segment = 'Comunidade acadêmica';
+        newUser.role = 'Pesquisador';
+        newUser.institution_name = 'UFPR';
+        newUser.state = 'PR';
+        newUser.city = 'Curitiba';
+
+        newUser.save((err) => {
+            if (err) {
+                console.log('MongoDB error:' + err);
+            }
+        }).then(function(newuser){
+            chai.request(server)
+            .post('/api/v1/user/authenticate')
+            .set('content-type', 'application/x-www-form-urlencoded')
+            .set('x-apicache-bypass', 'true')
+            .send({password: '123mudar'})
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.json;
+                res.body.should.have.property('success');
+                res.body.success.should.equal(false);
+                res.body.should.have.property('msg');
+                res.body.msg.should.equal('O campo Email é obrigatório.')
+                done();
+            });
+        });
+    });
+
+    it('should not authenticate a user with missing password', (done) => {
+        let newUser = new User();
+
+        newUser.email = 'lorem@ipsum.com';
+        newUser.password = '123mudar';
+        newUser.name = 'Gute';
+        newUser.cpf = '08236017907';
+        newUser.schooling = 'Doutorado';
+        newUser.course = 'Ciência da Computação';
+        newUser.segment = 'Comunidade acadêmica';
+        newUser.role = 'Pesquisador';
+        newUser.institution_name = 'UFPR';
+        newUser.state = 'PR';
+        newUser.city = 'Curitiba';
+
+        newUser.save((err) => {
+            if (err) {
+                console.log('MongoDB error:' + err);
+            }
+        }).then(function(newuser){
+            chai.request(server)
+            .post('/api/v1/user/authenticate')
+            .set('content-type', 'application/x-www-form-urlencoded')
+            .set('x-apicache-bypass', 'true')
+            .send({email:'lorem@ipsum.com'})
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.json;
+                res.body.should.have.property('success');
+                res.body.success.should.equal(false);
+                res.body.should.have.property('msg');
+                res.body.msg.should.equal('O campo Senha é obrigatório.')
+                done();
+            });
+        });
+    });
+
+    it('should not authenticate a user with wrong password', (done) => {
+        let newUser = new User();
+
+        newUser.email = 'lorem@ipsum.com';
+        newUser.password = '123mudar';
+        newUser.name = 'Gute';
+        newUser.cpf = '08236017907';
+        newUser.schooling = 'Doutorado';
+        newUser.course = 'Ciência da Computação';
+        newUser.segment = 'Comunidade acadêmica';
+        newUser.role = 'Pesquisador';
+        newUser.institution_name = 'UFPR';
+        newUser.state = 'PR';
+        newUser.city = 'Curitiba';
+
+        newUser.save((err) => {
+            if (err) {
+                console.log('MongoDB error:' + err);
+            }
+        }).then(function(newuser){
+            chai.request(server)
+            .post('/api/v1/user/authenticate')
+            .set('content-type', 'application/x-www-form-urlencoded')
+            .set('x-apicache-bypass', 'true')
+            .send({email:'lorem@ipsum.com', password: '123'})
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.json;
+                res.body.should.have.property('success');
+                res.body.success.should.equal(false);
+                res.body.should.have.property('msg');
+                res.body.msg.should.equal('A Senha informada é inválida.')
+                done();
+            });
+        });
+    });
+});