Files
dependabot[bot] 3cafae5f54 chore(deps): bump @sentry/node from 10.75.1 to 11.0.0 (#23428)
* chore(deps): bump @sentry/node from 10.75.1 to 11.0.0

Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 10.75.1 to 11.0.0.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/10.75.1...11.0.0)

---
updated-dependencies:
- dependency-name: "@sentry/node"
  dependency-version: 11.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix: replace `skipOpenTelemetrySetup` with `enableOpenTelemetrySetup`

refs: https://github.com/getsentry/sentry-javascript/blob/develop/MIGRATION.md#choosing-an-opentelemetry-setup

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Tony <TonyRL@users.noreply.github.com>
2026-09-29 21:59:31 +08:00

172 lines
6.5 KiB
TypeScript

import Honeybadger from '@honeybadger-io/js';
import * as Sentry from '@sentry/node';
import { load } from 'cheerio';
import { Hono } from 'hono';
import { afterEach, describe, expect, it, vi } from 'vitest';
import app from '@/app';
import { config } from '@/config';
const requestWithErrorHandler = async (errorHandler: Parameters<Hono['onError']>[0]) => {
const testApp = new Hono();
testApp.onError(errorHandler);
testApp.get('/test/path', () => {
throw new Error('boom');
});
return await testApp.request('/test/path');
};
describe('error', () => {
it('error', async () => {
const response = await app.request('/test/error');
expect(response.status).toBe(503);
const text = await response.text();
expect(text).toMatch(/Error: Error test/);
});
});
describe('httperror', () => {
it('httperror', async () => {
const response = await app.request('/test/httperror');
expect(response.status).toBe(503);
const text = await response.text();
expect(text).toContain('FetchError: [GET] &quot;https://httpbingo.org/status/404&quot;: 404');
}, 20000);
});
describe('RequestInProgressError', () => {
it('RequestInProgressError with retry', async () => {
const responses = await Promise.all([app.request('/test/slow'), app.request('/test/slow')]);
expect(responses.map((r) => r.status)).toEqual([200, 200]);
});
it('RequestInProgressError', async () => {
const responses = await Promise.all([app.request('/test/slow4'), app.request('/test/slow4')]);
expect(new Set(responses.map((r) => r.status))).toEqual(new Set([200, 503]));
expect(new Set(responses.map((r) => r.headers.get('cache-control')))).toEqual(new Set([`public, max-age=${config.cache.routeExpire}`, `public, max-age=${config.requestTimeout / 1000}`]));
const texts = await Promise.all(responses.map((r) => r.text()));
expect(texts.filter((t) => t.includes('RequestInProgressError: This path is currently fetching, please come back later!'))).toHaveLength(1);
});
});
describe('config-not-found-error', () => {
it('config-not-found-error', async () => {
const response = await app.request('/test/config-not-found-error');
expect(response.status).toBe(503);
const text = await response.text();
expect(text).toMatch('ConfigNotFoundError: Test config not found error');
}, 20000);
});
describe('invalid-parameter-error', () => {
it('invalid-parameter-error', async () => {
const response = await app.request('/test/invalid-parameter-error');
expect(response.status).toBe(503);
const text = await response.text();
expect(text).toMatch('InvalidParameterError: Test invalid parameter error');
}, 20000);
});
describe('captcha-error', () => {
it('captcha-error', async () => {
const response = await app.request('/test/captcha-error');
expect(response.status).toBe(503);
const text = await response.text();
expect(text).toMatch('CaptchaError: Test captcha error');
}, 20000);
});
describe('route throws an error', () => {
it('route path error should have path mounted', async () => {
await app.request('/test/error');
await app.request('/thisDoesNotExist');
const response = await app.request('/');
const text = await response.text();
const $ = load(text);
$('.debug-item').each((_index, item) => {
const key = $(item).find('.debug-key').text().trim();
const value = $(item).find('.debug-value').html()?.trim();
switch (key) {
case 'Request Amount:':
expect(value).toBe('12');
break;
case 'Hot Routes:':
expect(value).toBe('9 /test/:id/:params?<br>');
break;
case 'Hot Paths:':
expect(value).toBe(
'2 /test/error<br>2 /test/slow<br>2 /test/slow4<br>1 /test/httperror<br>1 /test/config-not-found-error<br>1 /test/invalid-parameter-error<br>1 /test/captcha-error<br>1 /thisDoesNotExist<br>1 /<br>'
);
break;
case 'Hot Error Routes:':
expect(value).toBe('6 /test/:id/:params?<br>');
break;
case 'Hot Error Paths:':
expect(value).toBe('2 /test/error<br>1 /test/httperror<br>1 /test/slow4<br>1 /test/config-not-found-error<br>1 /test/invalid-parameter-error<br>1 /test/captcha-error<br>1 /thisDoesNotExist<br>');
break;
default:
}
});
});
});
describe('error handler honeybadger', () => {
afterEach(() => {
vi.restoreAllMocks();
vi.resetModules();
});
it('sends errors to honeybadger when enabled', async () => {
process.env.HONEYBADGER_API_KEY = 'hbp_test_key';
vi.resetModules();
const notify = vi.spyOn(Honeybadger, 'notify').mockReturnValue(true);
const { default: logger } = await import('@/utils/logger');
vi.spyOn(logger, 'error').mockReturnValue(logger);
const { errorHandler } = await import('@/errors');
const response = await requestWithErrorHandler(errorHandler);
expect(response.status).toBe(503);
expect(notify).toHaveBeenCalledWith(expect.any(Error), {
context: { name: 'test' },
});
delete process.env.HONEYBADGER_API_KEY;
});
});
describe('error handler sentry', () => {
afterEach(async () => {
await Sentry.close();
vi.restoreAllMocks();
vi.resetModules();
});
it('sends errors to sentry when enabled', async () => {
process.env.SENTRY = 'dsn';
vi.resetModules();
const beforeSend = vi.fn(() => null);
Sentry.init({
dsn: 'https://public@sentry.example.test/1',
beforeSend,
defaultIntegrations: false,
enableOpenTelemetrySetup: false,
});
const { default: logger } = await import('@/utils/logger');
vi.spyOn(logger, 'error').mockReturnValue(logger);
const { errorHandler } = await import('@/errors');
const response = await requestWithErrorHandler(errorHandler);
expect(response.status).toBe(503);
await vi.waitFor(() => {
expect(beforeSend).toHaveBeenCalledWith(expect.objectContaining({ tags: expect.objectContaining({ name: 'test' }) }), expect.any(Object));
});
delete process.env.SENTRY;
});
});