SuperTest API Testing — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited SuperTest API Testing (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
This skill makes an AI agent write integration tests for Express/Koa/Fastify-compatible Node HTTP apps using SuperTest: pass the app object directly to request() so no port is bound, chain .expect() for status/header checks, and assert response bodies with Jest matchers. Trigger it when a Node project exposes an Express app, when the user asks to test REST endpoints without spinning up a server, or when supertest is already in devDependencies.
request(app) binds to an ephemeral port per request and tears it down — no app.listen(), no port conflicts, no orphaned servers in CI.app.ts exports the Express app, server.ts calls listen(). Tests import app.ts only.await means the test passes before the request even fires.expect(res.body).toMatchObject(...) where failure diffs are readable.beforeEach), and make cleanup idempotent. Order-dependent suites rot within a sprint.npm install --save-dev supertest @types/supertest jest ts-jest @types/jestThe app/server split that makes everything testable:
// src/app.ts
import express from 'express';
import { usersRouter } from './routes/users';
export function createApp(): express.Express {
const app = express();
app.use(express.json());
app.use('/api/users', usersRouter);
app.get('/health', (_req, res) => res.json({ status: 'ok' }));
return app;
}// src/server.ts — the ONLY file that listens; never imported by tests
import { createApp } from './app';
const port = Number(process.env.PORT ?? 3000);
createApp().listen(port, () => console.log(`listening on :${port}`));First test:
// src/app.test.ts
import request from 'supertest';
import { createApp } from './app';
const app = createApp();
describe('GET /health', () => {
it('responds 200 with status ok', async () => {
const res = await request(app)
.get('/health')
.expect('Content-Type', /json/)
.expect(200);
expect(res.body).toEqual({ status: 'ok' });
});
});import request from 'supertest';
import { createApp } from './app';
import { resetDb } from '../test/helpers/db';
const app = createApp();
beforeEach(async () => {
await resetDb();
});
describe('POST /api/users', () => {
it('creates a user and returns 201 with the persisted record', async () => {
const res = await request(app)
.post('/api/users')
.send({ email: '[email protected]', name: 'Mira' })
.expect(201);
expect(res.body).toMatchObject({
email: '[email protected]',
name: 'Mira',
});
expect(res.body.id).toEqual(expect.any(String));
// Round-trip: the created resource is retrievable
const fetched = await request(app).get(`/api/users/${res.body.id}`).expect(200);
expect(fetched.body.email).toBe('[email protected]');
});
it('rejects an invalid email with 400 and a field-level error', async () => {
const res = await request(app)
.post('/api/users')
.send({ email: 'not-an-email', name: 'Mira' })
.expect(400);
expect(res.body.errors).toContainEqual(
expect.objectContaining({ field: 'email' }),
);
});
});// test/helpers/auth.ts — log in once per suite, reuse the token
import request from 'supertest';
import type { Express } from 'express';
export async function getAuthToken(app: Express): Promise<string> {
const res = await request(app)
.post('/api/auth/login')
.send({ email: '[email protected]', password: 'test-password-123' })
.expect(200);
return res.body.token as string;
}import request from 'supertest';
import { createApp } from './app';
import { getAuthToken } from '../test/helpers/auth';
const app = createApp();
let token: string;
beforeAll(async () => {
token = await getAuthToken(app);
});
describe('DELETE /api/users/:id', () => {
it('returns 401 without a token', async () => {
await request(app).delete('/api/users/u_123').expect(401);
});
it('deletes with a valid bearer token', async () => {
await request(app)
.delete('/api/users/u_123')
.set('Authorization', `Bearer ${token}`)
.expect(204);
});
});// Query strings via .query() — never hand-concatenate
const res = await request(app)
.get('/api/users')
.query({ page: 2, limit: 10, sort: 'createdAt' })
.expect(200);
expect(res.body.items).toHaveLength(10);
expect(res.body.page).toBe(2);
// multipart upload
await request(app)
.post('/api/avatars')
.set('Authorization', `Bearer ${token}`)
.attach('avatar', 'test/fixtures/avatar.png')
.field('alt', 'profile picture')
.expect(201);
// Function form of .expect() for response-wide invariants
await request(app)
.get('/api/users')
.expect(200)
.expect((response) => {
if (response.body.items.some((u: { password?: string }) => u.password)) {
throw new Error('password leaked in list endpoint');
}
});// Persist cookies across requests with an agent
const agent = request.agent(app);
await agent
.post('/api/auth/login')
.send({ email: '[email protected]', password: 'test-password-123' })
.expect(200);
// agent carries the session cookie automatically
await agent.get('/api/me').expect(200);'returns 409 when email already exists', not 'test create user 2'.jest --runInBand) or give each worker its own schema; parallel workers on one mutable DB produce heisenbugs.Content-Type assertions as regex (/json/) — servers append ; charset=utf-8.jest.setup.ts that fails tests on unhandled promise rejections; SuperTest chains silently swallow them otherwise.await app.ready() before passing app.server to request().request(app) exists precisely so you never listen.@typescript-eslint/no-floating-promises to make this a lint error.toMatchObject plus expect.any(String) for generated fields; full-body equality breaks on every schema addition.supertest is in devDependencies, or the user asks to test Express/Koa/NestJS HTTP endpoints.app.listen, missing await, or shared mutable test data.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.