Skip to content

chore: adding test cases for additional routes #762

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/forty-ducks-invite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@hyperdx/api": patch
---

Added test cases for the webhook and source routes.
151 changes: 151 additions & 0 deletions packages/api/src/routers/api/__tests__/sources.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import { SourceKind } from '@hyperdx/common-utils/dist/types';
import { Types } from 'mongoose';

import { getLoggedInAgent, getServer } from '@/fixtures';
import { Source } from '@/models/source';

const MOCK_SOURCE = {
kind: SourceKind.Log,
name: 'Test Source',
connection: new Types.ObjectId().toString(),
from: {
databaseName: 'test_db',
tableName: 'test_table',
},
timestampValueExpression: 'timestamp',
};

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

beforeAll(async () => {
await server.start();
});

afterEach(async () => {
await server.clearDBs();
});

afterAll(async () => {
await server.stop();
});

it('GET / - returns all sources for a team', async () => {
const { agent, team } = await getLoggedInAgent(server);

// Create test source
await Source.create({
...MOCK_SOURCE,
team: team._id,
});

const response = await agent.get('/sources').expect(200);

expect(response.body).toHaveLength(1);
expect(response.body[0]).toMatchObject({
kind: MOCK_SOURCE.kind,
name: MOCK_SOURCE.name,
from: MOCK_SOURCE.from,
timestampValueExpression: MOCK_SOURCE.timestampValueExpression,
});
});

it('GET / - returns empty array when no sources exist', async () => {
const { agent } = await getLoggedInAgent(server);

const response = await agent.get('/sources').expect(200);

expect(response.body).toEqual([]);
});

it('POST / - creates a new source', async () => {
const { agent } = await getLoggedInAgent(server);

const response = await agent.post('/sources').send(MOCK_SOURCE).expect(200);

expect(response.body).toMatchObject({
kind: MOCK_SOURCE.kind,
name: MOCK_SOURCE.name,
from: MOCK_SOURCE.from,
timestampValueExpression: MOCK_SOURCE.timestampValueExpression,
});

// Verify source was created in database
const sources = await Source.find({});
expect(sources).toHaveLength(1);
});

it('POST / - returns 400 when request body is invalid', async () => {
const { agent } = await getLoggedInAgent(server);

// Missing required fields
await agent
.post('/sources')
.send({
kind: SourceKind.Log,
name: 'Test Source',
})
.expect(400);
});

it('PUT /:id - updates an existing source', async () => {
const { agent, team } = await getLoggedInAgent(server);

// Create test source
const source = await Source.create({
...MOCK_SOURCE,
team: team._id,
});

const updatedSource = {
...MOCK_SOURCE,
id: source._id.toString(),
name: 'Updated Name',
};

await agent.put(`/sources/${source._id}`).send(updatedSource).expect(200);

// Verify source was updated
const updatedSourceFromDB = await Source.findById(source._id);
expect(updatedSourceFromDB?.name).toBe('Updated Name');
});

it('PUT /:id - returns 404 when source does not exist', async () => {
const { agent } = await getLoggedInAgent(server);

const nonExistentId = new Types.ObjectId().toString();

await agent
.put(`/sources/${nonExistentId}`)
.send({
...MOCK_SOURCE,
id: nonExistentId,
})
.expect(404);
});

it('DELETE /:id - deletes a source', async () => {
const { agent, team } = await getLoggedInAgent(server);

// Create test source
const source = await Source.create({
...MOCK_SOURCE,
team: team._id,
});

await agent.delete(`/sources/${source._id}`).expect(200);

// Verify source was deleted
const deletedSource = await Source.findById(source._id);
expect(deletedSource).toBeNull();
});

it('DELETE /:id - returns 200 when source does not exist', async () => {
const { agent } = await getLoggedInAgent(server);

const nonExistentId = new Types.ObjectId().toString();

// This will succeed even if the ID doesn't exist, consistent with the implementation
await agent.delete(`/sources/${nonExistentId}`).expect(200);
});
});
183 changes: 183 additions & 0 deletions packages/api/src/routers/api/__tests__/webhooks.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
import { Types } from 'mongoose';

import { getLoggedInAgent, getServer } from '@/fixtures';
import Webhook, { WebhookService } from '@/models/webhook';

