@vibe2founder/tests2dialects 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/examples/imperative.spec.d.ts +1 -0
- package/dist/examples/imperative.spec.js +46 -0
- package/dist/examples/math.spec.d.ts +1 -0
- package/dist/examples/math.spec.js +39 -0
- package/dist/examples/narrative.spec.d.ts +1 -0
- package/dist/examples/narrative.spec.js +47 -0
- package/dist/examples/polyglot-shopping-cart.spec.d.ts +11 -0
- package/dist/examples/polyglot-shopping-cart.spec.js +161 -0
- package/dist/examples/sanity.spec.d.ts +1 -0
- package/dist/examples/sanity.spec.js +39 -0
- package/dist/examples/showcase-api.spec.d.ts +1 -0
- package/dist/examples/showcase-api.spec.js +62 -0
- package/dist/examples/test-api.d.ts +1 -0
- package/dist/examples/test-api.js +32 -0
- package/dist/packages/api-test-dialect/index.d.ts +28 -0
- package/dist/packages/api-test-dialect/index.js +102 -0
- package/dist/packages/reqify/index.d.ts +12 -0
- package/dist/packages/reqify/index.js +24 -0
- package/dist/src/cli.d.ts +6 -0
- package/dist/src/cli.js +330 -0
- package/dist/src/index.d.ts +134 -0
- package/dist/src/index.js +374 -0
- package/dist/src/semantic/core.d.ts +24 -0
- package/dist/src/semantic/core.js +16 -0
- package/{types/api-types.ts → dist/types/api-types.d.ts} +6 -11
- package/dist/types/api-types.js +1 -0
- package/package.json +59 -35
- package/packages/api-test-dialect/index.ts +132 -132
- package/readme.md +58 -58
- package/src/cli.ts +1 -1
- package/src/index.ts +19 -16
- package/src/semantic/core.ts +26 -0
- package/CHANGELOG.md +0 -73
- package/bun.lock +0 -22
- package/bunfig.toml +0 -2
- package/critica.md +0 -77
- package/docs/4-ideias.md +0 -66
- package/docs/api-api.md +0 -93
- package/docs/api-imperativo.md +0 -125
- package/docs/api-matematico.md +0 -145
- package/docs/api-narrativo.md +0 -181
- package/docs/guia-rapido.md +0 -189
- package/docs/whitepaper.md +0 -21
- package/examples/imperative.spec.ts +0 -58
- package/examples/math.spec.ts +0 -52
- package/examples/narrative.spec.ts +0 -61
- package/examples/polyglot-shopping-cart.spec.ts +0 -212
- package/examples/sanity.spec.ts +0 -54
- package/examples/showcase-api.spec.ts +0 -70
- package/examples/test-api.ts +0 -36
- package/infograficos/detalhado.png +0 -0
- package/infograficos/mobile.png +0 -0
- package/infograficos/normal.png +0 -0
- package/landing-page/README.md +0 -38
- package/landing-page/bun.lock +0 -609
- package/landing-page/eslint.config.js +0 -23
- package/landing-page/index.html +0 -17
- package/landing-page/package-lock.json +0 -2962
- package/landing-page/package.json +0 -34
- package/landing-page/postcss.config.js +0 -6
- package/landing-page/public/vite.svg +0 -1
- package/landing-page/src/App.tsx +0 -358
- package/landing-page/src/assets/react.svg +0 -1
- package/landing-page/src/index.css +0 -34
- package/landing-page/src/main.tsx +0 -10
- package/landing-page/tailwind.config.js +0 -59
- package/landing-page/tsconfig.app.json +0 -28
- package/landing-page/tsconfig.json +0 -7
- package/landing-page/tsconfig.node.json +0 -26
- package/landing-page/vite.config.ts +0 -7
- package/logo.png +0 -0
- package/output.log +0 -60
- package/podcast/O_Matem/303/241tico,_o_Narrador_e_o_Engenheiro.json +0 -0
- package/podcast/O_Matem/303/241tico,_o_Narrador_e_o_Engenheiro.md +0 -0
- package/podcast/critica-Dialetos_de_teste__inova/303/247/303/243o_ou_fragmenta/303/247/303/243o_.json +0 -0
- package/podcast/critica-Dialetos_de_teste__inova/303/247/303/243o_ou_fragmenta/303/247/303/243o_.md +0 -0
- package/podcast/critica-Unificar_filosofia_e_pr/303/241tica_na_documenta/303/247/303/243o_(7_words__covers_t.md +0 -1
- package/podcast/critica-Unificar_filosofia_e_pr/303/241tica_na_documenta/303/247/303/243o__7_words__covers_t.ogg +0 -0
- package/podcast/critica2-Sil/303/252ncio_estrat/303/251gico_e_sobrecarga_em_READMEs.ogg +0 -0
- package/podcast/critica2.json +0 -3191
- package/podcast/critica2.md +0 -1
- package/podcast/debate-Dialetos_de_teste__inova/303/247/303/243o_ou_fragmenta/303/247/303/243o_.json +0 -0
- package/podcast/debate-Dialetos_de_teste__inova/303/247/303/243o_ou_fragmenta/303/247/303/243o_.md +0 -0
- package/reports/01-01-2026_00-45.md +0 -40
- package/reports/01-01-2026_02-30.md +0 -37
- package/reports/03-02-2026_10-55.md +0 -8
- package/reports/03-02-2026_11-45.md +0 -13
- package/reports/03-02-2026_11-50.md +0 -10
- package/reports/26-01-2026_16-25.md +0 -31
- package/reports/26-01-2026_19-20.md +0 -27
- package/reports/31-12-2025_22-35.md +0 -25
- package/reports/31-12-2025_22-45.md +0 -15
- package/slides/Dialetos_de_Teste_Um_Executor_M/303/272ltiplos_Vocabul/303/241rios.pdf +0 -0
- package/tabela.html +0 -350
- package/tsconfig.json +0 -22
- package/www/index.html +0 -1344
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ImperativeDialect } from "../src/index.js";
|
|
2
|
+
const { ensure, check, that, stub, initAll } = ImperativeDialect;
|
|
3
|
+
ensure("Sistema de Segurança e Compliance", () => {
|
|
4
|
+
// Stubs de Infraestrutura
|
|
5
|
+
const firewall = stub();
|
|
6
|
+
const logger = stub();
|
|
7
|
+
initAll(() => {
|
|
8
|
+
// Configuração Hardened
|
|
9
|
+
firewall.forceReturn({ allowed: true, threatLevel: 0 });
|
|
10
|
+
});
|
|
11
|
+
check("Deve bloquear conexões de IPs na blacklist", () => {
|
|
12
|
+
const maliciousIp = "192.168.1.666";
|
|
13
|
+
// Reconfigurando stub para este caso
|
|
14
|
+
firewall.executes((ip) => {
|
|
15
|
+
return ip === maliciousIp ? { allowed: false } : { allowed: true };
|
|
16
|
+
});
|
|
17
|
+
const access = firewall(maliciousIp);
|
|
18
|
+
that(access.allowed).is(false);
|
|
19
|
+
that(firewall).triggered();
|
|
20
|
+
that(firewall).calledWith(maliciousIp);
|
|
21
|
+
});
|
|
22
|
+
check("Deve registrar auditoria para cada acesso", () => {
|
|
23
|
+
logger("Acesso admin iniciado");
|
|
24
|
+
that(logger).triggeredCount(1);
|
|
25
|
+
that(logger).calledWith("Acesso admin iniciado");
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
ensure("Validação Estrutural da API V2", () => {
|
|
29
|
+
const apiResponse = {
|
|
30
|
+
status: 200,
|
|
31
|
+
data: {
|
|
32
|
+
id: "uuid-1234",
|
|
33
|
+
role: "admin",
|
|
34
|
+
meta: { version: 2 },
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
check("Payload deve conter metadados corretos", () => {
|
|
38
|
+
that(apiResponse.status).is(200);
|
|
39
|
+
that(apiResponse.data.role).is("admin");
|
|
40
|
+
that(apiResponse.data.meta.version).is(2);
|
|
41
|
+
});
|
|
42
|
+
check("Campos obrigatórios devem existir", () => {
|
|
43
|
+
that(apiResponse.data).isOk();
|
|
44
|
+
that(apiResponse.data.id).matches(/^uuid-/);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { MathDialect } from "../src/index.js";
|
|
2
|
+
const { axiom, proof, implies, arbitrary } = MathDialect;
|
|
3
|
+
// --- Domain Logic: Financial Calculation ---
|
|
4
|
+
const calculateInterests = (principal, rate, time) => {
|
|
5
|
+
return principal * Math.pow(1 + rate, time);
|
|
6
|
+
};
|
|
7
|
+
axiom("Teoria Financeira: Juros Compostos", () => {
|
|
8
|
+
proof("O capital deve crescer exponencialmente", () => {
|
|
9
|
+
const principal = 1000;
|
|
10
|
+
const rate = 0.05; // 5%
|
|
11
|
+
const time = 2; // anos
|
|
12
|
+
const result = calculateInterests(principal, rate, time);
|
|
13
|
+
// 1000 * (1.05)^2 = 1000 * 1.1025 = 1102.5
|
|
14
|
+
implies(result).is(1102.5);
|
|
15
|
+
});
|
|
16
|
+
proof("Taxa zero implica em imutabilidade do capital", () => {
|
|
17
|
+
const principal = 500;
|
|
18
|
+
const result = calculateInterests(principal, 0, 10);
|
|
19
|
+
implies(result).is(principal);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
axiom("Álgebra Booleana: Leis de De Morgan", () => {
|
|
23
|
+
const A = true;
|
|
24
|
+
const B = false;
|
|
25
|
+
proof("A negação da disjunção é a conjunção das negações", () => {
|
|
26
|
+
const lhs = !(A || B);
|
|
27
|
+
const rhs = !A && !B;
|
|
28
|
+
implies(lhs).is(rhs);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
axiom("Análise de Funções Recursivas", () => {
|
|
32
|
+
const factorial = arbitrary();
|
|
33
|
+
// Configurando comportamento axiomático
|
|
34
|
+
factorial.derive((n) => (n <= 1 ? 1 : n * factorial(n - 1)));
|
|
35
|
+
proof("Fatorial de 5 converge para 120", () => {
|
|
36
|
+
const res = factorial(5);
|
|
37
|
+
implies(res).is(120);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { NarrativeDialect } from "../src/index.js";
|
|
2
|
+
const { intend, detail, to, standIn, background, before } = NarrativeDialect;
|
|
3
|
+
intend("Jornada de Compra do Usuário", () => {
|
|
4
|
+
// Atores (Mocks)
|
|
5
|
+
const cart = standIn();
|
|
6
|
+
const paymentGateway = standIn();
|
|
7
|
+
const notificationService = standIn();
|
|
8
|
+
before(() => {
|
|
9
|
+
// Limpa estado para cada cena
|
|
10
|
+
cart.clear();
|
|
11
|
+
paymentGateway.clear();
|
|
12
|
+
notificationService.clear();
|
|
13
|
+
});
|
|
14
|
+
background(() => {
|
|
15
|
+
// Contexto: Carrinho tem itens e gateway está online
|
|
16
|
+
cart.getTotal.respondsWith(150.0); // Especifica o método
|
|
17
|
+
paymentGateway.process.respondsWith(true); // Especifica o método
|
|
18
|
+
});
|
|
19
|
+
detail("O cliente finaliza a compra com sucesso", () => {
|
|
20
|
+
// Ação: Usuário clica em 'Checkout'
|
|
21
|
+
const total = cart.getTotal();
|
|
22
|
+
const success = paymentGateway.process(total);
|
|
23
|
+
if (success) {
|
|
24
|
+
notificationService.sendEmail("Compra aprovada!");
|
|
25
|
+
}
|
|
26
|
+
// Expectativas narrativas
|
|
27
|
+
to(cart).wasCalled();
|
|
28
|
+
to(paymentGateway).uploaded(total); // Alias mental para 'received'
|
|
29
|
+
to(notificationService).received("Compra aprovada!");
|
|
30
|
+
});
|
|
31
|
+
detail("O sistema deve lidar com falha no pagamento", () => {
|
|
32
|
+
// Mudança de enredo: Gateway recusa
|
|
33
|
+
paymentGateway.process.respondsWith(false);
|
|
34
|
+
const success = paymentGateway.process(100);
|
|
35
|
+
to(success).be(false);
|
|
36
|
+
to(notificationService).not.wasCalled();
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
intend("Onboarding de Novos Membros", () => {
|
|
40
|
+
const database = standIn();
|
|
41
|
+
detail("Deve criar perfil padrão ao registrar", () => {
|
|
42
|
+
const newUser = { name: "Alice", email: "alice@wonder.land" };
|
|
43
|
+
// Simula o registro
|
|
44
|
+
database.save(newUser);
|
|
45
|
+
to(database).received(newUser);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🛒 Shopping Cart - Exemplo Poliglota
|
|
3
|
+
*
|
|
4
|
+
* Este arquivo demonstra como usar os 3 dialetos de forma semântica e coerente
|
|
5
|
+
* para testar diferentes camadas de uma mesma funcionalidade: um carrinho de compras.
|
|
6
|
+
*
|
|
7
|
+
* - 📐 Matemático: Testa a lógica PURA de cálculo (descontos, totais)
|
|
8
|
+
* - 📖 Narrativo: Testa a JORNADA do usuário (adicionar, remover, finalizar)
|
|
9
|
+
* - 🛡️ Imperativo: Testa a INTEGRAÇÃO com API (gateway de pagamento)
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🛒 Shopping Cart - Exemplo Poliglota
|
|
3
|
+
*
|
|
4
|
+
* Este arquivo demonstra como usar os 3 dialetos de forma semântica e coerente
|
|
5
|
+
* para testar diferentes camadas de uma mesma funcionalidade: um carrinho de compras.
|
|
6
|
+
*
|
|
7
|
+
* - 📐 Matemático: Testa a lógica PURA de cálculo (descontos, totais)
|
|
8
|
+
* - 📖 Narrativo: Testa a JORNADA do usuário (adicionar, remover, finalizar)
|
|
9
|
+
* - 🛡️ Imperativo: Testa a INTEGRAÇÃO com API (gateway de pagamento)
|
|
10
|
+
*/
|
|
11
|
+
import {
|
|
12
|
+
// 📐 Matemático - para lógica pura
|
|
13
|
+
axiom, proof, implies, given,
|
|
14
|
+
// 📖 Narrativo - para jornada do usuário
|
|
15
|
+
intend, scenario, to, standIn, background,
|
|
16
|
+
// 🛡️ Imperativo - para integração de sistema
|
|
17
|
+
ensure, check, that, stub, initAll, reset, } from "../src/index.js";
|
|
18
|
+
// =============================================================================
|
|
19
|
+
// 📐 CAMADA MATEMÁTICA: Lógica Pura de Preços
|
|
20
|
+
// =============================================================================
|
|
21
|
+
// Aqui provamos verdades universais sobre cálculos financeiros.
|
|
22
|
+
// Não há dependências externas, não há estado. Apenas matemática.
|
|
23
|
+
axiom("Teoria de Cálculo de Preços", () => {
|
|
24
|
+
let calcDiscount;
|
|
25
|
+
let calcTotal;
|
|
26
|
+
let applyTax;
|
|
27
|
+
given(() => {
|
|
28
|
+
// Funções puras - determinísticas, sem side effects
|
|
29
|
+
calcDiscount = (price, percent) => price - price * (percent / 100);
|
|
30
|
+
calcTotal = (items) => items.reduce((sum, i) => sum + i.price * i.qty, 0);
|
|
31
|
+
applyTax = (subtotal, rate) => Math.round(subtotal * (1 + rate) * 100) / 100;
|
|
32
|
+
});
|
|
33
|
+
proof("Desconto de 10% em R$100 implica R$90", () => {
|
|
34
|
+
implies(calcDiscount(100, 10)).is(90);
|
|
35
|
+
});
|
|
36
|
+
proof("Desconto de 0% preserva o valor original", () => {
|
|
37
|
+
implies(calcDiscount(250, 0)).is(250);
|
|
38
|
+
});
|
|
39
|
+
proof("Desconto de 100% anula o preço", () => {
|
|
40
|
+
implies(calcDiscount(500, 100)).is(0);
|
|
41
|
+
});
|
|
42
|
+
proof("Total de carrinho vazio converge para zero", () => {
|
|
43
|
+
implies(calcTotal([])).is(0);
|
|
44
|
+
});
|
|
45
|
+
proof("Total de 3 itens a R$10 cada (qty=2) implica R$60", () => {
|
|
46
|
+
const items = [
|
|
47
|
+
{ price: 10, qty: 2 },
|
|
48
|
+
{ price: 10, qty: 2 },
|
|
49
|
+
{ price: 10, qty: 2 },
|
|
50
|
+
];
|
|
51
|
+
implies(calcTotal(items)).is(60);
|
|
52
|
+
});
|
|
53
|
+
proof("Imposto de 10% sobre R$100 resulta em R$110", () => {
|
|
54
|
+
implies(applyTax(100, 0.1)).is(110);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
// =============================================================================
|
|
58
|
+
// 📖 CAMADA NARRATIVA: Jornada do Usuário
|
|
59
|
+
// =============================================================================
|
|
60
|
+
// Aqui contamos histórias que o PM pode ler e entender.
|
|
61
|
+
// Descrevemos a experiência do usuário em linguagem natural.
|
|
62
|
+
intend("Jornada de Compra do Usuário", () => {
|
|
63
|
+
const cart = standIn(); // Dublê do serviço de carrinho
|
|
64
|
+
const user = standIn(); // Dublê do usuário
|
|
65
|
+
background(() => {
|
|
66
|
+
// Preparação do cenário
|
|
67
|
+
cart.respondsWith({ items: [], total: 0 });
|
|
68
|
+
user.respondsWith({ name: "João", loggedIn: true });
|
|
69
|
+
});
|
|
70
|
+
scenario("Usuário adiciona primeiro produto ao carrinho", () => {
|
|
71
|
+
cart.add({ name: "Camiseta", price: 49.9 });
|
|
72
|
+
cart.respondsWith({
|
|
73
|
+
items: [{ name: "Camiseta", price: 49.9 }],
|
|
74
|
+
total: 49.9,
|
|
75
|
+
});
|
|
76
|
+
to(cart).wasCalled();
|
|
77
|
+
to(cart.items).have("length");
|
|
78
|
+
});
|
|
79
|
+
scenario("Usuário aplica cupom de desconto", () => {
|
|
80
|
+
const coupon = standIn();
|
|
81
|
+
coupon.respondsWith({ valid: true, discount: 15 });
|
|
82
|
+
// Link the mock logic for the purpose of this example
|
|
83
|
+
cart.applyCoupon.actsLike(() => coupon("DESCONTO15"));
|
|
84
|
+
cart.applyCoupon("DESCONTO15");
|
|
85
|
+
cart.respondsWith({ total: 42.42 }); // 49.9 - 15%
|
|
86
|
+
to(coupon).wasCalled();
|
|
87
|
+
to(cart.total).be(42.42);
|
|
88
|
+
});
|
|
89
|
+
scenario("Usuário remove item e carrinho fica vazio", () => {
|
|
90
|
+
cart.remove("Camiseta");
|
|
91
|
+
cart.respondsWith({ items: [], total: 0 });
|
|
92
|
+
to(cart.items).have("length");
|
|
93
|
+
to(cart.total).be(0);
|
|
94
|
+
});
|
|
95
|
+
scenario("Usuário não logado tenta finalizar compra", () => {
|
|
96
|
+
user.respondsWith({ loggedIn: false });
|
|
97
|
+
const checkout = standIn();
|
|
98
|
+
checkout.respondsWith({ error: "LOGIN_REQUIRED", status: 401 });
|
|
99
|
+
to(checkout.status).be(401);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
// =============================================================================
|
|
103
|
+
// 🛡️ CAMADA IMPERATIVA: Integração com Gateway de Pagamento
|
|
104
|
+
// =============================================================================
|
|
105
|
+
// Aqui verificamos contratos rígidos com sistemas externos.
|
|
106
|
+
// A linguagem é autoritária: ensure, verify, check.
|
|
107
|
+
ensure("Conformidade com Gateway de Pagamento v2.1", () => {
|
|
108
|
+
const paymentGateway = stub();
|
|
109
|
+
const transactionLogger = stub();
|
|
110
|
+
initAll(() => {
|
|
111
|
+
// Setup de infraestrutura
|
|
112
|
+
paymentGateway.process.forceReturn({
|
|
113
|
+
status: 200,
|
|
114
|
+
transactionId: "tx_abc123",
|
|
115
|
+
timestamp: "2026-01-01T00:00:00Z",
|
|
116
|
+
});
|
|
117
|
+
transactionLogger.log.forceReturn(true);
|
|
118
|
+
});
|
|
119
|
+
reset(() => {
|
|
120
|
+
// Limpa estado entre testes para garantir contagens precisas
|
|
121
|
+
paymentGateway.clear();
|
|
122
|
+
transactionLogger.clear();
|
|
123
|
+
});
|
|
124
|
+
check("Transação bem-sucedida retorna status 200", () => {
|
|
125
|
+
const response = paymentGateway.process({
|
|
126
|
+
amount: 99.9,
|
|
127
|
+
currency: "BRL",
|
|
128
|
+
cardToken: "tok_visa_xxxx",
|
|
129
|
+
});
|
|
130
|
+
that(response.status).is(200);
|
|
131
|
+
that(response.transactionId).matches(/^tx_[a-z0-9]+$/);
|
|
132
|
+
});
|
|
133
|
+
check("TransactionId segue padrão RFC do contrato", () => {
|
|
134
|
+
const response = paymentGateway.process({ amount: 50 });
|
|
135
|
+
// Validação rigorosa do formato do ID
|
|
136
|
+
that(response.transactionId).matches(/^tx_[a-z0-9]{6,}$/);
|
|
137
|
+
that(response.timestamp).matches(/^\d{4}-\d{2}-\d{2}T/);
|
|
138
|
+
});
|
|
139
|
+
check("Toda transação deve ser logada para auditoria", () => {
|
|
140
|
+
paymentGateway.process({ amount: 100 });
|
|
141
|
+
transactionLogger.log({ event: "PAYMENT_PROCESSED" });
|
|
142
|
+
that(transactionLogger).triggered();
|
|
143
|
+
that(transactionLogger).calledWith({ event: "PAYMENT_PROCESSED" });
|
|
144
|
+
});
|
|
145
|
+
check("Gateway deve ser acionado exatamente uma vez por request", () => {
|
|
146
|
+
paymentGateway.process({ amount: 100 });
|
|
147
|
+
that(paymentGateway).triggeredCount(1);
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
// =============================================================================
|
|
151
|
+
// 🎭 RESUMO: Por que usar os 3 dialetos?
|
|
152
|
+
// =============================================================================
|
|
153
|
+
//
|
|
154
|
+
// | Camada | Dialeto | Por quê? |
|
|
155
|
+
// |--------------|-------------|----------------------------------------------|
|
|
156
|
+
// | Cálculos | Matemático | Provas de verdades universais, sem estado |
|
|
157
|
+
// | UX/Jornada | Narrativo | Documentação viva legível por PMs |
|
|
158
|
+
// | Integração | Imperativo | Contratos rígidos, conformidade, auditoria |
|
|
159
|
+
//
|
|
160
|
+
// Cada dialeto expressa a NATUREZA do problema que está resolvendo.
|
|
161
|
+
// A linguagem do teste espelha a linguagem do domínio.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { MathDialect, NarrativeDialect, ImperativeDialect, } from "../src/index.js";
|
|
2
|
+
console.log("---------------------------------------------------");
|
|
3
|
+
console.log("🚀 INICIANDO TEST SUITE POLIGLOTA TEMPLATE");
|
|
4
|
+
console.log("---------------------------------------------------");
|
|
5
|
+
// 1. Teste Matemático
|
|
6
|
+
const { axiom, proof, implies, arbitrary } = MathDialect;
|
|
7
|
+
axiom("Teoria dos Conjuntos (Dialeto Matemático)", () => {
|
|
8
|
+
const f = arbitrary();
|
|
9
|
+
f.yields(10); // Configuração usando nome matemático
|
|
10
|
+
proof("f deve mapear para 10", () => {
|
|
11
|
+
const res = f();
|
|
12
|
+
implies(res).is(10);
|
|
13
|
+
implies(f).wasEvaluated();
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
// 2. Teste Narrativo
|
|
17
|
+
const { intend, detail, to, standIn } = NarrativeDialect;
|
|
18
|
+
intend("Autenticação de Usuário (Dialeto Narrativo)", () => {
|
|
19
|
+
const login = standIn();
|
|
20
|
+
login.respondsWith(true); // Configuração narrativa
|
|
21
|
+
detail("login bem sucedido", () => {
|
|
22
|
+
login("user", "pass");
|
|
23
|
+
to(login).received("user", "pass");
|
|
24
|
+
to(login).wasCalled();
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
// 3. Teste Imperativo
|
|
28
|
+
const { ensure, check, that, stub } = ImperativeDialect;
|
|
29
|
+
ensure("Serviço de Pagamento (Dialeto Imperativo)", () => {
|
|
30
|
+
const api = stub();
|
|
31
|
+
api.forceReturn(200); // Configuração imperativa
|
|
32
|
+
check("status da API é 200", () => {
|
|
33
|
+
const status = api();
|
|
34
|
+
that(status).is(200);
|
|
35
|
+
that(api).triggeredCount(1);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
console.log("\n---------------------------------------------------");
|
|
39
|
+
console.log("🏁 FIM DA EXECUÇÃO");
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { ApiSpec } from "../packages/api-test-dialect/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* 🌐 Teste de Showcase do Dialeto de API
|
|
4
|
+
* Mostrando todas as funcionalidades integradas:
|
|
5
|
+
* - GET, POST, PUT, DELETE
|
|
6
|
+
* - Headers customizados
|
|
7
|
+
* - Validação de Status Code
|
|
8
|
+
* - Validação de Schema (Nominal/Semântica)
|
|
9
|
+
*/
|
|
10
|
+
async function runShowcase() {
|
|
11
|
+
const BASE_URL = "https://jsonplaceholder.typicode.com";
|
|
12
|
+
console.log("\n--- 🚀 INICIANDO SHOWCASE DE FUNCIONALIDADES API ---");
|
|
13
|
+
try {
|
|
14
|
+
// 1. Teste GET Simples
|
|
15
|
+
await ApiSpec.define("1. Buscar Post Único (GET)")
|
|
16
|
+
.from(BASE_URL)
|
|
17
|
+
.get("/posts/1")
|
|
18
|
+
.shouldReturn(200)
|
|
19
|
+
.matchingSchema({
|
|
20
|
+
id: 1,
|
|
21
|
+
userId: 1,
|
|
22
|
+
title: "string",
|
|
23
|
+
body: "string"
|
|
24
|
+
})
|
|
25
|
+
.run();
|
|
26
|
+
// 2. Teste POST com Body e Header
|
|
27
|
+
await ApiSpec.define("2. Criar Novo Recurso (POST)")
|
|
28
|
+
.from(BASE_URL)
|
|
29
|
+
.header("Content-Type", "application/json; charset=UTF-8")
|
|
30
|
+
.header("X-Custom-Header", "vibe2founderPower")
|
|
31
|
+
.post("/posts", {
|
|
32
|
+
title: "Showcase Test",
|
|
33
|
+
body: "Testando todas as features do dialeto",
|
|
34
|
+
userId: 1
|
|
35
|
+
})
|
|
36
|
+
.shouldReturn(201)
|
|
37
|
+
.run();
|
|
38
|
+
// 3. Teste PUT (Atualização)
|
|
39
|
+
await ApiSpec.define("3. Atualizar Recurso (PUT)")
|
|
40
|
+
.from(BASE_URL)
|
|
41
|
+
.put("/posts/1", {
|
|
42
|
+
id: 1,
|
|
43
|
+
title: "Título Atualizado",
|
|
44
|
+
body: "Conteúdo atualizado via PUT",
|
|
45
|
+
userId: 1
|
|
46
|
+
})
|
|
47
|
+
.shouldReturn(200)
|
|
48
|
+
.run();
|
|
49
|
+
// 4. Teste DELETE
|
|
50
|
+
await ApiSpec.define("4. Remover Recurso (DELETE)")
|
|
51
|
+
.from(BASE_URL)
|
|
52
|
+
.delete("/posts/1")
|
|
53
|
+
.shouldReturn(200)
|
|
54
|
+
.run();
|
|
55
|
+
console.log("\n--- ✅ SHOWCASE FINALIZADO COM SUCESSO ---");
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
console.error("\n--- ❌ SHOWCASE FALHOU ---");
|
|
59
|
+
// O erro já é logado pelo dialeto no .run()
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
runShowcase();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ApiSpec } from "../packages/api-test-dialect/index.js";
|
|
2
|
+
// Simulating a test run against a public placeholder API (JSONPlaceholder)
|
|
3
|
+
async function runExample() {
|
|
4
|
+
try {
|
|
5
|
+
console.log("--- Starting API Dialect Test Example ---");
|
|
6
|
+
await ApiSpec.define("Get Single Post")
|
|
7
|
+
.from("https://jsonplaceholder.typicode.com")
|
|
8
|
+
.get("/posts/1")
|
|
9
|
+
.shouldReturn(200)
|
|
10
|
+
.matchingSchema({
|
|
11
|
+
id: 1,
|
|
12
|
+
userId: 1,
|
|
13
|
+
title: "string",
|
|
14
|
+
body: "string",
|
|
15
|
+
})
|
|
16
|
+
.run();
|
|
17
|
+
await ApiSpec.define("Create New Post")
|
|
18
|
+
.from("https://jsonplaceholder.typicode.com")
|
|
19
|
+
.post("/posts", {
|
|
20
|
+
title: "vibe2founder Test",
|
|
21
|
+
body: "Building dialects is fun",
|
|
22
|
+
userId: 1,
|
|
23
|
+
})
|
|
24
|
+
.shouldReturn(201)
|
|
25
|
+
.run();
|
|
26
|
+
console.log("--- All tests finished successfully ---");
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
// Error is already logged by the dialect
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
runExample();
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { HttpMethod, HttpStatusCode, ApiUrl, TestName, SchemaDefinition } from "../../types/api-types.js";
|
|
2
|
+
import { request2httpResponse } from "../reqify/index.js";
|
|
3
|
+
export declare class ApiTestDialect {
|
|
4
|
+
private _name;
|
|
5
|
+
private _baseUrl;
|
|
6
|
+
private _method;
|
|
7
|
+
private _path;
|
|
8
|
+
private _body;
|
|
9
|
+
private _headers;
|
|
10
|
+
private _expectedStatus;
|
|
11
|
+
private _expectedSchema;
|
|
12
|
+
constructor(name: TestName);
|
|
13
|
+
static define(name: TestName): ApiTestDialect;
|
|
14
|
+
from(baseUrl: ApiUrl): this;
|
|
15
|
+
method(cmd: HttpMethod, path: string): this;
|
|
16
|
+
get(path: string): this;
|
|
17
|
+
post(path: string, body?: unknown): this;
|
|
18
|
+
put(path: string, body?: unknown): this;
|
|
19
|
+
delete(path: string): this;
|
|
20
|
+
withBody(body: unknown): this;
|
|
21
|
+
header(key: string, value: string): this;
|
|
22
|
+
shouldReturn(status: HttpStatusCode): this;
|
|
23
|
+
matchingSchema(schema: SchemaDefinition): this;
|
|
24
|
+
run(): Promise<request2httpResponse>;
|
|
25
|
+
private validate;
|
|
26
|
+
private validateSchema;
|
|
27
|
+
}
|
|
28
|
+
export declare const ApiSpec: typeof ApiTestDialect;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { request2http } from "../reqify/index.js";
|
|
2
|
+
export class ApiTestDialect {
|
|
3
|
+
_name;
|
|
4
|
+
_baseUrl = "";
|
|
5
|
+
_method = "GET";
|
|
6
|
+
_path = "";
|
|
7
|
+
_body = null;
|
|
8
|
+
_headers = {};
|
|
9
|
+
_expectedStatus = null;
|
|
10
|
+
_expectedSchema = null;
|
|
11
|
+
constructor(name) {
|
|
12
|
+
this._name = name;
|
|
13
|
+
}
|
|
14
|
+
static define(name) {
|
|
15
|
+
return new ApiTestDialect(name);
|
|
16
|
+
}
|
|
17
|
+
from(baseUrl) {
|
|
18
|
+
this._baseUrl = baseUrl;
|
|
19
|
+
return this;
|
|
20
|
+
}
|
|
21
|
+
method(cmd, path) {
|
|
22
|
+
this._method = cmd;
|
|
23
|
+
this._path = path;
|
|
24
|
+
return this;
|
|
25
|
+
}
|
|
26
|
+
get(path) {
|
|
27
|
+
return this.method("GET", path);
|
|
28
|
+
}
|
|
29
|
+
post(path, body) {
|
|
30
|
+
this.method("POST", path);
|
|
31
|
+
if (body)
|
|
32
|
+
this.withBody(body);
|
|
33
|
+
return this;
|
|
34
|
+
}
|
|
35
|
+
put(path, body) {
|
|
36
|
+
this.method("PUT", path);
|
|
37
|
+
if (body)
|
|
38
|
+
this.withBody(body);
|
|
39
|
+
return this;
|
|
40
|
+
}
|
|
41
|
+
delete(path) {
|
|
42
|
+
return this.method("DELETE", path);
|
|
43
|
+
}
|
|
44
|
+
withBody(body) {
|
|
45
|
+
this._body = body;
|
|
46
|
+
return this;
|
|
47
|
+
}
|
|
48
|
+
header(key, value) {
|
|
49
|
+
this._headers[key] = value;
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
shouldReturn(status) {
|
|
53
|
+
this._expectedStatus = status;
|
|
54
|
+
return this;
|
|
55
|
+
}
|
|
56
|
+
matchingSchema(schema) {
|
|
57
|
+
this._expectedSchema = schema;
|
|
58
|
+
return this;
|
|
59
|
+
}
|
|
60
|
+
async run() {
|
|
61
|
+
const url = `${this._baseUrl}${this._path}`;
|
|
62
|
+
console.log(`🚀 [Test: ${this._name}] Running ${this._method} ${url}...`);
|
|
63
|
+
try {
|
|
64
|
+
const response = await request2http(url, {
|
|
65
|
+
method: this._method,
|
|
66
|
+
headers: this._headers,
|
|
67
|
+
body: this._body,
|
|
68
|
+
});
|
|
69
|
+
this.validate(response);
|
|
70
|
+
console.log(`✅ [Test: ${this._name}] Passed!`);
|
|
71
|
+
return response;
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
console.error(`❌ [Test: ${this._name}] Failed: ${error.message}`);
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
validate(response) {
|
|
79
|
+
if (this._expectedStatus !== null &&
|
|
80
|
+
response.status !== this._expectedStatus) {
|
|
81
|
+
throw new Error(`Expected status ${this._expectedStatus} but got ${response.status}`);
|
|
82
|
+
}
|
|
83
|
+
if (this._expectedSchema !== null) {
|
|
84
|
+
// Basic schema validation logic implemented locally
|
|
85
|
+
this.validateSchema(response.data, this._expectedSchema);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
validateSchema(data, schema) {
|
|
89
|
+
// Simple key-based validation for the dialect example
|
|
90
|
+
for (const key in schema) {
|
|
91
|
+
if (!(key in data)) {
|
|
92
|
+
throw new Error(`Schema validation failed: Missing key "${key}" in response`);
|
|
93
|
+
}
|
|
94
|
+
const expectedType = typeof schema[key];
|
|
95
|
+
const actualType = typeof data[key];
|
|
96
|
+
if (expectedType !== actualType) {
|
|
97
|
+
throw new Error(`Schema validation failed: Key "${key}" expected type ${expectedType} but got ${actualType}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
export const ApiSpec = ApiTestDialect;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { HttpMethod, HttpStatusCode, ApiUrl } from "../../types/api-types.js";
|
|
2
|
+
export interface request2httpOptions {
|
|
3
|
+
method?: HttpMethod;
|
|
4
|
+
headers?: Record<string, string>;
|
|
5
|
+
body?: unknown;
|
|
6
|
+
}
|
|
7
|
+
export interface request2httpResponse<T = any> {
|
|
8
|
+
status: HttpStatusCode;
|
|
9
|
+
data: T;
|
|
10
|
+
headers: Headers;
|
|
11
|
+
}
|
|
12
|
+
export declare const request2http: <T = any>(url: ApiUrl, options?: request2httpOptions) => Promise<request2httpResponse<T>>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export const request2http = async (url, options = {}) => {
|
|
2
|
+
const { method = "GET", headers = {}, body } = options;
|
|
3
|
+
const response = await fetch(url, {
|
|
4
|
+
method,
|
|
5
|
+
headers: {
|
|
6
|
+
"Content-Type": "application/json",
|
|
7
|
+
...headers,
|
|
8
|
+
},
|
|
9
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
10
|
+
});
|
|
11
|
+
let data;
|
|
12
|
+
const contentType = response.headers.get("content-type");
|
|
13
|
+
if (contentType && contentType.includes("application/json")) {
|
|
14
|
+
data = await response.json();
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
data = await response.text();
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
status: response.status,
|
|
21
|
+
data: data,
|
|
22
|
+
headers: response.headers,
|
|
23
|
+
};
|
|
24
|
+
};
|