var fs = require('fs');
var path = require('path');
var COS = require('../index');
var request = require('request');
var util = require('../demo/util');
var config = require('../demo/config');
var Writable = require('stream').Writable;

if (process.env.AppId) {
    config = {
        SecretId: process.env.SecretId,
        SecretKey: process.env.SecretKey,
        Bucket: process.env.Bucket, // Bucket 格式：test-1250000000
        Region: process.env.Region
    }
}

var cos = new COS({
    SecretId: config.SecretId,
    SecretKey: config.SecretKey,
    ChunkParallelLimit: 10,
    IgnoreRegionFormat: true,
    Protocol: 'http:',
    ServiceDomain: 'http://cos.default.xxx.com',
    Domain: 'http://{{Bucket}}-{{AppId}}.cos.{{Region}}.xxx.com',
});

var AppId = config.AppId;
var Bucket = config.Bucket;
var BucketShortName = Bucket;
var BucketLongName = Bucket + '-' + AppId;

var match = config.Bucket.match(/^(.+)-(\d+)$/);
if (match) {
    BucketLongName = config.Bucket; // Bucket 格式：test-1250000000
    BucketShortName = match[1];
    AppId = match[2];
}

var assert = require("assert");
assert.ok = assert;

function prepareBucket() {
    return new Promise(function (resolve, reject) {
        cos.putBucket({
            Bucket: config.Bucket, // Bucket 格式：test-1250000000
            Region: config.Region
        }, function (err, data) {
            resolve();
        });
    });
}

function prepareBigObject() {
    return new Promise(function (resolve, reject) {
        // 创建测试文件
        var filename = 'bigger.zip';
        var filepath = path.resolve(__dirname, filename);
        var put = function () {
            // 调用方法
            cos.putObject({
                Bucket: config.Bucket, // Bucket 格式：test-1250000000
                Region: config.Region,
                Key: filename,
                Body: fs.createReadStream(filepath),
                ContentLength: fs.statSync(filepath).size,
            }, function (err, data) {
                err ? reject(err) : resolve()
            });
        };
        if (fs.existsSync(filepath)) {
            put();
        } else {
            util.createFile(filepath, 1024 * 1024 * 10, put);
        }
    });
}

function comparePlainObject(a, b) {
    a instanceof Array && a.sort();
    b instanceof Array && b.sort();
    // var sep = '||||||';
    // if (a instanceof Array && b instanceof Array) {
    //     return a.join(sep) === b.join(sep);
    // }
    if (Object.keys(a).length !== Object.keys(b).length) {
        return false;
    }
    for (var key in a) {
        if (typeof a[key] === 'object' && typeof b[key] === 'object') {
            if (!comparePlainObject(a[key], b[key])) {
                return false;
            }
        } else if (a[key] != b[key]) {
            return false;
        }
    }
    return true;
}

describe('getService()', function () {
    this.timeout(60000);
    it('能正常列出 Bucket', function (done) {
        prepareBucket().then(function () {
            cos.getService(function (err, data) {
                var hasBucket = false;
                data.Buckets && data.Buckets.forEach(function (item) {
                    if (item.Name === BucketLongName && (item.Location === config.Region || !item.Location)) {
                        hasBucket = true;
                    }
                });
                assert.equal(true, hasBucket);
                done();
            });
        }).catch(function () {
        });
    });
});

describe('getAuth()', function () {
    this.timeout(60000);
    it('通过获取签名能正常获取文件', function (done) {
        var content = Date.now().toString();
        var key = '1.txt';
        prepareBucket().then(function () {
            cos.putObject({
                Bucket: config.Bucket, // Bucket 格式：test-1250000000
                Region: config.Region,
                Key: key,
                Body: Buffer.from(content)
            }, function (err, data) {
                var auth = cos.getAuth({
                    Method: 'get',
                    Key: key
                });
                var link = 'http://' + BucketLongName + '.cos.' + config.Region + '.yun.ccb.com/' + key +
                    '?sign=' + encodeURIComponent(auth);
                request(link, function (err, response, body) {
                    assert.ok(response.statusCode === 200);
                    assert.ok(body === content);
                    done();
                });
            });
        }).catch(function () {
        });
    });
});

describe('auth check', function () {
    this.timeout(60000);
    it('auth check !\'$()*', function (done) {
        cos.getBucket({
            Bucket: config.Bucket,
            Region: config.Region,
            Prefix: 'aksjhdlash sajlhj!@#$%^&*()_+=-[]{}\';:"/.<>?.,??sadasd#/.,/~`',
            Headers: {
                // !'$()* // 包含这几个特殊字符就出错
                'x-cos-test': 'aksjhdlash sajlhj!@#$%^&*()_+=-[]{}\';:\"/.<>?.,??sadasd#/.,/~`',
            },
        }, function (err, data) {
            assert.ok(!err);
            done();
        });
    });
});

describe('putBucket()', function () {
    this.timeout(60000);
    var NewBucket = 'test-put-bucket-' + AppId;
    it('正常创建 bucket', function (done) {
        cos.putBucket({
            Bucket: NewBucket,
            Region: config.Region
        }, function (err, data) {
            assert.equal(NewBucket + '.cos.' + config.Region + '.yun.ccb.com', data.Location);
            cos.headBucket({
                Bucket: NewBucket,
                Region: config.Region
            }, function (err, data) {
                assert.ok(data);
                cos.deleteBucket({
                    Bucket: NewBucket,
                    Region: config.Region
                }, function (err, data) {
                    done();
                });
            });
        });
    });
});

