As listas do painel respondiam com um LIMIT fixo e nenhum filtro: 200 caixas, 50 trabalhos na fila. Numa instalacao maior nao havia forma de chegar ao resto, e a fila so podia ser inspeccionada pela cabeca -- justamente quando esta acumulada e que alguem precisa de olhar para ela. - parsePageParams com testes proprios: pagina e tamanho tem de ser inteiros positivos, o tamanho e limitado, e um valor invalido cai no seguro em vez de chegar ao SQL como NaN, LIMIT negativo ou OFFSET fraccionario - o termo de pesquisa vai como parametro ligado, nunca interpolado; ha um teste que tenta terminar a instrucao e largar a tabela - a contagem usa o mesmo filtro da pagina, senao o numero de paginas descreve um conjunto de linhas diferente do que e mostrado - PATCH /admin/mailboxes/:id edita papel, quota, idioma, estado e liberta um bloqueio; so um super admin pode conceder super admin, ou um admin de dominio promovia-se para fora do seu proprio dominio Apelidos por utilizador: um apelido nao pertence a um utilizador, pertence ao dominio e leva uma lista de destinos que pode incluir enderecos noutros servidores, e um catch-all e do dominio inteiro. O que pode ser mostrado dentro de um utilizador sao os apelidos que entregam na caixa dele, e e isso que /admin/mailboxes/:id/aliases responde. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
61 lines
2.3 KiB
TypeScript
61 lines
2.3 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import { parsePageParams, MAX_PAGE_SIZE } from "./pagination.js";
|
|
|
|
const at = (qs: string) => new URL(`http://x/api${qs}`);
|
|
|
|
describe("pagination parameters", () => {
|
|
it("defaults to the first page when nothing is asked for", () => {
|
|
const p = parsePageParams(at(""));
|
|
expect(p.page).toBe(1);
|
|
expect(p.offset).toBe(0);
|
|
expect(p.search).toBe("");
|
|
});
|
|
|
|
it("turns a page number into the right offset", () => {
|
|
const p = parsePageParams(at("?page=3&page_size=25"));
|
|
expect(p.pageSize).toBe(25);
|
|
expect(p.offset).toBe(50);
|
|
});
|
|
|
|
// A page size taken at face value is a denial of service against your own
|
|
// database: one request asking for ten million rows.
|
|
it("caps the page size", () => {
|
|
expect(parsePageParams(at("?page_size=100000")).pageSize).toBe(MAX_PAGE_SIZE);
|
|
});
|
|
|
|
// Anything that is not a sane positive integer must land on the default
|
|
// rather than reaching SQL as NaN, a negative LIMIT, or a fractional OFFSET.
|
|
it.each(["0", "-5", "abc", "1.5", "", "1e9", "Infinity", "NaN"])(
|
|
"falls back to a safe page for page=%s",
|
|
(value) => {
|
|
const p = parsePageParams(at(`?page=${value}`));
|
|
expect(Number.isInteger(p.page)).toBe(true);
|
|
expect(p.page).toBeGreaterThanOrEqual(1);
|
|
expect(Number.isInteger(p.offset)).toBe(true);
|
|
expect(p.offset).toBeGreaterThanOrEqual(0);
|
|
},
|
|
);
|
|
|
|
it.each(["0", "-5", "abc", "1.5", "NaN"])(
|
|
"falls back to a safe page size for page_size=%s",
|
|
(value) => {
|
|
const p = parsePageParams(at(`?page_size=${value}`));
|
|
expect(Number.isInteger(p.pageSize)).toBe(true);
|
|
expect(p.pageSize).toBeGreaterThan(0);
|
|
expect(p.pageSize).toBeLessThanOrEqual(MAX_PAGE_SIZE);
|
|
},
|
|
);
|
|
|
|
it("trims the search term and keeps it as plain text", () => {
|
|
// It reaches SQL as a bound parameter, so the wildcards a user types are
|
|
// theirs to type; what must not happen is it arriving untrimmed and
|
|
// matching nothing.
|
|
expect(parsePageParams(at("?search=%20%20ana%20%20")).search).toBe("ana");
|
|
});
|
|
|
|
it("ignores an over-long search term instead of sending it to the database", () => {
|
|
const p = parsePageParams(at(`?search=${"a".repeat(500)}`));
|
|
expect(p.search.length).toBeLessThanOrEqual(200);
|
|
});
|
|
});
|