step 10.16: done
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
// Step 10.16: Leaderboard public + hero profile public — no login required.
|
||||
const { describe, it, before, after } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const { setup, teardown, cleanup } = require('../helpers');
|
||||
|
||||
describe('step 10.16 — leaderboard & hero profile public', () => {
|
||||
let baseUrl;
|
||||
|
||||
before(async () => {
|
||||
baseUrl = await setup();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await teardown();
|
||||
cleanup();
|
||||
});
|
||||
|
||||
// ─── Leaderboard: anonymous access ────────────────────────────────────
|
||||
|
||||
it('anonymous GET /api/leaderboard?type=level returns 200', async () => {
|
||||
const res = await fetch(`${baseUrl}/api/leaderboard?type=level`);
|
||||
assert.equal(res.status, 200);
|
||||
const json = await res.json();
|
||||
assert.equal(json.ok, true);
|
||||
assert.ok(Array.isArray(json.data.entries));
|
||||
assert.ok(json.data.pagination);
|
||||
});
|
||||
|
||||
it('anonymous GET /api/leaderboard?type=gold returns 200', async () => {
|
||||
const res = await fetch(`${baseUrl}/api/leaderboard?type=gold`);
|
||||
assert.equal(res.status, 200);
|
||||
const json = await res.json();
|
||||
assert.equal(json.ok, true);
|
||||
});
|
||||
|
||||
it('anonymous GET /api/leaderboard?type=kills returns 200', async () => {
|
||||
const res = await fetch(`${baseUrl}/api/leaderboard?type=kills`);
|
||||
assert.equal(res.status, 200);
|
||||
const json = await res.json();
|
||||
assert.equal(json.ok, true);
|
||||
});
|
||||
|
||||
it('leaderboard response contains no user id / username / password', async () => {
|
||||
const res = await fetch(`${baseUrl}/api/leaderboard?type=level`);
|
||||
assert.equal(res.status, 200);
|
||||
const json = await res.json();
|
||||
|
||||
for (const entry of json.data.entries) {
|
||||
assert.ok(!('id' in entry), `entry should not have 'id': ${entry.name}`);
|
||||
assert.ok(!('username' in entry), `entry should not have 'username': ${entry.name}`);
|
||||
assert.ok(!('password' in entry), `entry should not have 'password': ${entry.name}`);
|
||||
assert.ok(!('password_hash' in entry), `entry should not have 'password_hash': ${entry.name}`);
|
||||
assert.ok(!('user_id' in entry), `entry should not have 'user_id': ${entry.name}`);
|
||||
}
|
||||
});
|
||||
|
||||
// ─── Leaderboard: validation ──────────────────────────────────────────
|
||||
|
||||
it('rejects SQL injection in type with 400', async () => {
|
||||
const res = await fetch(
|
||||
`${baseUrl}/api/leaderboard?type=level%20OR%201=1`,
|
||||
);
|
||||
const body = await res.json();
|
||||
assert.equal(res.status, 400);
|
||||
assert.equal(body.ok, false);
|
||||
assert.equal(body.error.code, 'VALIDATION_ERROR');
|
||||
});
|
||||
|
||||
it('rejects page=-1 with 400', async () => {
|
||||
const res = await fetch(`${baseUrl}/api/leaderboard?type=level&page=-1`);
|
||||
const body = await res.json();
|
||||
assert.equal(res.status, 400);
|
||||
assert.equal(body.ok, false);
|
||||
assert.equal(body.error.code, 'VALIDATION_ERROR');
|
||||
});
|
||||
|
||||
it('rejects page=abc with 400', async () => {
|
||||
const res = await fetch(`${baseUrl}/api/leaderboard?type=level&page=abc`);
|
||||
const body = await res.json();
|
||||
assert.equal(res.status, 400);
|
||||
assert.equal(body.ok, false);
|
||||
assert.equal(body.error.code, 'VALIDATION_ERROR');
|
||||
});
|
||||
|
||||
// ─── Hero profile: anonymous access (already public, verify) ──────────
|
||||
|
||||
it('anonymous GET /api/heroes/:name returns 200', async () => {
|
||||
// Create a hero first (fresh DB has no heroes).
|
||||
const regRes = await fetch(`${baseUrl}/api/auth/register`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ username: 'pubuser1', password: 'password123' }),
|
||||
});
|
||||
assert.equal(regRes.status, 201);
|
||||
|
||||
const loginRes = await fetch(`${baseUrl}/api/auth/login`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ username: 'pubuser1', password: 'password123' }),
|
||||
});
|
||||
assert.equal(loginRes.status, 200);
|
||||
const cookie = loginRes.headers.get('set-cookie').split(';')[0];
|
||||
|
||||
const charRes = await fetch(`${baseUrl}/api/character`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', Cookie: cookie },
|
||||
body: JSON.stringify({ name: 'PublicHero16', class: 'warrior' }),
|
||||
});
|
||||
assert.equal(charRes.status, 201);
|
||||
|
||||
// Now access the profile anonymously.
|
||||
const res = await fetch(`${baseUrl}/api/heroes/PublicHero16`);
|
||||
assert.equal(res.status, 200);
|
||||
const json = await res.json();
|
||||
assert.equal(json.ok, true);
|
||||
assert.equal(json.data.name, 'PublicHero16');
|
||||
});
|
||||
|
||||
it('hero profile response contains no password / user_id', async () => {
|
||||
// Create a hero first.
|
||||
const regRes = await fetch(`${baseUrl}/api/auth/register`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ username: 'pubuser2', password: 'password123' }),
|
||||
});
|
||||
assert.equal(regRes.status, 201);
|
||||
|
||||
const loginRes = await fetch(`${baseUrl}/api/auth/login`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ username: 'pubuser2', password: 'password123' }),
|
||||
});
|
||||
assert.equal(loginRes.status, 200);
|
||||
const cookie = loginRes.headers.get('set-cookie').split(';')[0];
|
||||
|
||||
const charRes = await fetch(`${baseUrl}/api/character`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', Cookie: cookie },
|
||||
body: JSON.stringify({ name: 'PublicHero17', class: 'mage' }),
|
||||
});
|
||||
assert.equal(charRes.status, 201);
|
||||
|
||||
// Access the profile anonymously.
|
||||
const res = await fetch(`${baseUrl}/api/heroes/PublicHero17`);
|
||||
assert.equal(res.status, 200);
|
||||
const json = await res.json();
|
||||
const data = json.data;
|
||||
assert.ok(!('password' in data), 'should not have password');
|
||||
assert.ok(!('password_hash' in data), 'should not have password_hash');
|
||||
assert.ok(!('user_id' in data), 'should not have user_id');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user