Skip to content

Refatorações e ajustes finais da Milestone 2 - #45

Merged
AlAnNuB merged 18 commits into
mainfrom
final-adjustments
Mar 25, 2026
Merged

Refatorações e ajustes finais da Milestone 2#45
AlAnNuB merged 18 commits into
mainfrom
final-adjustments

Conversation

@AlAnNuB

@AlAnNuB AlAnNuB commented Mar 25, 2026

Copy link
Copy Markdown
Owner

Este PR agrega todos os ajustes finais conforme descrito na issue #44 e como explicado lá, irei parcialmente atualizando este PR com novos commits e atualizando a lista de tarefas 🤝

Sugestão: não olhe o diff agregado de todos os commits deste PR, pois ficará muito confuso. Olhe o diff de commit a commit: https://github.com/AlAnNuB/clone-tabnews/pull/45/commits

AlAnNuB added 18 commits March 25, 2026 18:05
…cation.getUser()`

Escolhi fazer esta refatoração, pois o método `getAuthenticatedUser()`estava muito longo. Então
dentro do contexto do model `authentication`,achei que continuaria legível com somente `getUser()` e
mantendo aassinatura com o objeto pedindo por `email` e `password`, e guardando oresultado na
variável `authenticatedUser` para se entender que é um"usuário autenticado" que está sendo buscado,
por exemplo:```js  const authenticatedUser = await authentication.getUser(    userInputValues.email,
userInputValues.password,  );
Faltou um `return` no final do handler do endpoint de status que não
causava nenhum bug, mas melhor adicionar por padronização e também
para evitar bugs futuros.

Esta sugestão foi apontada pelo aluno `lbpereira` 🤝
Padroniza o uso do caractere ` para envolver nomes de campos ou outros
termos técnicos nas descrições dos testes.
…gin}`

Após a introdução do `webserver` com o `origin` do serviço web, vários
testes continuaram apontando de forma fixa para `http://localhost:3000`.
Este commit padroniza o uso do `${webserver.origin}` e agora o servidor
local dos testes pode ser apontado para outro lugar apenas alterando uma
única linha dentro do `webserver`.
Estes dois módulos estavam listados como dependência para rodar o
serviço em produção, porém eles estão sendo usados exclusivamente pelo
`jest.config.js` e não há nenhum plano de usar esses módulos em outros
ambientes por enquanto.
…ookie()`

Estes dois métodos do componente `controller.js` estavam declarados como
funções assíncronas de forma indevida, pois operam apenas de forma
síncrona.
Este commit conserta o formato da data que utilizei em um dos testes
unitários do model `authorization`. Não teve nenhum impacto, pois o
objeto de `resource` estava apenas em memória e era usado diretamente
no filtro que hoje não faz validação do formato das coisas, mas no
irá fazer.
…` test descriptions

O termo "Running" casa melhor com a ação que um `POST` contra o endpoint
realmente faz que é de "Rodar" as migrações. Imagino que na hora de
duplicar o arquivo do teste, acabei esquecendo de atualizar a descrição
dos testes.

