Phase 2 Step 2: Implement Scan API Endpoints
Implemented all 5 scan management endpoints with comprehensive error handling, logging, and integration tests. ## Changes ### API Endpoints (web/api/scans.py) - POST /api/scans - Trigger new scan with config file validation - GET /api/scans - List scans with pagination and status filtering - GET /api/scans/<id> - Retrieve scan details with all relationships - DELETE /api/scans/<id> - Delete scan and associated files - GET /api/scans/<id>/status - Poll scan status for long-running scans ### Features - Comprehensive error handling (400, 404, 500) - Structured logging with appropriate levels - Input validation via validators - Consistent JSON error format - SQLAlchemy error handling with graceful degradation - HTTP status codes following REST conventions ### Testing (tests/test_scan_api.py) - 24 integration tests covering all endpoints - Empty/populated scan lists - Pagination with multiple pages - Status filtering - Error scenarios (invalid input, not found, etc.) - Complete workflow integration test ### Test Infrastructure (tests/conftest.py) - Flask app fixture with test database - Flask test client fixture - Database session fixture compatible with app context - Sample scan fixture for testing ### Documentation (docs/ai/PHASE2.md) - Updated progress: 4/14 days complete (29%) - Marked Step 2 as complete - Added implementation details and testing results ## Implementation Notes - All endpoints use ScanService for business logic separation - Scan triggering returns immediately; client polls status endpoint - Background job execution will be added in Step 3 - Authentication will be added in Step 4 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ Pytest configuration and fixtures for SneakyScanner tests.
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
@@ -11,7 +12,8 @@ import yaml
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
from web.models import Base
|
||||
from web.app import create_app
|
||||
from web.models import Base, Scan
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
@@ -194,3 +196,90 @@ def sample_invalid_config_file(tmp_path):
|
||||
f.write("invalid: yaml: content: [missing closing bracket")
|
||||
|
||||
return str(config_file)
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def app():
|
||||
"""
|
||||
Create Flask application for testing.
|
||||
|
||||
Returns:
|
||||
Configured Flask app instance with test database
|
||||
"""
|
||||
# Create temporary database
|
||||
db_fd, db_path = tempfile.mkstemp(suffix='.db')
|
||||
|
||||
# Create app with test config
|
||||
test_config = {
|
||||
'TESTING': True,
|
||||
'SQLALCHEMY_DATABASE_URI': f'sqlite:///{db_path}',
|
||||
'SECRET_KEY': 'test-secret-key'
|
||||
}
|
||||
|
||||
app = create_app(test_config)
|
||||
|
||||
yield app
|
||||
|
||||
# Cleanup
|
||||
os.close(db_fd)
|
||||
os.unlink(db_path)
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def client(app):
|
||||
"""
|
||||
Create Flask test client.
|
||||
|
||||
Args:
|
||||
app: Flask application fixture
|
||||
|
||||
Returns:
|
||||
Flask test client for making API requests
|
||||
"""
|
||||
return app.test_client()
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def db(app):
|
||||
"""
|
||||
Alias for database session that works with Flask app context.
|
||||
|
||||
Args:
|
||||
app: Flask application fixture
|
||||
|
||||
Returns:
|
||||
SQLAlchemy session
|
||||
"""
|
||||
with app.app_context():
|
||||
yield app.db_session
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_scan(db):
|
||||
"""
|
||||
Create a sample scan in the database for testing.
|
||||
|
||||
Args:
|
||||
db: Database session fixture
|
||||
|
||||
Returns:
|
||||
Scan model instance
|
||||
"""
|
||||
scan = Scan(
|
||||
timestamp=datetime.utcnow(),
|
||||
status='completed',
|
||||
config_file='/app/configs/test.yaml',
|
||||
title='Test Scan',
|
||||
duration=125.5,
|
||||
triggered_by='test',
|
||||
json_path='/app/output/scan_report_20251114_103000.json',
|
||||
html_path='/app/output/scan_report_20251114_103000.html',
|
||||
zip_path='/app/output/scan_report_20251114_103000.zip',
|
||||
screenshot_dir='/app/output/scan_report_20251114_103000_screenshots'
|
||||
)
|
||||
|
||||
db.add(scan)
|
||||
db.commit()
|
||||
db.refresh(scan)
|
||||
|
||||
return scan
|
||||
|
||||
Reference in New Issue
Block a user