<commit_before>/*jshint node:true */
/*global describe, beforeEach, it, expect, runs, waitsFor */
"use strict";

describe("HTTP util", function () {

    var httpStatusCodeDescrs = require("http").STATUS_CODES,
        nock = require("nock"),
        util = require("util"),
        http = require("http"),
        https = require("https"),
        fs = require("fs"),
        concat = require("concat-stream"),
        httpUtil = require(__dirname + "/../../http-util.js"),
        testFilePath = __dirname + "/../test.jpg";

    httpUtil.logLevel = 5;

    beforeEach(function() {
        this.addMatchers({
            toContainAllElementsOf: function(expected) {
                var expectedLength = expected.length,
                    actualLength = this.actual.length,
                    i;

                if (!this.isNot && actualLength < expectedLength) {
                    this.message = function () {
                        return "Expected actual buffer to have length of at least " + expectedLength;
                    };
                    return false;
                }

                this.message = function () {
                    return "Expected " + util.inspect(this.actual) + (this.isNot ? "not" : "") +
                        " to contain all elements of " + util.inspect(expected);
                };

                for (i = 0; i < expectedLength; ++i) {
                    if (expected[i] !== this.actual[i]) { return false; }
                }
                return true;
            }
        });
    });

    it("should be exported", function() {
        expect(httpUtil).toBeDefined();
    });

    it("should report its version", function() {
        expect(httpUtil.version).toMatch(/^\d+\.\d+\.\d+$/);
    });

    describe("/ bufferResponseContent", function () {

        it("should buffer response-content when content-length header is present", function () {
            var originHost, responseStatusCode, bufferedResponseContent, resourceContent = "Small Resource";

            runs(function () {
                originHost = nock("http://originhost")
                    .get("/resource/small")
                    .reply(200, resourceContent, { "content-length": resourceContent.length });

                http.request("http://originhost/resource/small", function (response) {

                    responseStatusCode = response.statusCode;

                    httpUtil.bufferResponseContent(response, {
                        onEnd: function (buffer, writtenContentLength) {
                            bufferedResponseContent = buffer.slice(0, writtenContentLength).toString();
                        }
                    });
                }).end();
            });

            waitsFor(function () {
                return bufferedResponseContent;
            }, "response-content to become available", 750);

            runs(function () {
                expect(responseStatusCode).toBe(200);
                expect(bufferedResponseContent).toEqual(resourceContent);
                originHost.done();
            });
        });

        it("should buffer response-content when content-length header is absent", function () {
            var originHost, responseStatusCode, bufferedResponseContent, resourceContent = "Small Resource";

            runs(function () {
                originHost = nock("http://originhost")
                    .get("/resource/small")
                    .reply(200, resourceContent);

                http.request("http://originhost/resource/small", function (response) {

                    responseStatusCode = response.statusCode;

                    httpUtil.bufferResponseContent(response, {
                        onEnd: function (buffer, writtenContentLength) {
                            bufferedResponseContent = buffer.slice(0, writtenContentLength).toString();
                        }
                    });
                }).end();
            });

            waitsFor(function () {
                return bufferedResponseContent;
            }, "response-content to become available", 750);

            runs(function () {
                expect(responseStatusCode).toBe(200);
                expect(bufferedResponseContent).toEqual(resourceContent);
                originHost.done();
            });
        });

        it("should buffer extended response-content when content-length header is absent", function () {
            var originHost,
                responseStatusCode,
                bufferedResponseContent,
                resourceContent;

            runs(function () {
                originHost = nock("http://originhost")
                    .get("/resource/small")
                    .replyWithFile(200, testFilePath);

                fs.readFile(testFilePath, function (error, buffer) { resourceContent = buffer; });

                http.request("http://originhost/resource/small", function (response) {

                    responseStatusCode = response.statusCode;

                    httpUtil.bufferResponseContent(response, {
                        onEnd: function (buffer, writtenContentLength) {
                            bufferedResponseContent = buffer.slice(0, writtenContentLength);
                        }
                    });
                }).end();

                waitsFor(function () {
                    return resourceContent && bufferedResponseContent;
                }, "response-content to become available", 750);

                runs(function () {
                    expect(responseStatusCode).toBe(200);
                    expect(bufferedResponseContent).toContainAllElementsOf(resourceContent);
                    originHost.done();
                });
            });
        });

    }); // describe("/ bufferResponseContent", ..

    describe("/ request", function() {

        var redirectStatusCodes = [300, 301, 302, 303, 307],
            request, responseStatusCode, responseContentStream, responseContent,

            // The path of requested resource will be the same in every request
            reqResourcePath = "/some/resource",

            // Define a number of 'transactions' (request - response pairs) to test
            transactions = [{
                req: { method: "GET" },
                rsp: { statusCode: 200, content: "Some Content" }
            }, {
                req: { method: "GET" },
                rsp: { statusCode: 404, content: "" }
            }, {
                req: { method: "POST" },
                rsp: { statusCode: 200, content: "Some Content" }
            }, {
                req: { method: "GET", body: ["please"] },
                rsp: { statusCode: 200, content: "Some Content" }
            }, {
                req: { method: "GET", body: ["pretty", "please"] },
                rsp: { statusCode: 200, content: "Some Content" }
            }, {
                req: { method: "GET", body: ["pretty", "please", "withcherries"] },
                rsp: { statusCode: 200, content: "Some Content" }
            }],
            writeRequestBody = function (requestBodyParts, request) {
                if (!requestBodyParts || !requestBodyParts.length) { return request.end(); }
                request.write(requestBodyParts.shift());
                global.setTimeout(function () { writeRequestBody(requestBodyParts, request); }, 100);
            };

        beforeEach(function () {
            request = null;
            responseStatusCode = null;
            responseContentStream = null;
            responseContent = null;
        });

        transactions.forEach(function (transaction) {

            describe("for transaction " + JSON.stringify(transaction), function () {

                ////
                ////
                it("should return response", function() {

                    var originHost;

                    runs(function () {
                        originHost = nock("http://originhost")
                            .intercept(reqResourcePath, transaction.req.method, transaction.req.body ? transaction.req.body.join("") : undefined)
                            .reply(transaction.rsp.statusCode, transaction.rsp.content);

                        responseContentStream = concat(function (buffer) {
                            responseContent = buffer;
                        });

                        request = httpUtil.request("http://originhost" + reqResourcePath, responseContentStream, {
                            method: transaction.req.method,
                            onResponse: function (statusCode) { responseStatusCode = statusCode; }
                        });

                        writeRequestBody(transaction.req.body, request);
                    });

                    waitsFor(function () {
                        return responseStatusCode && (transaction.rsp.content.length === 0 || responseContent);
                    }, "response-content to become available", 750);

                    runs(function () {
                        expect(responseStatusCode).toBe(transaction.rsp.statusCode);
                        expect(responseContent.toString()).toEqual(transaction.rsp.content);
                        originHost.done();
                    });
                });

                ////
                ////
                it("should return response with extended content", function() {

                    var originHost, resourceContent;

                    runs(function () {
                        originHost = nock("http://originhost")
                            .intercept(reqResourcePath, transaction.req.method, transaction.req.body ? transaction.req.body.join("") : undefined)
                            .replyWithFile(transaction.rsp.statusCode, testFilePath);

                        responseContentStream = concat(function (buffer) {
                            responseContent = buffer;
                        });

                        fs.readFile(testFilePath, function (error, buffer) { resourceContent = buffer; });

                        request = httpUtil.request("http://originhost" + reqResourcePath, responseContentStream, {
                            method: transaction.req.method,
                            onResponse: function (statusCode) { responseStatusCode = statusCode; }
                        });

                        writeRequestBody(transaction.req.body, request);
                    });

                    waitsFor(function () {
                        return responseStatusCode && responseContent && resourceContent;
                    }, "response-content to become available", 750);

                    runs(function () {
                        expect(responseStatusCode).toBe(transaction.rsp.statusCode);
                        expect(responseContent).toContainAllElementsOf(resourceContent);
                        originHost.done();
                    });
                });

                ////
                ////
                redirectStatusCodes.forEach(function (statusCode) {
                    it("should return response, following a single redirect (" + statusCode + ", " + httpStatusCodeDescrs[statusCode] + ")", function() {
                        var redirectingHost, originHost;

                        runs(function () {
                            redirectingHost = nock("http://redirectinghost")
                                .intercept(reqResourcePath, transaction.req.method)
                                .reply(statusCode, httpStatusCodeDescrs[statusCode], { Location: "http://originhost/some/resource"});

                            originHost = nock("http://originhost")
                                .intercept(reqResourcePath, transaction.req.method, transaction.req.body ? transaction.req.body.join("") : undefined)
                                .reply(transaction.rsp.statusCode, transaction.rsp.content);

                            responseContentStream = concat(function (buffer) {
                                responseContent = buffer;
                            });

                            request = httpUtil.request("http://redirectinghost" + reqResourcePath, responseContentStream, {
                                method: transaction.req.method,
                                followRedirects: true,
                                onResponse: function (statusCode) { responseStatusCode = statusCode; }
                            });

                            writeRequestBody(transaction.req.body, request);
                        });

                        waitsFor(function () {
                            return responseStatusCode && (transaction.rsp.content.length === 0 || responseContent);
                        }, "response-content to become available", 750);

                        runs(function () {
                            expect(responseStatusCode).toBe(transaction.rsp.statusCode);
                            expect(responseContent.toString()).toEqual(transaction.rsp.content);
                            redirectingHost.done();
                            originHost.done();
                        });
                    });

                    it("should return response with extended content, following a single redirect (" + statusCode + ", " + httpStatusCodeDescrs[statusCode] + ")", function() {
                        var redirectingHost, originHost, resourceContent;

                        runs(function () {
                            redirectingHost = nock("http://redirectinghost")
                                .intercept(reqResourcePath, transaction.req.method)
                                .reply(statusCode, httpStatusCodeDescrs[statusCode], { Location: "http://originhost/some/resource"});

                            originHost = nock("http://originhost")
                                .intercept(reqResourcePath, transaction.req.method, transaction.req.body ? transaction.req.body.join("") : undefined)
                                .replyWithFile(transaction.rsp.statusCode, testFilePath);

                            fs.readFile(testFilePath, function (error, buffer) { resourceContent = buffer; });

                            responseContentStream = concat(function (buffer) {
                                responseContent = buffer;
                            });

                            request = httpUtil.request("http://redirectinghost" + reqResourcePath, responseContentStream, {
                                protocol: "http",
                                hostname: "redirectinghost",
                                method: transaction.req.method,
                                path: reqResourcePath,
                                followRedirects: true,
                                onResponse: function (statusCode) { responseStatusCode = statusCode; }
                            });

                            writeRequestBody(transaction.req.body, request);
                        });

                        waitsFor(function () {
                            return responseStatusCode && resourceContent && responseContent;
                        }, "response-content to become available", 750);

                        runs(function () {
                            expect(responseStatusCode).toBe(transaction.rsp.statusCode);
                            expect(responseContent).toContainAllElementsOf(resourceContent);
                            redirectingHost.done();
                            originHost.done();
                        });
                    });
                });

                ////
                ////
                it("should return response following multiple redirects (" + redirectStatusCodes.join(", ") + ")", function() {
                    var redirectingHosts = [], originHost;

                    runs(function () {
                        var redirectLocation, i, l;
                        for (i = 0, l = redirectStatusCodes.length; i < l; ++i) {
                            redirectLocation = (i + 1) === l ? "http://originhost/some/resource" : "http://redirectinghost" + (i + 1) + "/some/resource";
                            redirectingHosts.push(nock("http://redirectinghost" + i)
                                .intercept(reqResourcePath, transaction.req.method)
                                .reply(redirectStatusCodes[i], httpStatusCodeDescrs[redirectStatusCodes[i]], { Location: redirectLocation }));
                        }

                        originHost = nock("http://originhost")
                            .intercept(reqResourcePath, transaction.req.method, transaction.req.body ? transaction.req.body.join("") : undefined)
                            .reply(transaction.rsp.statusCode, transaction.rsp.content);

                        responseContentStream = concat(function (buffer) {
                            responseContent = buffer;
                        });

                        request = httpUtil.request("http://redirectinghost0" + reqResourcePath, responseContentStream, {
                            method: transaction.req.method,
                            followRedirects: true,
                            onResponse: function (statusCode) { responseStatusCode = statusCode; }
                        });

                        writeRequestBody(transaction.req.body, request);
                    });

                    waitsFor(function () {
                        return responseStatusCode && (transaction.rsp.content.length === 0 || responseContent);
                    }, "response-content to become available", 750);

                    runs(function () {
                        expect(responseStatusCode).toBe(transaction.rsp.statusCode);
                        expect(responseContent.toString()).toEqual(transaction.rsp.content);
                        redirectingHosts.forEach(function (redirectingHost) { redirectingHost.done(); });
                        originHost.done();
                    });
                });

                ////
                ////
                it("should return response with extended content, following multiple redirects (" + redirectStatusCodes.join(", ") + ")", function() {
                    var redirectingHosts = [], originHost, resourceContent;

                    runs(function () {
                        var redirectLocation, i, l;
                        for (i = 0, l = redirectStatusCodes.length; i < l; ++i) {
                            redirectLocation = (i + 1) === l ? "http://originhost/some/resource" : "http://redirectinghost" + (i + 1) + "/some/resource";
                            redirectingHosts.push(nock("http://redirectinghost" + i)
                                .intercept(reqResourcePath, transaction.req.method)
                                .reply(redirectStatusCodes[i], httpStatusCodeDescrs[redirectStatusCodes[i]], { Location: redirectLocation }));
                        }

                        originHost = nock("http://originhost")
                            .intercept(reqResourcePath, transaction.req.method, transaction.req.body ? transaction.req.body.join("") : undefined)
                            .replyWithFile(transaction.rsp.statusCode, testFilePath);

                        fs.readFile(testFilePath, function (error, buffer) { resourceContent = buffer; });

                        responseContentStream = concat(function (buffer) {
                            responseContent = buffer;
                        });

                        request = httpUtil.request("http://redirectinghost0" + reqResourcePath, responseContentStream, {
                            method: transaction.req.method,
                            followRedirects: true,
                            onResponse: function (statusCode) { responseStatusCode = statusCode; }
                        });

                        writeRequestBody(transaction.req.body, request);
                    });

                    waitsFor(function () {
                        return responseStatusCode && resourceContent && responseContent;
                    }, "response-content to become available", 750);

                    runs(function () {
                        expect(responseStatusCode).toBe(transaction.rsp.statusCode);
                        expect(responseContent).toContainAllElementsOf(resourceContent);
                        redirectingHosts.forEach(function (redirectingHost) { redirectingHost.done(); });
                        originHost.done();
                    });
                });

            }); // describe("when origin-host response is ...
        }); // replies.forEach
    }); // describe("/ request", ..
}); // describe("HTTP util", ...<commit_msg>Lower test suite log-level<commit_after>/*jshint node:true */
/*global describe, beforeEach, it, expect, runs, waitsFor */
"use strict";