describe('getBucket()', function () {
    this.timeout(60000);
    it('正常获取 bucket 里的文件列表', function (done) {
        prepareBucket().then(function () {
            cos.getBucket({
                Bucket: config.Bucket, // Bucket 格式：test-1250000000
                Region: config.Region
            }, function (err, data) {
                assert.equal(true, data.Name === BucketLongName);
                assert.equal(data.Contents.constructor, Array);
                done();
            });
        }).catch(function () {
            assert.equal(false);
            done();
        });
    });
});

describe('putObject()', function () {
    this.timeout(60000);
    var filename = '1.txt';
    var filepath = path.resolve(__dirname, filename);
    var getObjectContent = function (callback) {
        var objectContent = Buffer.from([]);
        var outputStream = new Writable({
            write: function (chunk, encoding, callback) {
                objectContent = Buffer.concat([objectContent, chunk]);
            }
        });
        setTimeout(function () {
            cos.getObject({
                Bucket: config.Bucket, // Bucket 格式：test-1250000000
                Region: config.Region,
                Key: filename,
                Output: outputStream
            }, function (err, data) {
                var content = objectContent.toString();
                callback(content);
            });
        }, 2000);
    };
    it('fs.createReadStream 创建 object', function (done) {
        var content = Date.now().toString();
        fs.writeFileSync(filepath, content);
        var lastPercent = 0;
        cos.putObject({
            Bucket: config.Bucket, // Bucket 格式：test-1250000000
            Region: config.Region,
            Key: filename,
            Body: fs.createReadStream(filepath),
            ContentLength: fs.statSync(filepath).size,
            onProgress: function (processData) {
                lastPercent = processData.percent;
            },
        }, function (err, data) {
            if (err) throw err;
            assert.ok(data.ETag.length > 0);
            fs.unlinkSync(filepath);
            getObjectContent(function (objectContent) {
                assert.ok(objectContent === content);
                done();
                // cos.putObjectCopy({
                //     Bucket: config.Bucket, // Bucket 格式：test-1250000000
                //     Region: config.Region,
                //     //ServerSideEncryption: 'AES256',
                //     Key: '1.copy.text',
                //     CopySource: config.Bucket + '.cos.' + config.Region + '.yun.ccb.com/' + filename, // Bucket 格式：test-1250000000
                // }, function (err, data) {
                //     assert.ok(!err);
                //     assert.ok(data.ETag.length > 0);
                //     done();
                // });
            });
        });
    });
    it('fs.readFileSync 创建 object', function (done) {
        var content = Date.now().toString();
        fs.writeFileSync(filepath, content);
        var lastPercent = 0;
        cos.putObject({
            Bucket: config.Bucket, // Bucket 格式：test-1250000000
            Region: config.Region,
            Key: filename,
            Body: fs.readFileSync(filepath),
            onProgress: function (processData) {
                lastPercent = processData.percent;
            },
        }, function (err, data) {
            if (err) throw err;
            assert.ok(data.ETag.length > 0);
            fs.unlinkSync(filepath);
            getObjectContent(function (objectContent) {
                assert.ok(objectContent === content);
                done();
            });
        });
    });
    it('捕获输入流异常', function (done) {
        var filename = 'big.zip';
        var filepath = path.resolve(__dirname, filename);
        var put = function () {
            var Body = fs.createReadStream(filepath);
            setTimeout(function () {
                Body.emit('error', new Error('some error'))
            }, 1000);
            cos.putObject({
                Bucket: config.Bucket, // Bucket 格式：test-1250000000
                Region: config.Region,
                Key: filename,
                Body: Body,
                ContentLength: fs.statSync(filepath).size,
            }, function (err, data) {
                fs.unlinkSync(filepath);
                done();
            });
        };
        if (fs.existsSync(filepath)) {
            put();
        } else {
            util.createFile(filepath, 5 << 20, put);
        }
    });
    it('putObject(),buffer', function (done) {
        var content = Buffer.from('中文_' + Date.now());
        cos.putObject({
            Bucket: config.Bucket, // Bucket 格式：test-1250000000
            Region: config.Region,
            Key: '1.txt',
            Body: content,
        }, function (err, data) {
            var ETag = data.ETag;
            assert.ok(!err && ETag);
            cos.getObject({
                Bucket: config.Bucket, // Bucket 格式：test-1250000000
                Region: config.Region,
                Key: filename
            }, function (err, data) {
                assert.ok(data.Body && data.Body.toString() === content.toString() && (data.headers && data.headers.etag) === ETag);
                done();
            });
        });
    });
    it('putObject(),buffer,empty', function (done) {
        var content = Buffer.from('');
        cos.putObject({
            Bucket: config.Bucket, // Bucket 格式：test-1250000000
            Region: config.Region,
            Key: '1.txt',
            Body: content,
        }, function (err, data) {
            var ETag = data.ETag;
            assert.ok(!err && ETag);
            cos.getObject({
                Bucket: config.Bucket, // Bucket 格式：test-1250000000
                Region: config.Region,
                Key: filename
            }, function (err, data) {
                assert.ok(data.Body && data.Body.toString() === content.toString() && (data.headers && data.headers.etag) === ETag);
                done();
            });
        });
    });
    it('putObject(),string', function (done) {
        var content = '中文_' + Date.now();
        cos.putObject({
            Bucket: config.Bucket, // Bucket 格式：test-1250000000
            Region: config.Region,
            Key: '1.txt',
            Body: content,
        }, function (err, data) {
            var ETag = data.ETag;
            assert.ok(!err && ETag);
            cos.getObject({
                Bucket: config.Bucket, // Bucket 格式：test-1250000000
                Region: config.Region,
                Key: filename
            }, function (err, data) {
                assert.ok(data.Body && data.Body.toString() === content.toString() && (data.headers && data.headers.etag) === ETag);
                done();
            });
        });
    });
    it('putObject(),string,empty', function (done) {
        var content = '';
        cos.putObject({
            Bucket: config.Bucket, // Bucket 格式：test-1250000000
            Region: config.Region,
            Key: '1.txt',
            Body: content,
        }, function (err, data) {
            var ETag = data.ETag;
            assert.ok(!err && ETag);
            cos.getObject({
                Bucket: config.Bucket, // Bucket 格式：test-1250000000
                Region: config.Region,
                Key: filename
            }, function (err, data) {
                assert.ok(data.Body && data.Body.toString() === content && (data.headers && data.headers.etag) === ETag);
                done();
            });
        });
    });
});

