FIX_TMP_CLEAN_UP

This commit is contained in:
VENKATESHWARAN 2026-08-05 10:30:55 +05:30
parent b36a648995
commit 3e4447003a
17 changed files with 857 additions and 125 deletions

View File

@ -240,11 +240,15 @@ class StorageUploadTest extends BaseCommand
{
$path = storage_local_path($module, $subFolder, $fileName);
if (! is_file($path)) {
throw new FileStorageException('Local processing path not readable: ' . $path);
}
try {
if (! is_file($path)) {
throw new FileStorageException('Local processing path not readable: ' . $path);
}
CLI::write(' local path: ' . $path, 'white');
CLI::write(' local path: ' . $path, 'white');
} finally {
storage_cleanup_temp($path);
}
}
private function assertTemporaryUrl(string $module, string $subFolder, string $fileName): void

View File

@ -277,6 +277,13 @@ $routes->group('storage/browser', static function ($routes) {
$routes->get('download', 'StorageBrowserController::download');
});
$routes->group('logs', static function ($routes) {
$routes->get('/', 'LogViewerController::index');
$routes->get('list', 'LogViewerController::list');
$routes->get('view', 'LogViewerController::view');
$routes->get('download', 'LogViewerController::download');
});
$routes->get('checkPolicyDoc', 'PolicyController::readFile');
$routes->get('calculateCommission', 'PolicyController::calculateCommission');

View File

