Skip to content

Commit 3e61db9

Browse files
committed
Split request validation and batch mode tests into different files
1 parent 761a5db commit 3e61db9

File tree

6 files changed

+192
-160
lines changed

6 files changed

+192
-160
lines changed

test/integration/api/web_analytics.test.ts

Lines changed: 0 additions & 143 deletions
This file was deleted.
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import {describe, expect, it, beforeEach, beforeAll, vi} from 'vitest';
2+
import {FastifyInstance, FastifyRequest, FastifyReply} from 'fastify';
3+
import {expectResponse} from '../../../utils/assertions';
4+
import {createPubSubSpy} from '../../../utils/pubsub-spy';
5+
import defaultValidRequestQuery from '../../../utils/fixtures/defaultValidRequestQuery.json';
6+
import defaultValidRequestHeaders from '../../../utils/fixtures/defaultValidRequestHeaders.json';
7+
import defaultValidRequestBody from '../../../utils/fixtures/defaultValidRequestBody.json';
8+
import headersWithoutSiteUuid from '../../../utils/fixtures/headersWithoutSiteUuid.json';
9+
import {initializeApp} from '../../../../src/initializeApp';
10+
import {EventPublisher} from '../../../../src/services/events/publisher';
11+
12+
const preHandlerStub = async (_request: FastifyRequest, reply: FastifyReply) => {
13+
reply.code(202);
14+
};
15+
16+
describe('POST /tb/web_analytics', () => {
17+
let app: FastifyInstance;
18+
19+
describe('Batch Mode - Publishing to pub/sub', function () {
20+
let pubSubSpy: ReturnType<typeof createPubSubSpy>;
21+
const pageHitsRawTopic: string = 'page-hits-raw';
22+
23+
beforeAll(async function () {
24+
vi.stubEnv('PUBSUB_TOPIC_PAGE_HITS_RAW', pageHitsRawTopic);
25+
});
26+
beforeEach(async function () {
27+
app = await initializeApp({isWorkerMode: false});
28+
app.addHook('preHandler', preHandlerStub);
29+
pubSubSpy = createPubSubSpy();
30+
EventPublisher.resetInstance(pubSubSpy.mockPubSub as any);
31+
});
32+
33+
it('should transform the request body and publish to pub/sub', async function () {
34+
await app.inject({
35+
method: 'POST',
36+
url: '/tb/web_analytics',
37+
query: defaultValidRequestQuery,
38+
headers: defaultValidRequestHeaders,
39+
body: defaultValidRequestBody
40+
});
41+
42+
pubSubSpy.expectPublishedMessageToTopic(pageHitsRawTopic).withMessageData({
43+
timestamp: expect.any(String),
44+
action: 'page_hit',
45+
version: '1',
46+
site_uuid: defaultValidRequestHeaders['x-site-uuid'],
47+
payload: {
48+
event_id: defaultValidRequestBody.payload.event_id,
49+
href: 'https://www.example.com/',
50+
pathname: '/',
51+
member_uuid: 'undefined',
52+
member_status: 'undefined',
53+
post_uuid: 'undefined',
54+
post_type: 'null',
55+
parsedReferrer: {
56+
medium: '',
57+
source: '',
58+
url: ''
59+
},
60+
locale: 'en-US',
61+
location: 'US',
62+
referrer: null
63+
},
64+
meta: {
65+
ip: expect.any(String),
66+
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36'
67+
}
68+
});
69+
});
70+
71+
it('should not publish a message if the request fails validation', async function () {
72+
const response = await app.inject({
73+
method: 'POST',
74+
url: '/tb/web_analytics',
75+
query: defaultValidRequestQuery,
76+
headers: headersWithoutSiteUuid,
77+
body: defaultValidRequestBody
78+
});
79+
expectResponse({
80+
response,
81+
statusCode: 400,
82+
errorType: 'Bad Request',
83+
message: 'headers must have required property \'x-site-uuid\''
84+
});
85+
pubSubSpy.expectNoMessagesPublished();
86+
});
87+
});
88+
});
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import {describe, it, beforeEach} from 'vitest';
2+
import {FastifyInstance, FastifyRequest, FastifyReply} from 'fastify';
3+
import {expectResponse} from '../../../utils/assertions';
4+
import defaultValidRequestQuery from '../../../utils/fixtures/defaultValidRequestQuery.json';
5+
import defaultValidRequestHeaders from '../../../utils/fixtures/defaultValidRequestHeaders.json';
6+
import defaultValidRequestBody from '../../../utils/fixtures/defaultValidRequestBody.json';
7+
import headersWithInvalidContentType from '../../../utils/fixtures/headersWithInvalidContentType.json';
8+
import headersWithoutUserAgent from '../../../utils/fixtures/headersWithoutUserAgent.json';
9+
import headersWithoutSiteUuid from '../../../utils/fixtures/headersWithoutSiteUuid.json';
10+
import {initializeApp} from '../../../../src/initializeApp';
11+
12+
const preHandlerStub = async (_request: FastifyRequest, reply: FastifyReply) => {
13+
reply.code(202);
14+
};
15+
16+
describe('POST /tb/web_analytics', () => {
17+
let app: FastifyInstance;
18+
19+
describe('Request validation', function () {
20+
beforeEach(async function () {
21+
app = await initializeApp({isWorkerMode: false});
22+
app.addHook('preHandler', preHandlerStub);
23+
});
24+
25+
it('should accept a default valid request from the tracking script', async function () {
26+
const response = await app.inject({
27+
method: 'POST',
28+
url: '/tb/web_analytics',
29+
query: defaultValidRequestQuery,
30+
headers: defaultValidRequestHeaders,
31+
body: defaultValidRequestBody
32+
});
33+
expectResponse({response, statusCode: 202});
34+
});
35+
36+
describe('requests with missing or invalid required headers', function () {
37+
it('should reject a request with a missing site uuid header', async function () {
38+
const response = await app.inject({
39+
method: 'POST',
40+
url: '/tb/web_analytics',
41+
query: defaultValidRequestQuery,
42+
headers: headersWithoutSiteUuid,
43+
body: defaultValidRequestBody
44+
});
45+
expectResponse({
46+
response,
47+
statusCode: 400,
48+
errorType: 'Bad Request',
49+
message: 'headers must have required property \'x-site-uuid\''
50+
});
51+
});
52+
53+
it('should reject a request with a missing user agent header', async function () {
54+
// fastify.inject() adds a default user-agent header, so we need to remove it before validation
55+
app.addHook('onRequest', async (request) => {
56+
delete request.headers['user-agent'];
57+
});
58+
59+
const response = await app.inject({
60+
method: 'POST',
61+
url: '/tb/web_analytics',
62+
query: defaultValidRequestQuery,
63+
headers: headersWithoutUserAgent,
64+
body: defaultValidRequestBody
65+
});
66+
expectResponse({
67+
response,
68+
statusCode: 400,
69+
errorType: 'Bad Request',
70+
message: 'headers must have required property \'user-agent\''
71+
});
72+
});
73+
74+
it('should reject a request with a content type other than application/json', async function () {
75+
const response = await app.inject({
76+
method: 'POST',
77+
url: '/tb/web_analytics',
78+
query: defaultValidRequestQuery,
79+
headers: headersWithInvalidContentType,
80+
body: defaultValidRequestBody
81+
});
82+
expectResponse({
83+
response,
84+
statusCode: 415,
85+
errorType: 'Unsupported Media Type',
86+
message: 'Unsupported Media Type: application/xml'
87+
});
88+
});
89+
});
90+
});
91+
});

test/utils/assertions.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import {expect} from 'vitest';
2+
import type {Response} from 'light-my-request';
3+
4+
export function expectResponse({response, statusCode, errorType, message}: {response: Response, statusCode: number, errorType?: string, message?: string}) {
5+
const bodyJson = JSON.parse(response.body);
6+
expect(response.statusCode).toBe(statusCode);
7+
if (errorType) {
8+
expect(bodyJson.error).toBe(errorType);
9+
}
10+
if (message) {
11+
expect(bodyJson.message).toBe(message);
12+
}
13+
}

test/utils/assertions/assertions.ts

Lines changed: 0 additions & 16 deletions
This file was deleted.

test/utils/assertions/index.ts

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)