Este apontamento foi feito pelo aluno `IIsrael` 🤝
…s/[username]`

Depois de aplicar o filtro de saída que agora faz não devolver mais a
propriedade `email` do `user`, eu simplesmente removi a expectativa
deste valor ser retornado na resposta da API de todos os testes. E
apesar de que isto continua válido, no teste de atualização de email
ficou faltando a cobertura se a propriedade `email` foi atualizada de
forma persistende no banco de dados. Então este commit busca o usuário
no banco e verifica se de fato a informação foi alterada como deveria.

Isto foi apontado pelo aluno `IIsrael` 🤝
Antes estava sendo criado o objeto de router do `next-connect` e contra
este objeto é que estava sendo "anexado" os métodos disponíveis para um
certo endpoint na API (incluindo o `handler`) onde, depois disso, esse
objeto de router era exportado como `default` do arquivo do controller.
Mas agora com a refatoração deste commit, todo o export e encadeamento
de métodos e handler acontece de uma só vez, pois fica mais limpo e
direto de se ler o código. Isto é um tipo de refatoração que é muito bom
de se fazer quando você tem uma cobertua boa de testes `E2E` 🙏
Aqui eu faço o método `orchestrator.createSession()` aceitar um objeto
de `user` por inteiro (ao invés só do `id`) para ficar com a mesma
interface pública de `orchestrator.activateUser()`.
Os testes de `GET` do endpoint de status utiliza um usuário privilegiado
e era um teste que estava passando mesmo sem ter as migrations
executadas. Isto estava funcionando, pois numa bateria completa de
`npm test`, algum outro teste já teria feito o trabalho de criar as
tabelas, sendo que quando o teste era executado de forma isolada,
naturalmente quebrava por não ter a tabela `user` (necessária para criar
um usuário privilegiado). Então foi só questão de padronizar o hook
principal para limpar o banco de dados e rodar as migrations igual ao
restante dos outros testes.

Isto foi apontado pelo aluno `gleniosp` 🤝
O teste do `GET /api/v1/status` testava o retorno do endpoint com um
usuário anônimo e com um usuário privilegiado, mas não testava com um
usuário padrão, onde neste caso, não deveria vir a propriedade `version`
sobre o banco de dados. Esse teste então adiciona esta cobertura.
Esta modificação tem impacto real apenas para navegadores mais antigos,
antes de 2020 aproximadamente, porque os navegadores modernos já adotam
`SameSite=Lax` por padrão.

O `SameSite=Lax` faz o navegador ser mais criterioso na hora de enviar o
cookie de sessão (ou qualquer outro cookie), principalmente quando a
navegação ou a requisição foi iniciada a partir de outro site, por
exemplo, algum hacker tentando interferir em um site que você esteja
logado através de um outro site que ele tenha algum controle.

Então imagine o cenário:

1. Usuário está logado no FinTab usando um navegador antigo.
2. FinTab não definiu a propriedade `SameSite=Lax`.
3. Usuário acessa outro site externo em que hacker possui algum
controle, seja direto ou indireto (injeção de script por XSS).
3. Este site externo pode ter um comportamento malicioso de disparar uma
requisição `POST` contra um endpoint do FinTab que necessite de
autenticação, por exemplo, adicionar um comentário com um conteúdo de
marketing ou link para um vírus em uma publicação qualquer.
4. Neste cenário, o navegador irá reaproveitar a sessão ativa e outros
cookies do cookie jar do endereço do FinTab para executar esta ação,
efetivamente adicionando o comentário.
5. Agora, caso o cookie de sessão (ou qualquer outro cookie) esteja
marcado com `SameSite=Lax`, o navegador não irá enviar ele no cabeçalho
caso a origem da requisição tenha vindo de um outro endereço,
efetivamente bloqueando a tentativa do hacker de criar uma resposta
através da sua conta usando a sua sessão.

De qualquer forma, mesmo o `SameSite=Lax` sendo o padrão em navegadores
modernos, ainda assim faz sentido declarar ele no código de forma
explícita porque deixa a intenção de segurança documentada, evita
depender de comportamento implícito do navegador e abre as portas para
decidirmos mais para frente adotar o valor `Strict` e entender as
diferenças.

Fora tudo isso, o impacto da mudança é mínimo e o ganho de clareza e
previsibilidade compensa.

Isto foi apontado pelo aluno `arthurmousinho` 🤝
Nos testes sobre criar uma sessão válida, a propriedade `expires_at`
do objeto de sessão é calculada na camada da aplicação, antes da
persistência. Já a propriedade `created_at` é calculada depois, lá na
camada do banco de dados, o que faz uma sessão não ter exatamente
30 dias de expiração em milissegundos que seriam `2592000000` e ficando
então com valores muito próximos como `2591999991`, por exemplo, que
são 30 dias menos 9 milissegundos.

Os testes atuais já tentavam compensar esta diferença ao zerar os
segundos das datas envolvidas, mas é uma alternativa que possui um furo
dependendo de algumas condições como o virar do minuto. Tentei pegar
este comportamento para mostrar em uma aula, mas não consegui e isto
estava me agoniando ao pensar que algum aluno poderia ver o seu CI
quebrando e não entender o motivo.

Então para já evitar esta situação daqui para frente e depois de
sugestões de vários alunos, optei por adicionar uma margem de erro de
5 segundos entre o que é esperado (30 dias exatos de expiração) e o que
o objeto de sessão de fato possui de tempo de expiração.
Essa foi uma atualização bem simples de fazer. Os passos foram:

1. Primeiro verifiquei se eu estava com a versão certa do Node.js
ao rodar `node -v` e eu estava com a `v24.13.0`.
2. Rodei `npx npm-check-updates --format group`, que agrupa o resultado
por `patch`, `minor e `major`, e me foquei apenas no `patch` e `minor`.
3. Não utilizei a flag `-i` que habilita o modo interativo do módulo,
porque eu queria ir manualmente instalando as versões e testando.
4. Então num comando só eu instalei todas as novas versões dos módulos
que entraram no grupo `patch` e ficou assim (sugiro instalar as mesmas
versões aí do seu lado para ficarmos sincronizados):

