"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const chai_1 = require("chai");
const fs_extra_1 = require("fs-extra");
const path_1 = require("path");
const core_utils_1 = require("../../../../shared/core-utils");
const extra_utils_1 = require("../../../../shared/extra-utils");
const server_commands_1 = require("../../../../shared/server-commands");
describe('Test resumable upload', function () {
    const defaultFixture = 'video_short.mp4';
    let server;
    let rootId;
    let userAccessToken;
    let userChannelId;
    function buildSize(fixture, size) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (size !== undefined)
                return size;
            const baseFixture = (0, core_utils_1.buildAbsoluteFixturePath)(fixture);
            return (yield (0, fs_extra_1.stat)(baseFixture)).size;
        });
    }
    function prepareUpload(options = {}) {
        var _a;
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const { token, originalName, lastModified } = options;
            const size = yield buildSize(defaultFixture, options.size);
            const attributes = {
                name: 'video',
                channelId: (_a = options.channelId) !== null && _a !== void 0 ? _a : server.store.channel.id,
                privacy: 1,
                fixture: defaultFixture
            };
            const mimetype = 'video/mp4';
            const res = yield server.videos.prepareResumableUpload({ token, attributes, size, mimetype, originalName, lastModified });
            return res.header['location'].split('?')[1];
        });
    }
    function sendChunks(options) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const { token, pathUploadId, expectedStatus, contentLength, contentRangeBuilder, digestBuilder } = options;
            const size = yield buildSize(defaultFixture, options.size);
            const absoluteFilePath = (0, core_utils_1.buildAbsoluteFixturePath)(defaultFixture);
            return server.videos.sendResumableChunks({
                token,
                pathUploadId,
                videoFilePath: absoluteFilePath,
                size,
                contentLength,
                contentRangeBuilder,
                digestBuilder,
                expectedStatus
            });
        });
    }
    function checkFileSize(uploadIdArg, expectedSize) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const uploadId = uploadIdArg.replace(/^upload_id=/, '');
            const subPath = (0, path_1.join)('tmp', 'resumable-uploads', uploadId);
            const filePath = server.servers.buildDirectory(subPath);
            const exists = yield (0, fs_extra_1.pathExists)(filePath);
            if (expectedSize === null) {
                (0, chai_1.expect)(exists).to.be.false;
                return;
            }
            (0, chai_1.expect)(exists).to.be.true;
            (0, chai_1.expect)((yield (0, fs_extra_1.stat)(filePath)).size).to.equal(expectedSize);
        });
    }
    function countResumableUploads() {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const subPath = (0, path_1.join)('tmp', 'resumable-uploads');
            const filePath = server.servers.buildDirectory(subPath);
            const files = yield (0, fs_extra_1.readdir)(filePath);
            return files.length;
        });
    }
    before(function () {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            this.timeout(30000);
            server = yield (0, server_commands_1.createSingleServer)(1);
            yield (0, server_commands_1.setAccessTokensToServers)([server]);
            yield (0, server_commands_1.setDefaultVideoChannel)([server]);
            const body = yield server.users.getMyInfo();
            rootId = body.id;
            {
                userAccessToken = yield server.users.generateUserAndToken('user1');
                const { videoChannels } = yield server.users.getMyInfo({ token: userAccessToken });
                userChannelId = videoChannels[0].id;
            }
            yield server.users.update({ userId: rootId, videoQuota: 10000000 });
        });
    });
    describe('Directory cleaning', function () {
        it('Should not delete files after an unfinished upload', function () {
            return tslib_1.__awaiter(this, void 0, void 0, function* () {
                yield prepareUpload();
                (0, chai_1.expect)(yield countResumableUploads()).to.equal(2);
            });
        });
        it('Should not delete recent uploads', function () {
            return tslib_1.__awaiter(this, void 0, void 0, function* () {
                yield server.debug.sendCommand({ body: { command: 'remove-dandling-resumable-uploads' } });
                (0, chai_1.expect)(yield countResumableUploads()).to.equal(2);
            });
        });
        it('Should delete old uploads', function () {
            return tslib_1.__awaiter(this, void 0, void 0, function* () {
                yield server.debug.sendCommand({ body: { command: 'remove-dandling-resumable-uploads' } });
                (0, chai_1.expect)(yield countResumableUploads()).to.equal(0);
            });
        });
    });
    describe('Resumable upload and chunks', function () {
        it('Should accept the same amount of chunks', function () {
            return tslib_1.__awaiter(this, void 0, void 0, function* () {
                const uploadId = yield prepareUpload();
                yield sendChunks({ pathUploadId: uploadId });
                yield checkFileSize(uploadId, null);
            });
        });
        it('Should not accept more chunks than expected', function () {
            return tslib_1.__awaiter(this, void 0, void 0, function* () {
                const uploadId = yield prepareUpload({ size: 100 });
                yield sendChunks({ pathUploadId: uploadId, expectedStatus: 409 });
                yield checkFileSize(uploadId, 0);
            });
        });
        it('Should not accept more chunks than expected with an invalid content length/content range', function () {
            return tslib_1.__awaiter(this, void 0, void 0, function* () {
                const uploadId = yield prepareUpload({ size: 1500 });
                try {
                    yield sendChunks({ pathUploadId: uploadId, expectedStatus: 409, contentLength: 1000 });
                    yield checkFileSize(uploadId, 0);
                }
                catch (_a) {
                    yield sendChunks({ pathUploadId: uploadId, expectedStatus: 400, contentLength: 1000 });
                    yield checkFileSize(uploadId, 0);
                }
            });
        });
        it('Should not accept more chunks than expected with an invalid content length', function () {
            return tslib_1.__awaiter(this, void 0, void 0, function* () {
                const uploadId = yield prepareUpload({ size: 500 });
                const size = 1000;
                const expectedStatus = process.version.startsWith('v16')
                    ? 409
                    : 400;
                const contentRangeBuilder = (start) => `bytes ${start}-${start + size - 1}/${size}`;
                yield sendChunks({ pathUploadId: uploadId, expectedStatus, contentRangeBuilder, contentLength: size });
                yield checkFileSize(uploadId, 0);
            });
        });
        it('Should be able to accept 2 PUT requests', function () {
            return tslib_1.__awaiter(this, void 0, void 0, function* () {
                const uploadId = yield prepareUpload();
                const result1 = yield sendChunks({ pathUploadId: uploadId });
                const result2 = yield sendChunks({ pathUploadId: uploadId });
                (0, chai_1.expect)(result1.body.video.uuid).to.exist;
                (0, chai_1.expect)(result1.body.video.uuid).to.equal(result2.body.video.uuid);
                (0, chai_1.expect)(result1.headers['x-resumable-upload-cached']).to.not.exist;
                (0, chai_1.expect)(result2.headers['x-resumable-upload-cached']).to.equal('true');
                yield checkFileSize(uploadId, null);
            });
        });
        it('Should not have the same upload id with 2 different users', function () {
            return tslib_1.__awaiter(this, void 0, void 0, function* () {
                const originalName = 'toto.mp4';
                const lastModified = new Date().getTime();
                const uploadId1 = yield prepareUpload({ originalName, lastModified, token: server.accessToken });
                const uploadId2 = yield prepareUpload({ originalName, lastModified, channelId: userChannelId, token: userAccessToken });
                (0, chai_1.expect)(uploadId1).to.not.equal(uploadId2);
            });
        });
        it('Should have the same upload id with the same user', function () {
            return tslib_1.__awaiter(this, void 0, void 0, function* () {
                const originalName = 'toto.mp4';
                const lastModified = new Date().getTime();
                const uploadId1 = yield prepareUpload({ originalName, lastModified });
                const uploadId2 = yield prepareUpload({ originalName, lastModified });
                (0, chai_1.expect)(uploadId1).to.equal(uploadId2);
            });
        });
        it('Should not cache a request with 2 different users', function () {
            return tslib_1.__awaiter(this, void 0, void 0, function* () {
                const originalName = 'toto.mp4';
                const lastModified = new Date().getTime();
                const uploadId = yield prepareUpload({ originalName, lastModified, token: server.accessToken });
                yield sendChunks({ pathUploadId: uploadId, token: server.accessToken });
                yield sendChunks({ pathUploadId: uploadId, token: userAccessToken, expectedStatus: 403 });
            });
        });
        it('Should not cache a request after a delete', function () {
            return tslib_1.__awaiter(this, void 0, void 0, function* () {
                const originalName = 'toto.mp4';
                const lastModified = new Date().getTime();
                const uploadId1 = yield prepareUpload({ originalName, lastModified, token: server.accessToken });
                yield sendChunks({ pathUploadId: uploadId1 });
                yield server.videos.endResumableUpload({ pathUploadId: uploadId1 });
                const uploadId2 = yield prepareUpload({ originalName, lastModified, token: server.accessToken });
                (0, chai_1.expect)(uploadId1).to.equal(uploadId2);
                const result2 = yield sendChunks({ pathUploadId: uploadId1 });
                (0, chai_1.expect)(result2.headers['x-resumable-upload-cached']).to.not.exist;
            });
        });
        it('Should refuse an invalid digest', function () {
            return tslib_1.__awaiter(this, void 0, void 0, function* () {
                const uploadId = yield prepareUpload({ token: server.accessToken });
                yield sendChunks({
                    pathUploadId: uploadId,
                    token: server.accessToken,
                    digestBuilder: () => 'sha=' + 'a'.repeat(40),
                    expectedStatus: 460
                });
            });
        });
        it('Should accept an appropriate digest', function () {
            return tslib_1.__awaiter(this, void 0, void 0, function* () {
                const uploadId = yield prepareUpload({ token: server.accessToken });
                yield sendChunks({
                    pathUploadId: uploadId,
                    token: server.accessToken,
                    digestBuilder: (chunk) => {
                        return 'sha1=' + (0, extra_utils_1.sha1)(chunk, 'base64');
                    }
                });
            });
        });
    });
    after(function () {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            yield (0, server_commands_1.cleanupTests)([server]);
        });
    });
});
//# sourceMappingURL=resumable-upload.js.map