describe("HTTP util", function () {

    var httpStatusCodeDescrs = require("http").STATUS_CODES,
        nock = require("nock"),
        util = require("util"),
        http = require("http"),
        https = require("https"),
        fs = require("fs"),
        concat = require("concat-stream"),
        httpUtil = require(__dirname + "/../../http-util.js"),
        testFilePath = __dirname + "/../test.jpg";

    httpUtil.logLevel = 1;

    beforeEach(function() {
        this.addMatchers({
            toContainAllElementsOf: function(expected) {
                var expectedLength = expected.length,
                    actualLength = this.actual.length,
                    i;

                if (!this.isNot && actualLength < expectedLength) {
                    this.message = function () {
                        return "Expected actual buffer to have length of at least " + expectedLength;
                    };
                    return false;
                }

                this.message = function () {
                    return "Expected " + util.inspect(this.actual) + (this.isNot ? "not" : "") +
                        " to contain all elements of " + util.inspect(expected);
                };

                for (i = 0; i < expectedLength; ++i) {
                    if (expected[i] !== this.actual[i]) { return false; }
                }
                return true;
            }
        });
    });

    it("should be exported", function() {
        expect(httpUtil).toBeDefined();
    });

    it("should report its version", function() {
        expect(httpUtil.version).toMatch(/^\d+\.\d+\.\d+$/);
    });

    describe("/ bufferResponseContent", function () {

        it("should buffer response-content when content-length header is present", function () {
            var originHost, responseStatusCode, bufferedResponseContent, resourceContent = "Small Resource";

            runs(function () {
                originHost = nock("http://originhost")
                    .get("/resource/small")
                    .reply(200, resourceContent, { "content-length": resourceContent.length });

                http.request("http://originhost/resource/small", function (response) {

                    responseStatusCode = response.statusCode;

                    httpUtil.bufferResponseContent(response, {
                        onEnd: function (buffer, writtenContentLength) {
                            bufferedResponseContent = buffer.slice(0, writtenContentLength).toString();
                        }
                    });
                }).end();
            });

            waitsFor(function () {
                return bufferedResponseContent;
            }, "response-content to become available", 750);

            runs(function () {
                expect(responseStatusCode).toBe(200);
                expect(bufferedResponseContent).toEqual(resourceContent);
                originHost.done();
            });
        });

        it("should buffer response-content when content-length header is absent", function () {
            var originHost, responseStatusCode, bufferedResponseContent, resourceContent = "Small Resource";

            runs(function () {
                originHost = nock("http://originhost")
                    .get("/resource/small")
                    .reply(200, resourceContent);

                http.request("http://originhost/resource/small", function (response) {

                    responseStatusCode = response.statusCode;

                    httpUtil.bufferResponseContent(response, {
                        onEnd: function (buffer, writtenContentLength) {
                            bufferedResponseContent = buffer.slice(0, writtenContentLength).toString();
                        }
                    });
                }).end();
            });

            waitsFor(function () {
                return bufferedResponseContent;
            }, "response-content to become available", 750);

            runs(function () {
                expect(responseStatusCode).toBe(200);
                expect(bufferedResponseContent).toEqual(resourceContent);
                originHost.done();
            });
        });

        it("should buffer extended response-content when content-length header is absent", function () {
            var originHost,
                responseStatusCode,
                bufferedResponseContent,
                resourceContent;

            runs(function () {
                originHost = nock("http://originhost")
                    .get("/resource/small")
                    .replyWithFile(200, testFilePath);

                fs.readFile(testFilePath, function (error, buffer) { resourceContent = buffer; });

                http.request("http://originhost/resource/small", function (response) {

                    responseStatusCode = response.statusCode;

                    httpUtil.bufferResponseContent(response, {
                        onEnd: function (buffer, writtenContentLength) {
                            bufferedResponseContent = buffer.slice(0, writtenContentLength);
                        }
                    });
                }).end();

                waitsFor(function () {
                    return resourceContent && bufferedResponseContent;
                }, "response-content to become available", 750);

                runs(function () {
                    expect(responseStatusCode).toBe(200);
                    expect(bufferedResponseContent).toContainAllElementsOf(resourceContent);
                    originHost.done();
                });
            });
        });

    }); // describe("/ bufferResponseContent", ..

    describe("/ request", function() {

        var redirectStatusCodes = [300, 301, 302, 303, 307],
            request, responseStatusCode, responseContentStream, responseContent,

            // The path of requested resource will be the same in every request
            reqResourcePath = "/some/resource",

            // Define a number of 'transactions' (request - response pairs) to test
            transactions = [{
                req: { method: "GET" },
                rsp: { statusCode: 200, content: "Some Content" }
            }, {
                req: { method: "GET" },
                rsp: { statusCode: 404, content: "" }
            }, {
                req: { method: "POST" },
                rsp: { statusCode: 200, content: "Some Content" }
            }, {
                req: { method: "GET", body: ["please"] },
                rsp: { statusCode: 200, content: "Some Content" }
            }, {
                req: { method: "GET", body: ["pretty", "please"] },
                rsp: { statusCode: 200, content: "Some Content" }
            }, {
                req: { method: "GET", body: ["pretty", "please", "withcherries"] },
                rsp: { statusCode: 200, content: "Some Content" }
            }],
            writeRequestBody = function (requestBodyParts, request) {
                if (!requestBodyParts || !requestBodyParts.length) { return request.end(); }
                request.write(requestBodyParts.shift());
                global.setTimeout(function () { writeRequestBody(requestBodyParts, request); }, 100);
            };

        beforeEach(function () {
            request = null;
            responseStatusCode = null;
            responseContentStream = null;
            responseContent = null;
        });

        transactions.forEach(function (transaction) {

            describe("for transaction " + JSON.stringify(transaction), function () {

                ////
                ////
                it("should return response", function() {

                    var originHost;

                    runs(function () {
                        originHost = nock("http://originhost")
                            .intercept(reqResourcePath, transaction.req.method, transaction.req.body ? transaction.req.body.join("") : undefined)
                            .reply(transaction.rsp.statusCode, transaction.rsp.content);

                        responseContentStream = concat(function (buffer) {
                            responseContent = buffer;
                        });

                        request = httpUtil.request("http://originhost" + reqResourcePath, responseContentStream, {
                            method: transaction.req.method,
                            onResponse: function (statusCode) { responseStatusCode = statusCode; }
                        });

                        writeRequestBody(transaction.req.body, request);
                    });

                    waitsFor(function () {
                        return responseStatusCode && (transaction.rsp.content.length === 0 || responseContent);
                    }, "response-content to become available", 750);

                    runs(function () {
                        expect(responseStatusCode).toBe(transaction.rsp.statusCode);
                        expect(responseContent.toString()).toEqual(transaction.rsp.content);
                        originHost.done();
                    });
                });

                ////
                ////
                it("should return response with extended content", function() {

                    var originHost, resourceContent;

                    runs(function () {
                        originHost = nock("http://originhost")
                            .intercept(reqResourcePath, transaction.req.method, transaction.req.body ? transaction.req.body.join("") : undefined)
                            .replyWithFile(transaction.rsp.statusCode, testFilePath);

                        responseContentStream = concat(function (buffer) {
                            responseContent = buffer;
                        });

                        fs.readFile(testFilePath, function (error, buffer) { resourceContent = buffer; });

                        request = httpUtil.request("http://originhost" + reqResourcePath, responseContentStream, {
                            method: transaction.req.method,
                            onResponse: function (statusCode) { responseStatusCode = statusCode; }
                        });

                        writeRequestBody(transaction.req.body, request);
                    });

                    waitsFor(function () {
                        return responseStatusCode && responseContent && resourceContent;
                    }, "response-content to become available", 750);

                    runs(function () {
                        expect(responseStatusCode).toBe(transaction.rsp.statusCode);
                        expect(responseContent).toContainAllElementsOf(resourceContent);
                        originHost.done();
                    });
                });

                ////
                ////
                redirectStatusCodes.forEach(function (statusCode) {
                    it("should return response, following a single redirect (" + statusCode + ", " + httpStatusCodeDescrs[statusCode] + ")", function() {
                        var redirectingHost, originHost;

                        runs(function () {
                            redirectingHost = nock("http://redirectinghost")
                                .intercept(reqResourcePath, transaction.req.method)
                                .reply(statusCode, httpStatusCodeDescrs[statusCode], { Location: "http://originhost/some/resource"});

                            originHost = nock("http://originhost")
                                .intercept(reqResourcePath, transaction.req.method, transaction.req.body ? transaction.req.body.join("") : undefined)
                                .reply(transaction.rsp.statusCode, transaction.rsp.content);

                            responseContentStream = concat(function (buffer) {
                                responseContent = buffer;
                            });

                            request = httpUtil.request("http://redirectinghost" + reqResourcePath, responseContentStream, {
                                method: transaction.req.method,
                                followRedirects: true,
                                onResponse: function (statusCode) { responseStatusCode = statusCode; }
                            });

                            writeRequestBody(transaction.req.body, request);
                        });

                        waitsFor(function () {
                            return responseStatusCode && (transaction.rsp.content.length === 0 || responseContent);
                        }, "response-content to become available", 750);

                        runs(function () {
                            expect(responseStatusCode).toBe(transaction.rsp.statusCode);
                            expect(responseContent.toString()).toEqual(transaction.rsp.content);
                            redirectingHost.done();
                            originHost.done();
                        });
                    });

                    it("should return response with extended content, following a single redirect (" + statusCode + ", " + httpStatusCodeDescrs[statusCode] + ")", function() {
                        var redirectingHost, originHost, resourceContent;

                        runs(function () {
                            redirectingHost = nock("http://redirectinghost")
                                .intercept(reqResourcePath, transaction.req.method)
                                .reply(statusCode, httpStatusCodeDescrs[statusCode], { Location: "http://originhost/some/resource"});

                            originHost = nock("http://originhost")
                                .intercept(reqResourcePath, transaction.req.method, transaction.req.body ? transaction.req.body.join("") : undefined)
                                .replyWithFile(transaction.rsp.statusCode, testFilePath);

                            fs.readFile(testFilePath, function (error, buffer) { resourceContent = buffer; });

                            responseContentStream = concat(function (buffer) {
                                responseContent = buffer;
                            });

                            request = httpUtil.request("http://redirectinghost" + reqResourcePath, responseContentStream, {
                                protocol: "http",
                                hostname: "redirectinghost",
                                method: transaction.req.method,
                                path: reqResourcePath,
                                followRedirects: true,
                                onResponse: function (statusCode) { responseStatusCode = statusCode; }
                            });

                            writeRequestBody(transaction.req.body, request);
                        });

                        waitsFor(function () {
                            return responseStatusCode && resourceContent && responseContent;
                        }, "response-content to become available", 750);

                        runs(function () {
                            expect(responseStatusCode).toBe(transaction.rsp.statusCode);
                            expect(responseContent).toContainAllElementsOf(resourceContent);
                            redirectingHost.done();
                            originHost.done();
                        });
                    });
                });

                ////
                ////
                it("should return response following multiple redirects (" + redirectStatusCodes.join(", ") + ")", function() {
                    var redirectingHosts = [], originHost;

                    runs(function () {
                        var redirectLocation, i, l;
                        for (i = 0, l = redirectStatusCodes.length; i < l; ++i) {
                            redirectLocation = (i + 1) === l ? "http://originhost/some/resource" : "http://redirectinghost" + (i + 1) + "/some/resource";
                            redirectingHosts.push(nock("http://redirectinghost" + i)
                                .intercept(reqResourcePath, transaction.req.method)
                                .reply(redirectStatusCodes[i], httpStatusCodeDescrs[redirectStatusCodes[i]], { Location: redirectLocation }));
                        }

                        originHost = nock("http://originhost")
                            .intercept(reqResourcePath, transaction.req.method, transaction.req.body ? transaction.req.body.join("") : undefined)
                            .reply(transaction.rsp.statusCode, transaction.rsp.content);

                        responseContentStream = concat(function (buffer) {
                            responseContent = buffer;
                        });

                        request = httpUtil.request("http://redirectinghost0" + reqResourcePath, responseContentStream, {
                            method: transaction.req.method,
                            followRedirects: true,
                            onResponse: function (statusCode) { responseStatusCode = statusCode; }
                        });

                        writeRequestBody(transaction.req.body, request);
                    });

                    waitsFor(function () {
                        return responseStatusCode && (transaction.rsp.content.length === 0 || responseContent);
                    }, "response-content to become available", 750);

                    runs(function () {
                        expect(responseStatusCode).toBe(transaction.rsp.statusCode);
                        expect(responseContent.toString()).toEqual(transaction.rsp.content);
                        redirectingHosts.forEach(function (redirectingHost) { redirectingHost.done(); });
                        originHost.done();
                    });
                });

                ////
                ////
                it("should return response with extended content, following multiple redirects (" + redirectStatusCodes.join(", ") + ")", function() {
                    var redirectingHosts = [], originHost, resourceContent;

                    runs(function () {
                        var redirectLocation, i, l;
                        for (i = 0, l = redirectStatusCodes.length; i < l; ++i) {
                            redirectLocation = (i + 1) === l ? "http://originhost/some/resource" : "http://redirectinghost" + (i + 1) + "/some/resource";
                            redirectingHosts.push(nock("http://redirectinghost" + i)
                                .intercept(reqResourcePath, transaction.req.method)
                                .reply(redirectStatusCodes[i], httpStatusCodeDescrs[redirectStatusCodes[i]], { Location: redirectLocation }));
                        }

                        originHost = nock("http://originhost")
                            .intercept(reqResourcePath, transaction.req.method, transaction.req.body ? transaction.req.body.join("") : undefined)
                            .replyWithFile(transaction.rsp.statusCode, testFilePath);

                        fs.readFile(testFilePath, function (error, buffer) { resourceContent = buffer; });

                        responseContentStream = concat(function (buffer) {
                            responseContent = buffer;
                        });

                        request = httpUtil.request("http://redirectinghost0" + reqResourcePath, responseContentStream, {
                            method: transaction.req.method,
                            followRedirects: true,
                            onResponse: function (statusCode) { responseStatusCode = statusCode; }
                        });

                        writeRequestBody(transaction.req.body, request);
                    });

                    waitsFor(function () {
                        return responseStatusCode && resourceContent && responseContent;
                    }, "response-content to become available", 750);

                    runs(function () {
                        expect(responseStatusCode).toBe(transaction.rsp.statusCode);
                        expect(responseContent).toContainAllElementsOf(resourceContent);
                        redirectingHosts.forEach(function (redirectingHost) { redirectingHost.done(); });
                        originHost.done();
                    });
                });

            }); // describe("when origin-host response is ...
        }); // replies.forEach
    }); // describe("/ request", ..
}); // describe("HTTP util", ...<|endoftext|>