```
npm i -E bcryptjs@3.0.3 commitizen@4.3.1 husky@9.1.7
```

Depois de instalado, rodei `npm test` para ser se tudo estava ok, tudo
estava e, novamente, como é bom ter testes E2E 🙏

Como esse estado da aplicação estava ok, eu já fiz o commit dele com a
mensagem que está nesse commit mesmo:

```
chore: bump project dependencies within `minor` and `patch` releases
```

A idéia é a cada passo com sucesso, eu ir fazendo `git commit --amend`
contra esse mesmo commit, até ter todas as versões `minor` e `patch`
atualizadas dentro desse mesmo commit aqui, para em outros commits
trabalhar nos módulos com atualizações `major` que geralmente são
mais complicadas de se fazer.

Mas bom, com as versões `patch` estabilizadas, parti para atualizar
de uma só vez todas as `minor` para ver o que iria acontecer e rodei
o seguinte:

```
npm i -E cookie@1.1.1 pg@8.20.0 prettier@3.8.1 swr@2.4.1
```

Nesse caso, rodei de novo um `npm test`, deu tudo ok, mas como foi
atualizado o `Prettier`, eu aproveitei para também rodar o script dele:

```
npm run lint:prettier:check
```

E tudo funcionou. E então com os testes e `Prettier` funcionando, fiz
`amend` neste commit aqui para nos próximos commits me focar nas
atualizações `major`.
Neste commit eu resolvi colocar todas as atualizações de `major` que não
tive problema algum em atualizar, foi de forma transparente, sem
necessidade de alteração de código.

Só que o curioso é que o commit começou tentando atualizar o `uuid` para
versão `13.0.0` e quebrou muita coisa. Quebrou porque o módulo, a partir
da versão `12`, deprecou por completo o suporte ao `CommonJS` e o Jest
tentava importar o módulo usando esse padrão. Tinha algumas formas de
contornar isso, mas um contribuinte do módulo sugeriu pinar a
dependência na versão `11` que já era a que o projeto usava:

uuidjs/uuid#903 (comment)

Vou esperar o Jest melhorar o seu suporte a módulos `ESM` e caso o
módulo `uuid` venha a se tornar um problema, resolvo isso ou troco de
módulo por ele fazer coisas simples. Se tornou uma operação de saldo
negativo continuar tentando batalhar pela atualização dele, dado que
só vou investir tempo sem ter nenhum retorno prático.

E aí falando em Jest, resolvi atualizar ele e fiz dessa forma:

```
npm i -E jest@30.3.0
```

Os testes continuaram passando de forma transparente, tanto de forma
geral com `npm test` quanto com `npm run test:watch -- user/get` e foi
daí que esse commit começou com a seguinte mensagem:

```
chore: bump `jest` to the latest major version
```

Próximo passo foi atualizar o `set-cookie-parser` e fiz assim:

```
npm i -E set-cookie-parser@3.0.1
```

