Files
ECC/docker/context-profiles/complex-eval/reference4/chained-tickets/test/links.test.js
T

107 lines
3.9 KiB
JavaScript

'use strict';
const test = require('node:test');
const assert = require('node:assert/strict');
const { createApp } = require('../src/app');
process.env.DATA_FILE = require('node:path').join(require('node:os').tmpdir(),
`shortlink-test-${process.pid}.json`);
let server;
let port;
test.before(async () => {
server = createApp();
await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
port = server.address().port;
});
test.after(() => server.close());
const post = body => fetch(`http://127.0.0.1:${port}/links`, {
method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(body) });
const get = p => fetch(`http://127.0.0.1:${port}${p}`, { redirect: 'manual' });
test('creates a link with default expiry', async () => {
const res = await post({ url: 'https://example.com/a' });
assert.equal(res.status, 201);
const body = await res.json();
assert.match(body.code, /^[A-Za-z0-9]{6,10}$/);
assert.ok(Date.parse(body.expiresAt) > Date.now());
});
test('redirects with 302 and location', async () => {
const { code } = await (await post({ url: 'https://example.com/b' })).json();
const res = await get(`/${code}`);
assert.equal(res.status, 302);
assert.equal(res.headers.get('location'), 'https://example.com/b');
});
test('unknown code is a 404 envelope', async () => {
const res = await get('/zzzzzz');
assert.equal(res.status, 404);
assert.equal((await res.json()).error.code, 'NOT_FOUND');
});
test('invalid url is a 400 envelope', async () => {
const res = await post({ url: 'notaurl' });
assert.equal(res.status, 400);
assert.equal((await res.json()).error.code, 'INVALID_URL');
});
test('javascript scheme rejected', async () => {
const res = await post({ url: 'javascript:alert(1)' });
assert.equal(res.status, 400);
});
test('ttl bounds enforced', async () => {
const res = await post({ url: 'https://example.com', ttlSeconds: 99999999 });
assert.equal(res.status, 400);
assert.equal((await res.json()).error.code, 'INVALID_TTL');
});
test('delete flow', async () => {
const { code } = await (await post({ url: 'https://example.com/c' })).json();
const del = await fetch(`http://127.0.0.1:${port}/links/${code}`, { method: 'DELETE' });
assert.equal(del.status, 204);
assert.equal((await get(`/${code}`)).status, 404);
});
test('stats start at zero and count redirects', async () => {
const { code } = await (await post({ url: 'https://example.com/d' })).json();
const zero = await (await fetch(`http://127.0.0.1:${port}/links/${code}/stats`)).json();
assert.equal(zero.hits, 0);
await get(`/${code}`);
await get(`/${code}`);
const two = await (await fetch(`http://127.0.0.1:${port}/links/${code}/stats`)).json();
assert.equal(two.hits, 2);
});
test('stats for unknown code are a 404 envelope', async () => {
const res = await fetch(`http://127.0.0.1:${port}/links/zzzzzz/stats`);
assert.equal(res.status, 404);
assert.equal((await res.json()).error.code, 'NOT_FOUND');
});
test('expired links are 410', async () => {
const { code } = await (await post({ url: 'https://example.com/e', ttlSeconds: 1 })).json();
await new Promise(resolve => setTimeout(resolve, 1200));
assert.equal((await get(`/${code}`)).status, 410);
});
test('malformed json is a 400 envelope', async () => {
const res = await fetch(`http://127.0.0.1:${port}/links`, {
method: 'POST', headers: { 'content-type': 'application/json' }, body: '{nope' });
assert.equal(res.status, 400);
assert.equal((await res.json()).error.code, 'INVALID_JSON');
});
test('error responses never leak html', async () => {
const res = await get('/zzzzzz');
assert.match(res.headers.get('content-type'), /application\/json/);
});
// Last: the flood exhausts the per-client rate-limit bucket.
test('rate limiting kicks in under a flood', async () => {
const responses = await Promise.all(Array.from({ length: 30 }, (_, i) =>
post({ url: `https://example.com/flood-${i}` })));
assert.ok(responses.some(r => r.status === 429));
});