Skip to content

Commit

Permalink
add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ernestii committed Jan 26, 2025
1 parent ae2dbdf commit 6616f30
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 31 deletions.
4 changes: 2 additions & 2 deletions packages/api/src/controllers/alerts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,9 @@ export const createOrUpdateDashboardAlerts = async (
teamId: ObjectId,
alertsByTile: Record<string, AlertInput>,
) => {
return await Promise.all(
return Promise.all(
Object.entries(alertsByTile).map(async ([tileId, alert]) => {
return Alert.findOneAndUpdate(
return await Alert.findOneAndUpdate(
{
dashboard: dashboardId,
tileId,
Expand Down
13 changes: 10 additions & 3 deletions packages/api/src/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -302,16 +302,22 @@ export function buildMetricSeries({
export const randomMongoId = () =>
Math.floor(Math.random() * 1000000000000).toString();

export const makeTile = (opts?: { id?: string }): Tile => ({
export const makeTile = (opts?: {
id?: string;
alert?: SavedChartConfig['alert'];
}): Tile => ({
id: opts?.id ?? randomMongoId(),
x: 1,
y: 1,
w: 1,
h: 1,
config: makeChartConfig(),
config: makeChartConfig(opts),
});

export const makeChartConfig = (opts?: { id?: string }): SavedChartConfig => ({
export const makeChartConfig = (opts?: {
id?: string;
alert?: SavedChartConfig['alert'];
}): SavedChartConfig => ({
name: 'Test Chart',
source: 'test-source',
displayType: DisplayType.Line,
Expand All @@ -331,6 +337,7 @@ export const makeChartConfig = (opts?: { id?: string }): SavedChartConfig => ({
output: 'number',
},
filters: [],
alert: opts?.alert,
});

// TODO: DEPRECATED
Expand Down
144 changes: 134 additions & 10 deletions packages/api/src/routers/api/__tests__/dashboard.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { AlertThresholdType } from '@hyperdx/common-utils/dist/types';
import { omit } from 'lodash';

import {
getLoggedInAgent,
getServer,
Expand All @@ -11,6 +14,13 @@ const MOCK_DASHBOARD = {
tags: ['test'],
};

const MOCK_ALERT = {
channel: { type: 'webhook' as const, webhookId: 'abcde' },
interval: '12h' as const,
threshold: 1,
thresholdType: AlertThresholdType.ABOVE,
};

describe('dashboard router', () => {
const server = getServer();

Expand Down Expand Up @@ -74,6 +84,114 @@ describe('dashboard router', () => {
expect(dashboards.body.length).toBe(0);
});

it('alerts are created when creating dashboard', async () => {
const { agent } = await getLoggedInAgent(server);
const dashboard = await agent
.post('/dashboards')
.send({
name: 'Test Dashboard',
tiles: [makeTile({ alert: MOCK_ALERT })],
tags: [],
})
.expect(200);

const alerts = await agent.get(`/alerts`).expect(200);
expect(alerts.body.data).toMatchObject([
{
tileId: dashboard.body.tiles[0].id,
...omit(MOCK_ALERT, 'channel.webhookId'),
},
]);
});

it('alerts are created when updating dashboard (adding alert to tile)', async () => {
const { agent } = await getLoggedInAgent(server);
const dashboard = await agent
.post('/dashboards')
.send(MOCK_DASHBOARD)
.expect(200);

const updatedDashboard = await agent
.patch(`/dashboards/${dashboard.body.id}`)
.send({
...dashboard.body,
tiles: [...dashboard.body.tiles, makeTile({ alert: MOCK_ALERT })],
})
.expect(200);

const alerts = await agent.get(`/alerts`).expect(200);
expect(alerts.body.data).toMatchObject([
{
tileId: updatedDashboard.body.tiles[MOCK_DASHBOARD.tiles.length].id,
...omit(MOCK_ALERT, 'channel.webhookId'),
},
]);
});

it('alerts are deleted when updating dashboard (deleting tile alert settings)', async () => {
const { agent } = await getLoggedInAgent(server);
const dashboard = await agent
.post('/dashboards')
.send({
name: 'Test Dashboard',
tiles: [makeTile({ alert: MOCK_ALERT })],
tags: [],
})
.expect(200);

await agent
.patch(`/dashboards/${dashboard.body.id}`)
.send({
...dashboard.body,
tiles: dashboard.body.tiles.slice(1),
})
.expect(200);

const alerts = await agent.get(`/alerts`).expect(200);
expect(alerts.body.data).toEqual([]);
});

it('alerts are updated when updating dashboard (updating tile alert settings)', async () => {
const { agent } = await getLoggedInAgent(server);
const dashboard = await agent
.post('/dashboards')
.send({
name: 'Test Dashboard',
tiles: [makeTile({ alert: MOCK_ALERT })],
tags: [],
})
.expect(200);

const updatedAlert = {
...MOCK_ALERT,
threshold: 2,
};

await agent
.patch(`/dashboards/${dashboard.body.id}`)
.send({
...dashboard.body,
tiles: [
{
...dashboard.body.tiles[0],
config: {
...dashboard.body.tiles[0].config,
alert: updatedAlert,
},
},
],
})
.expect(200);

const alerts = await agent.get(`/alerts`).expect(200);
expect(alerts.body.data).toMatchObject([
{
tileId: dashboard.body.tiles[0].id,
...omit(updatedAlert, 'channel.webhookId'),
},
]);
});

it('deletes attached alerts when deleting tiles', async () => {
const { agent } = await getLoggedInAgent(server);

Expand All @@ -96,34 +214,40 @@ describe('dashboard router', () => {
),
);

const dashboards = await agent.get(`/dashboards`).expect(200);

// Make sure all alerts are attached to the dashboard charts
const allTiles = dashboard.tiles.map(tile => tile.id).sort();
const tilesWithAlerts = dashboards.body[0].alerts
const alertsPreDelete = await agent.get(`/alerts`).expect(200);
const alertsPreDeleteTiles = alertsPreDelete.body.data
.map(alert => alert.tileId)
.sort();
expect(allTiles).toEqual(tilesWithAlerts);
expect(allTiles).toEqual(alertsPreDeleteTiles);

// Delete the first chart
const dashboardPreDelete = await agent
.get('/dashboards')
.expect(200)
.then(res => res.body[0]);
await agent
.patch(`/dashboards/${dashboard._id}`)
.send({
...dashboard,
tiles: dashboard.tiles.slice(1),
...dashboardPreDelete,
tiles: dashboardPreDelete.tiles.slice(1),
})
.expect(200);

const dashboardPostDelete = (await agent.get(`/dashboards`).expect(200))
.body[0];
const dashboardPostDelete = await agent
.get('/dashboards')
.expect(200)
.then(res => res.body[0]);

// Make sure all alerts are attached to the dashboard charts
const allTilesPostDelete = dashboardPostDelete.tiles
.map(tile => tile.id)
.sort();
const tilesWithAlertsPostDelete = dashboardPostDelete.alerts
const alertsPostDelete = await agent.get(`/alerts`).expect(200);
const alertsPostDeleteTiles = alertsPostDelete.body.data
.map(alert => alert.tileId)
.sort();
expect(allTilesPostDelete).toEqual(tilesWithAlertsPostDelete);
expect(allTilesPostDelete).toEqual(alertsPostDeleteTiles);
});
});
35 changes: 19 additions & 16 deletions packages/app/src/components/DBEditTimeChartForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import DBTableChart from '@/components/DBTableChart';
import { DBTimeChart } from '@/components/DBTimeChart';
import { SQLInlineEditorControlled } from '@/components/SQLInlineEditor';
import { TimePicker } from '@/components/TimePicker';
import { IS_DEV } from '@/config';
import { GranularityPickerControlled } from '@/GranularityPicker';
import SearchInputV2 from '@/SearchInputV2';
import { getFirstTimestampValueExpression, useSource } from '@/source';
Expand Down Expand Up @@ -567,22 +568,24 @@ export default function EditTimeChartForm({
Add Series
</Button>
)}
{displayType === DisplayType.Line && dashboardId && (
<Button
variant="subtle"
size="sm"
color={alert ? 'red' : 'gray'}
onClick={() =>
setValue(
'alert',
alert ? undefined : DEFAULT_TILE_ALERT,
)
}
>
<i className="bi bi-bell-fill me-2" />
{!alert ? 'Add Alert' : 'Remove Alert'}
</Button>
)}
{displayType === DisplayType.Line &&
dashboardId &&
IS_DEV && (
<Button
variant="subtle"
size="sm"
color={alert ? 'red' : 'gray'}
onClick={() =>
setValue(
'alert',
alert ? undefined : DEFAULT_TILE_ALERT,
)
}
>
<i className="bi bi-bell-fill me-2" />
{!alert ? 'Add Alert' : 'Remove Alert'}
</Button>
)}
</Group>
<NumberFormatInputControlled control={control} />
</Flex>
Expand Down

0 comments on commit 6616f30

Please sign in to comment.