Testes continuaram passando sem problemas e então fiz o amend das
alterações contra esse mesmo commit aqui, atualizando também a lista
dos módulos na mensagem principal dele ficando assim:

```
chore: bump `jest`, `set-cookie-parser` to the latest major version
```

Só que daí percebi que com essa estratégia, iria rapidamente estourar a
quantidade de caracteres recomendados para uma mensagem de commit e
mudei então para sinalizar que aqui vão estar módulos da versão major
que foram atualizados sem problema algum, sem precisar modificar nada
a mais e ficou assim:

```
chore: upgrade `major` dependencies that required no code changes
```

Daí se eu instalar um próximo módulo e tudo ficar 100%, faço `amend`,
mas se der problema, desfaço as alterações e tento outro módulo.

Então abaixo segue o restante das instalações que funcionaram de forma
transparente, na ordem que eu executei:

```
npm i -E concurrently@9.2.1
npm i -E @commitlint/cli@20.5.0 @commitlint/config-conventional@20.5.0
npm i -E dotenv@17.3.1 dotenv-expand@12.0.3
npm i -E dotenv-expand@12.0.3
npm i -E nodemailer@8.0.3
```

O curioso é que o módulo `dotenv@17.3.1` instalou de forma transparente,
mas ele começou a injetar propagandas dentro dos logs no terminal. Então
escolhi voltar a versão que já estava para procurar uma alternativa.

Em paralelo, seguem os módulos que não instalaram de forma transparente:

```
@faker-js/faker@10.3.0
eslint@10.0.3
eslint-config-next@16.2.0
eslint-config-prettier@10.1.8
eslint-plugin-jest@29.15.0
node-pg-migrate@8.0.4
uuid@13.0.0
```

Fora isso, o que faltou foi o Next.js e React, mas estes vou fazer
através de um negócio chamado `codemod` em um próximo commit e que
inclusive vai ajudar na atualização do `eslint` e seus sub-módulos.
…ions

Neste commit eu inicio utilizando o `codemod` do Next.js para atualizar
tanto o framework, quanto o React e de quebra o ESLint.

Um `codemod` é uma facilidade que uma empresa pode fornecer para fazer
por você as migrações de código e dependência necessárias para que o
sistema volte a ficar compatível.

Então primeiro rodei o comando principal do `codemod` apontando para uma
versão específica para eu e você continuarmos sincronizados:

```
npx @next/codemod upgrade 16.2.0
```

A primeira pergunta que o `codemod` fez foi se eu queria continuar no
React versão `18` e escolhi que não, que queria atualizar para a `19`.

Depois disso ele me listou todos os modificadores que ele iria aplicar
no código, escolhi aplicar todos, sendo que no nosso caso, o principal é
o abaixo dado que o Next.js não faz mais o tunelamento do linting para o
ESLint e agora devemos usar diretamente o ESLint:

```
next-lint-to-eslint-cli
```

Depois ele perguntou se queria então rodar o modificador do React 19 e o
modificador dos tipos, e respondi sim para ambos, mesmo não usando
TypeScript no projeto.

Só que ao tentar aplicar o `codemod` para valer, o `npm` reclamou de uma
incompatibilidade de `Peer Dependency`:

```
Upgrading your project to Next.js 16.2.0...
npm error code ERESOLVE
npm error ERESOLVE unable to resolve dependency tree
npm error
npm error While resolving: clone-tabnews@1.0.0
npm error Found: eslint@8.57.0
npm error node_modules/eslint
npm error   dev eslint@"8.57.0" from the root project
npm error
npm error Could not resolve dependency:
npm error peer eslint@">=9.0.0" from eslint-config-next@16.2.0
npm error node_modules/eslint-config-next
npm error   dev eslint-config-next@"16.2.0" from the root project
```

Em resumo, o `codemod` quer instalar o módulo `eslint-config-next` na
sua versão `16.2.0`, mas ele agora necessita que o módulo `eslint`
esteja no mínimo na versão `9.0.0` e o que está instalado no projeto é a
versão `8.57.0`. Dado a isso, o `npm` não consegue resolver essa
exigência na árvore de dependências e interrompe a instalação com um
erro.

