Volver al blog

Síguenos y suscríbete

Sólo disponible en inglés

Por el momento, esta página solo está disponible en inglés. Lamentamos las molestias. Vuelva a visitar esta página más tarde.

Introducing scripted testing for Fastly fiddle

Andrew Betts

Principal Developer Advocate, Fastly

Fastly Fiddle allows for instant experimentation with Fastly's edge cloud behaviours without having to set up a Fastly account. Now, we're adding the ability to define assertions to specify the behaviour you are trying to create.

With the recent launch of our library of solution recipes that make it easier to build on Fastly, we now can demonstrate a selection of the solutions that people have created to run on Fastly.  Our growing library of recipes is nearing a hundred, and in building these, we realised that we needed a better way not only to verify that the solutions continue to work as we evolve our network, but also to make it clear what the solution is achieving.

Test for fiddles

Fiddle presents a lot of information— and it can be overwhelming.  How do you know what the key point is that the solution has actually achieved? We already automatically highlight 'interesting' HTTP headers, based on an analysis of your VCL code, but now, you can explicitly define what your fiddle is supposed to do:

To define tests, we start by writing a fiddle containing the logic that we want to test. Taking a simple example, we could add a header to each request as it comes into Fastly, so that the information in that new header is transmitted to your origin.

Here's a fiddle that does that. Feel free to run it:

Testing this behaviour in a normal Fastly service by external observation would be hard. There's no evidence in the response back to the browser that your header got added to the request to the origin. What we need to do is be able to assert things about the internal behaviour of Fastly.

Test targets: what we can test

There are three major sources of data about requests transiting Fastly: the client-side request/response, the origin request/response, and the events that are triggered within the Fastly platform. We expose these in our test syntax as clientFetch, originFetches (there may be more than one), and events.

These three top level objects expose lots of interesting and useful properties:

clientFetch Obj Petición procedente del cliente y dirigida a Fastly \(y la respuesta de Fastly\).
.req Str Bloque de peticiones HTTP. Contiene el método de petición, la ruta, la versión HTTP, los pares clave/valor del encabezado y el cuerpo de la petición.
.resp Str Encabezado de respuesta HTTP. Contiene la línea de estado de respuesta y los encabezados de respuesta \(no el cuerpo\).
.respType Str Valor de encabezado de respuesta Content\-type analizado \(solo tipo mime\).
.isText Bool Si el cuerpo de la respuesta puede o no tratarse como texto.
.isImage Bool Si el cuerpo de la respuesta puede tratarse o no como imagen.
.status Num Estado de la respuesta HTTP.
.bodyPreview Str Vista previa de texto UTF\-8 del cuerpo \(truncado en 1K\).
.bodyBytesReceived Num Cantidad de datos recibidos.
.bodyChunkCount Num Número de fragmentos recibidos.
.complete Bool Si la respuesta está completa o no.
.trailers Str Colas de respuesta HTTP.
originFetches Array Recuperaciones de origen realizadas durante la petición.
\[idx\] Obj Cada recuperación es un objeto.
.vclFlowKey Str ID del flujo de VCL que desencadenó esta recuperación.
.req Str Bloque de peticiones HTTP. Contiene el método de petición, la ruta, la versión HTTP, los pares clave/valor del encabezado y el cuerpo de la petición.
.resp Str Encabezado de respuesta HTTP. Contiene la línea de estado de respuesta y los encabezados de respuesta \(no el cuerpo\).
.remoteAddr Str Dirección IP de origen resuelta.
.remotePort Num Puerto en el servidor de origen.
.remoteHost Str Nombre del host del servidor de origen.
.elapsedTime Num Tiempo total dedicado a la recuperación de origen \(ms\).
events Array Eventos VCL de Fastly relacionados con la petición, en el orden de flujo VCL. Puede contener varios flujos VCL si hay reinicios, ESI o protección.
\[idx\] Obj Cada elemento de matriz es un evento VCL.
.fnName Str Tipo de evento. Puede ser «recv» «hash», «hit», «miss», «pass», «waf», «fetch», «deliver», «error» o «log».
.datacenter Str Código de tres letras que identifica la ubicación del centro de datos de Fastly en el que ocurrió este evento; por ejemplo, «LCY», «JFK» o «SYD».
.nodeID Str Identificador numérico del servidor concreto en el que ocurrió este evento.
.originFetch Obj Recuperación del origen asociada al evento \(consulta el modelo en originFetches, dispuesto anteriormente\). Solo los eventos FETCH tienen una propiedad originFetch.
.logs Array Matriz de cadenas y mensajes registrados de este evento.
. Cualquier propiedad que se notifique en la IU del fiddle respecto de un evento se puede señalar para someterla a pruebas. Por ejemplo, dentro de los eventos MISS, se notifica una propiedad «staleExists».
logs Array Matriz de cadenas, y mensajes registrados de *todos* los eventos VCL.
insights Array Etiquetas de información pertinente relativas a esta petición. Las etiquetas de información pertinente identifican recomendaciones o discrepancias con respecto a prácticas recomendadas. Por ejemplo: \[ "client\-cc\-missing", "invalid\-header" \].

To get to the headers that have been added to the origin request, we need to access the req property of the first item in the originRequests collection:

Simple enough, but what if you wanted to also check that the response from the origin was cached? We would need to access the .ttl property of the fetch event, but we can't be sure what the index of the fetch event is within the events collection.  What is needed is a way to filter the events collection for just the fetch events, and then pick the first one.

Aggregation functions

To make accessing test targets easier, we've added a set of aggregation functions. Drop these into the target string to transform the data:

listBy\(field\) Array => Array Toma una matriz de objetos y hace una matriz de matrices, donde en cada submatriz todos los objetos comparten el mismo valor de «field». Los elementos de la matriz de salida se ordenan en función del orden en el que aparecen en la entrada los valores del campo seleccionado. `events.listBy(vclflowkey)[1][0].fnName is "recv"`
where\(field=val\) Array => Array Toma una matriz de objetos y la filtra de modo que deja solo aquellos donde la propiedad llamada «field» tiene el valor «val». `events.where(fnName=recv)[0].url startsWith "/a/"`
groupBy\(field\) Array => Obj Toma una matriz de objetos y la divide en múltiples matrices, cada una de las cuales tiene el mismo valor en la propiedad «field», y organiza los datos resultantes en un objeto cuyos valores de «field» son claves. `events.groupBy(fnName).recv[0].url startsWith "/a/"`
transpose\(\) Array => Obj Toma una matriz de objetos y hace un objeto de matrices. Cuando varios objetos de entrada comparten el mismo nombre de propiedad, esa propiedad se convierte en una propiedad de nivel superior que tiene una matriz que contiene todos los valores. `events.transpose().return notIncludes "error"`
count\(\) Array => Num Toma una matriz y devuelve la longitud. `originFetches.count() greaterThan 1`
concat\(\) Array => Str Toma una matriz y devuelve una representación de cadena de todos los elementos de la matriz unidos entre sí y delimitados por nuevas líneas. `logs.concat() includes "Hello"`

So, now we can identify the first fetch event's ttl property with:

Finally, you need to assert something about this target data.

Assertions

We support any assertion defined in Chai, and have added a few more. These are likely to be the ones that are most useful to you:

| Nombre | Tipo de valor | Descripción | |--------------------|---------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | es | Cualquiera | Igualdad no estricta \(`==`\) | | isJSON | Ninguno | El destino es un JSON válido \(no requiere un valor de referencia, es decir, solo dos parámetros\) | | isTrue | Ninguno | El destino es verdadero | | isAtLeast, isAbove | número JSON | El destino es numéricamente más alto que el valor de referencia | | isAtMost, isBelow | número JSON | El destino es numéricamente más bajo que el valor de referencia | | incluye | Cualquiera | El destino incluye el valor de referencia. Se puede utilizar para afirmar la inclusión de un valor en una matriz, una subcadena en una cadena de caracteres o un subconjunto de propiedades en un objeto. | | coincide | JS RegExp | El destino coincide con una expresión regular. El Regex debe delimitarse con `/` y puede ir seguido de modificadores, por ejemplo, `/abc/i` | | oneOf | Matriz JSON | Comprueba que el valor del destino sea igual, al menos, a uno de los valores de la matriz de referencia. | | startsWith | cadena JSON | Comprueba que el valor del destino comienza con la cadena de caracteres de referencia | | endsWith | cadena JSON | Comprueba que el valor del destino termina con la cadena de caracteres de referencia |

So, now we can form the full tests for our example fiddle:

Note that the ttl property, along with all timing properties reported by Fastly, is in microseconds - so 3600 seconds (1 hour) is 3,600,000,000 μs. Let's look at how these tests are added to a fiddle through the UI:

Try running the fiddle again, but this time watch as the tests are run!

Tests are parsed when your fiddle runs, and will tell you if any of your test syntax is unparseable.

Debugging

This is all well and good if your tests are passing, but what if they are failing? How do you know that you wrote the test correctly, vs the fiddle itself having a bug that the test has caught?

If a test fails when it is run, Fiddle will display not just the failure state but also the actual value of the target:

This can also be a great help when constructing the target string. If you're not sure what properties exist on a target path, you can just write a test using that path, allowing it to fail so you can see what the target actually looks like:

Tests will run asynchronously and need to wait for asynchronous instrumentation to be delivered. For some targets, the delivery of the instrumentation data takes longer than others.  This is indicated by the hourglass icon on each test — no hourglass, no delay; running hourglass, short delay; completed hourglass, longer delay.

We hope that being able to run tests in fiddle helps to validate Fastly configurations and provide additional confidence in moving your logic to the edge. If you have any ideas or suggestions, feel free to post below in the comments or drop me a line.