const MOCK_WEBHOOK = {
name: 'Test Webhook',
service: WebhookService.Slack,
url: 'https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX',
description: 'Test webhook for Slack',
queryParams: { param1: 'value1' },
headers: { 'X-Custom-Header': 'Header Value' },
body: '{"text": "Test message"}',
};

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

beforeAll(async () => {
await server.start();
});

afterEach(async () => {
await server.clearDBs();
});

afterAll(async () => {
await server.stop();
});

it('GET / - returns webhooks filtered by service', async () => {
const { agent, team } = await getLoggedInAgent(server);

// Create test webhook
await Webhook.create({
...MOCK_WEBHOOK,
team: team._id,
});

// Create a webhook for a different service
await Webhook.create({
...MOCK_WEBHOOK,
service: WebhookService.Generic,
url: 'https://example.com/webhook/generic',
team: team._id,
});

// Get webhooks for Slack
const response = await agent
.get('/webhooks')
.query({ service: WebhookService.Slack })
.expect(200);

expect(response.body.data).toHaveLength(1);
expect(response.body.data[0]).toMatchObject({
name: MOCK_WEBHOOK.name,
service: MOCK_WEBHOOK.service,
url: MOCK_WEBHOOK.url,
});

// Get webhooks for multiple services
const multiResponse = await agent
.get('/webhooks')
.query({ service: [WebhookService.Slack, WebhookService.Generic] })
.expect(200);

expect(multiResponse.body.data).toHaveLength(2);
});

it('GET / - returns empty array when no webhooks exist', async () => {
const { agent } = await getLoggedInAgent(server);

const response = await agent
.get('/webhooks')
.query({ service: WebhookService.Slack })
.expect(200);

expect(response.body.data).toEqual([]);
});

it('POST / - creates a new webhook', async () => {
const { agent } = await getLoggedInAgent(server);

const response = await agent
.post('/webhooks')
.send(MOCK_WEBHOOK)
.expect(200);

expect(response.body.data).toMatchObject({
name: MOCK_WEBHOOK.name,
service: MOCK_WEBHOOK.service,
url: MOCK_WEBHOOK.url,
description: MOCK_WEBHOOK.description,
});

// Verify webhook was created in database
const webhooks = await Webhook.find({});
expect(webhooks).toHaveLength(1);
});

it('POST / - returns 400 when webhook with same URL already exists', async () => {
const { agent, team } = await getLoggedInAgent(server);

// Create webhook first
await Webhook.create({
...MOCK_WEBHOOK,
team: team._id,
});

// Try to create the same webhook again
const response = await agent
.post('/webhooks')
.send(MOCK_WEBHOOK)
.expect(400);

expect(response.body.message).toBe('Webhook already exists');

// Verify only one webhook exists
const webhooks = await Webhook.find({});
expect(webhooks).toHaveLength(1);
});

it('POST / - returns 400 when request body is invalid', async () => {
const { agent } = await getLoggedInAgent(server);

// Missing required fields
await agent
.post('/webhooks')
.send({
name: 'Invalid Webhook',
})
.expect(400);

// Invalid URL
await agent
.post('/webhooks')
.send({
...MOCK_WEBHOOK,
url: 'not-a-url',
})
.expect(400);

// Invalid service
await agent
.post('/webhooks')
.send({
...MOCK_WEBHOOK,
service: 'INVALID_SERVICE',
})
.expect(400);
});

it('DELETE /:id - deletes a webhook', async () => {
const { agent, team } = await getLoggedInAgent(server);

// Create test webhook
const webhook = await Webhook.create({
...MOCK_WEBHOOK,
team: team._id,
});

await agent.delete(`/webhooks/${webhook._id}`).expect(200);

// Verify webhook was deleted
const deletedWebhook = await Webhook.findById(webhook._id);
expect(deletedWebhook).toBeNull();
});

it('DELETE /:id - returns 200 when webhook does not exist', async () => {
const { agent } = await getLoggedInAgent(server);

const nonExistentId = new Types.ObjectId().toString();

// This will succeed even if the ID doesn't exist, consistent with the implementation
await agent.delete(`/webhooks/${nonExistentId}`).expect(200);
});

it('DELETE /:id - returns 400 when ID is invalid', async () => {
const { agent } = await getLoggedInAgent(server);

await agent.delete('/webhooks/invalid-id').expect(400);
});
});