@pbvision/fastify-firestore-service 0.0.16 → 0.0.18

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/README.md CHANGED
@@ -14,7 +14,6 @@ provides a way to define APIs, including managing data.
14
14
  - [Components](#components)
15
15
  - [Customizing Component Registration](#customizing-component-registration)
16
16
  - [Unit testing](#unit-testing)
17
- - [Setup](#setup)
18
17
  - [Generating SDKs](#generating-sdks)
19
18
  - [Swagger UI](#swagger-ui)
20
19
  - [OpenAPI SDKs](#openapi-sdks)
@@ -175,10 +174,6 @@ workflow with custom components. For example, to add a new type of component
175
174
  `makeService({ components: { ExampleComponent } })`
176
175
 
177
176
  # Unit testing
178
- ## Setup
179
- Apps store data in Firestore. You must start the Firestore emulator before
180
- running tests. You can run `yarn start-local-db` to start the local emulator.
181
-
182
177
  # Generating SDKs
183
178
  ## Swagger UI
184
179
  This library generates an interactive Swagger UI for all APIs at /[service]/docs.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pbvision/fastify-firestore-service",
3
- "version": "0.0.16",
3
+ "version": "0.0.18",
4
4
  "description": "Web Framework using Fastify and Firestore ORM",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -11,9 +11,10 @@
11
11
  "scripts": {
12
12
  "build-doc": "./docs/build.sh",
13
13
  "debug": "./node_modules/nodemon/bin/nodemon.js --no-lazy --legacy-watch --watch ./src --watch ./examples --watch ./test --inspect=9229 ./node_modules/jest/bin/jest.js --coverage --config=./jest.config.json --runInBand",
14
- "test": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js --config=./jest.config.json --coverage",
15
14
  "setup": "yarn install --frozen-lockfile && pip install -r requirements.txt",
16
- "start-local-db": "./node_modules/@pbvision/firestore-orm/scripts/start-local-db.sh"
15
+ "start-local-db": "./node_modules/@pbvision/firestore-orm/scripts/start-local-db.sh",
16
+ "test": "yarn -s start-local-db && yarn -s test-without-starting-db",
17
+ "test-without-starting-db": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js --config=./jest.config.json --coverage"
17
18
  },
18
19
  "contributors": [
19
20
  "David Underhill",
@@ -41,7 +42,7 @@
41
42
  "@fastify/static": "^6.12",
42
43
  "@fastify/swagger": "^8.1.0",
43
44
  "@fastify/swagger-ui": "^2.0.1",
44
- "@pbvision/firestore-orm": "^0.0.6",
45
+ "@pbvision/firestore-orm": "^0.0.7",
45
46
  "@pbvision/schema": "^0.2.15",
46
47
  "@sentry/node": "^7.91.0",
47
48
  "fastify": "^4.10.0",
@@ -3,23 +3,24 @@ import zlib from 'node:zlib'
3
3
 
4
4
  import realFetch from 'node-fetch'
5
5
 
6
- async function fetchWrapper (options, mockedFetch) {
7
- const { compress = true, method = 'POST', url, qsParams, json } = options
8
- let { body, headers } = options
6
+ async function fetchWrapper (request, mockedFetch) {
7
+ const { compress = true, method = 'POST', url, qsParams, json } = request
8
+ let { body, headers } = request
9
9
  headers = { ...headers } // copy the headers before we change them
10
10
 
11
11
  // compress the body using brotli
12
12
  if (json) {
13
13
  headers['content-type'] = 'application/json'
14
- body = JSON.stringify(options.json)
14
+ body = JSON.stringify(request.json)
15
15
  }
16
16
  if (body && compress) {
17
17
  body = zlib.brotliCompressSync(body)
18
18
  headers['content-encoding'] = 'br'
19
19
  }
20
20
 
21
- // istanbul ignore next
22
- const fetch = mockedFetch ?? fetchWrapper.__mock ?? realFetch
21
+ const fetch = (mockedFetch === false)
22
+ ? realFetch
23
+ : (mockedFetch ?? fetchWrapper.__mock ?? realFetch)
23
24
 
24
25
  // compute the full URL including search params
25
26
  let fullURL = url
@@ -29,7 +30,8 @@ async function fetchWrapper (options, mockedFetch) {
29
30
  fullURL += `?${qsStr}`
30
31
  }
31
32
  }
32
- return fetch(fullURL, { body, headers, method, compress: false })
33
+ const options = { body, headers, method, compress: false }
34
+ return fetch(fullURL, options)
33
35
  }
34
36
 
35
37
  export default fetchWrapper
package/test/base-test.js CHANGED
@@ -1,3 +1,4 @@
1
+ import querystring from 'node:querystring'
1
2
  import zlib from 'node:zlib'
2
3
 
3
4
  import { jest } from '@jest/globals'
@@ -24,6 +25,15 @@ class BaseAppTest extends BaseTest {
24
25
  return func
25
26
  }
26
27
 
28
+ beforeEach () {
29
+ this.fetchMock = fetchWrapper.__mock
30
+ fetchWrapper.__mock.mockClear()
31
+
32
+ // default response is a weird error to help make it obvious the unit test
33
+ // author forgot to provide a custom mock value
34
+ this.fetchMock.mockResp('custom mock value not set', 555)
35
+ }
36
+
27
37
  async beforeAll () {
28
38
  const makeService = await this.getMakeServiceFunc()
29
39
  this.fastify = FASTIFY_CACHE ?? await makeService()
@@ -56,10 +66,6 @@ class BaseAppTest extends BaseTest {
56
66
  }
57
67
  })
58
68
  }
59
-
60
- afterEach () {
61
- fetchWrapper.__mock.mockReset()
62
- }
63
69
  }
64
70
 
65
71
  function makeHeadersObj (headers) {
@@ -72,15 +78,15 @@ function makeHeadersObj (headers) {
72
78
  // so you can pass the output of app.post(), etc. as the promise here as is
73
79
  function makeNodeFetchMockValueFromPromise (promise) {
74
80
  const mockValue = new Promise(resolve => {
75
- promise.then(desiredHttpResponse => {
76
- let body = desiredHttpResponse.text || desiredHttpResponse.body || ''
81
+ promise.then(httpResponse => {
82
+ let body = httpResponse.text || httpResponse.body || ''
77
83
  const headers = {}
78
84
  if (typeof body !== 'string') {
79
85
  body = JSON.stringify(body)
80
86
  headers['content-type'] = 'application/json'
81
87
  }
82
88
  resolve({
83
- status: desiredHttpResponse.status || 200,
89
+ status: httpResponse.status || 200,
84
90
  headers: makeHeadersObj(headers),
85
91
  text: async () => body
86
92
  })
@@ -92,8 +98,8 @@ function makeNodeFetchMockValueFromPromise (promise) {
92
98
  function makeNodeFetchMockValue (body, status, callback) {
93
99
  const mockValue = new Promise(resolve => {
94
100
  // setTimeout is used so that this promise does not synchronously resolve
95
- // because unmocked got will NEVER return synchronously. This ensures
96
- // functions which call got() never resolve synchronously (which can
101
+ // because unmocked fetch will NEVER return synchronously. This ensures
102
+ // functions which call fetch() never resolve synchronously (which can
97
103
  // change their behavior... e.g., allow them to throw when called,
98
104
  // instead of only rejecting later when await'ed).
99
105
  // https://github.com/facebook/jest/issues/6028 (since jest 21.x)
@@ -119,9 +125,7 @@ function makeNodeFetchMockValue (body, status, callback) {
119
125
  }
120
126
 
121
127
  function mockNodeFetch () {
122
- const nodeFetchMock = jest.fn().mockImplementation(({ body }) => {
123
- expect(body).toEqual(zlib.brotliCompressSync('321'))
124
- })
128
+ const nodeFetchMock = jest.fn().mockImplementation()
125
129
 
126
130
  nodeFetchMock.mockResp = (body = '', statusCode = 200, callback) => {
127
131
  nodeFetchMock.mockReturnValue(makeNodeFetchMockValue(body, statusCode, callback))
@@ -135,13 +139,34 @@ function mockNodeFetch () {
135
139
  * provides a mock response then an error will be thrown
136
140
  */
137
141
  nodeFetchMock.mockRespWithCallback = (...callbacks) => {
138
- nodeFetchMock.mockImplementation(request => {
139
- const { url, ...options } = request
142
+ nodeFetchMock.mockImplementation((fullURL, options) => {
143
+ // construct the fetchWrapper's request parameter from the fullURL and
144
+ // options passed as parameters to node-fetch
145
+ const [baseUrl, qsParams] = fullURL.split('?', 2)
146
+ const request = {
147
+ compress: options.headers['content-encoding'] === 'br',
148
+ method: options.method,
149
+ url: baseUrl,
150
+ qsParams: querystring.parse(qsParams),
151
+ headers: options.headers
152
+ }
153
+ delete request.headers['content-encoding']
154
+ const body = options.body
155
+ ? (request.compress
156
+ ? zlib.brotliDecompressSync(options.body)
157
+ : options.body)
158
+ : options.body
159
+ if (options.headers['content-type'] === 'application/json') {
160
+ delete options.headers['content-type']
161
+ request.json = JSON.parse(body)
162
+ } else {
163
+ request.body = body // may be undefined
164
+ }
165
+
140
166
  for (const callback of callbacks) {
141
167
  const desiredHTTPResponse = callback(request)
142
168
  if (desiredHTTPResponse === true) {
143
- const unmockedFetch = jest.requireActual('../src/got')
144
- return unmockedFetch(url, options)
169
+ return fetchWrapper(request, false)
145
170
  }
146
171
  if (desiredHTTPResponse) {
147
172
  if (desiredHTTPResponse.then) {
@@ -153,7 +178,7 @@ function mockNodeFetch () {
153
178
  desiredHTTPResponse.status || 200)
154
179
  }
155
180
  }
156
- throw new Error(`un-mocked got() request: ${JSON.stringify(request)}`)
181
+ throw new Error(`un-mocked fetchWrapper() request: ${JSON.stringify(request)}`)
157
182
  })
158
183
  }
159
184
 
@@ -162,10 +187,11 @@ function mockNodeFetch () {
162
187
  let idx = 0
163
188
  function setupNextResponse () {
164
189
  if (idx < responses.length) {
165
- nodeFetchMock.mockResp(...responses[idx], setupNextResponse)
166
- } else if (idx > responses.length) {
190
+ const [body, code] = responses[idx]
191
+ nodeFetchMock.mockResp(body ?? '', code ?? 200, setupNextResponse)
192
+ } else {
167
193
  // exactly equal means we just got our last callback (okay)
168
- throw new Error('more requests made than we had mock responses')
194
+ nodeFetchMock.mockResp('ran out of mocked responses', 556)
169
195
  }
170
196
  idx += 1
171
197
  }