describe('getObject()', function () {
    this.timeout(60000);
    it('stream', function (done) {
        var key = '1.txt';
        var objectContent = Buffer.from([]);
        var outputStream = new Writable({
            write: function (chunk, encoding, callback) {
                objectContent = Buffer.concat([objectContent, chunk]);
            }
        });
        var content = Date.now().toString(36);
        cos.putObject({
            Bucket: config.Bucket, // Bucket 格式：test-1250000000
            Region: config.Region,
            Key: key,
            Body: Buffer.from(content)
        }, function (err, data) {
            setTimeout(function () {
                cos.getObject({
                    Bucket: config.Bucket, // Bucket 格式：test-1250000000
                    Region: config.Region,
                    Key: key,
                    Output: outputStream
                }, function (err, data) {
                    if (err) throw err;
                    objectContent = objectContent.toString();
                    assert.ok(data.headers['content-length'] === '' + content.length);
                    assert.ok(objectContent === content);
                    cos.headObject({
                        Bucket: config.Bucket,
                        Region: config.Region,
                        Key: key
                    }, function (err, data) {
                        assert.ok(!err);
                        done();
                    });
                });
            }, 2000);
        });
    });
    it('body', function (done) {
        var key = '1.txt';
        var content = Date.now().toString();
        cos.putObject({
            Bucket: config.Bucket, // Bucket 格式：test-1250000000
            Region: config.Region,
            Key: key,
            Body: Buffer.from(content)
        }, function (err, data) {
            setTimeout(function () {
                cos.getObject({
                    Bucket: config.Bucket, // Bucket 格式：test-1250000000
                    Region: config.Region,
                    Key: key
                }, function (err, data) {
                    if (err) throw err;
                    var objectContent = data.Body.toString();
                    assert.ok(data.headers['content-length'] === '' + content.length);
                    assert.ok(objectContent === content);
                    done();
                });
            }, 2000);
        });
    });
});

describe('putObjectCopy()', function () {
    this.timeout(60000);
    var fileName = '1.txt';
    it('正常复制 object', function (done) {
        cos.putObjectCopy({
            Bucket: config.Bucket, // Bucket 格式：test-1250000000
            Region: config.Region,
            Key: '1.copy.txt',
            CopySource: config.Bucket + '.cos.' + config.Region + '.yun.ccb.com/' + fileName,
        }, function (err, data) {
            assert.ok(!err);
            assert.ok(data.ETag.length > 0);
            done();
        });
    });
    it('捕获 object 异常', function (done) {
        var errFileName = '12345.txt';
        cos.putObjectCopy({
            Bucket: config.Bucket, // Bucket 格式：test-1250000000
            Region: config.Region,
            Key: '1.copy.txt',
            CopySource: config.Bucket + '.cos.' + config.Region + '.yun.ccb.com/' + errFileName,
        }, function (err, data) {
            assert.equal(true, err.statusCode === 404);
            assert.equal(true, err.error.Code === 'NoSuchKey')
            done();
        });
    });
});

describe('sliceCopyFile()', function () {
    this.timeout(60000);
    var fileName = 'bigger.zip';
    var filepath = path.resolve(__dirname, fileName);
    var Key = 'bigger.copy.zip';
    it('正常分片复制 object', function (done) {
        prepareBigObject().then(function () {
            cos.sliceCopyFile({
                Bucket: config.Bucket, // Bucket 格式：test-1250000000
                Region: config.Region,
                Key: Key,
                CopySource: config.Bucket + '.cos.' + config.Region + '.yun.ccb.com/'+ fileName,
                SliceSize: 5 * 1024 * 1024,
            },function (err, data) {
                if (err) throw err;
                assert.ok(data.ETag.length > 0);
                fs.unlinkSync(filepath);
                done();
            });
        }).catch(function (err) {
            assert.equal(false);
            done();
        });
    });
    it('单片复制 object', function (done) {
        prepareBigObject().then(function () {
            cos.sliceCopyFile({
                Bucket: config.Bucket, // Bucket 格式：test-1250000000
                Region: config.Region,
                Key: Key,
                CopySource: config.Bucket + '.cos.' + config.Region + '.yun.ccb.com/'+ fileName,
                SliceSize: 10 * 1024 * 1024,
            },function (err,data) {
                if (err) throw err;
                assert.ok(data.ETag.length > 0);
                fs.unlinkSync(filepath);
                done();
            });
        }).catch(function () {
            assert.equal(false);
            done();
        });
    });
});

describe('sliceUploadFile()', function () {
    this.timeout(120000);

    it('正常分片上传 object', function (done) {
        var filename = '3mb.zip';
        var filepath = path.resolve(__dirname, filename);
        var put = function () {
            var lastPercent = 0;
            cos.sliceUploadFile({
                Bucket: config.Bucket, // Bucket 格式：test-1250000000
                Region: config.Region,
                Key: filename,
                FilePath: filepath,
                SliceSize: 1024 * 1024,
                AsyncLimit: 5,
                onProgress: function (progressData) {
                    lastPercent = progressData.percent;
                },
            }, function (err, data) {
                assert.equal(true, data.ETag.length > 0 && lastPercent === 1);
                fs.unlinkSync(filepath);
                done();
            });
        };
        if (fs.existsSync(filepath)) {
            put();
        } else {
            util.createFile(filepath, 3 * 1024 * 1024, put);
        }
    });
});