Então eu desfiz todas as alterações que o `codemod` tentou fazer:

```
git checkout -- .
```

Reinstalei todas as dependências de novo para voltar como estava antes:

```
npm i
```

Atualizei todas as dependências relacionadas a ESLint da seguinte forma
antes de tentar de novo o `codemod`:

```
npm i -E eslint@10.0.3 eslint-config-next@16.2.0 \
  eslint-config-prettier@10.1.8 eslint-plugin-jest@29.15.0
```

O `npm` retornou alguns warnings a respeito de sobrescrever algumas peer
dependencies, mas instalou sem problemas.

Rodei de novo o `codemod` e ele começou a trabalhar de fato:

```
npx @next/codemod upgrade 16.2.0
```

Começou a aparecer um monte de coisa no terminal, até ele perguntar se
eu faço o deploy do meu app na Vercel, respondi que sim.

Mas infelizmente, final da história, o comando terminou com o seguinte
erro:

```
npm error code ECOMPROMISED
npm error Lock compromised
npm error A complete log of this run can be found in: /home/codespace/.npm/_logs/...
Error: Legacy codemod command failed with exit code: Some(1)
Error: Command failed: npx --yes codemod@latest react/19/migration-recipe --no-interactive
```

Doideira, então eu entendi que teria que arregaçar as mangas e continuar
de onde ele parou.

Analisando o repositório, o `codemod` alterou meu `package.json` com as
novas versões dos módulos e também alterou o `lint:eslint:check` para
apontar diretamente para o ESLint:

```
"lint:eslint:check": "eslint .",
```

Então decidi testar e ao rodar o ESLint através do script, quebrou:

```
npm run lint:eslint:check
```

Recebi um erro que começa desta forma:

```
Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@eslint/js' imported from ...
```

Investigando, o `codemod` no momento da migração leu o arquivo legado de
configuração do ESLint chamado `.eslintrc.json` e converteu ele para uma
nova versão chamado `eslint.config.mjs` e esta nova versão está pedindo
por um módulo chamado `@eslint/js` que não está instalado no projeto e,
de fato, não está.

Aí investigando na documentação de migração do ESLint no endereço
abaixo, eu vi que eles também fornecem um `codemod`:

https://eslint.org/docs/v10.x/use/configure/migration-guide

Então antes de rodar ele, removi o arquivo criado pelo `codemod` do
Next.js:

```
rm eslint.config.mjs
```

E rodei o `codemod` do ESLint:

```
npx @eslint/migrate-config .eslintrc.json
```

Ele rodou sem problemas, mas pelo menos me avisou:

```
You will need to install the following packages to use the new config:
- @eslint/js
- @eslint/eslintrc
```

Então eu instalei da seguinte forma:

```
npm i -E @eslint/js@10.0.1 @eslint/eslintrc@3.3.5
```

Rodei o script para testar de novo o ESLint:

```
npm run lint:eslint:check
```

E recebi um novo erro que começa com:

```
TypeError: Converting circular structure to JSON
    --> starting at object with constructor 'Object'
    |     property 'configs' -> object with constructor 'Object'
```

Aí pensei: “Essa migração automática está um LIXO… o arquivo
`eslint.config.mjs` está um NOJO. Vou começar tudo do zero”... e o
primeiro passo foi desinstalar todos os módulos relacionados ao ESLint.

```
npm uninstall @eslint/js @eslint/eslintrc eslint \
  eslint-config-next eslint-config-prettier eslint-plugin-jest
```

Removi também o arquivo de configuração:

```
rm eslint.config.mjs
```

Daí fui para a documentação do módulo sobre como instalar ele, o
“Getting Started”, pra realmente começar do zero:

https://eslint.org/docs/v10.x/use/getting-started

E para nós dois rodarmos o mesmo comando, rode o seguinte:

```
npm init @eslint/config@1.11.0
```

De início o ESLint perguntou “What do you want to lint?” e eu escolhi
todas as opções:

