Skip to content

Commit

Permalink
chore: add option for strict mock registry (npm#5902)
Browse files Browse the repository at this point in the history
  • Loading branch information
lukekarrys authored Nov 30, 2022
1 parent 153c142 commit 968e124
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 27 deletions.
81 changes: 59 additions & 22 deletions mock-registry/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class MockRegistry {
#authorization
#basic
#debug
#strict

constructor (opts) {
if (!opts.registry) {
Expand All @@ -19,20 +20,48 @@ class MockRegistry {
this.#authorization = opts.authorization
this.#basic = opts.basic
this.#debug = opts.debug
this.#strict = opts.strict
// Required for this.package
this.#tap = opts.tap
if (this.#tap) {
this.startNock()
}
}

static tnock (t, host, opts, { debug = false } = {}) {
if (debug) {
Nock.emitter.on('no match', req => console.error('NO MATCH', req.options))
static tnock (t, host, opts, { debug = false, strict = false } = {}) {
const noMatch = (req) => {
if (strict) {
// There are network requests that get caught regardless of error code.
// Turning on strict mode requires that those requests get explicitly
// mocked with a 404, 500, etc.
// XXX: this is opt-in currently because it breaks some existing CLI
// tests. We should work towards making this the default for all tests.
t.fail(`Unmatched request: ${JSON.stringify(req.options, null, 2)}`)
}
if (debug) {
console.error('NO MATCH', t.name, req.options)
}
}

Nock.emitter.on('no match', noMatch)
Nock.disableNetConnect()
const server = Nock(host, opts)

if (strict) {
// this requires that mocks not be shared between sub tests but it helps
// find mistakes quicker instead of waiting for the entire test to end
t.afterEach((t) => {
t.strictSame(server.pendingMocks(), [], 'no pending mocks after each')
t.strictSame(server.activeMocks(), [], 'no active mocks after each')
})
}

t.teardown(() => {
Nock.enableNetConnect()
server.done()
Nock.emitter.off('no match', noMatch)
})

return server
}

Expand All @@ -41,31 +70,38 @@ class MockRegistry {
}

get nock () {
if (!this.#nock) {
if (!this.#tap) {
throw new Error('cannot mock packages without a tap fixture')
}
const reqheaders = {}
if (this.#authorization) {
reqheaders.authorization = `Bearer ${this.#authorization}`
}
if (this.#basic) {
reqheaders.authorization = `Basic ${this.#basic}`
}
this.#nock = MockRegistry.tnock(
this.#tap,
this.#registry,
{ reqheaders },
{ debug: this.#debug }
)
}
return this.#nock
}

set nock (nock) {
this.#nock = nock
}

startNock () {
if (this.nock) {
return
}

if (!this.#tap) {
throw new Error('cannot mock packages without a tap fixture')
}

const reqheaders = {}
if (this.#authorization) {
reqheaders.authorization = `Bearer ${this.#authorization}`
}
if (this.#basic) {
reqheaders.authorization = `Basic ${this.#basic}`
}

this.nock = MockRegistry.tnock(
this.#tap,
this.#registry,
{ reqheaders },
{ debug: this.#debug, strict: this.#strict }
)
}

search ({ responseCode = 200, results = [], error }) {
// the flags, score, and searchScore parts of the response are never used
// by npm, only package is used
Expand Down Expand Up @@ -296,13 +332,14 @@ class MockRegistry {
manifest.users = users
}
for (const packument of packuments) {
const unscoped = name.includes('/') ? name.split('/')[1] : name
manifest.versions[packument.version] = {
_id: `${name}@${packument.version}`,
name,
description: 'test package mock manifest',
dependencies: {},
dist: {
tarball: `${this.#registry}/${name}/-/${name}-${packument.version}.tgz`,
tarball: `${this.#registry}/${name}/-/${unscoped}-${packument.version}.tgz`,
},
maintainers: [],
...packument,
Expand Down
7 changes: 2 additions & 5 deletions smoke-tests/test/fixtures/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ module.exports = async (t, { testdir = {}, debug } = {}) => {
tap: t,
registry: 'http://smoke-test-registry.club/',
debug,
strict: true,
})
const httpProxyRegistry = `http://localhost:${PORT}`
const proxy = httpProxy.createProxyServer({})
Expand All @@ -92,12 +93,8 @@ module.exports = async (t, { testdir = {}, debug } = {}) => {
t.teardown(() => server.close())

// update notifier should never be written
t.afterEach(async (t) => {
t.afterEach((t) => {
t.equal(existsSync(join(paths.cache, '_update-notifier-last-checked')), false)
// this requires that mocks not be shared between sub tests but it helps
// find mistakes quicker instead of waiting for the entire test to end
t.strictSame(registry.nock.pendingMocks(), [], 'no pending mocks after each')
t.strictSame(registry.nock.activeMocks(), [], 'no active mocks after each')
})

const debugLog = debug || CI ? (...a) => console.error(...a) : () => {}
Expand Down

0 comments on commit 968e124

Please sign in to comment.