Java tutorial
/* * Copyright 2009-2011 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.accounting; import static org.junit.Assert.*; import static org.powertac.util.ListTools.*; import static org.mockito.Matchers.anyList; import static org.mockito.Mockito.*; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.MapConfiguration; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; 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.*; import org.powertac.common.config.Configurator; import org.powertac.common.enumerations.PowerType; import org.powertac.common.interfaces.BrokerProxy; import org.powertac.common.interfaces.ServerConfiguration; import org.powertac.common.repo.BrokerRepo; import org.powertac.common.repo.TariffRepo; import org.powertac.common.repo.TimeslotRepo; import org.powertac.util.Predicate; 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; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "classpath:test-config.xml" }) @DirtiesContext public class AccountingServiceTests { @Autowired private TimeService timeService; // dependency injection @Autowired private AccountingService accountingService; @Autowired private TariffRepo tariffRepo; @Autowired private TimeslotRepo timeslotRepo; @Autowired private BrokerRepo brokerRepo; // get access to the mock services @Autowired private BrokerProxy mockProxy; @Autowired private ServerConfiguration mockServerProperties; private Configurator config; private Competition comp; private CustomerInfo customerInfo1; private CustomerInfo customerInfo2; private CustomerInfo customerInfo3; private Tariff tariffB1; private Tariff tariffB2; private Tariff tariffJ1; private Broker bob; private Broker jim; //private int nameCounter = 0; @SuppressWarnings("rawtypes") @Before public void setUp() { // Clean up from previous tests tariffRepo.recycle(); timeslotRepo.recycle(); brokerRepo.recycle(); reset(mockProxy); reset(mockServerProperties); // create a Competition, needed for initialization comp = Competition.newInstance("accounting-test"); // set the clock //Instant now = new DateTime(2011, 1, 26, 12, 0, 0, 0, DateTimeZone.UTC).toInstant(); Instant now = Competition.currentCompetition().getSimulationBaseTime(); now = now.plus(TimeService.HOUR); timeService.setCurrentTime(now); // set up brokers and customers bob = new Broker("Bob"); brokerRepo.add(bob); jim = new Broker("Jim"); brokerRepo.add(jim); customerInfo1 = new CustomerInfo("downtown", 42).withPowerType(PowerType.CONSUMPTION); customerInfo2 = new CustomerInfo("suburbs", 21).withPowerType(PowerType.CONSUMPTION); customerInfo3 = new CustomerInfo("exburbs", 11).withPowerType(PowerType.CONSUMPTION); // set up tariffs - tariff1 for consumption, tariff2 for production Instant exp = now.plus(TimeService.WEEK * 10); TariffSpecification tariffSpec = new TariffSpecification(bob, PowerType.CONSUMPTION).withExpiration(exp) .withMinDuration(TimeService.WEEK * 8).withPeriodicPayment(0.02) .addRate(new Rate().withValue(0.121)); tariffRepo.addSpecification(tariffSpec); tariffB1 = new Tariff(tariffSpec); tariffB1.init(); tariffRepo.addTariff(tariffB1); tariffSpec = new TariffSpecification(bob, PowerType.CONSUMPTION).withMinDuration(TimeService.WEEK * 8) .withExpiration(exp).addRate(new Rate().withValue(0.09)); tariffRepo.addSpecification(tariffSpec); tariffB2 = new Tariff(tariffSpec); tariffB2.init(); tariffRepo.addTariff(tariffB2); tariffSpec = new TariffSpecification(jim, PowerType.CONSUMPTION).withMinDuration(TimeService.WEEK * 8) .withExpiration(exp).withPeriodicPayment(0.01).addRate(new Rate().withValue(0.123)); tariffRepo.addSpecification(tariffSpec); tariffJ1 = new Tariff(tariffSpec); tariffJ1.init(); tariffRepo.addTariff(tariffJ1); // set up some timeslots timeslotRepo.makeTimeslot(now.minus(TimeService.HOUR)); timeslotRepo.makeTimeslot(now); timeslotRepo.makeTimeslot(now.plus(TimeService.HOUR)); timeslotRepo.makeTimeslot(now.plus(TimeService.HOUR * 2)); // 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(mockServerProperties).configureMe(anyObject()); } private void initializeService() { String result = accountingService.initialize(comp, new ArrayList<String>()); assertEquals("correct return", "AccountingService", result); } @Test public void testAccountingServiceNotNull() { assertNotNull(accountingService); } // initialization without a configuration @Test public void testNormalInitialization() { String result = accountingService.initialize(comp, new ArrayList<String>()); assertEquals("correct return value", "AccountingService", result); assertTrue("correct bank interest", accountingService.getMinInterest() <= accountingService.getBankInterest()); assertTrue("correct bank interest", accountingService.getMaxInterest() >= accountingService.getBankInterest()); } // config max/min @Test public void testMaxMinInitialization() { TreeMap<String, String> map = new TreeMap<String, String>(); map.put("accounting.accountingService.minInterest", "0.01"); map.put("accounting.accountingService.maxInterest", "0.20"); Configuration mapConfig = new MapConfiguration(map); config.setConfiguration(mapConfig); String result = accountingService.initialize(comp, new ArrayList<String>()); assertEquals("correct return value", "AccountingService", result); assertEquals("correct min value", 0.01, accountingService.getMinInterest(), 1e-6); assertEquals("correct max value", 0.20, accountingService.getMaxInterest(), 1e-6); assertTrue("correct bank interest", accountingService.getMinInterest() <= accountingService.getBankInterest()); assertTrue("correct bank interest", accountingService.getMaxInterest() >= accountingService.getBankInterest()); } // config interest rate directly @Test public void testInterestInitialization() { TreeMap<String, String> map = new TreeMap<String, String>(); map.put("accounting.accountingService.minInterest", "0.01"); map.put("accounting.accountingService.maxInterest", "0.20"); map.put("accounting.accountingService.bankInterest", "0.008"); Configuration mapConfig = new MapConfiguration(map); config.setConfiguration(mapConfig); accountingService.initialize(comp, new ArrayList<String>()); assertEquals("correct bank interest", 0.008, accountingService.getBankInterest(), 1e-6); } @Test public void testBrokerDb() { Broker b1 = brokerRepo.findById(bob.getId()); assertEquals("bob in db by id", bob, b1); Broker b2 = brokerRepo.findByUsername("Bob"); assertEquals("bob in db by name", bob, b2); } // create and test tariff transactions @Test public void testTariffTransaction() { initializeService(); accountingService.addTariffTransaction(TariffTransaction.Type.SIGNUP, tariffB1, customerInfo1, 2, 0.0, 42.1); accountingService.addTariffTransaction(TariffTransaction.Type.CONSUME, tariffB1, customerInfo2, 7, 77.0, 7.7); assertEquals("correct number in list", 2, accountingService.getPendingTransactions().size()); assertEquals("correct number in ttx list", 2, accountingService.getPendingTariffTransactions().size()); List<BrokerTransaction> pending = accountingService.getPendingTransactions(); List<BrokerTransaction> signups = filter(pending, new Predicate<BrokerTransaction>() { public boolean apply(BrokerTransaction tx) { return (tx instanceof TariffTransaction && ((TariffTransaction) tx).getTxType() == TariffTransaction.Type.SIGNUP); } }); assertEquals("one signup", 1, signups.size()); TariffTransaction ttx = (TariffTransaction) signups.get(0); assertNotNull("first ttx not null", ttx); assertEquals("correct charge id 0", 42.1, ttx.getCharge(), 1e-6); Broker b1 = ttx.getBroker(); Broker b2 = brokerRepo.findById(bob.getId()); assertEquals("same broker", b1, b2); List<BrokerTransaction> consumes = filter(pending, new Predicate<BrokerTransaction>() { public boolean apply(BrokerTransaction tx) { return (tx instanceof TariffTransaction && ((TariffTransaction) tx).getTxType() == TariffTransaction.Type.CONSUME); } }); assertEquals("one signup", 1, consumes.size()); ttx = (TariffTransaction) consumes.get(0); assertNotNull("second ttx not null", ttx); assertEquals("correct amount id 1", 77.0, ttx.getKWh(), 1e-6); } @Test public void testCurrentNetLoad() { initializeService(); // some usage for Bob accountingService.addTariffTransaction(TariffTransaction.Type.CONSUME, tariffB1, customerInfo1, 7, -77.0, 7.7); accountingService.addTariffTransaction(TariffTransaction.Type.CONSUME, tariffB1, customerInfo2, 6, -83.0, 8.0); accountingService.addTariffTransaction(TariffTransaction.Type.PRODUCE, tariffB2, customerInfo3, 3, 55.0, -4.5); // some usage for Jim accountingService.addTariffTransaction(TariffTransaction.Type.CONSUME, tariffJ1, customerInfo2, 12, -120.0, 8.4); assertEquals("correct net load for Bob", (-77.0 - 83.0 + 55.0), accountingService.getCurrentNetLoad(bob), 1e-6); assertEquals("correct net load for Jim", -120.0, accountingService.getCurrentNetLoad(jim), 1e-6); } @Test public void testCurrentSupplyDemand() { initializeService(); // some usage for Bob accountingService.addTariffTransaction(TariffTransaction.Type.CONSUME, tariffB1, customerInfo1, 7, -77.0, 7.7); accountingService.addTariffTransaction(TariffTransaction.Type.CONSUME, tariffB1, customerInfo2, 6, -83.0, 8.0); accountingService.addTariffTransaction(TariffTransaction.Type.PRODUCE, tariffB2, customerInfo3, 3, 55.0, -4.5); // some usage for Jim accountingService.addTariffTransaction(TariffTransaction.Type.CONSUME, tariffJ1, customerInfo2, 12, -120.0, 8.4); // retrieve the map Map<Broker, Map<TariffTransaction.Type, Double>> sd = accountingService.getCurrentSupplyDemandByBroker(); // check data for Bob Map<TariffTransaction.Type, Double> bsd = sd.get(bob); assertEquals("correct consumption for Bob", (-77.0 - 83.0), bsd.get(TariffTransaction.Type.CONSUME), 1e-6); assertEquals("correct production for Bob", 55.0, bsd.get(TariffTransaction.Type.PRODUCE), 1e-6); // check data for Jim bsd = sd.get(jim); assertEquals("correct consumption for Jim", -120.0, bsd.get(TariffTransaction.Type.CONSUME), 1e-6); assertEquals("correct production for Jim", 0.0, bsd.get(TariffTransaction.Type.PRODUCE), 1e-6); } // create and test market transactions @Test public void testMarketTransaction() { initializeService(); accountingService.addMarketTransaction(bob, timeslotRepo.findBySerialNumber(2), 0.5, -45.0); accountingService.addMarketTransaction(bob, timeslotRepo.findBySerialNumber(3), 0.7, -43.0); assertEquals("no tariff tx", 0, accountingService.getPendingTariffTransactions().size()); List<BrokerTransaction> pending = accountingService.getPendingTransactions(); assertEquals("correct number in list", 2, pending.size()); MarketTransaction mtx = (MarketTransaction) pending.get(0); assertNotNull("first mtx not null", mtx); assertEquals("correct timeslot id 0", 2, mtx.getTimeslot().getSerialNumber()); assertEquals("correct price id 0", -45.0, mtx.getPrice(), 1e-6); Broker b1 = mtx.getBroker(); Broker b2 = brokerRepo.findById(bob.getId()); assertEquals("same broker", b1, b2); mtx = (MarketTransaction) pending.get(1); assertNotNull("second mtx not null", mtx); assertEquals("correct quantity id 1", 0.7, mtx.getMWh(), 1e-6); // broker market positions should have been updated already MarketPosition mp2 = bob.findMarketPositionByTimeslot(2); assertNotNull("should be a market position in slot 2", mp2); assertEquals(".5 mwh in ts2", 0.5, mp2.getOverallBalance(), 1e-6); MarketPosition mp3 = bob.findMarketPositionByTimeslot(3); assertNotNull("should be a market position in slot 3", mp3); assertEquals(".7 mwh in ts3", 0.7, mp3.getOverallBalance(), 1e-6); } // simple activation @SuppressWarnings({ "rawtypes", "unchecked" }) @Test public void testSimpleActivate() { initializeService(); // Need to set the interest rate in the Accounting config accountingService.setBankInterest(0.12); // add a couple of transactions accountingService.addMarketTransaction(bob, timeslotRepo.findBySerialNumber(1), 0.6, -55.0); accountingService.addMarketTransaction(bob, timeslotRepo.findBySerialNumber(2), 0.5, -45.0); accountingService.addMarketTransaction(bob, timeslotRepo.findBySerialNumber(3), 0.7, -43.0); final Map<Broker, List> msgMap = new HashMap<Broker, List>(); doAnswer(new Answer() { public Object answer(InvocationOnMock invocation) { Object[] args = invocation.getArguments(); msgMap.put((Broker) args[0], (List) args[1]); return null; } }).when(mockProxy).sendMessages(isA(Broker.class), anyList()); // activate and check messages accountingService.activate(timeService.getCurrentTime(), 3); verify(mockProxy, times(2)).sendMessages(isA(Broker.class), anyList()); // should have cash position, no market positions for jim assertEquals("one message", 1, msgMap.get(jim).size()); Object msg = msgMap.get(jim).get(0); assertTrue("it's a CashPosition", msg instanceof CashPosition); assertEquals("no balance", 0.0, ((CashPosition) msg).getBalance(), 1e-6); // should be 3 market transactions, and cash and 3 mkt positions for bob List bobMsgs = msgMap.get(bob); assertEquals("seven messages", 7, bobMsgs.size()); Object obj = findFirst(bobMsgs, new Predicate<Object>() { public boolean apply(Object item) { Timeslot ts5 = timeslotRepo.findBySerialNumber(1); return (item instanceof MarketTransaction && ((MarketTransaction) item).getBroker() == bob && ((MarketTransaction) item).getTimeslot() == ts5); } }); MarketTransaction mtx1 = (MarketTransaction) obj; assertNotNull("found 1st tx", mtx1); assertEquals("correct quantity", 0.6, mtx1.getMWh(), 1e-6); obj = findFirst(bobMsgs, new Predicate<Object>() { public boolean apply(Object item) { Timeslot ts5 = timeslotRepo.findBySerialNumber(2); return (item instanceof MarketPosition && ((MarketPosition) item).getBroker() == bob && ((MarketPosition) item).getTimeslot() == ts5); } }); MarketPosition mp1 = (MarketPosition) obj; assertEquals("correct balance for ts5", 0.5, mp1.getOverallBalance(), 1e-6); obj = findFirst(bobMsgs, new Predicate<Object>() { public boolean apply(Object item) { return (item instanceof CashPosition && ((CashPosition) item).getBroker() == bob); } }); CashPosition cp1 = (CashPosition) obj; assertNotNull("non-null CashPosition", cp1); assertEquals("correct cash position", -55.0 * 0.6, cp1.getBalance(), 1e-6); } // test activation @SuppressWarnings({ "rawtypes", "unchecked" }) @Test public void testActivate() { initializeService(); // market transactions accountingService.addMarketTransaction(bob, timeslotRepo.findBySerialNumber(1), 0.6, -55.0); accountingService.addMarketTransaction(bob, timeslotRepo.findBySerialNumber(2), 0.5, -45.0); accountingService.addMarketTransaction(bob, timeslotRepo.findBySerialNumber(2), 0.3, -31.0); accountingService.addMarketTransaction(bob, timeslotRepo.findBySerialNumber(3), 0.7, -43.0); accountingService.addMarketTransaction(jim, timeslotRepo.findBySerialNumber(2), 0.4, -35.0); accountingService.addMarketTransaction(jim, timeslotRepo.findBySerialNumber(2), -0.2, 20.0); // tariff transactions accountingService.addTariffTransaction(TariffTransaction.Type.CONSUME, tariffB1, customerInfo1, 7, 77.0, 7.7); accountingService.addTariffTransaction(TariffTransaction.Type.CONSUME, tariffB1, customerInfo2, 6, 83.0, 8.0); accountingService.addTariffTransaction(TariffTransaction.Type.PRODUCE, tariffB2, customerInfo3, 3, -55.0, -4.5); accountingService.addTariffTransaction(TariffTransaction.Type.CONSUME, tariffJ1, customerInfo2, 12, 120.0, 8.4); assertEquals("correct number in list", 10, accountingService.getPendingTransactions().size()); // activate, gather messages, check cash and market positions final Map<Broker, List> msgMap = new HashMap<Broker, List>(); doAnswer(new Answer() { public Object answer(InvocationOnMock invocation) { Object[] args = invocation.getArguments(); msgMap.put((Broker) args[0], (List) args[1]); return null; } }).when(mockProxy).sendMessages(isA(Broker.class), anyList()); accountingService.activate(timeService.getCurrentTime(), 3); verify(mockProxy, times(2)).sendMessages(isA(Broker.class), anyList()); assertEquals("correct cash balance, Bob", (-55.0 * 0.6 + 7.7 + 8.0 - 4.5), bob.getCashBalance(), 1e-6); assertEquals("correct cash balance, Jim", 8.4, jim.getCashBalance(), 1e-6); List<Object> bobMkts = filter(msgMap.get(bob), new Predicate<Object>() { public boolean apply(Object item) { return (item instanceof MarketPosition); } }); assertEquals("Bob has 3 mkt positions", 3, bobMkts.size()); Object mkt = findFirst(bobMkts, new Predicate<Object>() { public boolean apply(Object thing) { Timeslot ts5 = timeslotRepo.findBySerialNumber(1); return (((MarketPosition) thing).getTimeslot() == ts5); } }); assertNotNull("found market position b5", mkt); assertEquals("correct mkt position, Bob, ts5", 0.6, ((MarketPosition) mkt).getOverallBalance(), 1e-6); mkt = findFirst(bobMkts, new Predicate<Object>() { public boolean apply(Object thing) { Timeslot ts6 = timeslotRepo.findBySerialNumber(2); return (((MarketPosition) thing).getTimeslot() == ts6); } }); assertNotNull("found market position b6", mkt); assertEquals("correct mkt position, Bob, ts6", 0.8, ((MarketPosition) mkt).getOverallBalance(), 1e-6); List<Object> jimMkts = filter(msgMap.get(jim), new Predicate<Object>() { public boolean apply(Object item) { return (item instanceof MarketPosition); } }); assertEquals("Jim has 1 mkt position", 1, jimMkts.size()); mkt = jimMkts.get(0); assertNotNull("found market position j5", mkt); assertEquals("correct timeslot", timeslotRepo.findBySerialNumber(2), ((MarketPosition) mkt).getTimeslot()); assertEquals("correct mkt position, Jim, ts5", 0.2, ((MarketPosition) mkt).getOverallBalance(), 1e-6); // activate in the next timeslot, see that market transactions for ts 2 // are posted double bobCash1 = bob.getCashBalance(); msgMap.clear(); timeService.setCurrentTime(timeService.getCurrentTime().plus(TimeService.HOUR)); accountingService.activate(timeService.getCurrentTime(), 3); double bobCash2 = bob.getCashBalance(); assertEquals("bob's ts2 power deliveries posted", bobCash1 - 0.5 * 45.0 - 0.3 * 31.0, bobCash2, 1e-6); } // net market position only works after activation @Test public void testCurrentMarketPosition() { initializeService(); accountingService.addMarketTransaction(bob, timeslotRepo.findBySerialNumber(2), 0.5, -45.0); accountingService.addMarketTransaction(bob, timeslotRepo.findBySerialNumber(2), 0.3, -31.0); accountingService.addMarketTransaction(bob, timeslotRepo.findBySerialNumber(3), 0.7, -43.0); accountingService.addMarketTransaction(jim, timeslotRepo.findBySerialNumber(2), 0.4, -35.0); accountingService.addMarketTransaction(jim, timeslotRepo.findBySerialNumber(2), -0.2, 20.0); assertEquals("correct number in list", 5, accountingService.getPendingTransactions().size()); accountingService.activate(timeService.getCurrentTime(), 3); // current timeslot is 4, should be 0 mkt posn assertEquals("correct position, bob, ts4", 0.0, accountingService.getCurrentMarketPosition(bob), 1e-6); assertEquals("correct position, jim, ts4", 0.0, accountingService.getCurrentMarketPosition(jim), 1e-6); // move forward to timeslot 5 and try again timeService.setCurrentTime(timeService.getCurrentTime().plus(TimeService.HOUR)); assertEquals("correct position, bob, ts5", 0.8, accountingService.getCurrentMarketPosition(bob), 1e-6); assertEquals("correct position, jim, ts5", 0.2, accountingService.getCurrentMarketPosition(jim), 1e-6); // another hour and try again timeService.setCurrentTime(timeService.getCurrentTime().plus(TimeService.HOUR)); assertEquals("correct position, bob, ts5", 0.7, accountingService.getCurrentMarketPosition(bob), 1e-6); assertEquals("correct position, jim, ts5", 0.0, accountingService.getCurrentMarketPosition(jim), 1e-6); } // interest should be paid/charged at midnight activation @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void testInterestPayment() { initializeService(); //assertEquals("interest set by bootstrap", 0.10, accountingService.bankInterest) // set the interest to 12% accountingService.setBankInterest(0.12); // bob is in the hole bob.updateCash(-1000.0); // move to midnight, activate and check messages timeService.setCurrentTime(new DateTime(2011, 1, 27, 0, 0, 0, 0, DateTimeZone.UTC).toInstant()); final Map<Broker, List> msgMap = new HashMap<Broker, List>(); doAnswer(new Answer() { public Object answer(InvocationOnMock invocation) { Object[] args = invocation.getArguments(); msgMap.put((Broker) args[0], (List) args[1]); return null; } }).when(mockProxy).sendMessages(isA(Broker.class), anyList()); accountingService.activate(timeService.getCurrentTime(), 3); verify(mockProxy, times(2)).sendMessages(isA(Broker.class), anyList()); // should have bank tx and cash position for Bob assertEquals("two messages", 2, msgMap.get(bob).size()); Object btx1 = findFirst(msgMap.get(bob), new Predicate<Object>() { public boolean apply(Object thing) { return (thing instanceof BankTransaction); } }); assertNotNull("found bank tx", btx1); assertEquals("correct amount", -1000.0 * 0.12 / 365.0, ((BankTransaction) btx1).getAmount(), 1e-6); Object cp1 = findFirst(msgMap.get(bob), new Predicate<Object>() { public boolean apply(Object thing) { return (thing instanceof CashPosition); } }); assertNotNull("found cash posn", cp1); assertEquals("correct amount", -1000.0 * (1.0 + 0.12 / 365.0), ((CashPosition) cp1).getBalance(), 1e-6); } }