/**
 * Copyright 2014 IBM Corp.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/

var should = require("should");
var sinon = require('sinon');
var request = require('supertest');
var http = require('http');
var express = require('express');

var fs = require('fs-extra');
var path = require('path');
var when = require('when');

var app = express();
var RED = require("../../red/red.js");
var server = require("../../red/server.js");
var nodes = require("../../red/nodes");

describe("library", function() {
    var userDir = path.join(__dirname,".testUserHome");
    before(function(done) {
        fs.remove(userDir,function(err) {
            fs.mkdir(userDir,function() {
                sinon.stub(nodes, 'load', function() {
                    return when.promise(function(resolve,reject){
                        resolve([]);
                    });
                });
                RED.init(http.createServer(function(req,res){app(req,res)}),
                         {userDir: userDir});
                server.start().then(function () { done(); });
            });
        });
    });

    after(function(done) {
        fs.remove(userDir,done);
        server.stop();
        nodes.load.restore();
    });

    afterEach(function(done) {
        fs.remove(userDir,function(err) {
            fs.mkdir(userDir,done);
        });
    });

    describe("flows", function() {
        it('returns empty result', function(done) {
            request(RED.httpAdmin)
                .get('/library/flows')
                .expect(200)
                .end(function(err,res) {
                    if (err) {
                        throw err;
                    }
                    res.body.should.not.have.property('f');
                    done();
                });
        });

        it('returns 404 for non-existent entry', function(done) {
            request(RED.httpAdmin)
                .get('/library/flows/foo')
                .expect(404)
                .end(done);
        });

        it('can store and retrieve item', function(done) {
            var flow = '[]';
            request(RED.httpAdmin)
                .post('/library/flows/foo')
                .set('Content-Type', 'text/plain')
                .send(flow)
                .expect(204).end(function (err, res) {
                    if (err) {
                        throw err;
                    }
                    request(RED.httpAdmin)
                        .get('/library/flows/foo')
                        .expect(200)
                        .end(function(err,res) {
                            if (err) {
                                throw err;
                            }
                            res.text.should.equal(flow);
                            done();
                        });
                });
        });

        it('lists a stored item', function(done) {
            request(RED.httpAdmin)
                .post('/library/flows/bar')
                .expect(204)
                .end(function () {
                    request(RED.httpAdmin)
                        .get('/library/flows')
                        .expect(200)
                        .end(function(err,res) {
                            if (err) {
                                throw err;
                            }
                            res.body.should.have.property('f');
                            should.deepEqual(res.body.f,['bar']);
                            done();
                        });
                });
        });

        it('returns 403 for malicious access attempt', function(done) {
            // without the userDir override the malicious url would be
            // http://127.0.0.1:1880/library/flows/../../package to
            // obtain package.json from the node-red root.
            request(RED.httpAdmin)
                .get('/library/flows/../../../../../package')
                .expect(403)
                .end(done);
        });

        it('returns 403 for malicious access attempt', function(done) {
            // without the userDir override the malicious url would be
            // http://127.0.0.1:1880/library/flows/../../package to
            // obtain package.json from the node-red root.
            request(RED.httpAdmin)
                .post('/library/flows/../../../../../package')
                .expect(403)
                .end(done);
        });

    });

    describe("type", function() {
        before(function() {
            RED.library.register('test');
        });

        it('returns empty result', function(done) {
            request(RED.httpAdmin)
                .get('/library/test')
                .expect(200)
                .end(function(err,res) {
                    if (err) {
                        throw err;
                    }
                    res.body.should.not.have.property('f');
                    done();
                });
        });

        it('returns 404 for non-existent entry', function(done) {
            request(RED.httpAdmin)
                .get('/library/test/foo')
                .expect(404)
                .end(done);
        });

        it('can store and retrieve item', function(done) {
            var flow = '[]';
            request(RED.httpAdmin)
                .post('/library/test/foo')
                .set('Content-Type', 'text/plain')
                .send(flow)
                .expect(204).end(function (err, res) {
                    if (err) {
                        throw err;
                    }
                    request(RED.httpAdmin)
                        .get('/library/test/foo')
                        .expect(200)
                        .end(function(err,res) {
                            if (err) {
                                throw err;
                            }
                            res.text.should.equal(flow);
                            done();
                        });
                });
        });

        it('lists a stored item', function(done) {
            request(RED.httpAdmin)
                .post('/library/test/bar')
                .expect(204)
                .end(function () {
                    request(RED.httpAdmin)
                        .get('/library/test')
                        .expect(200)
                        .end(function(err,res) {
                            if (err) {
                                throw err;
                            }
                            should.deepEqual(res.body,[{ fn: 'bar'}]);
                            done();
                        });
                });
        });


        it('returns 403 for malicious access attempt', function(done) {
            request(RED.httpAdmin)
                .get('/library/test/../../../../../../../../../../etc/passwd')
                .expect(403)
                .end(done);
        });

        it('returns 403 for malicious access attempt', function(done) {
            request(RED.httpAdmin)
                .get('/library/test/..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\etc\\passwd')
                .expect(403)
                .end(done);
        });

        it('returns 403 for malicious access attempt', function(done) {
            request(RED.httpAdmin)
                .post('/library/test/../../../../../../../../../../etc/passwd')
                .set('Content-Type', 'text/plain')
                .send('root:x:0:0:root:/root:/usr/bin/tclsh')
                .expect(403)
                .end(done);
        });

    });
});