Skip to content

Commit 2f0548c

Browse files
committed
Correcting the tests for the new structure.
1 parent f852863 commit 2f0548c

8 files changed

Lines changed: 525 additions & 2 deletions

File tree

.cspell/custom-dictionary-workspace.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ aceita
33
adzuna
44
Adzuna
55
algumas
6+
alternativa
67
ambiente
78
ativas
89
atuais
@@ -18,41 +19,53 @@ conhecidas
1819
defval
1920
diferentes
2021
doordash
22+
duplicadas
2123
Elementos
2224
encontrada
2325
encontradas
2426
encontrados
2527
exportacao
2628
exportadas
2729
ferramentas
30+
filtra
2831
filtros
2932
fintech
3033
flyio
3134
fonte
3235
formato
3336
hotjar
3437
huggingface
38+
inexistente
3539
informada
3640
Iniciando
3741
instacart
42+
interrompe
3843
isso
3944
KHTML
4045
klarna
46+
lança
47+
linhas
4148
Localização
4249
momentos
4350
Mostrando
4451
Nenhum
52+
normaliza
53+
paginação
4554
palavra
4655
palavras
4756
parseia
57+
pela
4858
permite
4959
planetscale
5060
positivos
5161
presencial
5262
primeiro
5363
Publicacao
64+
recente
5465
rejeita
5566
Resultado
67+
resultados
68+
retornadas
5669
robinhood
5770
salario
5871
scaleai
@@ -63,6 +76,7 @@ talvez
6376
teste
6477
themuse
6578
titulo
79+
trata
6680
typeform
6781
unicas
6882
únicas
@@ -71,5 +85,6 @@ usam
7185
vaga
7286
validos
7387
vazia
88+
vazio
7489
wealthfront
7590
weightsbiases
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
import axios from "axios";
2+
import { beforeEach, describe, expect, it, vi } from "vitest";
3+
import { createAdzunaAdapter } from "../../../src/adapters/adzuna.js";
4+
5+
vi.mock("axios");
6+
7+
const BASE_CONFIG = {
8+
searchLocation: "Brasil",
9+
resultsPerPage: 20,
10+
pageTimeoutMs: 1000,
11+
waitBetweenSearchesMs: 0,
12+
maxPagesPerKeyword: 1,
13+
};
14+
15+
describe("createAdzunaAdapter", () => {
16+
const adapter = createAdzunaAdapter({
17+
country: "br",
18+
appId: "test-app-id",
19+
appKey: "test-app-key",
20+
});
21+
22+
beforeEach(() => {
23+
vi.resetAllMocks();
24+
});
25+
26+
it("faz parse das vagas retornadas pela API", async () => {
27+
axios.get.mockResolvedValueOnce({
28+
data: {
29+
results: [
30+
{
31+
title: "Frontend React Developer",
32+
company: { display_name: "ACME" },
33+
location: { display_name: "São Paulo" },
34+
redirect_url: "https://adzuna.com/job/1",
35+
salary_min: 3000,
36+
salary_max: 5000,
37+
created: "2026-03-24T10:00:00Z",
38+
},
39+
],
40+
},
41+
});
42+
43+
const jobs = await adapter.search("React", BASE_CONFIG);
44+
45+
expect(axios.get).toHaveBeenCalledTimes(1);
46+
expect(jobs).toHaveLength(1);
47+
expect(jobs[0]).toMatchObject({
48+
source: "Adzuna",
49+
keyword: "React",
50+
titulo: "Frontend React Developer",
51+
empresa: "ACME",
52+
local: "São Paulo",
53+
link: "https://adzuna.com/job/1",
54+
salario: "3000-5000",
55+
dataPublicacao: "2026-03-24T10:00:00Z",
56+
});
57+
});
58+
59+
it("usa url alternativa quando redirect_url não existe", async () => {
60+
axios.get.mockResolvedValueOnce({
61+
data: {
62+
results: [
63+
{
64+
title: "Node Developer",
65+
company: { display_name: "Beta" },
66+
location: { display_name: "Remoto" },
67+
url: "https://adzuna.com/job/2",
68+
created: "2026-03-24T11:00:00Z",
69+
},
70+
],
71+
},
72+
});
73+
74+
const jobs = await adapter.search("Node", BASE_CONFIG);
75+
76+
expect(axios.get).toHaveBeenCalledTimes(1);
77+
expect(jobs).toHaveLength(1);
78+
expect(jobs[0]).toMatchObject({
79+
source: "Adzuna",
80+
keyword: "Node",
81+
titulo: "Node Developer",
82+
empresa: "Beta",
83+
local: "Remoto",
84+
link: "https://adzuna.com/job/2",
85+
salario: "",
86+
dataPublicacao: "2026-03-24T11:00:00Z",
87+
});
88+
});
89+
90+
it("busca mais de uma página enquanto houver resultados", async () => {
91+
axios.get
92+
.mockResolvedValueOnce({
93+
data: {
94+
results: [
95+
{
96+
title: "React Dev 1",
97+
company: { display_name: "ACME" },
98+
location: { display_name: "SP" },
99+
redirect_url: "https://adzuna.com/job/1",
100+
},
101+
],
102+
},
103+
})
104+
.mockResolvedValueOnce({
105+
data: {
106+
results: [
107+
{
108+
title: "React Dev 2",
109+
company: { display_name: "ACME 2" },
110+
location: { display_name: "RJ" },
111+
redirect_url: "https://adzuna.com/job/2",
112+
},
113+
],
114+
},
115+
});
116+
117+
const jobs = await adapter.search("React", {
118+
...BASE_CONFIG,
119+
maxPagesPerKeyword: 2,
120+
});
121+
122+
expect(axios.get).toHaveBeenCalledTimes(2);
123+
expect(jobs).toHaveLength(2);
124+
expect(jobs[0].titulo).toBe("React Dev 1");
125+
expect(jobs[1].titulo).toBe("React Dev 2");
126+
});
127+
128+
it("interrompe a paginação quando a API retorna results vazio", async () => {
129+
axios.get
130+
.mockResolvedValueOnce({
131+
data: {
132+
results: [
133+
{
134+
title: "React Dev 1",
135+
company: { display_name: "ACME" },
136+
location: { display_name: "SP" },
137+
redirect_url: "https://adzuna.com/job/1",
138+
},
139+
],
140+
},
141+
})
142+
.mockResolvedValueOnce({
143+
data: {
144+
results: [],
145+
},
146+
});
147+
148+
const jobs = await adapter.search("React", {
149+
...BASE_CONFIG,
150+
maxPagesPerKeyword: 2,
151+
});
152+
153+
expect(axios.get).toHaveBeenCalledTimes(2);
154+
expect(jobs).toHaveLength(1);
155+
});
156+
157+
it("lança erro quando a requisição falha", async () => {
158+
axios.get.mockRejectedValueOnce(new Error("falha na API"));
159+
160+
await expect(adapter.search("React", BASE_CONFIG)).rejects.toThrow(
161+
"falha na API",
162+
);
163+
});
164+
});