<commit_before>const gulp = require('gulp');
const uglify = require('gulp-uglify-es').default;
const useref = require('gulp-useref');
const rename = require('gulp-rename');
const mocha = require('gulp-mocha');
const babel = require('gulp-babel');
const eslint = require('gulp-eslint');
const shell = require('gulp-shell');

const observableSlimPath = './observable-slim.js';
const proxyPath = './proxy.js';
const testPath = './test/test.js';
const coverallsCoverageDirPath = './coverage';
const coverallsCoverageLcovPath = `${coverallsCoverageDirPath}/lcov.info`;
const coverallsBinPath = './node_modules/coveralls/bin/coveralls.js';

gulp.task('default', (done) => gulp.src([observableSlimPath, proxyPath])
    .pipe(babel({
        presets: ['@babel/preset-env'],
        sourceType: 'script' // Prevent insertion of "use strict".
    }))
    .pipe(useref())
    .pipe(uglify())
    .pipe(rename({
        suffix: '.min' // Add .min to the minified filename.
    }))
    .pipe(gulp.dest('./')) // Write it to the current directory.
    .on('end', done)
);

gulp.task('test', (done) => gulp.src([testPath])
    .pipe(mocha({
        compilers: babel
    }))
    .on('end', done)
);

gulp.task('coveralls', shell.task([
    `nyc report --reporter=lcov && cat ${coverallsCoverageLcovPath} | ${coverallsBinPath} && rm -rf ${coverallsCoverageDirPath}`
]));

