Skip to content

Mock an API by name #7

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,6 @@ dist
.tern-port

.DS_Store

scripts/elasticsearch
scripts/elasticsearch.tar.gz
72 changes: 72 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Logs
logs
*.log
npm-debug.log*

# Runtime data
pids
*.pid
*.seed

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# coverage output
coverage.lcov

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules
jspm_packages

# Optional npm cache directory
.npm

# Optional REPL history
.node_repl_history

# mac files
.DS_Store

# vim swap files
*.swp

package-lock.json

# elasticsearch repo or binary files
elasticsearch*

# Generated typings, we don't commit them
# because we should copy them in the main .d.ts file
api/generated.d.ts

# Ignore doc folder
docs

# Ignore test folder
test

# Ignore scripts folder
scripts

# ci configuration
.ci
.travis.yml
certs
.github
CODE_OF_CONDUCT.md
CONTRIBUTING.md
42 changes: 38 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,33 @@ mock.add({
method: ['GET', 'POST'],
path: ['/_search', '/:index/_search']
}, () => {
return { status: 'ok' }
return {
hits: {
total: { value: 0, relation: 'eq' },
hits: []
}
}
})
```

Finally, you can specify the API to mock with the `api` key, but be aware that if you specify the `api` instead of
`path` and `method`, you will not be able to differentiate between dynamic paths and API with multiple methods,
unless you use the parameters provided in the resolver function.

**Note:** When using this mock pattern, only Elasticsearch v7 is supported.

```js
// This mock will catch every search request against any index
mock.add({
api: 'search'
}, params => {
console.log(params)
return {
hits: {
total: { value: 0, relation: 'eq' },
hits: []
}
}
})
```

Expand Down Expand Up @@ -105,12 +131,20 @@ const client = new Client({

A pattern is an object that describes an http query to Elasticsearch, and it looks like this:
```ts
interface MockPattern {
method: string
path: string
interface MockPatternHTTP {
method: string | string[]
path: string | string[]
querystring?: Record<string, string>
body?: Record<string, any>
}

interface MockPatternAPI {
api: string
querystring?: Record<string, string>
body?: Record<string, any>
}

type MockPattern = MockPatternHTTP | MockPatternAPI
```

The more field you specify, the more the mock will be strict, for example:
Expand Down
16 changes: 12 additions & 4 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,25 @@ import { Connection } from '@elastic/elasticsearch'
declare class ClientMock {
constructor()
add(pattern: MockPattern, resolver: ResolverFn): ClientMock
get(pattern: MockPattern): ResolverFn | null
get(pattern: MockPatternHTTP): ResolverFn | null
getConnection(): typeof Connection
}

export declare type ResolverFn = (params: MockPattern) => Record<string, any> | string
export declare type ResolverFn = (params: MockPatternHTTP) => Record<string, any> | string

export interface MockPattern {
export interface MockPatternHTTP {
method: string | string[]
path: string | string[]
querystring?: Record<string, string>
body?: Record<string, any> | Record<string, any>[]
body?: Record<string, any>
}

export interface MockPatternAPI {
api: string
querystring?: Record<string, string>
body?: Record<string, any>
}

export type MockPattern = MockPatternHTTP | MockPatternAPI

export default ClientMock
11 changes: 11 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ const { Connection, errors } = require('@elastic/elasticsearch')
const Router = require('find-my-way')
const intoStream = require('into-stream')
const equal = require('fast-deep-equal')

const kRouter = Symbol('elasticsearch-mock-router')
const pathsDb = require('./paths.json')

/* istanbul ignore next */
const noop = () => {}
Expand All @@ -28,6 +30,15 @@ class Mocker {
}

add (pattern, fn) {
if (pattern.api) {
const api = pathsDb[pattern.api]
if (!api) throw new ConfigurationError(`The api '${pattern.api}' does not exist`)
const apiPattern = { path: api.path, method: api.method }
if (pattern.body) apiPattern.body = pattern.body
if (pattern.querystring) apiPattern.querystring = pattern.querystring
return this.add(apiPattern, fn)
}

for (const key of ['method', 'path']) {
if (Array.isArray(pattern[key])) {
for (const value of pattern[key]) {
Expand Down
37 changes: 30 additions & 7 deletions index.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import { expectType, expectError } from 'tsd'
import { Client } from '@elastic/elasticsearch'
import Mock, { MockPattern } from './'
import Mock, { MockPatternHTTP } from './'

const mock = new Mock()
const client = new Client({
Expand All @@ -16,15 +16,15 @@ mock.add({
method: 'GET',
path: '/'
}, params => {
expectType<MockPattern>(params)
expectType<MockPatternHTTP>(params)
return { status: 'ok' }
})

mock.add({
method: ['GET', 'POST'],
path: ['/_search', '/:index/_search']
}, params => {
expectType<MockPattern>(params)
expectType<MockPatternHTTP>(params)
return { status: 'ok' }
})

Expand All @@ -33,7 +33,7 @@ mock.add({
path: '/',
querystring: { pretty: 'true' }
}, params => {
expectType<MockPattern>(params)
expectType<MockPatternHTTP>(params)
return { status: 'ok' }
})

Expand All @@ -43,7 +43,7 @@ mock.add({
querystring: { pretty: 'true' },
body: { foo: 'bar' }
}, params => {
expectType<MockPattern>(params)
expectType<MockPatternHTTP>(params)
return { status: 'ok' }
})

Expand All @@ -52,15 +52,38 @@ mock.add({
path: '/_bulk',
body: [{ foo: 'bar' }]
}, params => {
expectType<MockPattern>(params)
expectType<MockPatternHTTP>(params)
return { status: 'ok' }
})

mock.add({
method: 'GET',
path: '/'
}, params => {
expectType<MockPattern>(params)
expectType<MockPatternHTTP>(params)
return 'ok'
})

mock.add({
api: 'info'
}, params => {
expectType<MockPatternHTTP>(params)
return 'ok'
})

mock.add({
api: 'info',
querystring: { pretty: 'true' }
}, params => {
expectType<MockPatternHTTP>(params)
return 'ok'
})

mock.add({
api: 'search',
body: { query: { match: { foo: 'bar' } } }
}, params => {
expectType<MockPatternHTTP>(params)
return 'ok'
})

Expand Down
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,12 @@
"devDependencies": {
"@elastic/elasticsearch": "^7.7.0-rc.2",
"ava": "^3.6.0",
"gunzip-maybe": "^1.4.1",
"minimist": "^1.2.5",
"nyc": "^15.0.1",
"simple-get": "^3.1.0",
"standard": "^14.3.3",
"tar": "^6.0.1",
"tsd": "^0.11.0"
},
"dependencies": {
Expand Down
1 change: 1 addition & 0 deletions paths.json

Large diffs are not rendered by default.

Loading