2025-10-16 15:14:51 +02:00
|
|
|
"""Integration test for complete feedback submission flow"""
|
|
|
|
|
import pytest
|
|
|
|
|
import io
|
|
|
|
|
import os
|
|
|
|
|
import yaml
|
2025-10-16 21:14:10 +02:00
|
|
|
from unittest.mock import patch
|
2025-10-16 15:14:51 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
|
def test_product(app):
|
|
|
|
|
"""Create a test product"""
|
|
|
|
|
with app.app_context():
|
|
|
|
|
# Create test product directory and config
|
|
|
|
|
product_dir = os.path.join(app.config['DATA_DIR'], 'products', 'test-product')
|
|
|
|
|
os.makedirs(product_dir, exist_ok=True)
|
|
|
|
|
|
|
|
|
|
# Create product config
|
|
|
|
|
config_file = os.path.join(product_dir, 'config.yaml')
|
|
|
|
|
config_data = {
|
|
|
|
|
'product_id': 'test-product',
|
|
|
|
|
'name': 'Test Product',
|
|
|
|
|
'submission_url_slug': 'test-product',
|
|
|
|
|
'owner_language': 'en',
|
|
|
|
|
'assigned_owner_ids': ['usr_0001'],
|
|
|
|
|
'status': 'active'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
with open(config_file, 'w') as f:
|
|
|
|
|
yaml.dump(config_data, f)
|
|
|
|
|
|
|
|
|
|
yield 'test-product'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.integration
|
|
|
|
|
def test_complete_feedback_submission_flow(client, app, test_product):
|
|
|
|
|
"""T039: Integration test for complete feedback submission flow
|
|
|
|
|
|
|
|
|
|
Test the entire user journey:
|
|
|
|
|
1. User visits submission form
|
|
|
|
|
2. User fills in feedback text
|
|
|
|
|
3. User attaches files
|
|
|
|
|
4. User submits form
|
|
|
|
|
5. System validates input
|
|
|
|
|
6. System saves feedback to filesystem
|
|
|
|
|
7. System displays confirmation
|
|
|
|
|
8. Feedback is retrievable from storage
|
|
|
|
|
"""
|
|
|
|
|
# Step 1: Visit submission form
|
|
|
|
|
response = client.get('/submit/test-product')
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
|
assert b'<form' in response.data
|
|
|
|
|
|
|
|
|
|
# Step 2-4: Submit feedback with text and files
|
|
|
|
|
feedback_text = 'I found a bug in the login page. When I enter my password, it does not accept special characters.'
|
|
|
|
|
|
|
|
|
|
data = {
|
|
|
|
|
'feedback_text': feedback_text,
|
|
|
|
|
'files': [
|
|
|
|
|
(io.BytesIO(b'PNG fake image data'), 'screenshot.png'),
|
|
|
|
|
(io.BytesIO(b'Error log contents\nLine 2\nLine 3'), 'error.log')
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-16 21:14:10 +02:00
|
|
|
# Mock threading to prevent background analysis (keep original Phase 3 behavior)
|
|
|
|
|
with patch('app.routes.submission.threading.Thread'):
|
|
|
|
|
response = client.post('/submit/test-product',
|
|
|
|
|
data=data,
|
|
|
|
|
content_type='multipart/form-data',
|
|
|
|
|
follow_redirects=True)
|
2025-10-16 15:14:51 +02:00
|
|
|
|
|
|
|
|
# Step 7: Verify success confirmation
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
|
assert b'success' in response.data.lower() or b'thank' in response.data.lower()
|
|
|
|
|
|
|
|
|
|
# Step 8: Verify feedback was saved to filesystem
|
|
|
|
|
with app.app_context():
|
|
|
|
|
data_dir = app.config['DATA_DIR']
|
|
|
|
|
products_dir = os.path.join(data_dir, 'products', 'test-product', 'feedback')
|
|
|
|
|
|
|
|
|
|
# Check that feedback directory was created
|
|
|
|
|
assert os.path.exists(products_dir)
|
|
|
|
|
|
|
|
|
|
# Find the created feedback directory (should be UUID-named)
|
|
|
|
|
feedback_dirs = [d for d in os.listdir(products_dir)
|
|
|
|
|
if os.path.isdir(os.path.join(products_dir, d))]
|
|
|
|
|
|
|
|
|
|
assert len(feedback_dirs) > 0, "No feedback directory was created"
|
|
|
|
|
|
|
|
|
|
feedback_dir = os.path.join(products_dir, feedback_dirs[0])
|
|
|
|
|
|
|
|
|
|
# Verify metadata.yaml exists
|
|
|
|
|
metadata_file = os.path.join(feedback_dir, 'metadata.yaml')
|
|
|
|
|
assert os.path.exists(metadata_file)
|
|
|
|
|
|
|
|
|
|
# Verify metadata content
|
|
|
|
|
with open(metadata_file, 'r') as f:
|
|
|
|
|
metadata = yaml.safe_load(f)
|
|
|
|
|
|
|
|
|
|
assert metadata['feedback_id'] == feedback_dirs[0]
|
|
|
|
|
assert metadata['product_id'] == 'test-product'
|
|
|
|
|
assert metadata['status'] == 'new'
|
|
|
|
|
assert 'submitted_at' in metadata
|
|
|
|
|
assert metadata.get('has_attachments') == True
|
|
|
|
|
assert metadata.get('attachment_count') == 2
|
|
|
|
|
|
|
|
|
|
# Verify content.txt exists and contains the feedback
|
|
|
|
|
content_file = os.path.join(feedback_dir, 'content.txt')
|
|
|
|
|
assert os.path.exists(content_file)
|
|
|
|
|
|
|
|
|
|
with open(content_file, 'r') as f:
|
|
|
|
|
saved_content = f.read()
|
|
|
|
|
|
|
|
|
|
assert feedback_text in saved_content
|
|
|
|
|
|
|
|
|
|
# Verify attachments directory and files exist
|
|
|
|
|
attachments_dir = os.path.join(feedback_dir, 'attachments')
|
|
|
|
|
assert os.path.exists(attachments_dir)
|
|
|
|
|
|
|
|
|
|
attachments = os.listdir(attachments_dir)
|
|
|
|
|
assert len(attachments) == 2
|
|
|
|
|
|
|
|
|
|
# Verify specific attachment files
|
|
|
|
|
attachment_names = [a for a in attachments]
|
|
|
|
|
assert 'screenshot.png' in attachment_names
|
|
|
|
|
assert 'error.log' in attachment_names
|
|
|
|
|
|
|
|
|
|
# Verify no IP address is stored (FR-055 compliance)
|
|
|
|
|
assert 'ip_address' not in metadata
|
|
|
|
|
assert 'submitter_ip' not in metadata
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.integration
|
|
|
|
|
def test_feedback_submission_without_attachments(client, app, test_product):
|
|
|
|
|
"""Integration test for feedback submission with text only (no files)"""
|
|
|
|
|
feedback_text = 'Simple text feedback without attachments.'
|
|
|
|
|
|
|
|
|
|
data = {
|
|
|
|
|
'feedback_text': feedback_text
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
response = client.post('/submit/test-product',
|
|
|
|
|
data=data,
|
|
|
|
|
follow_redirects=True)
|
|
|
|
|
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
|
|
|
|
|
|
# Verify feedback was saved
|
|
|
|
|
with app.app_context():
|
|
|
|
|
data_dir = app.config['DATA_DIR']
|
|
|
|
|
products_dir = os.path.join(data_dir, 'products', 'test-product', 'feedback')
|
|
|
|
|
|
|
|
|
|
feedback_dirs = [d for d in os.listdir(products_dir)
|
|
|
|
|
if os.path.isdir(os.path.join(products_dir, d))]
|
|
|
|
|
|
|
|
|
|
# Find the most recent feedback
|
|
|
|
|
feedback_dir = os.path.join(products_dir, feedback_dirs[-1])
|
|
|
|
|
|
|
|
|
|
# Verify metadata shows no attachments
|
|
|
|
|
metadata_file = os.path.join(feedback_dir, 'metadata.yaml')
|
|
|
|
|
with open(metadata_file, 'r') as f:
|
|
|
|
|
metadata = yaml.safe_load(f)
|
|
|
|
|
|
|
|
|
|
assert metadata.get('has_attachments') == False
|
|
|
|
|
assert metadata.get('attachment_count') == 0
|
|
|
|
|
|
|
|
|
|
# Verify attachments directory doesn't exist or is empty
|
|
|
|
|
attachments_dir = os.path.join(feedback_dir, 'attachments')
|
|
|
|
|
if os.path.exists(attachments_dir):
|
|
|
|
|
assert len(os.listdir(attachments_dir)) == 0
|