gulp.task('lint', (done) => gulp.src([observableSlimPath, proxyPath, testPath])
    .pipe(eslint())
    .pipe(eslint.format())
    .pipe(eslint.failOnError()) // Brick on failure to be super strict.
    .on('end', done)
);
<commit_msg>Windows support for the `gulp coveralls` command<commit_after>const process = require('process');
const path = require('path');
const gulp = require('gulp');
const uglify = require('gulp-uglify-es').default;
const useref = require('gulp-useref');
const rename = require('gulp-rename');
const mocha = require('gulp-mocha');
const babel = require('gulp-babel');
const eslint = require('gulp-eslint');
const shell = require('gulp-shell');

const observableSlimPath = './observable-slim.js';
const proxyPath = './proxy.js';
const testPath = './test/test.js';
const coverallsCoverageDirPath = './coverage';
const coverallsCoverageLcovPath = `${coverallsCoverageDirPath}/lcov.info`;
const coverallsBinPath = './node_modules/coveralls/bin/coveralls.js';
const coverallsCommand = (process.platform === 'win32')
// Windows (we have to resolve the paths).
? `nyc report --reporter=lcov`
    + ` && type ${path.resolve(coverallsCoverageLcovPath)} | ${path.resolve(coverallsBinPath)}`
    + ` && rmdir /s /q ${path.resolve(coverallsCoverageDirPath)}`
// Linux.
: `nyc report --reporter=lcov`
    + ` && cat ${coverallsCoverageLcovPath} | ${coverallsBinPath}`
    + ` && rm -rf ${coverallsCoverageDirPath}`;

gulp.task('default', (done) => gulp.src([observableSlimPath, proxyPath])
    .pipe(babel({
        presets: ['@babel/preset-env'],
        sourceType: 'script' // Prevent insertion of "use strict".
    }))
    .pipe(useref())
    .pipe(uglify())
    .pipe(rename({
        suffix: '.min' // Add .min to the minified filename.
    }))
    .pipe(gulp.dest('./')) // Write it to the current directory.
    .on('end', done)
);

gulp.task('test', (done) => gulp.src([testPath])
    .pipe(mocha({
        compilers: babel
    }))
    .on('end', done)
);

gulp.task('coveralls', shell.task([coverallsCommand]));

gulp.task('lint', (done) => gulp.src([observableSlimPath, proxyPath, testPath])
    .pipe(eslint())
    .pipe(eslint.format())
    .pipe(eslint.failOnError()) // Brick on failure to be super strict.
    .on('end', done)
);
<|endoftext|>


<commit_before>

const UserKinds = require('./constants').UserKinds;

// core state is namespaced, and merged with a particular app's state
const initialState = {
  core: {
    error: '',
    loading: true,
    session: { id: undefined,
               username: '',
               full_name: '',
               user_id: undefined,
               facility_id: undefined,
               kind: [UserKinds.ANONYMOUS],
               error: '200' },
    login_modal_state: false,
    fullname: '',
    logging: {
      summary: { progress: 0 },
      session: { },
    },
  },
};

const mutations = {
  CORE_SET_SESSION(state, value) {
    state.core.session = value;
    state.core.login_modal_state = false;
  },
  // Makes settings for wrong credentials 401 error
  CORE_HANDLE_WRONG_CREDS(state, value) {
    state.core.session = value;
  },
  CORE_CLEAR_SESSION(state) {
    state.core.session = { id: undefined,
                           username: '',
                           full_name: '',
                           user_id: undefined,
                           facility_id: undefined,
                           kind: [UserKinds.ANONYMOUS],
                           error: '200' };
  },
  CORE_SET_PAGE_LOADING(state, value) {
    state.core.loading = value;
  },
  CORE_SET_ERROR(state, error) {
    state.core.error = error;
  },
  // Handles state of login modal appearance
  CORE_SET_MODAL_STATE(state, value) {
    state.core.login_modal_state = value;
  },
  SET_LOGGING_SUMMARY_STATE(state, summaryState) {
    state.core.logging.summary = summaryState;
  },
  SET_LOGGING_SUMMARY_ID(state, summaryId) {
    state.core.logging.summary.id = summaryId;
  },
  SET_LOGGING_SESSION_ID(state, sessionId) {
    state.core.logging.session.id = sessionId;
  },
  SET_LOGGING_SESSION_STATE(state, sessionState) {
    state.core.logging.session = sessionState;
  },
  SET_LOGGING_PROGRESS(state, sessionProgress, summaryProgress) {
    state.core.logging.session.progress = sessionProgress;
    state.core.logging.summary.progress = summaryProgress;
  },
  SET_LOGGING_COMPLETION_TIME(state, time) {
    state.core.logging.summary.completion_timestamp = time;
  },
  SET_LOGGING_TIME(state, sessionTime, summaryTime, currentTime) {
    state.core.logging.session.end_timestamp = currentTime;
    state.core.logging.summary.end_timestamp = currentTime;
    state.core.logging.session.time_spent = sessionTime;
    state.core.logging.summary.time_spent = summaryTime;
  },
  SET_LOGGING_PENDING(state, summaryPending, sessionPending) {
    state.core.logging.summary.pending_create = summaryPending;
    state.core.logging.session.pending_create = sessionPending;
  },
  SET_LOGGING_THRESHOLD_CHECKS(state, progress, timeSpent) {
    state.core.logging.session.total_time_at_last_save = timeSpent;
    state.core.logging.session.progress_at_last_save = progress;
  },
};

module.exports = {
  initialState,
  mutations,
};
<commit_msg>Setup a pageSessionId and increment whenever a page is set to load.<commit_after>

const UserKinds = require('./constants').UserKinds;

// core state is namespaced, and merged with a particular app's state
const initialState = {
  core: {
    error: '',
    loading: true,
    pageSessionId: 0,
    session: { id: undefined,
               username: '',
               full_name: '',
               user_id: undefined,
               facility_id: undefined,
               kind: [UserKinds.ANONYMOUS],
               error: '200' },
    login_modal_state: false,
    fullname: '',
    logging: {
      summary: { progress: 0 },
      session: { },
    },
  },
};

const mutations = {
  CORE_SET_SESSION(state, value) {
    state.core.session = value;
    state.core.login_modal_state = false;
  },
  // Makes settings for wrong credentials 401 error
  CORE_HANDLE_WRONG_CREDS(state, value) {
    state.core.session = value;
  },
  CORE_CLEAR_SESSION(state) {
    state.core.session = { id: undefined,
                           username: '',
                           full_name: '',
                           user_id: undefined,
                           facility_id: undefined,
                           kind: [UserKinds.ANONYMOUS],
                           error: '200' };
  },
  CORE_SET_PAGE_LOADING(state, value) {
    const update = { loading: value };
    if (value) {
      Object.assign(update, { pageSessionId: state.core.pageSessionId + 1 });
    }
    Object.assign(state.core, update);
  },
  CORE_SET_ERROR(state, error) {
    state.core.error = error;
  },
  // Handles state of login modal appearance
  CORE_SET_MODAL_STATE(state, value) {
    state.core.login_modal_state = value;
  },
  SET_LOGGING_SUMMARY_STATE(state, summaryState) {
    state.core.logging.summary = summaryState;
  },
  SET_LOGGING_SUMMARY_ID(state, summaryId) {
    state.core.logging.summary.id = summaryId;
  },
  SET_LOGGING_SESSION_ID(state, sessionId) {
    state.core.logging.session.id = sessionId;
  },
  SET_LOGGING_SESSION_STATE(state, sessionState) {
    state.core.logging.session = sessionState;
  },
  SET_LOGGING_PROGRESS(state, sessionProgress, summaryProgress) {
    state.core.logging.session.progress = sessionProgress;
    state.core.logging.summary.progress = summaryProgress;
  },
  SET_LOGGING_COMPLETION_TIME(state, time) {
    state.core.logging.summary.completion_timestamp = time;
  },
  SET_LOGGING_TIME(state, sessionTime, summaryTime, currentTime) {
    state.core.logging.session.end_timestamp = currentTime;
    state.core.logging.summary.end_timestamp = currentTime;
    state.core.logging.session.time_spent = sessionTime;
    state.core.logging.summary.time_spent = summaryTime;
  },
  SET_LOGGING_PENDING(state, summaryPending, sessionPending) {
    state.core.logging.summary.pending_create = summaryPending;
    state.core.logging.session.pending_create = sessionPending;
  },
  SET_LOGGING_THRESHOLD_CHECKS(state, progress, timeSpent) {
    state.core.logging.session.total_time_at_last_save = timeSpent;
    state.core.logging.session.progress_at_last_save = progress;
  },
};

module.exports = {
  initialState,
  mutations,
};
<|endoftext|>


<commit_before>
'use strict';

const config = require('../config');

const winston = require('../log-setup');
const gamestory = winston.loggers.get('gamestory');

const request = require('request');
const sortByRank = require('poker-rank');
const getCombinations = require('poker-combinations');

const status = require('../domain/player-status');

const save = require('../storage').save;
const safeSum = require('../lib/safe-math').safeSum;
const safeDiff = require('../lib/safe-math').safeDiff;

const herokuService = Symbol('heroku-service');

const hasDB = Symbol.for('hasDB');
const gameProgressive = Symbol.for('game-progressive');
const roundProgressive = Symbol.for('hand-progressive');

