Skip to content

feat: Edit Distance Wrapper Service (Text and Graphs) #3

feat: Edit Distance Wrapper Service (Text and Graphs)

feat: Edit Distance Wrapper Service (Text and Graphs) #3

name: edit-distance-service
on:
push:
branches: ["master"]
paths:
- "services/edit-distance-service/**"
pull_request:
branches: ["master"]
paths:
- "services/edit-distance-service/**"
defaults:
run:
working-directory: services/edit-distance-service
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Cache dependencies
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('services/edit-distance-service/pyproject.toml') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Install dependencies
run: |
pip install --upgrade pip
pip install -e ".[dev]"
- name: Ruff linting
run: make lint
test:
name: Test
runs-on: ubuntu-latest
needs: lint
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Cache dependencies
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('services/edit-distance-service/pyproject.toml') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Install dependencies
run: |
pip install --upgrade pip
pip install -e ".[dev]"
- name: Run tests
run: pytest -v --tb=short
- name: Run smoke tests
run: |
set -e
# Start server in background
uvicorn src.main:app --host 0.0.0.0 --port 8000 &
SERVER_PID=$!
# Wait for server to be ready
for i in $(seq 1 30); do
if curl -s http://localhost:8000/health | grep -q '"status":"ok"'; then
echo "Server is ready"
break
fi
echo "Waiting for server... ($i/30)"
sleep 1
done
# Smoke test: health endpoint
echo "=== Health check ==="
curl -sf http://localhost:8000/health | python -c "import sys,json; d=json.load(sys.stdin); assert d['status']=='ok', f'Health failed: {d}'"
echo "Health: OK"
# Smoke test: text algorithms discovery
echo "=== Text algorithms ==="
curl -sf http://localhost:8000/v1/text/algorithms | python -c "import sys,json; d=json.load(sys.stdin); assert len(d)>0, 'No algorithms'; print(f'Found {len(d)} text algorithms')"
# Smoke test: GED algorithms discovery
echo "=== GED algorithms ==="
curl -sf http://localhost:8000/v1/graphs/algorithms | python -c "import sys,json; d=json.load(sys.stdin); assert len(d)>0, 'No GED algorithms'; print(f'Found {len(d)} GED algorithms')"
# Smoke test: Levenshtein compute
echo "=== Levenshtein compute ==="
curl -sf -X POST http://localhost:8000/v1/text/distance \
-H 'Content-Type: application/json' \
-d '{"algorithm":"levenshtein","params":{},"inputs":[{"id":"p1","a":"kitten","b":"sitting"}]}' \
| python -c "import sys,json; d=json.load(sys.stdin); assert d['algorithm']=='levenshtein'; assert len(d['results'])==1; print('Levenshtein: OK')"
# Smoke test: GED compute
echo "=== GED compute ==="
curl -sf -X POST http://localhost:8000/v1/graphs/distance \
-H 'Content-Type: application/json' \
-d '{"algorithm":"ged_astar","params":{"mode":"exact","timeout_ms":5000},"graphs":[{"id":"p1","g1":{"nodes":[{"id":"A"},{"id":"B"}],"edges":[{"source":"A","target":"B"}]},"g2":{"nodes":[{"id":"A"},{"id":"B"},{"id":"C"}],"edges":[{"source":"A","target":"B"},{"source":"B","target":"C"}]}}]}' \
| python -c "import sys,json; d=json.load(sys.stdin); assert d['status']=='completed'; assert len(d['results'])==1; print('GED compute: OK')"
echo "=== All smoke tests passed ==="
# Stop the server
kill $SERVER_PID 2>/dev/null || true
wait $SERVER_PID 2>/dev/null || true
generate-openapi:
name: Generate OpenAPI Spec
runs-on: ubuntu-latest
needs: test
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Cache dependencies
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('services/edit-distance-service/pyproject.toml') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Install dependencies
run: |
pip install --upgrade pip
pip install -e ".[dev]"
- name: Generate OpenAPI spec
run: make generate-openapi
- name: Upload OpenAPI spec
uses: actions/upload-artifact@v4
with:
name: openapi-spec-edit-distance-service
path: services/edit-distance-service/edit-distance-service.openapi.json
build:
name: Build & Push Docker Image
runs-on: ubuntu-latest
needs: generate-openapi
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository_owner }}/edit-distance-service
tags: |
type=sha,prefix=sha-
type=raw,value=latest
- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: services/edit-distance-service
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}