```
◉ JavaScript
◉ JSON
◉ JSON with comments
◉ JSON5
◉ Markdown
◉ CSS
```

Depois ele perguntou “How would you like to use ESLint?” e escolhi:

```
▸ To check syntax and find problems
```

Depois ele perguntou “What type of modules does your project use?” e
escolhi:

```
▸ JavaScript modules (import/export)
```

Depois ele perguntou “Which framework does your project use?” e escolhi:

```
▸ React
```

Depois ele perguntou “Does your project use TypeScript?” e eu escolhi:

```
‣ No
```

Depois ele perguntou “Where does your code run?” e eu escolhi as duas
opções:

```
◉ Browser
◉ Node
```

Depois ele perguntou “What flavor of Markdown do you want to lint?” e eu
escolhi:

```
▸ GitHub Flavored Markdown
```

E então ele me avisou que iria instalar alguns pacotes e perguntou se eu
queria instalar eles agora, mas respondi que não! Respondi isso, porque
se não ele iria instalar a última versão dos pacotes e eu e você
poderíamos ficar dessincronizados.

Dado a isso, escolha a opção `No` também e instale manualmente comigo
estas versões:

```
npm i -E -D eslint@9.39.4 @eslint/js@9.39.4 globals@17.4.0 \
  @eslint/json@1.1.0 @eslint/markdown@7.5.1 @eslint/css@1.0.0 \
  eslint-config-next@16.2.0 eslint-plugin-jest@29.15.0 eslint-config-prettier@10.1.8
```

Dois detalhes:

1. Atualmente a versão mais atualizada do ESLint é a `10`, que foi
lançada faz pouco tempo e, dado a isso, estou instalando a `9` porque o
ecossistema de plugins ainda não conseguiu se atualizar e ficar
compatível com as novas APIs da versão `10`. Um dos plugins é o
`eslint-plugin-react` que é utilizado por baixo dos panos pelo
`eslint-config-next`.

2. No comando de instalação já aproveitei para instalar o
`eslint-config-next`, `eslint-plugin-jest` e `eslint-config-prettier`
para ficar compatível com o que tínhamos anteriormente.

Em paralelo a tudo isso, o `codemod` do ESLint deve ter criado um
arquivo chamado `eslint.config.mjs` com algumas configurações iniciais,
mas que para utilizar os plugins extras e continuar exatamente como
estava antes, utilize a versão do arquivo encontrada neste commit que
apenas inclui o uso dos plugins extras que utilizamos para linting do
Next.js, Jest e evitar conflito das regras do Prettier.

Outro detalhe é que, com a atualização do ESLint e a criação deste novo
arquivo de configuração, o arquivo anterior `.eslintrc.json` não é mais
utilizado e pode ser removido, então antes do commit eu executei:

```
git rm .eslintrc.json
```

E mais um detalhe interessante, agora proposto pelo aluno
`rodrigofinkler`, é fazer o ESLint retornar um `exit code` acima de `0`
também para `warnings` para, por exemplo, quebrar o pipeline do CI caso
encontre alguma situação assim. Então o script do npm ficou desta forma:

```
"lint:eslint:check": "eslint . --max-warnings 0",
```

Por fim, rodei um `npm test` para entender se a camada da aplicação
estava ok e para minha surpresa estava tudo ok, todos os testes
rodaram 100%.

Também subi manualmente o servidor web com `npm run dev`, testei a
página do `/status` e também tudo funcionando 100%.

Último teste foi fazer um erro proposital no código para ver se o novo
ESLint continuaria quebrando o fluxo no CI e quebrou com sucesso e daí
parti para o commit final que é este que você está lendo neste exato
momento.

Próximo passo vai ser testar criação e ativação de usuários lá na URL de
homologação para ver se tudo continua ok.
@vercel

vercel Bot commented Mar 25, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
clone-tabnews Ready Ready Preview, Comment Mar 25, 2026 8:44pm

@AlAnNuB
AlAnNuB merged commit 722cbcf into main Mar 25, 2026
6 checks passed
@AlAnNuB
AlAnNuB deleted the final-adjustments branch March 25, 2026 20:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant