Initial commit: ROA2WEB - FastAPI + Vue.js + Telegram Bot

Modern ERP Reports Application with microservices architecture

Tech Stack:
- Backend: FastAPI + python-oracledb (Oracle DB integration)
- Frontend: Vue.js 3 + PrimeVue + Vite
- Telegram Bot: python-telegram-bot + SQLite
- Infrastructure: Shared database pool, JWT authentication, SSH tunnel

Features:
- FastAPI backend with async Oracle connection pool
- Vue.js 3 responsive frontend with PrimeVue components
- Telegram bot alternative interface
- Microservices architecture with shared components
- Complete deployment support (Linux Docker + Windows IIS)
- Comprehensive testing (Playwright E2E + pytest)

Repository Structure:
- reports-app/ - Main application (backend, frontend, telegram-bot)
- shared/ - Shared components (database pool, auth, utils)
- deployment/ - Deployment scripts (Linux & Windows)
- docs/ - Project documentation
- security/ - Security scanning and git hooks
This commit is contained in:
2025-10-25 14:55:08 +03:00
commit 6b13ffa183
237 changed files with 70035 additions and 0 deletions

View File

@@ -0,0 +1,324 @@
//! 🔍 DEBUGGING COMPREHENSIVE TEST - ROA2WEB Real Issues Detection
//! Created: 2025-08-04
//! Purpose: Find and fix REAL problems, not just "passing tests"
import { test, expect } from '@playwright/test';
import { LoginPage } from '../page-objects/LoginPage.js';
test.describe('🔍 ROA2WEB Real Issues Debugging Suite', () => {
let loginPage;
let networkRequests = [];
let consoleErrors = [];
let apiResponses = [];
test.beforeEach(async ({ page }) => {
// Reset monitoring arrays
networkRequests = [];
consoleErrors = [];
apiResponses = [];
// Setup comprehensive monitoring
page.on('request', request => {
networkRequests.push({
url: request.url(),
method: request.method(),
headers: request.headers(),
postData: request.postData(),
timestamp: new Date().toISOString()
});
});
page.on('response', response => {
apiResponses.push({
url: response.url(),
status: response.status(),
statusText: response.statusText(),
headers: response.headers(),
timestamp: new Date().toISOString()
});
// Log failed requests immediately
if (response.status() >= 400) {
console.log(`❌ API Error: ${response.status()} ${response.url()}`);
}
});
page.on('console', msg => {
if (msg.type() === 'error') {
const error = {
type: msg.type(),
text: msg.text(),
location: msg.location(),
timestamp: new Date().toISOString()
};
consoleErrors.push(error);
console.log(`🔥 Console Error: ${msg.text()}`);
}
});
page.on('pageerror', err => {
consoleErrors.push({
type: 'pageerror',
text: err.message,
stack: err.stack,
timestamp: new Date().toISOString()
});
console.log(`💥 Page Error: ${err.message}`);
});
loginPage = new LoginPage(page);
await loginPage.navigate();
});
test('🧪 REAL AUTH FLOW - Find FormData vs JSON Issues', async ({ page }) => {
console.log('\n🔍 === TESTING REAL AUTHENTICATION FLOW ===');
// Fill real credentials (update these with actual test credentials)
const username = 'test_user';
const password = 'test_password';
await page.fill('#username', username);
await page.fill('#password input', password);
// Monitor the actual request being sent
const [response] = await Promise.all([
page.waitForResponse('**/auth/login'),
page.click('button[type="submit"]')
]);
// CRITICAL: Analyze the actual request format
const request = response.request();
const postData = request.postData();
const contentType = request.headers()['content-type'];
console.log('\n📊 === REQUEST ANALYSIS ===');
console.log('Content-Type:', contentType);
console.log('Request Method:', request.method());
console.log('Request Body:', postData);
console.log('Response Status:', response.status());
// Check if FormData or JSON is being sent
if (contentType && contentType.includes('application/json')) {
console.log('✅ Sending JSON (correct)');
try {
const jsonData = JSON.parse(postData);
expect(jsonData).toHaveProperty('username');
expect(jsonData).toHaveProperty('password');
} catch (e) {
console.log('❌ Invalid JSON format');
}
} else if (contentType && contentType.includes('multipart/form-data')) {
console.log('⚠️ Sending FormData (may cause issues)');
} else {
console.log('❓ Unknown content type:', contentType);
}
// Check response
if (response.status() === 422) {
const responseBody = await response.text();
console.log('🚨 422 Validation Error:', responseBody);
}
// Generate comprehensive monitoring report
console.log('\n📈 === MONITORING REPORT ===');
console.log(`Network Requests: ${networkRequests.length}`);
console.log(`API Responses: ${apiResponses.length}`);
console.log(`Console Errors: ${consoleErrors.length}`);
// Take screenshot for analysis
await page.screenshot({
path: 'debug-auth-flow.png',
fullPage: true
});
});
test('🔧 LOGIN BUTTON STATE - Debug Disabled Logic', async ({ page }) => {
console.log('\n🔍 === DEBUGGING LOGIN BUTTON STATE ===');
// Test initial state
const initialDisabled = await page.locator('button[type="submit"]').isDisabled();
console.log('Initial button disabled:', initialDisabled);
// Test empty fields
await page.fill('#username', '');
await page.fill('#password input', '');
const emptyFieldsDisabled = await page.locator('button[type="submit"]').isDisabled();
console.log('Empty fields - button disabled:', emptyFieldsDisabled);
// Test with only username
await page.fill('#username', 'test');
const usernameOnlyDisabled = await page.locator('button[type="submit"]').isDisabled();
console.log('Username only - button disabled:', usernameOnlyDisabled);
// Test with both fields
await page.fill('#password input', 'password');
const bothFieldsDisabled = await page.locator('button[type="submit"]').isDisabled();
console.log('Both fields - button disabled:', bothFieldsDisabled);
// Check form validation state
const formValidation = await page.evaluate(() => {
const usernameInput = document.getElementById('username');
const passwordInput = document.querySelector('#password input');
const button = document.querySelector('button[type="submit"]');
return {
usernameValue: usernameInput?.value,
passwordValue: passwordInput?.value,
buttonDisabled: button?.disabled,
buttonClasses: button?.className,
usernameValid: usernameInput?.checkValidity(),
passwordValid: passwordInput?.checkValidity()
};
});
console.log('Form validation state:', JSON.stringify(formValidation, null, 2));
// Take screenshot of current state
await page.screenshot({ path: 'debug-button-state.png' });
});
test('🚨 ERROR MESSAGE FORMAT - Debug Toast Issues', async ({ page }) => {
console.log('\n🔍 === DEBUGGING ERROR MESSAGE FORMAT ===');
// Force a network error by using wrong endpoint
await page.route('**/auth/login', async route => {
await route.fulfill({
status: 500,
contentType: 'application/json',
body: JSON.stringify({ detail: 'Server error for testing' })
});
});
await page.fill('#username', 'test');
await page.fill('#password input', 'test');
await page.click('button[type="submit"]');
// Wait for error message and analyze its content
await page.waitForTimeout(2000); // Wait for toast to appear
// Check various possible error message selectors
const errorSelectors = [
'.error-message',
'.p-toast-message-error',
'.p-toast-message-text',
'.p-toast-summary',
'.p-toast-detail'
];
for (const selector of errorSelectors) {
const elements = await page.locator(selector).all();
for (let i = 0; i < elements.length; i++) {
const text = await elements[i].textContent();
if (text && text.trim()) {
console.log(`Error message found in ${selector}[${i}]:`, text);
}
}
}
// Check all visible text content that might contain error messages
const allText = await page.evaluate(() => {
const textNodes = [];
const walker = document.createTreeWalker(
document.body,
NodeFilter.SHOW_TEXT,
null,
false
);
let node;
while (node = walker.nextNode()) {
const text = node.textContent.trim();
if (text.toLowerCase().includes('eroare') ||
text.toLowerCase().includes('error') ||
text.toLowerCase().includes('conectare')) {
textNodes.push(text);
}
}
return textNodes;
});
console.log('All error-related text found:', allText);
// Take screenshot of error state
await page.screenshot({ path: 'debug-error-messages.png' });
});
test('🌐 NETWORK MONITORING - Real API Behavior', async ({ page }) => {
console.log('\n🔍 === COMPREHENSIVE NETWORK MONITORING ===');
// Test various scenarios
const testScenarios = [
{ name: 'Valid Login', username: 'valid_user', password: 'valid_pass' },
{ name: 'Invalid Credentials', username: 'invalid', password: 'invalid' },
{ name: 'Empty Credentials', username: '', password: '' }
];
for (const scenario of testScenarios) {
console.log(`\n--- Testing: ${scenario.name} ---`);
// Clear form
await page.fill('#username', '');
await page.fill('#password input', '');
// Fill credentials if provided
if (scenario.username) await page.fill('#username', scenario.username);
if (scenario.password) await page.fill('#password input', scenario.password);
// Reset monitoring arrays for this scenario
networkRequests.length = 0;
apiResponses.length = 0;
consoleErrors.length = 0;
// Try to submit (if button is enabled)
const isDisabled = await page.locator('button[type="submit"]').isDisabled();
if (!isDisabled) {
try {
const [response] = await Promise.all([
page.waitForResponse('**/auth/login', { timeout: 5000 }),
page.click('button[type="submit"]')
]);
console.log(`Response Status: ${response.status()}`);
const responseBody = await response.text();
console.log(`Response Body: ${responseBody.substring(0, 200)}...`);
} catch (error) {
console.log(`No API call made: ${error.message}`);
}
} else {
console.log('Button is disabled - no API call expected');
}
// Wait a bit for any async operations
await page.waitForTimeout(1000);
console.log(`Network requests: ${networkRequests.length}`);
console.log(`Console errors: ${consoleErrors.length}`);
}
});
test.afterEach(async ({ page }) => {
// Generate final report
console.log('\n📋 === FINAL TEST REPORT ===');
console.log(`Total Network Requests: ${networkRequests.length}`);
console.log(`Total API Responses: ${apiResponses.length}`);
console.log(`Total Console Errors: ${consoleErrors.length}`);
if (consoleErrors.length > 0) {
console.log('\n🚨 Console Errors Found:');
consoleErrors.forEach((error, index) => {
console.log(`${index + 1}. ${error.text}`);
});
}
if (apiResponses.some(r => r.status >= 400)) {
console.log('\n❌ Failed API Requests:');
apiResponses
.filter(r => r.status >= 400)
.forEach(response => {
console.log(`${response.status} ${response.url}`);
});
}
});
});