Pular para o conteúdo principal

🪢 Recursos Compartilhados

History
VersionChanges
v3.0.3-canary.60ff5ce2
Introduce Shared Resources.
1Experimental

Compartilhe estado e métodos entre arquivos e processos de teste, permitindo padrões avançados de integração e testes end-to-end.

O que são Recursos Compartilhados?

Recursos Compartilhados permitem que você defina um recurso (objeto, estado ou API) em um único arquivo e acesse ou altere esse recurso a partir de múltiplos arquivos ou processos de teste. Isso é útil para cenários como:

  • Compartilhar uma conexão de banco de dados ou armazenamento em memória.
  • Coordenar estado entre testes paralelos ou sequenciais.
  • Implementar lógica de setup/teardown entre arquivos.

Uso Básico

1. Defina um Recurso Compartilhado

Crie um arquivo .resource.ts e exporte um recurso compartilhado usando createSharedResource:

import { createSharedResource } from 'poku';

export default createSharedResource('sharedCounter', () => {
let count = 0;
return {
getCount: () => count,
increment: () => ++count,
reset: () => {
count = 0;
},
};
});

2. Acesse o Recurso Compartilhado nos Testes

Use getSharedResource em qualquer arquivo de teste para acessar e chamar métodos do recurso compartilhado:

import { getSharedResource, test, assert } from 'poku';

test('incrementa o contador', async () => {
const [counter, detach] = await getSharedResource('sharedCounter');
await counter.increment();
assert.equal(await counter.getCount(), 1);
detach();
});

3. Métodos se Tornam Chamadas de Procedimento Remoto

Todas as funções do seu recurso se tornam chamadas de procedimento remoto (RPCs), então você pode alterar ou ler o estado compartilhado com segurança entre processos.

É importante notar que os argumentos são serializados usando Advanced Serialization durante a comunicação entre diferentes processos, o que significa que alguns tipos complexos (como funções) não são suportados neste momento.

4. Desanexando o Recurso

Quando você chama getSharedResource, ele retorna uma tupla com o recurso e uma função detach. É essencial sempre chamar detach ao final do seu teste: isso cancela a inscrição do seu processo de teste nas atualizações do recurso compartilhado, garantindo a limpeza adequada e prevenindo vazamentos de memória. Se você não chamar detach, seu processo permanecerá inscrito, o que pode levar a vazamentos de recursos, uso desnecessário de memória e problemas de isolamento de testes. Ao sempre chamar detach, você mantém seu ambiente de teste limpo, previsível e livre de efeitos colaterais causados por inscrições remanescentes.

test('desanexa o recurso', async () => {
const [counter, detach] = await getSharedResource('sharedCounter');
await counter.increment();
assert.equal(await counter.getCount(), 1);
detach(); // Limpa o recurso
});

5. Lógica de Limpeza

Você pode definir uma função de limpeza ao criar o recurso. Ela será chamada quando o recurso não for mais necessário:

export default createSharedResource(
'sharedCounter',
() => {
let count = 0;
return {
getCount: () => count,
increment: () => ++count,
reset: () => {
count = 0;
},
};
},
(resource) => {
// Lógica de limpeza
resource.reset();
}
);

Exemplos do Mundo Real

1. Cache LRU Compartilhado (lru.min)

Compartilhe um cache LRU em memória entre testes:

lru.resource.ts

import { createSharedResource } from 'poku';
import { createLRU } from 'lru.min';

export default createSharedResource('lru', () => {
const cache = createLRU({ max: 3 });
return cache;
});

cache.test.ts

import { getSharedResource, test, assert } from 'poku';

test('pode compartilhar cache', async () => {
const [cache, detach] = await getSharedResource('lru');
await cache.set('foo', 123);
assert((await cache.get('foo')) === 123);
detach();
});

2. Conexão MySQL Compartilhada (mysql2)

Compartilhe uma única conexão MySQL entre testes:

mysql.resource.ts

import { createSharedResource } from 'poku';
import mysql from 'mysql2/promise';

export default createSharedResource(
'mysql',
async () => {
const connection = await mysql.createConnection({
host: 'localhost',
user: 'root',
database: 'test',
});
return connection;
},
(connection) => {
// Função de limpeza para fechar a conexão
return connection.destroy();
}
);

db.test.ts

import { getSharedResource, test, assert } from 'poku';

test('pode consultar usuários', async () => {
const [db, detach] = await getSharedResource('mysql');
const [rows] = await db.query('SELECT * FROM users LIMIT 10');

assert(Array.isArray(rows));
assert.equal(rows.length, 10);
detach();
});