"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
require("mocha");
const chai = (0, tslib_1.__importStar)(require("chai"));
const fs_extra_1 = require("fs-extra");
const path_1 = require("path");
const core_utils_1 = require("../../../../shared/core-utils");
const server_commands_1 = require("../../../../shared/server-commands");
const expect = chai.expect;
describe('Test resumable upload', function () {
    const defaultFixture = 'video_short.mp4';
    let server;
    let rootId;
    let userAccessToken;
    let userChannelId;
    function buildSize(fixture, size) {
        return (0, 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 (0, 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 (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
            const { token, pathUploadId, expectedStatus, contentLength, contentRangeBuilder } = 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,
                expectedStatus
            });
        });
    }
    function checkFileSize(uploadIdArg, expectedSize) {
        return (0, 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) {
                expect(exists).to.be.false;
                return;
            }
            expect(exists).to.be.true;
            expect((yield (0, fs_extra_1.stat)(filePath)).size).to.equal(expectedSize);
        });
    }
    function countResumableUploads() {
        return (0, 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 (0, 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 correctly delete files after an upload', function () {
            return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
                const uploadId = yield prepareUpload();
                yield sendChunks({ pathUploadId: uploadId });
                yield server.videos.endResumableUpload({ pathUploadId: uploadId });
                expect(yield countResumableUploads()).to.equal(0);
            });
        });
        it('Should not delete files after an unfinished upload', function () {
            return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
                yield prepareUpload();
                expect(yield countResumableUploads()).to.equal(2);
            });
        });
        it('Should not delete recent uploads', function () {
            return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
                yield server.debug.sendCommand({ body: { command: 'remove-dandling-resumable-uploads' } });
                expect(yield countResumableUploads()).to.equal(2);
            });
        });
        it('Should delete old uploads', function () {
            return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
                yield server.debug.sendCommand({ body: { command: 'remove-dandling-resumable-uploads' } });
                expect(yield countResumableUploads()).to.equal(0);
            });
        });
    });
    describe('Resumable upload and chunks', function () {
        it('Should accept the same amount of chunks', function () {
            return (0, 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 (0, 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 (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
                const uploadId = yield prepareUpload({ size: 1500 });
                if (process.version.startsWith('v16')) {
                    yield sendChunks({ pathUploadId: uploadId, expectedStatus: 409, contentLength: 1000 });
                    yield checkFileSize(uploadId, 1000);
                }
                else {
                    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 (0, 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 (0, 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 });
                expect(result1.body.video.uuid).to.exist;
                expect(result1.body.video.uuid).to.equal(result2.body.video.uuid);
                expect(result1.headers['x-resumable-upload-cached']).to.not.exist;
                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 (0, 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 });
                expect(uploadId1).to.not.equal(uploadId2);
            });
        });
        it('Should have the same upload id with the same user', function () {
            return (0, 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 });
                expect(uploadId1).to.equal(uploadId2);
            });
        });
        it('Should not cache a request with 2 different users', function () {
            return (0, 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 (0, 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 });
                expect(uploadId1).to.equal(uploadId2);
                const result2 = yield sendChunks({ pathUploadId: uploadId1 });
                expect(result2.headers['x-resumable-upload-cached']).to.not.exist;
            });
        });
    });
    after(function () {
        return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
            yield (0, server_commands_1.cleanupTests)([server]);
        });
    });
});
//# sourceMappingURL=resumable-upload.js.map