Skip to content

Commit 6048dc6

Browse files
committed
pytest
1 parent 90494b5 commit 6048dc6

18 files changed

Lines changed: 753 additions & 2 deletions

.github/workflows/pytest.yml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# This workflow will run pytest in the repository directory
2+
# This runs on pull request or on any push to the repository
3+
# based on https://www.youtube.com/watch?v=uFcXrWT4f80
4+
name: Run tests
5+
6+
on:
7+
pull_request:
8+
branches: ['main']
9+
push:
10+
branches: ['main']
11+
release:
12+
types: [prereleased, published]
13+
workflow_dispatch:
14+
15+
jobs:
16+
tests:
17+
runs-on: ubuntu-latest
18+
environment: release
19+
20+
steps:
21+
- uses: actions/checkout@v4
22+
23+
- name: Set up Python 3.12
24+
uses: actions/setup-python@v3
25+
with:
26+
python-version: "3.12"
27+
28+
- name: Install dependencies
29+
run: |
30+
python -m pip install --upgrade pip
31+
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
32+
pip install pytest
33+
pip install cryptography
34+
35+
- name: Test with pytest
36+
env:
37+
FERNET_KEY: ${{ secrets.FERNET_KEY }}
38+
run: |
39+
pytest tests/

tests/test_activity.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import sys
2+
from datetime import datetime, timedelta, timezone
3+
import warnings
4+
5+
6+
7+
def test_activity():
8+
sys.path.insert(0, ".")
9+
import scratchattach as sa
10+
from scratchattach.utils import exceptions
11+
import util
12+
if not util.credentials_available():
13+
warnings.warn("Skipped test_activity because there were no credentials available.")
14+
return
15+
sess = util.session()
16+
17+
# we cannot do assertions, but we can probe for any errors.
18+
messages = sess.messages()
19+
for msg in messages:
20+
print(msg, end=' ')
21+
22+
try:
23+
target = msg.target()
24+
except exceptions.CommentNotFound:
25+
target = None
26+
raise
27+
28+
print(target)
29+
30+
31+
if __name__ == "__main__":
32+
test_activity()

tests/test_auth.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import sys
2+
import os
3+
import warnings
4+
5+
def test_auth():
6+
sys.path.insert(0, ".")
7+
import util
8+
if not util.credentials_available():
9+
warnings.warn("Skipped test_auth because there were no credentials available.")
10+
return
11+
sess = util.session()
12+
13+
assert "FERNET_KEY" in os.environ
14+
assert len(os.environ["FERNET_KEY"]) == 32
15+
assert sess.username == "ScratchAttachV2"

tests/test_comment.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import sys
2+
from datetime import datetime, timedelta, timezone
3+
import warnings
4+
5+
def test_comment():
6+
sys.path.insert(0, ".")
7+
import scratchattach as sa
8+
import util
9+
if not util.credentials_available():
10+
warnings.warn("Skipped test_comment because there were no credentials available.")
11+
return
12+
sess = util.session()
13+
14+
user = sess.connect_linked_user()
15+
proj = sess.connect_project(1108326850)
16+
studio = sess.connect_studio(50809872)
17+
18+
comment = user.comments(limit=1)[0]
19+
20+
assert comment.id == "387076703"
21+
assert comment.source == sa.CommentSource.USER_PROFILE
22+
assert comment.source_id == "ScratchAttachV2"
23+
assert comment.parent_id is None
24+
assert comment.content == "Sample comment"
25+
assert datetime.fromisoformat(comment.datetime_created) - datetime(2025, 8, 25, tzinfo=timezone.utc) < timedelta(days=1)
26+
assert comment.reply_count == 0
27+
assert comment.text == "Sample comment"
28+
29+
comment = proj.comments(limit=1)[0]
30+
31+
assert comment.id == 494890468
32+
assert comment.source == sa.CommentSource.PROJECT
33+
assert comment.source_id == 1108326850
34+
assert comment.parent_id is None
35+
assert comment.content == ("&lt;&amp;;&apos;!\n"
36+
"newline\n"
37+
"testing escaping")
38+
assert datetime.fromisoformat(comment.datetime_created) - datetime(2025, 9, 20, tzinfo=timezone.utc) < timedelta(days=1)
39+
assert comment.reply_count == 0
40+
assert comment.text == ("<&;'!\n"
41+
"newline\n"
42+
"testing escaping")
43+
44+
comment = studio.comments(limit=1)[0]
45+
46+
assert comment.id == 302129887
47+
assert comment.source == sa.CommentSource.STUDIO
48+
assert comment.source_id == 50809872
49+
assert comment.parent_id is None
50+
assert comment.content == "Sample"
51+
assert datetime.fromisoformat(comment.datetime_created) - datetime(2025, 8, 26, tzinfo=timezone.utc) < timedelta(days=1)
52+
assert comment.reply_count == 1
53+
assert not comment.written_by_scratchteam
54+
assert comment.text == "Sample"
55+
56+
comment = comment.replies(limit=1)[0]
57+
58+
assert comment.id == 302129910
59+
assert comment.source == sa.CommentSource.STUDIO
60+
assert comment.source_id == 50809872
61+
assert comment.parent_id == 302129887
62+
assert comment.commentee_id == 58743127
63+
assert comment.content == "text"
64+
assert datetime.fromisoformat(comment.datetime_created) - datetime(2025, 8, 26, tzinfo=timezone.utc) < timedelta(days=1)
65+
assert comment.reply_count == 0
66+
assert not comment.written_by_scratchteam
67+
assert comment.text == "text"
68+
69+
if __name__ == "__main__":
70+
test_comment()