(function () {
    var AccessControlPolicy = {
        "Owner": {
            "ID": 'qcs::cam::uin/100004603008:uin/100004603008' // 100004603008 是 QQ 号
        },
        "Grants": [{
            "Grantee": {
                "ID": "qcs::cam::uin/100004603008:uin/100004603008", // 100004603008 是 QQ 号
            },
            "Permission": "READ"
        }]
    };
    var AccessControlPolicy2 = {
        "Owner": {
            "ID": 'qcs::cam::uin/100004603008:uin/100004603008' // 100004603008 是 QQ 号
        },
        "Grant": {
            "Grantee": {
                "ID": "qcs::cam::uin/100004603008:uin/100004603008", // 100004603008 是 QQ 号
            },
            "Permission": "READ"
        }
    };
    describe('BucketAcl', function () {
        this.timeout(60000);
        it('putBucketAcl() header ACL:private', function (done) {
            cos.putBucketAcl({
                Bucket: config.Bucket, // Bucket 格式：test-1250000000
                Region: config.Region,
                ACL: 'private'
            }, function (err, data) {
                assert.ok(!err);
                cos.getBucketAcl({
                    Bucket: config.Bucket, // Bucket 格式：test-1250000000
                    Region: config.Region
                }, function (err, data) {
                    AccessControlPolicy.Owner.ID = data.Owner.ID;
                    AccessControlPolicy2.Owner.ID = data.Owner.ID;
                    assert.ok(data.ACL === 'private' || data.ACL === 'default');
                    done();
                });
            });
        });
        it('putBucketAcl() header ACL:public-read', function (done) {
            cos.putBucketAcl({
                Bucket: config.Bucket, // Bucket 格式：test-1250000000
                Region: config.Region,
                ACL: 'public-read',
            }, function (err, data) {
                assert.ok(!err);
                cos.getBucketAcl({Bucket: config.Bucket, Region: config.Region}, function (err, data) { // Bucket 格式：test-1250000000
                    assert.ok(data.ACL === 'public-read');
                    done();
                });
            });
        });
        it('putBucketAcl() header ACL:public-read-write', function (done) {
            cos.putBucketAcl({
                Bucket: config.Bucket, // Bucket 格式：test-1250000000
                Region: config.Region,
                ACL: 'public-read-write',
            }, function (err, data) {
                assert.ok(!err);
                cos.getBucketAcl({Bucket: config.Bucket, Region: config.Region}, function (err, data) { // Bucket 格式：test-1250000000
                    assert.ok(data.ACL === 'public-read-write');
                    done();
                });
            });
        });
        it('putBucketAcl() header GrantRead:1001,1002', function (done) {
            var GrantRead = 'id="qcs::cam::uin/1001:uin/1001", id="qcs::cam::uin/1002:uin/1002"';
            cos.putBucketAcl({
                Bucket: config.Bucket, // Bucket 格式：test-1250000000
                Region: config.Region,
                GrantRead: GrantRead,
            }, function (err, data) {
                assert.ok(!err);
                cos.getBucketAcl({Bucket: config.Bucket, Region: config.Region}, function (err, data) { // Bucket 格式：test-1250000000
                    assert.ok(data.GrantRead = GrantRead);
                    done();
                });
            });
        });
        it('putBucketAcl() header GrantWrite:1001,1002', function (done) {
            var GrantWrite = 'id="qcs::cam::uin/1001:uin/1001", id="qcs::cam::uin/1002:uin/1002"';
            cos.putBucketAcl({
                Bucket: config.Bucket, // Bucket 格式：test-1250000000
                Region: config.Region,
                GrantWrite: GrantWrite,
            }, function (err, data) {
                assert.ok(!err);
                cos.getBucketAcl({Bucket: config.Bucket, Region: config.Region}, function (err, data) { // Bucket 格式：test-1250000000
                    assert.ok(data.GrantWrite = GrantWrite);
                    done();
                });
            });
        });
        it('putBucketAcl() header GrantFullControl:1001,1002', function (done) {
            var GrantFullControl = 'id="qcs::cam::uin/1001:uin/1001", id="qcs::cam::uin/1002:uin/1002"';
            cos.putBucketAcl({
                Bucket: config.Bucket, // Bucket 格式：test-1250000000
                Region: config.Region,
                GrantFullControl: GrantFullControl,
            }, function (err, data) {
                assert.ok(!err);
                cos.getBucketAcl({Bucket: config.Bucket, Region: config.Region}, function (err, data) { // Bucket 格式：test-1250000000
                    assert.ok(data.GrantFullControl = GrantFullControl);
                    done();
                });
            });
        });
        it('putBucketAcl() header ACL:public-read, GrantFullControl:1001,1002', function (done) {
            var GrantFullControl = 'id="qcs::cam::uin/1001:uin/1001", id="qcs::cam::uin/1002:uin/1002"';
            cos.putBucketAcl({
                Bucket: config.Bucket, // Bucket 格式：test-1250000000
                Region: config.Region,
                GrantFullControl: GrantFullControl,
                ACL: 'public-read',
            }, function (err, data) {
                assert.ok(!err);
                cos.getBucketAcl({Bucket: config.Bucket, Region: config.Region}, function (err, data) { // Bucket 格式：test-1250000000
                    assert.ok(data.GrantFullControl = GrantFullControl);
                    assert.ok(data.ACL === 'public-read');
                    done();
                });
            });
        });
        it('putBucketAcl() xml', function (done) {
            cos.putBucketAcl({
                Bucket: config.Bucket, // Bucket 格式：test-1250000000
                Region: config.Region,
                AccessControlPolicy: AccessControlPolicy,
            }, function (err, data) {
                assert.ok(!err);
                cos.getBucketAcl({Bucket: config.Bucket, Region: config.Region}, function (err, data) { // Bucket 格式：test-1250000000
                    assert.ok(data.Grants.length === 1);
                    assert.ok(data.Grants[0] && data.Grants[0].Grantee.ID === 'qcs::cam::uin/100004603008:uin/100004603008', '设置 AccessControlPolicy ID 正确');
                    assert.ok(data.Grants[0] && data.Grants[0].Permission === 'READ', '设置 AccessControlPolicy Permission 正确');
                    done();
                });
            });
        });
        it('putBucketAcl() xml2', function (done) {
            cos.putBucketAcl({
                Bucket: config.Bucket, // Bucket 格式：test-1250000000
                Region: config.Region,
                AccessControlPolicy: AccessControlPolicy2,
            }, function (err, data) {
                assert.ok(!err);
                cos.getBucketAcl({Bucket: config.Bucket, Region: config.Region}, function (err, data) { // Bucket 格式：test-1250000000
                    assert.ok(data.Grants.length === 1);
                    assert.ok(data.Grants[0] && data.Grants[0].Grantee.ID === 'qcs::cam::uin/100004603008:uin/100004603008');
                    assert.ok(data.Grants[0] && data.Grants[0].Permission === 'READ');
                    done();
                });
            });
        });
        it('putBucketAcl() decodeAcl', function (done) {
            cos.getBucketAcl({
                Bucket: config.Bucket, // Bucket 格式：test-1250000000
                Region: config.Region
            }, function (err, data) {
                cos.putBucketAcl({
                    Bucket: config.Bucket, // Bucket 格式：test-1250000000
                    Region: config.Region,
                    GrantFullControl: data.GrantFullControl,
                    GrantWrite: data.GrantWrite,
                    GrantRead: data.GrantRead,
                    ACL: data.ACL,
                }, function (err, data) {
                    assert.ok(data);
                    done();
                });
            });
        });
    });
})();

