Skip to content

Commit cda7670

Browse files
committed
Implement autocomplete service tests
1 parent b81fe1b commit cda7670

File tree

7 files changed

+279
-2
lines changed

7 files changed

+279
-2
lines changed

node/mocks/contextFactory.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import parse from 'co-body'
2+
3+
import { MockedIntelligentSearchApiClient } from './intelligent-search-api'
4+
import type { IntelligentSearchClientArgs } from './intsch'
5+
import { MockedIntschClient } from './intsch'
6+
7+
export function createContext<Ctx = Context>({
8+
accountName,
9+
intelligentSearchApiSettings,
10+
intschSettings,
11+
cookies,
12+
body,
13+
req,
14+
query,
15+
production,
16+
}: {
17+
production?: boolean
18+
appSettings?: Record<string, any>
19+
intschSettings?: IntelligentSearchClientArgs
20+
intelligentSearchApiSettings?: IntelligentSearchClientArgs
21+
req?: {
22+
body?: any
23+
}
24+
query?: Record<string, unknown>
25+
body?: any
26+
cookies?: Record<string, string>
27+
accountName?: string
28+
}) {
29+
if (req?.body instanceof Error) {
30+
jest.spyOn(parse, 'json').mockRejectedValue(req.body)
31+
} else {
32+
jest.spyOn(parse, 'json').mockReturnValue(req?.body ?? {})
33+
}
34+
35+
return {
36+
req: {},
37+
body: body ?? {},
38+
cookies: {
39+
get: jest.fn().mockImplementation((key: string) => cookies?.[key]),
40+
},
41+
get: jest.fn().mockReturnValue('localhost'),
42+
response: {
43+
set: jest.fn(),
44+
},
45+
clients: {
46+
intsch: new MockedIntschClient(intschSettings),
47+
intelligentSearchApi: new MockedIntelligentSearchApiClient(
48+
intelligentSearchApiSettings
49+
),
50+
apps: {
51+
getAppSettings: jest.fn().mockReturnValue({}),
52+
},
53+
},
54+
vtex: {
55+
production: production ?? false,
56+
account: accountName ?? 'biggy',
57+
logger: {
58+
info: jest.fn(),
59+
warn: jest.fn(),
60+
error: jest.fn(),
61+
},
62+
},
63+
query: query ?? {},
64+
} as unknown as Ctx
65+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import type { IIntelligentSearchClient } from '../clients/intsch/types'
2+
import type { IntelligentSearchClientArgs } from './intsch'
3+
4+
export class MockedIntelligentSearchApiClient
5+
implements IIntelligentSearchClient
6+
{
7+
constructor(args?: IntelligentSearchClientArgs) {
8+
if (args?.fetchAutocompleteSuggestions instanceof Error) {
9+
this.fetchAutocompleteSuggestions.mockRejectedValue(
10+
args.fetchAutocompleteSuggestions
11+
)
12+
} else {
13+
this.fetchAutocompleteSuggestions.mockResolvedValue(
14+
args?.fetchAutocompleteSuggestions ?? null
15+
)
16+
}
17+
}
18+
19+
public fetchAutocompleteSuggestions = jest.fn()
20+
}

node/mocks/intsch.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import type {
2+
IIntelligentSearchClient,
3+
AutocompleteSuggestionsResponse,
4+
} from '../clients/intsch/types'
5+
6+
export class MockedIntschClient implements IIntelligentSearchClient {
7+
constructor(args?: IntelligentSearchClientArgs) {
8+
if (args?.fetchAutocompleteSuggestions instanceof Error) {
9+
this.fetchAutocompleteSuggestions.mockRejectedValue(
10+
args.fetchAutocompleteSuggestions
11+
)
12+
} else {
13+
this.fetchAutocompleteSuggestions.mockResolvedValue(
14+
args?.fetchAutocompleteSuggestions ?? null
15+
)
16+
}
17+
}
18+
19+
public fetchAutocompleteSuggestions = jest.fn()
20+
}
21+
22+
export type IntelligentSearchClientArgs = {
23+
fetchAutocompleteSuggestions?: AutocompleteSuggestionsResponse | Error
24+
}

node/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"@gocommerce/utils": "^0.6.11",
99
"atob": "^2.1.2",
1010
"camelcase": "^5.0.0",
11+
"co-body": "^6.2.0",
1112
"cookie": "^0.3.1",
1213
"graphql": "^14.7.0",
1314
"graphql-tools": "^4.0.6",
@@ -19,6 +20,7 @@
1920
"devDependencies": {
2021
"@types/atob": "^2.1.2",
2122
"@types/camelcase": "^4.1.0",
23+
"@types/co-body": "^6.1.3",
2224
"@types/cookie": "^0.4.1",
2325
"@types/graphql": "^14.5.0",
2426
"@types/jest": "^29.5.14",
@@ -32,8 +34,8 @@
3234
"eslint-config-vtex": "^10.1.0",
3335
"jest": "^29.7.0",
3436
"ts-jest": "^29.2.5",
35-
"typescript": "5.5.3",
3637
"typemoq": "^2.1.0",
38+
"typescript": "5.5.3",
3739
"vtex.catalog-api-proxy": "http://vtex.vtexassets.com/_v/public/typings/v1/[email protected]/public/_types/react",
3840
"vtex.messages": "http://vtex.vtexassets.com/_v/public/typings/v1/[email protected]/public/@types/vtex.messages",
3941
"vtex.rewriter": "http://vtex.vtexassets.com/_v/public/typings/v1/[email protected]/public/@types/vtex.rewriter"

node/services/autocomplete.test.ts

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import { fetchAutocompleteSuggestions } from './autocomplete'
2+
import { createContext } from '../mocks/contextFactory'
3+
4+
describe('fetchAutocompleteSuggestions', () => {
5+
beforeEach(() => {
6+
jest.clearAllMocks()
7+
})
8+
9+
it('should call both clients and not log errors', async () => {
10+
const ctx = createContext({
11+
intelligentSearchApiSettings: {
12+
fetchAutocompleteSuggestions: {
13+
searches: [{ term: 'test', count: 1 }],
14+
},
15+
},
16+
intschSettings: {
17+
fetchAutocompleteSuggestions: {
18+
searches: [{ term: 'test', count: 1 }],
19+
},
20+
},
21+
})
22+
23+
await fetchAutocompleteSuggestions(ctx, 'test')
24+
25+
expect(
26+
ctx.clients.intelligentSearchApi.fetchAutocompleteSuggestions
27+
).toHaveBeenCalledWith({
28+
query: 'test',
29+
})
30+
expect(
31+
ctx.clients.intsch.fetchAutocompleteSuggestions
32+
).toHaveBeenCalledWith({
33+
query: 'test',
34+
})
35+
36+
// no errors should be logged because the results are equal
37+
expect(ctx.vtex.logger.error).not.toHaveBeenCalled()
38+
})
39+
40+
it('should log if the results are different', async () => {
41+
const result = {
42+
searches: [{ term: 'test', count: 1 }],
43+
}
44+
45+
const ctx = createContext({
46+
intelligentSearchApiSettings: {
47+
fetchAutocompleteSuggestions: result,
48+
},
49+
intschSettings: {
50+
fetchAutocompleteSuggestions: {
51+
searches: [
52+
{
53+
term: 'camisa feminina',
54+
count: 1,
55+
attributes: [
56+
{
57+
key: 'departamento',
58+
labelKey: 'Departamento',
59+
labelValue: 'Apparel & Accessories',
60+
value: 'apparel---accessories',
61+
},
62+
{
63+
key: 'categoria',
64+
labelKey: 'Categoria',
65+
labelValue: 'Roupa',
66+
value: 'roupa',
67+
},
68+
],
69+
},
70+
{
71+
term: 'camisa masculina',
72+
count: 1,
73+
},
74+
{
75+
term: 'camiseta',
76+
count: 2,
77+
},
78+
{
79+
term: 'camisa',
80+
count: 1,
81+
},
82+
],
83+
},
84+
},
85+
})
86+
87+
const response = await fetchAutocompleteSuggestions(ctx, 'test')
88+
89+
expect(
90+
ctx.clients.intelligentSearchApi.fetchAutocompleteSuggestions
91+
).toHaveBeenCalledWith({
92+
query: 'test',
93+
})
94+
expect(
95+
ctx.clients.intsch.fetchAutocompleteSuggestions
96+
).toHaveBeenCalledWith({
97+
query: 'test',
98+
})
99+
expect(ctx.vtex.logger.error).toHaveBeenCalledWith({
100+
message: 'Autocomplete Suggestions: "test": Results differ',
101+
params: JSON.stringify({ query: 'test' }),
102+
})
103+
expect(response).toEqual(result)
104+
})
105+
106+
it('should still function if the new client fails', async () => {
107+
const result = {
108+
searches: [{ term: 'test', count: 1 }],
109+
}
110+
111+
const ctx = createContext({
112+
intelligentSearchApiSettings: {
113+
fetchAutocompleteSuggestions: result,
114+
},
115+
intschSettings: {
116+
fetchAutocompleteSuggestions: new Error('Failed to fetch suggestions'),
117+
},
118+
})
119+
120+
const response = await fetchAutocompleteSuggestions(ctx, 'test')
121+
122+
expect(
123+
ctx.clients.intelligentSearchApi.fetchAutocompleteSuggestions
124+
).toHaveBeenCalledWith({
125+
query: 'test',
126+
})
127+
expect(
128+
ctx.clients.intsch.fetchAutocompleteSuggestions
129+
).toHaveBeenCalledWith({
130+
query: 'test',
131+
})
132+
133+
expect(ctx.vtex.logger.error).not.toHaveBeenCalled()
134+
// the result should be the result from the old client
135+
expect(response).toEqual(result)
136+
})
137+
})

node/utils/compareResults.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ export async function compareApiResults<T>(
129129
})
130130
}
131131

