Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: tests #185

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
35 changes: 35 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: "Test Pearl Installation E2E"
on:
push:
branches:
- develop
- main
pull_request:

jobs:
e2e-test:
continue-on-error: False

runs-on: ${{ matrix.os }}

strategy:
matrix:
os: [ubuntu-latest, macos-12, macos-14]

timeout-minutes: 30

steps:

- uses: actions/checkout@v2

- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: "20.11"

- name: Install node dependencies
run: yarn install

- name: Run Test
run: |
node electron/test.js
48 changes: 48 additions & 0 deletions .github/workflows/integration.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: "Integration tests"
on:
push:
branches:
- develop
- main
pull_request:

jobs:
integration-test:
continue-on-error: False

runs-on: ${{ matrix.os }}

strategy:
matrix:
os: [ubuntu-latest, macos-12, macos-14]
python-version: ["3.10", "3.11", "3.12"]

timeout-minutes: 30

steps:

- uses: actions/checkout@v3

- uses: actions/setup-python@v4
with:
submodules: recursive
python-version: ${{ matrix.python-version }}

- name: Install and configure Poetry
uses: snok/install-poetry@v1
with:
version: ${{ matrix.poetry-version }}
virtualenvs-create: true
virtualenvs-in-project: true
installer-parallel: true

- name: Check if dependencies can be locked
run: poetry lock --check

- name: Poetry install requirements
run: poetry install --all-extras --no-interaction -vvv

- name: Run Test
run: |
source $(poetry env info --path)/bin/activate
pytest -s tests
233 changes: 118 additions & 115 deletions poetry.lock

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ include = [
operate = "operate.cli:main"

[tool.poetry.dependencies]
python = "<3.13,>=3.8"
python = "<3.13,>=3.10"
open-autonomy = "==0.14.11.post1"
open-aea-ledger-ethereum = "==1.51.0"
open-aea-cli-ipfs = "==1.51.0"
Expand Down Expand Up @@ -46,6 +46,8 @@ web3 = "==6.1.0"
psutil = "^5.9.8"
pyinstaller = "^6.8.0"
aiohttp = "3.9.5"
httpx = "^0.27.0"
pytest-asyncio = "^0.23.7"

[tool.poetry.group.development.dependencies]
tomte = {version = "0.2.15", extras = ["cli"]}
Expand Down
14 changes: 14 additions & 0 deletions run_operate.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Execute this script in a poetry environment (poetry shell, poetry install)
# cp next.config.mjs frontend/next.config.mjs

# Services.ts
source .env
export DEV_RPC=$GNOSIS_RPC
export NODE_ENV=development
cd frontend
yarn install
cd ..


yarn install
yarn start
49 changes: 49 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"""Fixtures"""

import asyncio
from dataclasses import dataclass
from pathlib import Path
from typing import AsyncGenerator, Type

import pytest_asyncio # type: ignore
from uvicorn import Config, Server

from operate import cli


@dataclass
class ServerConfig:
"""Server configuration"""

TEST_HOME = Path("/", "tmp", ".operate")
TEST_HOST = "localhost"
TEST_PORT = 8814


SERVER_ENDPOINT = f"http://{ServerConfig.TEST_HOST}:{ServerConfig.TEST_PORT}"


@pytest_asyncio.fixture
async def server() -> AsyncGenerator[None, Type[Server]]:
"""Server start"""
app = cli.create_app(home=ServerConfig.TEST_HOME)
config = Config(
app=app,
host=ServerConfig.TEST_HOST,
port=ServerConfig.TEST_PORT,
log_level="info",
)
server = Server(config=config)
server_task = asyncio.create_task(server.serve())

# Await until the server is up
while not server.started:
await asyncio.sleep(0.1)

yield server

# Raise the exit flag
server.should_exit = True

# Await until the server exits
await server_task
124 changes: 124 additions & 0 deletions tests/test_backend.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
"""Backend tests"""

from dataclasses import dataclass
from pathlib import Path
from typing import Dict, List, Optional, Type, Union

import httpx # type: ignore
import pytest # type: ignore
from uvicorn import Server

from tests.conftest import SERVER_ENDPOINT, ServerConfig


@dataclass
class HTTP:
"""HTTP codes"""

OK = 200
UNAUTHORIZED = 401
GET = "get"
POST = "post"
PUT = "put"


async def request_test(
url: str, method: str, http_code: int, json: Optional[Union[List, Dict]]
) -> None:
"""Request test"""
async with httpx.AsyncClient() as client:
response = await getattr(client, method)(url)
assert response.status_code == http_code
if json is not None:
assert response.json() == json


# /api
@pytest.mark.parametrize(
"url, method, http_code, json",
[
(
f"{SERVER_ENDPOINT}/api",
HTTP.GET,
HTTP.OK,
{
"name": "Operate HTTP server",
"version": "0.1.0.rc0",
"home": str(ServerConfig.TEST_HOME),
},
),
],
)
@pytest.mark.asyncio
async def test_api(
server: Type[Server],
url: str,
method: str,
http_code: int,
json: Optional[Union[List, Dict]],
) -> None:
"""Test api"""
url = f"{SERVER_ENDPOINT}/api"
await request_test(url, method, http_code, json)


# /shutdown_endpoint
@pytest.mark.asyncio
async def test_shutdown(server: Type[Server]) -> None:
"""Test shutdown"""
with open(
Path(ServerConfig.TEST_HOME, "operate.kill"), "r", encoding="utf-8"
) as file:
shutdown_endpoint = file.read().strip()
url = f"{SERVER_ENDPOINT}/{shutdown_endpoint}"
await request_test(url, HTTP.GET, HTTP.OK, None)


# /api/services
@pytest.mark.parametrize(
"url, method, http_code, json",
[
(f"{SERVER_ENDPOINT}/api/services", HTTP.GET, HTTP.OK, []),
(f"{SERVER_ENDPOINT}/api/services", HTTP.POST, HTTP.UNAUTHORIZED, None),
(f"{SERVER_ENDPOINT}/api/services", HTTP.PUT, HTTP.UNAUTHORIZED, None),
],
)
@pytest.mark.asyncio
async def test_api_services(
server: Type[Server],
url: str,
method: str,
http_code: int,
json: Optional[Union[List, Dict]],
) -> None:
"""Test services"""
await request_test(url, method, http_code, json)


# # account
# @app.get("/api/account")
# @app.post("/api/account")
# @app.put("/api/account")
# @app.post("/api/account/login")

# # wallet
# @app.get("/api/wallet")
# @app.get("/api/wallet/{chain}")
# @app.post("/api/wallet")
# @app.get("/api/wallet/safe")
# @app.get("/api/wallet/safe/{chain}")
# @app.post("/api/wallet/safe")
# @app.put("/api/wallet/safe")

# # Services
# @app.get("/api/services")
# @app.post("/api/services")
# @app.put("/api/services")
# @app.get("/api/services/{service}")
# @app.post("/api/services/{service}/onchain/deploy")
# @app.post("/api/services/{service}/onchain/stop")
# @app.get("/api/services/{service}/deployment")
# @app.post("/api/services/{service}/deployment/build")
# @app.post("/api/services/{service}/deployment/start")
# @app.post("/api/services/{service}/deployment/stop")
# @app.post("/api/services/{service}/deployment/delete")