@ -86,6 +86,8 @@ class AgentIncentiveController extends ResourceController
// -------------------------------------------------------------------------
public function uploadGridFile()
{
$localPath = null;
try {
/* ====================================================================
* STEP 1 Validate POST input
@ -342,6 +344,8 @@ class AgentIncentiveController extends ResourceController
} catch (\Exception $e) {
return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500);
} finally {
storage_cleanup_temp($localPath);
}
}
// public function uploadGridFile_old()

View File

@ -1209,70 +1209,75 @@ class InvoiceController extends ResourceController
'created_by' => $updatedBy > 0 ? $updatedBy : null,
], true);
$localPath = storage_local_path('agent', 'incentive_file', $storedFileName);
$spreadsheet = IOFactory::load($localPath);
$excelRows = $spreadsheet->getActiveSheet()->toArray(null, true, true, false);
$localPath = null;
try {
$localPath = storage_local_path('agent', 'incentive_file', $storedFileName);
$spreadsheet = IOFactory::load($localPath);
$excelRows = $spreadsheet->getActiveSheet()->toArray(null, true, true, false);
if (empty($excelRows)) {
return $this->respond([
'status' => 'failed',
'code' => 400,
'message' => 'Uploaded file is empty',
], 400);
}
$headerRow = $excelRows[0] ?? [];
$headerIndex = [];
foreach ($headerRow as $idx => $headerValue) {
$normalized = preg_replace('/[^a-z0-9]/', '', strtolower(trim((string)$headerValue)));
if (!empty($normalized)) {
$headerIndex[$normalized] = (int)$idx;
}
}
$policyIdx = $headerIndex['policynumber'] ?? null;
$invoiceIdx = $headerIndex['invoicenumber'] ?? null;
$commissionIdx = $headerIndex['commission'] ?? ($headerIndex['commissionamount'] ?? null);
if ($policyIdx === null || $commissionIdx === null) {
return $this->respond([
'status' => 'failed',
'code' => 400,
'message' => 'Expected columns: Policy Number, Invoice Number, Commission or Commission Amount',
], 400);
}
$fallbackInvoiceNo = trim((string)($post['invoice_number'] ?? ''));
$rows = [];
foreach ($excelRows as $index => $row) {
if ($index === 0) {
continue;
}
$policyNo = trim((string)($row[$policyIdx] ?? ''));
$invoiceNoFromFile = $invoiceIdx !== null ? trim((string)($row[$invoiceIdx] ?? '')) : '';
$invoiceNo = $invoiceNoFromFile !== '' ? $invoiceNoFromFile : $fallbackInvoiceNo;
$commissionRaw = trim((string)($row[$commissionIdx] ?? ''));
if ($policyNo === '' && $invoiceNo === '' && $commissionRaw === '') {
continue;
}
$commission = (float)str_replace(',', '', $commissionRaw);
if ($policyNo === '' || $invoiceNo === '' || !is_numeric(str_replace(',', '', $commissionRaw))) {
if (empty($excelRows)) {
return $this->respond([
'status' => 'failed',
'code' => 400,
'message' => 'Invalid data at line ' . ($index + 1),
'message' => 'Uploaded file is empty',
], 400);
}
$rows[] = [
'line_no' => $index + 1,
'policy_number' => $policyNo,
'invoice_no' => $invoiceNo,
'commission_amount' => $commission,
];
$headerRow = $excelRows[0] ?? [];
$headerIndex = [];
foreach ($headerRow as $idx => $headerValue) {
$normalized = preg_replace('/[^a-z0-9]/', '', strtolower(trim((string)$headerValue)));
if (!empty($normalized)) {
$headerIndex[$normalized] = (int)$idx;
}
}
$policyIdx = $headerIndex['policynumber'] ?? null;
$invoiceIdx = $headerIndex['invoicenumber'] ?? null;
$commissionIdx = $headerIndex['commission'] ?? ($headerIndex['commissionamount'] ?? null);
if ($policyIdx === null || $commissionIdx === null) {
return $this->respond([
'status' => 'failed',
'code' => 400,
'message' => 'Expected columns: Policy Number, Invoice Number, Commission or Commission Amount',
], 400);
}
$fallbackInvoiceNo = trim((string)($post['invoice_number'] ?? ''));
$rows = [];
foreach ($excelRows as $index => $row) {
if ($index === 0) {
continue;
}
$policyNo = trim((string)($row[$policyIdx] ?? ''));
$invoiceNoFromFile = $invoiceIdx !== null ? trim((string)($row[$invoiceIdx] ?? '')) : '';
$invoiceNo = $invoiceNoFromFile !== '' ? $invoiceNoFromFile : $fallbackInvoiceNo;
$commissionRaw = trim((string)($row[$commissionIdx] ?? ''));
if ($policyNo === '' && $invoiceNo === '' && $commissionRaw === '') {
continue;
}
$commission = (float)str_replace(',', '', $commissionRaw);
if ($policyNo === '' || $invoiceNo === '' || !is_numeric(str_replace(',', '', $commissionRaw))) {
return $this->respond([
'status' => 'failed',
'code' => 400,
'message' => 'Invalid data at line ' . ($index + 1),
], 400);
}
$rows[] = [
'line_no' => $index + 1,
'policy_number' => $policyNo,
'invoice_no' => $invoiceNo,
'commission_amount' => $commission,
];
}
} finally {
storage_cleanup_temp($localPath);
}
}

View File

@ -0,0 +1,175 @@
<?php
namespace App\Controllers;
use CodeIgniter\API\ResponseTrait;
use CodeIgniter\HTTP\DownloadResponse;
use CodeIgniter\HTTP\ResponseInterface;
class LogViewerController extends BaseController
{
use ResponseTrait;
private string $logsPath;
public function __construct()
{
$this->logsPath = rtrim(WRITEPATH . 'logs', DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
}
public function index(): string
{
return view('logs/index');
}
public function list(): ResponseInterface
{
$search = trim((string) ($this->request->getGet('q') ?? ''));
$page = max(1, (int) ($this->request->getGet('page') ?? 1));
$limit = max(1, min(50, (int) ($this->request->getGet('limit') ?? 10)));
$all = $this->listLogFiles($search !== '' ? $search : null);
$total = count($all);
$offset = ($page - 1) * $limit;
$files = array_slice($all, $offset, $limit);
$pages = $total > 0 ? (int) ceil($total / $limit) : 0;
return $this->respond([
'status' => 'success',
'files' => $files,
'page' => $page,
'limit' => $limit,
'total' => $total,
'total_pages'=> $pages,
'has_more' => $page < $pages,
]);
}
public function view(): string|ResponseInterface
{
$file = $this->resolveLogFile((string) ($this->request->getGet('file') ?? ''));
if ($file === null) {
return $this->response->setStatusCode(404)->setBody('Log file not found.');
}
$content = @file_get_contents($file['path']);
if ($content === false) {
return $this->response->setStatusCode(500)->setBody('Unable to read log file.');
}
$maxBytes = 2 * 1024 * 1024;
$truncated = false;
if (strlen($content) > $maxBytes) {
$content = substr($content, -$maxBytes);
$truncated = true;
}
return view('logs/view', [
'file' => $file,
'content' => $content,
'truncated' => $truncated,
'maxBytes' => $maxBytes,
]);
}
public function download(): DownloadResponse|ResponseInterface
{
$file = $this->resolveLogFile((string) ($this->request->getGet('file') ?? ''));
if ($file === null) {
return $this->response->setStatusCode(404)->setBody('Log file not found.');
}
return $this->response->download($file['path'], null)->setFileName($file['name']);
}
/**
* @return list<array{name: string, size: int, size_human: string, modified: string, modified_ts: int}>
*/
private function listLogFiles(?string $search = null): array
{
if (! is_dir($this->logsPath)) {
return [];
}
$files = glob($this->logsPath . 'log-*.log') ?: [];
$result = [];
$needle = $search !== null ? strtolower($search) : null;
foreach ($files as $path) {
if (! is_file($path)) {
continue;
}
$name = basename($path);
if ($needle !== null && ! str_contains(strtolower($name), $needle)) {
continue;
}
$size = (int) filesize($path);
$mtime = (int) filemtime($path);
$result[] = [
'name' => $name,
'size' => $size,
'size_human' => $this->formatBytes($size),
'modified' => date('Y-m-d H:i:s', $mtime),
'modified_ts' => $mtime,
];
}
usort($result, static fn (array $a, array $b): int => $b['modified_ts'] <=> $a['modified_ts']);
return $result;
}
/**
* @return array{name: string, path: string, size: int, size_human: string, modified: string}|null
*/
private function resolveLogFile(string $name): ?array
{
$name = basename(trim($name));
if ($name === '' || ! preg_match('/^log-\d{4}-\d{2}-\d{2}\.log$/', $name)) {
return null;
}
$path = $this->logsPath . $name;
$realPath = realpath($path);
$realLogs = realpath($this->logsPath);
if ($realPath === false || $realLogs === false || ! str_starts_with($realPath, $realLogs) || ! is_file($realPath)) {
return null;
}
$size = (int) filesize($realPath);
return [
'name' => $name,
'path' => $realPath,
'size' => $size,
'size_human' => $this->formatBytes($size),
'modified' => date('Y-m-d H:i:s', (int) filemtime($realPath)),
];
}
private function formatBytes(int $bytes): string
{
if ($bytes < 1024) {
return $bytes . ' B';
}
$units = ['KB', 'MB', 'GB'];
$value = (float) $bytes;
foreach ($units as $unit) {
$value /= 1024;
if ($value < 1024) {
return round($value, 2) . ' ' . $unit;
}
}
return round($value / 1024, 2) . ' TB';
}
}

View File

@ -738,6 +738,7 @@ class PolicyController extends ResourceController
public function checkPolicyDoc($policyId = null , $return = null)
{
$pdfFilePath = null;
try
{
@ -794,7 +795,7 @@ class PolicyController extends ResourceController
]), JSON_UNESCAPED_SLASHES));
if (!$pdfExists) {
$missingPath = policy_local_pdf_path($pdfFileName);
$missingPath = storage()->resolveKey('policy', 'policy_pdf', $pdfFileName);
log_message('error', '[S3_FILE_GET][POLICY_READ][FAILED] policy PDF not found in storage | ' . json_encode(array_merge($s3LogCtx, [
'pdf_file_name' => $pdfFileName,
'resolved_path' => $missingPath,
@ -1089,6 +1090,8 @@ class PolicyController extends ResourceController
}else{
return ['status'=>"failed", 'message'=> "Error: " . $e->getMessage()];
}
} finally {
storage_cleanup_temp($pdfFilePath);
}
}

View File

@ -151,6 +151,7 @@ class PolicyRagController extends ResourceController
public function readPolicyDocViaRag($policyId = null)
{
$fileId = null;
$pdfFilePath = null;
$this->logRagStep('READ_START', 'started', 'readPolicyDocViaRag', ['policy_id' => $policyId]);
@ -170,9 +171,11 @@ class PolicyRagController extends ResourceController
$pdfFileName = $record['policy_pdf_file_name'] ?? '';
if ($pdfFileName === '' || !policy_exists_by_type($pdfFileName, 'policy_pdf')) {
$pdfFilePath = $pdfFileName !== '' ? policy_local_pdf_path($pdfFileName) : '';
$this->logRagStep('PDF_CHECK', 'failed', 'PDF file not found', ['path' => $pdfFilePath]);
return $this->formatReadResponse('failed', "File not found at {$pdfFilePath}");
$missingPath = $pdfFileName !== ''
? storage()->resolveKey('policy', 'policy_pdf', $pdfFileName)
: '';
$this->logRagStep('PDF_CHECK', 'failed', 'PDF file not found', ['path' => $missingPath]);
return $this->formatReadResponse('failed', "File not found at {$missingPath}");
}
$pdfFilePath = policy_local_pdf_path($pdfFileName);
@ -255,6 +258,8 @@ class PolicyRagController extends ResourceController
]);
return $this->formatReadResponse('failed', 'Error: ' . $e->getMessage());
} finally {
storage_cleanup_temp($pdfFilePath);
if (!empty($fileId)) {
$this->logRagStep('DELETE', 'started', 'Deleting RAG file', [
'policy_id' => $policyId,

View File

@ -58,6 +58,17 @@ if (!function_exists('storage_local_path')) {
}
}
if (!function_exists('storage_cleanup_temp')) {
/**
* Remove an S3 downloadToTemp working copy under the OS temp dir.
* Safe no-op for local writable paths and missing files.
*/
function storage_cleanup_temp(?string $path): bool
{
return storage()->cleanupTempPath($path);
}
}
if (!function_exists('storage_delete')) {
function storage_delete(string $module, string $subFolder, string $fileName): bool
{

View File

@ -381,6 +381,58 @@ class FileStorageService
}
}
/**
* Delete a path returned by getLocalPathForProcessing / downloadToTemp when it lives
* under the OS temp directory (S3 working copies). Never deletes local writable/uploads files.
*/
public function cleanupTempPath(?string $path): bool
{
if ($path === null || $path === '') {
return false;
}
if (! is_file($path)) {
return false;
}
if (! $this->isS3TempProcessingPath($path)) {
return false;
}
$deleted = @unlink($path);
StorageLogger::log($deleted ? 'SUCCESS' : 'WARNING', 'Temp processing path cleanup', $this->baseContext([
'path' => $path,
'deleted' => $deleted,
]));
return $deleted;
}
/**
* True for S3 downloadToTemp paths like /tmp/s3_<uniqid>_<basename>.
*/
public function isS3TempProcessingPath(string $path): bool
{
$tempRoot = rtrim(str_replace(['/', '\\'], DIRECTORY_SEPARATOR, sys_get_temp_dir()), DIRECTORY_SEPARATOR);
$normalized = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $path);
$realTemp = realpath(sys_get_temp_dir());
if ($realTemp !== false) {
$tempRoot = rtrim(str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $realTemp), DIRECTORY_SEPARATOR);
}
$realPath = realpath($path);
$checkPath = $realPath !== false
? str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $realPath)
: $normalized;
if (! str_starts_with($checkPath, $tempRoot . DIRECTORY_SEPARATOR)) {
return false;
}
return str_starts_with(basename($checkPath), 's3_');
}
public function getTemporaryUrl(string $module, string $subFolder, string $fileName, ?int $ttlSeconds = null): string
{
return $this->getTemporaryUrlForKey(

331
app/Views/logs/index.php Normal file
View File

@ -0,0 +1,331 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Application Logs</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
:root {
--brand: #0d9488;
--brand-dark: #0f766e;
--brand-light: #ccfbf1;
}
body {
background: #f8fafc;
font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
color: #0f172a;
}
.page-wrap {
max-width: 1000px;
margin: 0 auto;
padding: 2rem 1rem 3rem;
}
.page-header {
margin-bottom: 1.5rem;
}
.page-title {
font-size: 1.75rem;
font-weight: 700;
margin: 0;
}
.page-subtitle {
color: #64748b;
margin: 0.35rem 0 0;
}
.panel {
background: #fff;
border: 1px solid #e2e8f0;
border-radius: 12px;
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
overflow: hidden;
}
.filters {
padding: 1.25rem;
display: grid;
grid-template-columns: 1fr auto;
gap: 1rem;
align-items: end;
border-bottom: 1px solid #e2e8f0;
}
.form-label {
font-size: 0.85rem;
font-weight: 600;
color: #334155;
}
.btn-brand {
background: var(--brand);
border-color: var(--brand);
color: #fff;
min-width: 110px;
}
.btn-brand:hover,
.btn-brand:focus {
background: var(--brand-dark);
border-color: var(--brand-dark);
color: #fff;
}
.table thead th {
font-size: 0.75rem;
letter-spacing: 0.04em;
text-transform: uppercase;
color: #64748b;
border-bottom-color: #e2e8f0;
background: #f8fafc;
}
.table tbody td {
vertical-align: middle;
border-bottom-color: #f1f5f9;
}
.empty-state {
text-align: center;
padding: 3rem 1rem;
color: #64748b;
}
.footer-bar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.9rem 1.25rem;
border-top: 1px solid #e2e8f0;
color: #64748b;
font-size: 0.9rem;
gap: 1rem;
flex-wrap: wrap;
}
.alert-inline {
margin: 1rem 1.25rem 0;
}
@media (max-width: 768px) {
.filters {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="page-wrap">
<div class="page-header">
<h1 class="page-title">Application Logs</h1>
<p class="page-subtitle">View or download daily log files. Results load 10 at a time.</p>
</div>
<div class="panel">
<div id="alertBox"></div>
<div class="filters">
<div>
<label for="searchInput" class="form-label">File name</label>
<input id="searchInput" type="text" class="form-control" placeholder="Search file name...">
</div>
<div>
<button id="searchBtn" class="btn btn-brand w-100">Search</button>
</div>
</div>
<div class="table-responsive">
<table class="table mb-0">
<thead>
<tr>
<th>File name</th>
<th>Size</th>
<th>Last modified</th>
<th>Action</th>
</tr>
</thead>
<tbody id="logsBody">
<tr>
<td colspan="4" class="text-center py-4 text-muted">Loading logs...</td>
</tr>
</tbody>
</table>
</div>
<div class="footer-bar">
<div id="pageInfo">Page </div>
<div class="d-flex gap-2">
<button id="prevBtn" class="btn btn-outline-secondary btn-sm" disabled>Previous</button>
<button id="nextBtn" class="btn btn-brand btn-sm" disabled>Next</button>
</div>
</div>
</div>
</div>
<script>
const baseUrl = <?= json_encode(base_url()) ?>.replace(/\/$/, '');
const apiBase = baseUrl + '/logs';
const searchInput = document.getElementById('searchInput');
const searchBtn = document.getElementById('searchBtn');
const logsBody = document.getElementById('logsBody');
const pageInfo = document.getElementById('pageInfo');
const prevBtn = document.getElementById('prevBtn');
const nextBtn = document.getElementById('nextBtn');
const alertBox = document.getElementById('alertBox');
let currentSearch = '';
let pageNumber = 1;
let totalPages = 0;
let totalFiles = 0;
function showAlert(message, type = 'danger') {
alertBox.innerHTML = `<div class="alert alert-${type} alert-inline mb-0">${escapeHtml(message)}</div>`;
}
function clearAlert() {
alertBox.innerHTML = '';
}
function escapeHtml(value) {
return String(value)
.replaceAll('&', '&amp;')
.replaceAll('<', '&lt;')
.replaceAll('>', '&gt;')
.replaceAll('"', '&quot;')
.replaceAll("'", '&#39;');
}
function renderEmpty(message) {
logsBody.innerHTML = `
<tr>
<td colspan="4">
<div class="empty-state">${escapeHtml(message)}</div>
</td>
</tr>`;
}
function renderLoading() {
logsBody.innerHTML = `
<tr>
<td colspan="4" class="text-center py-4 text-muted">Loading logs...</td>
</tr>`;
}
function renderFiles(files) {
if (!files.length) {
renderEmpty('No log files found');
return;
}
logsBody.innerHTML = files.map((file) => {
const viewUrl = `${apiBase}/view?file=${encodeURIComponent(file.name)}`;
const downloadUrl = `${apiBase}/download?file=${encodeURIComponent(file.name)}`;
return `
<tr>
<td><code>${escapeHtml(file.name)}</code></td>
<td>${escapeHtml(file.size_human)}</td>
<td>${escapeHtml(file.modified || '—')}</td>
<td>
<a class="btn btn-sm btn-outline-primary me-1" href="${viewUrl}" target="_blank" rel="noopener">View</a>
<a class="btn btn-sm btn-brand" href="${downloadUrl}">Download</a>
</td>
</tr>`;
}).join('');
}
function updatePagination() {
if (totalFiles === 0) {
pageInfo.textContent = 'No results';
} else {
pageInfo.textContent = `Page ${pageNumber} of ${totalPages} · ${totalFiles} file${totalFiles === 1 ? '' : 's'}`;
}
prevBtn.disabled = pageNumber <= 1;
nextBtn.disabled = pageNumber >= totalPages || totalPages === 0;
}
async function loadLogs(resetPage = true) {
clearAlert();
if (resetPage) {
pageNumber = 1;
}
renderLoading();
const params = new URLSearchParams({
page: String(pageNumber),
limit: '10',
});
if (currentSearch) {
params.set('q', currentSearch);
}
try {
const response = await fetch(`${apiBase}/list?${params.toString()}`);
const data = await response.json();
if (data.status !== 'success') {
showAlert(data.message || 'Unable to load logs');
renderEmpty('Unable to load logs');
totalPages = 0;
totalFiles = 0;
updatePagination();
return;
}
totalPages = data.total_pages || 0;
totalFiles = data.total || 0;
pageNumber = data.page || pageNumber;
renderFiles(data.files || []);
updatePagination();
} catch (error) {
showAlert('Network error while loading logs');
renderEmpty('Unable to load logs');
totalPages = 0;
totalFiles = 0;
updatePagination();
}
}
function runSearch() {
currentSearch = searchInput.value.trim();
loadLogs(true);
}
searchBtn.addEventListener('click', runSearch);
searchInput.addEventListener('keydown', (event) => {
if (event.key === 'Enter') {
runSearch();
}
});
prevBtn.addEventListener('click', () => {
if (pageNumber <= 1) {
return;
}
pageNumber -= 1;
loadLogs(false);
});
nextBtn.addEventListener('click', () => {
if (pageNumber >= totalPages) {
return;
}
pageNumber += 1;
loadLogs(false);
});
loadLogs(true);
</script>
</body>
</html>

139
app/Views/logs/view.php Normal file
View File

@ -0,0 +1,139 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= esc($file['name']) ?> — Log Viewer</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
:root {
--brand: #0d9488;
--brand-dark: #0f766e;
--brand-light: #ccfbf1;
}
body {
background: #0f172a;
color: #e2e8f0;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
margin: 0;
}
.topbar {
position: sticky;
top: 0;
z-index: 10;
background: #1e293b;
border-bottom: 1px solid #334155;
padding: 0.75rem 1rem;
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
flex-wrap: wrap;
}
.meta {
display: flex;
flex-direction: column;
gap: 0.15rem;
}
.meta h1 {
font-size: 1rem;
font-weight: 700;
margin: 0;
color: #f8fafc;
font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
}
.meta span {
font-size: 0.8rem;
color: #94a3b8;
font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
}
.actions {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
}
.btn-brand {
background: var(--brand);
border-color: var(--brand);
color: #fff;
}
.btn-brand:hover,
.btn-brand:focus {
background: var(--brand-dark);
border-color: var(--brand-dark);
color: #fff;
}
.truncate-note {
background: #422006;
color: #fde68a;
border-bottom: 1px solid #854d0e;
padding: 0.55rem 1rem;
font-size: 0.85rem;
font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
}
.log-body {
padding: 1rem;
white-space: pre-wrap;
word-break: break-word;
font-size: 0.82rem;
line-height: 1.45;
margin: 0;
}
.level-error { color: #fca5a5; }
.level-critical, .level-emergency, .level-alert { color: #fb7185; }
.level-warning { color: #fcd34d; }
.level-info, .level-notice { color: #7dd3fc; }
.level-debug { color: #a5b4fc; }
</style>
</head>
<body>
<div class="topbar">
<div class="meta">
<h1><?= esc($file['name']) ?></h1>
<span><?= esc($file['size_human']) ?> · modified <?= esc($file['modified']) ?></span>
</div>
<div class="actions">
<a class="btn btn-sm btn-outline-light" href="<?= esc(site_url('logs')) ?>"> All logs</a>
<a class="btn btn-sm btn-brand" href="<?= esc(site_url('logs/download?file=' . urlencode($file['name']))) ?>">Download</a>
</div>
</div>
<?php if (! empty($truncated)): ?>
<div class="truncate-note">
Showing the last <?= number_format((int) $maxBytes) ?> bytes of this file. Use Download for the full log.
</div>
<?php endif; ?>
<pre class="log-body" id="logContent"><?= esc($content) ?></pre>
<script>
// Soft-highlight log levels in the pre content without changing text
(function () {
const el = document.getElementById('logContent');
if (!el) return;
const html = el.textContent
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/\b(ERROR|CRITICAL|EMERGENCY|ALERT|WARNING|INFO|NOTICE|DEBUG)\b/g, (match) => {
return `<span class="level-${match.toLowerCase()}">${match}</span>`;
});
el.innerHTML = html;
window.scrollTo(0, document.body.scrollHeight);
})();
</script>
</body>
</html>

View File

@ -1 +1 @@
{"version":2,"defects":{"Tests\\Unit\\Storage\\FileStorageServiceTest::testUploadModuleFileStoresPolicyPdf":8,"Tests\\Unit\\Storage\\FileStorageServiceTest::testDownloadReturnsAttachmentResponse":7,"Tests\\Unit\\Storage\\LocalStorageDriverTest::testUploadFromUploadedFileStoresUnderLocalRoot":8,"Tests\\Unit\\Storage\\LocalStorageDriverTest::testStreamDownloadReturnsResponseWithBody":7,"Tests\\Unit\\Storage\\PolicyStorageHelperTest::testPolicyUploadPdfStoresFile":8,"Tests\\Unit\\Storage\\PolicyStorageHelperTest::testPolicyUploadReceiptStoresFile":8,"Tests\\Unit\\Storage\\PolicyStorageHelperTest::testPolicyDownloadByTypeAttachment":7,"Tests\\Unit\\Storage\\PolicyStorageHelperTest::testPolicyDownloadByTypeInline":7,"Tests\\Unit\\Storage\\S3StorageIntegrationTest::testS3PolicyPdfUploadExistsReadAndDelete":1,"Tests\\Unit\\Storage\\S3StorageIntegrationTest::testS3PolicyReceiptUploadAndTemporaryUrl":1,"Tests\\Unit\\Storage\\S3StorageIntegrationTest::testS3EndorsementOriginalAndCompletionUpload":1,"Tests\\Unit\\Storage\\S3StorageIntegrationTest::testLocalDriverScenarioStillWorksWhenEnvIsLocal":1,"Tests\\Unit\\Storage\\S3StorageIntegrationTest::testS3DriverIsActiveFromEnv":1,"Tests\\Unit\\Storage\\S3StorageIntegrationTest::testS3PolicyMarkdownPutAndRead":1},"times":{"Tests\\Unit\\Storage\\FileStorageServiceTest::testResolveKeyWithSubFolder":0.006,"Tests\\Unit\\Storage\\FileStorageServiceTest::testResolveKeyWithoutSubFolder":0.001,"Tests\\Unit\\Storage\\FileStorageServiceTest::testUploadModuleFileStoresPolicyPdf":0.006,"Tests\\Unit\\Storage\\FileStorageServiceTest::testReadWriteAndDeleteByModule":0.003,"Tests\\Unit\\Storage\\FileStorageServiceTest::testGetLocalPathForProcessingPointsToWritableFile":0.002,"Tests\\Unit\\Storage\\FileStorageServiceTest::testCopyBetweenKeys":0.002,"Tests\\Unit\\Storage\\FileStorageServiceTest::testDownloadReturnsAttachmentResponse":0.005,"Tests\\Unit\\Storage\\FileStorageServiceTest::testGetTemporaryUrlForLocalUsesBaseUrl":0.003,"Tests\\Unit\\Storage\\LocalStorageDriverTest::testUploadFromUploadedFileStoresUnderLocalRoot":0.002,"Tests\\Unit\\Storage\\LocalStorageDriverTest::testUploadFromLocalPathCopiesFile":0.002,"Tests\\Unit\\Storage\\LocalStorageDriverTest::testPutWriteRawContents":0.002,"Tests\\Unit\\Storage\\LocalStorageDriverTest::testExistsReturnsFalseForMissingFile":0.001,"Tests\\Unit\\Storage\\LocalStorageDriverTest::testDeleteRemovesFile":0.001,"Tests\\Unit\\Storage\\LocalStorageDriverTest::testDownloadToTempReturnsExistingLocalPath":0.001,"Tests\\Unit\\Storage\\LocalStorageDriverTest::testCopyDuplicatesFile":0.002,"Tests\\Unit\\Storage\\LocalStorageDriverTest::testTemporaryUrlUsesBaseUrlForLocalDriver":0.002,"Tests\\Unit\\Storage\\LocalStorageDriverTest::testStreamDownloadReturnsResponseWithBody":0.002,"Tests\\Unit\\Storage\\PolicyStorageHelperTest::testPolicyFileTypeMapContainsExpectedTypes":0.001,"Tests\\Unit\\Storage\\PolicyStorageHelperTest::testPolicyMdFileNameConvertsPdfToMd":0.001,"Tests\\Unit\\Storage\\PolicyStorageHelperTest::testPolicyUploadPdfStoresFile":0.003,"Tests\\Unit\\Storage\\PolicyStorageHelperTest::testPolicyUploadReceiptStoresFile":0.003,"Tests\\Unit\\Storage\\PolicyStorageHelperTest::testPolicyUploadReturnsNullWhenNoFile":0.001,"Tests\\Unit\\Storage\\PolicyStorageHelperTest::testPolicyExistsByTypeReturnsFalseForInvalidType":0.001,"Tests\\Unit\\Storage\\PolicyStorageHelperTest::testPolicyDownloadByTypeAttachment":0.002,"Tests\\Unit\\Storage\\PolicyStorageHelperTest::testPolicyDownloadByTypeInline":0.002,"Tests\\Unit\\Storage\\PolicyStorageHelperTest::testPolicyDownloadByTypeThrowsForInvalidType":0.001,"Tests\\Unit\\Storage\\PolicyStorageHelperTest::testPolicyLocalPdfPathResolvesStoredPdf":0.002,"Tests\\Unit\\Storage\\PolicyStorageHelperTest::testPolicyMarkdownPutReadAndExists":0.002,"Tests\\Unit\\Storage\\S3StorageIntegrationTest::testS3DriverIsActiveFromEnv":0.097,"Tests\\Unit\\Storage\\S3StorageIntegrationTest::testS3PolicyPdfUploadExistsReadAndDelete":0.4,"Tests\\Unit\\Storage\\S3StorageIntegrationTest::testS3PolicyReceiptUploadAndTemporaryUrl":0.342,"Tests\\Unit\\Storage\\S3StorageIntegrationTest::testS3PolicyMarkdownPutAndRead":0.419,"Tests\\Unit\\Storage\\S3StorageIntegrationTest::testS3EndorsementOriginalAndCompletionUpload":0.534,"Tests\\Unit\\Storage\\S3StorageIntegrationTest::testLocalDriverScenarioStillWorksWhenEnvIsLocal":0.011}}
{"version":2,"defects":{"Tests\\Unit\\Storage\\FileStorageServiceTest::testUploadModuleFileStoresPolicyPdf":8,"Tests\\Unit\\Storage\\FileStorageServiceTest::testDownloadReturnsAttachmentResponse":7,"Tests\\Unit\\Storage\\LocalStorageDriverTest::testUploadFromUploadedFileStoresUnderLocalRoot":8,"Tests\\Unit\\Storage\\LocalStorageDriverTest::testStreamDownloadReturnsResponseWithBody":7,"Tests\\Unit\\Storage\\PolicyStorageHelperTest::testPolicyUploadPdfStoresFile":8,"Tests\\Unit\\Storage\\PolicyStorageHelperTest::testPolicyUploadReceiptStoresFile":8,"Tests\\Unit\\Storage\\PolicyStorageHelperTest::testPolicyDownloadByTypeAttachment":7,"Tests\\Unit\\Storage\\PolicyStorageHelperTest::testPolicyDownloadByTypeInline":7,"Tests\\Unit\\Storage\\S3StorageIntegrationTest::testS3PolicyPdfUploadExistsReadAndDelete":1,"Tests\\Unit\\Storage\\S3StorageIntegrationTest::testS3PolicyReceiptUploadAndTemporaryUrl":1,"Tests\\Unit\\Storage\\S3StorageIntegrationTest::testS3EndorsementOriginalAndCompletionUpload":1,"Tests\\Unit\\Storage\\S3StorageIntegrationTest::testLocalDriverScenarioStillWorksWhenEnvIsLocal":1,"Tests\\Unit\\Storage\\S3StorageIntegrationTest::testS3DriverIsActiveFromEnv":1,"Tests\\Unit\\Storage\\S3StorageIntegrationTest::testS3PolicyMarkdownPutAndRead":1},"times":{"Tests\\Unit\\Storage\\FileStorageServiceTest::testResolveKeyWithSubFolder":0.004,"Tests\\Unit\\Storage\\FileStorageServiceTest::testResolveKeyWithoutSubFolder":0.001,"Tests\\Unit\\Storage\\FileStorageServiceTest::testUploadModuleFileStoresPolicyPdf":0.013,"Tests\\Unit\\Storage\\FileStorageServiceTest::testReadWriteAndDeleteByModule":0.002,"Tests\\Unit\\Storage\\FileStorageServiceTest::testGetLocalPathForProcessingPointsToWritableFile":0.002,"Tests\\Unit\\Storage\\FileStorageServiceTest::testCopyBetweenKeys":0.003,"Tests\\Unit\\Storage\\FileStorageServiceTest::testDownloadReturnsAttachmentResponse":0.005,"Tests\\Unit\\Storage\\FileStorageServiceTest::testGetTemporaryUrlForLocalUsesBaseUrl":0.004,"Tests\\Unit\\Storage\\LocalStorageDriverTest::testUploadFromUploadedFileStoresUnderLocalRoot":0.002,"Tests\\Unit\\Storage\\LocalStorageDriverTest::testUploadFromLocalPathCopiesFile":0.001,"Tests\\Unit\\Storage\\LocalStorageDriverTest::testPutWriteRawContents":0.001,"Tests\\Unit\\Storage\\LocalStorageDriverTest::testExistsReturnsFalseForMissingFile":0.001,"Tests\\Unit\\Storage\\LocalStorageDriverTest::testDeleteRemovesFile":0.001,"Tests\\Unit\\Storage\\LocalStorageDriverTest::testDownloadToTempReturnsExistingLocalPath":0.001,"Tests\\Unit\\Storage\\LocalStorageDriverTest::testCopyDuplicatesFile":0.002,"Tests\\Unit\\Storage\\LocalStorageDriverTest::testTemporaryUrlUsesBaseUrlForLocalDriver":0.003,"Tests\\Unit\\Storage\\LocalStorageDriverTest::testStreamDownloadReturnsResponseWithBody":0.002,"Tests\\Unit\\Storage\\PolicyStorageHelperTest::testPolicyFileTypeMapContainsExpectedTypes":0.001,"Tests\\Unit\\Storage\\PolicyStorageHelperTest::testPolicyMdFileNameConvertsPdfToMd":0.001,"Tests\\Unit\\Storage\\PolicyStorageHelperTest::testPolicyUploadPdfStoresFile":0.003,"Tests\\Unit\\Storage\\PolicyStorageHelperTest::testPolicyUploadReceiptStoresFile":0.003,"Tests\\Unit\\Storage\\PolicyStorageHelperTest::testPolicyUploadReturnsNullWhenNoFile":0.001,"Tests\\Unit\\Storage\\PolicyStorageHelperTest::testPolicyExistsByTypeReturnsFalseForInvalidType":0.001,"Tests\\Unit\\Storage\\PolicyStorageHelperTest::testPolicyDownloadByTypeAttachment":0.002,"Tests\\Unit\\Storage\\PolicyStorageHelperTest::testPolicyDownloadByTypeInline":0.003,"Tests\\Unit\\Storage\\PolicyStorageHelperTest::testPolicyDownloadByTypeThrowsForInvalidType":0.001,"Tests\\Unit\\Storage\\PolicyStorageHelperTest::testPolicyLocalPdfPathResolvesStoredPdf":0.002,"Tests\\Unit\\Storage\\PolicyStorageHelperTest::testPolicyMarkdownPutReadAndExists":0.002,"Tests\\Unit\\Storage\\S3StorageIntegrationTest::testS3DriverIsActiveFromEnv":0.097,"Tests\\Unit\\Storage\\S3StorageIntegrationTest::testS3PolicyPdfUploadExistsReadAndDelete":0.4,"Tests\\Unit\\Storage\\S3StorageIntegrationTest::testS3PolicyReceiptUploadAndTemporaryUrl":0.342,"Tests\\Unit\\Storage\\S3StorageIntegrationTest::testS3PolicyMarkdownPutAndRead":0.419,"Tests\\Unit\\Storage\\S3StorageIntegrationTest::testS3EndorsementOriginalAndCompletionUpload":0.534,"Tests\\Unit\\Storage\\S3StorageIntegrationTest::testLocalDriverScenarioStillWorksWhenEnvIsLocal":0.011,"Tests\\Unit\\Storage\\FileStorageServiceTest::testCleanupTempPathRemovesS3StyleTempFileOnly":0.003}}

View File

@ -1,49 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<testsuites>
<testsuite name="CLI Arguments" tests="34" assertions="74" errors="0" failures="0" skipped="1" time="1.942890">
<testsuite name="Tests\Unit\Storage\FileStorageServiceTest" file="/var/www/html/nhance_partner_be/tests/unit/Storage/FileStorageServiceTest.php" tests="8" assertions="15" errors="0" failures="0" skipped="0" time="0.051454">
<testcase name="testResolveKeyWithSubFolder" file="/var/www/html/nhance_partner_be/tests/unit/Storage/FileStorageServiceTest.php" line="13" class="Tests\Unit\Storage\FileStorageServiceTest" classname="Tests.Unit.Storage.FileStorageServiceTest" assertions="1" time="0.016696"/>
<testcase name="testResolveKeyWithoutSubFolder" file="/var/www/html/nhance_partner_be/tests/unit/Storage/FileStorageServiceTest.php" line="20" class="Tests\Unit\Storage\FileStorageServiceTest" classname="Tests.Unit.Storage.FileStorageServiceTest" assertions="1" time="0.002569"/>
<testcase name="testUploadModuleFileStoresPolicyPdf" file="/var/www/html/nhance_partner_be/tests/unit/Storage/FileStorageServiceTest.php" line="27" class="Tests\Unit\Storage\FileStorageServiceTest" classname="Tests.Unit.Storage.FileStorageServiceTest" assertions="2" time="0.007923"/>
<testcase name="testReadWriteAndDeleteByModule" file="/var/www/html/nhance_partner_be/tests/unit/Storage/FileStorageServiceTest.php" line="37" class="Tests\Unit\Storage\FileStorageServiceTest" classname="Tests.Unit.Storage.FileStorageServiceTest" assertions="4" time="0.005252"/>
<testcase name="testGetLocalPathForProcessingPointsToWritableFile" file="/var/www/html/nhance_partner_be/tests/unit/Storage/FileStorageServiceTest.php" line="48" class="Tests\Unit\Storage\FileStorageServiceTest" classname="Tests.Unit.Storage.FileStorageServiceTest" assertions="2" time="0.003537"/>
<testcase name="testCopyBetweenKeys" file="/var/www/html/nhance_partner_be/tests/unit/Storage/FileStorageServiceTest.php" line="59" class="Tests\Unit\Storage\FileStorageServiceTest" classname="Tests.Unit.Storage.FileStorageServiceTest" assertions="1" time="0.004232"/>
<testcase name="testDownloadReturnsAttachmentResponse" file="/var/www/html/nhance_partner_be/tests/unit/Storage/FileStorageServiceTest.php" line="67" class="Tests\Unit\Storage\FileStorageServiceTest" classname="Tests.Unit.Storage.FileStorageServiceTest" assertions="3" time="0.006791"/>
<testcase name="testGetTemporaryUrlForLocalUsesBaseUrl" file="/var/www/html/nhance_partner_be/tests/unit/Storage/FileStorageServiceTest.php" line="78" class="Tests\Unit\Storage\FileStorageServiceTest" classname="Tests.Unit.Storage.FileStorageServiceTest" assertions="1" time="0.004454"/>
<testsuite name="CLI Arguments" tests="29" assertions="61" errors="0" failures="0" skipped="0" time="0.135343">
<testsuite name="Tests\Unit\Storage\FileStorageServiceTest" file="/var/www/html/nhance_partner_be/tests/unit/Storage/FileStorageServiceTest.php" tests="9" assertions="21" errors="0" failures="0" skipped="0" time="0.061455">
<testcase name="testResolveKeyWithSubFolder" file="/var/www/html/nhance_partner_be/tests/unit/Storage/FileStorageServiceTest.php" line="13" class="Tests\Unit\Storage\FileStorageServiceTest" classname="Tests.Unit.Storage.FileStorageServiceTest" assertions="1" time="0.014114"/>
<testcase name="testResolveKeyWithoutSubFolder" file="/var/www/html/nhance_partner_be/tests/unit/Storage/FileStorageServiceTest.php" line="20" class="Tests\Unit\Storage\FileStorageServiceTest" classname="Tests.Unit.Storage.FileStorageServiceTest" assertions="1" time="0.002833"/>
<testcase name="testUploadModuleFileStoresPolicyPdf" file="/var/www/html/nhance_partner_be/tests/unit/Storage/FileStorageServiceTest.php" line="27" class="Tests\Unit\Storage\FileStorageServiceTest" classname="Tests.Unit.Storage.FileStorageServiceTest" assertions="2" time="0.014719"/>
<testcase name="testReadWriteAndDeleteByModule" file="/var/www/html/nhance_partner_be/tests/unit/Storage/FileStorageServiceTest.php" line="37" class="Tests\Unit\Storage\FileStorageServiceTest" classname="Tests.Unit.Storage.FileStorageServiceTest" assertions="4" time="0.004287"/>
<testcase name="testGetLocalPathForProcessingPointsToWritableFile" file="/var/www/html/nhance_partner_be/tests/unit/Storage/FileStorageServiceTest.php" line="48" class="Tests\Unit\Storage\FileStorageServiceTest" classname="Tests.Unit.Storage.FileStorageServiceTest" assertions="2" time="0.003452"/>
<testcase name="testCleanupTempPathRemovesS3StyleTempFileOnly" file="/var/www/html/nhance_partner_be/tests/unit/Storage/FileStorageServiceTest.php" line="59" class="Tests\Unit\Storage\FileStorageServiceTest" classname="Tests.Unit.Storage.FileStorageServiceTest" assertions="6" time="0.004347"/>
<testcase name="testCopyBetweenKeys" file="/var/www/html/nhance_partner_be/tests/unit/Storage/FileStorageServiceTest.php" line="77" class="Tests\Unit\Storage\FileStorageServiceTest" classname="Tests.Unit.Storage.FileStorageServiceTest" assertions="1" time="0.005200"/>
<testcase name="testDownloadReturnsAttachmentResponse" file="/var/www/html/nhance_partner_be/tests/unit/Storage/FileStorageServiceTest.php" line="85" class="Tests\Unit\Storage\FileStorageServiceTest" classname="Tests.Unit.Storage.FileStorageServiceTest" assertions="3" time="0.006838"/>
<testcase name="testGetTemporaryUrlForLocalUsesBaseUrl" file="/var/www/html/nhance_partner_be/tests/unit/Storage/FileStorageServiceTest.php" line="96" class="Tests\Unit\Storage\FileStorageServiceTest" classname="Tests.Unit.Storage.FileStorageServiceTest" assertions="1" time="0.005665"/>
</testsuite>
<testsuite name="Tests\Unit\Storage\LocalStorageDriverTest" file="/var/www/html/nhance_partner_be/tests/unit/Storage/LocalStorageDriverTest.php" tests="9" assertions="16" errors="0" failures="0" skipped="0" time="0.033338">
<testcase name="testUploadFromUploadedFileStoresUnderLocalRoot" file="/var/www/html/nhance_partner_be/tests/unit/Storage/LocalStorageDriverTest.php" line="101" class="Tests\Unit\Storage\LocalStorageDriverTest" classname="Tests.Unit.Storage.LocalStorageDriverTest" assertions="2" time="0.003572"/>
<testcase name="testUploadFromLocalPathCopiesFile" file="/var/www/html/nhance_partner_be/tests/unit/Storage/LocalStorageDriverTest.php" line="112" class="Tests\Unit\Storage\LocalStorageDriverTest" classname="Tests.Unit.Storage.LocalStorageDriverTest" assertions="2" time="0.003582"/>
<testcase name="testPutWriteRawContents" file="/var/www/html/nhance_partner_be/tests/unit/Storage/LocalStorageDriverTest.php" line="124" class="Tests\Unit\Storage\LocalStorageDriverTest" classname="Tests.Unit.Storage.LocalStorageDriverTest" assertions="2" time="0.004164"/>
<testcase name="testExistsReturnsFalseForMissingFile" file="/var/www/html/nhance_partner_be/tests/unit/Storage/LocalStorageDriverTest.php" line="134" class="Tests\Unit\Storage\LocalStorageDriverTest" classname="Tests.Unit.Storage.LocalStorageDriverTest" assertions="1" time="0.003338"/>
<testcase name="testDeleteRemovesFile" file="/var/www/html/nhance_partner_be/tests/unit/Storage/LocalStorageDriverTest.php" line="139" class="Tests\Unit\Storage\LocalStorageDriverTest" classname="Tests.Unit.Storage.LocalStorageDriverTest" assertions="2" time="0.003199"/>
<testcase name="testDownloadToTempReturnsExistingLocalPath" file="/var/www/html/nhance_partner_be/tests/unit/Storage/LocalStorageDriverTest.php" line="148" class="Tests\Unit\Storage\LocalStorageDriverTest" classname="Tests.Unit.Storage.LocalStorageDriverTest" assertions="1" time="0.003496"/>
<testcase name="testCopyDuplicatesFile" file="/var/www/html/nhance_partner_be/tests/unit/Storage/LocalStorageDriverTest.php" line="158" class="Tests\Unit\Storage\LocalStorageDriverTest" classname="Tests.Unit.Storage.LocalStorageDriverTest" assertions="2" time="0.004179"/>
<testcase name="testTemporaryUrlUsesBaseUrlForLocalDriver" file="/var/www/html/nhance_partner_be/tests/unit/Storage/LocalStorageDriverTest.php" line="168" class="Tests\Unit\Storage\LocalStorageDriverTest" classname="Tests.Unit.Storage.LocalStorageDriverTest" assertions="1" time="0.004374"/>
<testcase name="testStreamDownloadReturnsResponseWithBody" file="/var/www/html/nhance_partner_be/tests/unit/Storage/LocalStorageDriverTest.php" line="178" class="Tests\Unit\Storage\LocalStorageDriverTest" classname="Tests.Unit.Storage.LocalStorageDriverTest" assertions="3" time="0.003432"/>
<testsuite name="Tests\Unit\Storage\LocalStorageDriverTest" file="/var/www/html/nhance_partner_be/tests/unit/Storage/LocalStorageDriverTest.php" tests="9" assertions="16" errors="0" failures="0" skipped="0" time="0.034179">
<testcase name="testUploadFromUploadedFileStoresUnderLocalRoot" file="/var/www/html/nhance_partner_be/tests/unit/Storage/LocalStorageDriverTest.php" line="101" class="Tests\Unit\Storage\LocalStorageDriverTest" classname="Tests.Unit.Storage.LocalStorageDriverTest" assertions="2" time="0.003866"/>
<testcase name="testUploadFromLocalPathCopiesFile" file="/var/www/html/nhance_partner_be/tests/unit/Storage/LocalStorageDriverTest.php" line="112" class="Tests\Unit\Storage\LocalStorageDriverTest" classname="Tests.Unit.Storage.LocalStorageDriverTest" assertions="2" time="0.003424"/>
<testcase name="testPutWriteRawContents" file="/var/www/html/nhance_partner_be/tests/unit/Storage/LocalStorageDriverTest.php" line="124" class="Tests\Unit\Storage\LocalStorageDriverTest" classname="Tests.Unit.Storage.LocalStorageDriverTest" assertions="2" time="0.003003"/>
<testcase name="testExistsReturnsFalseForMissingFile" file="/var/www/html/nhance_partner_be/tests/unit/Storage/LocalStorageDriverTest.php" line="134" class="Tests\Unit\Storage\LocalStorageDriverTest" classname="Tests.Unit.Storage.LocalStorageDriverTest" assertions="1" time="0.002957"/>
<testcase name="testDeleteRemovesFile" file="/var/www/html/nhance_partner_be/tests/unit/Storage/LocalStorageDriverTest.php" line="139" class="Tests\Unit\Storage\LocalStorageDriverTest" classname="Tests.Unit.Storage.LocalStorageDriverTest" assertions="2" time="0.004788"/>
<testcase name="testDownloadToTempReturnsExistingLocalPath" file="/var/www/html/nhance_partner_be/tests/unit/Storage/LocalStorageDriverTest.php" line="148" class="Tests\Unit\Storage\LocalStorageDriverTest" classname="Tests.Unit.Storage.LocalStorageDriverTest" assertions="1" time="0.003267"/>
<testcase name="testCopyDuplicatesFile" file="/var/www/html/nhance_partner_be/tests/unit/Storage/LocalStorageDriverTest.php" line="158" class="Tests\Unit\Storage\LocalStorageDriverTest" classname="Tests.Unit.Storage.LocalStorageDriverTest" assertions="2" time="0.003318"/>
<testcase name="testTemporaryUrlUsesBaseUrlForLocalDriver" file="/var/www/html/nhance_partner_be/tests/unit/Storage/LocalStorageDriverTest.php" line="168" class="Tests\Unit\Storage\LocalStorageDriverTest" classname="Tests.Unit.Storage.LocalStorageDriverTest" assertions="1" time="0.004557"/>
<testcase name="testStreamDownloadReturnsResponseWithBody" file="/var/www/html/nhance_partner_be/tests/unit/Storage/LocalStorageDriverTest.php" line="178" class="Tests\Unit\Storage\LocalStorageDriverTest" classname="Tests.Unit.Storage.LocalStorageDriverTest" assertions="3" time="0.004998"/>
</testsuite>
<testsuite name="Tests\Unit\Storage\PolicyStorageHelperTest" file="/var/www/html/nhance_partner_be/tests/unit/Storage/PolicyStorageHelperTest.php" tests="11" assertions="24" errors="0" failures="0" skipped="0" time="0.040864">
<testcase name="testPolicyFileTypeMapContainsExpectedTypes" file="/var/www/html/nhance_partner_be/tests/unit/Storage/PolicyStorageHelperTest.php" line="14" class="Tests\Unit\Storage\PolicyStorageHelperTest" classname="Tests.Unit.Storage.PolicyStorageHelperTest" assertions="4" time="0.003233"/>
<testcase name="testPolicyMdFileNameConvertsPdfToMd" file="/var/www/html/nhance_partner_be/tests/unit/Storage/PolicyStorageHelperTest.php" line="24" class="Tests\Unit\Storage\PolicyStorageHelperTest" classname="Tests.Unit.Storage.PolicyStorageHelperTest" assertions="2" time="0.002691"/>
<testcase name="testPolicyUploadPdfStoresFile" file="/var/www/html/nhance_partner_be/tests/unit/Storage/PolicyStorageHelperTest.php" line="30" class="Tests\Unit\Storage\PolicyStorageHelperTest" classname="Tests.Unit.Storage.PolicyStorageHelperTest" assertions="2" time="0.005686"/>
<testcase name="testPolicyUploadReceiptStoresFile" file="/var/www/html/nhance_partner_be/tests/unit/Storage/PolicyStorageHelperTest.php" line="40" class="Tests\Unit\Storage\PolicyStorageHelperTest" classname="Tests.Unit.Storage.PolicyStorageHelperTest" assertions="2" time="0.004434"/>
<testcase name="testPolicyUploadReturnsNullWhenNoFile" file="/var/www/html/nhance_partner_be/tests/unit/Storage/PolicyStorageHelperTest.php" line="50" class="Tests\Unit\Storage\PolicyStorageHelperTest" classname="Tests.Unit.Storage.PolicyStorageHelperTest" assertions="2" time="0.002654"/>
<testcase name="testPolicyExistsByTypeReturnsFalseForInvalidType" file="/var/www/html/nhance_partner_be/tests/unit/Storage/PolicyStorageHelperTest.php" line="56" class="Tests\Unit\Storage\PolicyStorageHelperTest" classname="Tests.Unit.Storage.PolicyStorageHelperTest" assertions="1" time="0.002477"/>
<testcase name="testPolicyDownloadByTypeAttachment" file="/var/www/html/nhance_partner_be/tests/unit/Storage/PolicyStorageHelperTest.php" line="61" class="Tests\Unit\Storage\PolicyStorageHelperTest" classname="Tests.Unit.Storage.PolicyStorageHelperTest" assertions="3" time="0.004665"/>
<testcase name="testPolicyDownloadByTypeInline" file="/var/www/html/nhance_partner_be/tests/unit/Storage/PolicyStorageHelperTest.php" line="72" class="Tests\Unit\Storage\PolicyStorageHelperTest" classname="Tests.Unit.Storage.PolicyStorageHelperTest" assertions="3" time="0.003591"/>
<testcase name="testPolicyDownloadByTypeThrowsForInvalidType" file="/var/www/html/nhance_partner_be/tests/unit/Storage/PolicyStorageHelperTest.php" line="83" class="Tests\Unit\Storage\PolicyStorageHelperTest" classname="Tests.Unit.Storage.PolicyStorageHelperTest" assertions="1" time="0.003291"/>
<testcase name="testPolicyLocalPdfPathResolvesStoredPdf" file="/var/www/html/nhance_partner_be/tests/unit/Storage/PolicyStorageHelperTest.php" line="90" class="Tests\Unit\Storage\PolicyStorageHelperTest" classname="Tests.Unit.Storage.PolicyStorageHelperTest" assertions="2" time="0.003791"/>
<testcase name="testPolicyMarkdownPutReadAndExists" file="/var/www/html/nhance_partner_be/tests/unit/Storage/PolicyStorageHelperTest.php" line="100" class="Tests\Unit\Storage\PolicyStorageHelperTest" classname="Tests.Unit.Storage.PolicyStorageHelperTest" assertions="2" time="0.004351"/>
</testsuite>
<testsuite name="Tests\Unit\Storage\S3StorageIntegrationTest" file="/var/www/html/nhance_partner_be/tests/unit/Storage/S3StorageIntegrationTest.php" tests="6" assertions="19" errors="0" failures="0" skipped="1" time="1.817235">
<testcase name="testS3DriverIsActiveFromEnv" file="/var/www/html/nhance_partner_be/tests/unit/Storage/S3StorageIntegrationTest.php" line="39" class="Tests\Unit\Storage\S3StorageIntegrationTest" classname="Tests.Unit.Storage.S3StorageIntegrationTest" assertions="1" time="0.099073"/>
<testcase name="testS3PolicyPdfUploadExistsReadAndDelete" file="/var/www/html/nhance_partner_be/tests/unit/Storage/S3StorageIntegrationTest.php" line="46" class="Tests\Unit\Storage\S3StorageIntegrationTest" classname="Tests.Unit.Storage.S3StorageIntegrationTest" assertions="7" time="0.401832"/>
<testcase name="testS3PolicyReceiptUploadAndTemporaryUrl" file="/var/www/html/nhance_partner_be/tests/unit/Storage/S3StorageIntegrationTest.php" line="69" class="Tests\Unit\Storage\S3StorageIntegrationTest" classname="Tests.Unit.Storage.S3StorageIntegrationTest" assertions="4" time="0.344284"/>
<testcase name="testS3PolicyMarkdownPutAndRead" file="/var/www/html/nhance_partner_be/tests/unit/Storage/S3StorageIntegrationTest.php" line="87" class="Tests\Unit\Storage\S3StorageIntegrationTest" classname="Tests.Unit.Storage.S3StorageIntegrationTest" assertions="2" time="0.422262"/>
<testcase name="testS3EndorsementOriginalAndCompletionUpload" file="/var/www/html/nhance_partner_be/tests/unit/Storage/S3StorageIntegrationTest.php" line="101" class="Tests\Unit\Storage\S3StorageIntegrationTest" classname="Tests.Unit.Storage.S3StorageIntegrationTest" assertions="5" time="0.536949"/>
<testcase name="testLocalDriverScenarioStillWorksWhenEnvIsLocal" file="/var/www/html/nhance_partner_be/tests/unit/Storage/S3StorageIntegrationTest.php" line="121" class="Tests\Unit\Storage\S3StorageIntegrationTest" classname="Tests.Unit.Storage.S3StorageIntegrationTest" assertions="0" time="0.012836">
<skipped/>
</testcase>
<testsuite name="Tests\Unit\Storage\PolicyStorageHelperTest" file="/var/www/html/nhance_partner_be/tests/unit/Storage/PolicyStorageHelperTest.php" tests="11" assertions="24" errors="0" failures="0" skipped="0" time="0.039708">
<testcase name="testPolicyFileTypeMapContainsExpectedTypes" file="/var/www/html/nhance_partner_be/tests/unit/Storage/PolicyStorageHelperTest.php" line="14" class="Tests\Unit\Storage\PolicyStorageHelperTest" classname="Tests.Unit.Storage.PolicyStorageHelperTest" assertions="4" time="0.002677"/>
<testcase name="testPolicyMdFileNameConvertsPdfToMd" file="/var/www/html/nhance_partner_be/tests/unit/Storage/PolicyStorageHelperTest.php" line="24" class="Tests\Unit\Storage\PolicyStorageHelperTest" classname="Tests.Unit.Storage.PolicyStorageHelperTest" assertions="2" time="0.002543"/>
<testcase name="testPolicyUploadPdfStoresFile" file="/var/www/html/nhance_partner_be/tests/unit/Storage/PolicyStorageHelperTest.php" line="30" class="Tests\Unit\Storage\PolicyStorageHelperTest" classname="Tests.Unit.Storage.PolicyStorageHelperTest" assertions="2" time="0.005102"/>
<testcase name="testPolicyUploadReceiptStoresFile" file="/var/www/html/nhance_partner_be/tests/unit/Storage/PolicyStorageHelperTest.php" line="40" class="Tests\Unit\Storage\PolicyStorageHelperTest" classname="Tests.Unit.Storage.PolicyStorageHelperTest" assertions="2" time="0.006170"/>
<testcase name="testPolicyUploadReturnsNullWhenNoFile" file="/var/www/html/nhance_partner_be/tests/unit/Storage/PolicyStorageHelperTest.php" line="50" class="Tests\Unit\Storage\PolicyStorageHelperTest" classname="Tests.Unit.Storage.PolicyStorageHelperTest" assertions="2" time="0.002235"/>
<testcase name="testPolicyExistsByTypeReturnsFalseForInvalidType" file="/var/www/html/nhance_partner_be/tests/unit/Storage/PolicyStorageHelperTest.php" line="56" class="Tests\Unit\Storage\PolicyStorageHelperTest" classname="Tests.Unit.Storage.PolicyStorageHelperTest" assertions="1" time="0.002131"/>
<testcase name="testPolicyDownloadByTypeAttachment" file="/var/www/html/nhance_partner_be/tests/unit/Storage/PolicyStorageHelperTest.php" line="61" class="Tests\Unit\Storage\PolicyStorageHelperTest" classname="Tests.Unit.Storage.PolicyStorageHelperTest" assertions="3" time="0.003352"/>
<testcase name="testPolicyDownloadByTypeInline" file="/var/www/html/nhance_partner_be/tests/unit/Storage/PolicyStorageHelperTest.php" line="72" class="Tests\Unit\Storage\PolicyStorageHelperTest" classname="Tests.Unit.Storage.PolicyStorageHelperTest" assertions="3" time="0.004934"/>
<testcase name="testPolicyDownloadByTypeThrowsForInvalidType" file="/var/www/html/nhance_partner_be/tests/unit/Storage/PolicyStorageHelperTest.php" line="83" class="Tests\Unit\Storage\PolicyStorageHelperTest" classname="Tests.Unit.Storage.PolicyStorageHelperTest" assertions="1" time="0.003290"/>
<testcase name="testPolicyLocalPdfPathResolvesStoredPdf" file="/var/www/html/nhance_partner_be/tests/unit/Storage/PolicyStorageHelperTest.php" line="90" class="Tests\Unit\Storage\PolicyStorageHelperTest" classname="Tests.Unit.Storage.PolicyStorageHelperTest" assertions="2" time="0.003276"/>
<testcase name="testPolicyMarkdownPutReadAndExists" file="/var/www/html/nhance_partner_be/tests/unit/Storage/PolicyStorageHelperTest.php" line="100" class="Tests\Unit\Storage\PolicyStorageHelperTest" classname="Tests.Unit.Storage.PolicyStorageHelperTest" assertions="2" time="0.003998"/>
</testsuite>
</testsuite>
</testsuites>

View File

@ -58,6 +58,7 @@
<li class="success">Upload module file stores policy pdf</li>
<li class="success">Read write and delete by module</li>
<li class="success">Get local path for processing points to writable file</li>
<li class="success">Cleanup temp path removes s 3 style temp file only</li>
<li class="success">Copy between keys</li>
<li class="success">Download returns attachment response</li>
<li class="success">Get temporary url for local uses base url</li>
@ -88,14 +89,5 @@
<li class="success">Policy local pdf path resolves stored pdf</li>
<li class="success">Policy markdown put read and exists</li>
</ul>
<h2>S3Storage Integration (Tests\Unit\Storage\S3StorageIntegration)</h2>
<ul>
<li class="success">S 3 driver is active from env</li>
<li class="success">S 3 policy pdf upload exists read and delete</li>
<li class="success">S 3 policy receipt upload and temporary url</li>
<li class="success">S 3 policy markdown put and read</li>
<li class="success">S 3 endorsement original and completion upload</li>
<li class="defect">Local driver scenario still works when env is local</li>
</ul>
</body>
</html>

View File

@ -4,6 +4,7 @@ File Storage Service (Tests\Unit\Storage\FileStorageService)
[x] Upload module file stores policy pdf
[x] Read write and delete by module
[x] Get local path for processing points to writable file
[x] Cleanup temp path removes s 3 style temp file only
[x] Copy between keys
[x] Download returns attachment response
[x] Get temporary url for local uses base url
@ -32,11 +33,3 @@ Policy Storage Helper (Tests\Unit\Storage\PolicyStorageHelper)
[x] Policy local pdf path resolves stored pdf
[x] Policy markdown put read and exists
S3Storage Integration (Tests\Unit\Storage\S3StorageIntegration)
[x] S 3 driver is active from env
[x] S 3 policy pdf upload exists read and delete
[x] S 3 policy receipt upload and temporary url
[x] S 3 policy markdown put and read
[x] S 3 endorsement original and completion upload
[ ] Local driver scenario still works when env is local

View File

@ -56,6 +56,24 @@ final class FileStorageServiceTest extends StorageTestCase
$this->assertSame('local', file_get_contents($path));
}
public function testCleanupTempPathRemovesS3StyleTempFileOnly(): void
{
$tempPath = sys_get_temp_dir() . DIRECTORY_SEPARATOR . uniqid('s3_', true) . '_policy.pdf';
file_put_contents($tempPath, 'temp-pdf');
$this->assertTrue(storage()->isS3TempProcessingPath($tempPath));
$this->assertTrue(storage_cleanup_temp($tempPath));
$this->assertFileDoesNotExist($tempPath);
$fileName = 'keep-local.pdf';
storage()->put(storage()->resolveKey('policy', 'policy_pdf', $fileName), 'keep');
$localPath = storage()->getLocalPathForProcessing('policy', 'policy_pdf', $fileName);
$this->assertFalse(storage()->isS3TempProcessingPath($localPath));
$this->assertFalse(storage_cleanup_temp($localPath));
$this->assertFileExists($localPath);
}
public function testCopyBetweenKeys(): void
{
storage()->put(storage()->resolveKey('policy', 'policy_pdf', 'from.pdf'), 'from');

View File

@ -60,6 +60,8 @@ final class S3StorageIntegrationTest extends StorageTestCase
$tempPath = policy_local_pdf_path($stored);
$this->assertFileExists($tempPath);
storage_cleanup_temp($tempPath);
$this->assertFileDoesNotExist($tempPath);
$this->assertTrue(storage_delete('policy', 'policy_pdf', $stored));
$this->uploadedPolicyKey = null;