Files
LambdaMail/services/auth/src/pagination.test.ts
T
Lucas RayanandClaude Opus 5 52bf43b74f feat(admin): paginacao e filtragem no servidor, e edicao de utilizadores
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>
2026-08-24 09:19:49 -03:00

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);
});
});