const actions = {

  //
  // prepare the gamestate model with the only information
  // each player should know
  // then send an http request to the bot service
  // to get the bet amount
  talk: function talk(gs){

    //
    // the state of the game, filtered with the only information
    // available to the current player
    const ps = Object.create(null);

    // the unique id of the current tournament
    ps.tournamentId = gs.tournamentId;

    // game number of the current tournament
    ps.game = gs[gameProgressive];

    // round number of the current tournament
    ps.round = gs[roundProgressive];

    // count the number of time
    // that players had already have the possibility to bet in the current session.
    ps.spinCount = gs.spinCount;

    // value of the small blinds
    // ... big blind is always twice
    ps.sb = gs.sb;

    // value of the pot
    // it's updated after each bet
    ps.pot = gs.pot;

    // list of the community cards on the table
    // everyone is able to access this same list
    ps.commonCards = gs.commonCards;

    // the list of the players...
    // make sure that the current players can see only its cards
    ps.players = gs.players.map((player, id) => {
      // clean player data
      let mysterious = Object.assign({}, player);
      Object.getOwnPropertySymbols(mysterious).forEach(symb => delete mysterious[symb]);
      if (this.id === id){
        return mysterious;
      }
      delete mysterious.cards;
      delete mysterious.bestCards;
      return mysterious;
    });

    // index of the player with the dealer button
    ps.db = Math.max(gs.players.findIndex(player => player[hasDB]), 0);

    // index of the player 'this' in the players array
    ps.me = this.id;

    // amount of chips the current player must bet
    // in order to remain in the game;
    // it depends by how much he bet previously;
    // so it can change for each player
    ps.callAmount = Math.max(safeDiff(gs.callAmount, this.chipsBet), 0);

    const tag = { id: gs.handId, type: 'bet' };

    gamestory.info('We\'re asking to %s (%d) the amount of his bet on the basis of %s', this.name, this.id, JSON.stringify(ps), tag);

    // send a network request to the Heroku's service
    // to get the bot's bet amount
    const service = `${this[herokuService]}bet`
    return new Promise((resolve, reject) => {
      request.post(service, { body: ps, json: true, timeout: 5000 }, (err, response, playerBetAmount) => {
        if (err){
          return void reject(err);
        }
        gamestory.info('%s (%d) bets %d', this.name, this.id, playerBetAmount, tag);
        resolve(playerBetAmount);
      });
    });

  },

  //
  // updates the player state, and the game state
  // accordingly to the player bet
  bet: function bet(gs, amount){

    //
    // sanitization
    if (typeof amount != 'number'){
      // yep, i really mean parseFloat.
      // a player usually does not own, or bet a decimal amount;
      // however after a plot split it's possible that a player has
      // a not-integer amount of chips.
      amount = Number.parseFloat(amount);
    }

    if (Number.isNaN(amount)){
      amount = 0;
    }

    //
    // amount should be in the range [ 0 ... player.chips ]
    if (amount < 0){
      amount = 0;
    }
    else if (amount > this.chips){
      amount = this.chips;
    }

    if (this.isAllin(amount)){
      gamestory.info('%s (%d) is allin', this.name, this.id, { id: gs.handId, type: 'status' });
      this[Symbol.for('allin')] = true;
    }
    else {

      // the total amount of chips which the player
      // has bet in the current hand
      const chipsBet = safeSum(this.chipsBet, amount);

      if (chipsBet < gs.callAmount){
        // player is betting less than the required amount;
        // since he is not betting all the chips he owns
        // we treat this as a "fold" declaration
        gamestory.info('%s (%d) folded', this.name, this.id, { id: gs.handId, type: 'status' });
        return this.fold(gs);
      }
      else if (chipsBet > gs.callAmount) {
        // player is betting a raise;
        // since the player is not going allin,
        // we accept only raise of an amount that is multiple of the small blind.
        const raiseAmount = safeDiff(chipsBet, gs.callAmount);
        if (raiseAmount % gs.sb != 0){
          // in case the raise amount is not a multiple of the small blind
          // the raise, is treated as a simple call.
          amount = safeDiff(amount, raiseAmount);
        }
      }

    }

    //
    // update chip values
    this.chipsBet = safeSum(amount, this.chipsBet);
    this.chips = safeDiff(this.chips, amount);

    gs.pot = safeSum(gs.pot, amount);
    gs.callAmount = Math.max(this.chipsBet, gs.callAmount);

    gamestory.info('Game state after %s (%d)\'s bet: %s', this.name, this.id, JSON.stringify({ pot:gs.pot, callAmount: gs.callAmount, player: { name: this.name, chips: this.chips, chipsBet: this.chipsBet } }), { id: gs.handId, type: 'bet' });

    return save(gs, { type: 'bet', handId: gs.handId, session: gs.session, playerId: this.id, amount: amount });

  },

  //
  // compute the player best combination
  showdown: function showdown(commonCards){
    let combs = getCombinations(this.cards.concat(commonCards), 5);
    let bestHand = sortByRank(combs)[0];
    this.bestCards = combs[bestHand.index];
    gamestory.info('%s (%d)\'s best combination is: %s', this.name, this.id, JSON.stringify(this.bestCards));
    return this.bestCards;
  },

  //
  // return true if the specified amount
  // is equal to the player total number of chips
  isAllin: function isAllin(amount){
    return amount === this.chips;
  },

  //
  // change the player status to folded
  fold: function fold(gs){
    this.status = status.folded;
    return save(gs, { type: 'status', handId: gs.handId, session: gs.session, playerId: this.id, status: 'folded' });
  }

}

exports = module.exports = function factory(obj, i){

  //
  // create a new player object
  let player = Object.create(actions);

  player.id = i;
  player.name = obj.name;
  player.version = 'Poker folder star!';

  // url of the host where the bot is running
  player[herokuService] = obj.url;

  player.chips = config.BUYIN;
  player.status = status.active;

  // the two private cards of the player
  player.cards = [];

  // the total amount of chips the player bet
  // in the current "hand".
  // it is the sum of the chips the player bet
  // in each "betting session" of the current hand.
  // "betting session": [preflop, flop, turn, river]
  player.chipsBet = 0;

  return player;

}
<commit_msg>follow one redirects<commit_after>
'use strict';

const config = require('../config');

const winston = require('../log-setup');
const gamestory = winston.loggers.get('gamestory');

const request = require('request');
const sortByRank = require('poker-rank');
const getCombinations = require('poker-combinations');

const status = require('../domain/player-status');

const save = require('../storage').save;
const safeSum = require('../lib/safe-math').safeSum;
const safeDiff = require('../lib/safe-math').safeDiff;

const herokuService = Symbol('heroku-service');

const hasDB = Symbol.for('hasDB');
const gameProgressive = Symbol.for('game-progressive');
const roundProgressive = Symbol.for('hand-progressive');

const actions = {

  //
  // prepare the gamestate model with the only information
  // each player should know
  // then send an http request to the bot service
  // to get the bet amount
  talk: function talk(gs){

    //
    // the state of the game, filtered with the only information
    // available to the current player
    const ps = Object.create(null);

    // the unique id of the current tournament
    ps.tournamentId = gs.tournamentId;

    // game number of the current tournament
    ps.game = gs[gameProgressive];

    // round number of the current tournament
    ps.round = gs[roundProgressive];

    // count the number of time
    // that players had already have the possibility to bet in the current session.
    ps.spinCount = gs.spinCount;

    // value of the small blinds
    // ... big blind is always twice
    ps.sb = gs.sb;

    // value of the pot
    // it's updated after each bet
    ps.pot = gs.pot;

    // list of the community cards on the table
    // everyone is able to access this same list
    ps.commonCards = gs.commonCards;

    // the list of the players...
    // make sure that the current players can see only its cards
    ps.players = gs.players.map((player, id) => {
      // clean player data
      let mysterious = Object.assign({}, player);
      Object.getOwnPropertySymbols(mysterious).forEach(symb => delete mysterious[symb]);
      if (this.id === id){
        return mysterious;
      }
      delete mysterious.cards;
      delete mysterious.bestCards;
      return mysterious;
    });

    // index of the player with the dealer button
    ps.db = Math.max(gs.players.findIndex(player => player[hasDB]), 0);

    // index of the player 'this' in the players array
    ps.me = this.id;

    // amount of chips the current player must bet
    // in order to remain in the game;
    // it depends by how much he bet previously;
    // so it can change for each player
    ps.callAmount = Math.max(safeDiff(gs.callAmount, this.chipsBet), 0);

    const tag = { id: gs.handId, type: 'bet' };

    gamestory.info('We\'re asking to %s (%d) the amount of his bet on the basis of %s', this.name, this.id, JSON.stringify(ps), tag);

    // send a network request to the Heroku's service
    // to get the bot's bet amount
    const service = `${this[herokuService]}bet`
    return new Promise((resolve, reject) => {
      request.post(service, { body: ps, json: true, followAllRedirects: true, maxRedirects: 1, timeout: 5000 }, (err, response, playerBetAmount) => {
        if (err){
          return void reject(err);
        }
        gamestory.info('%s (%d) bets %d', this.name, this.id, playerBetAmount, tag);
        resolve(playerBetAmount);
      });
    });

  },

  //
  // updates the player state, and the game state
  // accordingly to the player bet
  bet: function bet(gs, amount){

    //
    // sanitization
    if (typeof amount != 'number'){
      // yep, i really mean parseFloat.
      // a player usually does not own, or bet a decimal amount;
      // however after a plot split it's possible that a player has
      // a not-integer amount of chips.
      amount = Number.parseFloat(amount);
    }

    if (Number.isNaN(amount)){
      amount = 0;
    }

    //
    // amount should be in the range [ 0 ... player.chips ]
    if (amount < 0){
      amount = 0;
    }
    else if (amount > this.chips){
      amount = this.chips;
    }

    if (this.isAllin(amount)){
      gamestory.info('%s (%d) is allin', this.name, this.id, { id: gs.handId, type: 'status' });
      this[Symbol.for('allin')] = true;
    }
    else {

      // the total amount of chips which the player
      // has bet in the current hand
      const chipsBet = safeSum(this.chipsBet, amount);

      if (chipsBet < gs.callAmount){
        // player is betting less than the required amount;
        // since he is not betting all the chips he owns
        // we treat this as a "fold" declaration
        gamestory.info('%s (%d) folded', this.name, this.id, { id: gs.handId, type: 'status' });
        return this.fold(gs);
      }
      else if (chipsBet > gs.callAmount) {
        // player is betting a raise;
        // since the player is not going allin,
        // we accept only raise of an amount that is multiple of the small blind.
        const raiseAmount = safeDiff(chipsBet, gs.callAmount);
        if (raiseAmount % gs.sb != 0){
          // in case the raise amount is not a multiple of the small blind
          // the raise, is treated as a simple call.
          amount = safeDiff(amount, raiseAmount);
        }
      }

    }

    //
    // update chip values
    this.chipsBet = safeSum(amount, this.chipsBet);
    this.chips = safeDiff(this.chips, amount);

    gs.pot = safeSum(gs.pot, amount);
    gs.callAmount = Math.max(this.chipsBet, gs.callAmount);

    gamestory.info('Game state after %s (%d)\'s bet: %s', this.name, this.id, JSON.stringify({ pot:gs.pot, callAmount: gs.callAmount, player: { name: this.name, chips: this.chips, chipsBet: this.chipsBet } }), { id: gs.handId, type: 'bet' });

    return save(gs, { type: 'bet', handId: gs.handId, session: gs.session, playerId: this.id, amount: amount });

  },

  //
  // compute the player best combination
  showdown: function showdown(commonCards){
    let combs = getCombinations(this.cards.concat(commonCards), 5);
    let bestHand = sortByRank(combs)[0];
    this.bestCards = combs[bestHand.index];
    gamestory.info('%s (%d)\'s best combination is: %s', this.name, this.id, JSON.stringify(this.bestCards));
    return this.bestCards;
  },

  //
  // return true if the specified amount
  // is equal to the player total number of chips
  isAllin: function isAllin(amount){
    return amount === this.chips;
  },

  //
  // change the player status to folded
  fold: function fold(gs){
    this.status = status.folded;
    return save(gs, { type: 'status', handId: gs.handId, session: gs.session, playerId: this.id, status: 'folded' });
  }

}

exports = module.exports = function factory(obj, i){

  //
  // create a new player object
  let player = Object.create(actions);

  player.id = i;
  player.name = obj.name;
  player.version = 'Poker folder star!';

  // url of the host where the bot is running
  player[herokuService] = obj.url;

  player.chips = config.BUYIN;
  player.status = status.active;

  // the two private cards of the player
  player.cards = [];

  // the total amount of chips the player bet
  // in the current "hand".
  // it is the sum of the chips the player bet
  // in each "betting session" of the current hand.
  // "betting session": [preflop, flop, turn, river]
  player.chipsBet = 0;

  return player;

}
<|endoftext|>


<commit_before>/* eslint max-nested-callbacks: 0*/
/* eslint no-undef: 0*/
import { expect } from 'chai';
import ob from './src/objob';

describe('Objob', () => {
  let ob1;
  let ob2;
  let obArr1;

  before((done) => {
    ob1 = {
      name: 'Bob',
      age: 22,
      weight: 170,
    };

    ob2 = {
      name: 'Bob',
      feet: 5,
      weight: 170,
    };

    obArr1 = [ob1, ob2];

    done();
  });

  describe('keys', () => {
    it('should return all keys for an object', (done) => {
      expect(ob(ob1).keys()).to.include.members(['name','age', 'weight']);
      done();
    });
    it('should return all keys for an array', (done) => {
      expect(ob(obArr1).keys()).to.include.members(['name','age', 'weight','feet']);
      done();
    });
  });

  describe('values', () => {
    it('should return all values for an object', (done) => {
      expect(ob(ob1).values()).to.include.members([ob1.name, ob1.age, ob1.weight]);
      done();
    });
    it('should return all values for an array', (done) => {
      expect(ob(obArr1).values()).to.include.members([ob1.name, ob1.age, ob1.weight, ob2.feet]);
      done();
    });
  });

  describe('many', () => {
    it('should return the correct number of objects', (done) => {
      expect(ob(ob1).many()).to.deep.equal([ob1,ob1]);
      expect(ob(ob1).many(5)).to.deep.equal([ob1,ob1,ob1,ob1,ob1]);
      done();
    });

    it('should return the array if an array', (done) => {
      expect(ob(obArr1).many()).to.equal(obArr1);
      done();
    });
  });

  describe('with', () => {
    it('should return the object only with the given keys', (done) => {
      expect(ob(ob1).with(['name'])).to.deep.equal({name: ob1.name});
      expect(ob(ob2).with(['name', 'age'])).to.deep.equal({name: ob2.name, age: ob2.age});
      done();
    });

    it('should return an array of objects only with the given keys', (done) => {
      expect(ob(obArr1).with(['name'])).to.deep.equal([{name: ob1.name},{name: ob2.name}]);
      done();
    });
  });

  describe('without', () => {
    it('should return the object only without the given keys', (done) => {
      expect(ob(ob1).without(['name'])).to.deep.equal({age: ob1.age, weight: ob1.weight});
      expect(ob(ob2).without(['name', 'age'])).to.deep.equal({weight: ob2.weight, feet: ob2.feet});
      done();
    });

    it('should return an array of objects only without the given keys', (done) => {
      expect(ob(obArr1).without(['name', 'weight', 'feet']))
      .to.deep.equal([{age: ob1.age},{age: ob2.age}]);

      done();
    });
  });
});
<commit_msg>Confirm that chaining works<commit_after>/* eslint max-nested-callbacks: 0*/
/* eslint no-undef: 0*/
import { expect } from 'chai';
import ob from './src/objob';

describe('Objob', () => {
  let ob1;
  let ob2;
  let obArr1;

  before((done) => {
    ob1 = {
      name: 'Bob',
      age: 22,
      weight: 170,
    };

    ob2 = {
      name: 'Bob',
      feet: 5,
      weight: 170,
    };

    obArr1 = [ob1, ob2];

    done();
  });

  describe('keys', () => {
    it('should return all keys for an object', (done) => {
      expect(ob(ob1).keys()).to.include.members(['name','age', 'weight']);
      done();
    });
    it('should return all keys for an array', (done) => {
      expect(ob(obArr1).keys()).to.include.members(['name','age', 'weight','feet']);
      done();
    });
  });

  describe('values', () => {
    it('should return all values for an object', (done) => {
      expect(ob(ob1).values()).to.include.members([ob1.name, ob1.age, ob1.weight]);
      done();
    });
    it('should return all values for an array', (done) => {
      expect(ob(obArr1).values()).to.include.members([ob1.name, ob1.age, ob1.weight, ob2.feet]);
      done();
    });
  });

  describe('many', () => {
    it('should return the correct number of objects', (done) => {
      expect(ob(ob1).many()).to.deep.equal([ob1,ob1]);
      expect(ob(ob1).many(5)).to.deep.equal([ob1,ob1,ob1,ob1,ob1]);
      done();
    });

    it('should return the array if an array', (done) => {
      expect(ob(obArr1).many()).to.equal(obArr1);
      done();
    });
  });

  describe('with', () => {
    it('should return the object only with the given keys', (done) => {
      expect(ob(ob1).with(['name'])).to.deep.equal({name: ob1.name});
      expect(ob(ob2).with(['name', 'age'])).to.deep.equal({name: ob2.name, age: ob2.age});
      done();
    });

    it('should return an array of objects only with the given keys', (done) => {
      expect(ob(obArr1).with(['name'])).to.deep.equal([{name: ob1.name},{name: ob2.name}]);
      done();
    });
  });

  describe('without', () => {
    it('should return the object only without the given keys', (done) => {
      expect(ob(ob1).without(['name'])).to.deep.equal({age: ob1.age, weight: ob1.weight});
      expect(ob(ob2).without(['name', 'age'])).to.deep.equal({weight: ob2.weight, feet: ob2.feet});
      done();
    });

    it('should return an array of objects only without the given keys', (done) => {
      expect(ob(obArr1).without(['name', 'weight', 'feet']))
      .to.deep.equal([{age: ob1.age},{age: ob2.age}]);

      done();
    });
  });

  describe('Chaining', () => {
    it('should return many of an object after filtering with with', (done) => {
      expect(ob(ob(ob1).with(['name', 'age'])).many(3))
      .to.deep.equal([
        {age: ob1.age, name: ob1.name},
        {age: ob1.age, name: ob1.name},
        {age: ob1.age, name: ob1.name},
      ]);
      done();
    });
  });
});
<|endoftext|>


<commit_before>/*eslint strict: [2, "global"], no-var: 0*/

'use strict';

var fs = require('fs');
var path = require('path');
var gulp = require('gulp');
var gutil = require('gulp-util');
var eslint = require('gulp-eslint');
var through = require('through2');
var sourcemaps = require('gulp-sourcemaps');
var babel = require('gulp-babel');

var MAIN_FILES = './*.js';
var SRC_FILES = './src/**/*.js';
var DIST_DIR = './dist/';

// options for transpiling es6 to es5
var babelOptions = {
  // generators are available in Brackets' shell and also break sourcemaps
  blacklist: ['regenerator', 'strict']
};

// provides pipe to log stuff to console when certain task finishes
function logPipe(str) {
  return through.obj(function (file, enc, cb) {
    cb();
  }, function (cb) {
    gutil.log(str);
    cb();
  });
}

// prevents watch from crashing on errors
function swallowError(error) {
  gutil.log(gutil.colors.red(error.toString()));
  this.emit('end');
}

// helper for transpiling es6 files to es5
function doBabel(globs, singleFile) {
  if (singleFile) {
    gutil.log(gutil.colors.cyan('Start Babel ' + globs[0]));
  }

  var task = gulp.src(globs, {base: path.resolve(__dirname, 'src')})
    .pipe(sourcemaps.init())
    .pipe(babel(babelOptions))
    .on('error', swallowError)
    .pipe(sourcemaps.write('.'))
    .pipe(gulp.dest(DIST_DIR));

  return singleFile ?
    task.pipe(logPipe(gutil.colors.cyan('Finish Babel ' + globs[0]))) :
    task;
}

// helper for linting files
function doEslint(globs, singleFile) {
  if (singleFile) {
    gutil.log(gutil.colors.magenta('Start ESLint ' + globs[0]));
  }

  var task = gulp.src(globs)
    .pipe(eslint())
    .pipe(eslint.format());

  return singleFile ?
    task.pipe(logPipe(gutil.colors.magenta('Finish ESLint ' + globs[0]))) :
    task.pipe(eslint.failAfterError());
}

gulp.task('babel', function () {
  return doBabel([SRC_FILES], false);
});

gulp.task('eslint', function () {
  return doEslint([MAIN_FILES, SRC_FILES], false);
});

gulp.task('watch', function () {
  gulp.watch([SRC_FILES]).on('change', function (event) {
    var filePath = path.relative(__dirname, event.path);
    if (fs.statSync(filePath).isFile()) {
      doEslint([filePath], true);
      doBabel([filePath], true);
    }
  });
});

gulp.task('build', ['babel']);
gulp.task('test', ['eslint']);
gulp.task('default', ['build', 'test']);
<commit_msg>fix gulp sourcemaps<commit_after>/*eslint strict: [2, "global"], no-var: 0*/

'use strict';

var fs = require('fs');
var path = require('path');
var gulp = require('gulp');
var gutil = require('gulp-util');
var eslint = require('gulp-eslint');
var through = require('through2');
var sourcemaps = require('gulp-sourcemaps');
var babel = require('gulp-babel');

var MAIN_FILES = './*.js';
var SRC_FILES = './src/**/*.js';
var DIST_DIR = './dist/';

// options for transpiling es6 to es5
var babelOptions = {
  // generators are available in Brackets' shell and also break sourcemaps
  blacklist: ['regenerator', 'strict']
};

// provides pipe to log stuff to console when certain task finishes
function logPipe(str) {
  return through.obj(function (file, enc, cb) {
    cb();
  }, function (cb) {
    gutil.log(str);
    cb();
  });
}

// prevents watch from crashing on errors
function swallowError(error) {
  gutil.log(gutil.colors.red(error.toString()));
  this.emit('end');
}

// helper for transpiling es6 files to es5
function doBabel(globs, singleFile) {
  if (singleFile) {
    gutil.log(gutil.colors.cyan('Start Babel ' + globs[0]));
  }

  var task = gulp.src(globs, {base: path.resolve(__dirname, 'src')})
    .pipe(sourcemaps.init())
    .pipe(babel(babelOptions))
    .on('error', swallowError)
    .pipe(sourcemaps.write('.', {
      sourceMappingURLPrefix: path.resolve(__dirname, 'dist') + '/'
    }))
    .pipe(gulp.dest(DIST_DIR));

  return singleFile ?
    task.pipe(logPipe(gutil.colors.cyan('Finish Babel ' + globs[0]))) :
    task;
}

// helper for linting files
function doEslint(globs, singleFile) {
  if (singleFile) {
    gutil.log(gutil.colors.magenta('Start ESLint ' + globs[0]));
  }

  var task = gulp.src(globs)
    .pipe(eslint())
    .pipe(eslint.format());

  return singleFile ?
    task.pipe(logPipe(gutil.colors.magenta('Finish ESLint ' + globs[0]))) :
    task.pipe(eslint.failAfterError());
}

gulp.task('babel', function () {
  return doBabel([SRC_FILES], false);
});

gulp.task('eslint', function () {
  return doEslint([MAIN_FILES, SRC_FILES], false);
});

gulp.task('watch', function () {
  gulp.watch([SRC_FILES]).on('change', function (event) {
    var filePath = path.relative(__dirname, event.path);
    if (fs.statSync(filePath).isFile()) {
      doEslint([filePath], true);
      doBabel([filePath], true);
    }
  });
});

gulp.task('build', ['babel']);
gulp.task('test', ['eslint']);
gulp.task('default', ['build', 'test']);
<|endoftext|>


<commit_before>/**
 * modules/search-console data store: report.
 *
 * Site Kit by Google, Copyright 2020 Google LLC
 *
 * 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
 *
 *     https://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.
 */

/**
 * External dependencies
 */
import invariant from 'invariant';
import isPlainObject from 'lodash/isPlainObject';

/**
 * Internal dependencies
 */
import API from 'googlesitekit-api';
import Data from 'googlesitekit-data';
import { STORE_NAME } from './constants';
import { stringifyObject } from '../../../util';
import { createFetchStore } from '../../../googlesitekit/data/create-fetch-store';
import { isValidDateRange } from '../../../util/report-validation';

const fetchGetReportStore = createFetchStore( {
	baseName: 'getReport',
	controlCallback: ( { options } ) => {
		return API.get( 'modules', 'search-console', 'searchanalytics', options );
	},
	reducerCallback: ( state, report, { options } ) => {
		return {
			...state,
			reports: {
				...state.reports,
				[ stringifyObject( options ) ]: report,
			},
		};
	},
	argsToParams: ( options ) => {
		const {
			startDate,
			endDate,
			dateRange,
		} = options;

		invariant( isPlainObject( options ), 'Options for Analytics report must be an object.' );
		invariant( isValidDateRange( dateRange, startDate, endDate ), 'Either date range or start/end dates must be provided for Analytics report.' );

		return { options };
	},
} );

const BASE_INITIAL_STATE = {
	reports: {},
};

const baseResolvers = {
	*getReport( options = {} ) {
		const registry = yield Data.commonActions.getRegistry();
		const existingReport = registry.select( STORE_NAME ).getReport( options );

		// If there are already alerts loaded in state, consider it fulfilled
		// and don't make an API request.
		if ( existingReport ) {
			return;
		}

		yield fetchGetReportStore.actions.fetchGetReport( options );
	},
};

const baseSelectors = {
	/**
	 * Gets a Search Console report for the given options.
	 *
	 * @since n.e.x.t
	 *
	 * @param {Object}         state                     Data store's state.
	 * @param {Object}         options                   Optional. Options for generating the report.
	 * @param {string}         options.startDate         Required, unless dateRange is provided. Start date to query report data for as YYYY-mm-dd.
	 * @param {string}         options.endDate           Required, unless dateRange is provided. End date to query report data for as YYYY-mm-dd.
	 * @param {string}         options.dateRange         Required, alternatively to startDate and endDate. A date range string such as 'last-28-days'.
	 * @param {boolean}        options.compareDateRanges Optional. Only relevant with dateRange. Default false.
	 * @param {Array.<string>} options.dimensions        Optional. List of dimensions to group results by. Default an empty array.
	 * @param {string}         options.url               Optional. URL to get a report for only this URL. Default an empty string.
	 * @param {number}         options.limit             Optional. Maximum number of entries to return. Default 1000.
	 * @return {(Array.<Object>|undefined)} A Search Console report; `undefined` if not loaded.
	 */
	getReport( state, options = {} ) {
		const { reports } = state;

		return reports[ stringifyObject( options ) ];
	},
};

const store = Data.combineStores(
	fetchGetReportStore,
	{
		INITIAL_STATE: BASE_INITIAL_STATE,
		resolvers: baseResolvers,
		selectors: baseSelectors,
	}
);

export const INITIAL_STATE = store.INITIAL_STATE;
export const actions = store.actions;
export const controls = store.controls;
export const reducer = store.reducer;
export const resolvers = store.resolvers;
export const selectors = store.selectors;

export default store;
<commit_msg>Update SC report datastore to use new signature of the isValidDateRange function.<commit_after>/**
 * modules/search-console data store: report.
 *
 * Site Kit by Google, Copyright 2020 Google LLC
 *
 * 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
 *
 *     https://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.
 */

/**
 * External dependencies
 */
import invariant from 'invariant';
import isPlainObject from 'lodash/isPlainObject';

/**
 * Internal dependencies
 */
import API from 'googlesitekit-api';
import Data from 'googlesitekit-data';
import { STORE_NAME } from './constants';
import { stringifyObject } from '../../../util';
import { createFetchStore } from '../../../googlesitekit/data/create-fetch-store';
import { isValidDateRange } from '../../../util/report-validation';

const fetchGetReportStore = createFetchStore( {
	baseName: 'getReport',
	controlCallback: ( { options } ) => {
		return API.get( 'modules', 'search-console', 'searchanalytics', options );
	},
	reducerCallback: ( state, report, { options } ) => {
		return {
			...state,
			reports: {
				...state.reports,
				[ stringifyObject( options ) ]: report,
			},
		};
	},
	argsToParams: ( options ) => {
		invariant( isPlainObject( options ), 'Options for Analytics report must be an object.' );
		invariant( isValidDateRange( options ), 'Either date range or start/end dates must be provided for Analytics report.' );

		return { options };
	},
} );

const BASE_INITIAL_STATE = {
	reports: {},
};

const baseResolvers = {
	*getReport( options = {} ) {
		const registry = yield Data.commonActions.getRegistry();
		const existingReport = registry.select( STORE_NAME ).getReport( options );

		// If there are already alerts loaded in state, consider it fulfilled
		// and don't make an API request.
		if ( existingReport ) {
			return;
		}

		yield fetchGetReportStore.actions.fetchGetReport( options );
	},
};

const baseSelectors = {
	/**
	 * Gets a Search Console report for the given options.
	 *
	 * @since n.e.x.t
	 *
	 * @param {Object}         state                     Data store's state.
	 * @param {Object}         options                   Optional. Options for generating the report.
	 * @param {string}         options.startDate         Required, unless dateRange is provided. Start date to query report data for as YYYY-mm-dd.
	 * @param {string}         options.endDate           Required, unless dateRange is provided. End date to query report data for as YYYY-mm-dd.
	 * @param {string}         options.dateRange         Required, alternatively to startDate and endDate. A date range string such as 'last-28-days'.
	 * @param {boolean}        options.compareDateRanges Optional. Only relevant with dateRange. Default false.
	 * @param {Array.<string>} options.dimensions        Optional. List of dimensions to group results by. Default an empty array.
	 * @param {string}         options.url               Optional. URL to get a report for only this URL. Default an empty string.
	 * @param {number}         options.limit             Optional. Maximum number of entries to return. Default 1000.
	 * @return {(Array.<Object>|undefined)} A Search Console report; `undefined` if not loaded.
	 */
	getReport( state, options = {} ) {
		const { reports } = state;

		return reports[ stringifyObject( options ) ];
	},
};

const store = Data.combineStores(
	fetchGetReportStore,
	{
		INITIAL_STATE: BASE_INITIAL_STATE,
		resolvers: baseResolvers,
		selectors: baseSelectors,
	}
);

export const INITIAL_STATE = store.INITIAL_STATE;
export const actions = store.actions;
export const controls = store.controls;
export const reducer = store.reducer;
export const resolvers = store.resolvers;
export const selectors = store.selectors;

export default store;
<|endoftext|>


<commit_before>/*
 Copyright (C) 2016 Rolando Santamaria Maso (@kyberneees)

 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 specifi8c language governing permissions and
 limitations under the License.0
 */
/*jshint esversion: 6*/

const expect = require("chai").expect;
const DistributedEventEmitter = require("./main.js");

// connection opened flag
var connected = false,
    disconnected = false;

// instantiating event emitters
const outgoing = new DistributedEventEmitter();
const incoming = new DistributedEventEmitter();

incoming.on('connected', () => {
    connected = true;
});
incoming.on('disconnected', () => {
    disconnected = true;
});
incoming.on('error', (error) => {
    console.log(error);
});

outgoing.connect();

// message to be sent/published
var messageout = {
    data: 'hello!'
};
// received message
var messagein;

