#!/usr/bin/env python3 from datetime import datetime, date, timedelta import json import os from shutil import copyfile from string import Template import subprocess import unittest import geldschieberbot alice, bob, charlie = "alice", "bob", "charlie" num = {alice: "+49123456", bob: "+49654321", charlie: "+49615243"} now = datetime.now().date() STATE_PATH = "test/state.json" msg_template = Template(""" {"envelope": {"source":"$sender", "sourceDevice":2, "relay":null, "timestamp":1544101248419, "isReceipt":false, "dataMessage": {"timestamp":1544101248419, "message":"$msg", "expiresInSeconds":0, "attachments":[], "groupInfo": {"groupId":"test", "members":null, "name":null, "type":"DELIVER"} }, "syncMessage":null, "callMessage":null} }""".replace("\n", "")) scheduled_state_template = Template(""" {"balance": {"alice": {"bob": -100, "charlie": -100}, "bob": {"alice": 100, "charlie": 0}, "charlie": {"alice": 100, "bob": 0}}, "name2num": {"alice": "+49123456", "bob": "+49654321", "charlie": "+49615243"}, "num2name": {"+49123456": "alice", "+49654321": "bob", "+49615243": "charlie"}, "cars": {}, "scheduled_cmds": {"stuff": {"schedule": "$schedule", "last_time": "$last_time", "sender": "+49123456", "cmd": ["split", "3", "bob", "charlie"]}}, "changes": {"alice": [], "bob": [], "charlie": []}}""") METHOD_COUNTER = 0 BOT = None def run_bot(test, sender, cmd): global BOT if not BOT: env = {"GSB_STATE_FILE": STATE_PATH} BOT = subprocess.Popen("./geldschieberbot.py", stdout=subprocess.PIPE, stdin=subprocess.PIPE, text=True, env=env) global METHOD_COUNTER payload = { "method": "receive", "params": { "messageId": 0, "sender": sender, "message": cmd, "groupId": "test", }, "jsonrpc": "2.0", "id": METHOD_COUNTER, } METHOD_COUNTER += 1 if BOT.poll(): print("BOT died!") BOT = None print(json.dumps(payload), file=BOT.stdin, flush=True) response = json.loads(BOT.stdout.readline()) return "/n".join([result['message'] for result in response["result"]]) # res = geldschieberbot.receive(0, sender, message=cmd, timestamp=1544101248419, groupId='test') # msg = msg_template.substitute(sender=sender, msg=cmd).replace("\n", "\\n") + "\n" # res = subprocess.run(["python3", "./geldschieberbot.py"], text=True, capture_output=True, # # res = subprocess.run(["python3", "./bot.py"], text=True, capture_output=True, # input=msg) # if res.returncode != 0: # print(res) # print(res.stderr) # test.assertEqual(res.returncode, 0) # test.assertEqual(res.stderr, "") return res def save_state(dest): copyfile(STATE_PATH, dest) def reset_state(state=None): if state: copyfile(state, STATE_PATH) else: state = STATE_PATH if os.path.isfile(state): os.remove(state) def reset_state_string(string): with open(STATE_PATH, "w") as f: json.dump(json.loads(string), f) def compare_state(comp_state): with open(comp_state, "r") as csf, \ open(STATE_PATH, "r") as sf: cs = csf.read() s = sf.read() return cs == s class TestRegCmd(unittest.TestCase): def setUp(self): reset_state() def test_correct_reg(self): res = run_bot(self, num[alice], "!reg "+alice) self.assertEqual(res, 'Happy geldschiebing {}!'.format(alice)) def test_double_reg(self): res = run_bot(self, num[alice], f"!reg {alice}") self.assertEqual(res, f'Happy geldschiebing {alice}!') res = run_bot(self, num[alice], f"!reg {alice}") self.assertEqual(res, f'AlreadyRegisteredError: "{alice}" already registered') def test_invalid_reg(self): res = run_bot(self, num[alice], "!reg nase 03") self.assertEqual(res, 'InvalidArgumentError: "!reg nase 03" not in form "!reg name"') def test_additional_reg(self): res = run_bot(self, num[alice], "!reg "+alice) self.assertEqual(res, 'Happy geldschiebing {}!'.format(alice)) res = run_bot(self, num[bob], "!reg "+bob) self.assertEqual(res, 'Happy geldschiebing {}!'.format(bob)) res = run_bot(self, num[charlie], "!reg "+charlie) self.assertEqual(res, 'Happy geldschiebing {}!'.format(charlie)) self.assertTrue(compare_state("test/state.json_3users")) class TestTransactionCmd(unittest.TestCase): @classmethod def tearDownClass(cls): reset_state() def setUp(self): reset_state("test/state.json_3users") def test_correct_schieb(self): res = run_bot(self, num[alice], "!schieb 10 "+bob) self.assertEqual(res, 'New Balance: {} <- 10.00 {}\n'.format(alice, bob)) res = run_bot(self, num[bob], "!schieb 10 "+alice) self.assertEqual(res, 'New Balance: {} <- 0.00 {}\n'.format(bob, alice)) def test_correct_gib(self): res = run_bot(self, num[alice], "!gib 10 "+bob) self.assertEqual(res, 'New Balance: {} <- 10.00 {}\n'.format(alice, bob)) res = run_bot(self, num[bob], "!gib 10 "+alice) self.assertEqual(res, 'New Balance: {} <- 0.00 {}\n'.format(bob, alice)) def test_correct_amount(self): res = run_bot(self, num[bob], "!schieb 1.1 "+alice) self.assertEqual(res, 'New Balance: {} <- 1.10 {}\n'.format(bob, alice)) res = run_bot(self, num[bob], "!gib 1,1 "+alice) self.assertEqual(res, 'New Balance: {} <- 2.20 {}\n'.format(bob, alice)) def test_invalid_amount(self): res = run_bot(self, num[bob], "!schieb 1b1 "+alice) self.assertEqual(res, 'InvalidAmountError: "1b1" not in form .') res = run_bot(self, num[bob], "!schieb ä€ "+alice) self.assertEqual(res, 'InvalidAmountError: "ä€" not in form .') def test_correct_schieb_name_before_amount(self): res = run_bot(self, num[alice], "!schieb "+bob+ " 10") self.assertEqual(res, 'New Balance: {} <- 10.00 {}\n'.format(alice, bob)) res = run_bot(self, num[bob], "!schieb "+alice+ " 10") self.assertEqual(res, 'New Balance: {} <- 0.00 {}\n'.format(bob, alice)) def test_correct_gib_name_before_amount(self): res = run_bot(self, num[alice], "!gib "+charlie+ " 10") self.assertEqual(res, 'New Balance: {} <- 10.00 {}\n'.format(alice, charlie)) res = run_bot(self, num[charlie], "!gib "+alice+ " 10") self.assertEqual(res, 'New Balance: {} <- 0.00 {}\n'.format(charlie, alice)) def test_correct_nimm(self): res = run_bot(self, num[alice], "!nimm 10 "+bob) self.assertEqual(res, 'New Balance: {} -> 10.00 {}\n'.format(alice, bob)) res = run_bot(self, num[bob], "!nimm 10 "+alice) self.assertEqual(res, 'New Balance: {} <- 0.00 {}\n'.format(bob, alice)) def test_correct_zieh_name_before_amount(self): res = run_bot(self, num[alice], "!zieh "+charlie+ " 10") self.assertEqual(res, 'New Balance: {} -> 10.00 {}\n'.format(alice, charlie)) res = run_bot(self, num[charlie], "!zieh "+alice+ " 10") self.assertEqual(res, 'New Balance: {} <- 0.00 {}\n'.format(charlie, alice)) def test_transactions_complex(self): res = run_bot(self, num[alice], "!schieb "+charlie+ " 1,1") self.assertEqual(res, 'New Balance: {} <- 1.10 {}\n'.format(alice, charlie)) res = run_bot(self, num[alice], "!zieh "+charlie+ " 2.1") self.assertEqual(res, 'New Balance: {} -> 1.00 {}\n'.format(alice, charlie)) res = run_bot(self, num[charlie], "!schieb "+bob+ " 42") self.assertEqual(res, 'New Balance: {} <- 42.00 {}\n'.format(charlie, bob)) res = run_bot(self, num[alice], "!zieh "+bob+ " 0.01") self.assertEqual(res, 'New Balance: {} -> 0.01 {}\n'.format(alice, bob)) compare_state("test/state.json_transactions1") class TestSumCmd(unittest.TestCase): def test_summary_single_user(self): reset_state("test/state.json_transactions1") res = run_bot(self, num[alice], "!sum "+alice) self.assertEqual(res, 'Summary:\nalice:\n\t-> bob 0.01\n\t-> charlie 1.00\n\tBalance: -1.01\n') def test_summary_invalid_single_user(self): reset_state() res = run_bot(self, num[alice], "!sum "+alice) self.assertEqual(res, 'NotRegisteredError: "alice" not registered') def test_summary_double_user(self): reset_state("test/state.json_transactions1") res = run_bot(self, num[alice], "!sum {} {}".format(alice, bob)) summary = \ """Summary: alice: \t-> bob 0.01 \t-> charlie 1.00 \tBalance: -1.01 bob: \t<- alice 0.01 \t-> charlie 42.00 \tBalance: -41.99 """ self.assertEqual(res, summary) def test_summary(self): reset_state("test/state.json_transactions1") res = run_bot(self, num[alice], "!sum") summary = \ """Summary: alice: \t-> bob 0.01 \t-> charlie 1.00 \tBalance: -1.01 bob: \t<- alice 0.01 \t-> charlie 42.00 \tBalance: -41.99 charlie: \t<- alice 1.00 \t<- bob 42.00 \tBalance: 43.00""" self.assertEqual(res, summary) class TestMisc(unittest.TestCase): def test_unknown_command(self): res = run_bot(self, num[alice], "!foo") self.assertEqual(res, "ERROR: unknown cmd. Enter !help for a list of commands.") def test_no_command(self): res = run_bot(self, num[alice], "Hi, how are you?") self.assertEqual(res, "") class TestListCmd(unittest.TestCase): def setUp(self): reset_state("test/state.json_3users") def test_ls(self): res = run_bot(self, num[alice], "!ls") msg = "alice: {}\nbob: {}\ncharlie: {}\n".format(num[alice], num[bob], num[charlie]) self.assertEqual(res, msg) def test_list(self): res = run_bot(self, num[bob], "!list") msg = "alice: {}\nbob: {}\ncharlie: {}\n".format(num[alice], num[bob], num[charlie]) self.assertEqual(res, msg) class TestSplitCmd(unittest.TestCase): def setUp(self): reset_state("test/state.json_3users") def test_split_unregistered(self): res = run_bot(self, "+4971576357", "!split") self.assertEqual(res, 'NotRegisteredError: please register first using !register') def test_split_invalid_args(self): res = run_bot(self, num[alice], "!split") self.assertEqual(res, 'InvalidArgumentError: "!split" not in form "!split amount [name]+"') res = run_bot(self, num[alice], "!split 10") self.assertEqual(res, 'InvalidArgumentError: "!split 10" not in form "!split amount [name]+"') res = run_bot(self, num[alice], "!split foo 10") self.assertEqual(res, 'InvalidAmountError: "foo" not in form .') def test_split_one_unknown_user(self): res = run_bot(self, num[alice], "!split 30 " + "foo" + " " + charlie) msg = \ """Split 30.00 between 3 -> 10.00 each foo not known. Please take care manually New Balance: alice: \t<- charlie 10.00 \tBalance: 10.00""" self.assertEqual(res, msg) def test_split(self): res = run_bot(self, num[alice], "!split 30 " + bob + " " + charlie) msg = \ """Split 30.00 between 3 -> 10.00 each New Balance: alice: \t<- bob 10.00 \t<- charlie 10.00 \tBalance: 20.00""" self.assertEqual(res, msg) def test_split_whitespace(self): res = run_bot(self, num[alice], "!split 30 " + bob + " " + charlie + " ") msg = \ """Split 30.00 between 3 -> 10.00 each New Balance: alice: \t<- bob 10.00 \t<- charlie 10.00 \tBalance: 20.00""" self.assertEqual(res, msg) class TestCarsAddCmd(unittest.TestCase): def setUp(self): reset_state("test/state.json_3users") def test_add_success(self): i = "!cars add foo 0.04" res = run_bot(self, num[alice], i) o = 'added "foo" as an available car' self.assertEqual(res, o) i = "!cars new bar 0.02" res = run_bot(self, num[alice], i) o = 'added "bar" as an available car' self.assertEqual(res, o) save_state("test/state.json_2cars") def test_add_invalid_service_charge(self): i = "!cars add foo 0.04hut" res = run_bot(self, num[alice], i) o = "InvalidAmountError: service-charge (0.04hut) must be a positive number" self.assertEqual(res, o) i = "!cars new bar -5" res = run_bot(self, num[alice], i) o = "InvalidAmountError: service-charge (-5) must be a positive number" self.assertEqual(res, o) def test_add_name_conflict(self): i = "!cars add foo 0.04" res = run_bot(self, num[alice], i) o = 'added "foo" as an available car' self.assertEqual(res, o) i = "!cars new alice 0.02" res = run_bot(self, num[alice], i) o = 'AlreadyRegisteredError: A user named "alice" already exists. Please use a different name for this car' self.assertEqual(res, o) class TestCarsTransactions(unittest.TestCase): def setUp(self): reset_state("test/state.json_2cars") def test_schieb_car(self): i = "!schieb foo 20" res = run_bot(self, num[alice], i) o = "New Balance: alice <- 20.00 foo\n" self.assertEqual(res, o) class TestCarPayCmd(unittest.TestCase): def setUp(self): reset_state("test/state.json_2cars") def test_alice_pays_exact(self): run_bot(self, num[bob], "!zieh foo 20") run_bot(self, num[charlie], "!zieh foo 10") i = "!cars pay foo 30" res = run_bot(self, num[alice], i) o = \ """alice payed 30.00 Transferring 100.00% of everybody's charges Transfere 20.00 from bob to alice Transfere 10.00 from charlie to alice New Balances: alice: \t<- bob 20.00 \t<- charlie 10.00 \tBalance: 30.00 foo: \tAll fine :)""" self.assertEqual(res, o) def test_alice_pays_more(self): run_bot(self, num[bob], "!zieh foo 20") run_bot(self, num[charlie], "!zieh foo 10") i = "!cars pay foo 40" res = run_bot(self, num[alice], i) o = \ """alice payed 40.00 Transferring 100.00% of everybody's charges Transfere 20.00 from bob to alice Transfere 10.00 from charlie to alice New Balances: alice: \t<- bob 20.00 \t<- charlie 10.00 \tBalance: 30.00 \tCars: \t<- foo 10.00 \tLiability: 10.00 foo: \t-> alice 10.00 \tBalance: -10.00""" self.assertEqual(res, o) def test_alice_pays_half(self): run_bot(self, num[bob], "!zieh foo 20") run_bot(self, num[charlie], "!zieh foo 10") i = "!cars pay foo 15" res = run_bot(self, num[alice], i) o = \ """alice payed 15.00 Transferring 50.00% of everybody's charges Transfere 10.00 from bob to alice Transfere 5.00 from charlie to alice New Balances: alice: \t<- bob 10.00 \t<- charlie 5.00 \tBalance: 15.00 foo: \t<- bob 10.00 \t<- charlie 5.00 \tBalance: 15.00""" self.assertEqual(res, o) class TestCarsListCmd(unittest.TestCase): def setUp(self): reset_state("test/state.json_2cars") def test_no_cars(self): reset_state() i = "!cars" res = run_bot(self, num[alice], i) o = "No cars registered yet." self.assertEqual(res, o) def test_implicit_call(self): i = "!cars" res = run_bot(self, num[alice], i) o = \ """foo - service charge 4ct/km foo: \tAll fine :) bar - service charge 2ct/km bar: \tAll fine :)""" self.assertEqual(res, o) def test_list_all(self): i = "!cars ls" res = run_bot(self, num[alice], i) o = \ """foo - service charge 4ct/km foo: \tAll fine :) bar - service charge 2ct/km bar: \tAll fine :)""" self.assertEqual(res, o) i = "!cars list" res = run_bot(self, num[alice], i) o = \ """foo - service charge 4ct/km foo: \tAll fine :) bar - service charge 2ct/km bar: \tAll fine :)""" self.assertEqual(res, o) def test_list_explicit_car(self): i = "!cars ls foo" res = run_bot(self, num[alice], i) o = \ """foo - service charge 4ct/km foo: \tAll fine :)""" self.assertEqual(res, o) def test_list_invalid_explicit_car(self): i = "!cars ls alice" res = run_bot(self, num[alice], i) o = 'NotRegisteredError: "alice" is no available car\n' self.assertEqual(res, o) class TestTankenCmd(unittest.TestCase): def setUp(self): reset_state("test/state.json_3users") def test_tanken_3users(self): i = \ """!tanken 10 alice 20 alice bob 10 charlie""" res = run_bot(self, num[alice], i) o = \ """alice: 20km = fuel: 3.33, service charge: 0.00 bob: 20km = fuel: 3.33, service charge: 0.00 charlie: 10km = fuel: 3.33, service charge: 0.00 New Balance: alice: \t<- bob 3.33 \t<- charlie 3.33 \tBalance: 6.66""" self.assertEqual(res, o) def test_tanken_unknown_user(self): i = \ """!tanken 10 alice 20 alice dieter 10 charlie""" res = run_bot(self, num[alice], i) o = \ """alice: 20km = fuel: 3.33, service charge: 0.00 dieter: 20km = fuel: 3.33, service charge: 0.00 dieter not known. Please collect fuel cost manually charlie: 10km = fuel: 3.33, service charge: 0.00 New Balance: alice: \t<- charlie 3.33 \tBalance: 3.33""" self.assertEqual(res, o) def test_tanken_3users_with_car(self): i = "!cars add foo 0.04" res = run_bot(self, num[alice], i) o = 'added "foo" as an available car' self.assertEqual(res, o) i = \ """!tanken 10 alice foo 20 alice bob 10 charlie""" res = run_bot(self, num[alice], i) o = \ """alice: 20km = fuel: 3.33, service charge: 0.40 bob: 20km = fuel: 3.33, service charge: 0.40 charlie: 10km = fuel: 3.33, service charge: 0.40 New Balance: alice: \t<- bob 3.33 \t<- charlie 3.33 \tBalance: 6.66 \tCars: \t-> foo 0.40 \tLiability: -0.40 Car foo: \t<- alice 0.40 \t<- bob 0.40 \t<- charlie 0.40 \tBalance: 1.20""" self.assertEqual(res, o) def test_tanken_unknown_user_with_car(self): i = "!cars add foo 0.04" res = run_bot(self, num[alice], i) o = 'added "foo" as an available car' self.assertEqual(res, o) i = \ """!tanken 10 alice foo 20 alice bob 10 dieter""" res = run_bot(self, num[alice], i) o = \ """alice: 20km = fuel: 3.33, service charge: 0.40 bob: 20km = fuel: 3.33, service charge: 0.40 dieter: 10km = fuel: 3.33, service charge: 0.40 dieter not known. alice held accountable for service charge. Please collect fuel cost manually New Balance: alice: \t<- bob 3.33 \tBalance: 3.33 \tCars: \t-> foo 0.80 \tLiability: -0.80 Car foo: \t<- alice 0.80 \t<- bob 0.40 \tBalance: 1.20""" self.assertEqual(res, o) def test_tanken_3users_with_car(self): i = "!cars add foo 0.04" res = run_bot(self, num[alice], i) o = 'added "foo" as an available car' self.assertEqual(res, o) i = \ """!tanken 10 alice foo 20 alice bob 10 charlie""" res = run_bot(self, num[alice], i) o = \ """alice: 20km = fuel: 3.33, service charge: 0.40 bob: 20km = fuel: 3.33, service charge: 0.40 charlie: 10km = fuel: 3.33, service charge: 0.40 New Balance: alice: \t<- bob 3.33 \t<- charlie 3.33 \tBalance: 6.66 \tCars: \t-> foo 0.40 \tLiability: -0.40 Car foo: \t<- alice 0.40 \t<- bob 0.40 \t<- charlie 0.40 \tBalance: 1.20""" self.assertEqual(res, o) class TestTransferCmd(unittest.TestCase): def setUp(self): reset_state("test/state.json_3users") def test_transfer(self): i = '!transfer 5 bob charlie' res = run_bot(self, num[alice], i) o = \ """New Balance: alice -> 5.00 bob New Balance: alice <- 5.00 charlie New Balance: bob -> 5.00 charlie """ self.assertEqual(res, o) def test_transfer_credit(self): res = run_bot(self, num[alice], "!schieb bob 5") i = '!transfer 5 bob charlie' res = run_bot(self, num[alice], i) o = \ """New Balance: alice <- 0.00 bob New Balance: alice <- 5.00 charlie New Balance: bob -> 5.00 charlie """ self.assertEqual(res, o) def test_transfer_change_dept(self): res = run_bot(self, num[alice], "!schieb bob 5") i = '!transfer 5 bob charlie' res = run_bot(self, num[alice], i) o = \ """New Balance: alice <- 0.00 bob New Balance: alice <- 5.00 charlie New Balance: bob -> 5.00 charlie """ self.assertEqual(res, o) #TODO: tanken, transfer, cars pay class TestFuckCmd(unittest.TestCase): def setUp(self): reset_state("test/state.json_3users") def test_fuck_unregistered(self): res = run_bot(self, "+4971576357", "!fuck") self.assertEqual(res, 'NotRegisteredError: please register first using !register') def test_fuck_nothing(self): res = run_bot(self, num[alice], "!fuck") self.assertEqual(res, 'Nothing to rewind') def test_fuck_transaction(self): for cmd in ["fuck", "undo", "rewind"]: run_bot(self, num[alice], f"!schieb {bob} 10") res = run_bot(self, num[alice], f"!{cmd}") msg = \ """alice: sorry I fucked up! Rewinding: !schieb bob 10 alice <- 10.00 bob """ self.assertEqual(res, msg) compare_state("test/state.json_3users") def test_fuck_split(self): run_bot(self, num[alice], "!split 3 bob charlie") res = run_bot(self, num[alice], "!fuck") msg = \ """alice: sorry I fucked up! Rewinding: !split 3 bob charlie alice <- 1.00 bob alice <- 1.00 charlie """ self.assertEqual(res, msg) compare_state("test/state.json_3users") def test_fuck_transaction(self): run_bot(self, num[alice], "!schieb "+bob+ " 10") res = run_bot(self, num[alice], "!fuck") msg = \ """alice: sorry I fucked up! Rewinding: !schieb bob 10 alice <- 10.00 bob """ self.assertEqual(res, msg) compare_state("test/state.json_3users") class TestScheduleCmd(unittest.TestCase): def setUp(self): reset_state("test/state.json_3users") def test_weekly(self): res = run_bot(self, num[alice], "!weekly stuff split 3 bob charlie") msg = \ """Recorded the weekly command "split 3 bob charlie" as "stuff" Running weekly command stuff for alice initially Split 3.00 between 3 -> 1.00 each New Balance: alice: \t<- bob 1.00 \t<- charlie 1.00 \tBalance: 2.00""" self.assertEqual(res, msg) save_state("test/state.json_schedule_weekly") res = run_bot(self, num[alice], "!fuck") msg = \ """alice: sorry I fucked up! Rewinding: split 3 bob charlie alice <- 1.00 bob alice <- 1.00 charlie Cancelled the weekly command "stuff\"""" self.assertEqual(res, msg) # Last exec onw week ago reset_state_string(scheduled_state_template.substitute(last_time=now-timedelta(7),schedule="weekly")) res = run_bot(self, num[alice], "!fuck") # There is no new line because the real bot uses two Messages msg = \ """Nothing to rewindRunning weekly command stuff for alice triggered on {} Split 3.00 between 3 -> 1.00 each New Balance: alice: \t<- bob 2.00 \t<- charlie 2.00 \tBalance: 4.00""".format(now.isoformat()) self.assertEqual(res, msg) compare_state("test/state.json_schedule_weekly") res = run_bot(self, num[alice], "!fuck") self.assertEqual(res, 'Nothing to rewind') # Last exec two week ago reset_state_string(scheduled_state_template.substitute(last_time=now-timedelta(14),schedule="weekly")) res = run_bot(self, num[alice], "!fuck") # There is no new line because the real bot uses two Messages msg = \ """Nothing to rewindRunning weekly command stuff for alice triggered on {} Split 3.00 between 3 -> 1.00 each New Balance: alice: \t<- bob 2.00 \t<- charlie 2.00 \tBalance: 4.00Running weekly command stuff for alice triggered on {} Split 3.00 between 3 -> 1.00 each New Balance: alice: \t<- bob 3.00 \t<- charlie 3.00 \tBalance: 6.00""".format((now - timedelta(7)).isoformat(), now.isoformat()) self.assertEqual(res, msg) os.remove("test/state.json_schedule_weekly") def test_monthly(self): self.maxDiff = None res = run_bot(self, num[alice], "!monthly stuff split 3 bob charlie") msg = \ """Recorded the monthly command "split 3 bob charlie" as "stuff" Running monthly command stuff for alice initially Split 3.00 between 3 -> 1.00 each New Balance: alice: \t<- bob 1.00 \t<- charlie 1.00 \tBalance: 2.00""" self.assertEqual(res, msg) save_state("test/state.json_schedule_monthly") res = run_bot(self, num[alice], "!fuck") msg = \ """alice: sorry I fucked up! Rewinding: split 3 bob charlie alice <- 1.00 bob alice <- 1.00 charlie Cancelled the monthly command "stuff\"""" self.assertEqual(res, msg) # Last exec one month ago if now.month > 1: one_month_ago = date(now.year, now.month-1, now.day) else: one_month_ago = date(now.year-1, 12, now.day) reset_state_string(scheduled_state_template.substitute(last_time=one_month_ago, schedule="monthly")) res = run_bot(self, num[alice], "!fuck") # There is no new line because the real bot uses two Messages msg = \ """Nothing to rewindRunning monthly command stuff for alice triggered on {} Split 3.00 between 3 -> 1.00 each New Balance: alice: \t<- bob 2.00 \t<- charlie 2.00 \tBalance: 4.00""".format(now.isoformat()) self.assertEqual(res, msg) compare_state("test/state.json_schedule_monthly") res = run_bot(self, num[alice], "!fuck") self.assertEqual(res, 'Nothing to rewind') # Last exec two month ago if now.month > 2: two_months_ago = date(now.year, now.month-2, now.day) else: two_months_ago = date(now.year-1, 12-now.month+1, now.day) reset_state_string(scheduled_state_template.substitute(last_time=two_months_ago, schedule="monthly")) res = run_bot(self, num[alice], "!fuck") # There is no new line because the real bot uses to Messages msg = \ """Nothing to rewindRunning monthly command stuff for alice triggered on {} Split 3.00 between 3 -> 1.00 each New Balance: alice: \t<- bob 2.00 \t<- charlie 2.00 \tBalance: 4.00Running monthly command stuff for alice triggered on {} Split 3.00 between 3 -> 1.00 each New Balance: alice: \t<- bob 3.00 \t<- charlie 3.00 \tBalance: 6.00""".format(one_month_ago.isoformat(), now.isoformat()) self.assertEqual(res, msg) os.remove("test/state.json_schedule_monthly") def test_monthly_car(self): run_bot(self, num[alice], "!cars add fiat 0.5") res = run_bot(self, num[alice], "!monthly versicherung cars pay fiat 3") msg = \ """Recorded the monthly command "cars pay fiat 3" as "versicherung" Running monthly command versicherung for alice initially alice payed 3.00 Transferring 100.00% of everybody's charges New Balances: alice: \tAll fine :) \tCars: \t<- fiat 3.00 \tLiability: 3.00 fiat: \t-> alice 3.00 \tBalance: -3.00""" self.assertEqual(res, msg) def test_cancel_not_allowed(self): run_bot(self, num[alice], "!cars add fiat 0.5") run_bot(self, num[alice], "!monthly versicherung cars pay fiat 3") res = run_bot(self, num[bob], "!cancel versicherung") msg = "NotAllowedError: only alice, the original creator can cancel this command" self.assertEqual(res, msg) def test_cancel_not_registered(self): res = run_bot(self, "+490000", "!cancel versicherung") msg = "NotRegisteredError: please register first using !register" self.assertEqual(res, msg) def test_cancel_not_scheduled_cmd(self): res = run_bot(self, num[alice], "!cancel versicherung") msg = 'NotRegisteredError: "versicherung" is not a scheduled command' self.assertEqual(res, msg) def test_cancel(self): run_bot(self, num[alice], "!cars add fiat 0.5") run_bot(self, num[alice], "!monthly versicherung cars pay fiat 3") res = run_bot(self, num[alice], "!cancel versicherung") msg = 'Cancelled the monthly command "versicherung"' self.assertEqual(res, msg) if __name__ == '__main__': unittest.main() BOT.terminate()