Files
badgedex/test/badge.api.test.js
T
echomike 2104680c4e
Node.js CI / build (push) Successful in 5m28s
update failed test, increase timeout
2026-07-21 12:51:41 +02:00

257 lines
9.1 KiB
JavaScript

const request = require('supertest');
const express = require('express');
const helmet = require('helmet');
// --- Mock axios before requiring the router ---
jest.mock('axios');
const axios = require('axios');
const MOCK_SVG = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><title>SimpleIcons</title><path fill="#333" d="M12 2L2 22h20z"/></svg>`;
beforeEach(() => {
axios.get.mockResolvedValue({ status: 200, data: MOCK_SVG });
});
afterEach(() => {
jest.restoreAllMocks();
});
// Fresh app for each test to avoid middleware leakage
function createApp() {
const badgeRouter = require('../src/api/badge');
const app = express();
app.use(helmet({ crossOriginResourcePolicy: false }));
app.use(badgeRouter);
return app;
}
describe('GET /badge', () => {
// --- Basic happy-path tests ---
it('should return SVG with default params', async () => {
const res = await request(createApp()).get('/badge');
const body = Buffer.from(res.body).toString();
expect(res.statusCode).toBe(200);
expect(res.headers['content-type']).toMatch(/image\/svg\+xml/);
expect(body).toContain('<svg');
expect(body).toContain('<rect');
}, 20000);
it('should use custom icon and label', async () => {
const res = await request(createApp()).get('/badge?icon=github&label=GitHub');
const body = Buffer.from(res.body).toString();
expect(res.statusCode).toBe(200);
expect(body).toContain('GitHub');
expect(body).toContain('<svg');
});
it('should use label from SVG title if label param is missing', async () => {
const res = await request(createApp()).get('/badge?icon=github');
const body = Buffer.from(res.body).toString();
expect(res.statusCode).toBe(200);
expect(body).toContain('<svg');
expect(body.toLowerCase()).toContain('simpleicons');
});
// --- Background & colour ---
it('should set icon background to transparent', async () => {
const res = await request(createApp()).get('/badge?bgicon=none');
const body = Buffer.from(res.body).toString();
expect(res.statusCode).toBe(200);
expect(body).toContain('fill="none"');
});
it('should set label background to transparent', async () => {
const res = await request(createApp()).get('/badge?bglabel=none');
const body = Buffer.from(res.body).toString();
expect(res.statusCode).toBe(200);
expect(body).toContain('fill="none"');
});
it('should apply custom bgicon and bglabel colours', async () => {
const res = await request(createApp()).get('/badge?bgicon=red&bglabel=blue');
const body = Buffer.from(res.body).toString();
expect(res.statusCode).toBe(200);
expect(body).toContain('fill="red"');
expect(body).toContain('fill="blue"');
});
it('should apply custom text colour', async () => {
const res = await request(createApp()).get('/badge?color=white');
const body = Buffer.from(res.body).toString();
expect(res.statusCode).toBe(200);
expect(body).toContain('fill="white"');
});
// --- Styles ---
it('should use flat style with rounded corners (rx=3)', async () => {
const res = await request(createApp()).get('/badge?style=flat');
const body = Buffer.from(res.body).toString();
expect(res.statusCode).toBe(200);
expect(body).toContain('rx="3"');
});
it('should use rect style with square corners (rx=0)', async () => {
const res = await request(createApp()).get('/badge?style=rect');
const body = Buffer.from(res.body).toString();
expect(res.statusCode).toBe(200);
expect(body).toContain('rx="0"');
});
it('should fall back to rect for invalid style', async () => {
const res = await request(createApp()).get('/badge?style=invalid');
const body = Buffer.from(res.body).toString();
expect(res.statusCode).toBe(200);
expect(body).toContain('rx="0"');
});
// --- labelpos ---
it('should handle labelpos left', async () => {
const res = await request(createApp()).get('/badge?labelpos=left');
const body = Buffer.from(res.body).toString();
expect(res.statusCode).toBe(200);
expect(body).toContain('<rect');
expect(body).toContain('<text');
});
it('should handle labelpos above', async () => {
const res = await request(createApp()).get('/badge?labelpos=above');
const body = Buffer.from(res.body).toString();
expect(res.statusCode).toBe(200);
expect(body).toContain('<rect');
expect(body).toContain('<text');
});
it('should handle labelpos below', async () => {
const res = await request(createApp()).get('/badge?labelpos=below');
const body = Buffer.from(res.body).toString();
expect(res.statusCode).toBe(200);
expect(body).toContain('<rect');
expect(body).toContain('<text');
});
it('should fall back to right for invalid labelpos', async () => {
const res = await request(createApp()).get('/badge?labelpos=diagonal');
const body = Buffer.from(res.body).toString();
expect(res.statusCode).toBe(200);
expect(body).toContain('<svg');
});
// --- fontweight ---
it('should handle custom fontweight bold', async () => {
const res = await request(createApp()).get('/badge?fontweight=bold');
const body = Buffer.from(res.body).toString();
expect(res.statusCode).toBe(200);
expect(body).toContain('font-weight="bold"');
});
it('should handle numeric fontweight 600', async () => {
const res = await request(createApp()).get('/badge?fontweight=600');
const body = Buffer.from(res.body).toString();
expect(res.statusCode).toBe(200);
expect(body).toContain('font-weight="600"');
});
it('should fall back to normal for invalid fontweight', async () => {
const res = await request(createApp()).get('/badge?fontweight=ultra');
const body = Buffer.from(res.body).toString();
expect(res.statusCode).toBe(200);
expect(body).toContain('font-weight="normal"');
});
// --- size ---
it('should handle minimum valid size (8)', async () => {
const res = await request(createApp()).get('/badge?size=8');
const body = Buffer.from(res.body).toString();
expect(res.statusCode).toBe(200);
expect(body).toContain('<svg');
});
it('should handle large size (128)', async () => {
const res = await request(createApp()).get('/badge?size=128');
const body = Buffer.from(res.body).toString();
expect(res.statusCode).toBe(200);
expect(body).toContain('<svg');
});
it('should fall back to default size for NaN', async () => {
const res = await request(createApp()).get('/badge?size=abc');
const body = Buffer.from(res.body).toString();
expect(res.statusCode).toBe(200);
expect(body).toContain('<svg');
});
it('should fall back to default size for negative value', async () => {
const res = await request(createApp()).get('/badge?size=-5');
const body = Buffer.from(res.body).toString();
expect(res.statusCode).toBe(200);
expect(body).toContain('<svg');
});
it('should fall back to default size for value above max', async () => {
const res = await request(createApp()).get('/badge?size=9999');
const body = Buffer.from(res.body).toString();
expect(res.statusCode).toBe(200);
expect(body).toContain('<svg');
});
// --- XSS / injection ---
it('should escape XSS in label', async () => {
const res = await request(createApp()).get('/badge?label=<script>alert(1)</script>');
const body = Buffer.from(res.body).toString();
expect(res.statusCode).toBe(200);
expect(body).not.toContain('<script>');
expect(body).toContain('&lt;script&gt;');
});
it('should escape quotes in label', async () => {
const res = await request(createApp()).get('/badge?label=he%20said%20%22hi%22');
const body = Buffer.from(res.body).toString();
expect(res.statusCode).toBe(200);
expect(body).toContain('&quot;');
});
it('should reject invalid color with injection attempt', async () => {
const res = await request(createApp()).get('/badge?color=javascript:alert(1)');
const body = Buffer.from(res.body).toString();
expect(res.statusCode).toBe(200);
expect(body).not.toContain('javascript:');
});
// --- CDN error handling ---
it('should return 404 when CDN returns 404', async () => {
axios.get.mockRejectedValue({ response: { status: 404 } });
const res = await request(createApp()).get('/badge?icon=notarealicon');
expect(res.statusCode).toBe(404);
});
it('should return 500 on unexpected CDN error', async () => {
axios.get.mockRejectedValue(new Error('Network timeout'));
const res = await request(createApp()).get('/badge?icon=github');
expect(res.statusCode).toBe(500);
expect(res.text).toBe('Internal Server Error');
});
// --- SVG structure ---
it('should include drop shadow filter', async () => {
const res = await request(createApp()).get('/badge');
const body = Buffer.from(res.body).toString();
expect(body).toContain('filter id="shadow"');
expect(body).toContain('feDropShadow');
});
it('should include xmlns', async () => {
const res = await request(createApp()).get('/badge');
const body = Buffer.from(res.body).toString();
expect(body).toContain('xmlns="http://www.w3.org/2000/svg"');
});
});