fakeforge-br 0.1.0__tar.gz
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.
- fakeforge_br-0.1.0/.gitignore +13 -0
- fakeforge_br-0.1.0/LICENSE +21 -0
- fakeforge_br-0.1.0/PKG-INFO +345 -0
- fakeforge_br-0.1.0/README.md +310 -0
- fakeforge_br-0.1.0/pyproject.toml +68 -0
- fakeforge_br-0.1.0/src/fakeforge/__init__.py +28 -0
- fakeforge_br-0.1.0/src/fakeforge/client.py +218 -0
- fakeforge_br-0.1.0/src/fakeforge/exceptions.py +36 -0
- fakeforge_br-0.1.0/src/fakeforge/py.typed +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Everton Paula
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: fakeforge-br
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: SDK oficial do FakeForge para gerar dados brasileiros válidos (CPF, CNPJ, CEP, PIX, cartão de crédito) em testes de software. Zero dependências runtime.
|
|
5
|
+
Project-URL: Homepage, https://fakeforge.com.br
|
|
6
|
+
Project-URL: Documentation, https://fakeforge.com.br/docs
|
|
7
|
+
Project-URL: Repository, https://github.com/everpaula/fakeforge-br
|
|
8
|
+
Project-URL: Issues, https://github.com/everpaula/fakeforge-br/issues
|
|
9
|
+
Author-email: Everton Paula <hey@fakeforge.com.br>
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: brasil,brazil,brazilian,cep,cnpj,cpf,fake-data,faker,fixture,gerador,mock,pix,python,seed,test-data,testing
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Natural Language :: Portuguese (Brazilian)
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
25
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
26
|
+
Classifier: Topic :: Software Development :: Testing
|
|
27
|
+
Classifier: Typing :: Typed
|
|
28
|
+
Requires-Python: >=3.8
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: mypy>=1.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: ruff>=0.1; extra == 'dev'
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
|
|
36
|
+
# fakeforge
|
|
37
|
+
|
|
38
|
+
SDK oficial do [FakeForge](https://fakeforge.com.br) para Python — gera dados brasileiros válidos (CPF, CNPJ, CEP, PIX, cartão de crédito) para testes de software.
|
|
39
|
+
|
|
40
|
+
- ✅ **Zero dependências runtime** (usa `urllib` nativo)
|
|
41
|
+
- ✅ **Type hints completos** (compatível com mypy strict)
|
|
42
|
+
- ✅ **Python 3.8+**
|
|
43
|
+
- ✅ **Validação real** — todos os documentos passam mod-11 da Receita Federal, Luhn, ANATEL
|
|
44
|
+
- ✅ **Presets correlacionados** — pessoa completa com CPF + email + endereço + telefone em 1 chamada
|
|
45
|
+
- ✅ **CNPJ alfanumérico 2026** — cobertura do novo formato (IN RFB 2.229)
|
|
46
|
+
- ✅ **Grátis** — 50 chamadas/dia sem API key, ou 10.000/dia com plano Dev (R$29/mês)
|
|
47
|
+
|
|
48
|
+
## Instalação
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pip install fakeforge-br
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
poetry add fakeforge
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
uv add fakeforge
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Uso rápido
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
from fakeforge import FakeForge
|
|
66
|
+
|
|
67
|
+
ff = FakeForge()
|
|
68
|
+
|
|
69
|
+
# CPFs válidos (mod-11 da Receita Federal)
|
|
70
|
+
cpfs = ff.cpf(10)
|
|
71
|
+
# ['123.456.789-09', '987.654.321-00', ...]
|
|
72
|
+
|
|
73
|
+
# CNPJs válidos (mod-11)
|
|
74
|
+
cnpjs = ff.cnpj(5)
|
|
75
|
+
|
|
76
|
+
# Chave PIX no formato BACEN
|
|
77
|
+
pix = ff.pix_key(3)
|
|
78
|
+
|
|
79
|
+
# Cartão de crédito com Luhn válido
|
|
80
|
+
cards = ff.credit_card(5)
|
|
81
|
+
# [{'number': '...', 'brand': 'visa', 'cvv': '123', 'expiry': '12/28'}, ...]
|
|
82
|
+
|
|
83
|
+
# Pessoa completa correlacionada
|
|
84
|
+
[pessoa] = ff.person(1)
|
|
85
|
+
print(pessoa["name"], pessoa["cpf"], pessoa["email"])
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Presets: dados correlacionados em 1 chamada
|
|
89
|
+
|
|
90
|
+
Presets retornam objetos com múltiplos campos que se relacionam — email deriva do nome, DDD bate com o estado do endereço, etc.
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
customers = ff.preset("customer", 100)
|
|
94
|
+
|
|
95
|
+
for c in customers:
|
|
96
|
+
print({
|
|
97
|
+
"name": c["name"],
|
|
98
|
+
"cpf": c["cpf"],
|
|
99
|
+
"email": c["email"],
|
|
100
|
+
"phone": c["phone"],
|
|
101
|
+
"address": c["address"],
|
|
102
|
+
})
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Presets disponíveis:
|
|
106
|
+
|
|
107
|
+
| Preset | Retorna |
|
|
108
|
+
|---|---|
|
|
109
|
+
| `customer` | pessoa + endereço + email + telefone + PIX |
|
|
110
|
+
| `employee` | pessoa + conta bancária + PIX |
|
|
111
|
+
| `company` | empresa + endereço + contato |
|
|
112
|
+
| `ecommerce_order` | cliente + cartão + entrega |
|
|
113
|
+
| `contact_list` | nome + email + telefone |
|
|
114
|
+
|
|
115
|
+
## Comparação com faker (pt-BR) e python-brasilidades
|
|
116
|
+
|
|
117
|
+
| Recurso | faker (pt-BR) | python-brasilidades | fakeforge |
|
|
118
|
+
|---|---|---|---|
|
|
119
|
+
| CPF com mod-11 válido | ❌ | ✅ | ✅ |
|
|
120
|
+
| CNPJ com mod-11 válido | ❌ | ✅ | ✅ |
|
|
121
|
+
| CNPJ alfanumérico 2026 | ❌ | ❌ | ✅ |
|
|
122
|
+
| Cartão com Luhn | ❌ | ❌ | ✅ |
|
|
123
|
+
| PIX BACEN (4 formatos) | ❌ | ❌ | ✅ |
|
|
124
|
+
| Correlação nome ↔ email ↔ DDD | ❌ | ❌ | ✅ |
|
|
125
|
+
| DDDs oficiais ANATEL | ❌ | Parcial | ✅ (67 DDDs) |
|
|
126
|
+
| 17 bancos brasileiros com DV | ❌ | ❌ | ✅ |
|
|
127
|
+
| Presets bundle (customer, employee, etc) | ❌ | ❌ | ✅ |
|
|
128
|
+
| API HTTP (sem instalar dep em outra linguagem) | ❌ | ❌ | ✅ |
|
|
129
|
+
|
|
130
|
+
**fakeforge** é o único com API HTTP + SDK Python que permite escalar geração em CI/CD sem instalar dep de biblioteca em cada linguagem do stack. Perfeito pra times que usam Python no backend mas Node no frontend.
|
|
131
|
+
|
|
132
|
+
## Uso com pytest
|
|
133
|
+
|
|
134
|
+
### Fixture reutilizável
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
# conftest.py
|
|
138
|
+
import pytest
|
|
139
|
+
from fakeforge import FakeForge
|
|
140
|
+
|
|
141
|
+
@pytest.fixture(scope="session")
|
|
142
|
+
def customers():
|
|
143
|
+
"""100 customers correlacionados. Escopo session pra reutilizar entre testes."""
|
|
144
|
+
ff = FakeForge()
|
|
145
|
+
return ff.preset("customer", 100)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
@pytest.fixture(scope="session")
|
|
149
|
+
def cpfs_validos():
|
|
150
|
+
"""1000 CPFs válidos pra teste de load."""
|
|
151
|
+
ff = FakeForge()
|
|
152
|
+
return ff.cpf(1000)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
# test_checkout.py
|
|
157
|
+
def test_checkout_aceita_cpf_valido(customers, client):
|
|
158
|
+
for customer in customers[:20]:
|
|
159
|
+
response = client.post("/checkout", json={
|
|
160
|
+
"cpf": customer["cpf"],
|
|
161
|
+
"email": customer["email"],
|
|
162
|
+
})
|
|
163
|
+
assert response.status_code == 200
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Django ORM seed
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
# management/commands/seed_customers.py
|
|
170
|
+
from django.core.management.base import BaseCommand
|
|
171
|
+
from fakeforge import FakeForge
|
|
172
|
+
from myapp.models import Customer
|
|
173
|
+
|
|
174
|
+
class Command(BaseCommand):
|
|
175
|
+
help = "Popula banco com 1000 customers via FakeForge"
|
|
176
|
+
|
|
177
|
+
def handle(self, *args, **options):
|
|
178
|
+
ff = FakeForge(api_key="sua_key_dev") # 10.000/dia no Dev
|
|
179
|
+
customers = ff.preset("customer", 1000)
|
|
180
|
+
|
|
181
|
+
Customer.objects.bulk_create([
|
|
182
|
+
Customer(
|
|
183
|
+
cpf=c["cpf"],
|
|
184
|
+
name=c["name"],
|
|
185
|
+
email=c["email"],
|
|
186
|
+
phone=c["phone"],
|
|
187
|
+
)
|
|
188
|
+
for c in customers
|
|
189
|
+
])
|
|
190
|
+
|
|
191
|
+
self.stdout.write(f"✓ {len(customers)} customers inseridos")
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### FastAPI mock
|
|
195
|
+
|
|
196
|
+
```python
|
|
197
|
+
# tests/conftest.py
|
|
198
|
+
import pytest
|
|
199
|
+
from httpx import AsyncClient
|
|
200
|
+
from fakeforge import FakeForge
|
|
201
|
+
|
|
202
|
+
@pytest.fixture
|
|
203
|
+
def fake_customer():
|
|
204
|
+
ff = FakeForge()
|
|
205
|
+
return ff.preset("customer", 1)[0]
|
|
206
|
+
|
|
207
|
+
@pytest.mark.asyncio
|
|
208
|
+
async def test_signup(fake_customer, client: AsyncClient):
|
|
209
|
+
response = await client.post("/signup", json=fake_customer)
|
|
210
|
+
assert response.status_code == 201
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## API key opcional (plano Dev/Team)
|
|
214
|
+
|
|
215
|
+
Sem API key: 50 chamadas/dia por IP, até 100 items por chamada. Perfeito pra dev local.
|
|
216
|
+
|
|
217
|
+
Com API key do plano [Dev (R$29/mês)](https://fakeforge.com.br/pricing?plan=dev): 10.000 chamadas/dia, até 10.000 items por chamada. Ideal pra CI/CD, seed em produção, load test.
|
|
218
|
+
|
|
219
|
+
```python
|
|
220
|
+
import os
|
|
221
|
+
from fakeforge import FakeForge
|
|
222
|
+
|
|
223
|
+
ff = FakeForge(api_key=os.environ["FAKEFORGE_API_KEY"])
|
|
224
|
+
cpfs = ff.cpf(10_000) # no Dev, cabe em 1 chamada
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Pegue sua API key em [fakeforge.com.br/dashboard](https://fakeforge.com.br/dashboard).
|
|
228
|
+
|
|
229
|
+
## Tratamento de erros
|
|
230
|
+
|
|
231
|
+
```python
|
|
232
|
+
from fakeforge import FakeForge, FakeForgeError
|
|
233
|
+
|
|
234
|
+
ff = FakeForge()
|
|
235
|
+
|
|
236
|
+
try:
|
|
237
|
+
cpfs = ff.cpf(1000)
|
|
238
|
+
except FakeForgeError as e:
|
|
239
|
+
if e.status == 429:
|
|
240
|
+
print(f"Rate limit: {e.used_today}/{e.daily_limit}")
|
|
241
|
+
print(f"Upgrade: {e.upgrade_url}")
|
|
242
|
+
else:
|
|
243
|
+
raise
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## API completa
|
|
247
|
+
|
|
248
|
+
### Documentos pessoais
|
|
249
|
+
|
|
250
|
+
- `cpf(quantity=1, formatted=True) -> list[str]`
|
|
251
|
+
- `cnpj(quantity=1, formatted=True) -> list[str]`
|
|
252
|
+
- `cnpj_alfa(quantity=1, formatted=True) -> list[str]` — novo formato 2026
|
|
253
|
+
- `cnh(quantity=1, formatted=True) -> list[str]`
|
|
254
|
+
- `rg(quantity=1, formatted=True) -> list[str]`
|
|
255
|
+
- `pis(quantity=1, formatted=True) -> list[str]`
|
|
256
|
+
- `renavam(quantity=1, formatted=True) -> list[str]`
|
|
257
|
+
- `titulo_eleitor(quantity=1, formatted=True) -> list[str]`
|
|
258
|
+
- `placa(quantity=1) -> list[str]`
|
|
259
|
+
|
|
260
|
+
### Contato
|
|
261
|
+
|
|
262
|
+
- `email(quantity=1) -> list[str]`
|
|
263
|
+
- `phone(quantity=1, formatted=True) -> list[str]` — celular ANATEL
|
|
264
|
+
- `landline(quantity=1, formatted=True) -> list[str]` — fixo
|
|
265
|
+
|
|
266
|
+
### Endereço
|
|
267
|
+
|
|
268
|
+
- `cep(quantity=1, formatted=True) -> list[str]`
|
|
269
|
+
- `address(quantity=1, formatted=True) -> list[dict]`
|
|
270
|
+
|
|
271
|
+
### Pessoa completa
|
|
272
|
+
|
|
273
|
+
- `person(quantity=1, formatted=True) -> list[dict]`
|
|
274
|
+
- `full_name(quantity=1) -> list[str]`
|
|
275
|
+
|
|
276
|
+
### Financeiro
|
|
277
|
+
|
|
278
|
+
- `credit_card(quantity=1, formatted=True) -> list[dict]`
|
|
279
|
+
- `pix_key(quantity=1) -> list[str]`
|
|
280
|
+
- `bank_account(quantity=1, formatted=True) -> list[dict]`
|
|
281
|
+
|
|
282
|
+
### Empresa
|
|
283
|
+
|
|
284
|
+
- `company(quantity=1, formatted=True) -> list[dict]`
|
|
285
|
+
|
|
286
|
+
### Presets
|
|
287
|
+
|
|
288
|
+
- `preset(name, quantity=1, formatted=True) -> list[dict]`
|
|
289
|
+
|
|
290
|
+
### Genérico
|
|
291
|
+
|
|
292
|
+
- `generate(type_, quantity=1, formatted=True) -> list[Any]`
|
|
293
|
+
|
|
294
|
+
## Perguntas frequentes
|
|
295
|
+
|
|
296
|
+
### É legal usar CPFs/CNPJs gerados em testes?
|
|
297
|
+
|
|
298
|
+
Sim. Gerar números que passam validação matemática (mod-11) pra fins de teste é prática padrão em desenvolvimento. Crime é usar CPF/CNPJ (fake ou real) pra fraude, sonegação ou cadastro em nome de terceiro.
|
|
299
|
+
|
|
300
|
+
### Os dados batem no DICT/SPC/Serasa?
|
|
301
|
+
|
|
302
|
+
Não. São dados matematicamente válidos mas não existem em nenhuma base oficial. Perfeito pra teste de formato, validação de schema e seed de staging. Não serve pra teste com API externa que consulta base real.
|
|
303
|
+
|
|
304
|
+
### Como configurar em CI (GitHub Actions)?
|
|
305
|
+
|
|
306
|
+
```yaml
|
|
307
|
+
- name: Rodar testes com FakeForge
|
|
308
|
+
env:
|
|
309
|
+
FAKEFORGE_API_KEY: ${{ secrets.FAKEFORGE_API_KEY }}
|
|
310
|
+
run: pytest
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
Cache dos dados na primeira chamada evita esgotar quota:
|
|
314
|
+
|
|
315
|
+
```python
|
|
316
|
+
# tests/fixtures.py
|
|
317
|
+
import json
|
|
318
|
+
from pathlib import Path
|
|
319
|
+
from fakeforge import FakeForge
|
|
320
|
+
|
|
321
|
+
CACHE = Path(__file__).parent / "customers.json"
|
|
322
|
+
|
|
323
|
+
def get_customers():
|
|
324
|
+
if CACHE.exists():
|
|
325
|
+
return json.loads(CACHE.read_text())
|
|
326
|
+
|
|
327
|
+
ff = FakeForge()
|
|
328
|
+
customers = ff.preset("customer", 100)
|
|
329
|
+
CACHE.write_text(json.dumps(customers, indent=2, ensure_ascii=False))
|
|
330
|
+
return customers
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
## Suporte
|
|
334
|
+
|
|
335
|
+
- 📚 Docs completos: [fakeforge.com.br/docs](https://fakeforge.com.br/docs)
|
|
336
|
+
- 💬 Email direto: `hey@fakeforge.com.br`
|
|
337
|
+
- 🐛 Issues: [github.com/everpaula/fakeforge-br/issues](https://github.com/everpaula/fakeforge-br/issues)
|
|
338
|
+
|
|
339
|
+
## Licença
|
|
340
|
+
|
|
341
|
+
MIT — veja [LICENSE](./LICENSE) para detalhes.
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
Feito por [Everton Paula](https://fakeforge.com.br) — engenheiro brasileiro que precisou de dados válidos pra testar checkout PIX e escreveu essa lib porque nenhuma outra funcionava direito.
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
# fakeforge
|
|
2
|
+
|
|
3
|
+
SDK oficial do [FakeForge](https://fakeforge.com.br) para Python — gera dados brasileiros válidos (CPF, CNPJ, CEP, PIX, cartão de crédito) para testes de software.
|
|
4
|
+
|
|
5
|
+
- ✅ **Zero dependências runtime** (usa `urllib` nativo)
|
|
6
|
+
- ✅ **Type hints completos** (compatível com mypy strict)
|
|
7
|
+
- ✅ **Python 3.8+**
|
|
8
|
+
- ✅ **Validação real** — todos os documentos passam mod-11 da Receita Federal, Luhn, ANATEL
|
|
9
|
+
- ✅ **Presets correlacionados** — pessoa completa com CPF + email + endereço + telefone em 1 chamada
|
|
10
|
+
- ✅ **CNPJ alfanumérico 2026** — cobertura do novo formato (IN RFB 2.229)
|
|
11
|
+
- ✅ **Grátis** — 50 chamadas/dia sem API key, ou 10.000/dia com plano Dev (R$29/mês)
|
|
12
|
+
|
|
13
|
+
## Instalação
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install fakeforge-br
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
poetry add fakeforge
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
uv add fakeforge
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Uso rápido
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
from fakeforge import FakeForge
|
|
31
|
+
|
|
32
|
+
ff = FakeForge()
|
|
33
|
+
|
|
34
|
+
# CPFs válidos (mod-11 da Receita Federal)
|
|
35
|
+
cpfs = ff.cpf(10)
|
|
36
|
+
# ['123.456.789-09', '987.654.321-00', ...]
|
|
37
|
+
|
|
38
|
+
# CNPJs válidos (mod-11)
|
|
39
|
+
cnpjs = ff.cnpj(5)
|
|
40
|
+
|
|
41
|
+
# Chave PIX no formato BACEN
|
|
42
|
+
pix = ff.pix_key(3)
|
|
43
|
+
|
|
44
|
+
# Cartão de crédito com Luhn válido
|
|
45
|
+
cards = ff.credit_card(5)
|
|
46
|
+
# [{'number': '...', 'brand': 'visa', 'cvv': '123', 'expiry': '12/28'}, ...]
|
|
47
|
+
|
|
48
|
+
# Pessoa completa correlacionada
|
|
49
|
+
[pessoa] = ff.person(1)
|
|
50
|
+
print(pessoa["name"], pessoa["cpf"], pessoa["email"])
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Presets: dados correlacionados em 1 chamada
|
|
54
|
+
|
|
55
|
+
Presets retornam objetos com múltiplos campos que se relacionam — email deriva do nome, DDD bate com o estado do endereço, etc.
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
customers = ff.preset("customer", 100)
|
|
59
|
+
|
|
60
|
+
for c in customers:
|
|
61
|
+
print({
|
|
62
|
+
"name": c["name"],
|
|
63
|
+
"cpf": c["cpf"],
|
|
64
|
+
"email": c["email"],
|
|
65
|
+
"phone": c["phone"],
|
|
66
|
+
"address": c["address"],
|
|
67
|
+
})
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Presets disponíveis:
|
|
71
|
+
|
|
72
|
+
| Preset | Retorna |
|
|
73
|
+
|---|---|
|
|
74
|
+
| `customer` | pessoa + endereço + email + telefone + PIX |
|
|
75
|
+
| `employee` | pessoa + conta bancária + PIX |
|
|
76
|
+
| `company` | empresa + endereço + contato |
|
|
77
|
+
| `ecommerce_order` | cliente + cartão + entrega |
|
|
78
|
+
| `contact_list` | nome + email + telefone |
|
|
79
|
+
|
|
80
|
+
## Comparação com faker (pt-BR) e python-brasilidades
|
|
81
|
+
|
|
82
|
+
| Recurso | faker (pt-BR) | python-brasilidades | fakeforge |
|
|
83
|
+
|---|---|---|---|
|
|
84
|
+
| CPF com mod-11 válido | ❌ | ✅ | ✅ |
|
|
85
|
+
| CNPJ com mod-11 válido | ❌ | ✅ | ✅ |
|
|
86
|
+
| CNPJ alfanumérico 2026 | ❌ | ❌ | ✅ |
|
|
87
|
+
| Cartão com Luhn | ❌ | ❌ | ✅ |
|
|
88
|
+
| PIX BACEN (4 formatos) | ❌ | ❌ | ✅ |
|
|
89
|
+
| Correlação nome ↔ email ↔ DDD | ❌ | ❌ | ✅ |
|
|
90
|
+
| DDDs oficiais ANATEL | ❌ | Parcial | ✅ (67 DDDs) |
|
|
91
|
+
| 17 bancos brasileiros com DV | ❌ | ❌ | ✅ |
|
|
92
|
+
| Presets bundle (customer, employee, etc) | ❌ | ❌ | ✅ |
|
|
93
|
+
| API HTTP (sem instalar dep em outra linguagem) | ❌ | ❌ | ✅ |
|
|
94
|
+
|
|
95
|
+
**fakeforge** é o único com API HTTP + SDK Python que permite escalar geração em CI/CD sem instalar dep de biblioteca em cada linguagem do stack. Perfeito pra times que usam Python no backend mas Node no frontend.
|
|
96
|
+
|
|
97
|
+
## Uso com pytest
|
|
98
|
+
|
|
99
|
+
### Fixture reutilizável
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
# conftest.py
|
|
103
|
+
import pytest
|
|
104
|
+
from fakeforge import FakeForge
|
|
105
|
+
|
|
106
|
+
@pytest.fixture(scope="session")
|
|
107
|
+
def customers():
|
|
108
|
+
"""100 customers correlacionados. Escopo session pra reutilizar entre testes."""
|
|
109
|
+
ff = FakeForge()
|
|
110
|
+
return ff.preset("customer", 100)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@pytest.fixture(scope="session")
|
|
114
|
+
def cpfs_validos():
|
|
115
|
+
"""1000 CPFs válidos pra teste de load."""
|
|
116
|
+
ff = FakeForge()
|
|
117
|
+
return ff.cpf(1000)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
# test_checkout.py
|
|
122
|
+
def test_checkout_aceita_cpf_valido(customers, client):
|
|
123
|
+
for customer in customers[:20]:
|
|
124
|
+
response = client.post("/checkout", json={
|
|
125
|
+
"cpf": customer["cpf"],
|
|
126
|
+
"email": customer["email"],
|
|
127
|
+
})
|
|
128
|
+
assert response.status_code == 200
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Django ORM seed
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
# management/commands/seed_customers.py
|
|
135
|
+
from django.core.management.base import BaseCommand
|
|
136
|
+
from fakeforge import FakeForge
|
|
137
|
+
from myapp.models import Customer
|
|
138
|
+
|
|
139
|
+
class Command(BaseCommand):
|
|
140
|
+
help = "Popula banco com 1000 customers via FakeForge"
|
|
141
|
+
|
|
142
|
+
def handle(self, *args, **options):
|
|
143
|
+
ff = FakeForge(api_key="sua_key_dev") # 10.000/dia no Dev
|
|
144
|
+
customers = ff.preset("customer", 1000)
|
|
145
|
+
|
|
146
|
+
Customer.objects.bulk_create([
|
|
147
|
+
Customer(
|
|
148
|
+
cpf=c["cpf"],
|
|
149
|
+
name=c["name"],
|
|
150
|
+
email=c["email"],
|
|
151
|
+
phone=c["phone"],
|
|
152
|
+
)
|
|
153
|
+
for c in customers
|
|
154
|
+
])
|
|
155
|
+
|
|
156
|
+
self.stdout.write(f"✓ {len(customers)} customers inseridos")
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### FastAPI mock
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
# tests/conftest.py
|
|
163
|
+
import pytest
|
|
164
|
+
from httpx import AsyncClient
|
|
165
|
+
from fakeforge import FakeForge
|
|
166
|
+
|
|
167
|
+
@pytest.fixture
|
|
168
|
+
def fake_customer():
|
|
169
|
+
ff = FakeForge()
|
|
170
|
+
return ff.preset("customer", 1)[0]
|
|
171
|
+
|
|
172
|
+
@pytest.mark.asyncio
|
|
173
|
+
async def test_signup(fake_customer, client: AsyncClient):
|
|
174
|
+
response = await client.post("/signup", json=fake_customer)
|
|
175
|
+
assert response.status_code == 201
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## API key opcional (plano Dev/Team)
|
|
179
|
+
|
|
180
|
+
Sem API key: 50 chamadas/dia por IP, até 100 items por chamada. Perfeito pra dev local.
|
|
181
|
+
|
|
182
|
+
Com API key do plano [Dev (R$29/mês)](https://fakeforge.com.br/pricing?plan=dev): 10.000 chamadas/dia, até 10.000 items por chamada. Ideal pra CI/CD, seed em produção, load test.
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
import os
|
|
186
|
+
from fakeforge import FakeForge
|
|
187
|
+
|
|
188
|
+
ff = FakeForge(api_key=os.environ["FAKEFORGE_API_KEY"])
|
|
189
|
+
cpfs = ff.cpf(10_000) # no Dev, cabe em 1 chamada
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Pegue sua API key em [fakeforge.com.br/dashboard](https://fakeforge.com.br/dashboard).
|
|
193
|
+
|
|
194
|
+
## Tratamento de erros
|
|
195
|
+
|
|
196
|
+
```python
|
|
197
|
+
from fakeforge import FakeForge, FakeForgeError
|
|
198
|
+
|
|
199
|
+
ff = FakeForge()
|
|
200
|
+
|
|
201
|
+
try:
|
|
202
|
+
cpfs = ff.cpf(1000)
|
|
203
|
+
except FakeForgeError as e:
|
|
204
|
+
if e.status == 429:
|
|
205
|
+
print(f"Rate limit: {e.used_today}/{e.daily_limit}")
|
|
206
|
+
print(f"Upgrade: {e.upgrade_url}")
|
|
207
|
+
else:
|
|
208
|
+
raise
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## API completa
|
|
212
|
+
|
|
213
|
+
### Documentos pessoais
|
|
214
|
+
|
|
215
|
+
- `cpf(quantity=1, formatted=True) -> list[str]`
|
|
216
|
+
- `cnpj(quantity=1, formatted=True) -> list[str]`
|
|
217
|
+
- `cnpj_alfa(quantity=1, formatted=True) -> list[str]` — novo formato 2026
|
|
218
|
+
- `cnh(quantity=1, formatted=True) -> list[str]`
|
|
219
|
+
- `rg(quantity=1, formatted=True) -> list[str]`
|
|
220
|
+
- `pis(quantity=1, formatted=True) -> list[str]`
|
|
221
|
+
- `renavam(quantity=1, formatted=True) -> list[str]`
|
|
222
|
+
- `titulo_eleitor(quantity=1, formatted=True) -> list[str]`
|
|
223
|
+
- `placa(quantity=1) -> list[str]`
|
|
224
|
+
|
|
225
|
+
### Contato
|
|
226
|
+
|
|
227
|
+
- `email(quantity=1) -> list[str]`
|
|
228
|
+
- `phone(quantity=1, formatted=True) -> list[str]` — celular ANATEL
|
|
229
|
+
- `landline(quantity=1, formatted=True) -> list[str]` — fixo
|
|
230
|
+
|
|
231
|
+
### Endereço
|
|
232
|
+
|
|
233
|
+
- `cep(quantity=1, formatted=True) -> list[str]`
|
|
234
|
+
- `address(quantity=1, formatted=True) -> list[dict]`
|
|
235
|
+
|
|
236
|
+
### Pessoa completa
|
|
237
|
+
|
|
238
|
+
- `person(quantity=1, formatted=True) -> list[dict]`
|
|
239
|
+
- `full_name(quantity=1) -> list[str]`
|
|
240
|
+
|
|
241
|
+
### Financeiro
|
|
242
|
+
|
|
243
|
+
- `credit_card(quantity=1, formatted=True) -> list[dict]`
|
|
244
|
+
- `pix_key(quantity=1) -> list[str]`
|
|
245
|
+
- `bank_account(quantity=1, formatted=True) -> list[dict]`
|
|
246
|
+
|
|
247
|
+
### Empresa
|
|
248
|
+
|
|
249
|
+
- `company(quantity=1, formatted=True) -> list[dict]`
|
|
250
|
+
|
|
251
|
+
### Presets
|
|
252
|
+
|
|
253
|
+
- `preset(name, quantity=1, formatted=True) -> list[dict]`
|
|
254
|
+
|
|
255
|
+
### Genérico
|
|
256
|
+
|
|
257
|
+
- `generate(type_, quantity=1, formatted=True) -> list[Any]`
|
|
258
|
+
|
|
259
|
+
## Perguntas frequentes
|
|
260
|
+
|
|
261
|
+
### É legal usar CPFs/CNPJs gerados em testes?
|
|
262
|
+
|
|
263
|
+
Sim. Gerar números que passam validação matemática (mod-11) pra fins de teste é prática padrão em desenvolvimento. Crime é usar CPF/CNPJ (fake ou real) pra fraude, sonegação ou cadastro em nome de terceiro.
|
|
264
|
+
|
|
265
|
+
### Os dados batem no DICT/SPC/Serasa?
|
|
266
|
+
|
|
267
|
+
Não. São dados matematicamente válidos mas não existem em nenhuma base oficial. Perfeito pra teste de formato, validação de schema e seed de staging. Não serve pra teste com API externa que consulta base real.
|
|
268
|
+
|
|
269
|
+
### Como configurar em CI (GitHub Actions)?
|
|
270
|
+
|
|
271
|
+
```yaml
|
|
272
|
+
- name: Rodar testes com FakeForge
|
|
273
|
+
env:
|
|
274
|
+
FAKEFORGE_API_KEY: ${{ secrets.FAKEFORGE_API_KEY }}
|
|
275
|
+
run: pytest
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Cache dos dados na primeira chamada evita esgotar quota:
|
|
279
|
+
|
|
280
|
+
```python
|
|
281
|
+
# tests/fixtures.py
|
|
282
|
+
import json
|
|
283
|
+
from pathlib import Path
|
|
284
|
+
from fakeforge import FakeForge
|
|
285
|
+
|
|
286
|
+
CACHE = Path(__file__).parent / "customers.json"
|
|
287
|
+
|
|
288
|
+
def get_customers():
|
|
289
|
+
if CACHE.exists():
|
|
290
|
+
return json.loads(CACHE.read_text())
|
|
291
|
+
|
|
292
|
+
ff = FakeForge()
|
|
293
|
+
customers = ff.preset("customer", 100)
|
|
294
|
+
CACHE.write_text(json.dumps(customers, indent=2, ensure_ascii=False))
|
|
295
|
+
return customers
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
## Suporte
|
|
299
|
+
|
|
300
|
+
- 📚 Docs completos: [fakeforge.com.br/docs](https://fakeforge.com.br/docs)
|
|
301
|
+
- 💬 Email direto: `hey@fakeforge.com.br`
|
|
302
|
+
- 🐛 Issues: [github.com/everpaula/fakeforge-br/issues](https://github.com/everpaula/fakeforge-br/issues)
|
|
303
|
+
|
|
304
|
+
## Licença
|
|
305
|
+
|
|
306
|
+
MIT — veja [LICENSE](./LICENSE) para detalhes.
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
Feito por [Everton Paula](https://fakeforge.com.br) — engenheiro brasileiro que precisou de dados válidos pra testar checkout PIX e escreveu essa lib porque nenhuma outra funcionava direito.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "fakeforge-br"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "SDK oficial do FakeForge para gerar dados brasileiros válidos (CPF, CNPJ, CEP, PIX, cartão de crédito) em testes de software. Zero dependências runtime."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Everton Paula", email = "hey@fakeforge.com.br" }
|
|
14
|
+
]
|
|
15
|
+
keywords = [
|
|
16
|
+
"cpf", "cnpj", "cep", "pix", "gerador", "brazilian", "brazil",
|
|
17
|
+
"faker", "testing", "mock", "fixture", "seed", "fake-data",
|
|
18
|
+
"test-data", "python", "brasil"
|
|
19
|
+
]
|
|
20
|
+
classifiers = [
|
|
21
|
+
"Development Status :: 4 - Beta",
|
|
22
|
+
"Intended Audience :: Developers",
|
|
23
|
+
"License :: OSI Approved :: MIT License",
|
|
24
|
+
"Natural Language :: Portuguese (Brazilian)",
|
|
25
|
+
"Operating System :: OS Independent",
|
|
26
|
+
"Programming Language :: Python :: 3",
|
|
27
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
28
|
+
"Programming Language :: Python :: 3.8",
|
|
29
|
+
"Programming Language :: Python :: 3.9",
|
|
30
|
+
"Programming Language :: Python :: 3.10",
|
|
31
|
+
"Programming Language :: Python :: 3.11",
|
|
32
|
+
"Programming Language :: Python :: 3.12",
|
|
33
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
34
|
+
"Topic :: Software Development :: Testing",
|
|
35
|
+
"Typing :: Typed",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[project.urls]
|
|
39
|
+
Homepage = "https://fakeforge.com.br"
|
|
40
|
+
Documentation = "https://fakeforge.com.br/docs"
|
|
41
|
+
Repository = "https://github.com/everpaula/fakeforge-br"
|
|
42
|
+
Issues = "https://github.com/everpaula/fakeforge-br/issues"
|
|
43
|
+
|
|
44
|
+
[project.optional-dependencies]
|
|
45
|
+
dev = [
|
|
46
|
+
"pytest>=7.0",
|
|
47
|
+
"pytest-asyncio>=0.21",
|
|
48
|
+
"mypy>=1.0",
|
|
49
|
+
"ruff>=0.1",
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
[tool.hatch.build.targets.wheel]
|
|
53
|
+
packages = ["src/fakeforge"]
|
|
54
|
+
|
|
55
|
+
[tool.hatch.build.targets.sdist]
|
|
56
|
+
include = [
|
|
57
|
+
"src/fakeforge",
|
|
58
|
+
"README.md",
|
|
59
|
+
"LICENSE",
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
[tool.ruff]
|
|
63
|
+
line-length = 100
|
|
64
|
+
target-version = "py38"
|
|
65
|
+
|
|
66
|
+
[tool.mypy]
|
|
67
|
+
python_version = "3.8"
|
|
68
|
+
strict = true
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""
|
|
2
|
+
fakeforge - SDK oficial pra gerar dados brasileiros válidos.
|
|
3
|
+
|
|
4
|
+
Gere CPFs, CNPJs, CEPs, chaves PIX e cartões de crédito com validação
|
|
5
|
+
real (mod-11, Luhn, ANATEL) para testes de software, seed de banco e
|
|
6
|
+
fixture de QA.
|
|
7
|
+
|
|
8
|
+
Uso rápido:
|
|
9
|
+
|
|
10
|
+
from fakeforge import FakeForge
|
|
11
|
+
|
|
12
|
+
ff = FakeForge()
|
|
13
|
+
cpfs = ff.cpf(10) # 10 CPFs válidos
|
|
14
|
+
customers = ff.preset("customer", 100) # 100 pessoas correlacionadas
|
|
15
|
+
|
|
16
|
+
Para volumes maiores, use API key do plano Dev:
|
|
17
|
+
|
|
18
|
+
ff = FakeForge(api_key="sua_key")
|
|
19
|
+
cpfs = ff.cpf(10000) # até 10.000 por chamada
|
|
20
|
+
|
|
21
|
+
Homepage: https://fakeforge.com.br
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from fakeforge.client import FakeForge
|
|
25
|
+
from fakeforge.exceptions import FakeForgeError
|
|
26
|
+
|
|
27
|
+
__version__ = "0.1.0"
|
|
28
|
+
__all__ = ["FakeForge", "FakeForgeError"]
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"""Cliente principal do SDK FakeForge."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import urllib.error
|
|
5
|
+
import urllib.parse
|
|
6
|
+
import urllib.request
|
|
7
|
+
from typing import Any, Optional, TypeVar, cast
|
|
8
|
+
|
|
9
|
+
from fakeforge.exceptions import FakeForgeError
|
|
10
|
+
|
|
11
|
+
T = TypeVar("T")
|
|
12
|
+
|
|
13
|
+
DEFAULT_BASE_URL = "https://fakeforge.com.br"
|
|
14
|
+
DEFAULT_TIMEOUT = 30.0
|
|
15
|
+
USER_AGENT = "fakeforge-python-sdk/0.1.0"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class FakeForge:
|
|
19
|
+
"""
|
|
20
|
+
Cliente principal do SDK FakeForge.
|
|
21
|
+
|
|
22
|
+
Example:
|
|
23
|
+
>>> from fakeforge import FakeForge
|
|
24
|
+
>>> ff = FakeForge()
|
|
25
|
+
>>> cpfs = ff.cpf(10) # 10 CPFs válidos (mod-11)
|
|
26
|
+
>>> customers = ff.preset("customer", 100) # pessoa correlacionada
|
|
27
|
+
|
|
28
|
+
Example com API key (plano Dev/Team):
|
|
29
|
+
>>> import os
|
|
30
|
+
>>> ff = FakeForge(api_key=os.environ["FAKEFORGE_API_KEY"])
|
|
31
|
+
>>> cpfs = ff.cpf(10000) # até 10.000 por chamada no Dev
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
api_key: Optional[str] = None,
|
|
37
|
+
base_url: str = DEFAULT_BASE_URL,
|
|
38
|
+
timeout: float = DEFAULT_TIMEOUT,
|
|
39
|
+
) -> None:
|
|
40
|
+
"""
|
|
41
|
+
Args:
|
|
42
|
+
api_key: API key opcional (plano Dev/Team). Sem key, usa tier
|
|
43
|
+
grátis: 50 chamadas/dia por IP.
|
|
44
|
+
base_url: URL base da API. Só mude se for self-hosted.
|
|
45
|
+
timeout: Timeout em segundos pra cada request. Default: 30s.
|
|
46
|
+
"""
|
|
47
|
+
self.api_key = api_key
|
|
48
|
+
self.base_url = base_url.rstrip("/")
|
|
49
|
+
self.timeout = timeout
|
|
50
|
+
|
|
51
|
+
def _request(
|
|
52
|
+
self,
|
|
53
|
+
type_or_preset: str,
|
|
54
|
+
quantity: int = 1,
|
|
55
|
+
formatted: bool = True,
|
|
56
|
+
is_preset: bool = False,
|
|
57
|
+
) -> list[Any]:
|
|
58
|
+
params = {
|
|
59
|
+
"quantity": str(quantity),
|
|
60
|
+
"formatted": "true" if formatted else "false",
|
|
61
|
+
}
|
|
62
|
+
if is_preset:
|
|
63
|
+
params["preset"] = type_or_preset
|
|
64
|
+
else:
|
|
65
|
+
params["type"] = type_or_preset
|
|
66
|
+
|
|
67
|
+
url = f"{self.base_url}/api/generate?{urllib.parse.urlencode(params)}"
|
|
68
|
+
|
|
69
|
+
headers = {
|
|
70
|
+
"Content-Type": "application/json",
|
|
71
|
+
"User-Agent": USER_AGENT,
|
|
72
|
+
}
|
|
73
|
+
if self.api_key:
|
|
74
|
+
headers["X-API-Key"] = self.api_key
|
|
75
|
+
|
|
76
|
+
req = urllib.request.Request(url, headers=headers, method="GET")
|
|
77
|
+
|
|
78
|
+
try:
|
|
79
|
+
with urllib.request.urlopen(req, timeout=self.timeout) as response:
|
|
80
|
+
status = response.status
|
|
81
|
+
body_bytes = response.read()
|
|
82
|
+
except urllib.error.HTTPError as e:
|
|
83
|
+
status = e.code
|
|
84
|
+
body_bytes = e.read()
|
|
85
|
+
except urllib.error.URLError as e:
|
|
86
|
+
raise FakeForgeError(f"Erro de rede: {e.reason}", status=0)
|
|
87
|
+
except TimeoutError:
|
|
88
|
+
raise FakeForgeError(f"Timeout após {self.timeout}s", status=0)
|
|
89
|
+
|
|
90
|
+
try:
|
|
91
|
+
body = json.loads(body_bytes)
|
|
92
|
+
except json.JSONDecodeError:
|
|
93
|
+
raise FakeForgeError(f"Resposta inválida ({status})", status=status)
|
|
94
|
+
|
|
95
|
+
if status >= 400:
|
|
96
|
+
message = body.get("message") or body.get("error") or f"HTTP {status}"
|
|
97
|
+
raise FakeForgeError(message, status=status, body=body)
|
|
98
|
+
|
|
99
|
+
data = body.get("data", [])
|
|
100
|
+
return cast(list[Any], data)
|
|
101
|
+
|
|
102
|
+
# === Documentos pessoais ===
|
|
103
|
+
|
|
104
|
+
def cpf(self, quantity: int = 1, formatted: bool = True) -> list[str]:
|
|
105
|
+
"""Gera CPFs válidos (mod-11 da Receita Federal)."""
|
|
106
|
+
return self._request("cpf", quantity, formatted)
|
|
107
|
+
|
|
108
|
+
def cnpj(self, quantity: int = 1, formatted: bool = True) -> list[str]:
|
|
109
|
+
"""Gera CNPJs numéricos válidos (mod-11)."""
|
|
110
|
+
return self._request("cnpj", quantity, formatted)
|
|
111
|
+
|
|
112
|
+
def cnpj_alfa(self, quantity: int = 1, formatted: bool = True) -> list[str]:
|
|
113
|
+
"""Gera CNPJs no novo formato alfanumérico (IN RFB 2.229, vigência 01/07/2026)."""
|
|
114
|
+
return self._request("cnpjAlfa", quantity, formatted)
|
|
115
|
+
|
|
116
|
+
def cnh(self, quantity: int = 1, formatted: bool = True) -> list[str]:
|
|
117
|
+
"""Gera CNHs válidas (mod-11 do DENATRAN)."""
|
|
118
|
+
return self._request("cnh", quantity, formatted)
|
|
119
|
+
|
|
120
|
+
def rg(self, quantity: int = 1, formatted: bool = True) -> list[str]:
|
|
121
|
+
"""Gera RGs (formato por estado, mod-11)."""
|
|
122
|
+
return self._request("rg", quantity, formatted)
|
|
123
|
+
|
|
124
|
+
def pis(self, quantity: int = 1, formatted: bool = True) -> list[str]:
|
|
125
|
+
"""Gera PIS/PASEP/NIT/NIS válidos."""
|
|
126
|
+
return self._request("pis", quantity, formatted)
|
|
127
|
+
|
|
128
|
+
def renavam(self, quantity: int = 1, formatted: bool = True) -> list[str]:
|
|
129
|
+
"""Gera RENAVAMs válidos (mod-11 do DENATRAN)."""
|
|
130
|
+
return self._request("renavam", quantity, formatted)
|
|
131
|
+
|
|
132
|
+
def titulo_eleitor(self, quantity: int = 1, formatted: bool = True) -> list[str]:
|
|
133
|
+
"""Gera Títulos de Eleitor válidos."""
|
|
134
|
+
return self._request("tituloEleitor", quantity, formatted)
|
|
135
|
+
|
|
136
|
+
def placa(self, quantity: int = 1) -> list[str]:
|
|
137
|
+
"""Gera placas Mercosul (formato LLLNLNN)."""
|
|
138
|
+
return self._request("placa", quantity)
|
|
139
|
+
|
|
140
|
+
# === Contato ===
|
|
141
|
+
|
|
142
|
+
def email(self, quantity: int = 1) -> list[str]:
|
|
143
|
+
"""Gera emails com nomes brasileiros e domínios populares."""
|
|
144
|
+
return self._request("email", quantity)
|
|
145
|
+
|
|
146
|
+
def phone(self, quantity: int = 1, formatted: bool = True) -> list[str]:
|
|
147
|
+
"""Gera celulares ANATEL (11 dígitos com 9 na frente + DDD válido)."""
|
|
148
|
+
return self._request("phone", quantity, formatted)
|
|
149
|
+
|
|
150
|
+
def landline(self, quantity: int = 1, formatted: bool = True) -> list[str]:
|
|
151
|
+
"""Gera telefones fixos residenciais (10 dígitos, sem 9)."""
|
|
152
|
+
return self._request("landline", quantity, formatted)
|
|
153
|
+
|
|
154
|
+
# === Endereço ===
|
|
155
|
+
|
|
156
|
+
def cep(self, quantity: int = 1, formatted: bool = True) -> list[str]:
|
|
157
|
+
"""Gera CEPs válidos por estado."""
|
|
158
|
+
return self._request("cep", quantity, formatted)
|
|
159
|
+
|
|
160
|
+
def address(self, quantity: int = 1, formatted: bool = True) -> list[dict[str, Any]]:
|
|
161
|
+
"""Gera endereços completos (rua, bairro, cidade, estado, CEP)."""
|
|
162
|
+
return self._request("address", quantity, formatted)
|
|
163
|
+
|
|
164
|
+
# === Pessoa completa ===
|
|
165
|
+
|
|
166
|
+
def person(self, quantity: int = 1, formatted: bool = True) -> list[dict[str, Any]]:
|
|
167
|
+
"""Gera pessoa completa correlacionada (nome + CPF + email + telefone)."""
|
|
168
|
+
return self._request("person", quantity, formatted)
|
|
169
|
+
|
|
170
|
+
def full_name(self, quantity: int = 1) -> list[str]:
|
|
171
|
+
"""Gera nomes completos brasileiros."""
|
|
172
|
+
return self._request("fullName", quantity)
|
|
173
|
+
|
|
174
|
+
# === Financeiro ===
|
|
175
|
+
|
|
176
|
+
def credit_card(self, quantity: int = 1, formatted: bool = True) -> list[dict[str, Any]]:
|
|
177
|
+
"""Gera cartões de crédito com Luhn válido (qualquer bandeira)."""
|
|
178
|
+
return self._request("creditCard", quantity, formatted)
|
|
179
|
+
|
|
180
|
+
def pix_key(self, quantity: int = 1) -> list[str]:
|
|
181
|
+
"""Gera chaves PIX no formato BACEN (CPF, email, telefone ou EVP UUID)."""
|
|
182
|
+
return self._request("pixKey", quantity)
|
|
183
|
+
|
|
184
|
+
def bank_account(self, quantity: int = 1, formatted: bool = True) -> list[dict[str, Any]]:
|
|
185
|
+
"""Gera conta bancária brasileira (banco + agência + conta com DV)."""
|
|
186
|
+
return self._request("bankAccount", quantity, formatted)
|
|
187
|
+
|
|
188
|
+
# === Empresa ===
|
|
189
|
+
|
|
190
|
+
def company(self, quantity: int = 1, formatted: bool = True) -> list[dict[str, Any]]:
|
|
191
|
+
"""Gera empresa completa (CNPJ + razão social + endereço)."""
|
|
192
|
+
return self._request("company", quantity, formatted)
|
|
193
|
+
|
|
194
|
+
# === Presets correlacionados ===
|
|
195
|
+
|
|
196
|
+
def preset(self, name: str, quantity: int = 1, formatted: bool = True) -> list[dict[str, Any]]:
|
|
197
|
+
"""
|
|
198
|
+
Gera dados correlacionados via preset.
|
|
199
|
+
|
|
200
|
+
Presets disponíveis:
|
|
201
|
+
- customer: pessoa + endereço + email + telefone + PIX
|
|
202
|
+
- employee: pessoa + conta bancária + PIX
|
|
203
|
+
- company: empresa + endereço + contato
|
|
204
|
+
- ecommerce_order: cliente + cartão + entrega
|
|
205
|
+
- contact_list: nome + email + telefone
|
|
206
|
+
|
|
207
|
+
Example:
|
|
208
|
+
>>> customers = ff.preset("customer", 100)
|
|
209
|
+
>>> for c in customers:
|
|
210
|
+
... print(c["name"], c["cpf"], c["email"])
|
|
211
|
+
"""
|
|
212
|
+
return self._request(name, quantity, formatted, is_preset=True)
|
|
213
|
+
|
|
214
|
+
# === Genérico ===
|
|
215
|
+
|
|
216
|
+
def generate(self, type_: str, quantity: int = 1, formatted: bool = True) -> list[Any]:
|
|
217
|
+
"""Genérico: chama a API com qualquer type. Útil pra types sem método dedicado."""
|
|
218
|
+
return self._request(type_, quantity, formatted)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Exceções específicas do FakeForge SDK."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Optional
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class FakeForgeError(Exception):
|
|
7
|
+
"""
|
|
8
|
+
Erro específico do FakeForge com contexto adicional.
|
|
9
|
+
|
|
10
|
+
Attributes:
|
|
11
|
+
status: Código HTTP retornado pela API (0 se erro de rede).
|
|
12
|
+
code: Nome do erro (rate_limit_exceeded, quantity_limit_exceeded, etc).
|
|
13
|
+
upgrade_url: URL do checkout do plano superior, quando aplicável.
|
|
14
|
+
plan: Plano do user (anon, free, dev, team).
|
|
15
|
+
daily_limit: Limite diário do plano atual.
|
|
16
|
+
used_today: Quantas chamadas já foram feitas hoje.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
message: str,
|
|
22
|
+
status: int = 0,
|
|
23
|
+
body: Optional[dict[str, Any]] = None,
|
|
24
|
+
) -> None:
|
|
25
|
+
super().__init__(message)
|
|
26
|
+
self.status = status
|
|
27
|
+
body = body or {}
|
|
28
|
+
self.code = str(body.get("error", "unknown"))
|
|
29
|
+
upgrade_obj = body.get("upgrade")
|
|
30
|
+
if isinstance(upgrade_obj, dict):
|
|
31
|
+
self.upgrade_url = upgrade_obj.get("url")
|
|
32
|
+
else:
|
|
33
|
+
self.upgrade_url = body.get("upgrade_url")
|
|
34
|
+
self.plan = body.get("plan")
|
|
35
|
+
self.daily_limit = body.get("daily_limit")
|
|
36
|
+
self.used_today = body.get("your_usage_today")
|
|
File without changes
|