def get_nomination_comment(event: Event, discussions_json: object) -> str: """Returns the text of the last discussion by the user if it is a praise or mapper note, otherwise any hype prior to the event by the user, if any, else None.""" latest_discussion_json = None latest_hype_discussion_json = None for discussion_json in get_map_page_discussion_jsons(event.beatmapset, discussions_json): if discussion_json is None: continue latest_time = from_string(latest_discussion_json["created_at"]) if latest_discussion_json else None current_time = from_string(discussion_json["created_at"]) if ( discussion_json["user_id"] == event.user.id and current_time < event.time and (not latest_time or current_time > latest_time) ): latest_discussion_json = discussion_json if discussion_json["message_type"] == types.HYPE: latest_hype_discussion_json = discussion_json if latest_discussion_json and latest_discussion_json["message_type"] in [types.PRAISE, types.NOTE]: return latest_discussion_json["posts"][0]["message"] if latest_hype_discussion_json: return latest_hype_discussion_json["posts"][0]["message"] return None
async def test_retrieve_beatmapset_events(scraper_test_database): beatmapset = Beatmapset(3, "artist", "title", User(4, "creator"), ["osu"], genre="g", language="l") nom_event = Event("nominate", from_string("2020-01-01 00:00:00"), beatmapset, user=User(1, "someone")) qual_event = Event("qualify", from_string("2020-01-01 05:00:00"), beatmapset, user=User(2, "sometwo")) suggestion_event = Event("suggestion", from_string("2020-01-01 01:00:00"), beatmapset, user=User(3, "somethree")) scraper_test_database.insert_event(suggestion_event) scraper_test_database.insert_event(nom_event) scraper_test_database.insert_event(qual_event) events = await scraper_test_database.retrieve_beatmapset_events(beatmapset) assert nom_event in events assert qual_event in events assert suggestion_event in events
async def test_retrieve_beatmapset_events_cache(scraper_test_database): beatmapset = Beatmapset(3, "artist", "title", User(4, "creator"), ["osu"], genre="g", language="l") nom_event = Event("nominate", from_string("2020-01-01 00:00:00"), beatmapset, user=User(1, "someone")) qual_event = Event("qualify", from_string("2020-01-01 05:00:00"), beatmapset, user=User(2, "sometwo")) scraper_test_database.insert_event(nom_event) scraper_test_database.insert_event(qual_event) await scraper_test_database.retrieve_beatmapset_events(beatmapset) assert db_module.beatmapset_event_cache[SCRAPER_TEST_DB_NAME][3] == [ qual_event, nom_event ] db_module.clear_cache(SCRAPER_TEST_DB_NAME) assert not db_module.beatmapset_event_cache[SCRAPER_TEST_DB_NAME]
def test_format_timeago_not_bold(): with mock.patch("bot.formatter.datetime") as mock_datetime: mock_datetime.utcnow.return_value = from_string("2020-01-01 05:30:06") mock_datetime.side_effect = datetime assert format_timeago(from_string("2020-01-01 00:00:00"), bold=False) == "5 hours ago"
def test_update_beatmapset_status_nom_qual_dq(test_database): creator = User(3, name="test") user1 = User(1, name="someone") user2 = User(2, name="sometwo") beatmapset = Beatmapset(1, creator=creator, allow_api=False) event1 = Event("nominate", from_string("2020-01-01 00:00:00"), beatmapset, user=user1) event2 = Event("qualify", from_string("2020-01-01 00:01:00"), beatmapset, user=user2) event3 = Event("disqualify", from_string("2020-01-02 00:00:00"), beatmapset, user=user1) test_database.insert_event(event1) test_database.insert_event(event2) test_database.insert_event(event3) retrieved_status = test_database.retrieve_beatmapset_status( "beatmapset_id=%s ORDER BY time DESC", (beatmapset.id, )) assert retrieved_status.status == "pending" assert retrieved_status.nominators == [] retrieved_status = test_database.retrieve_beatmapset_status( "beatmapset_id=%s AND time=%s", (beatmapset.id, from_string("2020-01-01 00:01:00"))) assert retrieved_status.status == "qualified" assert retrieved_status.nominators == [user1, user2]
async def test_history_truncated(test_database): beatmapset = Beatmapset(3, "artist", "title", User(4, "mapper"), ["osu"], allow_api=False) nom_event = Event("nominate", from_string("2020-01-01 00:00:00"), beatmapset, user=User(1, "someone")) qual_event = Event("qualify", from_string("2020-01-01 05:00:00"), beatmapset, user=User(2, "sometwo")) for _ in range(20): test_database.insert_event(nom_event) nom_event.time += timedelta(seconds=15) test_database.insert_event(qual_event) history = await format_history(beatmapset, length_limit=200, database=test_database) expected_history = "\n..." for _ in range(10): expected_history += ":thought_balloon: " assert history == expected_history + ":heart:" assert len(history) <= 200
def get_discussion_events(page: int = 1, limit: int = 50): if page == 1: yield Event("problem", from_string("2020-01-01 03:00:00"), beatmapset, discussion_dq, user=User(2, "sometwo"), content="no wait") yield Event("hype", from_string("2020-01-01 02:30:00"), beatmapset, user=User(2, "sometwo"), content="hype") yield Event("praise", from_string("2020-01-01 02:00:00"), beatmapset, user=User(2, "sometwo"), content="amazing") if page == 2: yield Event("issue_resolve", from_string("2020-01-01 01:00:00"), beatmapset, discussion, user=User(1, "someone")) yield Event("praise", from_string("2020-01-01 00:00:00"), beatmapset, user=User(2, "sometwo"), content="wow")
def get_reply_events(page: int = 1, limit: int = 50): if page == 1: yield Event("reply", from_string("2020-01-01 01:04:00"), beatmapset, user=User(2, "sometwo"), content="thanks") yield Event("reply", from_string("2020-01-01 01:00:00"), beatmapset, user=User(1, "someone"), content="hi") yield Event("reply", from_string("2020-01-01 00:31:00"), beatmapset, discussion, user=User(2, "sometwo"), content="say hi back") if page == 2: yield Event("reply", from_string("2020-01-01 00:30:00"), beatmapset, discussion, user=User(1, "someone"), content="yes?") yield Event("reply", from_string("2020-01-01 00:00:00"), beatmapset, discussion, user=User(2, "sometwo"), content="please reply")
def test_parse_fetch_unchanged(): with mock.patch("scraper.parsers.sev_parser.SCRAPER_DB_NAME", SCRAPER_TEST_DB_NAME): user = User(_id=1, allow_api=False) beatmapset = Beatmapset(_id=2, creator=user, allow_api=False) discussion = Discussion(_id=4, beatmapset=beatmapset, user=user, content="123") Database(SCRAPER_TEST_DB_NAME).insert_discussion(discussion) Database(SCRAPER_TEST_DB_NAME).insert_obv_sev(discussion, obv=1, sev=2) # This event basically does: 1/2 -> 0/2 event = sev_parser.parse(discussion_id=4, obv=0, sev=None, time=from_string("2020-07-22T21:00:00+00:00")) expected_event = Event(_type="sev", time=from_string("2020-07-22T21:00:00+00:00"), beatmapset=beatmapset, discussion=discussion, content="0/2") assert event.type == expected_event.type assert event.time == expected_event.time assert event.beatmapset == expected_event.beatmapset assert event.discussion == expected_event.discussion assert event.content == expected_event.content assert event == expected_event
async def test_history_filtering(test_database): beatmapset = Beatmapset(3, "artist", "title", User(4, "mapper"), ["osu"], allow_api=False) nom_event = Event("nominate", from_string("2020-01-01 00:00:00"), beatmapset, user=User(1, "someone")) qual_event = Event("qualify", from_string("2020-01-01 05:00:00"), beatmapset, user=User(2, "sometwo")) suggestion_event = Event("suggestion", from_string("2020-01-01 01:00:00"), beatmapset, user=User(3, "somethree")) test_database.insert_event(nom_event) test_database.insert_event(qual_event) test_database.insert_event(suggestion_event) # The suggestion event should not appear in the history. history = await format_history(beatmapset, database=test_database) assert history == "\n:thought_balloon: [someone](https://osu.ppy.sh/users/1)\u2000:heart: [sometwo](https://osu.ppy.sh/users/2)"
def test_merge_concurrent_1_second_off(): event1 = Event(_type="nominate", time=timestamp.from_string("2020-01-01 13:00:00"), user=User(1, "someone")) event2 = Event(_type="qualify", time=timestamp.from_string("2020-01-01 13:00:01")) merged_events = merge_concurrent([event1, event2]) assert len(merged_events) == 1 assert merged_events[0].type == event2.type assert merged_events[0].user == event1.user
def test_merge_concurrent_different_times(): event1 = Event(_type="nominate", time=timestamp.from_string("2020-01-01 11:00:00"), user=User(1, "someone")) event2 = Event(_type="qualify", time=timestamp.from_string("2020-01-01 13:00:00")) merged_events = merge_concurrent([event1, event2]) assert len(merged_events) == 2 assert merged_events[0] == event1 assert merged_events[1] == event2
def test_format_timeago(): with mock.patch("bot.formatter.datetime") as mock_datetime: # Mock `datetime.utcnow`, but retain the original datetime class functionality through the `side_effect` attribute. mock_datetime.utcnow.return_value = from_string("2020-01-01 05:30:06") mock_datetime.side_effect = datetime assert format_timeago( from_string("2020-01-01 00:00:00")) == "**5 hours** ago"
def test_from_ISO_8601_to_datetime_raise(): faulty_values = ["", "https://osu.ppy.sh/users/1653229"] with pytest.raises(TypeError): from_string(None) for value in faulty_values: with pytest.raises(ValueError): from_string(value)
def test_merge_concurrent(): event1 = Event(_type="nominate", time=timestamp.from_string("2020-01-01 05:00:00"), user=User(1, "someone")) event2 = Event(_type="qualify", time=timestamp.from_string("2020-01-01 05:00:00")) event3 = Event(_type="something else", time=timestamp.from_string("2020-01-01 07:00:00")) merged_events = merge_concurrent([event1, event2, event3]) assert len(merged_events) == 2 assert merged_events[0].type == event2.type assert merged_events[0].user == event1.user assert merged_events[1] == event3
def test_status_idle(mock_client, mock_reader): mock_reader.latest_event_time = timestamp.from_string( "2020-01-01 00:00:00") with mock.patch("bot.activity.datetime") as mock_datetime: mock_datetime.utcnow.return_value = timestamp.from_string( "2020-01-01 00:30:01") mock_datetime.side_effect = datetime assert get_status(mock_client, mock_reader) == Status.idle
def test_merge_concurrent_different_beatmapsets(): beatmapset1 = Beatmapset(1, "artist", "title", User(1, "someone"), modes=["osu"], genre="g", language="l") beatmapset2 = Beatmapset(2, "artist", "title", User(2, "sometwo"), modes=["osu"], genre="g", language="l") event1 = Event(_type="nominate", time=timestamp.from_string("2020-01-01 13:00:00"), beatmapset=beatmapset1, user=User(1, "someone")) event2 = Event(_type="qualify", time=timestamp.from_string("2020-01-01 13:00:01"), beatmapset=beatmapset2) merged_events = merge_concurrent([event1, event2]) assert len(merged_events) == 2 assert merged_events[0] == event1 assert merged_events[1] == event2
async def test_push_all_new_events(reader): event1 = Event(_type="nominate", time=timestamp.from_string("2020-01-01 01:00:00")) event2 = Event(_type="news", time=timestamp.from_string("2020-01-01 02:00:00")) event3 = Event(_type="nominate", time=timestamp.from_string("2020-01-01 03:00:00")) event4 = Event(_type="qualify", time=timestamp.from_string("2020-01-01 04:00:00")) event5 = Event(_type="news", time=timestamp.from_string("2020-01-01 05:00:00")) reader.database.insert_event(event1) reader.database.insert_event(event2) reader.database.insert_event(event3) reader.database.insert_event(event4) reader.database.insert_event(event5) mapset_scope = Scope("mapset", None) news_scope = Scope("news", None) timestamp.set_last( new_datetime=timestamp.from_string("2020-01-01 00:00:00"), _id=reader._Reader__time_id(mapset_scope)) timestamp.set_last( new_datetime=timestamp.from_string("2020-01-01 00:00:00"), _id=reader._Reader__time_id(news_scope)) await reader._Reader__push_all_new_events() assert received_events == [event1, event3, event4, event2, event5] assert timestamp.get_last(reader._Reader__time_id( mapset_scope)) == timestamp.from_string("2020-01-01 04:00:00") assert timestamp.get_last(reader._Reader__time_id( news_scope)) == timestamp.from_string("2020-01-01 05:00:00")
def test_merge_concurrent_almost_same_time_nominates(): nom_event1 = Event(_type="nominate", time=timestamp.from_string("2020-01-01 05:00:01"), user=User(1, "someone")) nom_event2 = Event(_type="nominate", time=timestamp.from_string("2020-01-01 05:00:00"), user=User(2, "sometwo")) qual_event1 = Event(_type="qualify", time=timestamp.from_string("2020-01-01 05:00:01")) merged_events = merge_concurrent([nom_event1, nom_event2, qual_event1]) assert len(merged_events) == 2 assert merged_events[0].type == nom_event1.type assert merged_events[0].user == nom_event2.user assert merged_events[1].type == qual_event1.type assert merged_events[1].user == nom_event1.user
async def test_populate_from_bnsite_mode_integration(): # Mock datetime such that the eval won't be outdated. with mock.patch("scraper.populator.datetime") as mock_datetime: mock_datetime.utcnow.return_value = from_string("2020-10-24 02:00:00") mock_datetime.side_effect = datetime event = Event("add", from_string("2020-10-22 00:00:00"), user=User(12402453), group=Usergroup(28)) await populate_from_bnsite(event) assert event.content is None assert event.group.mode == "osu"
def test_merge_concurrent_duplicates(): nom_event1 = Event(_type="nominate", time=timestamp.from_string("2020-01-01 05:00:00"), user=User(1, "someone")) nom_event2 = Event(_type="nominate", time=timestamp.from_string("2020-01-01 05:00:00"), user=User(1, "someone")) nom_event3 = Event(_type="nominate", time=timestamp.from_string("2020-01-01 05:00:00"), user=User(1, "someone")) qual_event1 = Event(_type="qualify", time=timestamp.from_string("2020-01-01 05:00:00")) qual_event2 = Event(_type="qualify", time=timestamp.from_string("2020-01-01 05:00:00")) merged_events = merge_concurrent([nom_event1, nom_event2, nom_event3, qual_event1, qual_event2]) assert len(merged_events) == 1 assert merged_events[0].type == qual_event1.type assert merged_events[0].user == nom_event1.user
async def test_events_between_greater_than(reader): event1 = Event(_type="test", time=timestamp.from_string("2020-01-01 05:00:00")) event2 = Event(_type="test", time=timestamp.from_string("2020-01-01 07:00:00")) reader.database.insert_event(event1) reader.database.insert_event(event2) # If we resume at 05:00:00, the event at exactly 05:00:00 should be ignored. events = await reader.events_between(timestamp.from_string("2020-01-01 05:00:00"), timestamp.from_string("2020-01-01 07:00:00")) assert await anext(events, None) == event2 assert await anext(events, None) is None
def test_parse_event_time(event_parser): tests = [[ event_parser.parse_event_time(issue_resolve.tag), from_string("2019-12-05T10:26:54+00:00") ], [ event_parser.parse_event_time(problem.tag), from_string("2019-12-05T16:50:10+00:00") ]] for actual, expected in tests: assert actual == expected
def test_activity_delay_large(mock_client, mock_reader): mock_reader.latest_event_time = timestamp.from_string( "2020-01-01 00:00:00") with mock.patch("bot.activity.datetime") as mock_datetime: mock_datetime.utcnow.return_value = timestamp.from_string( "2020-01-01 04:01:30") mock_datetime.side_effect = datetime assert get_activity( mock_client, mock_reader) == Game("/subscribe | 3 servers | 4 hours delay")
def test_merge_concurrent_same_time_nominates(): # Should pick the last event in the list before the qualify as the new qualify event, so the one by sometwo in this case. nom_event1 = Event(_type="nominate", time=timestamp.from_string("2020-01-01 05:00:00"), user=User(1, "someone")) nom_event2 = Event(_type="nominate", time=timestamp.from_string("2020-01-01 05:00:00"), user=User(2, "sometwo")) qual_event1 = Event(_type="qualify", time=timestamp.from_string("2020-01-01 05:00:00")) merged_events = merge_concurrent([nom_event1, nom_event2, qual_event1]) assert len(merged_events) == 2 assert merged_events[0].type == nom_event1.type assert merged_events[0].user == nom_event1.user assert merged_events[1].type == qual_event1.type assert merged_events[1].user == nom_event2.user
def test_activity_delay_small(mock_client, mock_reader): mock_reader.latest_event_time = timestamp.from_string( "2020-01-01 00:00:00") with mock.patch("bot.activity.datetime") as mock_datetime: mock_datetime.utcnow.return_value = timestamp.from_string( "2020-01-01 00:03:30") mock_datetime.side_effect = datetime # Delay is too small to be worth displaying here. assert get_activity(mock_client, mock_reader) == Game("/subscribe | 3 servers")
async def test_on_event(reader): event1 = Event(_type="hello", time=timestamp.from_string("2020-01-01 05:00:00")) event2 = Event(_type="there", time=timestamp.from_string("2020-01-01 07:00:00")) reader.database.insert_event(event1) reader.database.insert_event(event2) _from = timestamp.from_string("2020-01-01 00:00:00") to = timestamp.from_string("2020-01-01 10:00:00") scope = Scope("any") await reader._Reader__push_events_between(_from, to, scope) assert received_events == [event1, event2] assert reader.latest_event_time == timestamp.from_string("2020-01-01 07:00:00")
async def test_on_events(reader): event1 = Event(_type="hello", time=timestamp.from_string("2020-01-01 05:00:00")) event2 = Event(_type="there", time=timestamp.from_string("2020-01-01 07:00:00")) event3 = Event(_type="hi", time=timestamp.from_string("2020-01-01 11:00:00")) event4 = Event(_type="yes", time=timestamp.from_string("2020-01-01 13:00:00")) event5 = Event(_type="no", time=timestamp.from_string("2020-01-01 15:00:00")) reader.database.insert_event(event1) reader.database.insert_event(event2) reader.database.insert_event(event3) reader.database.insert_event(event4) reader.database.insert_event(event5) start = timestamp.from_string("2020-01-01 00:00:00") middle = timestamp.from_string("2020-01-01 10:00:00") end = timestamp.from_string("2020-01-01 18:00:00") scope = Scope("any") await reader._Reader__push_events_between(start, middle, scope) await reader._Reader__push_events_between(middle, end, scope) assert len(received_event_batches) == 2
def test_merge_concurrent_different_beatmapsets(): beatmapset1 = Beatmapset(1, User(1, "someone"), allow_api=False) beatmapset2 = Beatmapset(2, User(2, "sometwo"), allow_api=False) event1 = Event(_type="nominate", time=timestamp.from_string("2020-01-01 13:00:00"), beatmapset=beatmapset1, user=User(1, "someone")) event2 = Event(_type="qualify", time=timestamp.from_string("2020-01-01 13:00:01"), beatmapset=beatmapset2) merged_events = merge_concurrent([event1, event2]) assert len(merged_events) == 2 assert merged_events[0] == event1 assert merged_events[1] == event2
def test_format_link_no_beatmapset(): event = Event("test", from_string("2020-04-11 20:00:00")) with pytest.raises(ValueError) as err: format_link(event) assert "missing a beatmapset" in str(err)