Wrangler: Implement local testing

Created on 3 Feb 2020  路  8Comments  路  Source: cloudflare/wrangler

Wrangler is awesome, but it'd be fantastic if we were able to locally test Workers without the need for an internet connection, etc. or without having to constantly upload to Cloudflare.

https://github.com/dollarshaveclub/cloudworker is generally great for testing Workers locally, but unfortunately is no longer actively maintained, is missing functionality that makes Cloudflare Workers go great, like HTMLRewriter, and is looking for volunteers. It seems Cloudflare are pretty familiar with the project already, as there's a blog post detailing Unit Testing Worker Functions.

Cloudflare wrote about Unit Testing Workers in Cloudflare Workers last year too, and commented:

Even though Cloudflare Workers deploy globally within seconds, software developers often choose to use local mock environments to have the fastest possible feedback loop while developing on their local machines. CloudWorker is perfect for this use case but as it is still a mock environment it does not guarantee an identical runtime or environment with all Cloudflare Worker APIs and features. This gap can make developers uneasy as they do not have 100% certainty that their tests will succeed in the production environment.

I think something that can "guarantee an identical runtime or environment with all Cloudflare Worker APIs and features" or at least as near-to as possible would greatly improve adoption of Workers.

I think it would be a fantastic opportunity for Cloudflare to maintain cloudworker, or even implement it directly into wrangler. Having a go-to and maintained solution for testing Workers locally that was up to date with the latest available features would be so useful.

user report wontfix

All 8 comments

I agree that it's not ideal.

cloudworker not being maintenaned is news to me, and that it's pretty bad given that's the only way we give to user to unit test...

Once we have https://github.com/cloudflare/lol-html/issues/38 implemented (which is WIP), someone integrate it into cloudworker to have the real HTMLRewriter. (I also commented into the issue you linked).

I think something that can "guarantee an identical runtime or environment with all Cloudflare Worker APIs and features" or at least as near-to as possible would greatly improve adoption of Workers.

We have discussions internally. I doubt that we can guarantee that with something like cloudworker .

@defjosiah Suggested a mock HTMLRewriter workaround here: https://github.com/cloudflare/wrangler/issues/1047#issuecomment-610074742

We ended up generating a Cloudflare worker in the before block of our integration test suite and then sending the correct cookie authentication headers with each request: https://gist.github.com/faun/cdd68a828cf9de3200e280e177348e9e

Hey @faun - you can use wrangler dev to achieve functional parity with the integration tests you have written, but it's important to note that there are a few important differences between cloudflareworkers.com and real workers. The biggest differences are that CPU limits are not enforced and request.cf is undefined, but there are other differences as well. When wrangler dev is out of beta you'll be able to run integration tests on localhost (though wrangler will still be connecting to Cloudflare's servers)

In case anyone is interested.... I have been working on an update to the DSC cloudworker. Frankly, I didn't know about wrangler dev until I was a ways into it. I was planning to simply use it for my own purposes. However; if there is interest, I'd be willing to share the code. I have zero intention of maintaining it though (I already have more OSS projects than I can maintain). That said, I used my own CLI libs, which I do intend to support (a more formal announcement about this will come at OpenJS World in June). author/shell is the basis of it. Of course, this could be swapped out for something else too.

Here's what makes my version different:

  1. Uses modern ES Module syntax.
  2. Tracks compute time & logs the time (green < 10ms, yellow <50ms, red >50ms)
  3. It's _sort of capable_ of tracking memory usage. I've implemented the experimental vm.measureMemory(). Color-coded warnings are built-in at various levels of memory consumption.*
  4. Implements a better reloader (seems quite a bit faster than wrangler)**
  5. Supports custom headers, allowing devs to add headers that may not be baked in (like the CF IP Country header).
  6. Supports KV expiring keys and the KV list method.
  7. Supports multiple KV persistence files.
  8. Experimental KV API support.*
  9. It's not tested, but would likely pass the original project's unit tests since it was mostly a refactor job with a few additions.

* The memory measurement is experimental in Node 14.0.0. The upgrade to V8 8.1 caused a silent failure, but it has been patched and should be in the next patch update. This tool has logic to only use the feature when it works.

** The original cloudworker leverages Node's watch feature, a native feature which has always had a bunch of duplication issues (amongst others). I implemented it with Chokidar instead. However; the performance gains are achieved by _not restarting the HTTP server_. Instead, the vm context is reloaded dynamically, so there is no reboot at all.

* I added an HTTP router and a few endpoints for managing the KV store (i.e mimicking the KV REST API). It's only partially complete., but it is currently possible to interact with the KV store files in realtime (making them accessible to the worker).

image

Contrived Example Worker.js

addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request))
})

async function handleRequest (request) {
  const headers = new Map(request.headers)
  console.log(headers)

  let result = await mykv.get('test')
  if (result === null) {
    await mykv.put('test', 'blah', { expirationTtl: 60 })
  }

  console.log(await mykv.get('test'))
  setTimeout(async () => {
    console.log('A record should have expired and null should print below this.')
    console.log(await mykv.get('test'))
  }, 61000)
  // await mykv.delete('test')
  // console.log(await mykv.get('test'))
  // for (let i = 0; i < 100; i++) {
  //   await mykv.put(`example_${i+1}`, (i+1).toString())
  // }
  console.log(await mykv.get('a'))
  console.log(await mykv.list({ limit: 10 }))
  console.log(await mykv.list({ limit: 10, cursor: 10 }))
  return new Response('Unsupported Source', { status: 501 })
}

Console Output

_I took this screenshot after waiting a minute, to highlight how KV items are expired._

image

Response

image

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

This issue has been automatically closed because it has not had recent activity. You may re-open the issue if it is still relevant.

Yeah worth calling out that wrangler dev won't ever be a replacement for https://github.com/dollarshaveclub/cloudworker while it has a the cloud dependency, in my example the constraint of only being able to fetch() public interface facing URLs is a non-starter for CI/testing uses, as none of the endpoints it might hit are public!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

geelen picture geelen  路  5Comments

xtuc picture xtuc  路  5Comments

adaptive picture adaptive  路  5Comments

matthew-petrie picture matthew-petrie  路  4Comments

EverlastingBugstopper picture EverlastingBugstopper  路  6Comments