directory = storage_path('framework/testing/receipt-health-'.Str::uuid()); File::ensureDirectoryExists($this->directory); Storage::set('receipt_documents', Storage::build(['driver' => 'local', 'root' => $this->directory, 'throw' => true])); $this->f = receiptFixture(); $this->actingAs($this->f['receiver']); $this->receipt = app(ReceiptWorkflow::class)->confirm($this->f['receiver'], $this->f['order'], receiptPayload($this->f)); $this->bytes = base64_decode('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAusB9Wl6E4sAAAAASUVORK5CYII='); $this->document = app(ReceiptDocuments::class)->add($this->f['receiver'], $this->receipt, ['operation_key' => (string) Str::uuid(), 'document_type' => 'delivery_note'], UploadedFile::fake()->createWithContent('private-name.png', $this->bytes)); auth()->logout(); }); afterEach(function () { File::deleteDirectory($this->directory); }); test('receipt document health checks native evidence without mutations or disclosing private identifiers', function () { $other = receiptFixture(); $this->actingAs($other['receiver']); $receipt = app(ReceiptWorkflow::class)->confirm($other['receiver'], $other['order'], receiptPayload($other)); app(ReceiptDocuments::class)->add($other['receiver'], $receipt, ['operation_key' => (string) Str::uuid(), 'document_type' => 'quality'], UploadedFile::fake()->createWithContent('other-private.png', $this->bytes)); auth()->logout(); $before = []; foreach (['receipt_documents', 'audit_logs', 'goods_receipts', 'stock_movements', 'approval_steps'] as $table) { $before[$table] = DB::table($table)->orderBy('id')->get()->toArray(); } expect(Artisan::call('sgc:receipt-documents-check', ['--json' => true]))->toBe(0); $output = Artisan::output(); $result = json_decode($output, true, 32, JSON_THROW_ON_ERROR); expect($result['ok'])->toBeTrue()->and($result['complete'])->toBeTrue()->and($result['documents_checked'])->toBe(2)->and($result['files_checked'])->toBe(2); expect($output)->not->toContain($this->directory, $this->document->path, 'private-name.png'); foreach ($before as $table => $rows) { expect(DB::table($table)->orderBy('id')->get()->toArray())->toEqual($rows); } expect(Storage::disk('receipt_documents')->get($this->document->path))->toBe($this->bytes); }); test('receipt document health detects missing altered and unreferenced files without removing any', function () { $disk = Storage::disk('receipt_documents'); $disk->delete($this->document->path); expect(app(ReceiptDocumentHealth::class)->inspect()['missing'])->toBe(1); $changed = $this->bytes; $changed[10] = 'x'; $disk->put($this->document->path, $changed); $disk->put('recent.bin', 'recent'); $disk->put('older.bin', 'older'); touch($disk->path('older.bin'), now()->subHours(2)->timestamp); $result = app(ReceiptDocumentHealth::class)->inspect(); expect($result['ok'])->toBeFalse()->and($result['altered'])->toBe(1)->and($result['unreferenced_recent'])->toBe(1)->and($result['unreferenced_older'])->toBe(1); expect($disk->allFiles())->toHaveCount(3)->and($disk->get($this->document->path))->toBe($changed); expect(ReceiptDocument::withoutGlobalScopes()->count())->toBe(1); }); test('receipt document health rejects invalid paths and reports a bounded incomplete scan', function () { DB::table('receipt_documents')->insert([...$this->document->getRawOriginal(), 'id' => $this->document->id + 1, 'document_type' => 'other', 'path' => '../outside-secret.txt']); expect(app(ReceiptDocumentHealth::class)->inspect()['invalid_reference'])->toBe(1); $result = app(ReceiptDocumentHealth::class)->inspect(1); expect($result['ok'])->toBeFalse()->and($result['complete'])->toBeFalse()->and($result['reasons'])->toContain('limit_reached'); expect(Artisan::call('sgc:receipt-documents-check', ['--limit' => '10001']))->toBe(2); }); test('receipt document health distinguishes unavailable installation and hides technical errors', function () { Schema::drop('receipt_documents'); expect(app(ReceiptDocumentHealth::class)->inspect()['reasons'])->toBe(['not_installed']); $this->mock(ReceiptDocumentHealth::class)->shouldReceive('inspect')->andThrow(new RuntimeException('private secret path/password')); expect(Artisan::call('sgc:receipt-documents-check', ['--json' => true]))->toBe(1); expect(Artisan::output())->toContain('unavailable')->not->toContain('password', 'secret'); }); test('receipt document health refuses an interactive identity instead of exposing a global tenant scan', function () { $this->actingAs($this->f['receiver']); expect(fn () => app(ReceiptDocumentHealth::class)->inspect())->toThrow(HttpException::class); }); test('receipt document health does not certify an unavailable storage root', function () { File::deleteDirectory($this->directory); $result = app(ReceiptDocumentHealth::class)->inspect(); expect($result['ok'])->toBeFalse()->and($result['complete'])->toBeFalse()->and($result['reasons'])->toBe(['storage_unavailable']); });