audio streaming app
plyr.fm
1"""tests for OAuth scope validation functions."""
2
3from backend._internal.auth.scopes import (
4 check_scope_coverage,
5 get_missing_scopes,
6)
7
8
9class TestCheckScopeCoverage:
10 """tests for check_scope_coverage."""
11
12 def test_returns_true_when_all_scopes_granted(self):
13 """should return True when granted scope covers all required."""
14 granted = "atproto repo:fm.plyr.track repo:fm.plyr.like repo:fm.plyr.comment"
15 required = "atproto repo:fm.plyr.track repo:fm.plyr.like repo:fm.plyr.comment"
16 assert check_scope_coverage(granted, required) is True
17
18 def test_returns_true_when_extra_scopes_granted(self):
19 """should return True when granted has more than required."""
20 granted = "atproto repo:fm.plyr.track repo:fm.plyr.like repo:fm.plyr.comment repo:fm.plyr.extra"
21 required = "atproto repo:fm.plyr.track repo:fm.plyr.like"
22 assert check_scope_coverage(granted, required) is True
23
24 def test_returns_false_when_missing_scope(self):
25 """should return False when granted is missing required scope."""
26 granted = "atproto repo:fm.plyr.track repo:fm.plyr.like"
27 required = "atproto repo:fm.plyr.track repo:fm.plyr.like repo:fm.plyr.comment"
28 assert check_scope_coverage(granted, required) is False
29
30 def test_returns_false_when_granted_empty(self):
31 """should return False when granted scope is empty."""
32 granted = ""
33 required = "atproto repo:fm.plyr.track"
34 assert check_scope_coverage(granted, required) is False
35
36 def test_returns_true_when_both_empty(self):
37 """should return True when both are empty."""
38 assert check_scope_coverage("", "") is True
39
40 def test_wildcard_collection_covers_specific(self):
41 """repo:* should cover any specific collection."""
42 granted = "atproto repo:*"
43 required = "atproto repo:fm.plyr.track"
44 assert check_scope_coverage(granted, required) is True
45
46 def test_format_equivalence(self):
47 """repo:nsid == repo?collection=nsid."""
48 granted = "atproto repo?collection=fm.plyr.track"
49 required = "atproto repo:fm.plyr.track"
50 assert check_scope_coverage(granted, required) is True
51
52 def test_blob_scope_coverage(self):
53 """blob scopes should be checked."""
54 granted = "atproto blob:*/*"
55 required = "atproto blob:*/*"
56 assert check_scope_coverage(granted, required) is True
57
58 def test_include_covered_by_expanded_repo_scopes(self):
59 """PDS expands include: into repo: scopes — should still pass."""
60 granted = "atproto blob:*/* repo:fm.plyr.stg.track repo:fm.plyr.stg.like repo:fm.plyr.stg.comment"
61 required = "atproto blob:*/* include:fm.plyr.stg.authFullApp"
62 assert check_scope_coverage(granted, required) is True
63
64 def test_include_not_covered_by_wrong_namespace(self):
65 """include: should fail if granted scopes are from a different namespace."""
66 granted = "atproto blob:*/* repo:fm.other.track"
67 required = "atproto blob:*/* include:fm.plyr.stg.authFullApp"
68 assert check_scope_coverage(granted, required) is False
69
70 def test_include_missing_shows_in_get_missing(self):
71 """get_missing_scopes should report include: as missing when not covered."""
72 granted = "atproto blob:*/* repo:fm.other.track"
73 required = "atproto blob:*/* include:fm.plyr.stg.authFullApp"
74 result = get_missing_scopes(granted, required)
75 assert result == {"include:fm.plyr.stg.authFullApp"}
76
77
78class TestGetMissingScopes:
79 """tests for get_missing_scopes."""
80
81 def test_returns_empty_when_all_covered(self):
82 """should return empty set when all scopes are granted."""
83 granted = "atproto repo:fm.plyr.track repo:fm.plyr.like repo:fm.plyr.comment"
84 required = "atproto repo:fm.plyr.track repo:fm.plyr.like"
85 result = get_missing_scopes(granted, required)
86 assert result == set()
87
88 def test_returns_missing_scope(self):
89 """should return the missing scope."""
90 granted = "atproto repo:fm.plyr.track repo:fm.plyr.like"
91 required = "atproto repo:fm.plyr.track repo:fm.plyr.like repo:fm.plyr.comment"
92 result = get_missing_scopes(granted, required)
93 assert result == {"repo:fm.plyr.comment"}
94
95 def test_returns_multiple_missing_scopes(self):
96 """should return all missing scopes."""
97 granted = "atproto repo:fm.plyr.track"
98 required = "atproto repo:fm.plyr.track repo:fm.plyr.like repo:fm.plyr.comment"
99 result = get_missing_scopes(granted, required)
100 assert result == {"repo:fm.plyr.like", "repo:fm.plyr.comment"}