From 66019f87d555a4023f7957fa2138fc59979d0b94 Mon Sep 17 00:00:00 2001 From: Venkatesh Date: Tue, 21 Jul 2026 10:46:42 +0530 Subject: [PATCH] FEAT_HR_LOGIN_DIGONOSIS --- app/Config/HrLoginDiagnoseRoutes.php | 18 + app/Config/Routing.php | 1 + app/Controllers/HrLoginDiagnoseController.php | 526 ++++++++++++++++ app/Views/hr_login_diagnose.php | 590 ++++++++++++++++++ 4 files changed, 1135 insertions(+) create mode 100644 app/Config/HrLoginDiagnoseRoutes.php create mode 100644 app/Controllers/HrLoginDiagnoseController.php create mode 100644 app/Views/hr_login_diagnose.php diff --git a/app/Config/HrLoginDiagnoseRoutes.php b/app/Config/HrLoginDiagnoseRoutes.php new file mode 100644 index 00000000..b02f0c38 --- /dev/null +++ b/app/Config/HrLoginDiagnoseRoutes.php @@ -0,0 +1,18 @@ +group('hrLoginDiagnose', ['filter' => 'authMVC'], static function ($routes) { + $routes->get('/', 'HrLoginDiagnoseController::index'); + $routes->get('', 'HrLoginDiagnoseController::index'); + $routes->post('check', 'HrLoginDiagnoseController::check'); +}); + +// Fallback when request path still includes index.php (wrong baseURL / form action). +$routes->get('index.php/hrLoginDiagnose', 'HrLoginDiagnoseController::index', ['filter' => 'authMVC']); +$routes->get('index.php/hrLoginDiagnose/', 'HrLoginDiagnoseController::index', ['filter' => 'authMVC']); +$routes->post('index.php/hrLoginDiagnose/check', 'HrLoginDiagnoseController::check', ['filter' => 'authMVC']); diff --git a/app/Config/Routing.php b/app/Config/Routing.php index 8d3c7731..eda3998d 100755 --- a/app/Config/Routing.php +++ b/app/Config/Routing.php @@ -27,6 +27,7 @@ class Routing extends BaseRouting */ public array $routeFiles = [ APPPATH . 'Config/Routes.php', + APPPATH . 'Config/HrLoginDiagnoseRoutes.php', ]; /** diff --git a/app/Controllers/HrLoginDiagnoseController.php b/app/Controllers/HrLoginDiagnoseController.php new file mode 100644 index 00000000..0b7a1fa3 --- /dev/null +++ b/app/Controllers/HrLoginDiagnoseController.php @@ -0,0 +1,526 @@ +request->getGet('email')); + $mobile = trim((string) $this->request->getGet('mobile')); + + $data = [ + 'title' => 'HR Login Diagnostic', + 'email' => $email, + 'mobile' => $mobile, + 'result' => null, + 'has_search' => false, + ]; + + if ($email !== '' || $mobile !== '') { + $data['has_search'] = true; + $data['result'] = $this->diagnose($email, $mobile); + } + + return view('hr_login_diagnose', $data); + } + + public function check() + { + try { + $payload = $this->request->getJSON(true); + if (!is_array($payload) || empty($payload)) { + $payload = [ + 'email' => $this->request->getPost('email'), + 'mobile' => $this->request->getPost('mobile'), + ]; + } + + $email = trim((string) ($payload['email'] ?? '')); + $mobile = trim((string) ($payload['mobile'] ?? '')); + + if ($email === '' && $mobile === '') { + return $this->respond([ + 'status' => false, + 'message' => 'Enter email or mobile.', + ], 400); + } + + $result = $this->diagnose($email, $mobile); + + return $this->respond([ + 'status' => true, + 'data' => $result, + ], 200); + } catch (\Throwable $e) { + log_message('error', 'HrLoginDiagnose check failed: ' . $e->getMessage() . ' @ ' . $e->getFile() . ':' . $e->getLine()); + + return $this->respond([ + 'status' => false, + 'message' => 'Diagnosis failed: ' . $e->getMessage(), + ], 500); + } + } + + private function diagnose(string $email, string $mobile): array + { + $issues = []; + $postDb = \Config\Database::connect(); + $contacts = $this->fetchPostContacts($postDb, $email, $mobile); + + if (empty($contacts)) { + $issues[] = $this->issue( + 'HR_NOT_FOUND', + 'high', + 'No active HR found in post level_contacts for the given email/mobile (contact_type=client).', + 'Create/activate the HR contact under the correct client branch, or verify the email/mobile.' + ); + + return [ + 'input' => ['email' => $email, 'mobile' => $mobile], + 'overall_status' => 'FAIL', + 'summary' => 'HR not found in post DB.', + 'issues' => $issues, + 'post' => ['contacts' => []], + 'hr_access_control'=> [], + 'pre' => ['reachable' => null, 'mappings' => []], + 'login_simulation' => [ + 'would_authenticate' => false, + 'cards' => [], + ], + ]; + } + + $accessRows = []; + $preMaps = []; + $loginCards = []; + $preReachable = null; + + try { + $preDb = \Config\Database::connect('preDB'); + $preDb->connect(); + $preReachable = true; + } catch (\Throwable $e) { + $preReachable = false; + $preDb = null; + $issues[] = $this->issue( + 'PRE_DB_UNREACHABLE', + 'high', + 'Could not connect to preDB: ' . $e->getMessage(), + 'Check preDB credentials in Database config / .env.' + ); + } + + foreach ($contacts as $contact) { + $flags = [ + 'HR_FOUND' => true, + 'BRANCH_ACTIVE' => (int) ($contact['branch_active'] ?? 0) === 1, + 'CLIENT_ACTIVE' => (int) ($contact['client_active'] ?? 0) === 1, + ]; + + if (!$flags['BRANCH_ACTIVE']) { + $issues[] = $this->issue( + 'BRANCH_INACTIVE', + 'high', + "Branch inactive for post_hr_id={$contact['post_hr_id']} (branch #{$contact['post_branch_id']}).", + 'Activate client_branch.is_active=1 for this branch.' + ); + } + if (!$flags['CLIENT_ACTIVE']) { + $issues[] = $this->issue( + 'CLIENT_INACTIVE', + 'high', + "Client inactive for post_hr_id={$contact['post_hr_id']} (client #{$contact['post_client_id']}).", + 'Activate clients.is_active=1 for this client.' + ); + } + + $access = $this->fetchAccessRow( + $postDb, + (int) $contact['post_hr_id'], + (int) $contact['post_branch_id'], + (int) $contact['post_client_id'] + ); + + $modules = []; + $hasModules = false; + $tokenReady = false; + $failureReason = null; + + if (empty($access)) { + $failureReason = 'NO_HR_ACCESS_CONTROL_ROW'; + $issues[] = $this->issue( + 'NO_HR_ACCESS_CONTROL_ROW', + 'high', + "No hr_access_control row for post_hr_id={$contact['post_hr_id']}, post_branch_id={$contact['post_branch_id']}, post_client_id={$contact['post_client_id']}.", + 'Open Client Onboarding → HR Access and save at least one module for this HR/branch.' + ); + } else { + $modules = json_decode($access['allowed_modules'] ?? '[]', true); + if (!is_array($modules)) { + $modules = []; + } + $hasModules = $this->hasAnyModule($modules); + $tokenReady = $hasModules; + + if (!$hasModules) { + $failureReason = 'EMPTY_ALLOWED_MODULES'; + $issues[] = $this->issue( + 'EMPTY_ALLOWED_MODULES', + 'high', + "hr_access_control id={$access['id']} exists but allowed_modules is empty.", + 'Assign pre/post modules in HR Access Control and save.' + ); + } + } + + $accessRows[] = [ + 'id' => $access['id'] ?? null, + 'post_hr_id' => (int) $contact['post_hr_id'], + 'pre_hr_id' => isset($access['pre_hr_id']) ? (int) $access['pre_hr_id'] : null, + 'post_branch_id' => (int) $contact['post_branch_id'], + 'pre_branch_id' => isset($access['pre_branch_id']) ? (int) $access['pre_branch_id'] : null, + 'post_client_id' => (int) $contact['post_client_id'], + 'pre_client_id' => isset($access['pre_client_id']) ? (int) $access['pre_client_id'] : null, + 'allowed_modules' => $modules, + 'allowed_pre_policies' => json_decode($access['allowed_pre_policies'] ?? '[]', true) ?: [], + 'allowed_active_policies' => json_decode($access['allowed_active_policies'] ?? '[]', true) ?: [], + 'allowed_cd' => json_decode($access['allowed_cd'] ?? '[]', true) ?: [], + 'is_active' => isset($access['is_active']) ? (int) $access['is_active'] : null, + 'has_token' => $tokenReady, + 'token_ready' => $tokenReady, + 'failure_reason' => $failureReason, + 'note' => $failureReason ?: 'OK', + ]; + + $preMap = $this->buildPreMapping($preDb, $contact, $access); + $preMaps[] = $preMap; + + if ($preReachable === true) { + if (empty($preMap['PRE_BRANCH_LINKED'])) { + $issues[] = $this->issue( + 'PRE_BRANCH_NOT_LINKED', + 'medium', + "Post branch #{$contact['post_branch_id']} has no valid pre_branch_id link.", + 'Set client_branch.pre_branch_id to the matching pre branch id.' + ); + } elseif (empty($preMap['PRE_HR_MATCHED'])) { + $issues[] = $this->issue( + 'PRE_HR_NOT_MATCHED', + 'medium', + "No pre level_contacts match for email+mobile under pre client #{$preMap['pre_client_id']}.", + 'Align email and mobile between pre and post HR contacts.' + ); + } elseif (empty($preMap['PRE_IDS_CONSISTENT']) && !empty($access)) { + $issues[] = $this->issue( + 'PRE_IDS_INCONSISTENT', + 'medium', + "hr_access_control pre ids do not match live pre data for post_hr_id={$contact['post_hr_id']}.", + 'Re-save HR Access Control so pre_hr_id / pre_client_id / pre_branch_id are refreshed.' + ); + } + } + + $canLoginCard = $flags['BRANCH_ACTIVE'] && $flags['CLIENT_ACTIVE'] && $tokenReady; + $loginCards[] = [ + 'client' => $contact['client_name'] ?? '', + 'branch' => $contact['branch_name'] ?? '', + 'post_hr_id' => (int) $contact['post_hr_id'], + 'post_client_id' => (int) $contact['post_client_id'], + 'post_branch_id' => (int) $contact['post_branch_id'], + 'has_token' => $canLoginCard, + 'failure_reason' => $canLoginCard + ? null + : ($failureReason + ?: (!$flags['BRANCH_ACTIVE'] ? 'BRANCH_INACTIVE' + : (!$flags['CLIENT_ACTIVE'] ? 'CLIENT_INACTIVE' : 'UNKNOWN'))), + ]; + + $contact['hr_found'] = true; + $contact['flags'] = $flags; + } + + $anyToken = false; + foreach ($loginCards as $card) { + if (!empty($card['has_token'])) { + $anyToken = true; + break; + } + } + + $wouldAuth = !empty($contacts); + $overall = ($wouldAuth && $anyToken && empty(array_filter($issues, static fn ($i) => ($i['severity'] ?? '') === 'high'))) + ? 'PASS' + : (($anyToken && $wouldAuth) ? 'WARN' : 'FAIL'); + + if ($overall === 'PASS') { + $summary = 'HR identity, access row, and login readiness look OK.'; + $verdict = 'OK'; + } elseif ($overall === 'WARN') { + $summary = 'Login may work for some cards, but mapping/access issues remain.'; + $verdict = 'OK_WITH_WARNINGS'; + } else { + $summary = 'HR login will fail or return empty token/modules.'; + $verdict = 'WILL_FAIL'; + } + + // Deduplicate issues by code+message + $unique = []; + $deduped = []; + foreach ($issues as $issue) { + $key = ($issue['code'] ?? '') . '|' . ($issue['message'] ?? ''); + if (isset($unique[$key])) { + continue; + } + $unique[$key] = true; + $deduped[] = $issue; + } + + $postContacts = array_map(static function ($c) { + return [ + 'post_hr_id' => (int) $c['post_hr_id'], + 'hr_name' => $c['hr_name'] ?? '', + 'hr_mail' => $c['hr_mail'] ?? '', + 'hr_mobile' => $c['hr_mobile'] ?? '', + 'is_active' => (int) ($c['is_active'] ?? 0), + 'post_branch_id' => (int) ($c['post_branch_id'] ?? 0), + 'branch_name' => $c['branch_name'] ?? '', + 'branch_active' => (int) ($c['branch_active'] ?? 0), + 'pre_branch_id' => $c['pre_branch_id'] ?? null, + 'post_client_id' => (int) ($c['post_client_id'] ?? 0), + 'client_name' => $c['client_name'] ?? '', + 'client_active' => (int) ($c['client_active'] ?? 0), + 'hr_found' => true, + ]; + }, $contacts); + + $firstPost = $postContacts[0] ?? []; + $firstPre = $preMaps[0] ?? []; + $tokenCards = array_filter($loginCards, static fn ($c) => ! empty($c['has_token'])); + $preMatched = array_filter($preMaps, static fn ($m) => ! empty($m['PRE_HR_MATCHED'])); + + return [ + 'input' => ['email' => $email, 'mobile' => $mobile], + 'overall_status' => $overall, + 'verdict' => $verdict, + 'verdict_message' => $summary, + 'summary' => $summary, + 'issues' => $deduped, + 'problems' => $deduped, + 'post' => [ + 'contacts' => $postContacts, + 'raw_found' => count($postContacts) > 0, + 'eligible' => $anyToken, + 'filters_summary' => [ + 'hr_active' => ! empty($firstPost) && (int) ($firstPost['is_active'] ?? 0) === 1, + 'branch_active' => ! empty($firstPost) && (int) ($firstPost['branch_active'] ?? 0) === 1, + 'client_active' => ! empty($firstPost) && (int) ($firstPost['client_active'] ?? 0) === 1, + 'has_access' => count($tokenCards) > 0, + 'token_ready' => $anyToken, + ], + 'primary' => $firstPost, + ], + 'hr_access_control' => $accessRows, + 'pre' => [ + 'reachable' => $preReachable, + 'mappings' => $preMaps, + 'raw_found' => count($preMatched) > 0, + 'eligible' => count($preMatched) > 0, + 'filters_summary' => [ + 'db_reachable' => $preReachable === true, + 'PRE_BRANCH_LINKED' => ! empty($firstPre['PRE_BRANCH_LINKED']), + 'PRE_HR_MATCHED' => ! empty($firstPre['PRE_HR_MATCHED']), + 'PRE_IDS_CONSISTENT' => ! empty($firstPre['PRE_IDS_CONSISTENT']), + 'PRE_CLIENT_MAPPED' => ! empty($firstPre['PRE_CLIENT_MAPPED']), + ], + 'primary' => $firstPre, + 'error' => $preReachable === false ? 'preDB connection failed' : null, + ], + 'login_simulation' => [ + 'would_authenticate' => $wouldAuth, + 'cards' => $loginCards, + ], + 'merge' => [ + 'post_eligible' => $anyToken, + 'pre_eligible' => count($preMatched) > 0, + 'post_contacts' => count($postContacts), + 'pre_matched' => count($preMatched), + 'token_cards' => count($tokenCards), + 'access_rows' => count(array_filter($accessRows, static fn ($a) => ! empty($a['id']))), + 'would_return_both' => $anyToken && count($preMatched) > 0, + 'winner' => ($anyToken && count($preMatched) > 0) ? 'both' : ($anyToken ? 'post' : (count($preMatched) > 0 ? 'pre' : 'none')), + 'reason' => $summary, + ], + ]; + } + + private function fetchPostContacts($db, string $email, string $mobile): array + { + $builder = $db->table('level_contacts lc') + ->select(" + lc.id AS post_hr_id, + lc.name AS hr_name, + lc.email AS hr_mail, + lc.mobile AS hr_mobile, + lc.is_active, + cb.id AS post_branch_id, + cb.branch_name, + cb.is_active AS branch_active, + cb.pre_branch_id, + c.id AS post_client_id, + c.client_name, + c.is_active AS client_active + ") + ->join('client_branch cb', 'cb.id = lc.ref_id', 'left') + ->join('clients c', 'c.id = cb.client_id', 'left') + ->where('lc.contact_type', 'client') + ->where('lc.is_active', 1); + + if ($email !== '' && $mobile !== '') { + $builder->groupStart() + ->where('lc.email', $email) + ->orWhere('lc.mobile', $mobile) + ->groupEnd(); + } elseif ($email !== '') { + $builder->where('lc.email', $email); + } else { + $builder->where('lc.mobile', $mobile); + } + + return $builder->get()->getResultArray(); + } + + private function fetchAccessRow($db, int $postHrId, int $postBranchId, int $postClientId): ?array + { + $row = $db->table('hr_access_control') + ->where('post_hr_id', $postHrId) + ->where('post_branch_id', $postBranchId) + ->where('post_client_id', $postClientId) + ->where('is_active', 1) + ->get() + ->getRowArray(); + + return $row ?: null; + } + + private function hasAnyModule(array $modules): bool + { + if (isset($modules['pre']) || isset($modules['post'])) { + $pre = is_array($modules['pre'] ?? null) ? $modules['pre'] : []; + $post = is_array($modules['post'] ?? null) ? $modules['post'] : []; + + return count($pre) > 0 || count($post) > 0; + } + + return count($modules) > 0; + } + + private function buildPreMapping($preDb, array $contact, ?array $access): array + { + $result = [ + 'post_hr_id' => (int) $contact['post_hr_id'], + 'pre_hr_id' => null, + 'pre_client_id' => null, + 'pre_branch_id' => $contact['pre_branch_id'] ?? null, + 'pre_client_name' => null, + 'pre_branch_name' => null, + 'hr_name' => null, + 'hr_mail' => null, + 'hr_mobile' => null, + 'PRE_BRANCH_LINKED' => false, + 'PRE_HR_MATCHED' => false, + 'PRE_IDS_CONSISTENT' => false, + 'PRE_CLIENT_MAPPED' => false, + 'access_pre_hr_id' => isset($access['pre_hr_id']) ? (int) $access['pre_hr_id'] : null, + 'access_pre_client_id' => isset($access['pre_client_id']) ? (int) $access['pre_client_id'] : null, + 'access_pre_branch_id' => isset($access['pre_branch_id']) ? (int) $access['pre_branch_id'] : null, + ]; + + if ($preDb === null) { + return $result; + } + + $preBranchId = $contact['pre_branch_id'] ?? null; + if (empty($preBranchId)) { + return $result; + } + + $preBranch = $preDb->table('client_branch cb') + ->select('cb.id AS pre_branch_id, cb.client_id AS pre_client_id, cb.branch_name, c.client_name') + ->join('clients c', 'c.id = cb.client_id', 'left') + ->where('cb.id', $preBranchId) + ->where('cb.is_active', 1) + ->where('c.is_active', 1) + ->get() + ->getRowArray(); + + if (empty($preBranch)) { + return $result; + } + + $result['PRE_BRANCH_LINKED'] = true; + $result['pre_branch_id'] = (int) $preBranch['pre_branch_id']; + $result['pre_client_id'] = (int) $preBranch['pre_client_id']; + $result['pre_branch_name'] = $preBranch['branch_name'] ?? null; + $result['pre_client_name'] = $preBranch['client_name'] ?? null; + $result['PRE_CLIENT_MAPPED'] = true; + + $email = trim((string) ($contact['hr_mail'] ?? '')); + $mobile = trim((string) ($contact['hr_mobile'] ?? '')); + + $preHrBuilder = $preDb->table('level_contacts lc') + ->select('lc.id AS pre_hr_id, lc.name, lc.email, lc.mobile') + ->join('client_branch cb', 'cb.id = lc.ref_id') + ->where('lc.contact_type', 'client') + ->where('lc.is_active', 1) + ->where('cb.is_active', 1) + ->where('cb.client_id', $preBranch['pre_client_id']); + + if ($email !== '' && $mobile !== '') { + $preHrBuilder->where('lc.email', $email)->where('lc.mobile', $mobile); + } elseif ($email !== '') { + $preHrBuilder->where('lc.email', $email); + } elseif ($mobile !== '') { + $preHrBuilder->where('lc.mobile', $mobile); + } + + $preHr = $preHrBuilder->get()->getRowArray(); + if (!empty($preHr)) { + $result['PRE_HR_MATCHED'] = true; + $result['pre_hr_id'] = (int) $preHr['pre_hr_id']; + $result['hr_name'] = $preHr['name'] ?? null; + $result['hr_mail'] = $preHr['email'] ?? null; + $result['hr_mobile'] = $preHr['mobile'] ?? null; + } + + if (!empty($access) && $result['PRE_HR_MATCHED']) { + $idsMatch = + (int) ($access['pre_hr_id'] ?? 0) === (int) $result['pre_hr_id'] + && (int) ($access['pre_client_id'] ?? 0) === (int) $result['pre_client_id'] + && (int) ($access['pre_branch_id'] ?? 0) === (int) $result['pre_branch_id']; + $result['PRE_IDS_CONSISTENT'] = $idsMatch; + } elseif (empty($access)) { + $result['PRE_IDS_CONSISTENT'] = false; + } + + return $result; + } + + private function issue(string $code, string $severity, string $message, string $fix = ''): array + { + return [ + 'code' => $code, + 'severity' => $severity, + 'message' => $message, + 'fix' => $fix, + ]; + } +} diff --git a/app/Views/hr_login_diagnose.php b/app/Views/hr_login_diagnose.php new file mode 100644 index 00000000..7df3c90a --- /dev/null +++ b/app/Views/hr_login_diagnose.php @@ -0,0 +1,590 @@ + + + + + +<?= esc($title ?? 'HR Login Diagnostic') ?> + + + +
+ + +
+

