mirror of
https://github.com/misenhower/splatoon2.ink.git
synced 2026-09-29 04:37:00 -05:00
90 lines
3.6 KiB
JavaScript
90 lines
3.6 KiB
JavaScript
import { expect, it, vi, afterEach } from 'vitest';
|
|
import { adminRequest } from './src/admin/routes.mjs';
|
|
import { verifyAccess } from './src/admin/access.mjs';
|
|
vi.mock('./src/admin/access.mjs', () => ({ verifyAccess: vi.fn() }));
|
|
afterEach(() => vi.resetAllMocks());
|
|
|
|
const url = 'https://admin.example.test';
|
|
const post = (mode = 'both', origin = url) =>
|
|
new Request(url + '/admin/api/run', {
|
|
method: 'POST',
|
|
headers: { Origin: origin, 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ mode }),
|
|
});
|
|
|
|
it('requires Access before exposing the panel, status or actions', async () => {
|
|
verifyAccess.mockResolvedValue(null);
|
|
|
|
const get = vi.fn();
|
|
|
|
for (const request of [
|
|
new Request(url + '/'),
|
|
new Request(url + '/admin/'),
|
|
new Request(url + '/admin/api/status'),
|
|
post(),
|
|
])
|
|
expect((await adminRequest(request, {}, get)).status).toBe(401);
|
|
|
|
expect(get).not.toHaveBeenCalled();
|
|
});
|
|
it('rejects cross-origin requests and unknown modes before scheduling work', async () => {
|
|
verifyAccess.mockResolvedValue({ email: 'admin@example.test' });
|
|
|
|
const get = vi.fn();
|
|
|
|
expect((await adminRequest(post('both', 'https://other.test'), {}, get)).status).toBe(403);
|
|
expect((await adminRequest(post('force'), {}, get)).status).toBe(400);
|
|
expect(get).not.toHaveBeenCalled();
|
|
});
|
|
it('returns an accepted run immediately and reports overlap without starting another', async () => {
|
|
verifyAccess.mockResolvedValue({ email: 'admin@example.test' });
|
|
|
|
const startManual = vi.fn(async mode => ({
|
|
ok: true,
|
|
run: { id: 'one', mode, status: 'queued' },
|
|
}));
|
|
|
|
expect((await adminRequest(post('social'), {}, () => ({ startManual }))).status).toBe(202);
|
|
expect(startManual).toHaveBeenCalledWith('social');
|
|
|
|
startManual.mockResolvedValue({ ok: false, busy: true });
|
|
|
|
expect((await adminRequest(post(), {}, () => ({ startManual }))).status).toBe(409);
|
|
});
|
|
it.each(['/', '/admin/'])(
|
|
'serves the mobile panel at %s with no-store and a nonce-based content policy',
|
|
async path => {
|
|
verifyAccess.mockResolvedValue({ email: 'admin@example.test' });
|
|
|
|
const response = await adminRequest(new Request(url + path), {}, vi.fn());
|
|
|
|
expect(response.headers.get('cache-control')).toBe('no-store');
|
|
expect(response.headers.get('content-security-policy')).toContain('frame-ancestors \'none\'');
|
|
expect(await response.text()).not.toContain('__NONCE__');
|
|
},
|
|
);
|
|
|
|
it('protects scheduling changes with Access, same-origin checks and boolean validation', async () => {
|
|
const request = (enabled, origin = url) => new Request(url + '/admin/api/scheduling', {
|
|
method: 'POST',
|
|
headers: { Origin: origin, 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ enabled }),
|
|
});
|
|
const setAutomaticScheduling = vi.fn(async enabled => ({ ok: true, automaticSchedulingEnabled: enabled }));
|
|
const scheduler = () => ({ setAutomaticScheduling });
|
|
|
|
verifyAccess.mockResolvedValue(null);
|
|
expect((await adminRequest(request(false), {}, scheduler)).status).toBe(401);
|
|
|
|
verifyAccess.mockResolvedValue({ email: 'admin@example.test' });
|
|
expect((await adminRequest(request(false, 'https://other.test'), {}, scheduler)).status).toBe(403);
|
|
expect((await adminRequest(request('false'), {}, scheduler)).status).toBe(400);
|
|
expect(setAutomaticScheduling).not.toHaveBeenCalled();
|
|
|
|
expect((await adminRequest(request(false), {}, scheduler)).status).toBe(200);
|
|
expect(setAutomaticScheduling).toHaveBeenCalledWith(false);
|
|
|
|
setAutomaticScheduling.mockResolvedValue({ ok: false, busy: true });
|
|
expect((await adminRequest(request(true), {}, scheduler)).status).toBe(409);
|
|
});
|