use 'mailbox_sides' instead of cols in 'mailboxes'
This commit is contained in:
parent
6c725e4a86
commit
41f229de87
|
@ -36,20 +36,24 @@ CREATE TABLE `nameplate_sides`
|
|||
|
||||
-- Clients exchange messages through a "mailbox", which has a long (randomly
|
||||
-- unique) identifier and a queue of messages.
|
||||
-- `id` is randomly-generated and unique across all apps.
|
||||
CREATE TABLE `mailboxes`
|
||||
(
|
||||
`app_id` VARCHAR,
|
||||
`id` VARCHAR,
|
||||
`side1` VARCHAR, -- side name, or NULL
|
||||
`side2` VARCHAR, -- side name, or NULL
|
||||
`crowded` BOOLEAN, -- at some point, three or more sides were involved
|
||||
`first_mood` VARCHAR,
|
||||
-- timing data for the mailbox itself
|
||||
`started` INTEGER, -- time when opened
|
||||
`second` INTEGER -- time when second side opened
|
||||
`id` VARCHAR PRIMARY KEY,
|
||||
`updated` INTEGER -- time of last activity, used for pruning
|
||||
);
|
||||
CREATE INDEX `mailboxes_idx` ON `mailboxes` (`app_id`, `id`);
|
||||
|
||||
CREATE TABLE `mailbox_sides`
|
||||
(
|
||||
`mailbox_id` REFERENCES `mailboxes`(`id`),
|
||||
`opened` BOOLEAN, -- True after open(), False after close()
|
||||
`side` VARCHAR,
|
||||
`added` INTEGER, -- time when this side first claimed the nameplate
|
||||
`mood` VARCHAR
|
||||
);
|
||||
|
||||
CREATE TABLE `messages`
|
||||
(
|
||||
`app_id` VARCHAR,
|
||||
|
|
|
@ -16,33 +16,9 @@ EXPIRATION_CHECK_PERIOD = 1*HOUR
|
|||
def generate_mailbox_id():
|
||||
return base64.b32encode(os.urandom(8)).lower().strip(b"=").decode("ascii")
|
||||
|
||||
|
||||
SideResult = namedtuple("SideResult", ["changed", "empty", "side1", "side2"])
|
||||
Unchanged = SideResult(changed=False, empty=False, side1=None, side2=None)
|
||||
class CrowdedError(Exception):
|
||||
pass
|
||||
|
||||
def add_side(row, new_side):
|
||||
old_sides = [s for s in [row["side1"], row["side2"]] if s]
|
||||
assert old_sides
|
||||
if new_side in old_sides:
|
||||
return Unchanged
|
||||
if len(old_sides) == 2:
|
||||
raise CrowdedError("too many sides for this thing")
|
||||
return SideResult(changed=True, empty=False,
|
||||
side1=old_sides[0], side2=new_side)
|
||||
|
||||
def remove_side(row, side):
|
||||
old_sides = [s for s in [row["side1"], row["side2"]] if s]
|
||||
if side not in old_sides:
|
||||
return Unchanged
|
||||
remaining_sides = old_sides[:]
|
||||
remaining_sides.remove(side)
|
||||
if remaining_sides:
|
||||
return SideResult(changed=True, empty=False, side1=remaining_sides[0],
|
||||
side2=None)
|
||||
return SideResult(changed=True, empty=True, side1=None, side2=None)
|
||||
|
||||
Usage = namedtuple("Usage", ["started", "waiting_time", "total_time", "result"])
|
||||
TransitUsage = namedtuple("TransitUsage",
|
||||
["started", "waiting_time", "total_time",
|
||||
|
@ -65,23 +41,24 @@ class Mailbox:
|
|||
# requires caller to db.commit()
|
||||
assert isinstance(side, type("")), type(side)
|
||||
db = self._db
|
||||
row = db.execute("SELECT * FROM `mailboxes`"
|
||||
" WHERE `app_id`=? AND `id`=?",
|
||||
(self._app_id, self._mailbox_id)).fetchone()
|
||||
try:
|
||||
sr = add_side(row, side)
|
||||
except CrowdedError:
|
||||
db.execute("UPDATE `mailboxes` SET `crowded`=?"
|
||||
" WHERE `app_id`=? AND `id`=?",
|
||||
(True, self._app_id, self._mailbox_id))
|
||||
db.commit()
|
||||
raise
|
||||
if sr.changed:
|
||||
db.execute("UPDATE `mailboxes` SET"
|
||||
" `side1`=?, `side2`=?, `second`=?"
|
||||
" WHERE `app_id`=? AND `id`=?",
|
||||
(sr.side1, sr.side2, when,
|
||||
self._app_id, self._mailbox_id))
|
||||
|
||||
already = db.execute("SELECT * FROM `mailbox_sides`"
|
||||
" WHERE `mailbox_id`=? AND `side`=?",
|
||||
(self._mailbox_id, side)).fetchone()
|
||||
if not already:
|
||||
db.execute("INSERT INTO `mailbox_sides`"
|
||||
" (`mailbox_id`, `opened`, `side`, `added`)"
|
||||
" VALUES(?,?,?,?)",
|
||||
(self._mailbox_id, True, side, when))
|
||||
db.execute("UPDATE `mailboxes` SET `updated`=? WHERE `id`=?",
|
||||
(when, self._mailbox_id))
|
||||
db.commit() # XXX: reconcile the need for this with the comment above
|
||||
|
||||
rows = db.execute("SELECT * FROM `mailbox_sides`"
|
||||
" WHERE `mailbox_id`=?",
|
||||
(self._mailbox_id,)).fetchall()
|
||||
if len(rows) > 2:
|
||||
raise CrowdedError("too many sides have opened this mailbox")
|
||||
|
||||
def get_messages(self):
|
||||
messages = []
|
||||
|
@ -118,6 +95,8 @@ class Mailbox:
|
|||
" VALUES (?,?,?,?,?, ?,?)",
|
||||
(self._app_id, self._mailbox_id, sm.side,
|
||||
sm.phase, sm.body, sm.server_rx, sm.msg_id))
|
||||
self._db.execute("UPDATE `mailboxes` SET `updated`=? WHERE `id`=?",
|
||||
(sm.server_rx, self._mailbox_id))
|
||||
self._db.commit()
|
||||
|
||||
def add_message(self, sm):
|
||||
|
@ -133,34 +112,36 @@ class Mailbox:
|
|||
(self._app_id, self._mailbox_id)).fetchone()
|
||||
if not row:
|
||||
return
|
||||
sr = remove_side(row, side)
|
||||
if sr.empty:
|
||||
self._app._summarize_mailbox_and_store(self._mailbox_id, row,
|
||||
mood, when, pruned=False)
|
||||
self._delete()
|
||||
db.commit()
|
||||
elif sr.changed:
|
||||
db.execute("UPDATE `mailboxes`"
|
||||
" SET `side1`=?, `side2`=?, `first_mood`=?"
|
||||
" WHERE `app_id`=? AND `id`=?",
|
||||
(sr.side1, sr.side2, mood,
|
||||
self._app_id, self._mailbox_id))
|
||||
db.commit()
|
||||
|
||||
def _delete(self):
|
||||
# requires caller to db.commit()
|
||||
self._db.execute("DELETE FROM `mailboxes`"
|
||||
" WHERE `app_id`=? AND `id`=?",
|
||||
(self._app_id, self._mailbox_id))
|
||||
self._db.execute("DELETE FROM `messages`"
|
||||
" WHERE `app_id`=? AND `mailbox_id`=?",
|
||||
(self._app_id, self._mailbox_id))
|
||||
row = db.execute("SELECT * FROM `mailbox_sides`"
|
||||
" WHERE `mailbox_id`=? AND `side`=?",
|
||||
(self._mailbox_id, side)).fetchone()
|
||||
if not row:
|
||||
return
|
||||
db.execute("UPDATE `mailbox_sides` SET `opened`=?, `mood`=?"
|
||||
" WHERE `mailbox_id`=? AND `side`=?",
|
||||
(False, mood, self._mailbox_id, side))
|
||||
db.commit()
|
||||
|
||||
# are any sides still open?
|
||||
side_rows = db.execute("SELECT * FROM `mailbox_sides`"
|
||||
" WHERE `mailbox_id`=?",
|
||||
(self._mailbox_id,)).fetchall()
|
||||
if any([sr["opened"] for sr in side_rows]):
|
||||
return
|
||||
|
||||
# nope. delete and summarize
|
||||
db.execute("DELETE FROM `messages` WHERE `mailbox_id`=?",
|
||||
(self._mailbox_id,))
|
||||
db.execute("DELETE FROM `mailbox_sides` WHERE `mailbox_id`=?",
|
||||
(self._mailbox_id,))
|
||||
db.execute("DELETE FROM `mailboxes` WHERE `id`=?", (self._mailbox_id,))
|
||||
self._app._summarize_mailbox_and_store(side_rows, when, pruned=False)
|
||||
db.commit()
|
||||
# Shut down any listeners, just in case they're still lingering
|
||||
# around.
|
||||
for (send_f, stop_f) in self._listeners.values():
|
||||
stop_f()
|
||||
|
||||
self._app.free_mailbox(self._mailbox_id)
|
||||
|
||||
def is_active(self):
|
||||
|
@ -260,6 +241,7 @@ class AppNamespace:
|
|||
db.execute("UPDATE `nameplates` SET `updated`=? WHERE `id`=?",
|
||||
(when, npid))
|
||||
db.commit()
|
||||
|
||||
rows = db.execute("SELECT * FROM `nameplate_sides`"
|
||||
" WHERE `nameplates_id`=?", (npid,)).fetchall()
|
||||
if len(rows) > 2:
|
||||
|
@ -343,11 +325,11 @@ class AppNamespace:
|
|||
log.msg("spawning #%s for app_id %s" % (mailbox_id,
|
||||
self._app_id))
|
||||
db.execute("INSERT INTO `mailboxes`"
|
||||
" (`app_id`, `id`, `side1`, `crowded`, `started`)"
|
||||
" VALUES(?,?,?,?,?)",
|
||||
(self._app_id, mailbox_id, side, False, when))
|
||||
db.commit() # XXX
|
||||
# mailbox.open() does a SELECT to find the old sides
|
||||
" (`app_id`, `id`, `updated`)"
|
||||
" VALUES(?,?,?)",
|
||||
(self._app_id, mailbox_id, when))
|
||||
# we don't need a commit here, because mailbox.open() only
|
||||
# does SELECT FROM `mailbox_sides`, not from `mailboxes`
|
||||
self._mailboxes[mailbox_id] = Mailbox(self, self._db,
|
||||
self._app_id, mailbox_id)
|
||||
mailbox = self._mailboxes[mailbox_id]
|
||||
|
@ -365,15 +347,9 @@ class AppNamespace:
|
|||
# log.msg("freed+killed #%s, now have %d DB mailboxes, %d live" %
|
||||
# (mailbox_id, len(self.get_claimed()), len(self._mailboxes)))
|
||||
|
||||
def _summarize_mailbox_and_store(self, mailbox_id, row,
|
||||
second_mood, delete_time, pruned):
|
||||
def _summarize_mailbox_and_store(self, side_rows, delete_time, pruned):
|
||||
db = self._db
|
||||
rows = db.execute("SELECT DISTINCT(`side`) FROM `messages`"
|
||||
" WHERE `app_id`=? AND `mailbox_id`=?",
|
||||
(self._app_id, mailbox_id)).fetchall()
|
||||
num_sides = len(rows)
|
||||
u = self._summarize_mailbox(row, num_sides, second_mood, delete_time,
|
||||
pruned)
|
||||
u = self._summarize_mailbox(side_rows, delete_time, pruned)
|
||||
db.execute("INSERT INTO `mailbox_usage`"
|
||||
" (`app_id`,"
|
||||
" `started`, `total_time`, `waiting_time`, `result`)"
|
||||
|
@ -381,16 +357,17 @@ class AppNamespace:
|
|||
(self._app_id,
|
||||
u.started, u.total_time, u.waiting_time, u.result))
|
||||
|
||||
def _summarize_mailbox(self, row, num_sides, second_mood, delete_time,
|
||||
pruned):
|
||||
started = row["started"]
|
||||
def _summarize_mailbox(self, side_rows, delete_time, pruned):
|
||||
times = sorted([row["added"] for row in side_rows])
|
||||
started = times[0]
|
||||
if self._blur_usage:
|
||||
started = self._blur_usage * (started // self._blur_usage)
|
||||
waiting_time = None
|
||||
if row["second"]:
|
||||
waiting_time = row["second"] - row["started"]
|
||||
total_time = delete_time - row["started"]
|
||||
if len(times) > 1:
|
||||
waiting_time = times[1] - times[0]
|
||||
total_time = delete_time - times[0]
|
||||
|
||||
num_sides = len(times)
|
||||
if num_sides == 0:
|
||||
result = "quiet"
|
||||
elif num_sides == 1:
|
||||
|
@ -398,7 +375,8 @@ class AppNamespace:
|
|||
else:
|
||||
result = "happy"
|
||||
|
||||
moods = set([row["first_mood"], second_mood])
|
||||
# "mood" is only recorded at close()
|
||||
moods = [row["mood"] for row in side_rows if row.get("mood")]
|
||||
if "lonely" in moods:
|
||||
result = "lonely"
|
||||
if "errory" in moods:
|
||||
|
@ -407,7 +385,7 @@ class AppNamespace:
|
|||
result = "scary"
|
||||
if pruned:
|
||||
result = "pruney"
|
||||
if row["crowded"]:
|
||||
if num_sides > 2:
|
||||
result = "crowded"
|
||||
|
||||
return Usage(started=started, waiting_time=waiting_time,
|
||||
|
@ -425,47 +403,15 @@ class AppNamespace:
|
|||
# for all `mailboxes`: classify as new or old
|
||||
OLD = 0; NEW = 1
|
||||
all_mailboxes = {}
|
||||
all_mailbox_rows = {}
|
||||
for row in db.execute("SELECT * FROM `mailboxes`"
|
||||
" WHERE `app_id`=?",
|
||||
for row in db.execute("SELECT * FROM `mailboxes` WHERE `app_id`=?",
|
||||
(self._app_id,)).fetchall():
|
||||
mailbox_id = row["id"]
|
||||
all_mailbox_rows[mailbox_id] = row
|
||||
if row["started"] > old:
|
||||
which = NEW
|
||||
elif row["second"] and row["second"] > old:
|
||||
if row["updated"] > old:
|
||||
which = NEW
|
||||
else:
|
||||
which = OLD
|
||||
all_mailboxes[mailbox_id] = which
|
||||
#log.msg(" 2: all_mailboxes", all_mailboxes, all_mailbox_rows)
|
||||
|
||||
# for all mailbox ids used by `messages`:
|
||||
# if there is no matching mailbox: delete the messages
|
||||
# if there is at least one new message (select when>old limit 1):
|
||||
# classify the mailbox as new
|
||||
for row in db.execute("SELECT DISTINCT(`mailbox_id`)"
|
||||
" FROM `messages`"
|
||||
" WHERE `app_id`=?",
|
||||
(self._app_id,)).fetchall():
|
||||
mailbox_id = row["mailbox_id"]
|
||||
if mailbox_id not in all_mailboxes:
|
||||
log.msg(" deleting orphan messages", mailbox_id)
|
||||
db.execute("DELETE FROM `messages`"
|
||||
" WHERE `app_id`=? AND `mailbox_id`=?",
|
||||
(self._app_id, mailbox_id))
|
||||
modified = True
|
||||
else:
|
||||
new_msgs = db.execute("SELECT * FROM `messages`"
|
||||
" WHERE `app_id`=? AND `mailbox_id`=?"
|
||||
" AND `server_rx` > ?"
|
||||
" LIMIT 1",
|
||||
(self._app_id, mailbox_id, old)
|
||||
).fetchall()
|
||||
if new_msgs:
|
||||
#log.msg(" 3-: saved by new messages", new_msgs)
|
||||
all_mailboxes[mailbox_id] = NEW
|
||||
#log.msg(" 4: all_mailboxes", all_mailboxes)
|
||||
#log.msg(" 2: all_mailboxes", all_mailboxes)
|
||||
|
||||
# for all mailbox objects with active listeners:
|
||||
# classify the mailbox as new
|
||||
|
@ -484,8 +430,7 @@ class AppNamespace:
|
|||
# if the nameplate is new:
|
||||
# classify mailbox as new
|
||||
old_nameplate_ids = []
|
||||
for row in db.execute("SELECT * FROM `nameplates`"
|
||||
" WHERE `app_id`=?",
|
||||
for row in db.execute("SELECT * FROM `nameplates` WHERE `app_id`=?",
|
||||
(self._app_id,)).fetchall():
|
||||
npid = row["id"]
|
||||
if row["updated"] > old:
|
||||
|
@ -523,15 +468,16 @@ class AppNamespace:
|
|||
for mailbox_id, which in all_mailboxes.items():
|
||||
if which == OLD:
|
||||
log.msg(" deleting mailbox", mailbox_id)
|
||||
self._summarize_mailbox_and_store(mailbox_id,
|
||||
all_mailbox_rows[mailbox_id],
|
||||
"pruney", now, pruned=True)
|
||||
db.execute("DELETE FROM `messages`"
|
||||
" WHERE `app_id`=? AND `mailbox_id`=?",
|
||||
(self._app_id, mailbox_id))
|
||||
db.execute("DELETE FROM `mailboxes`"
|
||||
" WHERE `app_id`=? AND `id`=?",
|
||||
(self._app_id, mailbox_id))
|
||||
side_rows = db.execute("SELECT * FROM `mailbox_sides`"
|
||||
" WHERE `mailbox_id`=?",
|
||||
(mailbox_id,)).fetchall()
|
||||
db.execute("DELETE FROM `messages` WHERE `mailbox_id`=?",
|
||||
(mailbox_id,))
|
||||
db.execute("DELETE FROM `mailbox_sides` WHERE `mailbox_id`=?",
|
||||
(mailbox_id,))
|
||||
db.execute("DELETE FROM `mailboxes` WHERE `id`=?",
|
||||
(mailbox_id,))
|
||||
self._summarize_mailbox_and_store(side_rows, now, pruned=True)
|
||||
modified = True
|
||||
|
||||
if modified:
|
||||
|
|
|
@ -12,7 +12,7 @@ from autobahn.twisted import websocket
|
|||
from .. import __version__
|
||||
from .common import ServerBase
|
||||
from ..server import rendezvous, transit_server
|
||||
from ..server.rendezvous import Usage, SidedMessage, Mailbox
|
||||
from ..server.rendezvous import Usage, SidedMessage
|
||||
from ..server.database import get_db
|
||||
|
||||
class Server(ServerBase, unittest.TestCase):
|
||||
|
@ -152,77 +152,69 @@ class Server(ServerBase, unittest.TestCase):
|
|||
|
||||
|
||||
def _mailbox(self, app, mailbox_id):
|
||||
return app._db.execute("SELECT * FROM `mailboxes`"
|
||||
" WHERE `app_id`='appid' AND `id`=?",
|
||||
(mailbox_id,)).fetchone()
|
||||
mb_row = app._db.execute("SELECT * FROM `mailboxes`"
|
||||
" WHERE `app_id`='appid' AND `id`=?",
|
||||
(mailbox_id,)).fetchone()
|
||||
if not mb_row:
|
||||
return None, None
|
||||
side_rows = app._db.execute("SELECT * FROM `mailbox_sides`"
|
||||
" WHERE `mailbox_id`=?",
|
||||
(mailbox_id,)).fetchall()
|
||||
return mb_row, side_rows
|
||||
|
||||
def test_mailbox(self):
|
||||
app = self._rendezvous.get_app("appid")
|
||||
mailbox_id = "mid"
|
||||
m1 = app.open_mailbox(mailbox_id, "side1", 0)
|
||||
|
||||
row = self._mailbox(app, mailbox_id)
|
||||
self.assertEqual(row["side1"], "side1")
|
||||
self.assertEqual(row["side2"], None)
|
||||
self.assertEqual(row["crowded"], False)
|
||||
self.assertEqual(row["started"], 0)
|
||||
self.assertEqual(row["second"], None)
|
||||
mb_row, side_rows = self._mailbox(app, mailbox_id)
|
||||
self.assertEqual(len(side_rows), 1)
|
||||
self.assertEqual(side_rows[0]["side"], "side1")
|
||||
self.assertEqual(side_rows[0]["added"], 0)
|
||||
|
||||
# opening the same mailbox twice, by the same side, gets the same
|
||||
# object
|
||||
# object, and does not update the "added" timestamp
|
||||
self.assertIdentical(m1, app.open_mailbox(mailbox_id, "side1", 1))
|
||||
row = self._mailbox(app, mailbox_id)
|
||||
self.assertEqual(row["side1"], "side1")
|
||||
self.assertEqual(row["side2"], None)
|
||||
self.assertEqual(row["crowded"], False)
|
||||
self.assertEqual(row["started"], 0)
|
||||
self.assertEqual(row["second"], None)
|
||||
mb_row, side_rows = self._mailbox(app, mailbox_id)
|
||||
self.assertEqual(len(side_rows), 1)
|
||||
self.assertEqual(side_rows[0]["side"], "side1")
|
||||
self.assertEqual(side_rows[0]["added"], 0)
|
||||
|
||||
# opening a second side gets the same object, and adds a new claim
|
||||
self.assertIdentical(m1, app.open_mailbox(mailbox_id, "side2", 2))
|
||||
row = self._mailbox(app, mailbox_id)
|
||||
self.assertEqual(row["side1"], "side1")
|
||||
self.assertEqual(row["side2"], "side2")
|
||||
self.assertEqual(row["crowded"], False)
|
||||
self.assertEqual(row["started"], 0)
|
||||
self.assertEqual(row["second"], 2)
|
||||
mb_row, side_rows = self._mailbox(app, mailbox_id)
|
||||
self.assertEqual(len(side_rows), 2)
|
||||
adds = [(row["side"], row["added"]) for row in side_rows]
|
||||
self.assertIn(("side1", 0), adds)
|
||||
self.assertIn(("side2", 2), adds)
|
||||
|
||||
# a third open marks it as crowded
|
||||
self.assertRaises(rendezvous.CrowdedError,
|
||||
app.open_mailbox, mailbox_id, "side3", 3)
|
||||
row = self._mailbox(app, mailbox_id)
|
||||
self.assertEqual(row["side1"], "side1")
|
||||
self.assertEqual(row["side2"], "side2")
|
||||
self.assertEqual(row["crowded"], True)
|
||||
self.assertEqual(row["started"], 0)
|
||||
self.assertEqual(row["second"], 2)
|
||||
mb_row, side_rows = self._mailbox(app, mailbox_id)
|
||||
self.assertEqual(len(side_rows), 3)
|
||||
m1.close("side3", "company", 4)
|
||||
|
||||
# closing a side that never claimed the mailbox is ignored
|
||||
m1.close("side4", "mood", 4)
|
||||
row = self._mailbox(app, mailbox_id)
|
||||
self.assertEqual(row["side1"], "side1")
|
||||
self.assertEqual(row["side2"], "side2")
|
||||
self.assertEqual(row["crowded"], True)
|
||||
self.assertEqual(row["started"], 0)
|
||||
self.assertEqual(row["second"], 2)
|
||||
mb_row, side_rows = self._mailbox(app, mailbox_id)
|
||||
self.assertEqual(len(side_rows), 3)
|
||||
|
||||
# closing one side leaves the second claim
|
||||
m1.close("side1", "mood", 5)
|
||||
row = self._mailbox(app, mailbox_id)
|
||||
self.assertEqual(row["side1"], "side2")
|
||||
self.assertEqual(row["side2"], None)
|
||||
self.assertEqual(row["crowded"], True)
|
||||
self.assertEqual(row["started"], 0)
|
||||
self.assertEqual(row["second"], 2)
|
||||
mb_row, side_rows = self._mailbox(app, mailbox_id)
|
||||
sides = [(row["side"], row["opened"], row["mood"]) for row in side_rows]
|
||||
self.assertIn(("side1", False, "mood"), sides)
|
||||
self.assertIn(("side2", True, None), sides)
|
||||
self.assertIn(("side3", False, "company"), sides)
|
||||
|
||||
# closing one side multiple is ignored
|
||||
# closing one side multiple times is ignored
|
||||
m1.close("side1", "mood", 6)
|
||||
row = self._mailbox(app, mailbox_id)
|
||||
self.assertEqual(row["side1"], "side2")
|
||||
self.assertEqual(row["side2"], None)
|
||||
self.assertEqual(row["crowded"], True)
|
||||
self.assertEqual(row["started"], 0)
|
||||
self.assertEqual(row["second"], 2)
|
||||
mb_row, side_rows = self._mailbox(app, mailbox_id)
|
||||
sides = [(row["side"], row["opened"], row["mood"]) for row in side_rows]
|
||||
self.assertIn(("side1", False, "mood"), sides)
|
||||
self.assertIn(("side2", True, None), sides)
|
||||
self.assertIn(("side3", False, "company"), sides)
|
||||
|
||||
l1 = []; stop1 = []; stop1_f = lambda: stop1.append(True)
|
||||
m1.add_listener("handle1", l1.append, stop1_f)
|
||||
|
@ -231,8 +223,8 @@ class Server(ServerBase, unittest.TestCase):
|
|||
m1.close("side2", "mood", 7)
|
||||
self.assertEqual(stop1, [True])
|
||||
|
||||
row = self._mailbox(app, mailbox_id)
|
||||
self.assertEqual(row, None)
|
||||
mb_row, side_rows = self._mailbox(app, mailbox_id)
|
||||
self.assertEqual(mb_row, None)
|
||||
usage = app._db.execute("SELECT * FROM `mailbox_usage`").fetchone()
|
||||
self.assertEqual(usage["app_id"], "appid")
|
||||
self.assertEqual(usage["started"], 0)
|
||||
|
@ -384,22 +376,20 @@ class Prune(unittest.TestCase):
|
|||
def test_lots(self):
|
||||
OLD = "old"; NEW = "new"
|
||||
for nameplate in [None, OLD, NEW]:
|
||||
for mailbox in [None, OLD, NEW]:
|
||||
for mailbox in [OLD, NEW]:
|
||||
listeners = [False]
|
||||
if mailbox is not None:
|
||||
listeners = [False, True]
|
||||
for has_listeners in listeners:
|
||||
for messages in [None, OLD, NEW]:
|
||||
self.one(nameplate, mailbox, has_listeners, messages)
|
||||
self.one(nameplate, mailbox, has_listeners)
|
||||
|
||||
# def test_one(self):
|
||||
# # to debug specific problems found by test_lots
|
||||
# self.one(None, "old", False, 'new')
|
||||
def test_one(self):
|
||||
# to debug specific problems found by test_lots
|
||||
self.one(None, "new", False)
|
||||
|
||||
def one(self, nameplate, mailbox, has_listeners, messages):
|
||||
desc = ("nameplate=%s, mailbox=%s, has_listeners=%s,"
|
||||
" messages=%s" %
|
||||
(nameplate, mailbox, has_listeners, messages))
|
||||
def one(self, nameplate, mailbox, has_listeners):
|
||||
desc = ("nameplate=%s, mailbox=%s, has_listeners=%s" %
|
||||
(nameplate, mailbox, has_listeners))
|
||||
log.msg(desc)
|
||||
|
||||
db = get_db(":memory:")
|
||||
|
@ -412,46 +402,27 @@ class Prune(unittest.TestCase):
|
|||
when = {OLD: 1, NEW: 60}
|
||||
nameplate_survives = False
|
||||
mailbox_survives = False
|
||||
messages_survive = False
|
||||
|
||||
mbid = "mbid"
|
||||
if nameplate is not None:
|
||||
app.claim_nameplate("npid", "side1", when[nameplate],
|
||||
_test_mailbox_id=mbid)
|
||||
if mailbox is not None:
|
||||
mb = app.open_mailbox(mbid, "side1", when[mailbox])
|
||||
else:
|
||||
# We might want a Mailbox, because that's the easiest way to add
|
||||
# a "messages" row, but we can't use app.open_mailbox() because
|
||||
# that modifies both the "mailboxes" table and app._mailboxes,
|
||||
# and sometimes we're testing what happens when there are
|
||||
# messages but not a mailbox
|
||||
mb = Mailbox(app, db, APPID, mbid)
|
||||
# we need app._mailboxes to know about this, because that's
|
||||
# where it looks to find listeners
|
||||
app._mailboxes[mbid] = mb
|
||||
mb = app.open_mailbox(mbid, "side1", when[mailbox])
|
||||
|
||||
if messages is not None:
|
||||
sm = SidedMessage("side1", "phase", "body", when[messages],
|
||||
"msgid")
|
||||
mb.add_message(sm)
|
||||
# the pruning algorithm doesn't care about the age of messages,
|
||||
# because mailbox.updated is always updated each time we add a
|
||||
# message
|
||||
sm = SidedMessage("side1", "phase", "body", when[mailbox], "msgid")
|
||||
mb.add_message(sm)
|
||||
|
||||
if has_listeners:
|
||||
mb.add_listener("handle", None, None)
|
||||
|
||||
if mailbox is None and messages is not None:
|
||||
# orphaned messages, even new ones, can't keep a nameplate alive
|
||||
messages = None
|
||||
messages_survive = False
|
||||
|
||||
if (nameplate == NEW or mailbox == NEW
|
||||
or has_listeners or messages == NEW):
|
||||
if (nameplate == NEW or mailbox == NEW or has_listeners):
|
||||
if nameplate is not None:
|
||||
nameplate_survives = True
|
||||
if mailbox is not None:
|
||||
mailbox_survives = True
|
||||
if messages is not None:
|
||||
messages_survive = True
|
||||
mailbox_survives = True
|
||||
messages_survive = mailbox_survives
|
||||
|
||||
rv.prune(now=123, old=50)
|
||||
|
||||
|
@ -931,41 +902,45 @@ class Summary(unittest.TestCase):
|
|||
def test_mailbox(self):
|
||||
app = rendezvous.AppNamespace(None, None, False, None)
|
||||
# starts at time 1, maybe gets second open at time 3, closes at 5
|
||||
base_row = {"started": 1, "second": None,
|
||||
"first_mood": None, "crowded": False}
|
||||
def summ(num_sides, second_mood=None, pruned=False, **kwargs):
|
||||
row = base_row.copy()
|
||||
row.update(kwargs)
|
||||
return app._summarize_mailbox(row, num_sides, second_mood, 5,
|
||||
pruned)
|
||||
def s(rows, pruned=False):
|
||||
return app._summarize_mailbox(rows, 5, pruned)
|
||||
|
||||
self.assertEqual(summ(1), Usage(1, None, 4, "lonely"))
|
||||
self.assertEqual(summ(1, "lonely"), Usage(1, None, 4, "lonely"))
|
||||
self.assertEqual(summ(1, "errory"), Usage(1, None, 4, "errory"))
|
||||
self.assertEqual(summ(1, crowded=True), Usage(1, None, 4, "crowded"))
|
||||
rows = [dict(added=1)]
|
||||
self.assertEqual(s(rows), Usage(1, None, 4, "lonely"))
|
||||
rows = [dict(added=1, mood="lonely")]
|
||||
self.assertEqual(s(rows), Usage(1, None, 4, "lonely"))
|
||||
rows = [dict(added=1, mood="errory")]
|
||||
self.assertEqual(s(rows), Usage(1, None, 4, "errory"))
|
||||
rows = [dict(added=1, mood=None)]
|
||||
self.assertEqual(s(rows, pruned=True), Usage(1, None, 4, "pruney"))
|
||||
rows = [dict(added=1, mood="happy")]
|
||||
self.assertEqual(s(rows, pruned=True), Usage(1, None, 4, "pruney"))
|
||||
|
||||
self.assertEqual(summ(2, first_mood="happy",
|
||||
second=3, second_mood="happy"),
|
||||
Usage(1, 2, 4, "happy"))
|
||||
rows = [dict(added=1, mood="happy"), dict(added=3, mood="happy")]
|
||||
self.assertEqual(s(rows), Usage(1, 2, 4, "happy"))
|
||||
|
||||
self.assertEqual(summ(2, first_mood="errory",
|
||||
second=3, second_mood="happy"),
|
||||
Usage(1, 2, 4, "errory"))
|
||||
rows = [dict(added=1, mood="errory"), dict(added=3, mood="happy")]
|
||||
self.assertEqual(s(rows), Usage(1, 2, 4, "errory"))
|
||||
|
||||
self.assertEqual(summ(2, first_mood="happy",
|
||||
second=3, second_mood="errory"),
|
||||
Usage(1, 2, 4, "errory"))
|
||||
rows = [dict(added=1, mood="happy"), dict(added=3, mood="errory")]
|
||||
self.assertEqual(s(rows), Usage(1, 2, 4, "errory"))
|
||||
|
||||
self.assertEqual(summ(2, first_mood="scary",
|
||||
second=3, second_mood="happy"),
|
||||
Usage(1, 2, 4, "scary"))
|
||||
rows = [dict(added=1, mood="scary"), dict(added=3, mood="happy")]
|
||||
self.assertEqual(s(rows), Usage(1, 2, 4, "scary"))
|
||||
|
||||
self.assertEqual(summ(2, first_mood="scary",
|
||||
second=3, second_mood="errory"),
|
||||
Usage(1, 2, 4, "scary"))
|
||||
rows = [dict(added=1, mood="scary"), dict(added=3, mood="errory")]
|
||||
self.assertEqual(s(rows), Usage(1, 2, 4, "scary"))
|
||||
|
||||
self.assertEqual(summ(2, first_mood="happy", second=3, pruned=True),
|
||||
Usage(1, 2, 4, "pruney"))
|
||||
rows = [dict(added=1, mood="happy"), dict(added=3, mood=None)]
|
||||
self.assertEqual(s(rows, pruned=True), Usage(1, 2, 4, "pruney"))
|
||||
rows = [dict(added=1, mood="happy"), dict(added=3, mood="happy")]
|
||||
self.assertEqual(s(rows, pruned=True), Usage(1, 2, 4, "pruney"))
|
||||
|
||||
rows = [dict(added=1), dict(added=3), dict(added=4)]
|
||||
self.assertEqual(s(rows), Usage(1, 2, 4, "crowded"))
|
||||
|
||||
rows = [dict(added=1), dict(added=3), dict(added=4)]
|
||||
self.assertEqual(s(rows, pruned=True), Usage(1, 2, 4, "crowded"))
|
||||
|
||||
def test_nameplate(self):
|
||||
a = rendezvous.AppNamespace(None, None, False, None)
|
||||
|
|
Loading…
Reference in New Issue
Block a user