import pytest
from mastodon.Mastodon import MastodonAPIError, MastodonNotFoundError
import datetime
try:
import zoneinfo
timezone = zoneinfo.ZoneInfo
except:
import pytz
timezone = pytz.timezone
import vcr
import time
import pickle
import os
@pytest.mark.vcr()
def test_status(status, api):
status2 = api.status(status['id'])
assert status2
@pytest.mark.vcr()
def test_status_reply(status, api2):
status2 = api2.status_reply(status, "same!")
try:
assert status2
assert status2.mentions[0].id == status.account.id
finally:
api2.status_delete(status2['id'])
@pytest.mark.vcr()
def test_status_empty(api):
with pytest.raises(MastodonAPIError):
api.status_post('')
@pytest.mark.vcr()
def test_status_missing(api):
with pytest.raises(MastodonNotFoundError):
api.status(0)
# Messy and will only work if there is an internet connection that is decent, obviously.
# Also, deprecated, but still a good test (Mastodon.py tries to fake the old behaviour
# internally)
@pytest.mark.vcr()
def test_status_card(api):
import time
status = api.status_post("http://example.org/")
time.sleep(5) # Card generation may take time
card = api.status_card(status['id'])
try:
assert card
assert card.url == "http://example.org/"
finally:
api.status_delete(status['id'])
# Old-version card api
def test_status_card_pre_2_9_2(api):
with vcr.use_cassette('test_status_card.yaml', cassette_library_dir='tests/cassettes_pre_2_9_2', record_mode='none'):
import time
status = api.status_post("http://example.org/")
time.sleep(5) # Card generation may take time
api.verify_minimum_version("2.9.2", cached=False)
card = api.status_card(status['id'])
try:
assert card
assert card.url == "http://example.org/"
finally:
api.status_delete(status['id'])
@pytest.mark.vcr()
def test_status_context(status, api):
context = api.status_context(status['id'])
assert context
@pytest.mark.vcr()
def test_status_reblogged_by(status, api):
api.status_reblog(status['id'])
reblogs = api.status_reblogged_by(status['id'])
assert reblogs
@pytest.mark.vcr()
def test_status_reblog_visibility(status, api):
reblog_result = api.status_reblog(status['id'], visibility = 'unlisted')
assert reblog_result.visibility == 'unlisted'
@pytest.mark.vcr()
def test_status_favourited_by(status, api):
api.status_favourite(status['id'])
favourites = api.status_favourited_by(status['id'])
assert favourites
@pytest.mark.vcr()
def test_toot(api):
status = api.toot('Toot!')
try:
assert status
finally:
api.status_delete(status['id'])
@pytest.mark.vcr()
@pytest.mark.parametrize('visibility', (None, 'direct', 'private', 'unlisted', 'public',
pytest.param('foobar', marks=pytest.mark.xfail(strict=True))))
@pytest.mark.parametrize('spoiler_text', (None, 'Content warning'))
def test_status_post(api, visibility, spoiler_text):
status = api.status_post(
'Toot!',
visibility=visibility,
spoiler_text=spoiler_text)
try:
assert status
if visibility:
assert status['visibility'] == visibility
if spoiler_text:
assert status['spoiler_text'] == spoiler_text
finally:
api.status_delete(status['id'])
@pytest.mark.vcr()
def test_status_reblog_unreblog(status, api):
reblog = api.status_reblog(status['id'])
assert reblog
status = reblog['reblog']
assert status['reblogged']
status = api.status_unreblog(status['id'])
assert not status['reblogged']
@pytest.mark.vcr()
def test_status_fav_unfav(status, api):
status = api.status_favourite(status['id'])
assert status['favourited']
status = api.status_unfavourite(status['id'])
assert not status['favourited']
@pytest.mark.vcr()
def test_favourites(api):
favs = api.favourites()
assert isinstance(favs, list)
@pytest.mark.vcr()
def test_status_mute_unmute(status, api):
status = api.status_mute(status['id'])
assert status['muted']
status = api.status_unmute(status['id'])
assert not status['muted']
@pytest.mark.vcr()
def test_status_pin_unpin(status, api):
status = api.status_pin(status['id'])
assert status['pinned']
status = api.status_unpin(status['id'])
assert not status['pinned']
@pytest.mark.vcr(match_on=['path'])
def test_scheduled_status(api):
base_time = datetime.datetime(4000, 1, 1, 12, 13, 14, 0, timezone("Etc/GMT+2"))
the_future = base_time + datetime.timedelta(minutes=20)
scheduled_toot = api.status_post("please ensure adequate headroom", scheduled_at=the_future)
assert scheduled_toot
the_immediate_future = base_time + datetime.timedelta(minutes=10)
scheduled_toot_2 = api.scheduled_status_update(scheduled_toot, the_immediate_future)
assert scheduled_toot_2
assert scheduled_toot_2.id == scheduled_toot.id
assert scheduled_toot_2.scheduled_at < scheduled_toot.scheduled_at
scheduled_toot_list = api.scheduled_statuses()
assert scheduled_toot_2.id in map(lambda x: x.id, scheduled_toot_list)
scheduled_toot_3 = api.scheduled_status(scheduled_toot.id)
assert scheduled_toot_2.id == scheduled_toot_3.id
api.scheduled_status_delete(scheduled_toot_2)
scheduled_toot_list_2 = api.scheduled_statuses()
assert not scheduled_toot_2.id in map(lambda x: x.id, scheduled_toot_list_2)
if os.path.exists("tests/cassettes/test_scheduled_status_datetimeobjects.pkl"):
the_very_immediate_future = datetime.datetime.fromtimestamp(pickle.load(open("tests/cassettes/test_scheduled_status_datetimeobjects.pkl", 'rb')))
else:
the_very_immediate_future = datetime.datetime.now() + datetime.timedelta(seconds=5)
pickle.dump(the_very_immediate_future.timestamp(), open("tests/cassettes/test_scheduled_status_datetimeobjects.pkl", 'wb'))
scheduled_toot_4 = api.status_post("please ensure adequate headroom", scheduled_at=the_very_immediate_future)
time.sleep(15)
statuses = api.timeline_home()
scheduled_toot_list_3 = api.scheduled_statuses()
assert scheduled_toot_4.id in map(lambda x: x.id, statuses)
assert not scheduled_toot_4.id in map(lambda x: x.id, scheduled_toot_list_3)
# The following two tests need to be manually (!) ran 10 minutes apart when recording.
# Sorry, I can't think of a better way to test scheduled statuses actually work as intended.
@pytest.mark.vcr(match_on=['path'])
def test_scheduled_status_long_part1(api):
with vcr.use_cassette('test_scheduled_status_long_part1.yaml', cassette_library_dir='tests/cassettes_special', record_mode='once'):
if os.path.exists("tests/cassettes_special/test_scheduled_status_long_datetimeobjects.pkl"):
the_medium_term_future = datetime.datetime.fromtimestamp(pickle.load(open("tests/cassettes_special/test_scheduled_status_long_datetimeobjects.pkl", 'rb')))
else:
the_medium_term_future = datetime.datetime.now() + datetime.timedelta(minutes=6)
pickle.dump(the_medium_term_future.timestamp(), open("tests/cassettes_special/test_scheduled_status_long_datetimeobjects.pkl", 'wb'))
scheduled_toot = api.status_post(f"please ensure maximum headroom at {the_medium_term_future}", scheduled_at=the_medium_term_future)
scheduled_toot_list = api.scheduled_statuses()
assert scheduled_toot.id in map(lambda x: x.id, scheduled_toot_list)
pickle.dump(scheduled_toot.params.text, open("tests/cassettes_special/test_scheduled_status_long_text.pkl", 'wb'))
@pytest.mark.vcr(match_on=['path'])
def test_scheduled_status_long_part2(api):
with vcr.use_cassette('test_scheduled_status_long_part2.yaml', cassette_library_dir='tests/cassettes_special', record_mode='once'):
text = pickle.load(open("tests/cassettes_special/test_scheduled_status_long_text.pkl", 'rb'))
statuses = api.timeline_home()
print(text)
found_status = False
for status in statuses:
if text in status.content:
found_status = True
assert found_status
@pytest.mark.vcr()
def test_status_edit(api, api2):
status = api.status_post("the best editor? why, of course it is VS Code")
edit_list_1 = api2.status_history(status)
status_edited = api.status_update(status, "the best editor? why, of course it is the KDE Advanced Text Editor, Kate")
status_result = api2.status(status)
edit_list_2 = api2.status_history(status)
assert len(edit_list_1) == 0
assert len(edit_list_2) == 2
assert "the best editor? why, of course it is the KDE Advanced Text Editor, Kate" in status_result.content
source = api2.status_source(status)
assert source.text == "the best editor? why, of course it is the KDE Advanced Text Editor, Kate"