def test_display_name(self, formatdate): formatdate.return_value = "11111" meeting = Meeting("n", "c", "n") meeting.start_time = datetime(2021, 3, 7, 13, 14, 0) # in UTC by default assert meeting.display_name() == "c/n@11111" formatdate.assert_called_once_with(meeting.start_time)
def test_track_nick(self): meeting = Meeting("nick", "channel", "network") meeting.track_nick("xxx") assert meeting.nicks["xxx"] == 1 meeting.track_nick("yyy", messages=0) assert meeting.nicks["yyy"] == 0 meeting.track_nick("nick") assert meeting.nicks["nick"] == 1 meeting.track_nick("nick", messages=4) assert meeting.nicks["nick"] == 5
def test_track_message_non_action(self): message = Message("id", MagicMock(), "nick", "channel", "network", "Hello, world") meeting = Meeting("n", "c", "n") tracked = meeting.track_message(message) assert meeting.nicks["nick"] == 1 assert tracked in meeting.messages assert tracked.sender == "nick" assert tracked.payload == "Hello, world" assert tracked.action is False
def test_track_event_no_attributes(self): meeting = Meeting("n", "c", "n") timestamp = MagicMock() message = MagicMock(timestamp=timestamp) tracked = meeting.track_event(event_type=EventType.VOTE, message=message) assert tracked in meeting.events assert tracked.id is not None assert tracked.event_type == EventType.VOTE assert tracked.message is message assert tracked.timestamp is timestamp assert tracked.operand is None
def test_track_message_action(self): # Trying to replicate "^AACTION waves goodbye^A" as in the Wikipedia article # See "DCC CHAT" under: https://en.wikipedia.org/wiki/Client-to-client_protocol message = Message("id", MagicMock(), "nick", "channel", "network", "\x01ACTION waves goodbye\x01") meeting = Meeting("n", "c", "n") tracked = meeting.track_message(message) assert meeting.nicks["nick"] == 1 assert tracked in meeting.messages assert tracked.id is not None assert tracked.sender == "nick" assert tracked.payload == "waves goodbye" assert tracked.action is True
def test_track_attendee(self): meeting = Meeting("n", "c", "n") meeting.track_attendee("one", None) assert meeting.nicks["one"] == 0 assert meeting.aliases["one"] is None meeting.track_attendee("two", "three") assert meeting.nicks["two"] == 0 assert meeting.aliases["two"] == "three" meeting.track_attendee("one", "four") assert meeting.nicks["one"] == 0 assert meeting.aliases["one"] == "four" meeting.track_attendee("five", "five") assert meeting.nicks["five"] == 0 assert meeting.aliases[ "five"] is None # an equivalent alias is not tracked
def test_constructor(self): before = datetime.now(utc) meeting = Meeting("nick", "channel", "network") assert meeting.id is not None assert meeting.name == "channel" assert meeting.founder == "nick" assert meeting.channel == "channel" assert meeting.network == "network" assert meeting.chair == "nick" assert meeting.chairs == ["nick"] assert meeting.nicks == {"nick": 0} assert meeting.start_time >= before assert meeting.end_time is None assert meeting.original_topic is None assert meeting.current_topic is None assert meeting.messages == [] assert meeting.events == [] assert meeting.aliases == {} assert meeting.key() == "channel/network" assert meeting.active is False
def test_derive_locations_with_attempted_path_traversal_relative(self): config = Config( conf_file=None, log_dir="/data/meetings/hcoop", url_prefix="https://whatever", timezone="UTC", pattern="%Y/../../%m%d.%H%M", ) meeting = Meeting(id="i", name="n", founder="f", channel="c", network="n", start_time=datetime(2021, 3, 7, 13, 14, 0)) with pytest.raises(ValueError): derive_locations(config, meeting)
def test_derive_locations_with_attempted_path_traversal_absolute(self): config = Config(conf_file=None, log_dir="/data/meetings/hcoop", url_prefix="https://whatever", timezone="UTC", pattern="/%Y%m%d.%H%M") meeting = Meeting(id="i", name="n", founder="f", channel="c", network="n", start_time=datetime(2021, 3, 7, 13, 14, 0)) locations = derive_locations(config, meeting) assert locations.log.path == "/data/meetings/hcoop/20210307.1314.log.html" assert locations.log.url == "https://whatever/20210307.1314.log.html" assert locations.minutes.path == "/data/meetings/hcoop/20210307.1314.html" assert locations.minutes.url == "https://whatever/20210307.1314.html"
def test_pop_event(self): meeting = Meeting("n", "c", "n") timestamp = MagicMock() message = MagicMock(timestamp=timestamp) start = meeting.track_event(event_type=EventType.START_MEETING, message=message) assert start in meeting.events vote = meeting.track_event(event_type=EventType.VOTE, message=message) assert vote in meeting.events assert meeting.pop_event() is vote assert start in meeting.events assert meeting.pop_event() is None
def test_derive_locations_with_subsitution_variables(self): config = Config( conf_file=None, log_dir="/data/meetings/hcoop", url_prefix="https://whatever", timezone="UTC", pattern="{id}-{name}-{founder}-{channel}-{network}", ) meeting = Meeting(id="i", name="n", founder="f", channel="c", network="n", start_time=datetime(2021, 3, 7, 13, 14, 0)) locations = derive_locations(config, meeting) assert locations.log.path == "/data/meetings/hcoop/i-n-f-c-n.log.html" assert locations.log.url == "https://whatever/i-n-f-c-n.log.html" assert locations.minutes.path == "/data/meetings/hcoop/i-n-f-c-n.html" assert locations.minutes.url == "https://whatever/i-n-f-c-n.html"
def test_derive_locations_with_constant_pattern(self): config = Config( conf_file=None, log_dir="/data/meetings/hcoop", url_prefix="https://whatever", timezone="UTC", pattern="constant", ) meeting = Meeting(id="i", name="n", founder="f", channel="c", network="n", start_time=datetime(2021, 3, 7, 13, 14, 0)) locations = derive_locations(config, meeting) assert locations.log.path == "/data/meetings/hcoop/constant.log.html" assert locations.log.url == "https://whatever/constant.log.html" assert locations.minutes.path == "/data/meetings/hcoop/constant.html" assert locations.minutes.url == "https://whatever/constant.html"
def test_derive_locations_with_normalization(self): config = Config( conf_file=None, log_dir="/data/meetings/hcoop", url_prefix="https://whatever", timezone="UTC", pattern="{name}", ) meeting = Meeting( id="i", name= r"!@#$%^&*()+=][}{}~`?<>,{network}\\", # more than 1 consecutive bad char is normalized to single _ founder="f", channel="c", network="n", start_time=datetime(2021, 3, 7, 13, 14, 0), ) locations = derive_locations(config, meeting) assert locations.log.path == "/data/meetings/hcoop/_network_.log.html" assert locations.log.url == "https://whatever/_network_.log.html" assert locations.minutes.path == "/data/meetings/hcoop/_network_.html" assert locations.minutes.url == "https://whatever/_network_.html"
def _meeting() -> Meeting: """Generate a semi-realistic meeting that can be used for unit tests""" # Initialize the meeting meeting = Meeting(founder="pronovic", channel="#hcoop", network="network") # this gets us some data in the attendees section without having to add tons of messages meeting.track_nick("unknown_lamer", 13) meeting.track_nick("layline", 32) meeting.track_nick("bhkl", 3) # Start the meeting meeting.active = True meeting.start_time = _time(0) tracked = meeting.track_message( message=_message(0, "pronovic", "#startmeeting", 0)) meeting.track_event(event_type=EventType.START_MEETING, message=tracked) # these messages and events will be associated with the prologue, because no topic has been set yet tracked = meeting.track_message(message=_message( 1, "pronovic", "Hello everyone, is it ok to get started?", 32)) tracked = meeting.track_message( message=_message(2, "unknown_lamer", "Yeah, let's do it", 97)) tracked = meeting.track_message(message=_message( 3, "pronovic", "#link Agenda at https://whatever/agenda.html like usual", 123)) meeting.track_event( event_type=EventType.LINK, message=tracked, operand="Agenda at https://whatever/agenda.html like usual") # these messages and events are associated with the attendance topic # note that we track attendees manually since that's what would be done by the command interpreter tracked = meeting.track_message( message=_message(4, "pronovic", "#topic Attendance", 125)) meeting.track_event(event_type=EventType.TOPIC, message=tracked, operand="Attendance") tracked = meeting.track_message(message=_message( 5, "pronovic", 'If you are present please write "#here $hcoop_username"', 126)) tracked = meeting.track_message(message=_message( 6, "pronovic", "#here Pronovic", 127)) # note: alias != nick meeting.track_event(event_type=EventType.ATTENDEE, message=tracked, operand="Pronovic") meeting.track_attendee(nick="pronovic", alias="Pronovic") tracked = meeting.track_message(message=_message( 7, "unknown_lamer", "#here Clinton Alias", 128)) # note: alias != nick meeting.track_event(event_type=EventType.ATTENDEE, message=tracked, operand="Clinton Alias") meeting.track_attendee(nick="unknown_lamer", alias="Clinton Alias") tracked = meeting.track_message(message=_message( 8, "keverets", "#here keverets", 129)) # note: alias == nick meeting.track_event(event_type=EventType.ATTENDEE, message=tracked, operand="keverets") meeting.track_attendee(nick="keverets", alias="keverets") tracked = meeting.track_message(message=_message( 9, "layline", "#here", 130)) # note: no alias, so it's set to nick meeting.track_event(event_type=EventType.ATTENDEE, message=tracked, operand="layline") meeting.track_attendee(nick="layline", alias="layline") tracked = meeting.track_message( message=_message(10, "pronovic", "Thanks, everyone", 130)) # these messages and events are associated with the first topic tracked = meeting.track_message( message=_message(11, "pronovic", "#topic The first topic", 199)) meeting.track_event(event_type=EventType.TOPIC, message=tracked, operand="The first topic") tracked = meeting.track_message(message=_message( 12, "pronovic", "Does anyone have any discussion?", 231)) tracked = meeting.track_message( message=_message(13, "layline", "Is this important?", 232)) tracked = meeting.track_message( message=_message(14, "unknown_lamer", "Yes it is", 299)) tracked = meeting.track_message( message=_message(15, "pronovic", "#info moving on then", 305)) meeting.track_event(event_type=EventType.INFO, message=tracked, operand="moving on then") # these messages and events are associated with the second topic tracked = meeting.track_message( message=_message(16, "pronovic", "#topic The second topic", 332)) meeting.track_event(event_type=EventType.TOPIC, message=tracked, operand="The second topic") tracked = meeting.track_message(message=_message( 17, "layline", "\x01unknown_lamer: I need you for this action\x01", 334)) tracked = meeting.track_message(message=_message( 18, "pronovic", "#action clinton alias will work with layline on this", 401)) meeting.track_event(event_type=EventType.ACTION, message=tracked, operand="clinton alias will work with layline on this") # these messages and events are associated with the third topic tracked = meeting.track_message( message=_message(19, "pronovic", "#topic The third topic", 407)) meeting.track_event(event_type=EventType.TOPIC, message=tracked, operand="The third topic") tracked = meeting.track_message(message=_message( 20, "pronovic", "#idea we should improve MeetBot", 414)) meeting.track_event(event_type=EventType.IDEA, message=tracked, operand="we should improve MeetBot") tracked = meeting.track_message(message=_message( 21, "pronovic", "I'll just take this one myself", 435)) tracked = meeting.track_message(message=_message( 22, "pronovic", "#action Pronovic will deal with it", 449)) meeting.track_event(event_type=EventType.ACTION, message=tracked, operand="Pronovic will deal with it") # these messages and events are associated with the final topic tracked = meeting.track_message( message=_message(23, "pronovic", "#topic Cross-site Scripting", 453)) tracked = meeting.track_message(message=_message( 24, "pronovic", "#action <script>alert('malicious')</script>", 497)) meeting.track_event(event_type=EventType.ACTION, message=tracked, operand="<script>alert('malicious')</script>") tracked = meeting.track_message( message=_message(25, "pronovic", "#motion the motion", 502)) meeting.track_event(event_type=EventType.MOTION, message=tracked, operand="the motion") tracked = meeting.track_message( message=_message(26, "pronovic", "#vote +1", 553)) meeting.track_event(event_type=EventType.VOTE, message=tracked, operand=VotingAction.IN_FAVOR) tracked = meeting.track_message( message=_message(27, "unknown_lamer", "#vote +1", 555)) meeting.track_event(event_type=EventType.VOTE, message=tracked, operand=VotingAction.IN_FAVOR) tracked = meeting.track_message( message=_message(28, "layline", "#vote -1", 557)) meeting.track_event(event_type=EventType.VOTE, message=tracked, operand=VotingAction.OPPOSED) tracked = meeting.track_message( message=_message(29, "pronovic", "#close", 559)) meeting.track_event(event_type=EventType.ACCEPTED, message=tracked, operand="Motion accepted: 2 in favor to 1 opposed") # End the meeting tracked = meeting.track_message( message=_message(30, "pronovic", "#endmeeting", 567)) meeting.track_event(event_type=EventType.END_MEETING, message=tracked) meeting.active = False meeting.end_time = _time(567) return meeting
def test_chair_behavior(self): meeting = Meeting("nick", "channel", "network") assert meeting.chair == "nick" assert meeting.chairs == ["nick"] assert meeting.nicks == {"nick": 0} assert meeting.is_chair("nick") is True assert meeting.is_chair("xxx") is False assert meeting.is_chair("yyy") is False assert meeting.is_chair("zzz") is False meeting.add_chair("yyy") assert meeting.founder == "nick" assert meeting.chair == "yyy" assert meeting.chairs == ["nick", "yyy"] assert meeting.nicks == {"nick": 0, "yyy": 0} assert meeting.is_chair("nick") is True assert meeting.is_chair("xxx") is False assert meeting.is_chair("yyy") is True assert meeting.is_chair("zzz") is False meeting.add_chair("xxx", primary=False) assert meeting.founder == "nick" assert meeting.chair == "yyy" assert meeting.chairs == ["nick", "xxx", "yyy"] assert meeting.nicks == {"nick": 0, "xxx": 0, "yyy": 0} assert meeting.is_chair("nick") is True assert meeting.is_chair("xxx") is True assert meeting.is_chair("yyy") is True assert meeting.is_chair("zzz") is False meeting.add_chair("nick") assert meeting.founder == "nick" assert meeting.chair == "nick" assert meeting.chairs == ["nick", "xxx", "yyy"] assert meeting.nicks == {"nick": 0, "xxx": 0, "yyy": 0} assert meeting.is_chair("nick") is True assert meeting.is_chair("xxx") is True assert meeting.is_chair("yyy") is True assert meeting.is_chair("zzz") is False meeting.add_chair("zzz") assert meeting.founder == "nick" assert meeting.chair == "zzz" assert meeting.chairs == ["nick", "xxx", "yyy", "zzz"] assert meeting.nicks == {"nick": 0, "xxx": 0, "yyy": 0, "zzz": 0} assert meeting.is_chair("nick") is True assert meeting.is_chair("xxx") is True assert meeting.is_chair("yyy") is True assert meeting.is_chair("zzz") is True meeting.remove_chair("yyy") assert meeting.founder == "nick" assert meeting.chair == "zzz" assert meeting.chairs == ["nick", "xxx", "zzz"] assert meeting.nicks == {"nick": 0, "xxx": 0, "yyy": 0, "zzz": 0} assert meeting.is_chair("nick") is True assert meeting.is_chair("xxx") is True assert meeting.is_chair("yyy") is False assert meeting.is_chair("zzz") is True meeting.remove_chair("nick") # you can't remove the founder assert meeting.founder == "nick" assert meeting.chair == "zzz" assert meeting.chairs == ["nick", "xxx", "zzz"] assert meeting.nicks == {"nick": 0, "xxx": 0, "yyy": 0, "zzz": 0} assert meeting.is_chair("nick") is True assert meeting.is_chair("xxx") is True assert meeting.is_chair("yyy") is False assert meeting.is_chair("zzz") is True meeting.remove_chair( "zzz") # removing the primary chair makes the founder the primary assert meeting.founder == "nick" assert meeting.chair == "nick" assert meeting.chairs == ["nick", "xxx"] assert meeting.nicks == {"nick": 0, "xxx": 0, "yyy": 0, "zzz": 0} assert meeting.is_chair("nick") is True assert meeting.is_chair("xxx") is True assert meeting.is_chair("yyy") is False assert meeting.is_chair("zzz") is False
def test_meeting_key(self): assert Meeting.meeting_key("channel", "network") == "channel/network"