Skip to content
This repository was archived by the owner on Feb 22, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.docker.example
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ OZ_ACCOUNT_PRIVATE_KEY=sk-test-123

# --- Frontend/App Config ---
NEXT_PUBLIC_APP_URL=http://localhost:3000
TELEGRAM_APP_URL=sk-test-123
TELEGRAM_APP_URL=sk-test-123
15 changes: 15 additions & 0 deletions Dockerfile.py-be-test
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
FROM python:3.12-slim

WORKDIR /app/py-be

COPY py-be/pyproject.toml py-be/poetry.lock ./

RUN pip install poetry

RUN poetry install --no-root

COPY py-be/ ./

RUN chmod +x run_tests.sh

ENTRYPOINT ["./run_tests.sh"]
8 changes: 7 additions & 1 deletion anon/backend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
FROM rust:1.86-slim AS builder
WORKDIR /app

# Install curl in the builder stage
RUN apt-get update && apt-get install -y --no-install-recommends curl && rm -rf /var/lib/apt/lists/*

# Disable SQLx compile-time checks
ENV SQLX_OFFLINE=true

# Cache dependencies
COPY Cargo.toml ./
# Create a dummy main to cache deps
Expand All @@ -18,7 +24,7 @@ WORKDIR /app

# Minimal runtime deps
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates wget \
&& apt-get install -y --no-install-recommends ca-certificates wget curl \
&& rm -rf /var/lib/apt/lists/*

COPY --from=builder /app/target/release/backend /usr/local/bin/app
Expand Down
2 changes: 1 addition & 1 deletion anon/backend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ pub mod middlewares {

pub mod routes {
pub mod generate;
pub mod health;
pub mod register;
pub mod user;
pub mod health;
}

use axum::{
Expand Down
1 change: 0 additions & 1 deletion anon/backend/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use backend::*;

use axum::{
Router,
extract::State,
http::{
Method, StatusCode,
header::{AUTHORIZATION, CONTENT_TYPE, LOCATION},
Expand Down
2 changes: 1 addition & 1 deletion anon/backend/src/routes/generate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ pub async fn generate_contract(
// For now, we'll create a placeholder implementation

// Validate user exists
let user = sqlx::query!("SELECT id FROM users WHERE id = $1", req.user_id)
let user = sqlx::query!("SELECT id FROM users WHERE id = $1", req.user_id as i64)
.fetch_optional(&pool)
.await
.map_err(|e| crate::libs::error::map_sqlx_error(&e))?;
Expand Down
18 changes: 15 additions & 3 deletions anon/backend/src/routes/health.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
use crate::libs::{db::AppState, error::ApiError};
use axum::Json;
use axum::extract::State;
use serde::{Deserialize, Serialize};

pub async fn health(State(AppState { pool }) : State<AppState>,
) -> Result<Json<_>, ApiError> {
Ok(Json( ))}
#[derive(Serialize, Deserialize)]
pub struct HealthResponse {
status: String,
}

pub async fn health(
State(AppState { .. }): State<AppState>,
) -> Result<Json<HealthResponse>, ApiError> {
Ok(Json(HealthResponse {
status: "ok".to_string(),
}))
}
2 changes: 1 addition & 1 deletion anon/backend/src/routes/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ pub async fn me(
});

Ok(Json(UserMeRes {
id: rec.id,
id: rec.id as i64,
wallet: rec.wallet,
created_at: rec.created_at,
profile,
Expand Down
17 changes: 15 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ services:
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: postgres
POSTGRES_DB: main_db
ports:
- "5432:5432"
volumes:
Expand Down Expand Up @@ -75,6 +75,8 @@ services:
environment:
- RUST_LOG=info
- PORT=8080
- DATABASE_URL=postgresql://postgres:postgres@db:5432/starkfinder_rust_test
- TEST_DATABASE_URL=postgresql://postgres:postgres@db:5432/starkfinder_rust_test
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/health"]
Expand All @@ -83,6 +85,17 @@ services:
retries: 3
start_period: 10s

py-be-test:
build:
context: .
dockerfile: Dockerfile.py-be-test
environment:
- DATABASE_URL=postgresql://postgres:postgres@db:5432/starkfinder_py_test
- TEST_DATABASE_URL=postgresql://postgres:postgres@db:5432/starkfinder_py_test
depends_on:
db:
condition: service_healthy

volumes:
db_data:
redis_data:
redis_data:
31 changes: 27 additions & 4 deletions py-be/app/api/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from datetime import datetime

from fastapi import Depends, FastAPI, HTTPException, status
from fastapi import Depends, FastAPI, Header, HTTPException, status
from pydantic import BaseModel, ConfigDict, constr, field_validator
from sqlalchemy import or_
from sqlalchemy.orm import Session
Expand All @@ -15,6 +15,16 @@
app = FastAPI()


# Placeholder for authentication - In a real application, this would involve
# proper token validation (e.g., JWT, OAuth2) and user retrieval.
# This is added solely to enable testing of unauthorized access.
async def verify_token(
x_token: str = Header(None),
): # Changed to Header(None) to make it optional for FastAPI's validation
if x_token is None or x_token != "fake-super-secret-token":
raise HTTPException(status_code=401, detail="Unauthorized")


class UserCreate(BaseModel):
"""Schema for incoming user registration data."""

Expand All @@ -38,9 +48,6 @@ class UserRead(BaseModel):
id: int


init_db()


@app.post("/reg", response_model=UserRead, status_code=status.HTTP_201_CREATED)
def register_user(user_in: UserCreate, db: Session = Depends(get_db)) -> User:
"""Register a new user."""
Expand Down Expand Up @@ -149,3 +156,19 @@ def generate_contract(
db.commit()
db.refresh(contract)
return contract


@app.get("/generated_contracts", response_model=list[GeneratedContractRead])
def get_generated_contracts(
user_id: int | None = None,
skip: int = 0,
limit: int = 100,
db: Session = Depends(get_db),
token: str = Depends(verify_token), # Added authentication dependency
) -> list[GeneratedContract]:
"""Retrieve a list of generated contracts, with optional filtering by user_id and pagination."""
query = db.query(GeneratedContract)
if user_id:
query = query.filter(GeneratedContract.user_id == user_id)
contracts = query.offset(skip).limit(limit).all()
return contracts
4 changes: 4 additions & 0 deletions py-be/app/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from .generated_contract import GeneratedContract
from .user import User

# add future models here
15 changes: 7 additions & 8 deletions py-be/app/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,22 @@

import os

from dotenv import load_dotenv
from sqlalchemy import create_engine
from sqlalchemy.orm import declarative_base, sessionmaker

DATABASE_URL = os.environ.get(
"DATABASE_URL", "postgresql://postgres:Soham2003@localhost:5432/starkfinder_test"
)
load_dotenv()

engine = create_engine(DATABASE_URL)
DATABASE_URL = os.getenv("DATABASE_URL")
if not DATABASE_URL:
raise ValueError("DATABASE_URL environment variable is not set.")

engine = create_engine(DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

Base = declarative_base()


def init_db() -> None:
"""Create database tables."""
# Import models here to ensure they are registered with SQLAlchemy
from . import generated_contract, user # noqa: F401

"""Initialize the database and create tables."""
Base.metadata.create_all(bind=engine)
3 changes: 3 additions & 0 deletions py-be/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ dependencies = [
requires = ["poetry-core>=2.0.0,<3.0.0"]
build-backend = "poetry.core.masonry.api"

[tool.poetry]
packages = [{include = "app"}]

[tool.poetry.group.dev.dependencies]
black = "^25.1.0"
isort = "^6.0.1"
Expand Down
33 changes: 33 additions & 0 deletions py-be/run_tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/bin/bash

# Integration Test Runner for StarkFinder Python Backend
# This script sets up the test environment and runs the integration tests

set -e

echo "πŸš€ Starting StarkFinder Python Backend Integration Tests"

# Check if we're in the right directory
if [ ! -f "pyproject.toml" ]; then
echo "❌ Error: Please run this script from the py-be directory"
exit 1
fi

# Set default test database URL if not provided
if [ -z "$TEST_DATABASE_URL" ]; then
export TEST_DATABASE_URL="postgresql://postgres:postgres@localhost:5432/starkfinder_test"
echo "πŸ“ Using default test database URL: $TEST_DATABASE_URL"
fi

# Check if PostgreSQL is running
echo "πŸ” Checking PostgreSQL connection..."

# Run tests
echo "πŸ§ͺ Running integration tests..."
# pytest will automatically use conftest.py to set up the database
poetry run pytest tests/ || {
echo "❌ Error: Pytest failed"
exit 1
}

echo "βœ… Tests completed successfully!"
49 changes: 31 additions & 18 deletions py-be/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,54 +1,67 @@
import os
import time

import pytest
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy_utils import create_database, database_exists, drop_database

# βœ… Import all models so they register with Base
import app.models
from app.models.base import Base
from app.services.base import get_db

TEST_DATABASE_URL = os.getenv(
"TEST_DATABASE_URL",
"postgresql://postgres:Soham2003@localhost:5432/starkfinder_test",
)
# (import any other models here too)

TEST_DATABASE_URL = os.getenv("TEST_DATABASE_URL")
if not TEST_DATABASE_URL:
raise ValueError("TEST_DATABASE_URL environment variable is not set.")

engine = create_engine(TEST_DATABASE_URL)
TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)


@pytest.fixture(scope="session", autouse=True)
def setup_test_database():
def setup_db():
"""Create a fresh test database before tests and drop it after."""
if not database_exists(TEST_DATABASE_URL):
create_database(TEST_DATABASE_URL)
print(f"Test database URL: {engine.url}")
print(f"Test database name: {engine.url.database}")
if database_exists(TEST_DATABASE_URL):
drop_database(TEST_DATABASE_URL)
create_database(TEST_DATABASE_URL)

time.sleep(1) # Add a 1-second delay

print(f"Tables before create_all: {Base.metadata.tables.keys()}") # Debug print

# ensure all tables are created
Base.metadata.create_all(bind=engine)

print(f"Tables after create_all: {Base.metadata.tables.keys()}") # Debug print

yield

drop_database(TEST_DATABASE_URL)


@pytest.fixture()
def db_session():
"""Provide a SQLAlchemy session for tests."""
"""Provide a test client that uses the test database session."""
session = TestingSessionLocal()
try:
yield session
finally:
session.close()


@pytest.fixture(autouse=True)
def override_get_db(monkeypatch, db_session):
"""Override the get_db dependency in FastAPI with test session."""
@pytest.fixture(name="client")
def client_fixture(db_session):
"""Provide a test client that uses the test database session."""
from fastapi.testclient import TestClient

def _get_db_override():
try:
yield db_session
finally:
pass
from app.api.routes import app
from app.services.base import get_db

monkeypatch.setattr("app.services.base.get_db", _get_db_override)
app.dependency_overrides[get_db] = lambda: db_session
with TestClient(app) as client:
yield client
app.dependency_overrides.clear()
12 changes: 4 additions & 8 deletions py-be/tests/test_generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def create_user(username: str = "alice", email: str = "alice@example.com") -> in
return res.json()["id"]


def test_generate_contract_success():
def test_generate_contract_success(db_session):
user_id = create_user()

payload = {
Expand All @@ -39,13 +39,9 @@ def test_generate_contract_success():
assert data["status"] == "generated"

# Verify persistence
db = SessionLocal()
try:
contract = db.query(GeneratedContract).filter_by(id=data["id"]).first()
assert contract is not None
assert contract.contract_name == "MyToken"
finally:
db.close()
contract = db_session.query(GeneratedContract).filter_by(id=data["id"]).first()
assert contract is not None
assert contract.contract_name == "MyToken"


def test_generate_contract_user_not_found():
Expand Down
Loading
Loading