drapi 1.8.53 → 1.8.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/db.js +18 -2
- package/lib/models.js +0 -2
- package/package.json +1 -1
- package/test/db.test.js +145 -0
package/lib/db.js
CHANGED
|
@@ -23,8 +23,24 @@ exports.getDatabaseConnection = function(dbName) {
|
|
|
23
23
|
//database connection already exist. Return connection object
|
|
24
24
|
return connections[dbName];
|
|
25
25
|
} else {
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
const connection = mongoose.createConnection(config.db.host + dbName);
|
|
27
|
+
connections[dbName] = connection;
|
|
28
|
+
//Si la conexion inicial falla (p. ej. un wp sin base ni usuario), su promesa rechazada
|
|
29
|
+
//sin manejar termina el proceso. Se registra sin la URI (lleva credenciales) ni la pila, y se saca
|
|
30
|
+
//de la cache para que la siguiente peticion vuelva a intentar. PE2-9756
|
|
31
|
+
if (typeof connection.catch === 'function') {
|
|
32
|
+
connection.catch(function(err) {
|
|
33
|
+
console.error('[drapi] conexión a la base falló', {
|
|
34
|
+
db: dbName,
|
|
35
|
+
name: err && err.name,
|
|
36
|
+
message: err && String(err.message).split('\n')[0]
|
|
37
|
+
});
|
|
38
|
+
if (connections[dbName] === connection) {
|
|
39
|
+
delete connections[dbName];
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
return connection;
|
|
28
44
|
}
|
|
29
45
|
}
|
|
30
46
|
|
package/lib/models.js
CHANGED
|
@@ -7,7 +7,6 @@ const db = require('./db');
|
|
|
7
7
|
const structure = require('../api/models/structure');
|
|
8
8
|
const controllers = require('../api/models/model');
|
|
9
9
|
const trigger = require('../api/models/trigger');
|
|
10
|
-
const sizeof = require('object-sizeof')
|
|
11
10
|
|
|
12
11
|
/**
|
|
13
12
|
* connectionschema = {
|
|
@@ -65,7 +64,6 @@ let models = {
|
|
|
65
64
|
modelSchema.plugin(mongooseLogs, {schemaName:model,createAction:"Creó",updateAction:"Actualizó",deleteAction:"Eliminó"});
|
|
66
65
|
// }
|
|
67
66
|
connectionschema[dbname][model] = modelSchema;
|
|
68
|
-
console.log("tamaño de schemas almacenados en memoria: ", sizeof(connectionschema));
|
|
69
67
|
}
|
|
70
68
|
|
|
71
69
|
return connection.model(model, connectionschema[dbname][model]);
|
package/package.json
CHANGED
package/test/db.test.js
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// Pruebas de lib/db.js getDatabaseConnection (PE2-9756). Sin dependencias: node:test y assert.
|
|
2
|
+
// mongoose.createConnection se sustituye sobre el módulo cacheado, que es el mismo
|
|
3
|
+
// objeto que db.js obtiene con require("mongoose").
|
|
4
|
+
const nodeTest = require('node:test');
|
|
5
|
+
|
|
6
|
+
// Los casos corren en serie dentro de un solo test: los dobles son globales
|
|
7
|
+
// (mongoose.createConnection, console.error) y en Node 16 los test() de primer nivel se solapan.
|
|
8
|
+
const casos = [];
|
|
9
|
+
const test = (nombre, fn) => casos.push([nombre, fn]);
|
|
10
|
+
const assert = require('node:assert');
|
|
11
|
+
const mongoose = require('mongoose');
|
|
12
|
+
const db = require('../lib/db');
|
|
13
|
+
|
|
14
|
+
const HOST = 'mongodb://persona:secreta@mongo-interno:27017/';
|
|
15
|
+
|
|
16
|
+
// Una conexión falsa con el mismo contrato que la de mongoose 5: then/catch sobre la
|
|
17
|
+
// conexión inicial, que rechaza o resuelve cuando el caso lo decide.
|
|
18
|
+
const conexionFalsa = () => {
|
|
19
|
+
let resolver, rechazar;
|
|
20
|
+
const inicial = new Promise((resolve, reject) => { resolver = resolve; rechazar = reject; });
|
|
21
|
+
const conexion = {
|
|
22
|
+
catch: (fn) => inicial.catch(fn),
|
|
23
|
+
then: (ok, ko) => inicial.then(() => ok && ok(conexion), ko)
|
|
24
|
+
};
|
|
25
|
+
return { conexion, resolver: () => resolver(conexion), rechazar: (err) => rechazar(err) };
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const errorAuth = () => {
|
|
29
|
+
const error = new Error('failed to connect to server [172.31.9.99:27017] on first connect [MongoError: Authentication failed.]');
|
|
30
|
+
error.name = 'MongoNetworkError';
|
|
31
|
+
return error;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const conDobles = (fn) => async () => {
|
|
35
|
+
const originalCreate = mongoose.createConnection;
|
|
36
|
+
const originalError = console.error;
|
|
37
|
+
const originalConfig = global.drapiConfig;
|
|
38
|
+
const creadas = [];
|
|
39
|
+
const urls = [];
|
|
40
|
+
const errores = [];
|
|
41
|
+
const rechazos = [];
|
|
42
|
+
const alRechazo = (razon) => rechazos.push(razon);
|
|
43
|
+
global.drapiConfig = { db: { host: HOST } };
|
|
44
|
+
mongoose.createConnection = (url) => {
|
|
45
|
+
urls.push(url);
|
|
46
|
+
const falsa = conexionFalsa();
|
|
47
|
+
creadas.push(falsa);
|
|
48
|
+
return falsa.conexion;
|
|
49
|
+
};
|
|
50
|
+
console.error = (...args) => (String(args[0]).startsWith('[drapi]') ? errores.push(args) : originalError(...args));
|
|
51
|
+
process.on('unhandledRejection', alRechazo);
|
|
52
|
+
try {
|
|
53
|
+
await fn({ creadas, urls, errores, rechazos });
|
|
54
|
+
} finally {
|
|
55
|
+
mongoose.createConnection = originalCreate;
|
|
56
|
+
console.error = originalError;
|
|
57
|
+
global.drapiConfig = originalConfig;
|
|
58
|
+
process.removeListener('unhandledRejection', alRechazo);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// Deja correr los microtasks y un ciclo del event loop para que un rechazo
|
|
63
|
+
// sin manejar alcance a dispararse.
|
|
64
|
+
const esperar = () => new Promise((resolve) => setImmediate(resolve));
|
|
65
|
+
|
|
66
|
+
// Cada caso usa su propia base: la caché de conexiones es del módulo.
|
|
67
|
+
test('una base sin usuario se registra y no queda rechazo sin manejar', conDobles(
|
|
68
|
+
async ({ creadas, urls, errores, rechazos }) => {
|
|
69
|
+
const conexion = db.getDatabaseConnection('Error');
|
|
70
|
+
assert.strictEqual(conexion, creadas[0].conexion);
|
|
71
|
+
assert.deepStrictEqual(urls, [HOST + 'Error']);
|
|
72
|
+
creadas[0].rechazar(errorAuth());
|
|
73
|
+
await esperar();
|
|
74
|
+
assert.strictEqual(rechazos.length, 0);
|
|
75
|
+
assert.strictEqual(errores.length, 1);
|
|
76
|
+
const [mensaje, detalle] = errores[0];
|
|
77
|
+
assert.strictEqual(mensaje, '[drapi] conexión a la base falló');
|
|
78
|
+
assert.deepStrictEqual(detalle, {
|
|
79
|
+
db: 'Error',
|
|
80
|
+
name: 'MongoNetworkError',
|
|
81
|
+
message: errorAuth().message
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
));
|
|
85
|
+
|
|
86
|
+
test('el log no lleva la pila: solo la primera línea del mensaje', conDobles(
|
|
87
|
+
async ({ creadas, errores }) => {
|
|
88
|
+
db.getDatabaseConnection('cuadra-sin-base');
|
|
89
|
+
const error = errorAuth();
|
|
90
|
+
error.message = error.message + '\n at Connection.messageHandler (connection.js:364:19)';
|
|
91
|
+
creadas[0].rechazar(error);
|
|
92
|
+
await esperar();
|
|
93
|
+
assert.strictEqual(errores[0][1].message, errorAuth().message);
|
|
94
|
+
}
|
|
95
|
+
));
|
|
96
|
+
|
|
97
|
+
test('el log no lleva la URI ni la contraseña', conDobles(
|
|
98
|
+
async ({ creadas, errores }) => {
|
|
99
|
+
db.getDatabaseConnection('fancap');
|
|
100
|
+
creadas[0].rechazar(errorAuth());
|
|
101
|
+
await esperar();
|
|
102
|
+
const texto = JSON.stringify(errores);
|
|
103
|
+
assert.ok(!texto.includes('secreta'));
|
|
104
|
+
assert.ok(!texto.includes('mongo-interno'));
|
|
105
|
+
}
|
|
106
|
+
));
|
|
107
|
+
|
|
108
|
+
test('tras el fallo, la siguiente llamada abre una conexión nueva', conDobles(
|
|
109
|
+
async ({ creadas }) => {
|
|
110
|
+
const primera = db.getDatabaseConnection('capitolio');
|
|
111
|
+
creadas[0].rechazar(errorAuth());
|
|
112
|
+
await esperar();
|
|
113
|
+
const segunda = db.getDatabaseConnection('capitolio');
|
|
114
|
+
assert.strictEqual(creadas.length, 2);
|
|
115
|
+
assert.notStrictEqual(segunda, primera);
|
|
116
|
+
}
|
|
117
|
+
));
|
|
118
|
+
|
|
119
|
+
test('una conexión que resuelve se queda en caché y no registra nada', conDobles(
|
|
120
|
+
async ({ creadas, errores }) => {
|
|
121
|
+
const primera = db.getDatabaseConnection('demo');
|
|
122
|
+
creadas[0].resolver();
|
|
123
|
+
await esperar();
|
|
124
|
+
const segunda = db.getDatabaseConnection('demo');
|
|
125
|
+
assert.strictEqual(segunda, primera);
|
|
126
|
+
assert.strictEqual(creadas.length, 1);
|
|
127
|
+
assert.strictEqual(errores.length, 0);
|
|
128
|
+
}
|
|
129
|
+
));
|
|
130
|
+
|
|
131
|
+
test('mientras conecta, las llamadas comparten la misma conexión', conDobles(
|
|
132
|
+
async ({ creadas }) => {
|
|
133
|
+
const primera = db.getDatabaseConnection('panavision');
|
|
134
|
+
const segunda = db.getDatabaseConnection('panavision');
|
|
135
|
+
assert.strictEqual(segunda, primera);
|
|
136
|
+
assert.strictEqual(creadas.length, 1);
|
|
137
|
+
creadas[0].resolver();
|
|
138
|
+
}
|
|
139
|
+
));
|
|
140
|
+
|
|
141
|
+
nodeTest('lib/db.js getDatabaseConnection', async (t) => {
|
|
142
|
+
for (const [nombre, fn] of casos) {
|
|
143
|
+
await t.test(nombre, fn);
|
|
144
|
+
}
|
|
145
|
+
});
|