Deploy: 2026-05-22 23:57:14
This commit is contained in:
@@ -1,141 +1,5 @@
|
||||
<?php
|
||||
/**
|
||||
* Driver Registration & Postponement Flow Simulation Script
|
||||
* Run this on the server: php backend/public/test_simulation.php
|
||||
*/
|
||||
|
||||
require_once dirname(__DIR__) . '/app/bootstrap.php';
|
||||
|
||||
use App\Core\Database;
|
||||
use App\Models\ConversationState;
|
||||
use App\Models\DriverReminder;
|
||||
use App\Core\Flows\ConversationFlowEngine;
|
||||
use App\Models\WhatsAppSession;
|
||||
|
||||
echo "=== Starting Driver Registration & Reminder Flow Simulation ===\n\n";
|
||||
|
||||
$companyId = 1;
|
||||
$phone = "963999999999"; // Test Phone Number
|
||||
|
||||
// Setup mock session
|
||||
$session = WhatsAppSession::findOrCreate($companyId);
|
||||
// Ensure session is marked connected for simulation to go through sendReply
|
||||
WhatsAppSession::updateState($session['id'], [
|
||||
'phone' => $phone,
|
||||
'status' => 'connected'
|
||||
]);
|
||||
// Refresh session data
|
||||
$session = WhatsAppSession::findByCompany($companyId);
|
||||
|
||||
echo "1. Cleaning up existing test states/reminders...\n";
|
||||
DriverReminder::ensureTableExists();
|
||||
\App\Models\DriverOcrData::ensureTableExists();
|
||||
Database::execute("DELETE FROM conversation_states WHERE contact_phone = ?", [$phone]);
|
||||
Database::execute("DELETE FROM driver_registration_reminders WHERE phone = ?", [$phone]);
|
||||
Database::execute("DELETE FROM driver_ocr_data WHERE phone = ?", [$phone]);
|
||||
|
||||
echo "2. Initializing conversation to 'id_front' state...\n";
|
||||
ConversationState::saveState([
|
||||
'company_id' => $companyId,
|
||||
'contact_phone' => $phone,
|
||||
'flow_name' => 'driver_registration_flow',
|
||||
'current_step' => 'id_front',
|
||||
'context_data' => json_encode(['name' => 'جميل الشام'], JSON_UNESCAPED_UNICODE),
|
||||
'expires_at' => date('Y-m-d H:i:s', strtotime('+1 hour'))
|
||||
]);
|
||||
|
||||
echo "3. Simulating user postponement request: 'بكرة المسا ببعتلك الهوية الشخصية'...\n";
|
||||
$msgData = [
|
||||
'phone' => $phone,
|
||||
'body' => 'بكرة المسا ببعتلك الهوية الشخصية'
|
||||
];
|
||||
|
||||
$handled = ConversationFlowEngine::processMessage($session, $msgData);
|
||||
echo " Message processed by flow engine: " . ($handled ? "YES" : "NO") . "\n";
|
||||
|
||||
// Fetch the new state and scheduled reminders
|
||||
$state = ConversationState::findActive($companyId, $phone);
|
||||
echo " New Flow Step: " . ($state ? $state['current_step'] : 'None (finished)') . "\n";
|
||||
echo " Context: " . ($state ? $state['context_data'] : 'None') . "\n";
|
||||
|
||||
$reminders = Database::select("SELECT * FROM driver_registration_reminders WHERE phone = ?", [$phone]);
|
||||
echo " Scheduled Reminders Count: " . count($reminders) . "\n";
|
||||
if (!empty($reminders)) {
|
||||
foreach ($reminders as $r) {
|
||||
echo " - ID: {$r['id']}, Scheduled At: {$r['scheduled_at']}, Postpone Count: {$r['postpone_count']}, Status: {$r['status']}\n";
|
||||
}
|
||||
if (php_sapi_name() !== 'cli') {
|
||||
http_response_code(403);
|
||||
exit('Access denied.');
|
||||
}
|
||||
|
||||
echo "\n4. Testing Cron Scheduler (Simulate reminder execution by setting scheduled_at to the past)...\n";
|
||||
if (!empty($reminders)) {
|
||||
Database::execute("UPDATE driver_registration_reminders SET scheduled_at = ? WHERE id = ?", [
|
||||
date('Y-m-d H:i:s', strtotime('-1 minute')),
|
||||
$reminders[0]['id']
|
||||
]);
|
||||
|
||||
echo " Triggering cron process via internal endpoint request simulation...\n";
|
||||
// We call the main cron logic directly by querying and executing the reminder
|
||||
$dueReminders = Database::select(
|
||||
"SELECT * FROM driver_registration_reminders WHERE status = 'pending' AND scheduled_at <= ?",
|
||||
[date('Y-m-d H:i:s')]
|
||||
);
|
||||
|
||||
echo " Due Reminders found: " . count($dueReminders) . "\n";
|
||||
foreach ($dueReminders as $reminder) {
|
||||
$rule = \App\Models\ChatbotRule::findActiveForRule($companyId);
|
||||
$geminiKey = ($rule && !empty($rule['gemini_api_key'])) ? $rule['gemini_api_key'] : getenv('GEMINI_API_KEY');
|
||||
$elApiKey = ($rule && !empty($rule['elevenlabs_api_key'])) ? $rule['elevenlabs_api_key'] : getenv('ELEVENLABS_API_KEY');
|
||||
$elVoiceId = ($rule && !empty($rule['elevenlabs_voice_id'])) ? $rule['elevenlabs_voice_id'] : getenv('ELEVENLABS_VOICE_ID');
|
||||
|
||||
$driverName = 'جميل الشام';
|
||||
$nameStr = $driverName ? " كابتن " . $driverName : " كابتن";
|
||||
$reminderMsg = "أهلاً بك{$nameStr}، حابين نذكرك تكمل خطوات تسجيلك لتنضم لعائلة انطلق 🚖. بقية الأوراق كتير مهمة لنفعل حسابك ونبدأ سوا. بانتظار إرسالها!";
|
||||
|
||||
echo " Generating reminder Audio via TTS...\n";
|
||||
$audioData = null;
|
||||
if (!empty($geminiKey)) {
|
||||
$audioData = \App\Services\GeminiService::generateAudioResponse(
|
||||
$geminiKey,
|
||||
"أنت روبوت خدمة العملاء لشركة انطلق، تتحدث باللهجة السورية الودودة.",
|
||||
$reminderMsg,
|
||||
'Puck',
|
||||
$elApiKey ?: null,
|
||||
$elVoiceId ?: null
|
||||
);
|
||||
}
|
||||
|
||||
echo " Sending outbound simulated message...\n";
|
||||
ConversationFlowEngine::sendReply($session, $phone, $reminderMsg);
|
||||
|
||||
if ($audioData && !empty($audioData['audio'])) {
|
||||
echo " Audio voice note generated successfully (Length: " . strlen($audioData['audio']) . " bytes). Sending...\n";
|
||||
ConversationFlowEngine::sendReply(
|
||||
$session,
|
||||
$phone,
|
||||
'',
|
||||
null,
|
||||
$audioData['audio'],
|
||||
$audioData['mimeType'] ?? 'audio/mp4'
|
||||
);
|
||||
} else {
|
||||
echo " Audio voice note generation skipped or failed (Gemini Key may be missing or empty).\n";
|
||||
}
|
||||
|
||||
DriverReminder::update($reminder['id'], ['status' => 'sent']);
|
||||
echo " Reminder ID {$reminder['id']} status set to 'sent'.\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n5. Simulating user resuming registration (sending message 'هلا يا كابتن')...\n";
|
||||
$msgData = [
|
||||
'phone' => $phone,
|
||||
'body' => 'هلا يا كابتن'
|
||||
];
|
||||
|
||||
$handled = ConversationFlowEngine::processMessage($session, $msgData);
|
||||
echo " Resumed Message handled: " . ($handled ? "YES" : "NO") . "\n";
|
||||
|
||||
$state = ConversationState::findActive($companyId, $phone);
|
||||
echo " Resumed Flow Step: " . ($state ? $state['current_step'] : 'None') . "\n";
|
||||
|
||||
echo "\n=== Simulation Finished ===\n";
|
||||
|
||||
@@ -1,141 +1,5 @@
|
||||
<?php
|
||||
/**
|
||||
* WooCommerce Integration & SaaS Limit Verification Simulation
|
||||
* Run this on the server: php backend/public/test_woocommerce_limits.php
|
||||
*/
|
||||
|
||||
require_once dirname(__DIR__) . '/app/bootstrap.php';
|
||||
|
||||
use App\Core\Database;
|
||||
use App\Models\CompanySubscription;
|
||||
use App\Models\CompanySubscriptionUsage;
|
||||
use App\Models\WooCommerceStore;
|
||||
use App\Services\WooCommerceService;
|
||||
|
||||
echo "=== Starting SaaS & WooCommerce Integration Tests ===\n\n";
|
||||
|
||||
$companyId = 999; // Mock company for testing limits
|
||||
$phone = "966555555555"; // Mock Saudi number
|
||||
|
||||
// Ensure clean database state for this mock company
|
||||
Database::execute("DELETE FROM company_subscriptions WHERE company_id = ?", [$companyId]);
|
||||
Database::execute("DELETE FROM company_subscription_usage WHERE company_id = ?", [$companyId]);
|
||||
Database::execute("DELETE FROM woocommerce_stores WHERE company_id = ?", [$companyId]);
|
||||
Database::execute("DELETE FROM companies WHERE id = ?", [$companyId]);
|
||||
|
||||
// 1. Create Mock Company
|
||||
echo "1. Creating mock company...\n";
|
||||
Database::execute("INSERT INTO companies (id, name, created_at) VALUES (?, 'Mock Merchant Co', NOW())", [$companyId]);
|
||||
|
||||
// 2. Add WooCommerce Mock Connection details
|
||||
echo "2. Saving mock WooCommerce store credentials...\n";
|
||||
$mockStoreUrl = "https://mock-woo-store.com";
|
||||
$mockConsumerKey = "ck_1234567890123456789012345678901234567890";
|
||||
$mockConsumerSecret = "cs_1234567890123456789012345678901234567890";
|
||||
$mockWebhookSecret = "webhook_secret_xyz";
|
||||
|
||||
$storeId = WooCommerceStore::saveStore(
|
||||
$companyId,
|
||||
$mockStoreUrl,
|
||||
$mockConsumerKey,
|
||||
$mockConsumerSecret,
|
||||
$mockWebhookSecret
|
||||
);
|
||||
echo " WooCommerce Store saved. ID: $storeId\n";
|
||||
|
||||
// Verify decryption
|
||||
$store = WooCommerceStore::findByCompany($companyId);
|
||||
$decrypted = WooCommerceStore::getDecryptedCredentials($store);
|
||||
if ($decrypted['consumer_key'] === $mockConsumerKey && $decrypted['consumer_secret'] === $mockConsumerSecret) {
|
||||
echo " ✅ Credentials decryption verified successfully.\n";
|
||||
} else {
|
||||
echo " ❌ Credentials decryption FAILED.\n";
|
||||
if (php_sapi_name() !== 'cli') {
|
||||
http_response_code(403);
|
||||
exit('Access denied.');
|
||||
}
|
||||
|
||||
// 3. Test Phone Trailing Digit Matching
|
||||
echo "3. Testing phone trailing digit comparison helper...\n";
|
||||
$testCases = [
|
||||
['+966555555555', '0555555555', true],
|
||||
['00966555555555', '966555555555', true],
|
||||
['0555555555', '555555555', true],
|
||||
['962799999999', '0799999999', true],
|
||||
['12345', '12345', true],
|
||||
['12345', '54321', false],
|
||||
];
|
||||
|
||||
foreach ($testCases as $case) {
|
||||
$res = WooCommerceService::comparePhones($case[0], $case[1]);
|
||||
$status = ($res === $case[2]) ? "PASS" : "FAIL";
|
||||
echo " Compare '{$case[0]}' with '{$case[1]}': " . ($res ? "MATCH" : "NO MATCH") . " ($status)\n";
|
||||
}
|
||||
|
||||
// 4. Test Subscription Limits and Dynamic Reset (تصفير ديناميكي)
|
||||
echo "\n4. Testing Subscription Limits and Usage...\n";
|
||||
|
||||
// Insert a Custom Plan for testing
|
||||
// ID 999 Plan: Max 3 requests, 1 voice note, 1 ocr
|
||||
Database::execute("INSERT INTO subscription_plans (id, name, price, max_sessions, max_requests, max_voice_requests, max_ocr_requests, features) VALUES (999, 'Test Plan', 0.00, 1, 3, 1, 1, '{}') ON DUPLICATE KEY UPDATE max_requests=3, max_voice_requests=1, max_ocr_requests=1");
|
||||
|
||||
// Create active subscription starting 5 days ago and ending 25 days from now
|
||||
$startsAt = date('Y-m-d H:i:s', strtotime('-5 days'));
|
||||
$endsAt = date('Y-m-d H:i:s', strtotime('+25 days'));
|
||||
Database::execute(
|
||||
"INSERT INTO company_subscriptions (company_id, plan_id, status, starts_at, ends_at) VALUES (?, 999, 'active', ?, ?)",
|
||||
[$companyId, $startsAt, $endsAt]
|
||||
);
|
||||
|
||||
$activeSub = CompanySubscription::findActiveByCompany($companyId);
|
||||
echo " Active Subscription found: Plan ID {$activeSub['plan_id']}\n";
|
||||
echo " Billing cycle starts: {$activeSub['starts_at']}, ends: {$activeSub['ends_at']}\n";
|
||||
|
||||
// Test dynamic usage record initialization
|
||||
$usage = CompanySubscriptionUsage::getOrCreateCurrentUsage($companyId, $activeSub);
|
||||
echo " Initialized usage: Requests={$usage['request_count']}, Voice={$usage['voice_count']}, OCR={$usage['ocr_count']}\n";
|
||||
|
||||
// Check limits
|
||||
echo " Checking initial limits:\n";
|
||||
echo " - Has request limit? " . (CompanySubscriptionUsage::hasRemainingLimit($companyId, 'request') ? "YES" : "NO") . "\n";
|
||||
echo " - Has voice limit? " . (CompanySubscriptionUsage::hasRemainingLimit($companyId, 'voice') ? "YES" : "NO") . "\n";
|
||||
echo " - Has OCR limit? " . (CompanySubscriptionUsage::hasRemainingLimit($companyId, 'ocr') ? "YES" : "NO") . "\n";
|
||||
|
||||
// Increment and check limits
|
||||
echo " Incrementing request and voice usage...\n";
|
||||
CompanySubscriptionUsage::incrementUsage($companyId, 'request');
|
||||
CompanySubscriptionUsage::incrementUsage($companyId, 'voice');
|
||||
|
||||
$usage = CompanySubscriptionUsage::getOrCreateCurrentUsage($companyId, $activeSub);
|
||||
echo " Current usage: Requests={$usage['request_count']}, Voice={$usage['voice_count']}, OCR={$usage['ocr_count']}\n";
|
||||
|
||||
echo " - Has remaining voice limit? " . (CompanySubscriptionUsage::hasRemainingLimit($companyId, 'voice') ? "YES" : "NO") . "\n";
|
||||
|
||||
// Exceed request limits
|
||||
echo " Exceeding request limits...\n";
|
||||
CompanySubscriptionUsage::incrementUsage($companyId, 'request'); // Request 2
|
||||
CompanySubscriptionUsage::incrementUsage($companyId, 'request'); // Request 3
|
||||
|
||||
$usage = CompanySubscriptionUsage::getOrCreateCurrentUsage($companyId, $activeSub);
|
||||
echo " Current usage: Requests={$usage['request_count']}\n";
|
||||
echo " - Has remaining request limit? " . (CompanySubscriptionUsage::hasRemainingLimit($companyId, 'request') ? "YES" : "NO") . "\n";
|
||||
|
||||
// 5. Test Webhook Signature verification logic
|
||||
echo "\n5. Testing Webhook Signature verification...\n";
|
||||
$mockPayload = json_encode(['id' => 1025, 'status' => 'completed', 'total' => '150.00']);
|
||||
$signature = base64_encode(hash_hmac('sha256', $mockPayload, $mockWebhookSecret, true));
|
||||
|
||||
echo " Calculated Signature: $signature\n";
|
||||
|
||||
// Verify matching logic
|
||||
$calculatedSig = base64_encode(hash_hmac('sha256', $mockPayload, $mockWebhookSecret, true));
|
||||
if (hash_equals($calculatedSig, $signature)) {
|
||||
echo " ✅ Signature verification logic PASSED.\n";
|
||||
} else {
|
||||
echo " ❌ Signature verification logic FAILED.\n";
|
||||
}
|
||||
|
||||
// Clean up mock company after tests
|
||||
Database::execute("DELETE FROM company_subscriptions WHERE company_id = ?", [$companyId]);
|
||||
Database::execute("DELETE FROM company_subscription_usage WHERE company_id = ?", [$companyId]);
|
||||
Database::execute("DELETE FROM woocommerce_stores WHERE company_id = ?", [$companyId]);
|
||||
Database::execute("DELETE FROM companies WHERE id = ?", [$companyId]);
|
||||
|
||||
echo "\n=== Tests Completed successfully! ===\n";
|
||||
|
||||
Reference in New Issue
Block a user