@coldiq/mcp 0.1.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/client.d.ts +8 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +47 -0
- package/dist/client.js.map +1 -0
- package/dist/executor.d.ts +21 -0
- package/dist/executor.d.ts.map +1 -0
- package/dist/executor.js +130 -0
- package/dist/executor.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +49 -0
- package/dist/index.js.map +1 -0
- package/dist/registry.d.ts +49 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +3104 -0
- package/dist/registry.js.map +1 -0
- package/dist/tools/enrich-company.d.ts +22 -0
- package/dist/tools/enrich-company.d.ts.map +1 -0
- package/dist/tools/enrich-company.js +21 -0
- package/dist/tools/enrich-company.js.map +1 -0
- package/dist/tools/enrich-email.d.ts +24 -0
- package/dist/tools/enrich-email.d.ts.map +1 -0
- package/dist/tools/enrich-email.js +19 -0
- package/dist/tools/enrich-email.js.map +1 -0
- package/dist/tools/enrich-emails.d.ts +31 -0
- package/dist/tools/enrich-emails.d.ts.map +1 -0
- package/dist/tools/enrich-emails.js +146 -0
- package/dist/tools/enrich-emails.js.map +1 -0
- package/dist/tools/enrich-person.d.ts +26 -0
- package/dist/tools/enrich-person.d.ts.map +1 -0
- package/dist/tools/enrich-person.js +23 -0
- package/dist/tools/enrich-person.js.map +1 -0
- package/dist/tools/fetch-page-content.d.ts +22 -0
- package/dist/tools/fetch-page-content.d.ts.map +1 -0
- package/dist/tools/fetch-page-content.js +32 -0
- package/dist/tools/fetch-page-content.js.map +1 -0
- package/dist/tools/find-email.d.ts +24 -0
- package/dist/tools/find-email.d.ts.map +1 -0
- package/dist/tools/find-email.js +19 -0
- package/dist/tools/find-email.js.map +1 -0
- package/dist/tools/find-emails.d.ts +31 -0
- package/dist/tools/find-emails.d.ts.map +1 -0
- package/dist/tools/find-emails.js +146 -0
- package/dist/tools/find-emails.js.map +1 -0
- package/dist/tools/find-influencers.d.ts +29 -0
- package/dist/tools/find-influencers.d.ts.map +1 -0
- package/dist/tools/find-influencers.js +30 -0
- package/dist/tools/find-influencers.js.map +1 -0
- package/dist/tools/find-people.d.ts +26 -0
- package/dist/tools/find-people.d.ts.map +1 -0
- package/dist/tools/find-people.js +61 -0
- package/dist/tools/find-people.js.map +1 -0
- package/dist/tools/find-phone.d.ts +24 -0
- package/dist/tools/find-phone.d.ts.map +1 -0
- package/dist/tools/find-phone.js +48 -0
- package/dist/tools/find-phone.js.map +1 -0
- package/dist/tools/find-signals.d.ts +26 -0
- package/dist/tools/find-signals.d.ts.map +1 -0
- package/dist/tools/find-signals.js +82 -0
- package/dist/tools/find-signals.js.map +1 -0
- package/dist/tools/search-ads.d.ts +33 -0
- package/dist/tools/search-ads.d.ts.map +1 -0
- package/dist/tools/search-ads.js +33 -0
- package/dist/tools/search-ads.js.map +1 -0
- package/dist/tools/search-companies.d.ts +42 -0
- package/dist/tools/search-companies.d.ts.map +1 -0
- package/dist/tools/search-companies.js +37 -0
- package/dist/tools/search-companies.js.map +1 -0
- package/dist/tools/search-jobs.d.ts +51 -0
- package/dist/tools/search-jobs.d.ts.map +1 -0
- package/dist/tools/search-jobs.js +64 -0
- package/dist/tools/search-jobs.js.map +1 -0
- package/dist/tools/search-places.d.ts +47 -0
- package/dist/tools/search-places.d.ts.map +1 -0
- package/dist/tools/search-places.js +42 -0
- package/dist/tools/search-places.js.map +1 -0
- package/dist/tools/search-reddit.d.ts +27 -0
- package/dist/tools/search-reddit.d.ts.map +1 -0
- package/dist/tools/search-reddit.js +30 -0
- package/dist/tools/search-reddit.js.map +1 -0
- package/dist/tools/search-seo.d.ts +37 -0
- package/dist/tools/search-seo.d.ts.map +1 -0
- package/dist/tools/search-seo.js +49 -0
- package/dist/tools/search-seo.js.map +1 -0
- package/dist/tools/search-web.d.ts +23 -0
- package/dist/tools/search-web.d.ts.map +1 -0
- package/dist/tools/search-web.js +20 -0
- package/dist/tools/search-web.js.map +1 -0
- package/dist/tools/verify-email.d.ts +20 -0
- package/dist/tools/verify-email.d.ts.map +1 -0
- package/dist/tools/verify-email.js +15 -0
- package/dist/tools/verify-email.js.map +1 -0
- package/package.json +28 -0
- package/src/client.ts +60 -0
- package/src/executor.ts +182 -0
- package/src/index.ts +155 -0
- package/src/registry.ts +3159 -0
- package/src/tools/enrich-company.ts +25 -0
- package/src/tools/enrich-person.ts +27 -0
- package/src/tools/fetch-page-content.ts +36 -0
- package/src/tools/find-email.ts +23 -0
- package/src/tools/find-emails.ts +190 -0
- package/src/tools/find-influencers.ts +34 -0
- package/src/tools/find-people.ts +69 -0
- package/src/tools/find-phone.ts +53 -0
- package/src/tools/find-signals.ts +93 -0
- package/src/tools/search-ads.ts +44 -0
- package/src/tools/search-companies.ts +41 -0
- package/src/tools/search-jobs.ts +73 -0
- package/src/tools/search-places.ts +52 -0
- package/src/tools/search-reddit.ts +34 -0
- package/src/tools/search-seo.ts +59 -0
- package/src/tools/search-web.ts +24 -0
- package/src/tools/verify-email.ts +19 -0
- package/test-ads-live.ts +77 -0
- package/test-company-live.ts +91 -0
- package/test-email-live.ts +171 -0
- package/test-influencers-live.ts +66 -0
- package/test-jobs-live.ts +69 -0
- package/test-linkupapi-live.ts +137 -0
- package/test-phone-live.ts +41 -0
- package/test-places-live.ts +89 -0
- package/test-reddit-live.ts +66 -0
- package/test-search-live.ts +79 -0
- package/test-seo-live.ts +68 -0
- package/test-web-live.ts +67 -0
- package/tests/client.test.ts +90 -0
- package/tests/executor.test.ts +83 -0
- package/tests/gtm/01-icp-to-emails.test.ts +43 -0
- package/tests/gtm/02-icp-bulk-emails.test.ts +38 -0
- package/tests/gtm/03-icp-to-phones.test.ts +39 -0
- package/tests/gtm/04-funding-signal-outreach.test.ts +42 -0
- package/tests/gtm/05-hiring-signal-decisionmakers.test.ts +41 -0
- package/tests/gtm/06-intent-signal-outreach.test.ts +44 -0
- package/tests/gtm/07-places-to-content.test.ts +50 -0
- package/tests/gtm/08-domain-to-account.test.ts +44 -0
- package/tests/gtm/09-linkedin-to-everything.test.ts +41 -0
- package/tests/gtm/10-jobs-vs-signals-routing.test.ts +38 -0
- package/tests/gtm/11-find-vs-enrich-routing.test.ts +39 -0
- package/tests/gtm/12-bogus-domain-graceful.test.ts +42 -0
- package/tests/gtm/13-private-linkedin-graceful.test.ts +44 -0
- package/tests/gtm/14-empty-handoff.test.ts +43 -0
- package/tests/gtm/15-seo-reddit-research.test.ts +38 -0
- package/tests/gtm/README.md +59 -0
- package/tests/gtm/harness.ts +217 -0
- package/tests/gtm/tools-bridge.ts +232 -0
- package/tests/gtm-scenarios.md +32 -0
- package/tests/live/smoke-report.ts +255 -0
- package/tests/live/smoke.test.ts +134 -0
- package/tests/registry-enrich-person.test.ts +447 -0
- package/tests/registry-fetch-page-content.test.ts +90 -0
- package/tests/registry-find-people.test.ts +467 -0
- package/tests/registry-find-signals.test.ts +470 -0
- package/tests/registry-linkupapi.test.ts +331 -0
- package/tests/registry-search-companies.test.ts +188 -0
- package/tests/registry-search-jobs.test.ts +116 -0
- package/tests/registry.test.ts +2210 -0
- package/tests/tools/enrich-company.test.ts +92 -0
- package/tests/tools/enrich-email.test.ts +94 -0
- package/tests/tools/enrich-emails.test.ts +271 -0
- package/tests/tools/enrich-person.test.ts +140 -0
- package/tests/tools/fetch-page-content.test.ts +108 -0
- package/tests/tools/find-influencers.test.ts +91 -0
- package/tests/tools/find-people.test.ts +344 -0
- package/tests/tools/find-phone.test.ts +100 -0
- package/tests/tools/find-signals.test.ts +110 -0
- package/tests/tools/search-ads.test.ts +182 -0
- package/tests/tools/search-companies.test.ts +58 -0
- package/tests/tools/search-jobs.test.ts +210 -0
- package/tests/tools/search-places.test.ts +114 -0
- package/tests/tools/search-reddit.test.ts +125 -0
- package/tests/tools/search-seo.test.ts +183 -0
- package/tests/tools/search-web.test.ts +79 -0
- package/tests/tools/verify-email.test.ts +68 -0
- package/tsconfig.json +17 -0
- package/vitest.config.ts +7 -0
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { getProviders } from '../src/registry.js'
|
|
3
|
+
|
|
4
|
+
const providers = () => getProviders('find_people')
|
|
5
|
+
const get = (id: string) => {
|
|
6
|
+
const p = providers().find((p) => p.id === id)
|
|
7
|
+
if (!p) throw new Error(`Provider '${id}' not found in find_people`)
|
|
8
|
+
return p
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// sumble-people-find
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
|
|
15
|
+
describe('sumble-people-find', () => {
|
|
16
|
+
const p = () => get('sumble-people-find')
|
|
17
|
+
|
|
18
|
+
it('isApplicable: true with company_domains + job_titles', () => {
|
|
19
|
+
expect(p().isApplicable!({ company_domains: ['coldiq.com'], job_titles: ['CEO'] })).toBe(true)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('isApplicable: true with company_linkedin_urls + seniorities', () => {
|
|
23
|
+
expect(p().isApplicable!({ company_linkedin_urls: ['https://www.linkedin.com/company/coldiq'], seniorities: ['C-Level'] })).toBe(true)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('isApplicable: false without company identifier', () => {
|
|
27
|
+
expect(p().isApplicable!({ job_titles: ['CEO'] })).toBe(false)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('isApplicable: false without job_titles or seniorities', () => {
|
|
31
|
+
expect(p().isApplicable!({ company_domains: ['coldiq.com'] })).toBe(false)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('mapParams uses domain when company_domains present', () => {
|
|
35
|
+
const result = p().mapParams({ company_domains: ['coldiq.com'], job_titles: ['CEO'], limit: 5 })
|
|
36
|
+
expect(result).toEqual({
|
|
37
|
+
body: {
|
|
38
|
+
organization: { domain: 'coldiq.com' },
|
|
39
|
+
filters: {
|
|
40
|
+
job_functions: ['CEO'],
|
|
41
|
+
job_levels: undefined,
|
|
42
|
+
countries: undefined,
|
|
43
|
+
query: undefined,
|
|
44
|
+
},
|
|
45
|
+
limit: 5,
|
|
46
|
+
},
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('mapParams falls back to linkedin_url when no domain', () => {
|
|
51
|
+
const result = p().mapParams({
|
|
52
|
+
company_linkedin_urls: ['https://www.linkedin.com/company/coldiq'],
|
|
53
|
+
seniorities: ['Director'],
|
|
54
|
+
})
|
|
55
|
+
const body = result.body as Record<string, unknown>
|
|
56
|
+
expect((body.organization as Record<string, unknown>).linkedin_url).toBe('https://www.linkedin.com/company/coldiq')
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('mapParams joins keywords into query string', () => {
|
|
60
|
+
const result = p().mapParams({
|
|
61
|
+
company_domains: ['coldiq.com'],
|
|
62
|
+
job_titles: ['CEO'],
|
|
63
|
+
keywords: ['sales', 'outbound'],
|
|
64
|
+
})
|
|
65
|
+
const body = result.body as Record<string, unknown>
|
|
66
|
+
expect((body.filters as Record<string, unknown>).query).toBe('sales outbound')
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it('hasResult: true when people array non-empty', () => {
|
|
70
|
+
expect(p().hasResult({ people: [{ name: 'Michel Lieben' }] })).toBe(true)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('hasResult: false when people is empty', () => {
|
|
74
|
+
expect(p().hasResult({ people: [] })).toBe(false)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('hasResult: false on empty object', () => {
|
|
78
|
+
expect(p().hasResult({})).toBe(false)
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
it('priority is 6', () => {
|
|
82
|
+
expect(p().priority).toBe(6)
|
|
83
|
+
})
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
// prospeo-search-person
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
|
|
90
|
+
describe('prospeo-search-person', () => {
|
|
91
|
+
const p = () => get('prospeo-search-person')
|
|
92
|
+
|
|
93
|
+
it('isApplicable: true with job_titles', () => {
|
|
94
|
+
expect(p().isApplicable!({ job_titles: ['CEO'] })).toBe(true)
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
it('isApplicable: true with company_domains', () => {
|
|
98
|
+
expect(p().isApplicable!({ company_domains: ['coldiq.com'] })).toBe(true)
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
it('isApplicable: false with only keywords (Prospeo does not support keyword search)', () => {
|
|
102
|
+
expect(p().isApplicable!({ keywords: ['outbound sales'] })).toBe(false)
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('isApplicable: false with only locations', () => {
|
|
106
|
+
expect(p().isApplicable!({ locations: ['Belgium'] })).toBe(false)
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('mapParams builds Prospeo filter shape', () => {
|
|
110
|
+
expect(p().mapParams({
|
|
111
|
+
job_titles: ['CEO', 'CTO'],
|
|
112
|
+
seniorities: ['C-Level'],
|
|
113
|
+
company_domains: ['coldiq.com'],
|
|
114
|
+
locations: ['Belgium'],
|
|
115
|
+
})).toEqual({
|
|
116
|
+
body: {
|
|
117
|
+
filters: {
|
|
118
|
+
job_title: { include: ['CEO', 'CTO'] },
|
|
119
|
+
person_seniority: { include: ['C-Level'] },
|
|
120
|
+
company: { websites: { include: ['coldiq.com'] } },
|
|
121
|
+
person_location: { include: ['Belgium'] },
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
})
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
it('mapParams omits absent filter keys', () => {
|
|
128
|
+
const result = p().mapParams({ job_titles: ['CEO'] })
|
|
129
|
+
const filters = (result.body as Record<string, unknown>).filters as Record<string, unknown>
|
|
130
|
+
expect(filters.person_seniority).toBeUndefined()
|
|
131
|
+
expect(filters.company).toBeUndefined()
|
|
132
|
+
expect(filters.person_location).toBeUndefined()
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('hasResult: true when data array non-empty', () => {
|
|
136
|
+
expect(p().hasResult({ data: [{ name: 'Michel Lieben' }] })).toBe(true)
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
it('hasResult: false when data is empty', () => {
|
|
140
|
+
expect(p().hasResult({ data: [] })).toBe(false)
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
it('hasResult: false on empty object', () => {
|
|
144
|
+
expect(p().hasResult({})).toBe(false)
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
it('priority is 7', () => {
|
|
148
|
+
expect(p().priority).toBe(7)
|
|
149
|
+
})
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
// ---------------------------------------------------------------------------
|
|
153
|
+
// ai-ark-people
|
|
154
|
+
// ---------------------------------------------------------------------------
|
|
155
|
+
|
|
156
|
+
describe('ai-ark-people', () => {
|
|
157
|
+
const p = () => get('ai-ark-people')
|
|
158
|
+
|
|
159
|
+
it('isApplicable: true with job_titles', () => {
|
|
160
|
+
expect(p().isApplicable!({ job_titles: ['VP Sales'] })).toBe(true)
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
it('isApplicable: true with seniorities', () => {
|
|
164
|
+
expect(p().isApplicable!({ seniorities: ['Director'] })).toBe(true)
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
it('isApplicable: true with keywords', () => {
|
|
168
|
+
expect(p().isApplicable!({ keywords: ['cold email'] })).toBe(true)
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
it('isApplicable: false with only company_domains', () => {
|
|
172
|
+
expect(p().isApplicable!({ company_domains: ['coldiq.com'] })).toBe(false)
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
it('mapParams builds AI-Ark contact shape', () => {
|
|
176
|
+
expect(p().mapParams({
|
|
177
|
+
job_titles: ['VP Sales'],
|
|
178
|
+
seniorities: ['Director'],
|
|
179
|
+
locations: ['Belgium'],
|
|
180
|
+
keywords: ['outbound'],
|
|
181
|
+
limit: 20,
|
|
182
|
+
})).toEqual({
|
|
183
|
+
body: {
|
|
184
|
+
contact: {
|
|
185
|
+
departmentAndFunction: { any: { include: ['VP Sales'] } },
|
|
186
|
+
seniority: { any: { include: ['Director'] } },
|
|
187
|
+
location: { any: { include: ['Belgium'] } },
|
|
188
|
+
keywordsInProfile: { any: { include: ['outbound'] } },
|
|
189
|
+
},
|
|
190
|
+
size: 20,
|
|
191
|
+
},
|
|
192
|
+
})
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
it('mapParams omits absent contact keys', () => {
|
|
196
|
+
const result = p().mapParams({ job_titles: ['CEO'] })
|
|
197
|
+
const contact = (result.body as Record<string, unknown>).contact as Record<string, unknown>
|
|
198
|
+
expect(contact.seniority).toBeUndefined()
|
|
199
|
+
expect(contact.location).toBeUndefined()
|
|
200
|
+
expect(contact.keywordsInProfile).toBeUndefined()
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
it('mapParams caps size at 100', () => {
|
|
204
|
+
const result = p().mapParams({ job_titles: ['CEO'], limit: 999 })
|
|
205
|
+
expect((result.body as Record<string, unknown>).size).toBe(100)
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
it('hasResult: true when content array non-empty', () => {
|
|
209
|
+
expect(p().hasResult({ content: [{ name: 'Michel Lieben' }] })).toBe(true)
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
it('hasResult: false when content is empty', () => {
|
|
213
|
+
expect(p().hasResult({ content: [] })).toBe(false)
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
it('hasResult: false on empty object', () => {
|
|
217
|
+
expect(p().hasResult({})).toBe(false)
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
it('priority is 8', () => {
|
|
221
|
+
expect(p().priority).toBe(8)
|
|
222
|
+
})
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
// ---------------------------------------------------------------------------
|
|
226
|
+
// fullenrich-people-search
|
|
227
|
+
// ---------------------------------------------------------------------------
|
|
228
|
+
|
|
229
|
+
describe('fullenrich-people-search', () => {
|
|
230
|
+
const p = () => get('fullenrich-people-search')
|
|
231
|
+
|
|
232
|
+
it('has no isApplicable gate (always runs)', () => {
|
|
233
|
+
expect(p().isApplicable).toBeUndefined()
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
it('mapParams wraps all fields in {value} objects', () => {
|
|
237
|
+
expect(p().mapParams({
|
|
238
|
+
company_domains: ['coldiq.com'],
|
|
239
|
+
job_titles: ['CEO'],
|
|
240
|
+
seniorities: ['C-Level'],
|
|
241
|
+
locations: ['Belgium'],
|
|
242
|
+
limit: 10,
|
|
243
|
+
})).toEqual({
|
|
244
|
+
body: {
|
|
245
|
+
current_company_domains: [{ value: 'coldiq.com' }],
|
|
246
|
+
current_position_titles: [{ value: 'CEO' }],
|
|
247
|
+
current_position_seniority_level: [{ value: 'C-Level' }],
|
|
248
|
+
person_locations: [{ value: 'Belgium' }],
|
|
249
|
+
limit: 10,
|
|
250
|
+
},
|
|
251
|
+
})
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
it('mapParams omits undefined fields', () => {
|
|
255
|
+
const result = p().mapParams({ job_titles: ['CEO'], limit: 5 })
|
|
256
|
+
const body = result.body as Record<string, unknown>
|
|
257
|
+
expect(body.current_company_domains).toBeUndefined()
|
|
258
|
+
expect(body.current_position_seniority_level).toBeUndefined()
|
|
259
|
+
expect(body.person_locations).toBeUndefined()
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
it('mapParams caps limit at 100', () => {
|
|
263
|
+
const result = p().mapParams({ limit: 500 })
|
|
264
|
+
expect((result.body as Record<string, unknown>).limit).toBe(100)
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
it('hasResult: true when people array non-empty', () => {
|
|
268
|
+
expect(p().hasResult({ people: [{ name: 'Michel Lieben' }] })).toBe(true)
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
it('hasResult: false when people is empty', () => {
|
|
272
|
+
expect(p().hasResult({ people: [] })).toBe(false)
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
it('hasResult: false on empty object', () => {
|
|
276
|
+
expect(p().hasResult({})).toBe(false)
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
it('priority is 9', () => {
|
|
280
|
+
expect(p().priority).toBe(9)
|
|
281
|
+
})
|
|
282
|
+
})
|
|
283
|
+
|
|
284
|
+
// ---------------------------------------------------------------------------
|
|
285
|
+
// findymail-search-employees
|
|
286
|
+
// ---------------------------------------------------------------------------
|
|
287
|
+
|
|
288
|
+
describe('findymail-search-employees', () => {
|
|
289
|
+
const p = () => get('findymail-search-employees')
|
|
290
|
+
|
|
291
|
+
it('isApplicable: true with company_domains + job_titles', () => {
|
|
292
|
+
expect(p().isApplicable!({ company_domains: ['coldiq.com'], job_titles: ['CEO'] })).toBe(true)
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
it('isApplicable: false without company_domains', () => {
|
|
296
|
+
expect(p().isApplicable!({ job_titles: ['CEO'] })).toBe(false)
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
it('isApplicable: false without job_titles', () => {
|
|
300
|
+
expect(p().isApplicable!({ company_domains: ['coldiq.com'] })).toBe(false)
|
|
301
|
+
})
|
|
302
|
+
|
|
303
|
+
it('mapParams uses first domain and caps count at 5', () => {
|
|
304
|
+
expect(p().mapParams({
|
|
305
|
+
company_domains: ['coldiq.com', 'microsoft.com'],
|
|
306
|
+
job_titles: ['CEO', 'CTO'],
|
|
307
|
+
limit: 3,
|
|
308
|
+
})).toEqual({
|
|
309
|
+
body: {
|
|
310
|
+
website: 'coldiq.com',
|
|
311
|
+
job_titles: ['CEO', 'CTO'],
|
|
312
|
+
count: 3,
|
|
313
|
+
},
|
|
314
|
+
})
|
|
315
|
+
})
|
|
316
|
+
|
|
317
|
+
it('mapParams hard-caps count at 5 even when limit > 5', () => {
|
|
318
|
+
const result = p().mapParams({ company_domains: ['coldiq.com'], job_titles: ['CEO'], limit: 100 })
|
|
319
|
+
expect((result.body as Record<string, unknown>).count).toBe(5)
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
it('hasResult: true when contacts key present', () => {
|
|
323
|
+
expect(p().hasResult({ contacts: [{ email: 'michel@coldiq.com' }] })).toBe(true)
|
|
324
|
+
})
|
|
325
|
+
|
|
326
|
+
it('hasResult: true when results key present', () => {
|
|
327
|
+
expect(p().hasResult({ results: [{ name: 'Michel Lieben' }] })).toBe(true)
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
it('hasResult: true when data is a non-empty array', () => {
|
|
331
|
+
expect(p().hasResult([{ email: 'michel@coldiq.com' }])).toBe(true)
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
it('hasResult: false when data is empty array', () => {
|
|
335
|
+
expect(p().hasResult([])).toBe(false)
|
|
336
|
+
})
|
|
337
|
+
|
|
338
|
+
it('hasResult: false on empty object', () => {
|
|
339
|
+
expect(p().hasResult({})).toBe(false)
|
|
340
|
+
})
|
|
341
|
+
|
|
342
|
+
it('priority is 10', () => {
|
|
343
|
+
expect(p().priority).toBe(10)
|
|
344
|
+
})
|
|
345
|
+
})
|
|
346
|
+
|
|
347
|
+
// ---------------------------------------------------------------------------
|
|
348
|
+
// apollo — keywords array is joined into a single string for q_keywords
|
|
349
|
+
// ---------------------------------------------------------------------------
|
|
350
|
+
|
|
351
|
+
describe('apollo (find_people)', () => {
|
|
352
|
+
const p = () => get('apollo')
|
|
353
|
+
|
|
354
|
+
it('mapParams joins keywords array into space-separated string', () => {
|
|
355
|
+
const result = p().mapParams({ keywords: ['growth', 'AI'], job_titles: ['CEO'], limit: 10 })
|
|
356
|
+
const body = result.body as Record<string, unknown>
|
|
357
|
+
expect(body.q_keywords).toBe('growth AI')
|
|
358
|
+
})
|
|
359
|
+
|
|
360
|
+
it('mapParams omits q_keywords when keywords absent', () => {
|
|
361
|
+
const result = p().mapParams({ job_titles: ['CEO'], limit: 10 })
|
|
362
|
+
const body = result.body as Record<string, unknown>
|
|
363
|
+
expect(body.q_keywords).toBeUndefined()
|
|
364
|
+
})
|
|
365
|
+
})
|
|
366
|
+
|
|
367
|
+
// ---------------------------------------------------------------------------
|
|
368
|
+
// linkupapi-search-profiles — keywords array is joined for keyword field
|
|
369
|
+
// ---------------------------------------------------------------------------
|
|
370
|
+
|
|
371
|
+
describe('linkupapi-search-profiles (find_people)', () => {
|
|
372
|
+
const p = () => get('linkupapi-search-profiles')
|
|
373
|
+
|
|
374
|
+
it('mapParams joins keywords array into space-separated string', () => {
|
|
375
|
+
const result = p().mapParams({ keywords: ['sales', 'B2B'], job_titles: ['AE'], limit: 10 })
|
|
376
|
+
const body = result.body as Record<string, unknown>
|
|
377
|
+
expect(body.keyword).toBe('sales B2B')
|
|
378
|
+
})
|
|
379
|
+
|
|
380
|
+
it('mapParams omits keyword when keywords absent', () => {
|
|
381
|
+
const result = p().mapParams({ job_titles: ['CEO'], limit: 5 })
|
|
382
|
+
const body = result.body as Record<string, unknown>
|
|
383
|
+
expect(body.keyword).toBeUndefined()
|
|
384
|
+
})
|
|
385
|
+
})
|
|
386
|
+
|
|
387
|
+
// ---------------------------------------------------------------------------
|
|
388
|
+
// LeadsFactory
|
|
389
|
+
// ---------------------------------------------------------------------------
|
|
390
|
+
|
|
391
|
+
describe('leadsfactory (find_people)', () => {
|
|
392
|
+
const p = () => get('leadsfactory')
|
|
393
|
+
const personas = (result: ReturnType<ReturnType<typeof p>['mapParams']>) =>
|
|
394
|
+
((result.body as Record<string, unknown>).search as Record<string, unknown>).personas as { job_title: string }[]
|
|
395
|
+
|
|
396
|
+
it('same-domain variants collapse to 1 persona with comma-joined titles', () => {
|
|
397
|
+
const result = p().mapParams({ company_domains: ['coldiq.com'], job_titles: ['VP Sales', 'Head of Sales'], limit: 5 })
|
|
398
|
+
expect(personas(result)).toEqual([{ job_title: 'VP Sales, Head of Sales' }])
|
|
399
|
+
})
|
|
400
|
+
|
|
401
|
+
it('"of" variant is deduped before domain grouping — VP Sales + VP of Sales + Head of Sales → 1 persona', () => {
|
|
402
|
+
const result = p().mapParams({
|
|
403
|
+
company_domains: ['coldiq.com'],
|
|
404
|
+
job_titles: ['VP Sales', 'VP of Sales', 'Head of Sales'],
|
|
405
|
+
limit: 5,
|
|
406
|
+
})
|
|
407
|
+
expect(personas(result)).toEqual([{ job_title: 'VP Sales, Head of Sales' }])
|
|
408
|
+
})
|
|
409
|
+
|
|
410
|
+
it('different-domain roles stay as separate personas', () => {
|
|
411
|
+
const result = p().mapParams({ company_domains: ['coldiq.com'], job_titles: ['CEO', 'VP of Sales'], limit: 5 })
|
|
412
|
+
expect(personas(result)).toEqual([{ job_title: 'CEO' }, { job_title: 'VP of Sales' }])
|
|
413
|
+
})
|
|
414
|
+
|
|
415
|
+
it('C-suite abbreviations are treated as distinct roles', () => {
|
|
416
|
+
const result = p().mapParams({ company_domains: ['coldiq.com'], job_titles: ['CEO', 'CRO'], limit: 5 })
|
|
417
|
+
expect(personas(result)).toEqual([{ job_title: 'CEO' }, { job_title: 'CRO' }])
|
|
418
|
+
})
|
|
419
|
+
|
|
420
|
+
it('case-insensitive "of" duplicates are deduped, first casing is kept', () => {
|
|
421
|
+
const result = p().mapParams({ company_domains: ['coldiq.com'], job_titles: ['VP Sales', 'vp sales'], limit: 3 })
|
|
422
|
+
expect(personas(result)).toEqual([{ job_title: 'VP Sales' }])
|
|
423
|
+
})
|
|
424
|
+
|
|
425
|
+
it('no job_titles → Decision Maker fallback', () => {
|
|
426
|
+
const result = p().mapParams({ company_domains: ['coldiq.com'], limit: 3 })
|
|
427
|
+
expect((result.body as Record<string, unknown>).search).toEqual({
|
|
428
|
+
max_persona_results: 3,
|
|
429
|
+
personas: [{ job_title: 'Decision Maker' }],
|
|
430
|
+
})
|
|
431
|
+
})
|
|
432
|
+
|
|
433
|
+
it('seniorities and keywords are not forwarded', () => {
|
|
434
|
+
const result = p().mapParams({
|
|
435
|
+
company_domains: ['coldiq.com'],
|
|
436
|
+
job_titles: ['CEO'],
|
|
437
|
+
seniorities: ['C-suite'],
|
|
438
|
+
keywords: ['AI'],
|
|
439
|
+
limit: 5,
|
|
440
|
+
})
|
|
441
|
+
const body = result.body as Record<string, unknown>
|
|
442
|
+
expect(body.seniorities).toBeUndefined()
|
|
443
|
+
expect(body.keywords).toBeUndefined()
|
|
444
|
+
})
|
|
445
|
+
})
|
|
446
|
+
|
|
447
|
+
// ---------------------------------------------------------------------------
|
|
448
|
+
// Ordering: find_people providers sorted correctly
|
|
449
|
+
// ---------------------------------------------------------------------------
|
|
450
|
+
|
|
451
|
+
describe('find_people priority ordering', () => {
|
|
452
|
+
it('all providers are sorted by priority', () => {
|
|
453
|
+
const ps = providers()
|
|
454
|
+
for (let i = 1; i < ps.length; i++) {
|
|
455
|
+
expect(ps[i].priority).toBeGreaterThanOrEqual(ps[i - 1].priority)
|
|
456
|
+
}
|
|
457
|
+
})
|
|
458
|
+
|
|
459
|
+
it('leadsfactory is first (priority 1)', () => {
|
|
460
|
+
expect(providers()[0].id).toBe('leadsfactory')
|
|
461
|
+
})
|
|
462
|
+
|
|
463
|
+
it('findymail-search-employees is last in the wired set (priority 10)', () => {
|
|
464
|
+
const ps = providers()
|
|
465
|
+
expect(ps[ps.length - 1].id).toBe('findymail-search-employees')
|
|
466
|
+
})
|
|
467
|
+
})
|