tests/test_editor_integration.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import pprint
2+
import sys
3+
import warnings
4+
5+
def test_project():
6+
sys.path.insert(0, ".")
7+
import scratchattach as sa
8+
import util
9+
if not util.credentials_available():
10+
warnings.warn("Skipped test_project because there were no credentials available.")
11+
return
12+
sess = util.session()
13+
14+
15+
project = sess.connect_project(104)
16+
body = project.body()
17+
body.to_json() # do nothing with the data, just make sure it works.
18+
19+
20+
21+
if __name__ == '__main__':
22+
test_project()

tests/test_editor_obfuscation.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import pprint
2+
import sys
3+
from pathlib import Path
4+
5+
6+
def test_project():
7+
sys.path.insert(0, ".")
8+
import scratchattach as sa
9+
10+
path = Path(__file__).parent.parent / "intro for kelmare (yoda tour) (p2).sb3"
11+
12+
if path.exists():
13+
print(f"loading cached {path}")
14+
body = sa.editor.Project.from_sb3(path.open("rb"))
15+
else:
16+
print(f"Could not find {path}")
17+
project = sa.get_project(1074489898)
18+
body = project.body()
19+
20+
body.obfuscate()
21+
22+
# body.save_json("obfuscated")
23+
body.export("obfuscated.sb3")
24+
25+
26+
if __name__ == '__main__':
27+
test_project()

tests/test_editor_project.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import sys
2+
3+
4+
def test_import():
5+
sys.path.insert(0, ".")
6+
import scratchattach as sa
7+
8+
proj = sa.get_project(104)
9+
body = proj.body()

tests/test_import.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
import sys
22
def test_import():
3-
sys.path.insert(0, ".")
4-
import scratchattach
3+
sys.path.insert(0, ".")
4+
import scratchattach as sa

