From 4be238bc2d53bee881701770bd594d95729ee225 Mon Sep 17 00:00:00 2001 From: cloudinary-pkoniu Date: Thu, 2 Oct 2025 21:08:27 +0200 Subject: [PATCH] chore: api restore methods are tested as unit tests --- test/integration/api/admin/api_spec.js | 283 ------------------------- test/unit/api_restore_spec.js | 73 +++++++ 2 files changed, 73 insertions(+), 283 deletions(-) create mode 100644 test/unit/api_restore_spec.js diff --git a/test/integration/api/admin/api_spec.js b/test/integration/api/admin/api_spec.js index d545d2f6..28c9ad84 100644 --- a/test/integration/api/admin/api_spec.js +++ b/test/integration/api/admin/api_spec.js @@ -1350,291 +1350,8 @@ describe("api", function () { }); }); }); - describe('.restore', function () { - it.skip('should restore different versions of a deleted asset', async function () { - this.timeout(TIMEOUT.LARGE); - - // Upload the same file twice (upload->delete->upload->delete) - - // Upload and delete a file - const firstUpload = await uploadImage({ - public_id: PUBLIC_ID_BACKUP_1, - backup: true - }); - await wait(1000)(); - - const firstDelete = await API_V2.delete_resources([PUBLIC_ID_BACKUP_1]); - - - // Upload and delete it again, this time add angle to create a different 'version' - const secondUpload = await uploadImage({ - public_id: PUBLIC_ID_BACKUP_1, - backup: true, - angle: '0' - }); - await wait(1000)(); - - const secondDelete = await API_V2.delete_resources([PUBLIC_ID_BACKUP_1]); - await wait(1000)(); - - // Sanity, ensure these uploads are different before we continue - expect(firstUpload.bytes).not.to.equal(secondUpload.bytes); - - // Ensure all files were uploaded correctly - expect(firstUpload).not.to.be(null); - expect(secondUpload).not.to.be(null); - - // Ensure all files were deleted correctly - expect(firstDelete).to.have.property("deleted"); - expect(secondDelete).to.have.property("deleted"); - - // Get the versions of the deleted asset - const getVersionsResp = await API_V2.resource(PUBLIC_ID_BACKUP_1, {versions: true}); - - const firstAssetVersion = getVersionsResp.versions[0].version_id; - const secondAssetVersion = getVersionsResp.versions[1].version_id; - - // Restore first version, ensure it's equal to the upload size - await wait(2000)(); - const firstVerRestore = await API_V2.restore([PUBLIC_ID_BACKUP_1], {versions: [firstAssetVersion]}); - expect(firstVerRestore[PUBLIC_ID_BACKUP_1].bytes).to.eql(firstUpload.bytes); - - // Restore second version, ensure it's equal to the upload size - await wait(2000)(); - const secondVerRestore = await API_V2.restore([PUBLIC_ID_BACKUP_1], {versions: [secondAssetVersion]}); - expect(secondVerRestore[PUBLIC_ID_BACKUP_1].bytes).to.eql(secondUpload.bytes); - - // Cleanup, - const finalDeleteResp = await API_V2.delete_resources([PUBLIC_ID_BACKUP_1]); - expect(finalDeleteResp).to.have.property("deleted"); - }); - - it.skip('should restore two different deleted assets', async () => { - this.timeout(TIMEOUT.LARGE); - - // Upload two different files - const firstUpload = await uploadImage({ - public_id: PUBLIC_ID_BACKUP_1, - backup: true - }); - const secondUpload = await uploadImage({ - public_id: PUBLIC_ID_BACKUP_2, - backup: true, - angle: '0' - }); - - // delete both resources - const deleteAll = await API_V2.delete_resources([PUBLIC_ID_BACKUP_1, PUBLIC_ID_BACKUP_2]); - - // Expect correct deletion of the assets - expect(deleteAll.deleted[PUBLIC_ID_BACKUP_1]).to.be("deleted"); - expect(deleteAll.deleted[PUBLIC_ID_BACKUP_2]).to.be("deleted"); - - const getFirstAssetVersion = await API_V2.resource(PUBLIC_ID_BACKUP_1, {versions: true}); - const getSecondAssetVersion = await API_V2.resource(PUBLIC_ID_BACKUP_2, {versions: true}); - - const firstAssetVersion = getFirstAssetVersion.versions[0].version_id; - const secondAssetVersion = getSecondAssetVersion.versions[0].version_id; - - const IDS_TO_RESTORE = [PUBLIC_ID_BACKUP_1, PUBLIC_ID_BACKUP_2]; - const VERSIONS_TO_RESTORE = [firstAssetVersion, secondAssetVersion]; - - const restore = await API_V2.restore(IDS_TO_RESTORE, {versions: VERSIONS_TO_RESTORE}); - - // Expect correct restorations - expect(restore[PUBLIC_ID_BACKUP_1].bytes).to.equal(firstUpload.bytes); - expect(restore[PUBLIC_ID_BACKUP_2].bytes).to.equal(secondUpload.bytes); - - // Cleanup - const finalDelete = await API_V2.delete_resources([PUBLIC_ID_BACKUP_1, PUBLIC_ID_BACKUP_2]); - // Expect correct deletion of the assets - expect(finalDelete.deleted[PUBLIC_ID_BACKUP_1]).to.be("deleted"); - expect(finalDelete.deleted[PUBLIC_ID_BACKUP_2]).to.be("deleted"); - }); - }); - - describe("restore_by_asset_ids", function () { - this.timeout(TIMEOUT.MEDIUM); - - const publicId = "api_test_restore" + UNIQUE_JOB_SUFFIX_ID; - let uploadedAssetId; - - before(() => uploadImage({ - public_id: publicId, - backup: true, - tags: UPLOAD_TAGS - }) - .then(wait(2000)) - .then(() => cloudinary.v2.api.resource(publicId)) - .then((resource) => { - uploadedAssetId = resource.asset_id; - expect(resource).not.to.be(null); - expect(resource.bytes).to.eql(3381); - return cloudinary.v2.api.delete_resources(publicId); - }) - .then(() => cloudinary.v2.api.resource(publicId)) - .then((resource) => { - expect(resource).not.to.be(null); - expect(resource.bytes).to.eql(0); - expect(resource.placeholder).to.eql(true); - }) - ); - - it("should restore a deleted resource when passed an asset ID", () => cloudinary.v2.api - .restore_by_asset_ids([uploadedAssetId]) - .then((response) => { - let info = response[uploadedAssetId]; - expect(info).not.to.be(null); - expect(info.bytes).to.eql(3381); - return cloudinary.v2.api.resources_by_asset_ids([uploadedAssetId]); - }) - .then((response) => { - const { resources } = response; - expect(resources[0]).not.to.be(null); - expect(resources[0].bytes).to.eql(3381); - })); - - it.skip("should restore different versions of a deleted asset when passed an asset ID", async function () { - this.timeout(TIMEOUT.LARGE); - - // Upload the same file twice (upload->delete->upload->delete) - - // Upload and delete a file - const firstUpload = await uploadImage({ - public_id: PUBLIC_ID_BACKUP_1, - backup: true - }); - await wait(1000)(); - - const firstDelete = await API_V2.delete_resources([ - PUBLIC_ID_BACKUP_1 - ]); - - // Upload and delete it again, this time add angle to create a different 'version' - const secondUpload = await uploadImage({ - public_id: PUBLIC_ID_BACKUP_1, - backup: true, - angle: "0" - }); - await wait(1000)(); - - const secondDelete = await API_V2.delete_resources([ - PUBLIC_ID_BACKUP_1 - ]); - await wait(1000)(); - - // Sanity, ensure these uploads are different before we continue - expect(firstUpload.bytes).not.to.equal(secondUpload.bytes); - - // Ensure all files were uploaded correctly - expect(firstUpload).not.to.be(null); - expect(secondUpload).not.to.be(null); - - // Ensure all files were deleted correctly - expect(firstDelete).to.have.property("deleted"); - expect(secondDelete).to.have.property("deleted"); - - // Get the asset ID and versions of the deleted asset - const getVersionsResp = await API_V2.resource(PUBLIC_ID_BACKUP_1, { - versions: true - }); - const assetId = getVersionsResp.asset_id; - - const firstAssetVersion = getVersionsResp.versions[0].version_id; - const secondAssetVersion = getVersionsResp.versions[1].version_id; - - // Restore first version by passing in the asset ID, ensure it's equal to the upload size - await wait(1000)(); - const firstVerRestore = await API_V2.restore_by_asset_ids([assetId], { - versions: [firstAssetVersion] - }); - expect(firstVerRestore[assetId].bytes).to.eql( - firstUpload.bytes - ); - - // Restore second version by passing in the asset ID, ensure it's equal to the upload size - await wait(1000)(); - const secondVerRestore = await API_V2.restore_by_asset_ids( - [assetId], - { versions: [secondAssetVersion] } - ); - expect(secondVerRestore[assetId].bytes).to.eql( - secondUpload.bytes - ); - - // Cleanup, - const finalDeleteResp = await API_V2.delete_resources([ - PUBLIC_ID_BACKUP_1 - ]); - expect(finalDeleteResp).to.have.property("deleted"); - }); - it("should restore two different deleted assets when passed asset IDs", async () => { - // Upload two different files - const firstUpload = await uploadImage({ - public_id: PUBLIC_ID_BACKUP_1, - backup: true - }); - const secondUpload = await uploadImage({ - public_id: PUBLIC_ID_BACKUP_2, - backup: true, - angle: "0" - }); - - // delete both resources - const deleteAll = await API_V2.delete_resources([ - PUBLIC_ID_BACKUP_1, - PUBLIC_ID_BACKUP_2 - ]); - - // Expect correct deletion of the assets - expect(deleteAll.deleted[PUBLIC_ID_BACKUP_1]).to.be("deleted"); - expect(deleteAll.deleted[PUBLIC_ID_BACKUP_2]).to.be("deleted"); - - const getFirstAssetVersion = await API_V2.resource( - PUBLIC_ID_BACKUP_1, - { versions: true } - ); - - const getSecondAssetVersion = await API_V2.resource( - PUBLIC_ID_BACKUP_2, - { versions: true } - ); - - const firstAssetId = getFirstAssetVersion.asset_id; - const secondAssetId = getSecondAssetVersion.asset_id; - - const firstAssetVersion = - getFirstAssetVersion.versions[0].version_id; - const secondAssetVersion = - getSecondAssetVersion.versions[0].version_id; - - const IDS_TO_RESTORE = [firstAssetId, secondAssetId]; - const VERSIONS_TO_RESTORE = [firstAssetVersion, secondAssetVersion]; - - const restore = await API_V2.restore_by_asset_ids(IDS_TO_RESTORE, { - versions: VERSIONS_TO_RESTORE - }); - - // Expect correct restorations - expect(restore[firstAssetId].bytes).to.equal( - firstUpload.bytes - ); - expect(restore[secondAssetId].bytes).to.equal( - secondUpload.bytes - ); - - // Cleanup - const finalDelete = await API_V2.delete_resources([ - PUBLIC_ID_BACKUP_1, - PUBLIC_ID_BACKUP_2 - ]); - // Expect correct deletion of the assets - expect(finalDelete.deleted[PUBLIC_ID_BACKUP_1]).to.be("deleted"); - expect(finalDelete.deleted[PUBLIC_ID_BACKUP_2]).to.be("deleted"); - }); - }); describe('mapping', function () { diff --git a/test/unit/api_restore_spec.js b/test/unit/api_restore_spec.js new file mode 100644 index 00000000..e6fd097b --- /dev/null +++ b/test/unit/api_restore_spec.js @@ -0,0 +1,73 @@ +const assert = require('assert'); +const sinon = require('sinon'); + +const cloudinary = require('../../lib/cloudinary'); +const createTestConfig = require('../testUtils/createTestConfig'); +const helper = require('../spechelper'); +const api_http = require("https"); +const ClientRequest = require('_http_client').ClientRequest; + +describe('api restore handlers', function () { + const mocked = {}; + + beforeEach(function () { + cloudinary.config(createTestConfig()); + mocked.xhr = sinon.useFakeXMLHttpRequest(); + mocked.write = sinon.spy(ClientRequest.prototype, 'write'); + mocked.request = sinon.spy(api_http, 'request'); + }); + + afterEach(function () { + mocked.request.restore(); + mocked.write.restore(); + mocked.xhr.restore(); + }); + + describe('.restore', function () { + it('sends public_ids and versions with JSON payload', function () { + const options = { + resource_type: 'video', + type: 'authenticated', + versions: ['ver-1', 'ver-2'] + }; + + cloudinary.v2.api.restore(['pub-1', 'pub-2'], options); + + sinon.assert.calledWith(mocked.request, sinon.match({ + pathname: sinon.match('resources/video/authenticated/restore'), + method: sinon.match('POST') + })); + + sinon.assert.calledWith(mocked.write, sinon.match(helper.apiJsonParamMatcher('public_ids', ['pub-1', 'pub-2']))); + sinon.assert.calledWith(mocked.write, sinon.match(helper.apiJsonParamMatcher('versions', ['ver-1', 'ver-2']))); + }); + }); + + describe('.restore_by_asset_ids', function () { + it('sends asset_ids and versions with JSON payload', function () { + const options = { versions: ['ver-3'] }; + const assetIds = ['asset-1', 'asset-2']; + + cloudinary.v2.api.restore_by_asset_ids(assetIds, options); + + sinon.assert.calledWith(mocked.request, sinon.match({ + pathname: sinon.match('resources/restore'), + method: sinon.match('POST') + })); + + sinon.assert.calledWith(mocked.write, sinon.match(helper.apiJsonParamMatcher('asset_ids', assetIds))); + sinon.assert.calledWith(mocked.write, sinon.match(helper.apiJsonParamMatcher('versions', ['ver-3']))); + }); + + it('wraps a single asset id into an array before calling the API', function () { + cloudinary.v2.api.restore_by_asset_ids('single-asset-id'); + + sinon.assert.calledWith(mocked.request, sinon.match({ + pathname: sinon.match('resources/restore'), + method: sinon.match('POST') + })); + + sinon.assert.calledWith(mocked.write, sinon.match(helper.apiJsonParamMatcher('asset_ids', ['single-asset-id']))); + }); + }); +});