org.powertac.auctioneer.AuctionServiceTests.java Source code

Java tutorial

Introduction

Here is the source code for org.powertac.auctioneer.AuctionServiceTests.java

Source

/*
 * Copyright (c) 2013 by the original author or authors.
 *
 * 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 org.powertac.auctioneer;

import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.MapConfiguration;
import org.joda.time.Instant;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.powertac.common.Broker;
import org.powertac.common.ClearedTrade;
import org.powertac.common.Competition;
import org.powertac.common.Order;
import org.powertac.common.Orderbook;
import org.powertac.common.TimeService;
import org.powertac.common.Timeslot;
import org.powertac.common.config.Configurator;
import org.powertac.common.interfaces.Accounting;
import org.powertac.common.interfaces.BrokerProxy;
import org.powertac.common.interfaces.CompetitionControl;
import org.powertac.common.interfaces.ServerConfiguration;
import org.powertac.common.msg.OrderStatus;
import org.powertac.common.repo.OrderbookRepo;
import org.powertac.common.repo.TimeslotRepo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.ArrayList;
import java.util.List;
import java.util.TreeMap;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyDouble;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;

/**
 * Test cases for AuctionService
 *
 * @author John Collins
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:test-config.xml" })
@DirtiesContext
public class AuctionServiceTests {
    @Autowired
    private AuctionService svc;

    @Autowired
    private TimeService timeService;

    @Autowired
    private Accounting accountingService;

    @Autowired
    private TimeslotRepo timeslotRepo;

    @Autowired
    private OrderbookRepo orderbookRepo;

    // get access to the mock services
    @Autowired
    private BrokerProxy mockProxy;

    @Autowired
    private CompetitionControl mockControl;

    @Autowired
    private ServerConfiguration mockServerProps;

    private Competition competition;
    private Configurator config;
    private Broker b1;
    private Broker b2;
    private Broker s1;
    private Broker s2;
    private Timeslot ts0;
    private Timeslot ts1;
    private Timeslot ts2;
    //private Timeslot ts3;
    //private Timeslot ts4;

    private List<Object[]> accountingArgs;
    private List<Object> brokerMsgs;

    @SuppressWarnings("rawtypes")
    @Before
    public void setUp() throws Exception {
        // clean up from previous tests
        timeslotRepo.recycle();
        reset(mockProxy);
        reset(mockControl);
        reset(mockServerProps);
        accountingArgs = new ArrayList<Object[]>();
        brokerMsgs = new ArrayList<Object>();

        // create a Competition, needed for initialization
        competition = Competition.newInstance("auctioneer-test").withTimeslotsOpen(4);
        Competition.setCurrent(competition);

        // mock the ServerProperties

        // Set up serverProperties mock
        config = new Configurator();
        doAnswer(new Answer() {
            @Override
            public Object answer(InvocationOnMock invocation) {
                Object[] args = invocation.getArguments();
                config.configureSingleton(args[0]);
                return null;
            }
        }).when(mockServerProps).configureMe(anyObject());

        // Create some brokers who can trade
        b1 = new Broker("Buyer #1");
        b2 = new Broker("Buyer #2");
        s1 = new Broker("Seller #1");
        s2 = new Broker("Seller #2");

        // set the clock, create some useful timeslots
        Instant now = Competition.currentCompetition().getSimulationBaseTime();
        timeService.setCurrentTime(now);
        ts0 = timeslotRepo.makeTimeslot(now);
        ts1 = timeslotRepo.makeTimeslot(now.plus(TimeService.HOUR));
        ts2 = timeslotRepo.makeTimeslot(now.plus(TimeService.HOUR * 2));
        //timeslotRepo.makeTimeslot(now.plus(TimeService.HOUR * 3));
        //timeslotRepo.makeTimeslot(now.plus(TimeService.HOUR * 4));
        svc.clearEnabledTimeslots();

        // mock the AccountingService, capture args
        doAnswer(new Answer() {
            @Override
            public Object answer(InvocationOnMock invocation) {
                Object[] args = invocation.getArguments();
                accountingArgs.add(args);
                return null;
            }
        }).when(accountingService).addMarketTransaction(isA(Broker.class), isA(Timeslot.class), anyDouble(),
                anyDouble());
        // mock the Broker Proxy, capture messages
        doAnswer(new Answer() {
            @Override
            public Object answer(InvocationOnMock invocation) {
                Object[] args = invocation.getArguments();
                brokerMsgs.add(args[0]);
                return null;
            }
        }).when(mockProxy).broadcastMessage(anyObject());

        // Configure the AuctionService
        TreeMap<String, String> map = new TreeMap<String, String>();
        map.put("auctioneer.auctionService.sellerSurplusRatio", "0.5");
        map.put("auctioneer.auctionService.defaultMargin", "0.2");
        map.put("auctioneer.auctionService.defaultClearingPrice", "40.0");
        Configuration mapConfig = new MapConfiguration(map);
        config.setConfiguration(mapConfig);
        svc.initialize(competition, new ArrayList<String>());
    }

    @Test
    public void testAuctionService() {
        assertNotNull("auction service created", svc);
        assertEquals("correct surplus", 0.5, svc.getSellerSurplusRatio(), 1e-6);
        assertEquals("empty incoming", 0, svc.getIncoming().size());
    }

    @Test
    public void testInit() {
        verify(mockProxy).registerBrokerMessageListener(svc, Order.class);
        verify(mockControl).registerTimeslotPhase(svc, 1);
    }

    @Test
    public void testReceiveMessage() {
        // try a good one
        Order good = new Order(b1, ts1.getSerialNumber(), 1.0, -22.0);
        svc.handleMessage(good);
        assertEquals("one order received", 1, svc.getIncoming().size());
    }

    @Test
    public void testValidateOrder() {
        // mock the Broker Proxy for b1, capture messages
        doAnswer(new Answer() {
            @Override
            public Object answer(InvocationOnMock invocation) {
                Object[] args = invocation.getArguments();
                brokerMsgs.add(args[1]);
                return null;
            }
        }).when(mockProxy).sendMessage(eq(b1), anyObject());

        competition.withMinimumOrderQuantity(0.1);
        Order good = new Order(b1, ts1.getSerialNumber(), 1.0, -22.0);
        assertTrue("ts1 enabled", timeslotRepo.isTimeslotEnabled(ts1));
        assertTrue("next timeslot valid", svc.validateOrder(good));

        Order bogus = new Order(b1, ts0.getSerialNumber(), 1.0, -22.0);
        assertFalse("ts0 not enabled", timeslotRepo.isTimeslotEnabled(ts0));
        assertFalse("current timeslot not valid", svc.validateOrder(bogus));
        assertEquals("1 message sent", 1, brokerMsgs.size());
        OrderStatus status = (OrderStatus) brokerMsgs.get(0);
        assertNotNull("status got sent", status);
        assertEquals("correct broker", b1, status.getBroker());
        assertEquals("correct order", bogus.getId(), status.getOrderId());

        Order smallSell = new Order(b1, ts1.getSerialNumber(), 0.09, -22.0);
        assertFalse("too small buy", svc.validateOrder(smallSell));
        Order smallBuy = new Order(b1, ts1.getSerialNumber(), -0.08, 22.0);
        assertFalse("too small buy", svc.validateOrder(smallBuy));
    }

    // one ask, one bid, equal qty, tradeable
    @Test
    public void testActivate1() {
        Order sell = new Order(s1, ts1.getSerialNumber(), -1.0, 20.0);
        Order buy = new Order(b1, ts1.getSerialNumber(), 1.0, -22.0);
        svc.handleMessage(sell);
        svc.handleMessage(buy);
        assertEquals("two orders received", 2, svc.getIncoming().size());
        svc.activate(timeService.getCurrentTime(), 2);
        assertEquals("accounting called twice", 2, accountingArgs.size());
        // first tx should be ask, second bid
        Object[] args = accountingArgs.get(0);
        assertEquals("s1", s1, args[0]);
        assertEquals("ts1", ts1, args[1]);
        assertEquals("mWh", -1.0, (Double) args[2], 1e-6);
        assertEquals("price", 21.0, (Double) args[3], 1e-6);
        args = accountingArgs.get(1);
        assertEquals("b1", b1, args[0]);
        assertEquals("ts1", ts1, args[1]);
        assertEquals("mWh", 1.0, (Double) args[2], 1e-6);
        assertEquals("price", -21.0, (Double) args[3], 1e-6);
        // two broker messages
        assertEquals("2 messages", 2, brokerMsgs.size());
        assertTrue("first is orderbook", brokerMsgs.get(0) instanceof Orderbook);
        Orderbook ob = (Orderbook) brokerMsgs.get(0);
        assertEquals("no uncleared asks", 0, ob.getAsks().size());
        assertEquals("no uncleared bids", 0, ob.getBids().size());
        assertEquals("correct timeslot", ts1, ob.getTimeslot());
        assertEquals("correct clearing", 21.0, ob.getClearingPrice(), 1e-6);
        assertTrue("second is clearedTrade", brokerMsgs.get(1) instanceof ClearedTrade);
        ClearedTrade ct = (ClearedTrade) brokerMsgs.get(1);
        assertEquals("correct timeslot", ts1, ct.getTimeslot());
        assertEquals("correct mWh", 1.0, ct.getExecutionMWh(), 1e-6);
        assertEquals("correct price", 21.0, ct.getExecutionPrice(), 1e-6);
        // check minAsk data
        Double[] minAsks = orderbookRepo.getMinAskPrices();
        assertEquals("four prices", 4, minAsks.length);
        assertEquals("correct first price", 20.0, minAsks[0], 1e-6);
        assertNull("second price null", minAsks[1]);
        assertNull("third price null", minAsks[2]);
        assertNull("fourth price null", minAsks[3]);
    }

    // one ask, one bid, equal qty, not tradeable
    @Test
    public void testActivate1_no() {
        Order sell = new Order(s1, ts1.getSerialNumber(), -1.0, 23.0);
        Order buy = new Order(b1, ts1.getSerialNumber(), 1.0, -22.0);
        svc.handleMessage(sell);
        svc.handleMessage(buy);
        assertEquals("two orders received", 2, svc.getIncoming().size());
        svc.activate(timeService.getCurrentTime(), 2);
        assertEquals("accounting not called", 0, accountingArgs.size());
        // one broker message
        assertEquals("one message", 1, brokerMsgs.size());
        assertTrue("first is orderbook", brokerMsgs.get(0) instanceof Orderbook);
        Orderbook ob = (Orderbook) brokerMsgs.get(0);
        assertNull("null clearing price", ob.getClearingPrice());
        assertEquals("one uncleared ask", 1, ob.getAsks().size());
        assertEquals("correct qty", -1.0, ob.getAsks().first().getMWh(), 1e-6);
        assertEquals("correct price", 23.0, ob.getAsks().first().getLimitPrice(), 1e-6);
        assertEquals("one uncleared bid", 1, ob.getBids().size());
        assertEquals("correct qty", 1.0, ob.getBids().first().getMWh(), 1e-6);
        assertEquals("correct price", -22.0, ob.getBids().first().getLimitPrice(), 1e-6);
        // check minAsk data
        Double[] minAsks = orderbookRepo.getMinAskPrices();
        assertEquals("four prices", 4, minAsks.length);
        assertEquals("correct first price", 23.0, minAsks[0], 1e-6);
        assertNull("second price null", minAsks[1]);
        assertNull("third price null", minAsks[2]);
        assertNull("fourth price null", minAsks[3]);
    }

    // one ask, one bid, equal qty, different timeslots
    @Test
    public void testActivate1_ts() {
        Order sell = new Order(s1, ts1.getSerialNumber(), -1.0, 23.0);
        Order buy = new Order(b1, ts2.getSerialNumber(), 1.0, -22.0);
        svc.handleMessage(sell);
        svc.handleMessage(buy);
        assertEquals("two orders received", 2, svc.getIncoming().size());
        svc.activate(timeService.getCurrentTime(), 2);
        assertEquals("accounting not called", 0, accountingArgs.size());
        // two broker messages, one for each timeslot
        assertEquals("two messages", 2, brokerMsgs.size());
        assertTrue("ts1 orderbook", brokerMsgs.get(0) instanceof Orderbook);
        Orderbook ob = (Orderbook) brokerMsgs.get(0);
        assertEquals("ts1", ts1, ob.getTimeslot());
        assertNull("null clearing price", ob.getClearingPrice());
        assertEquals("one uncleared ask", 1, ob.getAsks().size());
        assertEquals("no uncleared bids", 0, ob.getBids().size());
        assertEquals("correct qty", -1.0, ob.getAsks().first().getMWh(), 1e-6);
        assertEquals("correct price", 23.0, ob.getAsks().first().getLimitPrice(), 1e-6);

        assertTrue("ts2 orderbook", brokerMsgs.get(0) instanceof Orderbook);
        ob = (Orderbook) brokerMsgs.get(1);
        assertEquals("ts2", ts2, ob.getTimeslot());
        assertNull("null clearing price", ob.getClearingPrice());
        assertEquals("no uncleared asks", 0, ob.getAsks().size());
        assertEquals("one uncleared bid", 1, ob.getBids().size());
        assertEquals("correct qty", 1.0, ob.getBids().first().getMWh(), 1e-6);
        assertEquals("correct price", -22.0, ob.getBids().first().getLimitPrice(), 1e-6);
    }

    // one ask, two bids, all tradeable
    @Test
    public void testActivate1_2_tradeable() {
        Order sell = new Order(s1, ts1, -1.0, 20.0);
        Order buy1 = new Order(b1, ts1, 0.6, -21.0);
        Order buy2 = new Order(b2, ts1, 0.6, -22.0);
        svc.handleMessage(sell);
        svc.handleMessage(buy1);
        svc.handleMessage(buy2);
        assertEquals("three orders received", 3, svc.getIncoming().size());
        svc.activate(timeService.getCurrentTime(), 2);
        assertEquals("accounting: 4 calls", 4, accountingArgs.size());
        // first tx should be ask, second bid
        Object[] args = accountingArgs.get(0);
        assertEquals("s1", s1, args[0]);
        assertEquals("ts1", ts1, args[1]);
        assertEquals("mWh", -0.6, (Double) args[2], 1e-6);
        assertEquals("price", 20.5, (Double) args[3], 1e-6);

        args = accountingArgs.get(1);
        assertEquals("b2", b2, args[0]); // b2 had the higher bid price
        assertEquals("ts1", ts1, args[1]);
        assertEquals("mWh", 0.6, (Double) args[2], 1e-6);
        assertEquals("price", -20.5, (Double) args[3], 1e-6);

        args = accountingArgs.get(2);
        assertEquals("s1", s1, args[0]);
        assertEquals("ts1", ts1, args[1]);
        assertEquals("mWh", -0.4, (Double) args[2], 1e-6);
        assertEquals("price", 20.5, (Double) args[3], 1e-6);

        args = accountingArgs.get(3);
        assertEquals("b1", b1, args[0]); // b1 had the lower bid price
        assertEquals("ts1", ts1, args[1]);
        assertEquals("mWh", 0.4, (Double) args[2], 1e-6);
        assertEquals("price", -20.5, (Double) args[3], 1e-6);
        // two broker messages
        assertEquals("2 messages", 2, brokerMsgs.size());
        assertTrue("first is orderbook", brokerMsgs.get(0) instanceof Orderbook);
        Orderbook ob = (Orderbook) brokerMsgs.get(0);
        assertEquals("no uncleared asks", 0, ob.getAsks().size());
        assertEquals("one uncleared bid", 1, ob.getBids().size());
        assertEquals("correct qty", 0.2, ob.getBids().first().getMWh(), 1e-6);
        assertEquals("correct price", -21.0, ob.getBids().first().getLimitPrice(), 1e-6);
        assertEquals("correct timeslot", ts1, ob.getTimeslot());
        assertEquals("correct clearing", 20.5, ob.getClearingPrice(), 1e-6);

        assertTrue("second is clearedTrade", brokerMsgs.get(1) instanceof ClearedTrade);
        ClearedTrade ct = (ClearedTrade) brokerMsgs.get(1);
        assertEquals("correct timeslot", ts1, ct.getTimeslot());
        assertEquals("correct mWh", 1.0, ct.getExecutionMWh(), 1e-6);
        assertEquals("correct price", 20.5, ct.getExecutionPrice(), 1e-6);
    }

    // three asks, two bids, all tradeable
    @Test
    public void testActivate2_2_tradeable() {
        Order sell1 = new Order(s1, ts2.getSerialNumber(), -0.9, 18.0);
        Order sell2 = new Order(s2, ts2.getSerialNumber(), -1.0, 20.0);
        Order sell3 = new Order(s2, ts2.getSerialNumber(), -1.0, 21.5);
        Order buy1 = new Order(b1, ts2.getSerialNumber(), 1.4, -21.0);
        Order buy2 = new Order(b2, ts2.getSerialNumber(), 0.6, -22.0);
        svc.handleMessage(sell1);
        svc.handleMessage(sell2);
        svc.handleMessage(sell3);
        svc.handleMessage(buy1);
        svc.handleMessage(buy2);
        assertEquals("five orders received", 5, svc.getIncoming().size());
        svc.activate(timeService.getCurrentTime(), 2);
        assertEquals("accounting: 6 calls", 6, accountingArgs.size());
        // first tx should be ask, second bid
        Object[] args = accountingArgs.get(0);
        assertEquals("s1", s1, args[0]);
        assertEquals("ts2", ts2, args[1]);
        assertEquals("mWh", -0.6, (Double) args[2], 1e-6);
        assertEquals("price", 20.5, (Double) args[3], 1e-6);

        args = accountingArgs.get(1);
        assertEquals("b2", b2, args[0]); // b2 had the higher bid price
        assertEquals("ts2", ts2, args[1]);
        assertEquals("mWh", 0.6, (Double) args[2], 1e-6);
        assertEquals("price", -20.5, (Double) args[3], 1e-6);

        args = accountingArgs.get(2);
        assertEquals("s1", s1, args[0]);
        assertEquals("ts2", ts2, args[1]);
        assertEquals("mWh", -0.3, (Double) args[2], 1e-6);
        assertEquals("price", 20.5, (Double) args[3], 1e-6);

        args = accountingArgs.get(3);
        assertEquals("b1", b1, args[0]); // b1 had the lower bid price
        assertEquals("ts2", ts2, args[1]);
        assertEquals("mWh", 0.3, (Double) args[2], 1e-6);
        assertEquals("price", -20.5, (Double) args[3], 1e-6);

        args = accountingArgs.get(4);
        assertEquals("s2", s2, args[0]);
        assertEquals("ts2", ts2, args[1]);
        assertEquals("mWh", -1.0, (Double) args[2], 1e-6);
        assertEquals("price", 20.5, (Double) args[3], 1e-6);

        args = accountingArgs.get(5);
        assertEquals("b1", b1, args[0]); // b1 had the lower bid price
        assertEquals("ts2", ts2, args[1]);
        assertEquals("mWh", 1.0, (Double) args[2], 1e-6);
        assertEquals("price", -20.5, (Double) args[3], 1e-6);

        // two broker messages
        assertEquals("2 messages", 2, brokerMsgs.size());
        assertTrue("first is orderbook", brokerMsgs.get(0) instanceof Orderbook);
        Orderbook ob = (Orderbook) brokerMsgs.get(0);
        assertEquals("correct timeslot", ts2, ob.getTimeslot());
        assertEquals("correct clearing", 20.5, ob.getClearingPrice(), 1e-6);
        assertEquals("one uncleared ask", 1, ob.getAsks().size());
        assertEquals("correct qty", -1.0, ob.getAsks().first().getMWh(), 1e-6);
        assertEquals("correct price", 21.5, ob.getAsks().first().getLimitPrice(), 1e-6);
        assertEquals("one uncleared bid", 1, ob.getBids().size());
        assertEquals("correct qty", 0.1, ob.getBids().first().getMWh(), 1e-6);
        assertEquals("correct price", -21.0, ob.getBids().first().getLimitPrice(), 1e-6);

        assertTrue("second is clearedTrade", brokerMsgs.get(1) instanceof ClearedTrade);
        ClearedTrade ct = (ClearedTrade) brokerMsgs.get(1);
        assertEquals("correct timeslot", ts2, ct.getTimeslot());
        assertEquals("correct mWh", 1.9, ct.getExecutionMWh(), 1e-6);
        assertEquals("correct price", 20.5, ct.getExecutionPrice(), 1e-6);
    }

    // three asks, two bids, all tradeable
    @Test
    public void testActivate3_eq_tradeable() {
        Order sell1 = new Order(s1, ts2.getSerialNumber(), -0.9, 18.0);
        Order sell2 = new Order(s2, ts2.getSerialNumber(), -1.0, 20.0);
        Order sell3 = new Order(s2, ts2.getSerialNumber(), -1.0, 21.0);
        Order buy1 = new Order(b1, ts2.getSerialNumber(), 0.8, -21.0);
        Order buy1a = new Order(b1, ts2.getSerialNumber(), 0.2, -21.0);
        Order buy1b = new Order(b1, ts2.getSerialNumber(), 0.2, -21.0);
        Order buy1c = new Order(b1, ts2.getSerialNumber(), 0.2, -21.0);
        Order buy2 = new Order(b2, ts2.getSerialNumber(), 0.6, -22.0);
        svc.handleMessage(sell1);
        svc.handleMessage(sell2);
        svc.handleMessage(sell3);
        svc.handleMessage(buy1);
        svc.handleMessage(buy1a);
        svc.handleMessage(buy1b);
        svc.handleMessage(buy1c);
        svc.handleMessage(buy2);
        assertEquals("eight orders received", 8, svc.getIncoming().size());
        svc.activate(timeService.getCurrentTime(), 2);
        assertEquals("accounting: 14 calls", 14, accountingArgs.size());
        // first tx should be ask, second bid
        Object[] args = accountingArgs.get(0);
        assertEquals("s1", s1, args[0]);
        assertEquals("ts2", ts2, args[1]);
        assertEquals("mWh", -0.6, (Double) args[2], 1e-6);
        assertEquals("price", 21.0, (Double) args[3], 1e-6);

        args = accountingArgs.get(1);
        assertEquals("b2", b2, args[0]); // b2 had the higher bid price
        assertEquals("ts2", ts2, args[1]);
        assertEquals("mWh", 0.6, (Double) args[2], 1e-6);
        assertEquals("price", -21.0, (Double) args[3], 1e-6);

        args = accountingArgs.get(2);
        assertEquals("s1", s1, args[0]);
        assertEquals("ts2", ts2, args[1]);
        assertEquals("mWh", -0.3, (Double) args[2], 1e-6);
        assertEquals("price", 21.0, (Double) args[3], 1e-6);

        args = accountingArgs.get(3);
        assertEquals("b1", b1, args[0]); // b1 had the lower bid price
        assertEquals("ts2", ts2, args[1]);
        assertEquals("mWh", 0.3, (Double) args[2], 1e-6);
        assertEquals("price", -21.0, (Double) args[3], 1e-6);

        args = accountingArgs.get(4);
        assertEquals("s2", s2, args[0]);
        assertEquals("ts2", ts2, args[1]);
        assertEquals("mWh", -0.5, (Double) args[2], 1e-6);
        assertEquals("price", 21.0, (Double) args[3], 1e-6);

        args = accountingArgs.get(5);
        assertEquals("b1", b1, args[0]); // b1 had the lower bid price
        assertEquals("ts2", ts2, args[1]);
        assertEquals("mWh", 0.5, (Double) args[2], 1e-6);
        assertEquals("price", -21.0, (Double) args[3], 1e-6);

        args = accountingArgs.get(6);
        assertEquals("s2", s2, args[0]); // b1 had the lower bid price
        assertEquals("ts2", ts2, args[1]);
        assertEquals("mWh", -0.2, (Double) args[2], 1e-6);
        assertEquals("price", 21.0, (Double) args[3], 1e-6);

        args = accountingArgs.get(7);
        assertEquals("b1", b1, args[0]); // b1 had the lower bid price
        assertEquals("ts2", ts2, args[1]);
        assertEquals("mWh", 0.2, (Double) args[2], 1e-6);
        assertEquals("price", -21.0, (Double) args[3], 1e-6);

        args = accountingArgs.get(8);
        assertEquals("s2", s2, args[0]); // b1 had the lower bid price
        assertEquals("ts2", ts2, args[1]);
        assertEquals("mWh", -0.2, (Double) args[2], 1e-6);
        assertEquals("price", 21.0, (Double) args[3], 1e-6);

        args = accountingArgs.get(9);
        assertEquals("b1", b1, args[0]); // b1 had the lower bid price
        assertEquals("ts2", ts2, args[1]);
        assertEquals("mWh", 0.2, (Double) args[2], 1e-6);
        assertEquals("price", -21.0, (Double) args[3], 1e-6);

        // two broker messages
        assertEquals("2 messages", 2, brokerMsgs.size());
        assertTrue("first is orderbook", brokerMsgs.get(0) instanceof Orderbook);
        Orderbook ob = (Orderbook) brokerMsgs.get(0);
        assertEquals("correct timeslot", ts2, ob.getTimeslot());
        assertEquals("correct clearing", 21.0, ob.getClearingPrice(), 1e-6);
        assertEquals("one uncleared ask", 1, ob.getAsks().size());
        assertEquals("correct qty", -0.9, ob.getAsks().first().getMWh(), 1e-6);
        assertEquals("correct price", 21.0, ob.getAsks().first().getLimitPrice(), 1e-6);
        assertEquals("no uncleared bids", 0, ob.getBids().size());

        assertTrue("second is clearedTrade", brokerMsgs.get(1) instanceof ClearedTrade);
        ClearedTrade ct = (ClearedTrade) brokerMsgs.get(1);
        assertEquals("correct timeslot", ts2, ct.getTimeslot());
        assertEquals("correct mWh", 2.0, ct.getExecutionMWh(), 1e-6);
        assertEquals("correct price", 21.0, ct.getExecutionPrice(), 1e-6);
    }

    // two asks, two bids, market order on one ask
    @Test
    public void marketAskTest() {
        Order sell1 = new Order(s1, ts2, -0.9, 18.0);
        Order sell2 = new Order(s2, ts2, -1.0, null);
        Order buy1 = new Order(b1, ts2, 1.4, -21.0);
        Order buy2 = new Order(b2, ts2, 0.6, -22.0);
        svc.handleMessage(sell1);
        svc.handleMessage(sell2);
        svc.handleMessage(buy1);
        svc.handleMessage(buy2);
        assertEquals("four orders received", 4, svc.getIncoming().size());
        svc.activate(timeService.getCurrentTime(), 2);
        assertEquals("accounting: 6 calls", 6, accountingArgs.size());
        // first tx should be ask, second bid
        Object[] args = accountingArgs.get(0);
        assertEquals("s2", s2, args[0]);
        assertEquals("ts2", ts2, args[1]);
        assertEquals("mWh", -0.6, (Double) args[2], 1e-6);
        assertEquals("price", 19.5, (Double) args[3], 1e-6);

        args = accountingArgs.get(1);
        assertEquals("b2", b2, args[0]);
        assertEquals("mWh", 0.6, (Double) args[2], 1e-6);
        assertEquals("price", -19.5, (Double) args[3], 1e-6);

        // check the cleared trade
        assertTrue("ClearedTrade sent", brokerMsgs.get(1) instanceof ClearedTrade);
        ClearedTrade ct = (ClearedTrade) brokerMsgs.get(1);
        assertEquals("correct timeslot", ts2, ct.getTimeslot());
        assertEquals("correct mWh", 1.9, ct.getExecutionMWh(), 1e-6);
        assertEquals("correct price", 19.5, ct.getExecutionPrice(), 1e-6);
    }

    // two asks, two bids, market order on one ask, zero qty bid
    @Test
    public void marketAskZeroTest() {
        Order sell1 = new Order(s1, ts2, -1.9, 18.0);
        Order sell2 = new Order(s2, ts2, -1.0, null);
        Order buy1 = new Order(b1, ts2, 0.0, -24.0);
        Order buy2 = new Order(b2, ts2, 4.0, -16.0);
        svc.handleMessage(sell1);
        svc.handleMessage(sell2);
        svc.handleMessage(buy1);
        svc.handleMessage(buy2);
        assertEquals("four orders received", 3, svc.getIncoming().size());
        svc.activate(timeService.getCurrentTime(), 2);
        assertEquals("accounting: 2 calls", 2, accountingArgs.size());
        // first tx should be ask, second bid

        double expectedPrice = 16.0 / 1.2;
        Object[] args = accountingArgs.get(0);
        assertEquals("s2", s2, args[0]);
        assertEquals("ts2", ts2, args[1]);
        assertEquals("mWh", -1.0, (Double) args[2], 1e-6);
        assertEquals("price", expectedPrice, (Double) args[3], 1e-6);

        args = accountingArgs.get(1);
        assertEquals("b2", b2, args[0]);
        assertEquals("mWh", 1.0, (Double) args[2], 1e-6);
        assertEquals("price", -expectedPrice, (Double) args[3], 1e-6);

        // check the cleared trade
        assertTrue("ClearedTrade sent", brokerMsgs.get(1) instanceof ClearedTrade);
        ClearedTrade ct = (ClearedTrade) brokerMsgs.get(1);
        assertEquals("correct timeslot", ts2, ct.getTimeslot());
        assertEquals("correct mWh", 1.0, ct.getExecutionMWh(), 1e-6);
        assertEquals("correct price", expectedPrice, ct.getExecutionPrice(), 1e-6);
    }

    // two asks, two bids, market order on one bid
    @Test
    public void marketBidTest() {
        Order sell1 = new Order(s1, ts2, -0.9, 18.0);
        Order sell2 = new Order(s2, ts2, -1.0, 20.0);
        Order buy1 = new Order(b1, ts2, 1.4, -21.0);
        Order buy2 = new Order(b2, ts2, 0.6, null);
        svc.handleMessage(sell1);
        svc.handleMessage(sell2);
        svc.handleMessage(buy1);
        svc.handleMessage(buy2);
        assertEquals("four orders received", 4, svc.getIncoming().size());
        svc.activate(timeService.getCurrentTime(), 2);
        assertEquals("accounting: 6 calls", 6, accountingArgs.size());
        // first tx should be ask, second bid
        Object[] args = accountingArgs.get(0);
        assertEquals("s1", s1, args[0]);
        assertEquals("ts2", ts2, args[1]);
        assertEquals("mWh", -0.6, (Double) args[2], 1e-6);
        assertEquals("price", 20.5, (Double) args[3], 1e-6);

        args = accountingArgs.get(1);
        assertEquals("b2", b2, args[0]);
        assertEquals("mWh", 0.6, (Double) args[2], 1e-6);
        assertEquals("price", -20.5, (Double) args[3], 1e-6);

        args = accountingArgs.get(2);
        assertEquals("s1", s1, args[0]);
        assertEquals("mWh", -0.3, (Double) args[2], 1e-6);
        assertEquals("price", 20.5, (Double) args[3], 1e-6);

        // check the cleared trade
        assertTrue("ClearedTrade sent", brokerMsgs.get(1) instanceof ClearedTrade);
        ClearedTrade ct = (ClearedTrade) brokerMsgs.get(1);
        assertEquals("correct timeslot", ts2, ct.getTimeslot());
        assertEquals("correct mWh", 1.9, ct.getExecutionMWh(), 1e-6);
        assertEquals("correct price", 20.5, ct.getExecutionPrice(), 1e-6);
    }

    // two asks, one market bid
    @Test
    public void marketBidClear() {
        Order sell1 = new Order(s1, ts2, -0.9, 18.0);
        Order sell2 = new Order(s2, ts2, -1.0, 20.0);
        Order buy1 = new Order(b1, ts2, 1.4, null);
        svc.handleMessage(sell1);
        svc.handleMessage(sell2);
        svc.handleMessage(buy1);
        assertEquals("three orders received", 3, svc.getIncoming().size());
        svc.activate(timeService.getCurrentTime(), 2);
        assertEquals("accounting: 4 calls", 4, accountingArgs.size());
        double expectedPrice = 20 * 1.2;

        // first tx should be ask, second bid
        Object[] args = accountingArgs.get(0);
        assertEquals("s1", s1, args[0]);
        assertEquals("ts2", ts2, args[1]);
        assertEquals("mWh", -0.9, (Double) args[2], 1e-6);
        assertEquals("price", expectedPrice, (Double) args[3], 1e-6);

        args = accountingArgs.get(1);
        assertEquals("b1", b1, args[0]);
        assertEquals("ts2", ts2, args[1]);
        assertEquals("mWh", 0.9, (Double) args[2], 1e-6);
        assertEquals("price", -expectedPrice, (Double) args[3], 1e-6);

        // check the cleared trade
        assertTrue("ClearedTrade sent", brokerMsgs.get(1) instanceof ClearedTrade);
        ClearedTrade ct = (ClearedTrade) brokerMsgs.get(1);
        assertEquals("correct timeslot", ts2, ct.getTimeslot());
        assertEquals("correct mWh", 1.4, ct.getExecutionMWh(), 1e-6);
        assertEquals("correct price", expectedPrice, ct.getExecutionPrice(), 1e-6);
    }

    // two bids, one market ask
    @Test
    public void marketAskClear() {
        Order sell1 = new Order(s1, ts2, -1.0, null);
        Order buy1 = new Order(b1, ts2, 1.4, -21.0);
        Order buy2 = new Order(b2, ts2, 0.6, -22.0);
        svc.handleMessage(sell1);
        svc.handleMessage(buy1);
        svc.handleMessage(buy2);
        assertEquals("three orders received", 3, svc.getIncoming().size());
        svc.activate(timeService.getCurrentTime(), 2);
        assertEquals("accounting: 4 calls", 4, accountingArgs.size());
        double expectedPrice = 21.0 / 1.2;

        // first tx should be ask, second bid
        Object[] args = accountingArgs.get(0);
        assertEquals("s1", s1, args[0]);
        assertEquals("ts2", ts2, args[1]);
        assertEquals("mWh", -0.6, (Double) args[2], 1e-6);
        assertEquals("price", expectedPrice, (Double) args[3], 1e-6);

        args = accountingArgs.get(1);
        assertEquals("b2", b2, args[0]);
        assertEquals("ts2", ts2, args[1]);
        assertEquals("mWh", 0.6, (Double) args[2], 1e-6);
        assertEquals("price", -expectedPrice, (Double) args[3], 1e-6);

        // check the cleared trade
        assertTrue("ClearedTrade sent", brokerMsgs.get(1) instanceof ClearedTrade);
        ClearedTrade ct = (ClearedTrade) brokerMsgs.get(1);
        assertEquals("correct timeslot", ts2, ct.getTimeslot());
        assertEquals("correct mWh", 1.0, ct.getExecutionMWh(), 1e-6);
        assertEquals("correct price", expectedPrice, ct.getExecutionPrice(), 1e-6);
    }

    // one market ask, one market bid
    @Test
    public void marketClear() {
        Order sell1 = new Order(s1, ts2, -1.0, null);
        Order buy1 = new Order(b1, ts2, 1.4, null);
        svc.handleMessage(sell1);
        svc.handleMessage(buy1);
        assertEquals("two orders received", 2, svc.getIncoming().size());
        svc.activate(timeService.getCurrentTime(), 2);
        assertEquals("accounting: 2 calls", 2, accountingArgs.size());

        // first tx should be ask, second bid
        Object[] args = accountingArgs.get(0);
        assertEquals("s1", s1, args[0]);
        assertEquals("ts2", ts2, args[1]);
        assertEquals("mWh", -1.0, (Double) args[2], 1e-6);
        assertEquals("price", svc.getDefaultClearingPrice(), (Double) args[3], 1e-6);
    }

    // three asks, five bids, wide numeric range
    @Test
    public void testNumericRange() {
        competition.withMinimumOrderQuantity(0.001);
        Order sell1 = new Order(s1, ts2, -0.036040484378997206, 20.0);
        Order sell2 = new Order(s2, ts2, -0.3961457798682808, 21.8);
        Order sell3 = new Order(s2, ts2, -26.185209758164312, 35.0);
        Order buy1 = new Order(b1, ts2, 6.0, -35.0);
        Order buy2 = new Order(b2, ts2, 0.35, -50.0);
        Order buy3 = new Order(b2, ts2, 8.728125, null);
        Order buy4 = new Order(b2, ts2, 0.0075, -37.0);
        Order buy5 = new Order(b2, ts2, 7.875, -35.0);
        svc.handleMessage(sell1);
        svc.handleMessage(sell2);
        svc.handleMessage(sell3);
        svc.handleMessage(buy1);
        svc.handleMessage(buy2);
        svc.handleMessage(buy3);
        svc.handleMessage(buy4);
        svc.handleMessage(buy5);
        assertEquals("eight orders received", 8, svc.getIncoming().size());
        svc.activate(timeService.getCurrentTime(), 2);
        assertEquals("accounting: 14 calls", 14, accountingArgs.size());
        // first tx should be ask, second bid
        // sell1, buy3, finish off sell1
        Object[] args = accountingArgs.get(0);
        assertEquals("s1", s1, args[0]);
        assertEquals("mWh", -0.036040484378997206, (Double) args[2], 1e-6);
        assertEquals("price", 35.0, (Double) args[3], 1e-6);

        args = accountingArgs.get(1);
        assertEquals("b2", b2, args[0]); // b2 had market order
        assertEquals("mWh", 0.036040484378997206, (Double) args[2], 1e-6);
        assertEquals("price", -35.0, (Double) args[3], 1e-6);

        // sell2, buy3, finish off sell2
        args = accountingArgs.get(2);
        assertEquals("s2", s2, args[0]);
        assertEquals("mWh", -0.3961457798682808, (Double) args[2], 1e-6);
        assertEquals("price", 35.0, (Double) args[3], 1e-6);

        args = accountingArgs.get(3);
        assertEquals("b2", b2, args[0]); // still working on buy3
        assertEquals("mWh", 0.3961457798682808, (Double) args[2], 1e-6);
        assertEquals("price", -35.0, (Double) args[3], 1e-6);

        // sell3, buy3, finish off buy3
        args = accountingArgs.get(4);
        assertEquals("s2", s2, args[0]);
        assertEquals("mWh", -8.295938736, (Double) args[2], 1e-6);
        assertEquals("price", 35.0, (Double) args[3], 1e-6);

        args = accountingArgs.get(5);
        assertEquals("b2", b2, args[0]); // finish up market order
        assertEquals("mWh", 8.295938736, (Double) args[2], 1e-6);
        assertEquals("price", -35.0, (Double) args[3], 1e-6);

        // sell3, buy2, finish off buy2
        args = accountingArgs.get(6);
        assertEquals("s2", s2, args[0]);
        assertEquals("mWh", -0.35, (Double) args[2], 1e-6);
        assertEquals("price", 35.0, (Double) args[3], 1e-6);

        args = accountingArgs.get(7);
        assertEquals("b2", b2, args[0]); // finish up market order
        assertEquals("mWh", 0.35, (Double) args[2], 1e-6);
        assertEquals("price", -35.0, (Double) args[3], 1e-6);

        // sell3, buy4, finish off buy4
        args = accountingArgs.get(8);
        assertEquals("s2", s2, args[0]);
        assertEquals("mWh", -0.0075, (Double) args[2], 1e-6);
        assertEquals("price", 35.0, (Double) args[3], 1e-6);

        args = accountingArgs.get(9);
        assertEquals("b2", b2, args[0]); // finish up market order
        assertEquals("mWh", 0.0075, (Double) args[2], 1e-6);
        assertEquals("price", -35.0, (Double) args[3], 1e-6);

        // sell3, buy1/5
        args = accountingArgs.get(10);
        Object[] buyArgs = accountingArgs.get(11);
        Broker b11 = (Broker) buyArgs[0];
        if (b11 == b2) {
            // buy5
            assertEquals("mWh", -7.875, (Double) args[2], 1e-6);
        } else {
            // buy1
            assertEquals("mWh", -6.0, (Double) args[2], 1e-6);
        }

        // sell3, buy 1/5
        args = accountingArgs.get(12);
        buyArgs = accountingArgs.get(13);
        Broker b13 = (Broker) buyArgs[0];
        assertTrue("b1 != b2", b11 != b13);
        if (b13 == b2) {
            // buy5
            assertEquals("mWh", -7.875, (Double) args[2], 1e-6);
        } else {
            // buy1
            assertEquals("mWh", -6.0, (Double) args[2], 1e-6);
        }

        // check minAsk data
        Double[] minAsks = orderbookRepo.getMinAskPrices();
        assertEquals("four prices", 4, minAsks.length);
        assertNull("first price null", minAsks[0]);
        assertEquals("correct first min price", sell1.getLimitPrice(), minAsks[1], 1e-6);
        assertNull("third price null", minAsks[2]);
        assertNull("fourth price null", minAsks[3]);

        // check maxAsk data
        Double[] maxAsks = orderbookRepo.getMaxAskPrices();
        assertEquals("four prices", 4, maxAsks.length);
        assertNull("first price null", maxAsks[0]);
        assertEquals("correct first max price", sell3.getLimitPrice(), maxAsks[1], 1e-6);
        assertNull("third price null", maxAsks[2]);
        assertNull("fourth price null", maxAsks[3]);
    }

    @Test
    public void testQuantitytValidity() {
        Order buy1 = new Order(b2, ts2.getSerialNumber(), 0.75, -37.0);
        Order buy2 = new Order(b2, ts2.getSerialNumber(), Double.NaN, -35.0);
        Order buy3 = new Order(b2, ts2.getSerialNumber(), Double.POSITIVE_INFINITY, -35.0);
        Order buy4 = new Order(b2, ts2.getSerialNumber(), Double.NEGATIVE_INFINITY, -35.0);
        svc.handleMessage(buy1);
        svc.handleMessage(buy2);
        svc.handleMessage(buy3);
        svc.handleMessage(buy4);

        assertEquals("one order validated", 1, svc.getIncoming().size());
    }
}