backend/tests/unit/services/app.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ vi.mock("../../../src/exporter.js", () => ({
3131
}));
3232
vi.mock("../../../src/logger.js", () => ({ logInfo: mocks.logInfoMock }));
3333

34-
import { run } from "../../../src/runner.js";
34+
import { run } from "../../../src/app.js";
3535

3636
describe("run", () => {
3737
it("orquestra coleta e exportacao", async () => {
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import axios from "axios";
2+
import { beforeEach, describe, expect, it, vi } from "vitest";
3+
import { createGreenhouseAdapter } from "../../../src/adapters/greenhouse.js";
4+
5+
vi.mock("axios");
6+
7+
const BASE_CONFIG = {
8+
pageTimeoutMs: 1000,
9+
};
10+
11+
describe("createGreenhouseAdapter", () => {
12+
const adapter = createGreenhouseAdapter("acme-board", "ACME");
13+
14+
beforeEach(() => {
15+
vi.clearAllMocks();
16+
});
17+
18+
it("filtra vagas pela keyword e normaliza o retorno", async () => {
19+
axios.get.mockResolvedValueOnce({
20+
data: {
21+
jobs: [
22+
{
23+
title: "Senior React Engineer",
24+
location: { name: "São Paulo" },
25+
absolute_url: "https://greenhouse.io/job/1",
26+
updated_at: "2026-03-24T10:00:00Z",
27+
},
28+
{
29+
title: "Backend Python Engineer",
30+
location: { name: "Rio de Janeiro" },
31+
absolute_url: "https://greenhouse.io/job/2",
32+
updated_at: "2026-03-24T11:00:00Z",
33+
},
34+
],
35+
},
36+
});
37+
38+
const jobs = await adapter.search("React", BASE_CONFIG);
39+
40+
expect(axios.get).toHaveBeenCalledTimes(1);
41+
expect(jobs).toHaveLength(1);
42+
expect(jobs[0]).toMatchObject({
43+
source: "Green House",
44+
keyword: "React",
45+
titulo: "Senior React Engineer",
46+
empresa: "ACME",
47+
local: "São Paulo",
48+
link: "https://greenhouse.io/job/1",
49+
dataPublicacao: "2026-03-24T10:00:00Z",
50+
});
51+
});
52+
53+
it("retorna lista vazia quando nenhuma vaga bate com a keyword", async () => {
54+
axios.get.mockResolvedValueOnce({
55+
data: {
56+
jobs: [
57+
{
58+
title: "Backend Python Engineer",
59+
location: { name: "Rio de Janeiro" },
60+
absolute_url: "https://greenhouse.io/job/2",
61+
updated_at: "2026-03-24T11:00:00Z",
62+
},
63+
],
64+
},
65+
});
66+
67+
const jobs = await adapter.search("React", BASE_CONFIG);
68+
69+
expect(jobs).toEqual([]);
70+
});
71+
72+
it("retorna lista vazia quando jobs não é array", async () => {
73+
axios.get.mockResolvedValueOnce({
74+
data: {
75+
jobs: null,
76+
},
77+
});
78+
79+
const jobs = await adapter.search("React", BASE_CONFIG);
80+
81+
expect(jobs).toEqual([]);
82+
});
83+
84+
it("lança erro quando a requisição falha", async () => {
85+
axios.get.mockRejectedValueOnce(new Error("falha greenhouse"));
86+
87+
await expect(adapter.search("React", BASE_CONFIG)).rejects.toThrow(
88+
"falha greenhouse",
89+
);
90+
});
91+
});

0 commit comments

Comments
 (0)