diff --git a/tests/EncryptedFileTest.php b/tests/EncryptedFileTest.php index 0a7785e..47e6aad 100644 --- a/tests/EncryptedFileTest.php +++ b/tests/EncryptedFileTest.php @@ -9,6 +9,21 @@ protected function getCassetteName(): string return 'encrypted_files.yml'; } + protected function setUp(): void + { + parent::setUp(); + // Guard against php-vcr silently making a real sandbox call (and + // re-recording the cassette) if a request doesn't exactly match what + // is on tape: force a hard failure instead. + \VCR\VCR::configure()->setMode(\VCR\VCR::MODE_NONE); + } + + protected function tearDown(): void + { + \VCR\VCR::configure()->setMode(\VCR\VCR::MODE_NEW_EPISODES); + parent::tearDown(); + } + public function testAllWithPagination() { $encryptedFilesDocument = \Didww\Item\EncryptedFile::all( @@ -57,4 +72,93 @@ public function testUploadMethodSignature() $this->assertEquals('description', $params[2]->getName()); $this->assertTrue($params[2]->isOptional()); } + + private function buildDataFiles(string $delimiter, array $files, array $fields): string + { + $reflection = new \ReflectionMethod(\Didww\Item\EncryptedFile::class, 'buildDataFiles'); + + return $reflection->invoke(null, $delimiter, $files, $fields); + } + + public function testBuildDataFilesMultipartStructureWithDescription() + { + $body = $this->buildDataFiles( + '-------------TESTBOUNDARY0001', + [['name' => 'encrypted_files[file]', 'data' => 'plain file bytes']], + [ + ['name' => 'encrypted_files[encryption_fingerprint]', 'data' => 'fp-abc123'], + ['name' => 'encrypted_files[description]', 'data' => 'test upload description'], + ] + ); + + $this->assertEquals( + "---------------TESTBOUNDARY0001\r\n" + ."Content-Disposition: form-data; name=\"encrypted_files[encryption_fingerprint]\"\r\n\r\n" + ."fp-abc123\r\n" + ."---------------TESTBOUNDARY0001\r\n" + ."Content-Disposition: form-data; name=\"encrypted_files[description]\"\r\n\r\n" + ."test upload description\r\n" + ."---------------TESTBOUNDARY0001\r\n" + ."Content-Disposition: form-data; name=\"encrypted_files[file]\"; filename=\"file.enc\"\r\n" + ."Content-Type: application/octet-stream\r\n" + ."Content-Transfer-Encoding: binary\r\n\r\n" + ."plain file bytes\r\n" + .'---------------TESTBOUNDARY0001--'."\r\n", + $body + ); + } + + public function testBuildDataFilesMultipartStructureWithoutDescription() + { + $body = $this->buildDataFiles( + '-------------TESTBOUNDARY0002', + [['name' => 'encrypted_files[file]', 'data' => 'bad content']], + [['name' => 'encrypted_files[encryption_fingerprint]', 'data' => 'fp-error-999']] + ); + + // No description field part; only fingerprint + the single file part. + $this->assertEquals( + "---------------TESTBOUNDARY0002\r\n" + ."Content-Disposition: form-data; name=\"encrypted_files[encryption_fingerprint]\"\r\n\r\n" + ."fp-error-999\r\n" + ."---------------TESTBOUNDARY0002\r\n" + ."Content-Disposition: form-data; name=\"encrypted_files[file]\"; filename=\"file.enc\"\r\n" + ."Content-Type: application/octet-stream\r\n" + ."Content-Transfer-Encoding: binary\r\n\r\n" + ."bad content\r\n" + .'---------------TESTBOUNDARY0002--'."\r\n", + $body + ); + } + + public function testUploadSendsExpectedMultipartRequestAndParsesSuccessResponse() + { + $result = \Didww\Item\EncryptedFile::upload( + 'fp-abc123', + 'plain file bytes', + 'test upload description', + 'TESTBOUNDARY0001' + ); + + $this->assertEquals(201, $result->getResponseCode()); + $this->assertTrue($result->success()); + $this->assertFalse($result->hasErrors()); + $this->assertEquals('aaaa1111-2222-3333-4444-555566667777', $result->getId()); + } + + public function testUploadSendsExpectedMultipartRequestAndParsesErrorResponse() + { + $result = \Didww\Item\EncryptedFile::upload( + 'fp-error-999', + 'bad content', + null, + 'TESTBOUNDARY0002' + ); + + $this->assertEquals(422, $result->getResponseCode()); + $this->assertFalse($result->success()); + $this->assertTrue($result->hasErrors()); + $this->assertNull($result->getId()); + $this->assertEquals([['title' => 'Invalid fingerprint']], $result->getErrors()); + } } diff --git a/tests/SupportingDocumentTemplateTest.php b/tests/SupportingDocumentTemplateTest.php index 89bda56..b130a42 100644 --- a/tests/SupportingDocumentTemplateTest.php +++ b/tests/SupportingDocumentTemplateTest.php @@ -9,6 +9,21 @@ protected function getCassetteName(): string return 'supporting_document_templates.yml'; } + protected function setUp(): void + { + parent::setUp(); + // Guard against php-vcr silently making a real sandbox call (and + // re-recording the cassette) if a request doesn't exactly match what + // is on tape: force a hard failure instead. + \VCR\VCR::configure()->setMode(\VCR\VCR::MODE_NONE); + } + + protected function tearDown(): void + { + \VCR\VCR::configure()->setMode(\VCR\VCR::MODE_NEW_EPISODES); + parent::tearDown(); + } + public function testAllWithIncludesAndPagination() { $supportingDocumentTemplatesDocument = \Didww\Item\SupportingDocumentTemplate::all( @@ -31,4 +46,36 @@ public function testAllWithIncludesAndPagination() $this->assertEquals('Belgium Registration Form', $second->getName()); $this->assertTrue($second->getPermanent()); } + + public function testDownload() + { + $template = \Didww\Item\SupportingDocumentTemplate::build( + '206ccec2-1166-461f-9f58-3a56823db548', + ['url' => 'https://sandbox-api.didww.com/storage/public/w7f2irbo819la7vd7up7u67pkmkn'] + ); + + $destFile = tempnam(sys_get_temp_dir(), 'didww_test_'); + $result = $template->download($destFile); + + $this->assertTrue($result); + $this->assertEquals('Generic LOI template contents', file_get_contents($destFile)); + unlink($destFile); + } + + public function testDownloadToResourceHandle() + { + $template = \Didww\Item\SupportingDocumentTemplate::build( + '206ccec2-1166-461f-9f58-3a56823db548', + ['url' => 'https://sandbox-api.didww.com/storage/public/w7f2irbo819la7vd7up7u67pkmkn'] + ); + + $destFile = tempnam(sys_get_temp_dir(), 'didww_test_'); + $handle = fopen($destFile, 'w'); + $result = $template->download($handle); + fclose($handle); + + $this->assertTrue($result); + $this->assertEquals('Generic LOI template contents', file_get_contents($destFile)); + unlink($destFile); + } } diff --git a/tests/fixtures/encrypted_files.yml b/tests/fixtures/encrypted_files.yml index ad660cd..cdf9ac4 100644 --- a/tests/fixtures/encrypted_files.yml +++ b/tests/fixtures/encrypted_files.yml @@ -183,3 +183,41 @@ Access-Control-Allow-Methods: 'GET, POST, DELETE, PUT, PATCH, OPTIONS' Access-Control-Allow-Credentials: 'true' body: '{"data":{"id":"371eafbd-ac6a-485c-aadf-9e3c5da37eb4","type":"encrypted_files","attributes":{"description":null,"expires_at":"2021-04-06T16:38:34.437Z"}},"meta":{"api_version":"2026-04-16"}}' +- + request: + method: POST + url: 'https://sandbox-api.didww.com/v3/encrypted_files' + headers: + Api-Key: PLACEYOURAPIKEYHERE + X-DIDWW-API-Version: '2026-04-16' + Content-Type: 'multipart/form-data; boundary=-------------TESTBOUNDARY0001' + Content-Length: '501' + Accept: application/json + body: "---------------TESTBOUNDARY0001\r\nContent-Disposition: form-data; name=\"encrypted_files[encryption_fingerprint]\"\r\n\r\nfp-abc123\r\n---------------TESTBOUNDARY0001\r\nContent-Disposition: form-data; name=\"encrypted_files[description]\"\r\n\r\ntest upload description\r\n---------------TESTBOUNDARY0001\r\nContent-Disposition: form-data; name=\"encrypted_files[file]\"; filename=\"file.enc\"\r\nContent-Type: application/octet-stream\r\nContent-Transfer-Encoding: binary\r\n\r\nplain file bytes\r\n---------------TESTBOUNDARY0001--\r\n" + response: + status: + http_version: '1.1' + code: '201' + message: Created + headers: + Content-Type: application/vnd.api+json + body: '{"data":{"id":"aaaa1111-2222-3333-4444-555566667777","type":"encrypted_files","attributes":{"description":"test upload description","expires_at":"2026-04-22T10:00:00.000Z"}},"meta":{"api_version":"2026-04-16"}}' +- + request: + method: POST + url: 'https://sandbox-api.didww.com/v3/encrypted_files' + headers: + Api-Key: PLACEYOURAPIKEYHERE + X-DIDWW-API-Version: '2026-04-16' + Content-Type: 'multipart/form-data; boundary=-------------TESTBOUNDARY0002' + Content-Length: '370' + Accept: application/json + body: "---------------TESTBOUNDARY0002\r\nContent-Disposition: form-data; name=\"encrypted_files[encryption_fingerprint]\"\r\n\r\nfp-error-999\r\n---------------TESTBOUNDARY0002\r\nContent-Disposition: form-data; name=\"encrypted_files[file]\"; filename=\"file.enc\"\r\nContent-Type: application/octet-stream\r\nContent-Transfer-Encoding: binary\r\n\r\nbad content\r\n---------------TESTBOUNDARY0002--\r\n" + response: + status: + http_version: '1.1' + code: '422' + message: 'Unprocessable Entity' + headers: + Content-Type: application/vnd.api+json + body: '{"errors":[{"title":"Invalid fingerprint"}]}' diff --git a/tests/fixtures/supporting_document_templates.yml b/tests/fixtures/supporting_document_templates.yml index 1b0eba3..5dc517f 100644 --- a/tests/fixtures/supporting_document_templates.yml +++ b/tests/fixtures/supporting_document_templates.yml @@ -37,3 +37,15 @@ Access-Control-Allow-Methods: 'GET, POST, DELETE, PUT, PATCH, OPTIONS' Access-Control-Allow-Credentials: 'true' body: '{"data":[{"id":"206ccec2-1166-461f-9f58-3a56823db548","type":"supporting_document_templates","attributes":{"name":"Generic LOI","permanent":false,"url":"https://sandbox-api.didww.com/storage/public/w7f2irbo819la7vd7up7u67pkmkn"}},{"id":"fd38c86d-b69b-4ca8-b73c-286a3b93d107","type":"supporting_document_templates","attributes":{"name":"Belgium Registration Form","permanent":true,"url":"https://sandbox-api.didww.com/storage/public/e8lziulj68xetfa5ed6na3g7q7ra"}},{"id":"4199435f-646e-4e9d-a143-8f3b972b10c5","type":"supporting_document_templates","attributes":{"name":"Germany Special Registration Form","permanent":true,"url":"https://sandbox-api.didww.com/storage/public/4rghqnqtba0fa7mbdgig086xej1e"}},{"id":"94be4d74-c968-4d81-91c5-2d11b4e45328","type":"supporting_document_templates","attributes":{"name":"LOI Example","permanent":false,"url":"https://sandbox-api.didww.com/storage/public/xptvgb8derrz0ru95wr9oi68g9tg"}},{"id":"eb810289-8620-44e1-982d-13e6cee70404","type":"supporting_document_templates","attributes":{"name":"TestPermanDoc","permanent":true,"url":"https://sandbox-api.didww.com/storage/public/owwqi77007ks4qx198b7su3eukg6"}}],"meta":{"total_records":30,"api_version":"2026-04-16"},"links":{"first":"https://sandbox-api.didww.com/v3/supporting_document_templates?page%5Bnumber%5D=1&page%5Bsize%5D=5","next":"https://sandbox-api.didww.com/v3/supporting_document_templates?page%5Bnumber%5D=2&page%5Bsize%5D=5","last":"https://sandbox-api.didww.com/v3/supporting_document_templates?page%5Bnumber%5D=6&page%5Bsize%5D=5"}}' +- + request: + method: GET + url: 'https://sandbox-api.didww.com/storage/public/w7f2irbo819la7vd7up7u67pkmkn' + response: + status: + http_version: '1.1' + code: '200' + message: OK + headers: + Content-Type: application/octet-stream + body: 'Generic LOI template contents'