132-
if (!areEqual) {
132+
if (!areEqual && !hasError1 && !hasError2) {
133133
logger.error({
134134
message: `${logPrefix}: Results differ`,
135135
params: JSON.stringify(options.args),

node/yarn.lock

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,11 @@
344344
protobufjs "^7.5.3"
345345
yargs "^17.7.2"
346346

347+
"@hapi/bourne@^3.0.0":
348+
version "3.0.0"
349+
resolved "https://registry.yarnpkg.com/@hapi/bourne/-/bourne-3.0.0.tgz#f11fdf7dda62fe8e336fa7c6642d9041f30356d7"
350+
integrity sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w==
351+
347352
"@istanbuljs/load-nyc-config@^1.0.0":
348353
version "1.0.0"
349354
resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz#10602de5570baea82f8afbfa2630b24e7a8cfe5b"
@@ -1080,6 +1085,14 @@
10801085
resolved "https://registry.yarnpkg.com/@types/camelcase/-/camelcase-4.1.0.tgz#e054f7986f31658d49936261b5cd4588ef29d1ee"
10811086
integrity sha512-nsaprOtNLvUrLyFX5+mRpE9h2Q0d5YzQRr+Lav3fxdYtc1/E/U7G+Ld861NWBDDtWY3MnwKoUOhCrE1nrVxUQA==
10821087

1088+
"@types/co-body@^6.1.3":
1089+
version "6.1.3"
1090+
resolved "https://registry.yarnpkg.com/@types/co-body/-/co-body-6.1.3.tgz#201796c6389066b400cfcb4e1ec5c3db798265a2"
1091+
integrity sha512-UhuhrQ5hclX6UJctv5m4Rfp52AfG9o9+d9/HwjxhVB5NjXxr5t9oKgJxN8xRHgr35oo8meUEHUPFWiKg6y71aA==
1092+
dependencies:
1093+
"@types/node" "*"
1094+
"@types/qs" "*"
1095+
10831096
"@types/color-name@^1.1.1":
10841097
version "1.1.1"
10851098
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
@@ -1240,6 +1253,11 @@
12401253
dependencies:
12411254
undici-types "~6.19.2"
12421255

1256+
"@types/qs@*":
1257+
version "6.14.0"
1258+
resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.14.0.tgz#d8b60cecf62f2db0fb68e5e006077b9178b85de5"
1259+
integrity sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==
1260+
12431261
"@types/qs@^6.5.1":
12441262
version "6.5.3"
12451263
resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.5.3.tgz#1c3b71b091eaeaf5924538006b7f70603ce63d38"
@@ -1975,6 +1993,17 @@ co-body@^6.0.0:
19751993
raw-body "^2.3.3"
19761994
type-is "^1.6.16"
19771995

1996+
co-body@^6.2.0:
1997+
version "6.2.0"
1998+
resolved "https://registry.yarnpkg.com/co-body/-/co-body-6.2.0.tgz#afd776d60e5659f4eee862df83499698eb1aea1b"
1999+
integrity sha512-Kbpv2Yd1NdL1V/V4cwLVxraHDV6K8ayohr2rmH0J87Er8+zJjcTa6dAn9QMPC9CRgU8+aNajKbSf1TzDB1yKPA==
2000+
dependencies:
2001+
"@hapi/bourne" "^3.0.0"
2002+
inflation "^2.0.0"
2003+
qs "^6.5.2"
2004+
raw-body "^2.3.3"
2005+
type-is "^1.6.16"
2006+
19782007
co@^4.6.0:
19792008
version "4.6.0"
19802009
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"

0 commit comments

Comments
 (0)