(function () {
    var AccessControlPolicy = {
        "Owner": {
            "ID": 'qcs::cam::uin/100004603008:uin/100004603008' // 100004603008 是 QQ 号
        },
        "Grants": [{
            "Grantee": {
                "ID": "qcs::cam::uin/100004603008:uin/100004603008", // 100004603008 是 QQ 号
            },
            "Permission": "READ"
        }]
    };
    var AccessControlPolicy2 = {
        "Owner": {
            "ID": 'qcs::cam::uin/100004603008:uin/100004603008' // 100004603008 是 QQ 号
        },
        "Grant": {
            "Grantee": {
                "ID": "qcs::cam::uin/100004603008:uin/100004603008", // 100004603008 是 QQ 号
            },
            "Permission": "READ"
        }
    };
    describe('ObjectAcl', function () {
        this.timeout(60000);
        it('putObjectAcl() header ACL:private', function (done) {
            cos.putObject({
                Bucket: config.Bucket, // Bucket 格式：test-1250000000
                Region: config.Region,
                Key: '1.txt',
                Body: Buffer.from('hello!'),
            }, function (err, data) {
                assert.ok(!err);
                cos.putObjectAcl({
                    Bucket: config.Bucket, // Bucket 格式：test-1250000000
                    Region: config.Region,
                    ACL: 'private',
                    Key: '1.txt',
                }, function (err, data) {
                    assert.ok(!err, 'putObjectAcl 成功');
                    cos.getObjectAcl({
                        Bucket: config.Bucket, // Bucket 格式：test-1250000000
                        Region: config.Region,
                        Key: '1.txt'
                    }, function (err, data) {
                        assert.ok(data.ACL = 'private');
                        AccessControlPolicy.Owner.ID = data.Owner.ID;
                        AccessControlPolicy2.Owner.ID = data.Owner.ID;
                        assert.ok(data.Grants.length === 1);
                        done();
                    });
                });
            });
        });
        it('putObjectAcl() header ACL:default', function (done) {
            cos.putObjectAcl({
                Bucket: config.Bucket, // Bucket 格式：test-1250000000
                Region: config.Region,
                ACL: 'default',
                Key: '1.txt',
            }, function (err, data) {
                assert.ok(!err, 'putObjectAcl 成功');
                cos.getObjectAcl({
                    Bucket: config.Bucket, // Bucket 格式：test-1250000000
                    Region: config.Region,
                    Key: '1.txt'
                }, function (err, data) {
                    assert.ok(data.ACL = 'default');
                    done();
                });
            });
        });
        it('putObjectAcl() header ACL:public-read', function (done) {
            cos.putObjectAcl({
                Bucket: config.Bucket, // Bucket 格式：test-1250000000
                Region: config.Region,
                ACL: 'public-read',
                Key: '1.txt',
            }, function (err, data) {
                assert.ok(!err, 'putObjectAcl 成功');
                cos.getObjectAcl({Bucket: config.Bucket, Region: config.Region, Key: '1.txt'}, function (err, data) { // Bucket 格式：test-1250000000
                    assert.ok(data.ACL = 'public-read');
                    done();
                });
            });
        });
        it('putObjectAcl() header ACL:public-read-write', function (done) {
            cos.putObjectAcl({
                Bucket: config.Bucket, // Bucket 格式：test-1250000000
                Region: config.Region,
                ACL: 'public-read-write',
                Key: '1.txt',
            }, function (err, data) {
                assert.ok(!err, 'putObjectAcl 成功');
                cos.getObjectAcl({Bucket: config.Bucket, Region: config.Region, Key: '1.txt'}, function (err, data) { // Bucket 格式：test-1250000000
                    assert.ok(data.ACL = 'public-read-write');
                    done();
                });
            });
        });
        it('putObjectAcl() header GrantRead:1001,1002', function (done) {
            var GrantRead = 'id="qcs::cam::uin/1001:uin/1001",id="qcs::cam::uin/1002:uin/1002"';
            cos.putObjectAcl({
                Bucket: config.Bucket, // Bucket 格式：test-1250000000
                Region: config.Region,
                GrantRead: GrantRead,
                Key: '1.txt',
            }, function (err, data) {
                assert.ok(!err, 'putObjectAcl 成功');
                cos.getObjectAcl({Bucket: config.Bucket, Region: config.Region, Key: '1.txt'}, function (err, data) { // Bucket 格式：test-1250000000
                    assert.ok(data.GrantRead = GrantRead);
                    done();
                });
            });
        });
        it('putObjectAcl() header GrantWrite:1001,1002', function (done) {
            var GrantWrite = 'id="qcs::cam::uin/1001:uin/1001", id="qcs::cam::uin/1002:uin/1002"';
            cos.putObjectAcl({
                Bucket: config.Bucket, // Bucket 格式：test-1250000000
                Region: config.Region,
                GrantWrite: GrantWrite,
                Key: '1.txt',
            }, function (err, data) {
                assert.ok(!err, 'putObjectAcl 成功');
                cos.getObjectAcl({Bucket: config.Bucket, Region: config.Region, Key: '1.txt'}, function (err, data) { // Bucket 格式：test-1250000000
                    assert.ok(data.GrantWrite = GrantWrite);
                    done();
                });
            });
        });
        it('putObjectAcl() header GrantFullControl:1001,1002', function (done) {
            var GrantFullControl = 'id="qcs::cam::uin/1001:uin/1001", id="qcs::cam::uin/1002:uin/1002"';
            cos.putObjectAcl({
                Bucket: config.Bucket, // Bucket 格式：test-1250000000
                Region: config.Region,
                GrantFullControl: GrantFullControl,
                Key: '1.txt',
            }, function (err, data) {
                assert.ok(!err, 'putObjectAcl 成功');
                cos.getObjectAcl({Bucket: config.Bucket, Region: config.Region, Key: '1.txt'}, function (err, data) { // Bucket 格式：test-1250000000
                    assert.ok(data.GrantFullControl = GrantFullControl);
                    done();
                });
            });
        });
        it('putObjectAcl() header ACL:public-read, GrantRead:1001,1002', function (done) {
            var GrantFullControl = 'id="qcs::cam::uin/1001:uin/1001", id="qcs::cam::uin/1002:uin/1002"';
            cos.putObjectAcl({
                Bucket: config.Bucket, // Bucket 格式：test-1250000000
                Region: config.Region,
                GrantFullControl: GrantFullControl,
                ACL: 'public-read',
                Key: '1.txt',
            }, function (err, data) {
                assert.ok(!err, 'putObjectAcl 成功');
                cos.getObjectAcl({Bucket: config.Bucket, Region: config.Region, Key: '1.txt'}, function (err, data) { // Bucket 格式：test-1250000000
                    assert.ok(data.GrantFullControl = GrantFullControl);
                    assert.ok(data.ACL = 'public-read');
                    done();
                });
            });
        });
        it('putObjectAcl() xml', function (done) {
            cos.putObjectAcl({
                Bucket: config.Bucket, // Bucket 格式：test-1250000000
                Region: config.Region,
                AccessControlPolicy: AccessControlPolicy,
                Key: '1.txt',
            }, function (err, data) {
                assert.ok(!err, 'putObjectAcl 成功');
                cos.getBucketAcl({Bucket: config.Bucket, Region: config.Region, Key: '1.txt'}, function (err, data) { // Bucket 格式：test-1250000000
                    assert.ok(data.Grants.length === 1);
                    assert.ok(data.Grants[0] && data.Grants[0].Grantee.ID === 'qcs::cam::uin/100004603008:uin/100004603008', '设置 AccessControlPolicy ID 正确');
                    assert.ok(data.Grants[0] && data.Grants[0].Permission === 'READ', '设置 AccessControlPolicy Permission 正确');
                    done();
                });
            });
        });
        it('putObjectAcl() xml2', function (done) {
            cos.putObjectAcl({
                Bucket: config.Bucket, // Bucket 格式：test-1250000000
                Region: config.Region,
                AccessControlPolicy: AccessControlPolicy2,
                Key: '1.txt',
            }, function (err, data) {
                assert.ok(!err, 'putObjectAcl 成功');
                cos.getObjectAcl({
                    Bucket: config.Bucket,
                    Region: config.Region,
                    Key: '1.txt'
                }, function (err, data) { // Bucket 格式：test-1250000000
                    assert.ok(data.Grants.length === 1);
                    assert.ok(data.Grants[0] && data.Grants[0].Grantee.ID === 'qcs::cam::uin/100004603008:uin/100004603008', 'ID 正确');
                    assert.ok(data.Grants[0] && data.Grants[0].Permission === 'READ', 'Permission 正确');
                    done();
                });
            });
        });
        it('putObjectAcl() decodeAcl', function (done) {
            cos.getObjectAcl({
                Bucket: config.Bucket, // Bucket 格式：test-1250000000
                Region: config.Region,
                Key: '1.txt'
            }, function (err, data) {
                cos.putObjectAcl({
                    Bucket: config.Bucket, // Bucket 格式：test-1250000000
                    Region: config.Region,
                    Key: '1.txt',
                    GrantFullControl: data.GrantFullControl,
                    GrantWrite: data.GrantWrite,
                    GrantRead: data.GrantRead,
                    ACL: data.ACL,
                }, function (err, data) {
                    assert.ok(data);
                    done();
                });
            });
        });
    });
})();

