aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--geldschieberbot.py153
-rwxr-xr-xtest.py243
2 files changed, 244 insertions, 152 deletions
diff --git a/geldschieberbot.py b/geldschieberbot.py
index 3791bd1..33fee20 100644
--- a/geldschieberbot.py
+++ b/geldschieberbot.py
@@ -7,7 +7,6 @@ import subprocess
import sys
import tanken
-
"""Path where our data is stored persistent on disk"""
state_file = os.environ["GSB_STATE_FILE"]
@@ -21,12 +20,12 @@ else:
scheduled_cmds - dict associating names to cmds, their schedule, and the last execution
changes - dict associating users with their changes"""
state = {
- "balance" : {},
- "name2num" : {},
- "num2name" : {},
- "cars" : {},
- "scheduled_cmds" : {},
- "changes" : {},
+ "balance": {},
+ "name2num": {},
+ "num2name": {},
+ "cars": {},
+ "scheduled_cmds": {},
+ "changes": {},
}
# check if a legacy file layout is present
@@ -45,7 +44,7 @@ else:
for num in state["changes"]:
name = state["num2name"][num]
state["changes"][name] = state["changes"][num]
- del(state["changes"][num])
+ del (state["changes"][num])
balance = state["balance"]
name2num = state["name2num"]
@@ -58,16 +57,14 @@ group_id = os.environ["GSB_GROUP_ID"]
send_cmd = os.environ["GSB_SEND_CMD"]
group_send_cmd = send_cmd + group_id
-
"""Run without changing the stored state"""
dry_run = False
-
"""Run without sending messages"""
quiet = False
-
"""Should changes be recorded"""
record_changes = True
+
def record(recipient, donor, amount):
"""Apply changes to the balance"""
@@ -78,6 +75,7 @@ def record(recipient, donor, amount):
balance[donor][recipient] += amount
balance[recipient][donor] -= amount
+
def to_cent(euro):
if '.' in euro:
euro = euro.split('.')
@@ -98,19 +96,22 @@ def to_cent(euro):
raise ValueError
return amount
+
def to_euro(cents):
return f"{cents/100:.2f}"
+
def send(msg):
if not quiet:
subprocess.run(send_cmd.split(' '), input=msg.encode())
+
def create_summary(user):
summary = ""
cars_summary = ""
total = 0
cars_total = 0
- p_balances = balance[user] # failes if user is not in balance
+ p_balances = balance[user] # failes if user is not in balance
for person in p_balances:
amount = p_balances[person]
if amount == 0:
@@ -133,7 +134,8 @@ def create_summary(user):
cars_summary += f'\tLiability: {to_euro(cars_total)}'
ret_summary += f'\n\tCars:\n{cars_summary}'
- return ret_summary
+ return ret_summary
+
def create_total_summary():
summary = "Summary:"
@@ -150,12 +152,14 @@ def create_total_summary():
summary += f'\nCars:{cars_summary}'
return summary
+
def create_members():
r = ""
for m in name2num:
r += m + ": " + name2num[m] + "\n"
return r
+
def add_to_balance(name):
nb = {}
for m in balance:
@@ -163,6 +167,7 @@ def add_to_balance(name):
nb[m] = 0
balance[name] = nb
+
def create_help():
return """
Usage: send a message starting with '!' followed by a command
@@ -201,8 +206,10 @@ cancel name - stop repeating cmd
Happy Geldschieben!
"""
+
cmds = {}
+
def register(sender, args, msg):
if len(args) != 2:
return None, f'not in form "{args[0]} name"'
@@ -229,9 +236,11 @@ def register(sender, args, msg):
changes[name] = []
return f"Happy geldschiebing {name}!", None
+
cmds["reg"] = register
cmds["register"] = register
+
def summary(sender, args, msg):
if len(args) == 1:
return create_total_summary(), None
@@ -245,21 +254,27 @@ def summary(sender, args, msg):
err = f'name "{name}" not registered'
return msg, err
+
cmds["sum"] = summary
cmds["summary"] = summary
+
def list_users(sender, args, msg):
return create_members(), None
+
cmds["ls"] = list_users
cmds["list"] = list_users
+
def usage(sender, args, msg):
return create_help(), None
+
cmds["help"] = usage
cmds["usage"] = usage
+
def split(sender, args, msg):
if not sender in num2name:
return None, 'you must register first'
@@ -283,7 +298,7 @@ def split(sender, args, msg):
# persons + sender
npersons = len(persons) + 1
- amount_per_person = int(amount/npersons)
+ amount_per_person = int(amount / npersons)
if sender in num2name:
recipient = num2name[sender]
@@ -309,9 +324,11 @@ def split(sender, args, msg):
output += create_summary(recipient)
return output, None
+
cmds["split"] = split
cmds["teil"] = split
+
def transaction(sender, args, msg):
if len(args) != 3:
return None, f'not in form "{args[0]} amount recipient"'
@@ -346,17 +363,18 @@ def transaction(sender, args, msg):
p_balance = balance[sender][recipient]
- output = ("New Balance: {} {} {} {}\n".format(sender,
- ("->" if p_balance > 0 else "<-"),
- to_euro(abs(p_balance)),
- recipient))
+ output = ("New Balance: {} {} {} {}\n".format(
+ sender, ("->" if p_balance > 0 else "<-"), to_euro(abs(p_balance)),
+ recipient))
return output, None
+
cmds["schieb"] = transaction
cmds["gib"] = transaction
cmds["zieh"] = transaction
cmds["nimm"] = transaction
+
def transfer(sender, args, msg):
if len(args) < 4:
return None, f'not in form "{args[0]} amount source destination"'
@@ -403,7 +421,7 @@ def transfer(sender, args, msg):
# Sender -> X Destination
change.append((sender, destination, amount_cent))
- ret, err = transaction(source, ["!zieh", destination, amount_raw], "")
+ ret, err = transaction(source, ["!zieh", destination, amount_raw], "")
if err:
output += err + "\nThe balance may be in a inconsistent state please take care manually"
return output, None
@@ -418,8 +436,10 @@ def transfer(sender, args, msg):
return output, None
+
cmds["transfer"] = transfer
+
def cars(sender, args, msg):
# list cars
if len(args) < 2 or args[1] in ["ls", "list"]:
@@ -427,7 +447,7 @@ def cars(sender, args, msg):
return "No cars registered yet.", None
ret_msg = ""
-
+
if len(args) > 2:
cars_to_list = args[2:]
else:
@@ -501,7 +521,7 @@ def cars(sender, args, msg):
proportion = -1 * (amount / total_available_charge)
_, err = transaction(sender, f"!gib {car} {amount_euro}".split(), "")
- assert(err is None)
+ assert (err is None)
output += f"{sender_name} payed {amount_euro}\n"
# transfer money
@@ -512,10 +532,12 @@ def cars(sender, args, msg):
to_move = int(_amount * proportion)
to_move_euro = to_euro(to_move)
- ret, err = transfer(sender, ["transfer", to_move_euro, car, person], "")
- assert(err is None)
+ ret, err = transfer(sender,
+ ["transfer", to_move_euro, car, person], "")
+ assert (err is None)
- output += "Transfer {} from {} to {}\n".format(to_move_euro, person, sender_name)
+ output += "Transfer {} from {} to {}\n".format(
+ to_move_euro, person, sender_name)
output += "New Balances:\n"
output += create_summary(sender_name) + "\n"
@@ -529,11 +551,14 @@ def cars(sender, args, msg):
else:
return None, 'unknown car subcommand "{}".'.format(args[1])
+
cmds["cars"] = cars
+
def _tanken(sender, args, msg):
if len(args) < 2:
- return None, 'not in form "{} amount [person] [car] [info]"'.format(args[0])
+ return None, 'not in form "{} amount [person] [car] [info]"'.format(
+ args[0])
try:
amount = to_cent(args[1])
except:
@@ -566,7 +591,9 @@ def _tanken(sender, args, msg):
output = ""
change = [args]
for pname, values in parts.items():
- output += pname + ": {}km = fuel: {}, service charge: {}\n".format(values["distance"], to_euro(values["cost"]), to_euro(values["service_charge"]))
+ output += pname + ": {}km = fuel: {}, service charge: {}\n".format(
+ values["distance"], to_euro(values["cost"]),
+ to_euro(values["service_charge"]))
# record service charges
if pname not in name2num:
output += pname + " not known."
@@ -574,7 +601,8 @@ def _tanken(sender, args, msg):
person_to_charge = pname
if pname not in name2num:
person_to_charge = recipient
- output += " {} held accountable for service charge.".format(recipient)
+ output += " {} held accountable for service charge.".format(
+ recipient)
record(car, person_to_charge, values["service_charge"])
change.append([car, person_to_charge, values["service_charge"]])
@@ -599,8 +627,10 @@ def _tanken(sender, args, msg):
output += create_summary(car)
return output, None
+
cmds["tanken"] = _tanken
+
def fuck(sender, args, msg):
if not sender in num2name:
return None, "you must register first"
@@ -630,9 +660,9 @@ def fuck(sender, args, msg):
for change in last_changes:
if not change[0] in cmds:
output += "{} {} {} {}\n".format(change[0],
- ("->" if change[2] < 0 else "<-"),
- to_euro(abs(change[2])),
- change[1])
+ ("->" if change[2] < 0 else "<-"),
+ to_euro(abs(change[2])),
+ change[1])
record(change[1], change[0], change[2])
for change in last_changes:
@@ -642,14 +672,16 @@ def fuck(sender, args, msg):
if err:
output += "ERROR: " + err
else:
- output += ret
+ output += ret
return output, None
+
cmds["fuck"] = fuck
cmds["rewind"] = fuck
cmds["undo"] = fuck
+
def list_changes(sender, args, msg):
if not sender in num2name:
return None, "you must register first"
@@ -663,7 +695,6 @@ def list_changes(sender, args, msg):
except ValueError:
return None, 'the amount of changes to list must be a number'
-
nchanges = len(changes[sender_name])
if nchanges == 0:
return "Nothing to list", None
@@ -696,17 +727,18 @@ def list_changes(sender, args, msg):
msg += f'\t{" ".join(change[0])}\n'
for sender, recipient, amount in change[1:]:
msg += "\t{} {} {} {}\n".format(sender,
- ("->" if amount < 0 else "<-"),
- to_euro(abs(amount)),
- recipient)
+ ("->" if amount < 0 else "<-"),
+ to_euro(abs(amount)), recipient)
# prepend message header because we want to know how much changes we actually listed
msg = f'Changes from {sender_name} {first_to_list + 1}-{i + 1}\n' + msg
return msg, None
+
cmds["list-changes"] = list_changes
+
def schedule(sender, args, msg):
if not sender in num2name:
return None, "you must register first"
@@ -720,7 +752,8 @@ def schedule(sender, args, msg):
cmd = args[2:]
if name in scheduled_cmds:
- return None, 'there is already a scheduled command named "{}"'.format(name)
+ return None, 'there is already a scheduled command named "{}"'.format(
+ name)
# Test the command
global dry_run
@@ -733,16 +766,19 @@ def schedule(sender, args, msg):
if err:
return None, 'the command "{}" failed and will not be recorded'
- scheduled_cmd = {"schedule": args[0][1:],
- "last_time": None,
- "sender": sender,
- "cmd": cmd}
+ scheduled_cmd = {
+ "schedule": args[0][1:],
+ "last_time": None,
+ "sender": sender,
+ "cmd": cmd
+ }
scheduled_cmds[name] = scheduled_cmd
- output = 'Recorded the {} command "{}" as "{}"\n'.format(args[0][1:], ' '.join(cmd), name)
+ output = 'Recorded the {} command "{}" as "{}"\n'.format(
+ args[0][1:], ' '.join(cmd), name)
- output += "Running {} command {} for {} initially\n".format(scheduled_cmd["schedule"],
- name, sender_name)
+ output += "Running {} command {} for {} initially\n".format(
+ scheduled_cmd["schedule"], name, sender_name)
ret, err = cmds[cmd[0]](sender, cmd, "")
if err:
@@ -757,10 +793,12 @@ def schedule(sender, args, msg):
return output, None
+
cmds["weekly"] = schedule
cmds["monthly"] = schedule
cmds["yearly"] = schedule
+
def cancel(sender, args, msg):
cmd_name = args[1]
if not cmd_name in scheduled_cmds:
@@ -770,11 +808,13 @@ def cancel(sender, args, msg):
if not cmd["sender"] == sender:
return None, 'only the original creator can cancel this command'
- del(scheduled_cmds[cmd_name])
+ del (scheduled_cmds[cmd_name])
return 'Cancelled the {} cmd "{}"'.format(cmd["schedule"], cmd_name), None
+
cmds["cancel"] = cancel
+
def main():
if len(sys.argv) > 1 and sys.argv[1] in ["-d", "--dry-run"]:
global dry_run
@@ -790,11 +830,13 @@ def main():
continue
sender_number = message["source"]
- if not "dataMessage" in message or not message["dataMessage"] or not message["dataMessage"]["message"]:
+ if not "dataMessage" in message or not message[
+ "dataMessage"] or not message["dataMessage"]["message"]:
continue
else:
message = message["dataMessage"]
- if message["groupInfo"] and message["groupInfo"]["groupId"] != group_id:
+ if message["groupInfo"] and message["groupInfo"][
+ "groupId"] != group_id:
continue
body = [l.strip() for l in message["message"].lower().splitlines()]
@@ -832,24 +874,24 @@ def main():
d = last_time
while True:
if cmd["schedule"] == "yearly":
- d = date(d.year+1, d.month, d.day)
+ d = date(d.year + 1, d.month, d.day)
elif cmd["schedule"] == "monthly":
if d.day > 28:
d = date(d.year, d.month, 28)
if d.month == 12:
- d = date(d.year+1, 1, d.day)
+ d = date(d.year + 1, 1, d.day)
else:
- d = date(d.year, d.month+1, d.day)
+ d = date(d.year, d.month + 1, d.day)
else:
d = d + timedelta(7)
if d <= now:
- send("Running {} command {} for {} triggered on {}\n".format(cmd["schedule"],
- name,
- num2name[cmd["sender"]],
- d.isoformat()))
+ send("Running {} command {} for {} triggered on {}\n".
+ format(cmd["schedule"], name, num2name[cmd["sender"]],
+ d.isoformat()))
- ret, err = cmds[cmd["cmd"][0]](cmd["sender"], cmd["cmd"], "")
+ ret, err = cmds[cmd["cmd"][0]](cmd["sender"], cmd["cmd"],
+ "")
if err:
send("ERROR: " + err)
@@ -860,10 +902,9 @@ def main():
else:
break
-
with open(state_file, "w") as f:
json.dump(state, f)
+
if __name__ == "__main__":
main()
-
diff --git a/test.py b/test.py
index 023b369..c7e32c5 100755
--- a/test.py
+++ b/test.py
@@ -52,11 +52,16 @@ scheduled_state_template = Template("""
"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)
+ 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)
@@ -65,9 +70,11 @@ def run_bot(test, sender, cmd):
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"])
@@ -76,9 +83,11 @@ def reset_state(state=None):
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)
+ 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, \
@@ -87,20 +96,20 @@ def compare_state(comp_state):
s = sf.read()
return cs == s
-class TestRegCmd(unittest.TestCase):
+class TestRegCmd(unittest.TestCase):
def setUp(self):
reset_state()
def test_correct_reg(self):
- res = run_bot(self, num[alice], "!reg "+alice)
+ 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)
+ 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')
+ 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")
@@ -108,24 +117,25 @@ class TestRegCmd(unittest.TestCase):
def test_numerical_reg(self):
res = run_bot(self, num[alice], "!reg 9")
- self.assertEqual(res.stdout, 'ERROR: pure numerical names are not allowed')
+ self.assertEqual(res.stdout,
+ 'ERROR: pure numerical names are not allowed')
res = run_bot(self, num[alice], "!reg 9.7")
- self.assertEqual(res.stdout, 'ERROR: pure numerical names are not allowed')
+ self.assertEqual(res.stdout,
+ 'ERROR: pure numerical names are not allowed')
def test_additional_reg(self):
- res = run_bot(self, num[alice], "!reg "+alice)
+ res = run_bot(self, num[alice], "!reg " + alice)
self.assertEqual(res.stdout, 'Happy geldschiebing {}!'.format(alice))
- res = run_bot(self, num[bob], "!reg "+bob)
+ res = run_bot(self, num[bob], "!reg " + bob)
self.assertEqual(res.stdout, 'Happy geldschiebing {}!'.format(bob))
- res = run_bot(self, num[charlie], "!reg "+charlie)
+ 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()
@@ -134,92 +144,118 @@ class TestTransactionCmd(unittest.TestCase):
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[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))
+ 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[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))
+ 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], "!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))
+ 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)
+ 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)
+ 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[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))
+ 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[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))
+ 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[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))
+ 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[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))
+ 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], "!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[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[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))
+ 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")
def test_transactions_with_myself(self):
res = run_bot(self, num[alice], f"!schieb {alice} 1,1")
- self.assertEqual(res.stdout, 'ERROR: you can not transfer money to or from yourself')
+ self.assertEqual(
+ res.stdout,
+ 'ERROR: you can not transfer money to or from yourself')
res = run_bot(self, num[alice], f"!zieh {alice} 2.1")
- self.assertEqual(res.stdout, 'ERROR: you can not transfer money to or from yourself')
+ self.assertEqual(
+ res.stdout,
+ 'ERROR: you can not transfer money to or from yourself')
+
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')
+ 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)
+ res = run_bot(self, num[alice], "!sum " + alice)
self.assertEqual(res.stdout, 'ERROR: name "alice" not registered')
def test_summary_double_user(self):
@@ -257,11 +293,13 @@ charlie:
\tBalance: 43.00"""
self.assertEqual(res.stdout, summary)
-class TestMisc(unittest.TestCase):
+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.")
+ 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?")
@@ -269,22 +307,23 @@ class TestMisc(unittest.TestCase):
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])
+ 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])
+ msg = "alice: {}\nbob: {}\ncharlie: {}\n".format(
+ num[alice], num[bob], num[charlie])
self.assertEqual(res.stdout, msg)
-class TestSplitCmd(unittest.TestCase):
+class TestSplitCmd(unittest.TestCase):
def setUp(self):
reset_state("test/state.json_3users")
@@ -294,10 +333,12 @@ class TestSplitCmd(unittest.TestCase):
def test_split_invalid_args(self):
res = run_bot(self, num[alice], "!split")
- self.assertEqual(res.stdout, 'ERROR: not in form "!split amount [name]+"')
+ 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]+"')
+ self.assertEqual(res.stdout,
+ 'ERROR: not in form "!split amount [name]+"')
def test_split_one_unknown_user(self):
res = run_bot(self, num[alice], "!split 30 " + "foo" + " " + charlie)
@@ -330,7 +371,7 @@ alice:
\t<- charlie 20.00
\tBalance: 20.00"""
self.assertEqual(res.stdout, msg)
-
+
def test_split(self):
res = run_bot(self, num[alice], "!split 30 " + bob + " " + charlie)
msg = \
@@ -353,7 +394,8 @@ alice:
self.assertEqual(res.stdout, msg)
def test_split_whitespace(self):
- res = run_bot(self, num[alice], "!split 30 " + bob + " " + charlie + " ")
+ res = run_bot(self, num[alice],
+ "!split 30 " + bob + " " + charlie + " ")
msg = \
"""Split 30.00 between 3 -> 10.00 each
New Balance:
@@ -363,6 +405,7 @@ alice:
\tBalance: 20.00"""
self.assertEqual(res.stdout, msg)
+
class TestCarsAddCmd(unittest.TestCase):
def setUp(self):
reset_state("test/state.json_3users")
@@ -372,7 +415,7 @@ class TestCarsAddCmd(unittest.TestCase):
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'
@@ -385,23 +428,24 @@ class TestCarsAddCmd(unittest.TestCase):
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")
@@ -412,6 +456,7 @@ class TestCarsTransactions(unittest.TestCase):
o = "New Balance: alice <- 20.00 foo\n"
self.assertEqual(res.stdout, o)
+
class TestCarsSum(unittest.TestCase):
def setUp(self):
reset_state("test/state.json_2cars")
@@ -425,6 +470,7 @@ foo:
"""
self.assertEqual(res.stdout, o)
+
class TestCarPayCmd(unittest.TestCase):
def setUp(self):
reset_state("test/state.json_2cars")
@@ -473,7 +519,6 @@ foo:
\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")
@@ -496,6 +541,7 @@ foo:
\tBalance: 15.00"""
self.assertEqual(res.stdout, o)
+
class TestCarsListCmd(unittest.TestCase):
def setUp(self):
reset_state("test/state.json_2cars")
@@ -541,7 +587,7 @@ 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)
@@ -557,8 +603,8 @@ foo:
o = 'ERROR: "alice" is no available car\n'
self.assertEqual(res.stdout, o)
-class TestTankenCmd(unittest.TestCase):
+class TestTankenCmd(unittest.TestCase):
def setUp(self):
reset_state("test/state.json_3users")
@@ -687,7 +733,6 @@ Car foo:
class TestTransferCmd(unittest.TestCase):
-
def setUp(self):
reset_state("test/state.json_3users")
@@ -728,7 +773,6 @@ New Balance: bob -> 5.00 charlie
#TODO: tanken, transfer, cars pay
class TestListChangesCmd(unittest.TestCase):
-
def setUp(self):
reset_state("test/state.json_3users")
@@ -806,8 +850,8 @@ Change 2:
"""
self.assertEqual(res.stdout, msg)
-class TestFuckCmd(unittest.TestCase):
+class TestFuckCmd(unittest.TestCase):
def setUp(self):
reset_state("test/state.json_3users")
@@ -897,7 +941,7 @@ alice <- 1.00 charlie
compare_state("test/state.json_3users")
def test_fuck_transaction(self):
- run_bot(self, num[alice], "!schieb "+bob+ " 10")
+ run_bot(self, num[alice], "!schieb " + bob + " 10")
res = run_bot(self, num[alice], "!fuck")
msg = \
"""alice: sorry I fucked up!
@@ -908,8 +952,8 @@ alice <- 10.00 bob
self.assertEqual(res.stdout, msg)
compare_state("test/state.json_3users")
-class TestScheduleCmd(unittest.TestCase):
+class TestScheduleCmd(unittest.TestCase):
def setUp(self):
reset_state("test/state.json_3users")
@@ -939,7 +983,9 @@ 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"))
+ 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
@@ -959,7 +1005,9 @@ alice:
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"))
+ 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
@@ -979,7 +1027,7 @@ alice:
\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):
@@ -1010,11 +1058,12 @@ Cancelled the monthly cmd "stuff\""""
# Last exec one month ago
if now.month > 1:
- one_month_ago = date(now.year, now.month-1, now.day)
+ 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"))
+ 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
@@ -1035,12 +1084,13 @@ alice:
# Last exec two month ago
if now.month > 2:
- two_months_ago = date(now.year, now.month-2, now.day)
+ 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)
+ 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"))
+ 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
@@ -1060,13 +1110,13 @@ alice:
\tBalance: 6.00""".format(one_month_ago.isoformat(),
now.isoformat())
self.assertEqual(res.stdout, msg)
-
- os.remove("test/state.json_schedule_monthly")
+ 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")
+ 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
@@ -1083,5 +1133,6 @@ fiat:
\tBalance: -3.00"""
self.assertEqual(res.stdout, msg)
+
if __name__ == '__main__':
unittest.main()