"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const process_1 = __importDefault(require("process"));
const urllib_1 = __importDefault(require("urllib"));
const tempfile_1 = __importDefault(require("tempfile"));
const fs_1 = __importDefault(require("fs"));
const md5_1 = __importDefault(require("md5"));
const semaphore_promise_1 = require("semaphore-promise");
const crypto_1 = require("crypto");
const chai_1 = require("chai");
const qiniu_1 = require("../qiniu");
const uploader_1 = require("../uploader");
const downloader_1 = require("../downloader");
const kodo_1 = require("../kodo");
const stream_throttle_1 = require("stream-throttle");
process_1.default.on('uncaughtException', (err, origin) => {
    fs_1.default.writeSync(process_1.default.stderr.fd, `Caught exception: ${err}\n` +
        `Exception origin: ${origin}`);
    chai_1.assert.fail();
});
[qiniu_1.KODO_MODE, qiniu_1.S3_MODE].forEach((mode) => {
    describe(`${mode} Adapter`, () => {
        const bucketName = process_1.default.env.QINIU_TEST_BUCKET;
        const bucketRegionId = process_1.default.env.QINIU_TEST_BUCKET_REGION_ID;
        const accessKey = process_1.default.env.QINIU_ACCESS_KEY;
        const secretKey = process_1.default.env.QINIU_SECRET_KEY;
        const originalFileName = '测试文件名.data';
        context('objects operation', () => {
            it('get or getHeaders of unexisted object', async () => {
                const qiniu = new qiniu_1.Qiniu(accessKey, secretKey);
                const qiniuAdapter = qiniu.mode(mode);
                const key = `4k-文件-${Math.floor(Math.random() * (2 ** 64 - 1))}`;
                try {
                    await qiniuAdapter.getObjectInfo(bucketRegionId, { bucket: bucketName, key: key });
                    chai_1.assert.fail();
                }
                catch (_a) {
                }
                try {
                    await qiniuAdapter.getObjectHeader(bucketRegionId, { bucket: bucketName, key: key });
                    chai_1.assert.fail();
                }
                catch (_b) {
                }
            });
            it('moves and copies object', async () => {
                const qiniu = new qiniu_1.Qiniu(accessKey, secretKey);
                const qiniuAdapter = qiniu.mode(mode);
                const buffer = crypto_1.randomBytes(1 << 12);
                const key = `4k-文件-${Math.floor(Math.random() * (2 ** 64 - 1))}`;
                await qiniuAdapter.putObject(bucketRegionId, { bucket: bucketName, key: key }, buffer, originalFileName, { metadata: { 'Key-A': 'Value-A', 'Key-B': 'Value-B' }, contentType: 'application/json' });
                const keyCopied = `${key}-复制`;
                await qiniuAdapter.copyObject(bucketRegionId, { from: { bucket: bucketName, key: key }, to: { bucket: bucketName, key: keyCopied } });
                {
                    const info = await qiniuAdapter.getObjectInfo(bucketRegionId, { bucket: bucketName, key: keyCopied });
                    chai_1.expect(info.size).to.equal(1 << 12);
                }
                {
                    const header = await qiniuAdapter.getObjectHeader(bucketRegionId, { bucket: bucketName, key: keyCopied });
                    chai_1.expect(header.size).to.equal(1 << 12);
                    chai_1.expect(header.metadata['key-a']).to.equal('Value-A');
                    chai_1.expect(header.metadata['key-b']).to.equal('Value-B');
                    chai_1.expect(header.metadata).to.have.all.keys('key-a', 'key-b');
                    chai_1.expect(header.contentType).to.equal('application/json');
                }
                await qiniuAdapter.deleteObject(bucketRegionId, { bucket: bucketName, key: keyCopied });
                const keyMoved = `${key}-move`;
                await qiniuAdapter.moveObject(bucketRegionId, { from: { bucket: bucketName, key: key }, to: { bucket: bucketName, key: keyMoved } });
                {
                    const header = await qiniuAdapter.getObjectInfo(bucketRegionId, { bucket: bucketName, key: keyMoved });
                    chai_1.expect(header.size).to.equal(1 << 12);
                }
                {
                    const header = await qiniuAdapter.getObjectHeader(bucketRegionId, { bucket: bucketName, key: keyMoved });
                    chai_1.expect(header.size).to.equal(1 << 12);
                    chai_1.expect(header.metadata['key-a']).to.equal('Value-A');
                    chai_1.expect(header.metadata['key-b']).to.equal('Value-B');
                    chai_1.expect(header.metadata).to.have.all.keys('key-a', 'key-b');
                    chai_1.expect(header.contentType).to.equal('application/json');
                }
                await qiniuAdapter.deleteObject(bucketRegionId, { bucket: bucketName, key: keyMoved });
                let isExisted = await qiniuAdapter.isExists(bucketRegionId, { bucket: bucketName, key: key });
                chai_1.expect(isExisted).to.equal(false);
                isExisted = await qiniuAdapter.isExists(bucketRegionId, { bucket: bucketName, key: keyCopied });
                chai_1.expect(isExisted).to.equal(false);
                isExisted = await qiniuAdapter.isExists(bucketRegionId, { bucket: bucketName, key: keyMoved });
                chai_1.expect(isExisted).to.equal(false);
            });
            it('moves and copies object by force', async () => {
                const qiniu = new qiniu_1.Qiniu(accessKey, secretKey);
                const qiniuAdapter = qiniu.mode(mode);
                const buffer = crypto_1.randomBytes(1 << 12);
                const key = `4k-文件-${Math.floor(Math.random() * (2 ** 64 - 1))}`;
                await qiniuAdapter.putObject(bucketRegionId, { bucket: bucketName, key: key }, buffer, originalFileName, { metadata: { 'Key-A': 'Value-A', 'Key-B': 'Value-B' }, contentType: 'application/json' });
                const newKey = `${key}-新建`;
                await qiniuAdapter.copyObject(bucketRegionId, { from: { bucket: bucketName, key: key }, to: { bucket: bucketName, key: newKey } });
                let isExisted = await qiniuAdapter.isExists(bucketRegionId, { bucket: bucketName, key: key });
                chai_1.expect(isExisted).to.equal(true);
                isExisted = await qiniuAdapter.isExists(bucketRegionId, { bucket: bucketName, key: newKey });
                chai_1.expect(isExisted).to.equal(true);
                await qiniuAdapter.moveObject(bucketRegionId, { from: { bucket: bucketName, key: key }, to: { bucket: bucketName, key: newKey } });
                isExisted = await qiniuAdapter.isExists(bucketRegionId, { bucket: bucketName, key: key });
                chai_1.expect(isExisted).to.equal(false);
                isExisted = await qiniuAdapter.isExists(bucketRegionId, { bucket: bucketName, key: newKey });
                chai_1.expect(isExisted).to.equal(true);
                await qiniuAdapter.deleteObject(bucketRegionId, { bucket: bucketName, key: newKey });
            });
            it('moves, copies and deletes objects', async () => {
                const qiniu = new qiniu_1.Qiniu(accessKey, secretKey);
                const qiniuAdapter = qiniu.mode(mode);
                const semaphore = new semaphore_promise_1.Semaphore(5);
                const seed = Math.floor(Math.random() * (2 ** 64 - 1));
                const keys = new Array(250).fill('').map((_, idx) => `10b-${seed}-${idx}`);
                const uploadPromises = keys.map((key) => {
                    return new Promise((resolve, reject) => {
                        semaphore.acquire().then((release) => {
                            qiniuAdapter.putObject(bucketRegionId, { bucket: bucketName, key: key }, crypto_1.randomBytes(10), originalFileName)
                                .then(resolve, reject)
                                .finally(() => { release(); });
                        });
                    });
                });
                await Promise.all(uploadPromises);
                {
                    const transferObjects = keys.map((key) => {
                        return { from: { bucket: bucketName, key: key }, to: { bucket: bucketName, key: `${key}-copy` } };
                    });
                    {
                        const indexes = new Set();
                        await qiniuAdapter.copyObjects(bucketRegionId, transferObjects, (index, error) => {
                            indexes.add(index);
                            chai_1.expect(error).to.be.undefined;
                        });
                        chai_1.expect(indexes).to.have.lengthOf(250);
                    }
                    const existsResults = await Promise.all(transferObjects.map((transferObject) => {
                        return new Promise((resolve, reject) => {
                            semaphore.acquire().then((release) => {
                                qiniuAdapter.isExists(bucketRegionId, transferObject.from)
                                    .then(resolve, reject)
                                    .finally(() => { release(); });
                            });
                        });
                    }).concat(transferObjects.map((transferObject) => {
                        return new Promise((resolve, reject) => {
                            semaphore.acquire().then((release) => {
                                qiniuAdapter.isExists(bucketRegionId, transferObject.to)
                                    .then(resolve, reject)
                                    .finally(() => { release(); });
                            });
                        });
                    })));
                    for (const existsResult of existsResults) {
                        chai_1.expect(existsResult).to.equal(true);
                    }
                    await qiniuAdapter.deleteObjects(bucketRegionId, bucketName, transferObjects.map((transferObject) => transferObject.to.key));
                }
                {
                    const transferObjects = keys.map((key) => {
                        return { from: { bucket: bucketName, key: key }, to: { bucket: bucketName, key: `${key}-move` } };
                    });
                    {
                        const indexes = new Set();
                        await qiniuAdapter.moveObjects(bucketRegionId, transferObjects, (index, error) => {
                            indexes.add(index);
                            chai_1.expect(error).to.be.undefined;
                        });
                        chai_1.expect(indexes).to.have.lengthOf(250);
                    }
                    {
                        const existsResults = await Promise.all(transferObjects.map((transferObject) => {
                            return new Promise((resolve, reject) => {
                                semaphore.acquire().then((release) => {
                                    qiniuAdapter.isExists(bucketRegionId, transferObject.from)
                                        .then(resolve, reject)
                                        .finally(() => { release(); });
                                });
                            });
                        }));
                        for (const existsResult of existsResults) {
                            chai_1.expect(existsResult).to.equal(false);
                        }
                    }
                    {
                        const existsResults = await Promise.all(transferObjects.map((transferObject) => {
                            return new Promise((resolve, reject) => {
                                semaphore.acquire().then((release) => {
                                    qiniuAdapter.isExists(bucketRegionId, transferObject.to)
                                        .then(resolve, reject)
                                        .finally(() => { release(); });
                                });
                            });
                        }));
                        for (const existsResult of existsResults) {
                            chai_1.expect(existsResult).to.equal(true);
                        }
                    }
                    await qiniuAdapter.deleteObjects(bucketRegionId, bucketName, transferObjects.map((transferObject) => transferObject.to.key));
                }
            });
            it('unfreeze objects', async () => {
                const qiniu = new qiniu_1.Qiniu(accessKey, secretKey);
                const kodo = new kodo_1.Kodo({ accessKey: accessKey, secretKey: secretKey, regions: [] });
                const qiniuAdapter = qiniu.mode(mode);
                const buffer = crypto_1.randomBytes(1 << 12);
                const key = `4k-文件-${Math.floor(Math.random() * (2 ** 64 - 1))}`;
                await qiniuAdapter.putObject(bucketRegionId, { bucket: bucketName, key: key }, buffer, originalFileName);
                let frozenInfo = await kodo.getFrozenInfo(bucketRegionId, { bucket: bucketName, key: key });
                chai_1.expect(frozenInfo.status).to.equal('Normal');
                await kodo.freeze(bucketRegionId, { bucket: bucketName, key: key });
                frozenInfo = await kodo.getFrozenInfo(bucketRegionId, { bucket: bucketName, key: key });
                chai_1.expect(frozenInfo.status).to.equal('Frozen');
                await kodo.unfreeze(bucketRegionId, { bucket: bucketName, key: key }, 1);
                frozenInfo = await kodo.getFrozenInfo(bucketRegionId, { bucket: bucketName, key: key });
                chai_1.expect(frozenInfo.status).to.equal('Unfreezing');
                await qiniuAdapter.deleteObject(bucketRegionId, { bucket: bucketName, key: key });
            });
            it('list objects', async () => {
                const qiniu = new qiniu_1.Qiniu(accessKey, secretKey);
                const qiniuAdapter = qiniu.mode(mode);
                const semaphore = new semaphore_promise_1.Semaphore(5);
                const seed = Math.floor(Math.random() * (2 ** 64 - 1));
                let keys = [`10b-文件-${seed}/`];
                keys = keys.concat(new Array(250).fill('').map((_, idx) => {
                    let path = keys[0];
                    const idxParts = idx.toString().split('');
                    path += idxParts.join('/');
                    if (idxParts.length < 3) {
                        path += '/';
                    }
                    return path;
                }));
                const uploadPromises = keys.map((key) => {
                    return new Promise((resolve, reject) => {
                        semaphore.acquire().then((release) => {
                            qiniuAdapter.putObject(bucketRegionId, { bucket: bucketName, key: key }, crypto_1.randomBytes(10), originalFileName)
                                .then(resolve, reject)
                                .finally(() => { release(); });
                        });
                    });
                });
                await Promise.all(uploadPromises);
                let listedObjects = await qiniuAdapter.listObjects(bucketRegionId, bucketName, keys[0], { minKeys: 100, maxKeys: 20 });
                chai_1.expect(listedObjects.objects).to.have.lengthOf(100);
                listedObjects = await qiniuAdapter.listObjects(bucketRegionId, bucketName, keys[0], { minKeys: 110, maxKeys: 20 });
                chai_1.expect(listedObjects.objects).to.have.lengthOf(110);
                listedObjects = await qiniuAdapter.listObjects(bucketRegionId, bucketName, keys[0], { minKeys: 250, maxKeys: 20, delimiter: '/' });
                chai_1.expect(listedObjects.objects).to.have.lengthOf(1);
                chai_1.expect(listedObjects.commonPrefixes).to.have.lengthOf(10);
                listedObjects = await qiniuAdapter.listObjects(bucketRegionId, bucketName, `${keys[0]}1/`, { minKeys: 250, maxKeys: 20, delimiter: '/' });
                chai_1.expect(listedObjects.objects).to.have.lengthOf(1);
                chai_1.expect(listedObjects.commonPrefixes).to.have.lengthOf(10);
                listedObjects = await qiniuAdapter.listObjects(bucketRegionId, bucketName, `${keys[0]}1/1/`, { minKeys: 250, maxKeys: 20, delimiter: '/' });
                chai_1.expect(listedObjects.objects).to.have.lengthOf(11);
                chai_1.expect(listedObjects.commonPrefixes).to.be.undefined;
            });
        });
        context('objects upload / download', () => {
            it('uploads and gets object', async () => {
                const qiniu = new qiniu_1.Qiniu(accessKey, secretKey);
                const qiniuAdapter = qiniu.mode(mode);
                const buffer = crypto_1.randomBytes(1 << 12);
                const key = `4k-文件-${Math.floor(Math.random() * (2 ** 64 - 1))}`;
                const throttle = new stream_throttle_1.Throttle({ rate: 1 << 30 });
                let loaded = 0;
                await qiniuAdapter.putObject(bucketRegionId, { bucket: bucketName, key: key }, buffer, originalFileName, { metadata: { 'Key-A': 'Value-A', 'Key-B': 'Value-B' }, contentType: 'application/json' }, {
                    progressCallback: (uploaded, total) => {
                        chai_1.expect(total).to.at.least(buffer.length);
                        loaded = uploaded;
                    },
                    throttle: throttle,
                });
                chai_1.expect(loaded).to.at.least(buffer.length);
                let isExisted = await qiniuAdapter.isExists(bucketRegionId, { bucket: bucketName, key: key });
                chai_1.expect(isExisted).to.equal(true);
                {
                    const url = await qiniuAdapter.getObjectURL(bucketRegionId, { bucket: bucketName, key: key }, undefined, new Date(Date.now() + 86400000));
                    const response = await urllib_1.default.request(url.toString(), { method: 'GET', streaming: true });
                    chai_1.expect(response.status).to.equal(200);
                    if (mode == qiniu_1.KODO_MODE) {
                        chai_1.expect(response.headers['x-qn-meta-key-a']).to.equal('Value-A');
                        chai_1.expect(response.headers['x-qn-meta-key-b']).to.equal('Value-B');
                    }
                    else if (mode == qiniu_1.S3_MODE) {
                        chai_1.expect(response.headers['x-amz-meta-key-a']).to.equal('Value-A');
                        chai_1.expect(response.headers['x-amz-meta-key-b']).to.equal('Value-B');
                    }
                    chai_1.expect(response.headers['content-type']).to.equal('application/json');
                    response.res.destroy();
                }
                {
                    const result = await qiniuAdapter.getObject(bucketRegionId, { bucket: bucketName, key: key });
                    chai_1.expect(result.data).to.eql(buffer);
                    chai_1.expect(result.header.size).to.equal(1 << 12);
                    chai_1.expect(result.header.metadata['key-a']).to.equal('Value-A');
                    chai_1.expect(result.header.metadata['key-b']).to.equal('Value-B');
                    chai_1.expect(result.header.metadata).to.have.all.keys('key-a', 'key-b');
                    chai_1.expect(result.header.contentType).to.equal('application/json');
                }
                {
                    let dataLength = 0;
                    const readable = await qiniuAdapter.getObjectStream(bucketRegionId, { bucket: bucketName, key: key });
                    await new Promise((resolve, reject) => {
                        readable.on('data', (chunk) => {
                            dataLength += chunk.length;
                        });
                        readable.on('end', () => {
                            chai_1.expect(dataLength).to.equal(1 << 12);
                            resolve();
                        });
                        readable.on('error', reject);
                    });
                }
                {
                    const info = await qiniuAdapter.getObjectInfo(bucketRegionId, { bucket: bucketName, key: key });
                    chai_1.expect(info.size).to.equal(1 << 12);
                }
                {
                    const header = await qiniuAdapter.getObjectHeader(bucketRegionId, { bucket: bucketName, key: key });
                    chai_1.expect(header.size).to.equal(1 << 12);
                    chai_1.expect(header.metadata['key-a']).to.equal('Value-A');
                    chai_1.expect(header.metadata['key-b']).to.equal('Value-B');
                    chai_1.expect(header.metadata).to.have.all.keys('key-a', 'key-b');
                    chai_1.expect(header.contentType).to.equal('application/json');
                }
                await qiniuAdapter.deleteObject(bucketRegionId, { bucket: bucketName, key: key });
                isExisted = await qiniuAdapter.isExists(bucketRegionId, { bucket: bucketName, key: key });
                chai_1.expect(isExisted).to.equal(false);
            });
            it('uploads and gets big object', async () => {
                const qiniu = new qiniu_1.Qiniu(accessKey, secretKey);
                const qiniuAdapter = qiniu.mode(mode);
                const buffer = crypto_1.randomBytes((1 << 20) * 8);
                const key = `8m-${Math.floor(Math.random() * (2 ** 64 - 1))}`;
                await qiniuAdapter.putObject(bucketRegionId, { bucket: bucketName, key: key }, buffer, originalFileName, { metadata: { 'Key-A': 'Value-A', 'Key-B': 'Value-B' }, contentType: 'application/json' });
                {
                    let dataLength = 0;
                    const readable = await qiniuAdapter.getObjectStream(bucketRegionId, { bucket: bucketName, key: key });
                    await new Promise((resolve, reject) => {
                        readable.on('data', (chunk) => {
                            dataLength += chunk.length;
                        });
                        readable.on('end', () => {
                            chai_1.expect(dataLength).to.equal((1 << 20) * 8);
                            resolve();
                        });
                        readable.on('error', reject);
                    });
                }
                {
                    let dataLength = 0;
                    const readable = await qiniuAdapter.getObjectStream(bucketRegionId, { bucket: bucketName, key: key }, undefined, { rangeStart: (1 << 20), rangeEnd: (1 << 20) * 2 });
                    await new Promise((resolve, reject) => {
                        readable.on('data', (chunk) => {
                            dataLength += chunk.length;
                        });
                        readable.on('end', () => {
                            chai_1.expect(dataLength).to.equal((1 << 20) + 1);
                            resolve();
                        });
                        readable.on('error', reject);
                    });
                }
            });
            it('upload data by chunk', async () => {
                const qiniu = new qiniu_1.Qiniu(accessKey, secretKey);
                const qiniuAdapter = qiniu.mode(mode);
                const key = `2m-文件-${Math.floor(Math.random() * (2 ** 64 - 1))}`;
                const setHeader = { metadata: { 'Key-A': 'Value-A', 'Key-B': 'Value-B' }, contentType: 'application/json' };
                const throttleGroup = new stream_throttle_1.ThrottleGroup({ rate: 1 << 30 });
                const createResult = await qiniuAdapter.createMultipartUpload(bucketRegionId, { bucket: bucketName, key: key }, originalFileName, setHeader);
                const buffer_1 = crypto_1.randomBytes(1 << 20);
                let loaded = 0;
                const uploadPartResult_1 = await qiniuAdapter.uploadPart(bucketRegionId, { bucket: bucketName, key: key }, createResult.uploadId, 1, buffer_1, {
                    progressCallback: (uploaded, total) => {
                        chai_1.expect(total).to.equal(buffer_1.length);
                        loaded = uploaded;
                    },
                    throttle: throttleGroup.throttle({ rate: 1 << 30 }),
                });
                chai_1.expect(loaded).to.equal(buffer_1.length);
                const buffer_2 = crypto_1.randomBytes(1 << 20);
                loaded = 0;
                const uploadPartResult_2 = await qiniuAdapter.uploadPart(bucketRegionId, { bucket: bucketName, key: key }, createResult.uploadId, 2, buffer_2, {
                    progressCallback: (uploaded, total) => {
                        chai_1.expect(total).to.equal(buffer_2.length);
                        loaded = uploaded;
                    },
                    throttle: throttleGroup.throttle({ rate: 1 << 30 }),
                });
                chai_1.expect(loaded).to.equal(buffer_2.length);
                await qiniuAdapter.completeMultipartUpload(bucketRegionId, { bucket: bucketName, key: key }, createResult.uploadId, [{ partNumber: 1, etag: uploadPartResult_1.etag }, { partNumber: 2, etag: uploadPartResult_2.etag }], originalFileName, setHeader);
                const header = await qiniuAdapter.getObjectHeader(bucketRegionId, { bucket: bucketName, key: key });
                chai_1.expect(header.metadata['key-a']).to.equal('Value-A');
                chai_1.expect(header.metadata['key-b']).to.equal('Value-B');
                chai_1.expect(header.contentType).to.equal('application/json');
                {
                    const downloader = new downloader_1.Downloader(qiniuAdapter);
                    const targetFilePath = tempfile_1.default();
                    let fileDownloaded = 0;
                    await downloader.getObjectToFile(bucketRegionId, { bucket: bucketName, key: key }, targetFilePath, undefined, {
                        getCallback: {
                            progressCallback: (downloaded, total) => {
                                chai_1.expect(total).to.equal((1 << 20) * 2);
                                fileDownloaded = downloaded;
                            },
                            headerCallback: (header) => {
                                chai_1.expect(header.size).to.equal((1 << 20) * 2);
                            },
                        },
                        partSize: 1 << 20,
                        chunkTimeout: 3000,
                        downloadThrottleOption: { rate: 1 << 30 },
                    });
                    chai_1.expect(fileDownloaded).to.equal((1 << 20) * 2);
                }
                await qiniuAdapter.deleteObject(bucketRegionId, { bucket: bucketName, key: key });
            });
            it('upload object by uploader and download by downloader', async () => {
                const qiniu = new qiniu_1.Qiniu(accessKey, secretKey);
                const qiniuAdapter = qiniu.mode(mode);
                const key = `11m-文件-${Math.floor(Math.random() * (2 ** 64 - 1))}`;
                const tmpfilePath = tempfile_1.default();
                const tmpfile = await fs_1.default.promises.open(tmpfilePath, 'w+');
                try {
                    await tmpfile.write(crypto_1.randomBytes((1 << 20) * 11));
                    const uploader = new uploader_1.Uploader(qiniuAdapter);
                    let fileUploaded = 0;
                    const filePartUploaded = new Set();
                    await uploader.putObjectFromFile(bucketRegionId, { bucket: bucketName, key: key }, tmpfile, (1 << 20) * 11, originalFileName, {
                        header: { metadata: { 'Key-A': 'Value-A', 'Key-B': 'Value-B' }, contentType: 'application/json' },
                        putCallback: {
                            partsInitCallback: (info) => {
                                chai_1.expect(info.uploadId).to.be.ok;
                                chai_1.expect(info.parts).to.be.empty;
                            },
                            progressCallback: (uploaded, total) => {
                                chai_1.expect(total).to.equal((1 << 20) * 11);
                                fileUploaded = uploaded;
                            },
                            partPutCallback: (part) => {
                                filePartUploaded.add(part.partNumber);
                            },
                        },
                        uploadThrottleOption: { rate: 1 << 30 },
                    });
                    chai_1.expect(fileUploaded).to.equal((1 << 20) * 11);
                    chai_1.expect(filePartUploaded).to.have.lengthOf(3);
                    {
                        const isExisted = await qiniuAdapter.isExists(bucketRegionId, { bucket: bucketName, key: key });
                        chai_1.expect(isExisted).to.equal(true);
                    }
                    {
                        const header = await qiniuAdapter.getObjectHeader(bucketRegionId, { bucket: bucketName, key: key });
                        chai_1.expect(header.metadata['key-a']).to.equal('Value-A');
                        chai_1.expect(header.metadata['key-b']).to.equal('Value-B');
                        chai_1.expect(header.contentType).to.equal('application/json');
                    }
                    {
                        const downloader = new downloader_1.Downloader(qiniuAdapter);
                        const targetFilePath = tempfile_1.default();
                        let fileDownloaded = 0;
                        await downloader.getObjectToFile(bucketRegionId, { bucket: bucketName, key: key }, targetFilePath, undefined, {
                            getCallback: {
                                progressCallback: (downloaded, total) => {
                                    chai_1.expect(total).to.equal((1 << 20) * 11);
                                    fileDownloaded = downloaded;
                                },
                                headerCallback: (header) => {
                                    chai_1.expect(header.size).to.equal((1 << 20) * 11);
                                },
                            },
                            partSize: 1 << 20,
                            chunkTimeout: 3000,
                        });
                        chai_1.expect(fileDownloaded).to.equal((1 << 20) * 11);
                        const md5FromSource = await new Promise((resolve, reject) => {
                            fs_1.default.readFile(tmpfilePath, { encoding: 'binary' }, (err, buf) => {
                                if (err) {
                                    reject(err);
                                    return;
                                }
                                resolve(md5_1.default(buf, { encoding: 'binary', asBytes: true }));
                            });
                        });
                        const md5FromObject = await new Promise((resolve, reject) => {
                            fs_1.default.readFile(targetFilePath, { encoding: 'binary' }, (err, buf) => {
                                if (err) {
                                    reject(err);
                                    return;
                                }
                                resolve(md5_1.default(buf, { encoding: 'binary', asBytes: true }));
                            });
                        });
                        chai_1.expect(md5FromSource).to.eql(md5FromObject);
                    }
                    await qiniuAdapter.deleteObject(bucketRegionId, { bucket: bucketName, key: key });
                }
                finally {
                    await tmpfile.close();
                }
            });
            it('upload object and then cancel', async () => {
                const qiniu = new qiniu_1.Qiniu(accessKey, secretKey);
                const qiniuAdapter = qiniu.mode(mode);
                const key = `100m-文件-${Math.floor(Math.random() * (2 ** 64 - 1))}`;
                const tmpfilePath = tempfile_1.default();
                const tmpfile = await fs_1.default.promises.open(tmpfilePath, 'w+');
                try {
                    await tmpfile.write(crypto_1.randomBytes((1 << 20) * 100));
                    const uploader = new uploader_1.Uploader(qiniuAdapter);
                    const promise = uploader.putObjectFromFile(bucketRegionId, { bucket: bucketName, key: key }, tmpfile, (1 << 20) * 100, originalFileName);
                    await new Promise((resolve, reject) => {
                        setTimeout(() => {
                            promise.then(reject, resolve);
                            uploader.abort();
                        }, 1000);
                    });
                    try {
                        await uploader.putObjectFromFile(bucketRegionId, { bucket: bucketName, key: key }, tmpfile, (1 << 20) * 100, originalFileName, {
                            putCallback: {
                                progressCallback: (_uploaded, _total) => {
                                    throw new Error('Test Error 1');
                                },
                            },
                        });
                        chai_1.assert.fail();
                    }
                    catch (err) {
                        chai_1.expect(err.message).to.include('Test Error 1');
                    }
                    try {
                        await uploader.putObjectFromFile(bucketRegionId, { bucket: bucketName, key: key }, tmpfile, (1 << 20) * 100, originalFileName, {
                            putCallback: {
                                partsInitCallback: (_initInfo) => {
                                    throw new Error('Test Error 2');
                                },
                            },
                        });
                        chai_1.assert.fail();
                    }
                    catch (err) {
                        chai_1.expect(err.message).to.include('Test Error 2');
                    }
                    try {
                        await uploader.putObjectFromFile(bucketRegionId, { bucket: bucketName, key: key }, tmpfile, (1 << 20) * 100, originalFileName, {
                            putCallback: {
                                partPutCallback: (_part) => {
                                    throw new Error('Test Error 3');
                                },
                            },
                        });
                        chai_1.assert.fail();
                    }
                    catch (err) {
                        chai_1.expect(err.message).to.include('Test Error 3');
                    }
                }
                finally {
                    await tmpfile.close();
                }
            });
            it('download object and then cancel', async () => {
                const qiniu = new qiniu_1.Qiniu(accessKey, secretKey);
                const qiniuAdapter = qiniu.mode(mode);
                const key = `11m-文件-${Math.floor(Math.random() * (2 ** 64 - 1))}`;
                const tmpfilePath = tempfile_1.default();
                const tmpfile = await fs_1.default.promises.open(tmpfilePath, 'w+');
                try {
                    await tmpfile.write(crypto_1.randomBytes((1 << 20) * 11));
                    const uploader = new uploader_1.Uploader(qiniuAdapter);
                    await uploader.putObjectFromFile(bucketRegionId, { bucket: bucketName, key: key }, tmpfile, (1 << 20) * 11, originalFileName);
                    const downloader = new downloader_1.Downloader(qiniuAdapter);
                    const targetFilePath = tempfile_1.default();
                    const promise = downloader.getObjectToFile(bucketRegionId, { bucket: bucketName, key: key }, targetFilePath, undefined, {
                        partSize: 1 << 20,
                    });
                    await new Promise((resolve, reject) => {
                        setTimeout(() => {
                            promise.then(reject, resolve);
                            downloader.abort();
                        }, 1000);
                    });
                    try {
                        await downloader.getObjectToFile(bucketRegionId, { bucket: bucketName, key: key }, targetFilePath, undefined, {
                            getCallback: {
                                progressCallback: (_downloaded, _total) => {
                                    throw new Error('Test Error 4');
                                },
                            },
                            partSize: 1 << 20,
                        });
                        chai_1.assert.fail();
                    }
                    catch (err) {
                        chai_1.expect(err.message).to.include('Test Error 4');
                    }
                    try {
                        await downloader.getObjectToFile(bucketRegionId, { bucket: bucketName, key: key }, targetFilePath, undefined, {
                            getCallback: {
                                headerCallback: (_header) => {
                                    throw new Error('Test Error 5');
                                },
                            },
                            partSize: 1 << 20,
                        });
                        chai_1.assert.fail();
                    }
                    catch (err) {
                        chai_1.expect(err.message).to.include('Test Error 5');
                    }
                    try {
                        await downloader.getObjectToFile(bucketRegionId, { bucket: bucketName, key: key }, targetFilePath, undefined, {
                            getCallback: {
                                partGetCallback: (_partSize) => {
                                    throw new Error('Test Error 6');
                                },
                            },
                            partSize: 1 << 20,
                        });
                        chai_1.assert.fail();
                    }
                    catch (err) {
                        chai_1.expect(err.message).to.include('Test Error 6');
                    }
                }
                finally {
                    await tmpfile.close();
                }
            });
            it('recover object by uploader', async () => {
                const qiniu = new qiniu_1.Qiniu(accessKey, secretKey);
                const qiniuAdapter = qiniu.mode(mode);
                const key = `11m-文件-${Math.floor(Math.random() * (2 ** 64 - 1))}`;
                const tmpfilePath = tempfile_1.default();
                const tmpfile = await fs_1.default.promises.open(tmpfilePath, 'w+');
                try {
                    await tmpfile.write(crypto_1.randomBytes((1 << 20) * 11));
                    const createResult = await qiniuAdapter.createMultipartUpload(bucketRegionId, { bucket: bucketName, key: key }, originalFileName, { metadata: { 'Key-A': 'Value-A', 'Key-B': 'Value-B' } });
                    const { buffer: buffer_1 } = await tmpfile.read(Buffer.alloc(1 << 22), 0, 1 << 22, 1 << 22);
                    const uploadPartResult_1 = await qiniuAdapter.uploadPart(bucketRegionId, { bucket: bucketName, key: key }, createResult.uploadId, 2, buffer_1);
                    const { buffer: buffer_2 } = await tmpfile.read(Buffer.alloc((1 << 20) * 3), 0, (1 << 20) * 3, (1 << 22) * 2);
                    const uploadPartResult_2 = await qiniuAdapter.uploadPart(bucketRegionId, { bucket: bucketName, key: key }, createResult.uploadId, 3, buffer_2);
                    const uploader = new uploader_1.Uploader(qiniuAdapter);
                    let fileUploaded = 0;
                    const filePartUploaded = new Set();
                    await uploader.putObjectFromFile(bucketRegionId, { bucket: bucketName, key: key }, tmpfile, (1 << 20) * 11, originalFileName, {
                        header: { metadata: { 'Key-A': 'Value-A', 'Key-B': 'Value-B' } },
                        recovered: {
                            uploadId: createResult.uploadId,
                            parts: [
                                { partNumber: 2, etag: uploadPartResult_1.etag },
                                { partNumber: 3, etag: uploadPartResult_2.etag },
                            ],
                        },
                        putCallback: {
                            partsInitCallback: (info) => {
                                chai_1.expect(info.uploadId).to.equal(createResult.uploadId);
                                chai_1.expect(info.parts).to.have.lengthOf(2);
                            },
                            progressCallback: (uploaded, total) => {
                                chai_1.expect(total).to.equal((1 << 20) * 11);
                                fileUploaded = uploaded;
                            },
                            partPutCallback: (part) => {
                                filePartUploaded.add(part.partNumber);
                            },
                        },
                    });
                    chai_1.expect(fileUploaded).to.equal((1 << 20) * 11);
                    chai_1.expect(filePartUploaded).to.have.lengthOf(1);
                    const header = await qiniuAdapter.getObjectHeader(bucketRegionId, { bucket: bucketName, key: key });
                    chai_1.expect(header.metadata['key-a']).to.equal('Value-A');
                    chai_1.expect(header.metadata['key-b']).to.equal('Value-B');
                    {
                        const downloader = new downloader_1.Downloader(qiniuAdapter);
                        const targetFilePath = tempfile_1.default();
                        let fileDownloaded = 0;
                        await downloader.getObjectToFile(bucketRegionId, { bucket: bucketName, key: key }, targetFilePath, undefined, {
                            getCallback: {
                                progressCallback: (downloaded, total) => {
                                    chai_1.expect(total).to.equal((1 << 20) * 11);
                                    fileDownloaded = downloaded;
                                },
                                headerCallback: (header) => {
                                    chai_1.expect(header.size).to.equal((1 << 20) * 11);
                                },
                            },
                            partSize: 1 << 20,
                            chunkTimeout: 3000,
                        });
                        chai_1.expect(fileDownloaded).to.equal((1 << 20) * 11);
                        const md5FromSource = await new Promise((resolve, reject) => {
                            fs_1.default.readFile(tmpfilePath, { encoding: 'binary' }, (err, buf) => {
                                if (err) {
                                    reject(err);
                                    return;
                                }
                                resolve(md5_1.default(buf, { encoding: 'binary', asBytes: true }));
                            });
                        });
                        const md5FromObject = await new Promise((resolve, reject) => {
                            fs_1.default.readFile(targetFilePath, { encoding: 'binary' }, (err, buf) => {
                                if (err) {
                                    reject(err);
                                    return;
                                }
                                resolve(md5_1.default(buf, { encoding: 'binary', asBytes: true }));
                            });
                        });
                        chai_1.expect(md5FromSource).to.eql(md5FromObject);
                    }
                    await qiniuAdapter.deleteObject(bucketRegionId, { bucket: bucketName, key: key });
                }
                finally {
                    await tmpfile.close();
                }
            });
            it('upload small object by uploader', async () => {
                const qiniu = new qiniu_1.Qiniu(accessKey, secretKey);
                const qiniuAdapter = qiniu.mode(mode);
                const key = `11k-文件-${Math.floor(Math.random() * (2 ** 64 - 1))}`;
                const tmpfilePath = tempfile_1.default();
                const tmpfile = await fs_1.default.promises.open(tmpfilePath, 'w+');
                try {
                    await tmpfile.write(crypto_1.randomBytes((1 << 10) * 11));
                    const uploader = new uploader_1.Uploader(qiniuAdapter);
                    let fileUploaded = 0;
                    await uploader.putObjectFromFile(bucketRegionId, { bucket: bucketName, key: key }, tmpfile, (1 << 10) * 11, originalFileName, {
                        header: { metadata: { 'Key-A': 'Value-A', 'Key-B': 'Value-B' } },
                        putCallback: {
                            progressCallback: (uploaded, total) => {
                                chai_1.expect(total).to.at.least((1 << 10) * 11);
                                fileUploaded = uploaded;
                            },
                        },
                    });
                    chai_1.expect(fileUploaded).to.at.least((1 << 10) * 11);
                    const header = await qiniuAdapter.getObjectHeader(bucketRegionId, { bucket: bucketName, key: key });
                    chai_1.expect(header.metadata['key-a']).to.equal('Value-A');
                    chai_1.expect(header.metadata['key-b']).to.equal('Value-B');
                    {
                        const downloader = new downloader_1.Downloader(qiniuAdapter);
                        const targetFilePath = tempfile_1.default();
                        let fileDownloaded = 0;
                        await downloader.getObjectToFile(bucketRegionId, { bucket: bucketName, key: key }, targetFilePath, undefined, {
                            getCallback: {
                                progressCallback: (downloaded, total) => {
                                    chai_1.expect(total).to.equal((1 << 10) * 11);
                                    fileDownloaded = downloaded;
                                },
                                headerCallback: (header) => {
                                    chai_1.expect(header.size).to.equal((1 << 10) * 11);
                                },
                            },
                            partSize: 1 << 10,
                            chunkTimeout: 3000,
                        });
                        chai_1.expect(fileDownloaded).to.equal((1 << 10) * 11);
                        const md5FromSource = await new Promise((resolve, reject) => {
                            fs_1.default.readFile(tmpfilePath, { encoding: 'binary' }, (err, buf) => {
                                if (err) {
                                    reject(err);
                                    return;
                                }
                                resolve(md5_1.default(buf, { encoding: 'binary', asBytes: true }));
                            });
                        });
                        const md5FromObject = await new Promise((resolve, reject) => {
                            fs_1.default.readFile(targetFilePath, { encoding: 'binary' }, (err, buf) => {
                                if (err) {
                                    reject(err);
                                    return;
                                }
                                resolve(md5_1.default(buf, { encoding: 'binary', asBytes: true }));
                            });
                        });
                        chai_1.expect(md5FromSource).to.eql(md5FromObject);
                    }
                    await qiniuAdapter.deleteObject(bucketRegionId, { bucket: bucketName, key: key });
                }
                finally {
                    await tmpfile.close();
                }
            });
        });
        context('bucket', () => {
            it('creates a bucket and drops it', async () => {
                const qiniu = new qiniu_1.Qiniu(accessKey, secretKey);
                const qiniuAdapter = qiniu.mode(mode);
                const bucketName = `test-bucket-${Math.floor(Math.random() * (2 ** 64 - 1))}`;
                await qiniuAdapter.createBucket(bucketRegionId, bucketName);
                const regionId = await qiniuAdapter.getBucketLocation(bucketName);
                chai_1.expect(regionId).to.equal(bucketRegionId);
                await qiniuAdapter.deleteBucket(bucketRegionId, bucketName);
                try {
                    await qiniuAdapter.getBucketLocation(bucketName);
                    chai_1.assert.fail();
                }
                catch (_a) {
                }
            });
            it('lists all buckets', async () => {
                const qiniu = new qiniu_1.Qiniu(accessKey, secretKey);
                const qiniuAdapter = qiniu.mode(mode);
                const buckets = await qiniuAdapter.listBuckets();
                const bucket = buckets.find((bucket) => bucket.name === bucketName);
                chai_1.expect(bucket === null || bucket === void 0 ? void 0 : bucket.regionId).to.equal(bucketRegionId);
                chai_1.expect(bucket === null || bucket === void 0 ? void 0 : bucket.grantedPermission).to.be.undefined;
            });
            it('lists domain', async () => {
                const qiniu = new qiniu_1.Qiniu(accessKey, secretKey);
                const qiniuAdapter = qiniu.mode(mode);
                const domains = await qiniuAdapter.listDomains(bucketRegionId, bucketName);
                if (mode === qiniu_1.KODO_MODE) {
                    chai_1.expect(domains).to.have.lengthOf.at.least(1);
                }
                else {
                    chai_1.expect(domains).to.be.empty;
                }
            });
        });
    });
});
//# sourceMappingURL=adapter.js.map