describe('BucketCors', function () {
    this.timeout(60000);
    var CORSRules = [{
        "AllowedOrigins": ["*"],
        "AllowedMethods": ["GET", "POST", "PUT", "DELETE", "HEAD"],
        "AllowedHeaders": ["*"],
        "ExposeHeaders": ["ETag", "Content-Length", "x-cos-acl", "x-cos-version-id", "x-cos-request-id", "x-cos-delete-marker", "x-cos-server-side-encryption"],
        "MaxAgeSeconds": "5"
    }];
    var CORSRulesMulti = [{
        "AllowedOrigins": ["*"],
        "AllowedMethods": ["GET", "POST", "PUT", "DELETE", "HEAD"],
        "AllowedHeaders": ["*"],
        "ExposeHeaders": ["ETag", "Content-Length", "x-cos-acl", "x-cos-version-id", "x-cos-request-id", "x-cos-delete-marker", "x-cos-server-side-encryption"],
        "MaxAgeSeconds": "5"
    }, {
        "AllowedOrigins": ["http://qq.com", "http://qcloud.com"],
        "AllowedMethods": ["GET", "POST", "PUT", "DELETE", "HEAD"],
        "AllowedHeaders": ["*"],
        "ExposeHeaders": ["ETag", "Content-Length", "x-cos-acl", "x-cos-version-id", "x-cos-request-id", "x-cos-delete-marker", "x-cos-server-side-encryption"],
        "MaxAgeSeconds": "5"
    }];
    it('deleteBucketCors()', function (done) {
        cos.deleteBucketCors({
            Bucket: config.Bucket, // Bucket 格式：test-1250000000
            Region: config.Region
        }, function (err, data) {
            assert.ok(!err);
            setTimeout(function () {
                cos.getBucketCors({
                    Bucket: config.Bucket, // Bucket 格式：test-1250000000
                    Region: config.Region
                }, function (err, data) {
                    assert.ok(err && err.statusCode === 404);
                    done();
                });
            }, 2000);
        });
    });
    it('putBucketCors(),getBucketCors()', function (done) {
        CORSRules[0].AllowedHeaders[CORSRules[0].AllowedHeaders.length - 1] =
            'test-' + Date.now().toString(36);
        cos.putBucketCors({
            Bucket: config.Bucket, // Bucket 格式：test-1250000000
            Region: config.Region,
            CORSConfiguration: {
                CORSRules: CORSRules
            }
        }, function (err, data) {
            assert.ok(!err);
            setTimeout(function () {
                cos.getBucketCors({
                    Bucket: config.Bucket, // Bucket 格式：test-1250000000
                    Region: config.Region
                }, function (err, data) {
                    assert.ok(comparePlainObject(CORSRules, data.CORSRules));
                    done();
                });
            }, 2000);
        });
    });
    it('putBucketCors() old', function (done) {
        var testVal = 'test-' + Date.now().toString(36);
        CORSRules[0].AllowedHeaders.push(testVal);
        cos.putBucketCors({
            Bucket: config.Bucket, // Bucket 格式：test-1250000000
            Region: config.Region,
            CORSConfiguration: {
                CORSRules: CORSRules
            }
        }, function (err, data) {
            assert.ok(!err);
            setTimeout(function () {
                cos.getBucketCors({
                    Bucket: config.Bucket, // Bucket 格式：test-1250000000
                    Region: config.Region
                }, function (err, data) {
                    assert.ok(comparePlainObject(CORSRules, data.CORSRules));
                    done();
                });
            }, 2000);
        });
    });
    it('putBucketCors() old', function (done) {
        CORSRules[0].AllowedHeaders[CORSRules[0].AllowedHeaders.length - 1] =
            'test-' + Date.now().toString(36);
        cos.putBucketCors({
            Bucket: config.Bucket, // Bucket 格式：test-1250000000
            Region: config.Region,
            CORSRules: CORSRules
        }, function (err, data) {
            assert.ok(!err);
            setTimeout(function () {
                cos.getBucketCors({
                    Bucket: config.Bucket, // Bucket 格式：test-1250000000
                    Region: config.Region
                }, function (err, data) {
                    assert.ok(comparePlainObject(CORSRules, data.CORSRules));
                    done();
                });
            }, 2000);
        });
    });
    it('putBucketCors() multi', function (done) {
        cos.putBucketCors({
            Bucket: config.Bucket, // Bucket 格式：test-1250000000
            Region: config.Region,
            CORSConfiguration: {
                CORSRules: CORSRulesMulti
            }
        }, function (err, data) {
            assert.ok(!err);
            setTimeout(function () {
                cos.getBucketCors({
                    Bucket: config.Bucket, // Bucket 格式：test-1250000000
                    Region: config.Region
                }, function (err, data) {
                    assert.ok(comparePlainObject(CORSRulesMulti, data.CORSRules));
                    done();
                });
            }, 2000);
        });
    });
});

