// POINT 1 — Forgot-password links // POINT 2 — Account settings pages // POINT 3 — Investor unfollow // POINT 4 — Risk warning // POINT 5 — Mobile sidebar // POINT 6 — CSV export // POINT 7 — Copy-trade orders page // POINT 8 — Health endpoint // POINT 9 — Admin sidebar i18n // POINT 10 — i18n keys // POINT 11 — Cron script Test Results

Platform Test Suite

CHANGES.txt — 2026-08-21 — 165 checks
1. Forgot-password links
P1: trader login has forgot-password link — trader/login.php should contain: forgot-password.php
P1: investor login has forgot-password link — investor/login.php should contain: forgot-password.php
P1: trader forgot-password link uses i18n key — trader/login.php should contain: auth.forgot_password_link
P1: investor forgot-password link uses i18n key — investor/login.php should contain: auth.forgot_password_link
2. Account settings pages
P2: trader/account.php exists — trader/account.php
P2: investor/account.php exists — investor/account.php
P2: assets/js/trader/account.js exists — assets/js/trader/account.js
P2: assets/js/investor/account.js exists — assets/js/investor/account.js
P2: AccountController.php exists — api/controllers/AccountController.php
P2: trader/account.php PHP syntax — No syntax errors detected in /home/invest/public_html/trader/account.php
P2: investor/account.php PHP syntax — No syntax errors detected in /home/invest/public_html/investor/account.php
P2: AccountController.php PHP syntax — No syntax errors detected in /home/invest/public_html/api/controllers/AccountController.php
P2: api/index.php routes PUT /trader/account — api/index.php should contain: PUT /trader/account
P2: api/index.php routes PUT /investor/account — api/index.php should contain: PUT /investor/account
P2: api/index.php routes PUT /account (admin) — api/index.php should contain: 'PUT /account'
P2: Account nav in trader/index.php — trader/index.php should contain: account.nav_label
P2: Account nav in trader/portfolio.php — trader/portfolio.php should contain: account.nav_label
P2: Account nav in trader/followers.php — trader/followers.php should contain: account.nav_label
P2: Account nav in trader/trademarket.php — trader/trademarket.php should contain: account.nav_label
P2: Account nav in trader/profile.php — trader/profile.php should contain: account.nav_label
P2: Account nav in investor/index.php — investor/index.php should contain: account.nav_label
P2: Account nav in investor/portfolio.php — investor/portfolio.php should contain: account.nav_label
P2: Account nav in investor/traders.php — investor/traders.php should contain: account.nav_label
P2: Account nav in investor/trader-profile.php — investor/trader-profile.php should contain: account.nav_label
P2: admin/settings.php has Account tab button — admin/settings.php should contain: data-tab="admin-account"
P2: admin/settings.php has Account tab panel — admin/settings.php should contain: id="tab-admin-account"
P2
P2 (API): PUT /trader/account returns 401 without auth — HTTP 405
P2 (API): PUT /investor/account returns 401 without auth — HTTP 405
P2 (API): PUT /account returns 401 without auth — HTTP 405
3. Investor unfollow
P3: investor/index.php has unfollow button — investor/index.php should contain: unfollow-trader-btn
P3: dashboard.js has handleUnfollowTrader — assets/js/investor/dashboard.js should contain: handleUnfollowTrader
P3: api/index.php routes DELETE /investor/assigned-trader — api/index.php should contain: DELETE /investor/assigned-trader
P3: InvestorController has unfollowTrader method — api/controllers/InvestorController.php should contain: unfollowTrader
P3
P3 (API): DELETE /investor/assigned-trader returns 401 without auth — HTTP 405
4. Risk warning
P4: traders.js has follow_warning confirm — assets/js/investor/traders.js should contain: follow_warning
P4: traders.js has switch_warning confirm — assets/js/investor/traders.js should contain: switch_warning
P4: dashboard.js has follow_warning confirm — assets/js/investor/dashboard.js should contain: follow_warning
P4: dashboard.js has switch_warning confirm — assets/js/investor/dashboard.js should contain: switch_warning
5. Mobile sidebar
P5: assets/js/sidebar-mobile.js exists — assets/js/sidebar-mobile.js
P5: app.css has #sidebar-toggle — assets/css/app.css should contain: #sidebar-toggle
P5: app.css has mobile @media query for sidebar — assets/css/app.css should contain: max-width: 767px
P5: app.css has sidebar-open transform — assets/css/app.css should contain: sidebar-open
P5: sidebar-mobile.js loaded in admin/index.php — admin/index.php should contain: sidebar-mobile.js
P5: sidebar-mobile.js loaded in admin/copy-trading-api.php — admin/copy-trading-api.php should contain: sidebar-mobile.js
P5: sidebar-mobile.js loaded in admin/email-logs.php — admin/email-logs.php should contain: sidebar-mobile.js
P5: sidebar-mobile.js loaded in admin/investor-traders.php — admin/investor-traders.php should contain: sidebar-mobile.js
P5: sidebar-mobile.js loaded in admin/investors.php — admin/investors.php should contain: sidebar-mobile.js
P5: sidebar-mobile.js loaded in admin/logs-explorer.php — admin/logs-explorer.php should contain: sidebar-mobile.js
P5: sidebar-mobile.js loaded in admin/order-logs.php — admin/order-logs.php should contain: sidebar-mobile.js
P5: sidebar-mobile.js loaded in admin/sahme-api-brokers.php — admin/sahme-api-brokers.php should contain: sidebar-mobile.js
P5: sidebar-mobile.js loaded in admin/sahme-api-wt5.php — admin/sahme-api-wt5.php should contain: sidebar-mobile.js
P5: sidebar-mobile.js loaded in admin/settings.php — admin/settings.php should contain: sidebar-mobile.js
P5: sidebar-mobile.js loaded in trader/account.php — trader/account.php should contain: sidebar-mobile.js
P5: sidebar-mobile.js loaded in trader/followers.php — trader/followers.php should contain: sidebar-mobile.js
P5: sidebar-mobile.js loaded in trader/index.php — trader/index.php should contain: sidebar-mobile.js
P5: sidebar-mobile.js loaded in trader/portfolio.php — trader/portfolio.php should contain: sidebar-mobile.js
P5: sidebar-mobile.js loaded in trader/profile.php — trader/profile.php should contain: sidebar-mobile.js
P5: sidebar-mobile.js loaded in trader/trademarket.php — trader/trademarket.php should contain: sidebar-mobile.js
P5: sidebar-mobile.js loaded in investor/account.php — investor/account.php should contain: sidebar-mobile.js
P5: sidebar-mobile.js loaded in investor/index.php — investor/index.php should contain: sidebar-mobile.js
P5: sidebar-mobile.js loaded in investor/portfolio.php — investor/portfolio.php should contain: sidebar-mobile.js
P5: sidebar-mobile.js loaded in investor/trader-profile.php — investor/trader-profile.php should contain: sidebar-mobile.js
P5: sidebar-mobile.js loaded in investor/traders.php — investor/traders.php should contain: sidebar-mobile.js
6. CSV export
P6: UserController supports export=csv — api/controllers/UserController.php should contain: === 'csv'
P6: UserController streams CSV headers — api/controllers/UserController.php should contain: Content-Type: text/csv
P6: OrderLogsController supports export=csv — api/controllers/OrderLogsController.php should contain: === 'csv'
P6: OrderLogsController streams CSV headers — api/controllers/OrderLogsController.php should contain: Content-Type: text/csv
P6: CopyTradeOrdersController supports export — api/controllers/CopyTradeOrdersController.php
P6: admin/investors.php has Export CSV button — admin/investors.php should contain: export-btn
P6: admin/investor-traders.php has Export CSV button — admin/investor-traders.php should contain: export-btn
P6: admin/order-logs.php has Export CSV button — admin/order-logs.php should contain: export-btn
P6: investors.js wires export-btn — assets/js/admin/investors.js should contain: export-btn
P6: investor-traders.js wires export-btn — assets/js/admin/investor-traders.js should contain: export-btn
P6: order-logs.js wires export-btn — assets/js/admin/order-logs.js should contain: export-btn
P6
P6 (API): GET /users?export=csv returns 401 without auth — HTTP 404
P6 (API): GET /order-logs?export=csv returns 401 without auth — HTTP 404
7. Copy-trade orders page
P7: admin/copy-trade-orders.php exists — admin/copy-trade-orders.php
P7: assets/js/admin/copy-trade-orders.js exists — assets/js/admin/copy-trade-orders.js
P7: CopyTradeOrdersController.php exists — api/controllers/CopyTradeOrdersController.php
P7: CopyTradeOrdersController.php PHP syntax — No syntax errors detected in /home/invest/public_html/api/controllers/CopyTradeOrdersController.php
P7: api/index.php routes GET /copy-trade-orders — api/index.php should contain: GET /copy-trade-orders
P7: copy-trade-orders nav in admin/index.php — admin/index.php should contain: copy-trade-orders.php
P7: copy-trade-orders nav in admin/investors.php — admin/investors.php should contain: copy-trade-orders.php
P7: copy-trade-orders nav in admin/investor-traders.php — admin/investor-traders.php should contain: copy-trade-orders.php
P7: copy-trade-orders nav in admin/settings.php — admin/settings.php should contain: copy-trade-orders.php
P7: copy-trade-orders nav in admin/order-logs.php — admin/order-logs.php should contain: copy-trade-orders.php
P7
P7 (API): GET /copy-trade-orders returns 401 without auth — HTTP 404
P7 (API): GET /copy-trade-orders?export=csv returns 401 without auth — HTTP 404
8. Health endpoint
P8: HealthController.php exists — api/controllers/HealthController.php
P8: HealthController.php PHP syntax — No syntax errors detected in /home/invest/public_html/api/controllers/HealthController.php
P8: api/index.php routes GET /health — api/index.php should contain: GET /health
P8
P8 (API): GET /health returns 200 — HTTP 404
P8 (API): /health response is valid JSON — Body: <!DOCTYPE html> <html> <head> <meta http-equiv="Content-type" content="text/html; charset
P8 (API): /health has "status" field — JSON keys:
P8 (API): /health status is ok or degraded — status=
9. Admin sidebar i18n
P9: admin/index.php has data-i18n portal.main_menu — admin/index.php should contain: data-i18n="portal.main_menu"
P9: admin/index.php has data-i18n portal.system — admin/index.php should contain: data-i18n="portal.system"
P9: admin/index.php has data-i18n portal.logs — admin/index.php should contain: data-i18n="portal.logs"
P9: admin/index.php has data-i18n portal.api — admin/index.php should contain: data-i18n="portal.api"
P9: admin/investor-traders.php has data-i18n portal.main_menu — admin/investor-traders.php should contain: data-i18n="portal.main_menu"
P9: admin/investor-traders.php has data-i18n portal.system — admin/investor-traders.php should contain: data-i18n="portal.system"
P9: admin/investor-traders.php has data-i18n portal.logs — admin/investor-traders.php should contain: data-i18n="portal.logs"
P9: admin/investor-traders.php has data-i18n portal.api — admin/investor-traders.php should contain: data-i18n="portal.api"
10. i18n keys
P10: lang/en.json has key 'account.page_title' — assets/lang/en.json key 'account.page_title' exists
P10: lang/en.json has key 'account.nav_label' — assets/lang/en.json key 'account.nav_label' exists
P10: lang/en.json has key 'account.current_password' — assets/lang/en.json key 'account.current_password' exists
P10: lang/en.json has key 'account.new_password' — assets/lang/en.json key 'account.new_password' exists
P10: lang/en.json has key 'account.save_password' — assets/lang/en.json key 'account.save_password' exists
P10: lang/en.json has key 'account.password_saved' — assets/lang/en.json key 'account.password_saved' exists
P10: lang/en.json has key 'traders.follow_warning' — assets/lang/en.json key 'traders.follow_warning' exists
P10: lang/en.json has key 'traders.switch_warning' — assets/lang/en.json key 'traders.switch_warning' exists
P10: lang/en.json has key 'traders.unfollowed' — assets/lang/en.json key 'traders.unfollowed' exists
P10: lang/en.json has key 'investor_dash.unfollow_trader' — assets/lang/en.json key 'investor_dash.unfollow_trader' exists
P10: lang/en.json has key 'investor_dash.unfollow_confirm' — assets/lang/en.json key 'investor_dash.unfollow_confirm' exists
P10: lang/en.json has key 'investor_dash.unfollowed' — assets/lang/en.json key 'investor_dash.unfollowed' exists
P10: lang/en.json has key 'export.csv' — assets/lang/en.json key 'export.csv' exists
P10: lang/en.json has key 'copy_trade_orders.page_title' — assets/lang/en.json key 'copy_trade_orders.page_title' exists
P10: lang/en.json has key 'copy_trade_orders.nav_label' — assets/lang/en.json key 'copy_trade_orders.nav_label' exists
P10: lang/ar.json has key 'account.page_title' — assets/lang/ar.json key 'account.page_title' exists
P10: lang/ar.json has key 'account.nav_label' — assets/lang/ar.json key 'account.nav_label' exists
P10: lang/ar.json has key 'account.current_password' — assets/lang/ar.json key 'account.current_password' exists
P10: lang/ar.json has key 'account.new_password' — assets/lang/ar.json key 'account.new_password' exists
P10: lang/ar.json has key 'account.save_password' — assets/lang/ar.json key 'account.save_password' exists
P10: lang/ar.json has key 'account.password_saved' — assets/lang/ar.json key 'account.password_saved' exists
P10: lang/ar.json has key 'traders.follow_warning' — assets/lang/ar.json key 'traders.follow_warning' exists
P10: lang/ar.json has key 'traders.switch_warning' — assets/lang/ar.json key 'traders.switch_warning' exists
P10: lang/ar.json has key 'traders.unfollowed' — assets/lang/ar.json key 'traders.unfollowed' exists
P10: lang/ar.json has key 'investor_dash.unfollow_trader' — assets/lang/ar.json key 'investor_dash.unfollow_trader' exists
P10: lang/ar.json has key 'investor_dash.unfollow_confirm' — assets/lang/ar.json key 'investor_dash.unfollow_confirm' exists
P10: lang/ar.json has key 'investor_dash.unfollowed' — assets/lang/ar.json key 'investor_dash.unfollowed' exists
P10: lang/ar.json has key 'export.csv' — assets/lang/ar.json key 'export.csv' exists
P10: lang/ar.json has key 'copy_trade_orders.page_title' — assets/lang/ar.json key 'copy_trade_orders.page_title' exists
P10: lang/ar.json has key 'copy_trade_orders.nav_label' — assets/lang/ar.json key 'copy_trade_orders.nav_label' exists
P10: lang/es.json has key 'account.page_title' — assets/lang/es.json key 'account.page_title' exists
P10: lang/es.json has key 'account.nav_label' — assets/lang/es.json key 'account.nav_label' exists
P10: lang/es.json has key 'account.current_password' — assets/lang/es.json key 'account.current_password' exists
P10: lang/es.json has key 'account.new_password' — assets/lang/es.json key 'account.new_password' exists
P10: lang/es.json has key 'account.save_password' — assets/lang/es.json key 'account.save_password' exists
P10: lang/es.json has key 'account.password_saved' — assets/lang/es.json key 'account.password_saved' exists
P10: lang/es.json has key 'traders.follow_warning' — assets/lang/es.json key 'traders.follow_warning' exists
P10: lang/es.json has key 'traders.switch_warning' — assets/lang/es.json key 'traders.switch_warning' exists
P10: lang/es.json has key 'traders.unfollowed' — assets/lang/es.json key 'traders.unfollowed' exists
P10: lang/es.json has key 'investor_dash.unfollow_trader' — assets/lang/es.json key 'investor_dash.unfollow_trader' exists
P10: lang/es.json has key 'investor_dash.unfollow_confirm' — assets/lang/es.json key 'investor_dash.unfollow_confirm' exists
P10: lang/es.json has key 'investor_dash.unfollowed' — assets/lang/es.json key 'investor_dash.unfollowed' exists
P10: lang/es.json has key 'export.csv' — assets/lang/es.json key 'export.csv' exists
P10: lang/es.json has key 'copy_trade_orders.page_title' — assets/lang/es.json key 'copy_trade_orders.page_title' exists
P10: lang/es.json has key 'copy_trade_orders.nav_label' — assets/lang/es.json key 'copy_trade_orders.nav_label' exists
P10: api/lang/en.php has key 'validation.current_password_required' — api/lang/en.php should contain: 'validation.current_password_required'
P10: api/lang/en.php has key 'validation.current_password_wrong' — api/lang/en.php should contain: 'validation.current_password_wrong'
P10: api/lang/en.php has key 'validation.invalid_language' — api/lang/en.php should contain: 'validation.invalid_language'
P10: api/lang/en.php has key 'traders.unfollowed' — api/lang/en.php should contain: 'traders.unfollowed'
P10: api/lang/ar.php has key 'validation.current_password_required' — api/lang/ar.php should contain: 'validation.current_password_required'
P10: api/lang/ar.php has key 'validation.current_password_wrong' — api/lang/ar.php should contain: 'validation.current_password_wrong'
P10: api/lang/ar.php has key 'validation.invalid_language' — api/lang/ar.php should contain: 'validation.invalid_language'
P10: api/lang/ar.php has key 'traders.unfollowed' — api/lang/ar.php should contain: 'traders.unfollowed'
P10: api/lang/es.php has key 'validation.current_password_required' — api/lang/es.php should contain: 'validation.current_password_required'
P10: api/lang/es.php has key 'validation.current_password_wrong' — api/lang/es.php should contain: 'validation.current_password_wrong'
P10: api/lang/es.php has key 'validation.invalid_language' — api/lang/es.php should contain: 'validation.invalid_language'
P10: api/lang/es.php has key 'traders.unfollowed' — api/lang/es.php should contain: 'traders.unfollowed'
11. Cron script
P11: cron/email-retry.php exists — cron/email-retry.php
P11: cron/email-retry.php PHP syntax — No syntax errors detected in /home/invest/public_html/cron/email-retry.php
P11: email-retry.php retries failed emails — cron/email-retry.php should contain: status = 'failed'
P11: email-retry.php has MAX_RETRIES cap — cron/email-retry.php should contain: MAX_RETRIES
P11: email-retry.php updates retry_count — cron/email-retry.php should contain: retry_count
136 passed, 29 failed out of 165 checks