Skip to content

Commit e5dfefb

Browse files
authored
chore: adding test cases for additional routes (#762)
Ref: HDX-1619
1 parent 1c54b59 commit e5dfefb

File tree

3 files changed

+339
-0
lines changed

3 files changed

+339
-0
lines changed

.changeset/forty-ducks-invite.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@hyperdx/api": patch
3+
---
4+
5+
Added test cases for the webhook and source routes.
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
import { SourceKind } from '@hyperdx/common-utils/dist/types';
2+
import { Types } from 'mongoose';
3+
4+
import { getLoggedInAgent, getServer } from '@/fixtures';
5+
import { Source } from '@/models/source';
6+
7+
const MOCK_SOURCE = {
8+
kind: SourceKind.Log,
9+
name: 'Test Source',
10+
connection: new Types.ObjectId().toString(),
11+
from: {
12+
databaseName: 'test_db',
13+
tableName: 'test_table',
14+
},
15+
timestampValueExpression: 'timestamp',
16+
};
17+
18+
describe('sources router', () => {
19+
const server = getServer();
20+
21+
beforeAll(async () => {
22+
await server.start();
23+
});
24+
25+
afterEach(async () => {
26+
await server.clearDBs();
27+
});
28+
29+
afterAll(async () => {
30+
await server.stop();
31+
});
32+
33+
it('GET / - returns all sources for a team', async () => {
34+
const { agent, team } = await getLoggedInAgent(server);
35+
36+
// Create test source
37+
await Source.create({
38+
...MOCK_SOURCE,
39+
team: team._id,
40+
});
41+
42+
const response = await agent.get('/sources').expect(200);
43+
44+
expect(response.body).toHaveLength(1);
45+
expect(response.body[0]).toMatchObject({
46+
kind: MOCK_SOURCE.kind,
47+
name: MOCK_SOURCE.name,
48+
from: MOCK_SOURCE.from,
49+
timestampValueExpression: MOCK_SOURCE.timestampValueExpression,
50+
});
51+
});
52+
53+
it('GET / - returns empty array when no sources exist', async () => {
54+
const { agent } = await getLoggedInAgent(server);
55+
56+
const response = await agent.get('/sources').expect(200);
57+
58+
expect(response.body).toEqual([]);
59+
});
60+
61+
it('POST / - creates a new source', async () => {
62+
const { agent } = await getLoggedInAgent(server);
63+
64+
const response = await agent.post('/sources').send(MOCK_SOURCE).expect(200);
65+
66+
expect(response.body).toMatchObject({
67+
kind: MOCK_SOURCE.kind,
68+
name: MOCK_SOURCE.name,
69+
from: MOCK_SOURCE.from,
70+
timestampValueExpression: MOCK_SOURCE.timestampValueExpression,
71+
});
72+
73+
// Verify source was created in database
74+
const sources = await Source.find({});
75+
expect(sources).toHaveLength(1);
76+
});
77+
78+
it('POST / - returns 400 when request body is invalid', async () => {
79+
const { agent } = await getLoggedInAgent(server);
80+
81+
// Missing required fields
82+
await agent
83+
.post('/sources')
84+
.send({
85+
kind: SourceKind.Log,
86+
name: 'Test Source',
87+
})
88+
.expect(400);
89+
});
90+
91+
it('PUT /:id - updates an existing source', async () => {
92+
const { agent, team } = await getLoggedInAgent(server);
93+
94+
// Create test source
95+
const source = await Source.create({
96+
...MOCK_SOURCE,
97+
team: team._id,
98+
});
99+
100+
const updatedSource = {
101+
...MOCK_SOURCE,
102+
id: source._id.toString(),
103+
name: 'Updated Name',
104+
};
105+
106+
await agent.put(`/sources/${source._id}`).send(updatedSource).expect(200);
107+
108+
// Verify source was updated
109+
const updatedSourceFromDB = await Source.findById(source._id);
110+
expect(updatedSourceFromDB?.name).toBe('Updated Name');
111+
});
112+
113+
it('PUT /:id - returns 404 when source does not exist', async () => {
114+
const { agent } = await getLoggedInAgent(server);
115+
116+
const nonExistentId = new Types.ObjectId().toString();
117+
118+
await agent
119+
.put(`/sources/${nonExistentId}`)
120+
.send({
121+
...MOCK_SOURCE,
122+
id: nonExistentId,
123+
})
124+
.expect(404);
125+
});
126+
127+
it('DELETE /:id - deletes a source', async () => {
128+
const { agent, team } = await getLoggedInAgent(server);
129+
130+
// Create test source
131+
const source = await Source.create({
132+
...MOCK_SOURCE,
133+
team: team._id,
134+
});
135+
136+
await agent.delete(`/sources/${source._id}`).expect(200);
137+
138+
// Verify source was deleted
139+
const deletedSource = await Source.findById(source._id);
140+
expect(deletedSource).toBeNull();
141+
});
142+
143+
it('DELETE /:id - returns 200 when source does not exist', async () => {
144+
const { agent } = await getLoggedInAgent(server);
145+
146+
const nonExistentId = new Types.ObjectId().toString();
147+
148+
// This will succeed even if the ID doesn't exist, consistent with the implementation
149+
await agent.delete(`/sources/${nonExistentId}`).expect(200);
150+
});
151+
});
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
import { Types } from 'mongoose';
2+
3+
import { getLoggedInAgent, getServer } from '@/fixtures';
4+
import Webhook, { WebhookService } from '@/models/webhook';
5+
6+
const MOCK_WEBHOOK = {
7+
name: 'Test Webhook',
8+
service: WebhookService.Slack,
9+
url: 'https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX',
10+
description: 'Test webhook for Slack',
11+
queryParams: { param1: 'value1' },
12+
headers: { 'X-Custom-Header': 'Header Value' },
13+
body: '{"text": "Test message"}',
14+
};
15+
16+
describe('webhooks router', () => {
17+
const server = getServer();
18+
19+
beforeAll(async () => {
20+
await server.start();
21+
});
22+
23+
afterEach(async () => {
24+
await server.clearDBs();
25+
});
26+
27+
afterAll(async () => {
28+
await server.stop();
29+
});
30+
31+
it('GET / - returns webhooks filtered by service', async () => {
32+
const { agent, team } = await getLoggedInAgent(server);
33+
34+
// Create test webhook
35+
await Webhook.create({
36+
...MOCK_WEBHOOK,
37+
team: team._id,
38+
});
39+
40+
// Create a webhook for a different service
41+
await Webhook.create({
42+
...MOCK_WEBHOOK,
43+
service: WebhookService.Generic,
44+
url: 'https://example.com/webhook/generic',
45+
team: team._id,
46+
});
47+
48+
// Get webhooks for Slack
49+
const response = await agent
50+
.get('/webhooks')
51+
.query({ service: WebhookService.Slack })
52+
.expect(200);
53+
54+
expect(response.body.data).toHaveLength(1);
55+
expect(response.body.data[0]).toMatchObject({
56+
name: MOCK_WEBHOOK.name,
57+
service: MOCK_WEBHOOK.service,
58+
url: MOCK_WEBHOOK.url,
59+
});
60+
61+
// Get webhooks for multiple services
62+
const multiResponse = await agent
63+
.get('/webhooks')
64+
.query({ service: [WebhookService.Slack, WebhookService.Generic] })
65+
.expect(200);
66+
67+
expect(multiResponse.body.data).toHaveLength(2);
68+
});
69+
70+
it('GET / - returns empty array when no webhooks exist', async () => {
71+
const { agent } = await getLoggedInAgent(server);
72+
73+
const response = await agent
74+
.get('/webhooks')
75+
.query({ service: WebhookService.Slack })
76+
.expect(200);
77+
78+
expect(response.body.data).toEqual([]);
79+
});
80+
81+
it('POST / - creates a new webhook', async () => {
82+
const { agent } = await getLoggedInAgent(server);
83+
84+
const response = await agent
85+
.post('/webhooks')
86+
.send(MOCK_WEBHOOK)
87+
.expect(200);
88+
89+
expect(response.body.data).toMatchObject({
90+
name: MOCK_WEBHOOK.name,
91+
service: MOCK_WEBHOOK.service,
92+
url: MOCK_WEBHOOK.url,
93+
description: MOCK_WEBHOOK.description,
94+
});
95+
96+
// Verify webhook was created in database
97+
const webhooks = await Webhook.find({});
98+
expect(webhooks).toHaveLength(1);
99+
});
100+
101+
it('POST / - returns 400 when webhook with same URL already exists', async () => {
102+
const { agent, team } = await getLoggedInAgent(server);
103+
104+
// Create webhook first
105+
await Webhook.create({
106+
...MOCK_WEBHOOK,
107+
team: team._id,
108+
});
109+
110+
// Try to create the same webhook again
111+
const response = await agent
112+
.post('/webhooks')
113+
.send(MOCK_WEBHOOK)
114+
.expect(400);
115+
116+
expect(response.body.message).toBe('Webhook already exists');
117+
118+
// Verify only one webhook exists
119+
const webhooks = await Webhook.find({});
120+
expect(webhooks).toHaveLength(1);
121+
});
122+
123+
it('POST / - returns 400 when request body is invalid', async () => {
124+
const { agent } = await getLoggedInAgent(server);
125+
126+
// Missing required fields
127+
await agent
128+
.post('/webhooks')
129+
.send({
130+
name: 'Invalid Webhook',
131+
})
132+
.expect(400);
133+
134+
// Invalid URL
135+
await agent
136+
.post('/webhooks')
137+
.send({
138+
...MOCK_WEBHOOK,
139+
url: 'not-a-url',
140+
})
141+
.expect(400);
142+
143+
// Invalid service
144+
await agent
145+
.post('/webhooks')
146+
.send({
147+
...MOCK_WEBHOOK,
148+
service: 'INVALID_SERVICE',
149+
})
150+
.expect(400);
151+
});
152+
153+
it('DELETE /:id - deletes a webhook', async () => {
154+
const { agent, team } = await getLoggedInAgent(server);
155+
156+
// Create test webhook
157+
const webhook = await Webhook.create({
158+
...MOCK_WEBHOOK,
159+
team: team._id,
160+
});
161+
162+
await agent.delete(`/webhooks/${webhook._id}`).expect(200);
163+
164+
// Verify webhook was deleted
165+
const deletedWebhook = await Webhook.findById(webhook._id);
166+
expect(deletedWebhook).toBeNull();
167+
});
168+
169+
it('DELETE /:id - returns 200 when webhook does not exist', async () => {
170+
const { agent } = await getLoggedInAgent(server);
171+
172+
const nonExistentId = new Types.ObjectId().toString();
173+
174+
// This will succeed even if the ID doesn't exist, consistent with the implementation
175+
await agent.delete(`/webhooks/${nonExistentId}`).expect(200);
176+
});
177+
178+
it('DELETE /:id - returns 400 when ID is invalid', async () => {
179+
const { agent } = await getLoggedInAgent(server);
180+
181+
await agent.delete('/webhooks/invalid-id').expect(400);
182+
});
183+
});

0 commit comments

Comments
 (0)