From 3e4447003a7213c79f721ce9d1f03734c9be43ec Mon Sep 17 00:00:00 2001 From: Venkatesh Date: Wed, 5 Aug 2026 10:30:55 +0530 Subject: [PATCH] FIX_TMP_CLEAN_UP --- app/Commands/StorageUploadTest.php | 12 +- app/Config/Routes.php | 7 + app/Controllers/AgentIncentiveController.php | 4 + app/Controllers/InvoiceController.php | 119 ++++--- app/Controllers/LogViewerController.php | 175 +++++++++ app/Controllers/PolicyController.php | 5 +- app/Controllers/PolicyRagController.php | 11 +- app/Helpers/storage_helper.php | 11 + app/Services/FileStorageService.php | 52 +++ app/Views/logs/index.php | 331 ++++++++++++++++++ app/Views/logs/view.php | 139 ++++++++ build/.phpunit.cache/test-results | 2 +- build/logs/logfile.xml | 75 ++-- build/logs/testdox.html | 10 +- build/logs/testdox.txt | 9 +- tests/unit/Storage/FileStorageServiceTest.php | 18 + .../unit/Storage/S3StorageIntegrationTest.php | 2 + 17 files changed, 857 insertions(+), 125 deletions(-) create mode 100644 app/Controllers/LogViewerController.php create mode 100644 app/Views/logs/index.php create mode 100644 app/Views/logs/view.php diff --git a/app/Commands/StorageUploadTest.php b/app/Commands/StorageUploadTest.php index c54406c..f2a1ddb 100644 --- a/app/Commands/StorageUploadTest.php +++ b/app/Commands/StorageUploadTest.php @@ -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 diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 921d363..f1aca38 100755 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -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'); diff --git a/app/Controllers/AgentIncentiveController.php b/app/Controllers/AgentIncentiveController.php index 37df040..8551cb2 100644 --- a/app/Controllers/AgentIncentiveController.php +++ b/app/Controllers/AgentIncentiveController.php @@ -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() diff --git a/app/Controllers/InvoiceController.php b/app/Controllers/InvoiceController.php index ecacb48..b4441e4 100644 --- a/app/Controllers/InvoiceController.php +++ b/app/Controllers/InvoiceController.php @@ -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); } } diff --git a/app/Controllers/LogViewerController.php b/app/Controllers/LogViewerController.php new file mode 100644 index 0000000..53459ff --- /dev/null +++ b/app/Controllers/LogViewerController.php @@ -0,0 +1,175 @@ +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 + */ + 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'; + } +} diff --git a/app/Controllers/PolicyController.php b/app/Controllers/PolicyController.php index d749f92..6407722 100644 --- a/app/Controllers/PolicyController.php +++ b/app/Controllers/PolicyController.php @@ -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); } } diff --git a/app/Controllers/PolicyRagController.php b/app/Controllers/PolicyRagController.php index 54343de..b241efa 100644 --- a/app/Controllers/PolicyRagController.php +++ b/app/Controllers/PolicyRagController.php @@ -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, diff --git a/app/Helpers/storage_helper.php b/app/Helpers/storage_helper.php index 4769b78..12e21f8 100644 --- a/app/Helpers/storage_helper.php +++ b/app/Helpers/storage_helper.php @@ -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 { diff --git a/app/Services/FileStorageService.php b/app/Services/FileStorageService.php index 54ab50c..5736072 100644 --- a/app/Services/FileStorageService.php +++ b/app/Services/FileStorageService.php @@ -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__. + */ + 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( diff --git a/app/Views/logs/index.php b/app/Views/logs/index.php new file mode 100644 index 0000000..e49b76a --- /dev/null +++ b/app/Views/logs/index.php @@ -0,0 +1,331 @@ + + + + + + Application Logs + + + + +
+ + +
+
+ +
+
+ + +
+
+ +
+
+ +
+ + + + + + + + + + + + + + +
File nameSizeLast modifiedAction
Loading logs...
+
+ + +
+
+ + + + diff --git a/app/Views/logs/view.php b/app/Views/logs/view.php new file mode 100644 index 0000000..7bc06ae --- /dev/null +++ b/app/Views/logs/view.php @@ -0,0 +1,139 @@ + + + + + + <?= esc($file['name']) ?> — Log Viewer + + + + +
+
+

+ · modified +
+ +
+ + +
+ Showing the last bytes of this file. Use Download for the full log. +
+ + +
+ + + + diff --git a/build/.phpunit.cache/test-results b/build/.phpunit.cache/test-results index 994f289..f25ca13 100644 --- a/build/.phpunit.cache/test-results +++ b/build/.phpunit.cache/test-results @@ -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}} \ No newline at end of file +{"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}} \ No newline at end of file diff --git a/build/logs/logfile.xml b/build/logs/logfile.xml index 4656c10..fb80835 100644 --- a/build/logs/logfile.xml +++ b/build/logs/logfile.xml @@ -1,49 +1,40 @@ - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/build/logs/testdox.html b/build/logs/testdox.html index 0f3a340..0fed0ef 100644 --- a/build/logs/testdox.html +++ b/build/logs/testdox.html @@ -58,6 +58,7 @@
  • Upload module file stores policy pdf
  • Read write and delete by module
  • Get local path for processing points to writable file
  • +
  • Cleanup temp path removes s 3 style temp file only
  • Copy between keys
  • Download returns attachment response
  • Get temporary url for local uses base url
  • @@ -88,14 +89,5 @@
  • Policy local pdf path resolves stored pdf
  • Policy markdown put read and exists
  • -

    S3Storage Integration (Tests\Unit\Storage\S3StorageIntegration)

    -
      -
    • S 3 driver is active from env
    • -
    • S 3 policy pdf upload exists read and delete
    • -
    • S 3 policy receipt upload and temporary url
    • -
    • S 3 policy markdown put and read
    • -
    • S 3 endorsement original and completion upload
    • -
    • Local driver scenario still works when env is local
    • -
    \ No newline at end of file diff --git a/build/logs/testdox.txt b/build/logs/testdox.txt index 7a31bea..fe68975 100644 --- a/build/logs/testdox.txt +++ b/build/logs/testdox.txt @@ -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 - diff --git a/tests/unit/Storage/FileStorageServiceTest.php b/tests/unit/Storage/FileStorageServiceTest.php index d073bca..8580079 100644 --- a/tests/unit/Storage/FileStorageServiceTest.php +++ b/tests/unit/Storage/FileStorageServiceTest.php @@ -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'); diff --git a/tests/unit/Storage/S3StorageIntegrationTest.php b/tests/unit/Storage/S3StorageIntegrationTest.php index f286283..da3ffb1 100644 --- a/tests/unit/Storage/S3StorageIntegrationTest.php +++ b/tests/unit/Storage/S3StorageIntegrationTest.php @@ -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;