Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
12 changes: 1 addition & 11 deletions .github/workflows/api-scraper-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,6 @@ jobs:
python -m pip install --upgrade pip
# Install base testing dependencies
pip install pytest pydantic playwright requests
# Install dependencies for the api-scraper
cd quotron/api-scraper
pip install -r scripts/requirements.txt
cd ../..

- name: Install Playwright browsers
run: |
Expand Down Expand Up @@ -89,12 +85,6 @@ jobs:
if: github.ref != 'refs/heads/main'
run: |
cd quotron
# Install yfinance for the proxy server
cd api-scraper
pip install -r scripts/requirements.txt

cd ..

# Use Quotron CLI to start the proxy
echo "Starting Yahoo Finance Proxy using Quotron CLI..."
./quotron start yfinance-proxy
Expand All @@ -116,4 +106,4 @@ jobs:
- name: Run other integration tests
run: |
cd quotron
python tests/integration/test_integration.py --browser --auth --events
python tests/integration/test_integration.py
2 changes: 0 additions & 2 deletions .github/workflows/yahoo-finance-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ jobs:
- name: Install Python dependencies
run: |
python -m pip install --upgrade pip
cd quotron/api-scraper
pip install -r scripts/requirements.txt

- name: Download Latest CLI Release
run: |
Expand Down
93 changes: 1 addition & 92 deletions quotron/cli/pkg/services/service_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,98 +318,7 @@ func (sm *ServiceManager) GetServiceStatus() (*ServiceStatus, error) {

// startYFinanceProxy starts the YFinance proxy
func (sm *ServiceManager) startYFinanceProxy(ctx context.Context) error {
// Use a simplified approach that's known to work from the command line

// Check if already running
if sm.checkServiceResponding(sm.config.YFinanceProxyHost, sm.config.YFinanceProxyPort) {
fmt.Println("YFinance Proxy is already running and responding")
return nil
}

// Stop any existing daemon process
daemonPath := filepath.Join(sm.config.QuotronRoot, "api-scraper", "scripts", "daemon_proxy.sh")
if _, statErr := os.Stat(daemonPath); os.IsNotExist(statErr) {
fmt.Printf("Warning: daemon script not found at %s\n", daemonPath)
// Fall back to traditional method if daemon script isn't available
sm.stopService("YFinance Proxy", sm.config.YFinanceProxyPIDFile, "python.*yfinance_proxy.py")
} else {
fmt.Println("Stopping existing YFinance Proxy service...")
stopCmd := exec.Command(daemonPath, "stop")
stopCmd.Stdout = os.Stdout
stopCmd.Stderr = os.Stderr
_ = stopCmd.Run() // Ignore errors, we're stopping anyway
}

// Path setup
scriptsDir := filepath.Join(sm.config.QuotronRoot, "api-scraper", "scripts")
scriptPath := filepath.Join(scriptsDir, "yfinance_proxy.py")

// Verify the script exists
if _, err := os.Stat(scriptPath); os.IsNotExist(err) {
return fmt.Errorf("YFinance proxy script not found at %s", scriptPath)
}

// Use virtualenv if available
pythonPath := "python3"
venvPath := filepath.Join(sm.config.QuotronRoot, ".venv")
if _, err := os.Stat(filepath.Join(venvPath, "bin", "python")); err == nil {
pythonPath = filepath.Join(venvPath, "bin", "python")
fmt.Printf("Using Python from virtualenv: %s\n", pythonPath)
}

// Set up log file
logFile, err := os.OpenFile(sm.config.YFinanceLogFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
if err != nil {
return fmt.Errorf("failed to open log file: %w", err)
}
defer logFile.Close()

// Use the daemon script instead of directly running Python
daemonPath = filepath.Join(scriptsDir, "daemon_proxy.sh")
if _, statErr := os.Stat(daemonPath); os.IsNotExist(statErr) {
return fmt.Errorf("daemon script not found at %s", daemonPath)
}

// Make script executable
_ = os.Chmod(daemonPath, 0755)

fmt.Println("Starting YFinance Proxy daemon...")
cmd := exec.CommandContext(ctx, daemonPath,
"--host", sm.config.YFinanceProxyHost,
"--port", strconv.Itoa(sm.config.YFinanceProxyPort))
cmd.Dir = scriptsDir

// Capture output directly to terminal
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr

// Set health service URL in environment
cmd.Env = append(os.Environ(),
fmt.Sprintf("HEALTH_SERVICE_URL=%s", sm.config.HealthServiceURL))

// Run the daemon script (will run and wait for HTTP response)
runErr := cmd.Run()
if runErr != nil {
// Check if the error is because the daemon is already running
if sm.checkServiceResponding(sm.config.YFinanceProxyHost, sm.config.YFinanceProxyPort) {
fmt.Println("YFinance proxy is already running and responding - continuing")
fmt.Printf("UI available at http://%s:%d\n", sm.config.YFinanceProxyHost, sm.config.YFinanceProxyPort)
return nil
}

// If we're here, it's a real error
fmt.Printf("Error: Daemon script returned non-zero exit code: %v\n", runErr)
logTail, _ := exec.Command("tail", "-n", "20", sm.config.YFinanceLogFile).Output()
if len(logTail) > 0 {
fmt.Printf("\nLast log entries:\n%s\n", string(logTail))
}
return fmt.Errorf("failed to start YFinance Proxy daemon: %w", runErr)
}

// If we're here, the daemon script has successfully started the proxy
fmt.Printf("YFinance Proxy daemon started successfully\n")
fmt.Printf("UI available at http://%s:%d\n", sm.config.YFinanceProxyHost, sm.config.YFinanceProxyPort)
fmt.Printf("Log file: %s\n", sm.config.YFinanceLogFile)
// YFinance proxy is now integrated into the Go application, so this is a no-op
return nil
}

Expand Down
101 changes: 1 addition & 100 deletions quotron/tests/integration/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,127 +121,28 @@ def test_api_scraper():
except:
pass

def test_browser_scraper():
"""Test the browser scraper functionality."""
logger.info("Testing browser scraper...")

try:
# Import the scraper directly and run it in test mode
sys.path.append(str(project_root / "browser-scraper" / "playwright"))
from src.scraper import WebScraper

scraper = WebScraper(headless=True)
try:
scraper.start()
logger.info("Browser scraper initialized successfully")

# We can't actually scrape without a valid URL, but we can test initialization
logger.info("Browser scraper test successful")
except Exception as e:
logger.error(f"Error in browser scraper execution: {e}")
finally:
scraper.close()
except Exception as e:
logger.error(f"Error testing browser scraper: {e}")

def test_auth_engine():
"""Test the authentication engine functionality."""
logger.info("Testing auth engine...")

try:
# Import the auth engine directly
sys.path.append(str(project_root / "auth-engine"))
from service.auth_service import (
authenticate_user,
create_access_token,
users_db
)

# Test authentication with test user
username = "testuser"
password = "password123"

user = authenticate_user(users_db, username, password)
if user:
logger.info(f"Authentication successful for user: {username}")

# Test token creation
token_data = {"sub": username}
token = create_access_token(data=token_data)
logger.info(f"Token created successfully: {token[:20]}...")

logger.info("Auth engine test successful")
else:
logger.error(f"Authentication failed for user: {username}")
except Exception as e:
logger.error(f"Error testing auth engine: {e}")

# ETL tests removed - ETL should be tested with a proper database connection
# The test was removed because ETL's primary purpose is database operations
# and testing without a DB connection isn't meaningful
# For proper ETL testing, use:
# 1. Unit tests in Go for ETL components
# 2. Database integration tests with a test database

def test_events_system():
"""Test the events system functionality."""
logger.info("Testing events system...")

try:
# Import the events system components
sys.path.append(str(project_root / "events"))
from schemas.event_schema import StockQuoteEvent

# Create a test event
event = StockQuoteEvent(
event_id=str(uuid.uuid4()),
source="test-script",
data={
"symbol": "AAPL",
"price": 150.25,
"change": 2.5,
"change_percent": 1.69,
"volume": 12345678,
},
metadata={
"environment": "test",
}
)

# Validate that the event can be serialized to JSON
event_json = json.dumps(event.model_dump(), default=str)
logger.info(f"Event serialized successfully: {event_json[:100]}...")
logger.info("Events system test successful")
except Exception as e:
logger.error(f"Error testing events system: {e}")

def main():
"""Run all tests."""
parser = argparse.ArgumentParser(description="Quotron integration tests")
parser.add_argument("--api", action="store_true", help="Test API scraper")
parser.add_argument("--browser", action="store_true", help="Test browser scraper")
parser.add_argument("--auth", action="store_true", help="Test auth engine")
parser.add_argument("--events", action="store_true", help="Test events system")
parser.add_argument("--all", action="store_true", help="Test all components")

args = parser.parse_args()

# If no specific tests are specified, test all
if not any([args.api, args.browser, args.auth, args.events, args.all]):
if not any([args.api, args.all]):
args.all = True

if args.all or args.api:
test_api_scraper()

if args.all or args.browser:
test_browser_scraper()

if args.all or args.auth:
test_auth_engine()

if args.all or args.events:
test_events_system()

logger.info("All tests completed")

if __name__ == "__main__":
Expand Down
Loading