(function () {
    var Rules = [{
        'ID': '1',
        'Filter': {
            'Prefix': 'test_' + Date.now().toString(36),
        },
        'Status': 'Enabled',
        'Transition': {
            'Date': '2018-07-29T16:00:00.000Z',
            'StorageClass': 'STANDARD_IA'
        }
    }];
    var RulesMulti = [{
        'ID': '1',
        'Filter': {
            'Prefix': 'test1_' + Date.now().toString(36),
        },
        'Status': 'Enabled',
        'Transition': {
            'Date': '2018-07-29T16:00:00.000Z',
            'StorageClass': 'STANDARD_IA'
        }
    }, {
        'ID': '2',
        'Filter': {
            'Prefix': 'test2_' + Date.now().toString(36),
        },
        'Status': 'Enabled',
        'Transition': {
            'Date': '2018-07-29T16:00:00.000Z',
            'StorageClass': 'STANDARD_IA'
        }
    }];
    describe('BucketLifecycle', function () {
        this.timeout(60000);
        it('deleteBucketLifecycle()', function (done) {
            cos.deleteBucketLifecycle({
                Bucket: config.Bucket, // Bucket 格式：test-1250000000
                Region: config.Region
            }, function (err, data) {
                assert.ok(!err);
                setTimeout(function () {
                    cos.getBucketLifecycle({
                        Bucket: config.Bucket, // Bucket 格式：test-1250000000
                        Region: config.Region
                    }, function (err, data) {
                        assert.ok(comparePlainObject([], data.Rules));
                        done();
                    });
                }, 2000);
            });
        });
        it('putBucketLifecycle(),getBucketLifecycle()', function (done) {
            Rules[0].Filter.Prefix = 'test_' + Date.now().toString(36);
            cos.putBucketLifecycle({
                Bucket: config.Bucket, // Bucket 格式：test-1250000000
                Region: config.Region,
                LifecycleConfiguration: {
                    Rules: Rules
                }
            }, function (err, data) {
                assert.ok(!err);
                setTimeout(function () {
                    cos.getBucketLifecycle({
                        Bucket: config.Bucket, // Bucket 格式：test-1250000000
                        Region: config.Region
                    }, function (err, data) {
                        assert.ok(comparePlainObject(Rules, data && data.Rules));
                        done();
                    });
                }, 2000);
            });
        });
        it('putBucketLifecycle() multi', function (done) {
            Rules[0].Filter.Prefix = 'test_' + Date.now().toString(36);
            cos.putBucketLifecycle({
                Bucket: config.Bucket, // Bucket 格式：test-1250000000
                Region: config.Region,
                LifecycleConfiguration: {
                    Rules: RulesMulti
                }
            }, function (err, data) {
                assert.ok(!err);
                setTimeout(function () {
                    cos.getBucketLifecycle({
                        Bucket: config.Bucket, // Bucket 格式：test-1250000000
                        Region: config.Region
                    }, function (err, data) {
                        assert.ok(comparePlainObject(RulesMulti, data.Rules));
                        done();
                    });
                }, 2000);
            });
        });
    });
})();

