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

package com.google.code.log4mongo;

import static org.junit.Assert.assertTrue;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import org.junit.Test;

import com.mongodb.Mongo;
import com.mongodb.ServerAddress;
import com.wombatnation.privateer.Privateer;

/**
 * JUnit unit tests for MongoDbAppender to verify proper behavior when setting
 * the hostname and property properties.
 * 
 * <b>Note:</b> these tests require that a MongoDB server is running, and (by default)
 * assumes that server is listening on the default port (27017) on localhost.
 * <p>
 * Unless a replica set is configured with mongod instances listening on ports 27017
 * and 27018, errors will be logged. However, the tests will complete successfully.
 * 
 * To test on localhost with a replica set, do the following (either starting mongod
 * instances in separate terminals or start them with --fork):
 * <ul>
 * <li>$ mkdir -p /data/r0
 * <li>$ mkdir -p /data/r1
 * <li>$ mkdir -p /data/r2
 * <li>$ mongod --replSet foo --smallfiles --port 27017 --dbpath /data/r0
 * <li>$ mongod --replSet foo --smallfiles --port 27018 --dbpath /data/r1
 * <li>$ mongod --replSet foo --smallfiles --port 27019 --dbpath /data/r2
 * <li>$ mongo
 * <li>> config = {"_id": "foo", members:[{_id: 0, host: 'localhost:27017'},{_id: 1, host: 'localhost:27018'},{_id: 2, host: 'localhost:27019', arbiterOnly: true}]}
 * <li>> rs.initiate(config)
 * <li>Then wait about a minute until replica set is established. You can run rs.status() and look for direct confirmation.
 * </ul>
 * Since the unit tests create and drop databases several times, they run about twice as fast
 * if mongod is started with the --smallfiles argument.
 *
 * @author Robert Stewart (robert@wombatnation.com)
 * @version $Id$
 */
public class TestMongoDbAppenderHosts
{    
    private final static String TEST_MONGO_SERVER_HOSTNAME = "localhost";
    private final static int    TEST_MONGO_SERVER_PORT     = 27017;
    private final static String TEST_DATABASE_NAME         = "log4mongotest";
    
    private final static String MONGODB_APPENDER_NAME = "MongoDB";

    private final Privateer p = new Privateer();

    @Test
    public void testOneHost() throws Exception
    {
        String hostname = TEST_MONGO_SERVER_HOSTNAME;
        PropertyConfigurator.configure(getDefaultPortProperties(hostname));
        
        MongoDbAppender appender = (MongoDbAppender) Logger.getRootLogger().getAppender(MONGODB_APPENDER_NAME);
        Mongo mongo = (Mongo) p.getField(appender, "mongo");
        assertTrue(mongo.getAddress() != null);
        assertTrue(TEST_MONGO_SERVER_HOSTNAME.equals(mongo.getAddress().getHost()));
        assertTrue(TEST_MONGO_SERVER_PORT == mongo.getAddress().getPort());
        
        appender.close();
    }
    
    @Test
    public void testOneHostNonDefaultPort() throws Exception
    {
        String hostname = TEST_MONGO_SERVER_HOSTNAME;
        String port = "27018";
        int portNum = Integer.parseInt(port);
        PropertyConfigurator.configure(getNonDefaultPortProperties(hostname, port));
        
        MongoDbAppender appender = (MongoDbAppender) Logger.getRootLogger().getAppender(MONGODB_APPENDER_NAME);

        Mongo mongo = (Mongo) p.getField(appender, "mongo");
        assertTrue(mongo.getAddress() != null);
        assertTrue(TEST_MONGO_SERVER_HOSTNAME.equals(mongo.getAddress().getHost()));
        assertTrue(portNum == mongo.getAddress().getPort());
        
        appender.close();
    }
    
    /**
     * If this test is run without a mongod running on localhost port 27018, an error will be
     * logged to the console by the appender.
     * 
     * @throws Exception
     */
    @Test
    public void testTwoHostsTwoPorts() throws Exception
    {
        String hostname = "localhost localhost";
        List<String> hosts = new ArrayList<String>();
        hosts = Arrays.asList("localhost", "localhost");
        String port = "27017 27018";
        List<String>ports = Arrays.asList("27017", "27018");
        PropertyConfigurator.configure(getNonDefaultPortProperties(hostname, port));
        
        MongoDbAppender appender = (MongoDbAppender) Logger.getRootLogger().getAppender(MONGODB_APPENDER_NAME);

        Mongo mongo = (Mongo) p.getField(appender, "mongo");
        assertTrue(mongo.getAddress() != null);  // Should return the master
        List<ServerAddress> addresses = mongo.getAllAddress();
        assertTrue(addresses != null);
        for (ServerAddress address : addresses)
        {
            boolean found = false;
            int i = 0;
            for (String host : hosts)
            {
                if (host.equals(address.getHost()))
                {
                    String p = String.valueOf(address.getPort());
                    if (ports.get(i).equals(p))
                    {
                        found = true;
                        break;
                    }
                }
                i++; 
            }
            assertTrue(found);
        }
        
        appender.close();
    }
    
    private Properties getDefaultPortProperties(String hostname)
    {
        Properties props = new Properties();
        props.put("log4j.rootLogger", "DEBUG, MongoDB");
        props.put("log4j.appender.MongoDB", "com.google.code.log4mongo.MongoDbAppender");
        props.put("log4j.appender.MongoDB.databaseName", TEST_DATABASE_NAME);
        props.put("log4j.appender.MongoDB.hostname", hostname);
        return props;
    }
    
    private Properties getNonDefaultPortProperties(String hostname, String port)
    {
        Properties props = new Properties();
        props.put("log4j.rootLogger", "DEBUG, MongoDB");
        props.put("log4j.appender.MongoDB", "com.google.code.log4mongo.MongoDbAppender");
        props.put("log4j.appender.MongoDB.databaseName", TEST_DATABASE_NAME);
        props.put("log4j.appender.MongoDB.hostname", hostname);
        props.put("log4j.appender.MongoDB.port", port);
        return props;
    }
}

@@ -1479,53 +1479,105 @@
 ed. 
-However, the tests will complete successfully
+If a host is not listening on port 27018,%0D%0A * testOneHostNonDefaultPort() will fail with an error
 .%0D%0A 

Clarify unit test failure behavior when no mongod on port 27018