HR login diagnostic

+

PRE (enrollment) + POST (live) · hr_access_control · read-only · does not send OTP

+
+ +
+
+
+ + +
+
+ + +
+ + Clear +

Provide at least email or mobile. Checks post identity, hr_access_control, and pre mapping.

+
+
+ + + 'status-ok', + 'OK_WITH_WARNINGS' => 'status-warn', + 'PARTIAL' => 'status-partial', + default => 'status-fail', + }; + $pre = $result['pre'] ?? []; + $post = $result['post'] ?? []; + $merge = $result['merge'] ?? []; + $problems = $result['problems'] ?? ($result['issues'] ?? []); + $accessRows = $result['hr_access_control'] ?? []; + $loginSim = $result['login_simulation'] ?? []; + $postPrimary = $post['primary'] ?? []; + $prePrimary = $pre['primary'] ?? []; + $postFs = $post['filters_summary'] ?? []; + $preFs = $pre['filters_summary'] ?? []; + $mark = static fn (bool $ok) => $ok ? '' : ''; +?> + +
+
+ + +
+ +
+ +
Problems (failed checks only)
+ + + + + + + + + + + + + + + + + + + + +
SeverityCodeMessageFix
No problems detected.
+ +
+
+
PRE (enrollment)
+ +
+ + + + + + + + + + + + + + + + + + + + + + + +
Raw found
Eligible (matched)
preDB reachable
PRE_BRANCH_LINKED
PRE_HR_MATCHED
PRE_IDS_CONSISTENT
PRE_CLIENT_MAPPED
pre_hr_id
name
email
mobile
client
branch
pre_client_id
pre_branch_id
+
+ +
+
POST (live)
+ + + + + + + + + + + + + + + + + + + + +
Raw found + +
Eligible for login
hr active
branch active
client active
has access row
token ready
post_hr_id
name
email
mobile
client
branch
+
+
+ +
+ + + + + + +
+ +
+ + + + + + + + + + +
POST eligible
PRE eligible
POST contacts
PRE matched
Access rows with id
Token-ready cards
Would return both
Winner
Reason
+
+ +
+
+
hr_access_control ()
+ +

No access rows evaluated.

+ +
+ + + + + + + + + + + + + + + + + + + + + + + +
Access IDpost_hr_idBranch / ClientModulesToken readyNote
b# / c#
+
+ +
+
+ +
+
+
Login simulation (mirrors getVerifiedHrData lookup)
+ + + + + +
Would authenticate (OTP identity)Yes' : 'No' ?>
+ + +

No login cards.

+ +
+ + + + + + + + + + + + + + + + + + + +
ClientBranchHas tokenFailure
+
+ +
+
+ +
+
+
Pre mapping rows
+ + +

No pre mappings.

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
post_hr_idpre_hr_idNameEmailMobileClientBranchFlags
+ branch +   hr +   ids +
+
+ +
+
+ +
+
+
POST level_contacts
+ + +

No post contacts.

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
post_hr_idNameEmailMobileBranchClientFlags
+ hr +   branch +   client +
+
+ +
+
+ +
+
+
+ + + + +
+
+ WILL_FAIL + No diagnostic result returned. +
+
+ + +
+ +