tests/test_other_apis.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import pprint
2+
import sys
3+
from datetime import datetime, timedelta, timezone
4+
import warnings
5+
6+
7+
def test_other_apis():
8+
sys.path.insert(0, ".")
9+
import scratchattach as sa
10+
from scratchattach.utils import exceptions
11+
import util
12+
13+
assert sa.check_email("example@example.com")
14+
assert not sa.check_email("bad_email")
15+
16+
news = sa.get_news()
17+
found_wiki_wednesday = False
18+
for newsitem in news:
19+
if newsitem["headline"] == "Wiki Wednesday!":
20+
found_wiki_wednesday = True
21+
if not found_wiki_wednesday:
22+
warnings.warn(f"Did not find wiki wednesday! News dict: {news}")
23+
24+
featured_projects = sa.featured_projects()
25+
featured_studios = sa.featured_studios()
26+
top_loved = sa.top_loved()
27+
top_remixed = sa.top_remixed()
28+
newest_projects = sa.newest_projects()
29+
curated_projects = sa.curated_projects()
30+
design_studio_projects = sa.design_studio_projects()
31+
32+
def test_featured_data(_name, data: list[sa.Project | sa.Studio]):
33+
if not data:
34+
warnings.warn(f"Did not find {_name}! {data}")
35+
else:
36+
print(data)
37+
38+
test_featured_data("featured featured_projects", featured_projects)
39+
test_featured_data("featured featured_studios", featured_studios)
40+
test_featured_data("featured top_loved", top_loved)
41+
test_featured_data("featured top_remixed", top_remixed)
42+
test_featured_data("featured newest_projects", newest_projects)
43+
test_featured_data("featured curated_projects", curated_projects)
44+
test_featured_data("featured design_studio_projects", design_studio_projects)
45+
46+
stats = sa.total_site_stats()
47+
assert stats["PROJECT_COUNT"] >= 164307034
48+
assert stats["USER_COUNT"] >= 135078559
49+
assert stats["STUDIO_COMMENT_COUNT"] >= 259801679
50+
assert stats["PROFILE_COMMENT_COUNT"] >= 330786513
51+
assert stats["STUDIO_COUNT"] >= 34866300
52+
assert stats["COMMENT_COUNT"] >= 989937824
53+
assert stats["PROJECT_COMMENT_COUNT"] >= 399349632
54+
55+
site_traffic = sa.monthly_site_traffic()
56+
assert int(site_traffic["pageviews"]) > 10000
57+
assert int(site_traffic["users"]) > 10000
58+
assert int(site_traffic["sessions"]) > 10000
59+
60+
country_counts = sa.country_counts()
61+
for name, count in country_counts.items():
62+
assert count > 0, f"country_counts[{name!r}] = {count}"
63+
64+
65+
if __name__ == "__main__":
66+
test_other_apis()

tests/test_project.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import pprint
2+
import sys
3+
import warnings
4+
5+
def test_project():
6+
sys.path.insert(0, ".")
7+
import scratchattach as sa
8+
from util import session, credentials_available
9+
if not credentials_available():
10+
warnings.warn("Skipped test_project because there were no credentials available.")
11+
return
12+
sess = session()
13+
14+
15+
project = sess.connect_project(104)
16+
tree = project.remix_tree_pretty()
17+
assert len(tree) > 1000 # there is a lot of chars. Just assert that sth is generated
18+
19+
project = sess.connect_project(1209355136)
20+
21+
assert project
22+
assert project.title == "Sample project #1"
23+
assert project.author_name == "ScratchAttachV2"
24+
assert project.embed_url == "https://scratch.mit.edu/projects/1209355136/embed"
25+
assert project.is_shared()
26+
# create_remix
27+
# load_description
28+
# download
29+
# get_json
30+
# body
31+
# raw_json
32+
# raw_json_or_empty
33+
# creator_agent
34+
assert project.author().id == 147905216
35+
# studios
36+
comment = project.comments()[0] # TODO: move this to a separate comment tester
37+
# comment_by_id
38+
# comment_replies
39+
# comment by id
40+
# (un)love
41+
# (un)favorite
42+
# pos_view
43+
# set_fields
44+
# turn on/off/toggle commenting
45+
# (un)share
46+
# set thumb
47+
# delete comment
48+
# report comment
49+
# post/reply comment
50+
# set body/json/upload json from
51+
# set title/instruction/notes
52+
# visibility
53+
54+
assert comment.id == 489648029
55+
56+
remix = project.remixes()[0]
57+
assert remix.id == 1209582809
58+
assert remix.title == "Sample remix"
59+
assert remix.author_name == "ScratchAttachV2"
60+
assert remix.embed_url == "https://scratch.mit.edu/projects/1209582809/embed"
61+
62+
assert sess.connect_project(414601586).moderation_status() == "notsafe"
63+
assert sess.connect_project(1207314193).moderation_status() == "safe"
64+
assert sess.connect_project(
65+
1233).moderation_status() == "notreviewed" # if this becomes reviewed, please update this
66+
# ^^ also this project is an infinite remix loop!
67+
68+
assert sa.explore_projects()
69+
assert sa.search_projects(query="scratchattach")
70+
71+
if __name__ == '__main__':
72+
test_project()

0 commit comments

Comments
 (0)