Mercurial > silorider
diff tests/test_silos_bluesky.py @ 46:ac5911e76ef2
Add Bluesky silo
Currently supports:
- Uploading images
- URL facets
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Sun, 20 Aug 2023 11:20:37 -0700 |
parents | |
children | 486affad656e |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test_silos_bluesky.py Sun Aug 20 11:20:37 2023 -0700 @@ -0,0 +1,184 @@ +import pytest +import atproto.xrpc_client.models as atprotomodels +from .mockutil import mock_urllib + + +def test_one_article(cli, feedutil, bskymock): + feed = cli.createTempFeed(feedutil.makeFeed( + """<h1 class="p-name">A new article</h1> +<div class="e-content"> +<p>This is the text of the article.</p> +<p>It has 2 paragraphs.</p> +</div> +<a class="u-url" href="https://example.org/a-new-article">permalink</a>""" + )) + + cli.appendSiloConfig('test', 'bluesky') + cli.setFeedConfig('feed', feed) + bskymock.installCredentials(cli, 'test') + + ctx, _ = cli.run('process') + assert ctx.cache.wasPosted('test', 'https://example.org/a-new-article') + post = ctx.silos[0].client.posts[0] + assert post == ('A new article https://example.org/a-new-article', + None, None) + + +def test_one_micropost(cli, feedutil, bskymock): + feed = cli.createTempFeed(feedutil.makeFeed( + """<p class="p-name">This is a quick update.</p> +<a class="u-url" href="/01234.html">permalink</a>""" + )) + + cli.appendSiloConfig('test', 'bluesky') + cli.setFeedConfig('feed', feed) + bskymock.installCredentials(cli, 'test') + + ctx, _ = cli.run('process') + assert ctx.cache.wasPosted('test', '/01234.html') + post = ctx.silos[0].client.posts[0] + assert post == ("This is a quick update.", None, None) + + +def test_one_micropost_with_one_photo(cli, feedutil, bskymock, monkeypatch): + feed = cli.createTempFeed(feedutil.makeFeed( + """<p class="p-name">This is a quick photo update.</p> +<div> + <a class="u-photo" href="/fullimg.jpg"><img src="/thumbimg.jpg"/></a> +</div> +<a class="u-url" href="/01234.html">permalink</a>""" + )) + + cli.appendSiloConfig('test', 'bluesky') + cli.setFeedConfig('feed', feed) + bskymock.installCredentials(cli, 'test') + + with monkeypatch.context() as m: + import silorider.silos.bluesky + mock_urllib(m) + m.setattr(silorider.silos.bluesky.BlueskySilo, '_media_callback', + _patched_media_callback) + ctx, _ = cli.run('process') + + assert ctx.cache.wasPosted('test', '/01234.html') + blob = ctx.silos[0].client.blobs[0] + assert blob == ('/retrieved/fullimg.jpg', None) + post = ctx.silos[0].client.posts[0] + embed = atprotomodels.AppBskyEmbedImages.Main(images=[1]) + assert post == ("This is a quick photo update.", embed, None) + + +def test_one_micropost_with_two_photos(cli, feedutil, bskymock, monkeypatch): + feed = cli.createTempFeed(feedutil.makeFeed( + """<p class="p-name">This is a photo update with 2 photos.</p> +<div> + <a class="u-photo" href="/fullimg1.jpg"><img src="/thumbimg1.jpg"/></a> + <a class="u-photo" href="/fullimg2.jpg"><img src="/thumbimg2.jpg"/></a> +</div> +<a class="u-url" href="/01234.html">permalink</a>""" + )) + + cli.appendSiloConfig('test', 'bluesky') + cli.setFeedConfig('feed', feed) + bskymock.installCredentials(cli, 'test') + + with monkeypatch.context() as m: + import silorider.silos.bluesky + mock_urllib(m) + m.setattr(silorider.silos.bluesky.BlueskySilo, '_media_callback', + _patched_media_callback) + ctx, _ = cli.run('process') + + assert ctx.cache.wasPosted('test', '/01234.html') + blob = ctx.silos[0].client.blobs[0] + assert blob == ('/retrieved/fullimg1.jpg', None) + blob = ctx.silos[0].client.blobs[1] + assert blob == ('/retrieved/fullimg2.jpg', None) + post = ctx.silos[0].client.posts[0] + embed = atprotomodels.AppBskyEmbedImages.Main(images=[1, 2]) + assert post == ("This is a photo update with 2 photos.", embed, None) + + +def test_one_micropost_with_links(cli, feedutil, bskymock): + cli.appendSiloConfig('test', 'bluesky') + bskymock.installCredentials(cli, 'test') + + feed = cli.createTempFeed(feedutil.makeFeed( + """<p class="p-name">This is a link: http://example.org/blah</p> +<a class="u-url" href="/01234.html">permalink</a>""")) + + cli.setFeedConfig('feed', feed) + ctx, _ = cli.run('process') + post = ctx.silos[0].client.posts[0] + assert post[0] == "This is a link: http://example.org/blah" + assert post[2] == None + + feed = cli.createTempFeed(feedutil.makeFeed( + """<p class="e-content">This is another link: <a href="http://example.org/blah">http://example.org/blah</a></p> +<a class="u-url" href="/01234.html">permalink</a>""")) # NOQA + cli.setFeedConfig('feed', feed) + ctx, _ = cli.run('process') + post = ctx.silos[0].client.posts[0] + assert post[0] == "This is another link: http://example.org/blah" # NOQA + facet = _make_link_facet('http://example.org/blah', 22, 45) + assert post[2] == [facet] + + feed = cli.createTempFeed(feedutil.makeFeed( + """<p class="e-content">This is yet <a href="http://example.org/blah">another link</a></p> +<a class="u-url" href="/01234.html">permalink</a>""")) # NOQA + cli.setFeedConfig('feed', feed) + ctx, _ = cli.run('process') + post = ctx.silos[0].client.posts[0] + assert post[0] == "This is yet another link" # NOQA + facet = _make_link_facet('http://example.org/blah', 12, 24) + assert post[2] == [facet] + + +def _make_link_facet(url, start, end): + return atprotomodels.AppBskyRichtextFacet.Main( + features=[atprotomodels.AppBskyRichtextFacet.Link(uri=url)], + index=atprotomodels.AppBskyRichtextFacet.ByteSlice( + byteStart=start, byteEnd=end), + ) + + +def _patched_media_callback(self, tmpfile, mt, url, desc): + return self.client.upload_blob(tmpfile, desc) + + +@pytest.fixture(scope='session') +def bskymock(): + from silorider.silos.bluesky import BlueskySilo + BlueskySilo._CLIENT_CLASS = BlueskyMock + return BlueskyMockUtil() + + +class BlueskyMock: + def __init__(self, base_url): + # base_url is unused here. + self.posts = [] + self.blobs = [] + + def login(self, email, password): + assert email == 'TEST_EMAIL' + assert password == 'TEST_PASSWORD' + + def upload_blob(self, tmpfile, desc): + self.blobs.append((tmpfile, desc)) + return len(self.blobs) + + def send_post(self, text, embed=None, facets=None): + self.posts.append((text, embed, facets)) + + +class BlueskyMockUtil: + def installCredentials(self, cli, silo_name): + def do_install_credentials(ctx): + ctx.cache.setCustomValue( + '%s_email' % silo_name, + 'TEST_EMAIL') + ctx.cache.setCustomValue( + '%s_password' % silo_name, + 'TEST_PASSWORD') + + cli.preExecHook(do_install_credentials)