describe('params check', function () {
    it('Region', function (done) {
        cos.headBucket({
            Bucket: config.Bucket, // Bucket 格式：test-1250000000
            Region: 'gz'
        }, function (err, data) {
            assert.ok(err);
            done();
        });
    });
});

describe('Key 特殊字符处理', function () {
    it('Key 特殊字符处理', function (done) {
        var Key = '中文→↓←→↖↗↙↘! $&\'()+,-.0123456789=@ABCDEFGHIJKLMNOPQRSTUV？WXYZ[]^_`abcdefghijklmnopqrstuvwxyz{}~.jpg';
        cos.putObject({
            Bucket: config.Bucket,
            Region: config.Region,
            Key: Key,
            Body: 'hello',
        }, function (err, data) {
            assert.ok(!err);
            cos.deleteObject({
                Bucket: config.Bucket,
                Region: config.Region,
                Key: Key,
                Body: 'hello',
            }, function (err, data) {
                assert.ok(!err);
                cos.deleteMultipleObject({
                    Bucket: config.Bucket,
                    Region: config.Region,
                    Objects: {
                        Key: Key,
                    },
                }, function (err, data) {
                    assert.ok(!err);
                    done();
                });
            });
        });
    });
});

describe('Bucket 格式有误', function () {
    it('Bucket 带有中文', function (done) {
        cos.headBucket({
            Bucket: '中文-1250000000',
            Region: config.Region,
        }, function (err, data) {
            // assert.ok(err && err.error === 'Bucket should format as "test-1250000000".');
            done();
        });
    });
    it('Bucket 带有 /', function (done) {
        cos.headBucket({
            Bucket: 'te/st-1250000000',
            Region: config.Region,
        }, function (err, data) {
            assert.ok(err && err.error === 'Bucket should format as "test-1250000000".');
            done();
        });
    });
    it('Bucket 带有 .', function (done) {
        cos.headBucket({
            Bucket: 'te.st-1250000000',
            Region: config.Region,
        }, function (err, data) {
            assert.ok(err && err.error === 'Bucket should format as "test-1250000000".');
            done();
        });
    });
    it('Bucket 带有 :', function (done) {
        cos.headBucket({
            Bucket: 'te:st-1250000000',
            Region: config.Region,
        }, function (err, data) {
            assert.ok(err && err.error === 'Bucket should format as "test-1250000000".');
            done();
        });
    });
});

describe('Region 格式有误', function () {
    it('Region 带有中文', function (done) {
        cos.headBucket({
            Bucket: 'test-1250000000',
            Region: '中文',
        }, function (err, data) {
            assert.ok(err && err.error === 'Region format error.');
            done();
        });
    });
    it('Region 带有 /', function (done) {
        cos.headBucket({
            Bucket: 'test-1250000000',
            Region: 'test/',
        }, function (err, data) {
            assert.ok(err && err.error === 'Region format error.');
            done();
        });
    });
    it('Region 带有 :', function (done) {
        cos.headBucket({
            Bucket: 'te:st-1250000000',
            Region: 'test:',
        }, function (err, data) {
            assert.ok(err && err.error === 'Region format error.');
            done();
        });
    });
});