Skip to content

Commit

Permalink
Merge pull request #105 from xsnippet/syntaxes-tests
Browse files Browse the repository at this point in the history
Add declarative HTTP tests using Gabbi
  • Loading branch information
ikalnytskyi committed Jan 12, 2021
2 parents 0a980f6 + e6d39c2 commit 05c100b
Show file tree
Hide file tree
Showing 7 changed files with 173 additions and 3 deletions.
24 changes: 24 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,27 @@ jobs:
with:
command: rustdoc
args: -- -D intra-doc-link-resolution-failure

tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly
override: true

- name: Start PostgreSQL and create a database (Linux)
run: |
sudo systemctl start postgresql.service
sudo -u postgres createuser -s devel
sudo -u postgres createdb -O devel devel
sudo -u postgres psql -c "ALTER USER devel PASSWORD 'devel';" devel
echo "ROCKET_DATABASE_URL=postgres://devel:devel@localhost/devel" >> $GITHUB_ENV
- run: cargo build
- run: python -m venv testvenv
- run: ./testvenv/bin/python -m pip install -r tests/requirements.txt
- run: ./testvenv/bin/python -m pytest -vv tests/
6 changes: 3 additions & 3 deletions src/application.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::collections::HashSet;
use std::collections::BTreeSet;
use std::error::Error;

use super::routes;
Expand All @@ -11,7 +11,7 @@ pub struct Config {
/// kept in sync with a set of supported syntaxes in XSnippet Web in
/// order to ensure that the web part can properly syntax-highlight
/// snippets.
pub syntaxes: Option<HashSet<String>>,
pub syntaxes: Option<BTreeSet<String>>,
}

/// Create and return a Rocket application instance.
Expand All @@ -29,7 +29,7 @@ pub fn create_app() -> Result<rocket::Rocket, Box<dyn Error>> {
syntaxes
.iter()
.map(|v| v.clone().try_into::<String>())
.collect::<Result<HashSet<_>, _>>()?,
.collect::<Result<BTreeSet<_>, _>>()?,
),
Err(rocket::config::ConfigError::Missing(_)) => None,
Err(err) => return Err(Box::new(err)),
Expand Down
9 changes: 9 additions & 0 deletions tests/gabbits/syntaxes-null.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
fixtures:
- XSnippetApiWithNoSyntaxes

tests:
- name: test /syntaxes with no syntaxes configured
GET: /v1/syntaxes
status: 200
response_json_paths:
$: null
14 changes: 14 additions & 0 deletions tests/gabbits/syntaxes-sorted.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
fixtures:
- XSnippetApiWithShuffledSyntaxes

tests:
- name: test /syntaxes
GET: /v1/syntaxes
status: 200
response_json_paths:
$:
- clojure
- json
- lua
- python
- rust
14 changes: 14 additions & 0 deletions tests/gabbits/syntaxes.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
fixtures:
- XSnippetApi

tests:
- name: test /syntaxes
GET: /v1/syntaxes
status: 200
response_json_paths:
$:
- clojure
- json
- lua
- python
- rust
2 changes: 2 additions & 0 deletions tests/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
gabbi >= 2.1.0
pytest >= 6.2.1
107 changes: 107 additions & 0 deletions tests/test_gabbits.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import json
import os
import random
import socket
import subprocess
import sys
import time

import gabbi.driver
import gabbi.fixture

from gabbi.driver import test_pytest


XSNIPPET_API_HOST = "127.0.0.1"
XSNIPPET_API_PORT = 8000
XSNIPPET_API_DATABASE_URL = os.getenv("ROCKET_DATABASE_URL")


class XSnippetApi(gabbi.fixture.GabbiFixture):
"""Start live server of XSnippet API."""

_launch_command = ["cargo", "run"]
_launch_timeout = 5.0
_shutdown_timeout = 5.0
_syntaxes = ["python", "rust", "clojure", "json", "lua"]

def __init__(self):
self.environ = {
"ROCKET_ADDRESS": XSNIPPET_API_HOST,
"ROCKET_PORT": str(XSNIPPET_API_PORT),
"ROCKET_DATABASE_URL": XSNIPPET_API_DATABASE_URL,
}

if self._syntaxes:
# ROCKET_SYNTAXES expects a TOML array as input. Since TOML library
# for Python does not provide public means to serialize just a
# standalone array, we're relying on the fact that a JSON array of
# strings is fully compatible with a TOML array.
self.environ["ROCKET_SYNTAXES"] = json.dumps(self._syntaxes)

self.process = None

def start_fixture(self):
"""Start the live server."""

environ = os.environ.copy()
environ.update(self.environ)

self.process = subprocess.Popen(self._launch_command, env=environ)
_wait_for_socket(XSNIPPET_API_HOST, XSNIPPET_API_PORT, self._launch_timeout)

def stop_fixture(self):
"""Stop the live server."""

if self.process:
self.process.terminate()
try:
self.process.wait(timeout=self._shutdown_timeout)
except TimeoutError:
self.process.kill()
finally:
self.process = None


class XSnippetApiWithShuffledSyntaxes(XSnippetApi):
"""Start live server of XSnippet API with a shuffled list of syntaxes."""

@property
def _syntaxes(self):
syntaxes = super()._syntaxes.copy()
random.shuffle(syntaxes)
return syntaxes


class XSnippetApiWithNoSyntaxes(XSnippetApi):
"""Start live server of XSnippet API with no syntaxes configured."""

_syntaxes = None


def _wait_for_socket(host, port, timeout):
"""Wait for socket to start accepting connections."""

start_time = time.monotonic()

while True:
try:
with socket.create_connection((host, port), timeout=timeout):
break
except Exception:
if time.monotonic() - start_time >= timeout:
raise TimeoutError(
f"Waited to long for the port {port} on host {host} to "
f"start accepting connections."
)
time.sleep(0.1)


def pytest_generate_tests(metafunc):
gabbi.driver.py_test_generator(
os.path.join(os.path.dirname(__file__), "gabbits"),
host=XSNIPPET_API_HOST,
port=XSNIPPET_API_PORT,
fixture_module=sys.modules[__name__],
metafunc=metafunc,
)

0 comments on commit 05c100b

Please sign in to comment.