aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xtest.py903
-rw-r--r--test/add_reg_state1
-rw-r--r--test/state.json_2cars1
-rw-r--r--test/state.json_3users1
-rw-r--r--test/state.json_transactions11
5 files changed, 907 insertions, 0 deletions
diff --git a/test.py b/test.py
new file mode 100755
index 0000000..94235e1
--- /dev/null
+++ b/test.py
@@ -0,0 +1,903 @@
+#!/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
+
+alice, bob, charlie = "alice", "bob", "charlie"
+num = {alice: "+49123456", bob: "+49654321", charlie: "+49615243"}
+os.environ["GSB_GROUP_ID"] = "test"
+os.environ["GSB_STATE_FILE"] = "test/state.json"
+os.environ["GSB_SEND_CMD"] = "cat"
+os.environ["GSB_SEND_GROUP"] = "cat"
+os.environ["GSB_SEND_USER"] = "cat"
+os.environ["GSB_MODULES"] = "geldschiebing.py"
+
+now = datetime.now().date()
+
+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": []}}""")
+
+def run_bot(test, sender, cmd):
+ 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.stdout)
+ print(res.stderr)
+ test.assertEqual(res.returncode, 0)
+ test.assertEqual(res.stderr, "")
+ return res
+
+def save_state(dest):
+ copyfile(os.environ["GSB_STATE_FILE"], dest)
+
+def reset_state(state=None):
+ if state:
+ copyfile(state, os.environ["GSB_STATE_FILE"])
+ else:
+ state = os.environ["GSB_STATE_FILE"]
+ if os.path.isfile(state):
+ os.remove(state)
+
+def reset_state_string(string):
+ with open(os.environ["GSB_STATE_FILE"], "w") as f:
+ json.dump(json.loads(string), f)
+
+def compare_state(comp_state):
+ with open(comp_state, "r") as csf, \
+ open(os.environ["GSB_STATE_FILE"], "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.stdout, 'Happy geldschiebing {}!'.format(alice))
+
+ def test_double_reg(self):
+ res = run_bot(self, num[alice], "!reg "+alice)
+ self.assertEqual(res.stdout, 'Happy geldschiebing {}!'.format(alice))
+ res = run_bot(self, num[alice], "!reg "+alice)
+ self.assertEqual(res.stdout, 'ERROR: '+alice+' already registered')
+
+ def test_invalid_reg(self):
+ res = run_bot(self, num[alice], "!reg nase 03")
+ self.assertEqual(res.stdout, 'ERROR: not in form "!reg name"')
+
+ def test_additional_reg(self):
+ res = run_bot(self, num[alice], "!reg "+alice)
+ self.assertEqual(res.stdout, 'Happy geldschiebing {}!'.format(alice))
+ res = run_bot(self, num[bob], "!reg "+bob)
+ self.assertEqual(res.stdout, 'Happy geldschiebing {}!'.format(bob))
+ res = run_bot(self, num[charlie], "!reg "+charlie)
+ self.assertEqual(res.stdout, '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.stdout, 'New Balance: {} <- 10.00 {}\n'.format(alice, bob))
+
+ res = run_bot(self, num[bob], "!schieb 10 "+alice)
+ self.assertEqual(res.stdout, '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.stdout, 'New Balance: {} <- 10.00 {}\n'.format(alice, bob))
+
+ res = run_bot(self, num[bob], "!gib 10 "+alice)
+ self.assertEqual(res.stdout, '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.stdout, 'New Balance: {} <- 1.10 {}\n'.format(bob, alice))
+
+ res = run_bot(self, num[bob], "!gib 1,1 "+alice)
+ self.assertEqual(res.stdout, '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.stdout, 'ERROR: amount must be a positive number')
+
+ res = run_bot(self, num[bob], "!schieb ä€ "+alice)
+ self.assertEqual(res.stdout, 'ERROR: amount must be a positive number')
+
+ def test_correct_schieb_name_before_amount(self):
+ res = run_bot(self, num[alice], "!schieb "+bob+ " 10")
+ self.assertEqual(res.stdout, 'New Balance: {} <- 10.00 {}\n'.format(alice, bob))
+
+ res = run_bot(self, num[bob], "!schieb "+alice+ " 10")
+ self.assertEqual(res.stdout, '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.stdout, 'New Balance: {} <- 10.00 {}\n'.format(alice, charlie))
+
+ res = run_bot(self, num[charlie], "!gib "+alice+ " 10")
+ self.assertEqual(res.stdout, '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.stdout, 'New Balance: {} -> 10.00 {}\n'.format(alice, bob))
+
+ res = run_bot(self, num[bob], "!nimm 10 "+alice)
+ self.assertEqual(res.stdout, '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.stdout, 'New Balance: {} -> 10.00 {}\n'.format(alice, charlie))
+
+ res = run_bot(self, num[charlie], "!zieh "+alice+ " 10")
+ self.assertEqual(res.stdout, '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.stdout, 'New Balance: {} <- 1.10 {}\n'.format(alice, charlie))
+
+ res = run_bot(self, num[alice], "!zieh "+charlie+ " 2.1")
+ self.assertEqual(res.stdout, 'New Balance: {} -> 1.00 {}\n'.format(alice, charlie))
+
+ res = run_bot(self, num[charlie], "!schieb "+bob+ " 42")
+ self.assertEqual(res.stdout, 'New Balance: {} <- 42.00 {}\n'.format(charlie, bob))
+
+ res = run_bot(self, num[alice], "!zieh "+bob+ " 0.01")
+ self.assertEqual(res.stdout, '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.stdout, 'Summary:\nalice:\n\t-> bob 0.01\n\t-> charlie 1.00\n\tBalance: -1.01\n')
+
+ def test_summary_invalide_single_user(self):
+ reset_state()
+ res = run_bot(self, num[alice], "!sum "+alice)
+ self.assertEqual(res.stdout, 'ERROR: name "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.stdout, 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.stdout, summary)
+
+class TestMisc(unittest.TestCase):
+
+ def test_unknown_command(self):
+ res = run_bot(self, num[alice], "!foo")
+ self.assertEqual(res.stdout, "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.stdout, "")
+
+
+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.stdout, 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.stdout, 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.stdout, 'ERROR: you must register first')
+
+ def test_split_invalid_args(self):
+ res = run_bot(self, num[alice], "!split")
+ self.assertEqual(res.stdout, 'ERROR: not in form "!split amount [name]+"')
+
+ res = run_bot(self, num[alice], "!split 10")
+ self.assertEqual(res.stdout, 'ERROR: not in form "!split amount [name]+"')
+
+ res = run_bot(self, num[alice], "!split foo 10")
+ self.assertEqual(res.stdout, 'ERROR: amount must be a positive number')
+
+
+ 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.stdout, 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.stdout, 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.stdout, 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.stdout, o)
+
+ i = "!cars new bar 0.02"
+ res = run_bot(self, num[alice], i)
+ o = 'added "bar" as an available car'
+ self.assertEqual(res.stdout, 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 = "ERROR: service-charge must be a positive number"
+ self.assertEqual(res.stdout, o)
+
+ i = "!cars new bar -5"
+ res = run_bot(self, num[alice], i)
+ o = "ERROR: service-charge must be a positive number"
+ self.assertEqual(res.stdout, 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.stdout, o)
+
+ i = "!cars new alice 0.02"
+ res = run_bot(self, num[alice], i)
+ o = 'ERROR: A user named "alice" already exists. Please use a different name for this car'
+ self.assertEqual(res.stdout, 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.stdout, 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.stdout, 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.stdout, 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.stdout, 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.stdout, 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.stdout, 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.stdout, 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.stdout, 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.stdout, o)
+
+ def test_list_invalid_explicit_car(self):
+ i = "!cars ls alice"
+ res = run_bot(self, num[alice], i)
+ o = 'ERROR: "alice" is no available car\n'
+ self.assertEqual(res.stdout, 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.stdout, 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.stdout, 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.stdout, 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.stdout, 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.stdout, 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.stdout, 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.stdout, 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.stdout, 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.stdout, 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.stdout, 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.stdout, 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.stdout, 'ERROR: you must register first')
+
+ def test_fuck_nothing(self):
+ res = run_bot(self, num[alice], "!fuck")
+ self.assertEqual(res.stdout, '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.stdout, 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.stdout, 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.stdout, 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.stdout, 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 cmd "stuff\""""
+ self.assertEqual(res.stdout, 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.stdout, msg)
+
+ compare_state("test/state.json_schedule_weekly")
+
+ res = run_bot(self, num[alice], "!fuck")
+ self.assertEqual(res.stdout, '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.stdout, 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.stdout, 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 cmd "stuff\""""
+ self.assertEqual(res.stdout, 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.stdout, msg)
+
+ compare_state("test/state.json_schedule_monthly")
+
+ res = run_bot(self, num[alice], "!fuck")
+ self.assertEqual(res.stdout, '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.stdout, 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.stdout, msg)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/test/add_reg_state b/test/add_reg_state
new file mode 100644
index 0000000..94b5a54
--- /dev/null
+++ b/test/add_reg_state
@@ -0,0 +1 @@
+{"balance": {"alice": {"bob": 0, "charlie": 0}, "bob": {"alice": 0, "charlie": 0}, "charlie": {"alice": 0, "bob": 0}}, "name2num": {"alice": "+49123456", "bob": "+49654321", "charlie": "+49615243"}, "num2name": {"+49123456": "alice", "+49654321": "bob", "+49615243": "charlie"}, "cars": {}, "scheduled_cmds": {}, "changes": {"alice": [], "bob": [], "charlie": []}} \ No newline at end of file
diff --git a/test/state.json_2cars b/test/state.json_2cars
new file mode 100644
index 0000000..05091a9
--- /dev/null
+++ b/test/state.json_2cars
@@ -0,0 +1 @@
+{"balance": {"alice": {"bob": 0, "charlie": 0, "foo": 0, "bar": 0}, "bob": {"alice": 0, "charlie": 0, "foo": 0, "bar": 0}, "charlie": {"alice": 0, "bob": 0, "foo": 0, "bar": 0}, "foo": {"alice": 0, "bob": 0, "charlie": 0, "bar": 0}, "bar": {"alice": 0, "bob": 0, "charlie": 0, "foo": 0}}, "name2num": {"alice": "+49123456", "bob": "+49654321", "charlie": "+49615243"}, "num2name": {"+49123456": "alice", "+49654321": "bob", "+49615243": "charlie"}, "cars": {"foo": 4, "bar": 2}, "scheduled_cmds": {}, "changes": {"alice": [], "bob": [], "charlie": []}} \ No newline at end of file
diff --git a/test/state.json_3users b/test/state.json_3users
new file mode 100644
index 0000000..94b5a54
--- /dev/null
+++ b/test/state.json_3users
@@ -0,0 +1 @@
+{"balance": {"alice": {"bob": 0, "charlie": 0}, "bob": {"alice": 0, "charlie": 0}, "charlie": {"alice": 0, "bob": 0}}, "name2num": {"alice": "+49123456", "bob": "+49654321", "charlie": "+49615243"}, "num2name": {"+49123456": "alice", "+49654321": "bob", "+49615243": "charlie"}, "cars": {}, "scheduled_cmds": {}, "changes": {"alice": [], "bob": [], "charlie": []}} \ No newline at end of file
diff --git a/test/state.json_transactions1 b/test/state.json_transactions1
new file mode 100644
index 0000000..990f5fe
--- /dev/null
+++ b/test/state.json_transactions1
@@ -0,0 +1 @@
+{"balance": {"alice": {"bob": 1, "charlie": 100}, "bob": {"alice": -1, "charlie": 4200}, "charlie": {"alice": -100, "bob": -4200}}, "name2num": {"alice": "+49123456", "bob": "+49654321", "charlie": "+49615243"}, "num2name": {"+49123456": "alice", "+49654321": "bob", "+49615243": "charlie"}, "cars": {}, "scheduled_cmds": {}, "changes": {"alice": [[["!schieb", "charlie", "1,1"], ["alice", "charlie", 110]], [["!zieh", "charlie", "2.1"], ["alice", "charlie", -210]], [["!zieh", "bob", "0.01"], ["alice", "bob", -1]]], "bob": [], "charlie": [[["!schieb", "bob", "42"], ["charlie", "bob", 4200]]]}} \ No newline at end of file