Skip to content
This repository has been archived by the owner on Oct 27, 2023. It is now read-only.

Commit

Permalink
Add pytest (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
Nicole White authored Aug 28, 2023
1 parent 96fde38 commit f754181
Show file tree
Hide file tree
Showing 11 changed files with 130 additions and 67 deletions.
20 changes: 10 additions & 10 deletions .github/workflows/autoblocks-simulations.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ env:
POETRY_VERSION: "1.5.1"
PYTHON_VERSION: "3.11"

# Use the simulation ingestion key so that Autoblocks knows we're sending simulated events
AUTOBLOCKS_INGESTION_KEY: ${{ secrets.AUTOBLOCKS_SIMULATION_INGESTION_KEY }}

# Any other environment variables the application needs to run
OPENAI_API_KEY: ${{ secrets.DEMO_OPENAI_API_KEY }}

jobs:
autoblocks-simulations:
runs-on: ubuntu-latest
Expand All @@ -32,25 +38,19 @@ jobs:
- name: Install dependencies
run: poetry install

- name: Run tests
run: poetry run pytest

- name: Start the app
run: poetry run start &
env:
# Use the simulation ingestion key so that Autoblocks knows we're sending simulated events
AUTOBLOCKS_INGESTION_KEY: ${{ secrets.AUTOBLOCKS_SIMULATION_INGESTION_KEY }}

# Any other environment variables the application needs to run
OPENAI_API_KEY: ${{ secrets.DEMO_OPENAI_API_KEY }}

- name: Wait for the app to be ready
run: |
while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' http://localhost:5000/health)" != "200" ]]; do sleep 1; done
- name: Run simulation with static test cases
run: poetry run simulation-static

- name: Run simulation with production test cases
run: poetry run simulation-production-replay
env:
# Production events are fetched from the Autoblocks API,
# so we need the API key to authenticate
# so we need the API key to authenticate.
AUTOBLOCKS_API_KEY: ${{ secrets.AUTOBLOCKS_API_KEY }}
27 changes: 15 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,42 @@ This repository contains an example project that integrates Autoblocks Simulatio
via GitHub Actions.
See the [documentation](https://docs.autoblocks.ai/guides/simulations) for more information.

## Instructions
## Instructions for running locally

### 1. Install dependencies

```bash
poetry install
```

### 2. Start the application
### 2. Run the tests

Start the application with your simulation ingestion key,
a simulation id to uniquely identify your simulation run,
and any other environment variables needed to run your application:
Set the `AUTOBLOCKS_INGESTION_KEY` environment variable to your simulation ingestion key before running the tests
so that any events sent during the test run are sent as simulated events.

```bash
AUTOBLOCKS_INGESTION_KEY=<simulation-ingestion-key> \
AUTOBLOCKS_SIMULATION_ID=$(date +%Y%m%d%H%M%S) \
OPENAI_API_KEY=<openai-api-key> \
poetry run start
poetry run pytest
```

### 3. Run the simulation
[View your simulation](https://app.autoblocks.ai/simulations)

In another terminal, run either:
### 3. Run a simulation with production events

* `simulation-static`, which will replay a static set of test cases against your application:
To run a simulation that replays production events, first start the application:

```bash
poetry run simulation-static
AUTOBLOCKS_INGESTION_KEY=<simulation-ingestion-key> \
AUTOBLOCKS_SIMULATION_ID=$(date +%Y%m%d%H%M%S) \
OPENAI_API_KEY=<openai-api-key> \
poetry run start
```

* `simulation-production-replay`, which will replay a set of production events fetched from the Autoblocks API:
Then, in a separate terminal, run the simulation:

```bash
AUTOBLOCKS_API_KEY=<autoblocks-api-key> poetry run simulation-production-replay
```

[View your simulation](https://app.autoblocks.ai/simulations)
17 changes: 2 additions & 15 deletions demo_app/app.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import uuid

from autoblocks.tracer import AutoblocksTracer
from flask import Flask
from flask import request

from demo_app import bot
from demo_app.settings import AUTOBLOCKS_SIMULATION_TRACE_ID_HEADER_NAME
from demo_app.settings import REQUEST_PAYLOAD_MESSAGE
from demo_app.settings import env

app = Flask(__name__)

Expand All @@ -29,19 +26,9 @@ def main():
# but in a simulation scenario we use the trace id passed in via the simulation trace id header
trace_id = request.headers.get(AUTOBLOCKS_SIMULATION_TRACE_ID_HEADER_NAME) or str(uuid.uuid4())

autoblocks = AutoblocksTracer(
env.AUTOBLOCKS_INGESTION_KEY,
trace_id=trace_id,
properties=dict(source="DEMO_SIMULATIONS"),
)
autoblocks.send_event(REQUEST_PAYLOAD_MESSAGE, properties=dict(payload=payload))
output = bot.get_response(trace_id, query)

output = bot.get_response(autoblocks, query)

response = {"output": output}
autoblocks.send_event("request.response", properties=dict(response=response))

return response
return dict(output=output)


def start():
Expand Down
8 changes: 7 additions & 1 deletion demo_app/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@
from demo_app.settings import env


def get_response(autoblocks: AutoblocksTracer, query: str) -> str:
def get_response(trace_id: str, query: str) -> str:
autoblocks = AutoblocksTracer(
env.AUTOBLOCKS_INGESTION_KEY,
trace_id=trace_id,
properties=dict(source="DEMO_SIMULATIONS"),
)

ai = AIChat(
api_key=env.OPENAI_API_KEY,
model="gpt-3.5-turbo",
Expand Down
4 changes: 2 additions & 2 deletions demo_app/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
# the event being handled during the simulation.
AUTOBLOCKS_SIMULATION_TRACE_ID_HEADER_NAME = "x-autoblocks-simulation-trace-id"

# The message for the request.payload event, pulled into a variable here
# The message for the user input event, pulled into a variable here
# so that it's kept in sync between sending the events and replaying them.
REQUEST_PAYLOAD_MESSAGE = "request.payload"
USER_QUERY_MESSAGE = "user.query"


# Environment variables
Expand Down
24 changes: 3 additions & 21 deletions demo_app/simulations.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,10 @@
from autoblocks.api.models import TraceFilterOperator

from demo_app.settings import AUTOBLOCKS_SIMULATION_TRACE_ID_HEADER_NAME
from demo_app.settings import REQUEST_PAYLOAD_MESSAGE
from demo_app.settings import USER_QUERY_MESSAGE
from demo_app.settings import env


def static():
"""
Test a static set of events against the locally-running app.
"""
for trace_id, query in [
("san-francisco-tourist-attractions", "San Francisco tourist attractions"),
("paris-tourist-attractions", "Paris tourist attractions"),
("lombard-street", "Lombard Street"),
("eiffel-tower", "Eiffel Tower"),
]:
print(f"Testing static event {trace_id} - {query}")
requests.post(
"http://localhost:5000",
json={"query": query},
headers={AUTOBLOCKS_SIMULATION_TRACE_ID_HEADER_NAME: trace_id},
)


def production_replay():
"""
Replays production events fetched from the Autoblocks API against the locally-running app.
Expand All @@ -46,15 +28,15 @@ def production_replay():
EventFilter(
key=SystemEventFilterKey.MESSAGE,
operator=EventFilterOperator.EQUALS,
value=REQUEST_PAYLOAD_MESSAGE,
value=USER_QUERY_MESSAGE,
),
],
),
],
)
for trace in page.traces:
for event in trace.events:
if event.message == REQUEST_PAYLOAD_MESSAGE:
if event.message == USER_QUERY_MESSAGE:
print(f"Replaying past event {event}")

# The original payload
Expand Down
65 changes: 61 additions & 4 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ requests = "^2.31.0"
flask = "^2.3.2"
pydantic-settings = "^2.0.2"
simpleaichat = "^0.2.2"
autoblocksai = "0.0.6"
autoblocksai = "0.0.7"

[tool.poetry.group.dev.dependencies]
pre-commit = "^3.3.3"
pytest = "^7.4.0"

[build-system]
requires = ["poetry-core"]
Expand All @@ -35,5 +36,4 @@ known-first-party = ["demo_app"]

[tool.poetry.scripts]
start = "demo_app.app:start"
simulation-static = "demo_app.simulations:static"
simulation-production-replay = "demo_app.simulations:production_replay"
Empty file added tests/__init__.py
Empty file.
11 changes: 11 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import os
from datetime import datetime

import pytest


@pytest.fixture(scope="session", autouse=True)
def set_autoblocks_simulation_id():
os.environ["AUTOBLOCKS_SIMULATION_ID"] = "pytest-" + datetime.now().strftime("%Y%m%d-%H%M%S")
yield
del os.environ["AUTOBLOCKS_SIMULATION_ID"]
17 changes: 17 additions & 0 deletions tests/test_bot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import pytest

from demo_app import bot


@pytest.mark.parametrize(
"trace_id,query,expected_output",
[
("san-francisco-tourist-attractions", "San Francisco tourist attractions", "Lombard"),
("paris-tourist-attractions", "Paris tourist attractions", "Eiffel"),
("lombard-street", "Lombard Street", "San Francisco"),
("eiffel-tower", "Eiffel Tower", "Paris"),
],
)
def test_bot(trace_id: str, query: str, expected_output: str):
response = bot.get_response(trace_id, query)
assert expected_output in response

0 comments on commit f754181

Please sign in to comment.