-
Notifications
You must be signed in to change notification settings - Fork 73
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* docs(recipe): add vercel * Update docs/docs/examples-tutorials/recipes/testing-vercel-functions-with-opentelemetry-tracetest.mdx Co-authored-by: Julianne Fermi <[email protected]> * Update docs/docs/examples-tutorials/recipes/testing-vercel-functions-with-opentelemetry-tracetest.mdx Co-authored-by: Julianne Fermi <[email protected]> --------- Co-authored-by: Julianne Fermi <[email protected]>
- Loading branch information
Showing
4 changed files
with
345 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
316 changes: 316 additions & 0 deletions
316
...les-tutorials/recipes/testing-vercel-functions-with-opentelemetry-tracetest.mdx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,316 @@ | ||
--- | ||
id: testing-vercel-functions-with-opentelemetry-tracetest | ||
title: Testing Vercel Functions (Next.js) with OpenTelemetry and Tracetest | ||
description: Quick start on how to configure Vercel functions with OpenTelemetry and Tracetest for enhancing your integration tests with trace-based testing. | ||
hide_table_of_contents: false | ||
keywords: | ||
- tracetest | ||
- trace-based testing | ||
- observability | ||
- distributed tracing | ||
- testing | ||
- cypress | ||
- end to end testing | ||
- end-to-end testing | ||
- integration testing | ||
image: https://res.cloudinary.com/djwdcmwdz/image/upload/v1698686403/docs/Blog_Thumbnail_14_rsvkmo.jpg | ||
--- | ||
|
||
:::note | ||
[Check out the source code on GitHub here.](https://github.com/kubeshop/tracetest/tree/main/examples/integration-testing-vercel-functions) | ||
::: | ||
|
||
[Tracetest](https://tracetest.io/) is a testing tool based on [OpenTelemetry](https://opentelemetry.io/) that allows you to test your distributed application. It allows you to use data from distributed traces generated by OpenTelemetry to validate and assert if your application has the desired behavior defined by your test definitions. | ||
|
||
[Vercel](https://vercel.com/) is a platform that hosts serverless functions and front-end code offering developers scalability and flexibility with no infrastructure overhead. | ||
|
||
## Why is this important? | ||
|
||
Testing Serverless Functions has been a pain point for years. Not having visibility into the infrastructure and not knowing where a test fails causes the MTTR to be higher than for other tools. Including OpenTelemetry in Vercel functions exposes telemetry that you can use for both production visibility and trace-based testing. | ||
|
||
This sample shows how to run integration tests against Vercel Functions using [OpenTelemetry](https://opentelemetry.io/) and Tracetest. | ||
|
||
The Vercel function will fetch data from an external API, transform the data and insert it into a Vercel Postgres database. This particular flow has two failure points that are difficult to test. | ||
|
||
1. Validating that an external API request from a Vercel function is successful. | ||
2. Validating that a Postgres insert request is successful. | ||
|
||
## Prerequisites | ||
|
||
**Tracetest Account**: | ||
|
||
- Sign up to [`app.tracetest.io`](https://app.tracetest.io) or follow the [get started](/getting-started/installation) docs. | ||
- Create an [environment](/concepts/environments). | ||
- Create an [environment token](/concepts/environment-tokens). | ||
- Have access to the environment's [agent API key](/configuration/agent). | ||
- [Vercel Account](https://vercel.com/) | ||
- [Vercel Postgres Database](https://vercel.com/docs/storage/vercel-postgres) | ||
|
||
**Vercel Functions Example:** | ||
|
||
Clone the official [Tracetest Pokeshop Demo App Repo](https://github.com/kubeshop/pokeshop) to your local machine. | ||
|
||
```bash | ||
git clone https://github.com/kubeshop/tracetest.git | ||
cd tracetest/examples/integration-testing-vercel-functions | ||
``` | ||
|
||
Before moving forward, run `npm i` in the root folder to install the dependencies. | ||
|
||
```bash | ||
npm i | ||
``` | ||
|
||
**Docker**: | ||
|
||
- Have [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/) installed on your machine. | ||
|
||
## Project Structure | ||
|
||
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). | ||
|
||
It's using Vercel Functions via `/pages/api`, with [OpenTelemetry configured as explained in the Vercel docs](https://nextjs.org/docs/pages/building-your-application/optimizing/open-telemetry#manual-opentelemetry-configuration). | ||
|
||
### 1. Vercel (Next.js) Function | ||
|
||
The `docker-compose.yaml` file reference the Next.js app with `next-app`. | ||
|
||
### 2. Tracetest | ||
|
||
The `docker-compose.yaml` file also has a Tracetest Agent service and an integration tests service. | ||
|
||
### Docker Compose Network | ||
|
||
All `services` in the `docker-compose.yaml` are on the same network and will be reachable by hostname from within other services. E.g. `next-app:3000` in the `test/api.pokemon.spec.docker.yaml` will map to the `next-app` service. | ||
|
||
## Vercel (Next.js) Function | ||
|
||
The Vercel Function is a simple API, [contained in the `pages/api/pokemon.ts` file](https://github.com/kubeshop/tracetest/blob/main/examples/integration-testing-vercel-functions/pages/api/pokemon.ts). | ||
|
||
```typescript | ||
import { trace, SpanStatusCode } from '@opentelemetry/api' | ||
import type { NextApiRequest, NextApiResponse } from 'next' | ||
import { sql } from '@vercel/postgres' | ||
|
||
export async function addPokemon(pokemon: any) { | ||
return await sql` | ||
INSERT INTO pokemon (name) | ||
VALUES (${pokemon.name}) | ||
RETURNING *; | ||
` | ||
} | ||
|
||
export async function getPokemon(pokemon: any) { | ||
return await sql` | ||
SELECT * FROM pokemon where id=${pokemon.id}; | ||
` | ||
} | ||
|
||
export default async function handler( | ||
req: NextApiRequest, | ||
res: NextApiResponse | ||
) { | ||
const activeSpan = trace.getActiveSpan() | ||
const tracer = await trace.getTracer('integration-testing-vercel-functions') | ||
|
||
try { | ||
|
||
const externalPokemon = await tracer.startActiveSpan('GET Pokemon from pokeapi.co', async (externalPokemonSpan) => { | ||
const requestUrl = `https://pokeapi.co/api/v2/pokemon/${req.body.id || '6'}` | ||
const response = await fetch(requestUrl) | ||
const { id, name } = await response.json() | ||
|
||
externalPokemonSpan.setStatus({ code: SpanStatusCode.OK, message: String("Pokemon fetched successfully!") }) | ||
externalPokemonSpan.setAttribute('pokemon.name', name) | ||
externalPokemonSpan.setAttribute('pokemon.id', id) | ||
externalPokemonSpan.end() | ||
|
||
return { id, name } | ||
}) | ||
|
||
const addedPokemon = await tracer.startActiveSpan('Add Pokemon to Vercel Postgres', async (addedPokemonSpan) => { | ||
const { rowCount, rows: [addedPokemon, ...rest] } = await addPokemon(externalPokemon) | ||
addedPokemonSpan.setAttribute('pokemon.isAdded', rowCount === 1) | ||
addedPokemonSpan.setAttribute('pokemon.added.name', addedPokemon.name) | ||
addedPokemonSpan.end() | ||
return addedPokemon | ||
}) | ||
|
||
res.status(200).json(addedPokemon) | ||
|
||
} catch (err) { | ||
activeSpan?.setAttribute('error', String(err)) | ||
activeSpan?.recordException(String(err)) | ||
activeSpan?.setStatus({ code: SpanStatusCode.ERROR, message: String(err) }) | ||
res.status(500).json({ error: 'failed to load data' }) | ||
} finally { | ||
activeSpan?.end() | ||
} | ||
} | ||
``` | ||
|
||
The OpenTelemetry tracing is [contained in the `instrumentation.node.ts` file](https://github.com/kubeshop/tracetest/blob/main/examples/integration-testing-vercel-functions/instrumentation.node.ts). Traces will be sent to the Tracetest Agent. | ||
|
||
```typescript | ||
import { NodeSDK } from '@opentelemetry/sdk-node' | ||
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http' | ||
import { Resource } from '@opentelemetry/resources' | ||
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions' | ||
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node' | ||
import { FetchInstrumentation } from '@opentelemetry/instrumentation-fetch' | ||
|
||
const sdk = new NodeSDK({ | ||
// The OTEL_EXPORTER_OTLP_ENDPOINT env var is passed into "new OTLPTraceExporter" automatically. | ||
// If the OTEL_EXPORTER_OTLP_ENDPOINT env var is not set the "new OTLPTraceExporter" will | ||
// default to use "http://localhost:4317" for gRPC and "http://localhost:4318" for HTTP. | ||
// This sample is using HTTP. | ||
traceExporter: new OTLPTraceExporter(), | ||
instrumentations: [ | ||
getNodeAutoInstrumentations(), | ||
new FetchInstrumentation(), | ||
], | ||
resource: new Resource({ | ||
[SemanticResourceAttributes.SERVICE_NAME]: 'integration-testing-vercel-functions', | ||
}), | ||
}) | ||
sdk.start() | ||
``` | ||
|
||
### Set up Environment Variables | ||
|
||
Edit the `.env.development` file. Add your Vercel Postgres env vars. | ||
|
||
```bash title=.env.development.local | ||
# OTLP HTTP | ||
OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318" | ||
|
||
# Vercel Postgres | ||
POSTGRES_DATABASE="**********" | ||
POSTGRES_HOST="**********" | ||
POSTGRES_PASSWORD="**********" | ||
POSTGRES_PRISMA_URL="**********" | ||
POSTGRES_URL="**********" | ||
POSTGRES_URL_NON_POOLING="**********" | ||
POSTGRES_USER="**********" | ||
``` | ||
|
||
### Start the Next.js Vercel Function | ||
|
||
Spin up your Next.js app. | ||
|
||
```bash | ||
npm run dev | ||
``` | ||
|
||
This starts the function on `http://localhost:3000/api/pokemon`. | ||
|
||
## Testing the Vercel Function Locally | ||
|
||
[Download the CLI](/getting-started/installation#install-the-tracetest-cli) for your operating system. | ||
|
||
The CLI is bundled with [Tracetest Agent](/concepts/agent/) that runs in your infrastructure to collect responses and traces for tests. | ||
|
||
To start Tracetest Agent add the `--api-key` from your environment. | ||
|
||
```bash title=Terminal | ||
tracetest start --api-key YOUR_AGENT_API_KEY | ||
``` | ||
|
||
Run a test with the test definition `test/api.pokemon.spec.development.yaml`. | ||
|
||
```yaml title=test/api.pokemon.spec.development.yaml | ||
type: Test | ||
spec: | ||
id: kv8C-hOSR | ||
name: Test API | ||
trigger: | ||
type: http | ||
httpRequest: | ||
method: POST | ||
url: http://localhost:3000/api/pokemon | ||
body: "{\n \"id\": \"6\"\n}" | ||
headers: | ||
- key: Content-Type | ||
value: application/json | ||
specs: | ||
- selector: span[tracetest.span.type="http"] | ||
name: "All HTTP Spans: Status code is 200" | ||
assertions: | ||
- attr:http.status_code = 200 | ||
``` | ||
```bash title=Terminal | ||
tracetest run test -f ./test/api.pokemon.spec.development.yaml --required-gates test-specs --output pretty | ||
|
||
[Output] | ||
✔ Test API (https://app.tracetest.io/organizations/ttorg_e66318ba6544b856/environments/ttenv_0e807879e2e38d28/test/-gjd4idIR/run/22/test) - trace id: f2250362ff2f70f8f5be7b2fba74e4b2 | ||
✔ All HTTP Spans: Status code is 200 | ||
``` | ||
## Integration Testing the Vercel Function | ||
Edit the `.env.docker` file to use your Vercel Postgres env vars. | ||
|
||
```bash title=.env.docker | ||
# OTLP HTTP | ||
OTEL_EXPORTER_OTLP_ENDPOINT="http://tracetest-agent:4318" | ||
# Vercel Postgres | ||
POSTGRES_DATABASE="**********" | ||
POSTGRES_HOST="**********" | ||
POSTGRES_PASSWORD="**********" | ||
POSTGRES_PRISMA_URL="**********" | ||
POSTGRES_URL="**********" | ||
POSTGRES_URL_NON_POOLING="**********" | ||
POSTGRES_USER="**********" | ||
``` | ||
|
||
This configures the `OTEL_EXPORTER_OTLP_ENDPOINT` to send traces to Tracetest Agent. | ||
|
||
Edit the `docker-compose.yaml` in the root directory. Add your `TRACETEST_API_KEY`. | ||
|
||
```yaml title=docker-compose.yaml | ||
# [...] | ||
tracetest-agent: | ||
image: kubeshop/tracetest-agent:latest | ||
environment: | ||
- TRACETEST_API_KEY=YOUR_TRACETEST_API_KEY # Find the Agent API Key here: https://docs.tracetest.io/configuration/agent | ||
ports: | ||
- 4317:4317 | ||
- 4318:4318 | ||
networks: | ||
- tracetest | ||
``` | ||
|
||
Edit the `run.bash`. Add your `TRACETEST_API_TOKEN`. | ||
|
||
```bash | ||
#/bin/bash | ||
# Find the API Token here: https://docs.tracetest.io/concepts/environment-tokens | ||
tracetest configure -t YOUR_TRACETEST_API_TOKEN | ||
tracetest run test -f ./api.pokemon.spec.docker.yaml --required-gates test-specs --output pretty | ||
``` | ||
|
||
Now you can run the Vercel function and Tracetest Agent! | ||
|
||
```bash | ||
docker compose up -d --build | ||
``` | ||
|
||
And, trigger the integration tests. | ||
|
||
```bash | ||
docker compose run integration-tests | ||
[Ouput] | ||
[+] Creating 1/0 | ||
✔ Container integration-testing-vercel-functions-tracetest-agent-1 Running 0.0s | ||
SUCCESS Successfully configured Tracetest CLI | ||
✔ Test API (https://app.tracetest.io/organizations/ttorg_e66318ba6544b856/environments/ttenv_82af376d61da80a0/test/p00W82OIR/run/8/test) - trace id: d64ab3a6f52a98141d26679fff3373b6 | ||
✔ All HTTP Spans: Status code is 200 | ||
``` | ||
|
||
## Learn More | ||
|
||
Feel free to check out our [examples in GitHub](https://github.com/kubeshop/tracetest/tree/main/examples) and join our [Slack Community](https://dub.sh/tracetest-community) for more info! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters