"use strict";
var assert = require('assert');
var vows = require('vows');
var layouts = require('../lib/layouts');
var sandbox = require('sandboxed-module');
var LoggingEvent = require('../lib/logger').LoggingEvent;
var cluster = require('cluster');

vows.describe('log4js cluster appender').addBatch({
	'when in master mode': {
		topic: function() {

			var registeredClusterEvents = [];
			var loggingEvents = [];
			var onChildProcessForked;
			var onMasterReceiveChildMessage;
			
			// Fake cluster module, so no real cluster listeners be really added 
			var fakeCluster = {
			
				on: function(event, callback) {
					registeredClusterEvents.push(event);
					onChildProcessForked = callback;
				},
				
				isMaster: true,
				isWorker: false,
				
			};
			var fakeWorker = {
				on: function(event, callback) {
					onMasterReceiveChildMessage = callback;
				},
				process: {
					pid: 123
				},
				id: 'workerid'
			};
		
			var fakeActualAppender = function(loggingEvent) {
				loggingEvents.push(loggingEvent);
			}
			
			// Load appender and fake modules in it
			var appenderModule = sandbox.require('../lib/appenders/clustered', {
				requires: {
					'cluster': fakeCluster,
				}
			});
		
			var masterAppender = appenderModule.appender({
				actualAppenders: [fakeActualAppender, fakeActualAppender, fakeActualAppender],
				appenders: [{}, {category: "test"}, {category: "wovs"}]
			});

			// Actual test - log message using masterAppender
			masterAppender(new LoggingEvent('wovs', 'Info', ['masterAppender test']));

			// Simulate a 'fork' event to register the master's message handler on our fake worker.
			onChildProcessForked(fakeWorker);
			// Simulate a cluster message received by the masterAppender.
			var simulatedLoggingEvent = new LoggingEvent('wovs', 'Error', ['message deserialization test', {stack: 'my wrapped stack'}]);
			onMasterReceiveChildMessage({
				type : '::log-message',
				event : JSON.stringify(simulatedLoggingEvent)
			});

			var returnValue = {
				registeredClusterEvents: registeredClusterEvents,
				loggingEvents: loggingEvents,
			};
		
			return returnValue;
		}, 
		
		"should register 'fork' event listener on 'cluster'": function(topic) { 
			assert.equal(topic.registeredClusterEvents[0], 'fork');
		},
		
		"should log using actual appender": function(topic) {
			assert.equal(topic.loggingEvents.length, 4);
			assert.equal(topic.loggingEvents[0].data[0], 'masterAppender test');
			assert.equal(topic.loggingEvents[1].data[0], 'masterAppender test');
			assert.equal(topic.loggingEvents[2].data[0], 'message deserialization test');
			assert.equal(topic.loggingEvents[2].data[1], 'my wrapped stack');
			assert.equal(topic.loggingEvents[3].data[0], 'message deserialization test');
			assert.equal(topic.loggingEvents[3].data[1], 'my wrapped stack');
		},
		
	},
	
	'when in worker mode': {
		
		topic: function() {
			
			var registeredProcessEvents = [];
			
			// Fake cluster module, to fake we're inside a worker process
			var fakeCluster = {
			
				isMaster: false,
				isWorker: true,
				
			};
			
			var fakeProcess = {
			
				send: function(data) {
					registeredProcessEvents.push(data);
				},
				
			};
			
			// Load appender and fake modules in it
			var appenderModule = sandbox.require('../lib/appenders/clustered', {
				requires: {
					'cluster': fakeCluster,
				},
				globals: {
					'process': fakeProcess,
				}
			});
			
			var workerAppender = appenderModule.appender();

			// Actual test - log message using masterAppender
			workerAppender(new LoggingEvent('wovs', 'Info', ['workerAppender test']));
			workerAppender(new LoggingEvent('wovs', 'Info', [new Error('Error test')]));
			
			var returnValue = {
				registeredProcessEvents: registeredProcessEvents,
			};
			
			return returnValue;
		
		},
		
		"worker appender should call process.send" : function(topic) {
			assert.equal(topic.registeredProcessEvents[0].type, '::log-message');
			assert.equal(JSON.parse(topic.registeredProcessEvents[0].event).data[0], "workerAppender test");
		},
		
		"worker should serialize an Error correctly" : function(topic) {
			assert.equal(topic.registeredProcessEvents[1].type, '::log-message');
			assert(JSON.parse(topic.registeredProcessEvents[1].event).data[0].stack);
			var actual = JSON.parse(topic.registeredProcessEvents[1].event).data[0].stack;
			var expectedRegex = /^Error: Error test/;
			assert(actual.match(expectedRegex), "Expected: \n\n " + actual + "\n\n to match " + expectedRegex);
		}
		
	}

}).exportTo(module);