describe('Basic', () => {
    describe('connect', () => {
        it('connecting', (done) => {
            incoming.connect();
            setTimeout(() => {
                expect(connected).to.equal(true);
                done();
            }, 100);
        });
    });

    describe('getId', () => {
        it('getId', () => {
            expect(typeof incoming.getId()).to.equal('string');
        });
    });

    describe('subscribing', () => {
        it('event: news.*', () => {
            var callback = (message) => {
                // should be called only if message is published
                // then, intentionally we set null in 'data' property
                messagein = {
                    data: null
                };
            };

            incoming.on('news.*', callback);
            incoming.on('news.*', (message) => {
                messagein = message;
            });
            incoming.removeListener('news.*', callback);

            incoming.on('news.*', callback);
        });
    });

    describe('emitToOne (distributed)', () => {
        it("emit to 'news.public' > {data: 'hello!'}", (done) => {
            outgoing.emitToOne('news.public', messageout);

            setTimeout(() => {
                expect(messageout.data).to.equal(messagein.data);
                done();
            }, 100);
        });
    });

    describe('emit', () => {
        it("emit to 'news.private' > {data: 'hello!'}", (done) => {
            outgoing.emit('news.private', messageout);

            setTimeout(() => {
                expect(null).to.equal(messagein.data);
                done();
            }, 100);
        });
    });

    describe('disconnect', () => {
        it("disconnecting incoming instance", (done) => {
            incoming.disconnect();

            setTimeout(() => {
                expect(disconnected).to.equal(true);
                done();
            }, 100);
        });
    });
});

describe('Promise', () => {
    describe('connect', () => {
        it('connecting incoming instance', (done) => {
            incoming.connect().then(() => {
                done();
            });
        });
    });

    describe('subscribing', () => {
        it('event: my.action', (done) => {
            incoming.on('my.action', (message, resolve, reject) => {
                if (typeof (message) === 'string')
                    resolve('hello');
                else {
                    reject('invalid type');
                }
            });
            incoming.on('my.action2', (message, resolve, reject) => {
                resolve();
            });
            done();
        });
    });

    describe('emitToOne (distributed)', () => {
        it("emitting to 'my.action' > 'Hi there...'", (done) => {
            outgoing.emitToOne('my.action', 'Hi there...').then((response) => {
                expect('hello').to.equal(response);
                done();
            });
        });
    });

    describe('emitToOne (local)', () => {
        it("emitting to 'my.action.local' > 'Hi there...'", (done) => {
            outgoing.on('my.action.local', (message, resolve) => {
                if (typeof (message) === 'string')
                    resolve('hello');
                else
                    reject('invalid type');
            });

            outgoing.emitToOne('my.action.local', 'Hi there...', 100).then((response) => {
                expect('hello').to.equal(response);
                done();
            });
        });
    });

    describe('emitToOne (distributed) expecting rejection', () => {
        it("emitting to 'my.action' > 'Hi there...'", (done) => {
            outgoing.emitToOne('my.action', 5, 200).catch((error) => {
                expect('invalid type').to.equal(error);
                done();
            });
        });
    });

    describe('emitToOne (distributed) expecting null response', () => {
        it("emitting to 'my.action2' > 'Hi there...'", (done) => {
            outgoing.emitToOne('my.action2', 'Hi there...', 200).then((response) => {
                done();
            });
        });
    });

    describe('emitToOne (local) expecting null response', () => {
        it("emitting to 'my.action2.local' > 'Hi there...'", (done) => {
            outgoing.on('my.action2.local', (message, resolve) => {
                resolve();
            });
            outgoing.emitToOne('my.action2.local', 'Hi there...').then((response) => {
                done();
            });
        });
    });

    describe('emitToOne expecting timeout', () => {
        it("emitting to 'my.timeout' > 'Hi there...'", (done) => {
            outgoing.emitToOne('my.timeout', 'Hi there...', 25).catch((error) => {
                expect('timeout').to.equal(error);
                done();
            });
        });
    });

    describe('once', () => {
        it("testing 'once' listeners registration", (done) => {
            incoming.once('onetimelistener', (data, resolve) => {
                resolve();
            });
            outgoing.emitToOne('onetimelistener', 'hello').then((error) => {
                return outgoing.emitToOne('onetimelistener', 'hello again', 100);
            }).catch((error) => {
                expect('timeout').to.equal(error);
                done();
            });
        });
    });

    describe('emit', () => {
        it("emit to 'news.private' > {data: 'hello!'}", (done) => {
            outgoing.emit('news.private', messageout).then((response) => {
                done();
            });
        });
    });

    describe('disconnect', () => {
        it('disconnecting', (done) => {
            incoming.disconnect().then(() => {
                return outgoing.disconnect();
            }).then(() => {
                done();
            });
        });
    });
});
<commit_msg>Adding test cases for new request/response events.<commit_after>/*
 Copyright (C) 2016 Rolando Santamaria Maso (@kyberneees)

 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 specifi8c language governing permissions and
 limitations under the License.0
 */
/*jshint esversion: 6*/

const expect = require("chai").expect;
const DistributedEventEmitter = require("./main.js");

// connection opened flag
var connected = false,
    disconnected = false;

// instantiating event emitters
const outgoing = new DistributedEventEmitter();
const incoming = new DistributedEventEmitter();

incoming.on('connected', () => {
    connected = true;
});
incoming.on('disconnected', () => {
    disconnected = true;
});
incoming.on('error', (error) => {
    console.log(error);
});

outgoing.connect();

// message to be sent/published
var messageout = {
    data: 'hello!'
};
// received message
var messagein;

describe('Basic', () => {
    describe('connect', () => {
        it('connecting', (done) => {
            incoming.connect();
            setTimeout(() => {
                expect(connected).to.equal(true);
                done();
            }, 100);
        });
    });

    describe('getId', () => {
        it('getId', () => {
            expect(typeof incoming.getId()).to.equal('string');
        });
    });

    describe('subscribing', () => {
        it('event: news.*', () => {
            var callback = (data) => {
                // should be called only if message is published
                // then, intentionally we set null in 'data' property
                messagein = {
                    data: null
                };
            };

            incoming.on('news.*', callback);
            incoming.on('news.*', (data) => {
                messagein = data;
            });
            incoming.removeListener('news.*', callback);

            incoming.on('news.*', callback);
        });
    });

    describe('emitToOne (distributed)', () => {
        it("emit to 'news.public' > {data: 'hello!'}", (done) => {
            outgoing.emitToOne('news.public', messageout);

            setTimeout(() => {
                expect(messageout.data).to.equal(messagein.data);
                done();
            }, 100);
        });
    });

    describe('emit', () => {
        it("emit to 'news.private' > {data: 'hello!'}", (done) => {
            outgoing.emit('news.private', messageout);

            setTimeout(() => {
                expect(null).to.equal(messagein.data);
                done();
            }, 100);
        });
    });

    describe('disconnect', () => {
        it("disconnecting incoming instance", (done) => {
            incoming.disconnect();

            setTimeout(() => {
                expect(disconnected).to.equal(true);
                done();
            }, 100);
        });
    });
});

describe('Promise', () => {
    describe('connect', () => {
        it('connecting incoming instance', (done) => {
            incoming.connect().then(() => {
                done();
            });
        });
    });

    describe('subscribing', () => {
        it('event: my.action', (done) => {
            incoming.on('request', (event, request) => {
                if ('my.action' === event) {
                    if ('string' === typeof request.data) {
                        request.data = request.data.toUpperCase();
                    }
                }
            });
            incoming.on('response', (event, response) => {
                if ('my.action' === event && response.ok) {
                    response.data = response.data.toUpperCase();
                }
            });
            incoming.on('my.action', (data, resolve, reject) => {
                if (typeof (data) === 'string' && 'HI THERE...' === data) {
                    resolve('hello');
                } else {
                    reject('invalid type');
                }
            });
            incoming.on('my.action2', (data, resolve, reject) => {
                resolve();
            });
            done();
        });
    });

    describe('emitToOne (distributed)', () => {
        it("emitting to 'my.action' > 'Hi there...'", (done) => {
            outgoing.emitToOne('my.action', 'Hi there...').then((response) => {
                expect('HELLO').to.equal(response);
                done();
            });
        });
    });

    describe('emitToOne (local)', () => {
        it("emitting to 'my.action.local' > 'Hi there...'", (done) => {
            outgoing.on('my.action.local', (data, resolve) => {
                if (typeof (data) === 'string')
                    resolve('hello');
                else
                    reject('invalid type');
            });

            outgoing.emitToOne('my.action.local', 'Hi there...', 100).then((response) => {
                expect('hello').to.equal(response);
                done();
            });
        });
    });

    describe('emitToOne (distributed) expecting rejection', () => {
        it("emitting to 'my.action' > 'Hi there...'", (done) => {
            outgoing.emitToOne('my.action', 5, 200).catch((error) => {
                expect('invalid type').to.equal(error);
                done();
            });
        });
    });

    describe('emitToOne (distributed) expecting null response', () => {
        it("emitting to 'my.action2' > 'Hi there...'", (done) => {
            outgoing.emitToOne('my.action2', 'Hi there...', 200).then((response) => {
                done();
            });
        });
    });

    describe('emitToOne (local) expecting null response', () => {
        it("emitting to 'my.action2.local' > 'Hi there...'", (done) => {
            outgoing.on('my.action2.local', (message, resolve) => {
                resolve();
            });
            outgoing.emitToOne('my.action2.local', 'Hi there...').then((response) => {
                done();
            });
        });
    });

    describe('emitToOne expecting timeout', () => {
        it("emitting to 'my.timeout' > 'Hi there...'", (done) => {
            outgoing.emitToOne('my.timeout', 'Hi there...', 25).catch((error) => {
                expect('timeout').to.equal(error);
                done();
            });
        });
    });

    describe('once', () => {
        it("testing 'once' listeners registration", (done) => {
            incoming.once('onetimelistener', (data, resolve) => {
                resolve();
            });
            outgoing.emitToOne('onetimelistener', 'hello').then((error) => {
                return outgoing.emitToOne('onetimelistener', 'hello again', 100);
            }).catch((error) => {
                expect('timeout').to.equal(error);
                done();
            });
        });
    });

    describe('emit', () => {
        it("emit to 'news.private' > {data: 'hello!'}", (done) => {
            outgoing.emit('news.private', messageout).then((response) => {
                done();
            });
        });
    });

    describe('disconnect', () => {
        it('disconnecting', (done) => {
            incoming.disconnect().then(() => {
                return outgoing.disconnect();
            }).then(() => {
                done();
            });
        });
    });
});
<|endoftext|>