DC-130 rev3: log.error on unexpected bridge status in /status + test asserting the log call (13 tests green)
This commit is contained in:
@@ -19,6 +19,10 @@ const FIXTURE_ROWS = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function buildApp(fetchT, env = {}) {
|
function buildApp(fetchT, env = {}) {
|
||||||
|
return buildAppWithLogCapture(fetchT, env, { info: jest.fn(), warn: jest.fn(), error: jest.fn() });
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildAppWithLogCapture(fetchT, env = {}, log) {
|
||||||
process.env.SHIPDECK_BRIDGE_URL = env.url !== undefined ? env.url : 'http://172.17.0.1:8977';
|
process.env.SHIPDECK_BRIDGE_URL = env.url !== undefined ? env.url : 'http://172.17.0.1:8977';
|
||||||
process.env.SHIPDECK_BRIDGE_TOKEN_FILE = env.tokenFile !== undefined ? env.tokenFile : '';
|
process.env.SHIPDECK_BRIDGE_TOKEN_FILE = env.tokenFile !== undefined ? env.tokenFile : '';
|
||||||
jest.resetModules();
|
jest.resetModules();
|
||||||
@@ -27,7 +31,7 @@ function buildApp(fetchT, env = {}) {
|
|||||||
asyncHandler: (fn) => async (req, res, next) => {
|
asyncHandler: (fn) => async (req, res, next) => {
|
||||||
try { await fn(req, res, next); } catch (e) { next(e); }
|
try { await fn(req, res, next); } catch (e) { next(e); }
|
||||||
},
|
},
|
||||||
log: { info: jest.fn(), warn: jest.fn(), error: jest.fn() },
|
log,
|
||||||
auditLogger: { log: jest.fn(async () => {}) },
|
auditLogger: { log: jest.fn(async () => {}) },
|
||||||
fetchT,
|
fetchT,
|
||||||
});
|
});
|
||||||
@@ -41,11 +45,20 @@ function buildApp(fetchT, env = {}) {
|
|||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// expose log on the fetcher wrapper for log-capture assertions
|
||||||
|
function jsonFetcherWithLog(responses) {
|
||||||
|
const calls = [];
|
||||||
|
const log = { info: jest.fn(), warn: jest.fn(), error: jest.fn() };
|
||||||
|
return { calls, log, fetchT: null };
|
||||||
|
}
|
||||||
|
|
||||||
function jsonFetcher(responses) {
|
function jsonFetcher(responses) {
|
||||||
// responses: map of "METHOD path" -> {status, body}
|
// responses: map of "METHOD path" -> {status, body}
|
||||||
const calls = [];
|
const calls = [];
|
||||||
return {
|
const log = { info: jest.fn(), warn: jest.fn(), error: jest.fn() };
|
||||||
|
const wrapper = {
|
||||||
calls,
|
calls,
|
||||||
|
log,
|
||||||
fetchT: jest.fn(async (url, opts) => {
|
fetchT: jest.fn(async (url, opts) => {
|
||||||
const key = `${(opts && opts.method) || 'GET'} ${url.replace(/^https?:\/\/[^/]+/, '')}`;
|
const key = `${(opts && opts.method) || 'GET'} ${url.replace(/^https?:\/\/[^/]+/, '')}`;
|
||||||
calls.push({ key, opts });
|
calls.push({ key, opts });
|
||||||
@@ -56,6 +69,7 @@ function jsonFetcher(responses) {
|
|||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
return wrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('routes/deploys — feature gate', () => {
|
describe('routes/deploys — feature gate', () => {
|
||||||
@@ -155,16 +169,21 @@ describe('routes/deploys — proxied endpoints', () => {
|
|||||||
expect(body.error).toMatch(/bridge/i);
|
expect(body.error).toMatch(/bridge/i);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('GET /status maps unexpected bridge statuses to 502', async () => {
|
test('GET /status maps unexpected bridge statuses to 502 with error logging', async () => {
|
||||||
const f = jsonFetcher({
|
const f = jsonFetcher({
|
||||||
'GET /api/status?service=helloworld': { status: 404, body: { ok: false, error: 'not found' } },
|
'GET /api/status?service=helloworld': { status: 404, body: { ok: false, error: 'not found' } },
|
||||||
});
|
});
|
||||||
const app = buildApp(f.fetchT);
|
const app = buildAppWithLogCapture(f.fetchT, {}, f.log);
|
||||||
const server = app.listen(0);
|
const server = app.listen(0);
|
||||||
const port = server.address().port;
|
const port = server.address().port;
|
||||||
const r = await fetch(`http://127.0.0.1:${port}/api/v1/deploys/status?service=helloworld`);
|
const r = await fetch(`http://127.0.0.1:${port}/api/v1/deploys/status?service=helloworld`);
|
||||||
server.close();
|
server.close();
|
||||||
expect(r.status).toBe(502);
|
expect(r.status).toBe(502);
|
||||||
|
expect(f.log.error).toHaveBeenCalledWith(
|
||||||
|
'deploys',
|
||||||
|
'status probe: unexpected bridge response',
|
||||||
|
expect.objectContaining({ service: 'helloworld', status: 404 }),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('GET /status maps bridge connection failure to 502', async () => {
|
test('GET /status maps bridge connection failure to 502', async () => {
|
||||||
|
|||||||
@@ -136,7 +136,12 @@ module.exports = function ({ asyncHandler, log, auditLogger, fetchT }) {
|
|||||||
}
|
}
|
||||||
if (status !== 200) {
|
if (status !== 200) {
|
||||||
// malformed upstream route or other unexpected bridge failure
|
// malformed upstream route or other unexpected bridge failure
|
||||||
return errorResponse(res, 502, body && body.error ? body.error : 'unexpected bridge response');
|
log.error('deploys', 'status probe: unexpected bridge response', {
|
||||||
|
service,
|
||||||
|
status,
|
||||||
|
bridgeError: body && body.error ? String(body.error).slice(0, 200) : 'none',
|
||||||
|
});
|
||||||
|
return errorResponse(res, 502, 'shipdeck bridge protocol error (unexpected response, status ' + status + ')');
|
||||||
}
|
}
|
||||||
return ok(res, { ok: body.ok === true, output: body.output || '' });
|
return ok(res, { ok: body.ok === true, output: body.output || '' });
|
||||||
}));
|
}));
|
||||||
|
|||||||
Reference in New Issue
Block a user