Skip to content

Commit 61bd735

Browse files
committed
Add some auth view tests
1 parent 1bc40ce commit 61bd735

File tree

9 files changed

+146
-1
lines changed

9 files changed

+146
-1
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
python-version: ["3.11", "3.12", "3.13"]
1212

1313
env:
14-
PACKAGES: plain plain-worker plain-flags plain-sessions plain-admin plain-oauth plain-models plain-api plain-elements plain-htmx
14+
PACKAGES: plain plain-worker plain-flags plain-sessions plain-admin plain-oauth plain-auth plain-models plain-api plain-elements plain-htmx
1515

1616
steps:
1717
- uses: actions/checkout@v4

plain-auth/pyproject.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ dependencies = [
1313
"plain.sessions<1.0.0",
1414
]
1515

16+
[tool.uv]
17+
dev-dependencies = [
18+
"plain.pytest<1.0.0",
19+
]
20+
1621
[tool.hatch.build.targets.wheel]
1722
packages = ["plain"]
1823

plain-auth/tests/app/settings.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
SECRET_KEY = "test"
2+
URLS_ROUTER = "app.urls.AppRouter"
3+
INSTALLED_PACKAGES = [
4+
"plain.auth",
5+
"plain.sessions",
6+
"plain.models",
7+
"app.users",
8+
]
9+
DATABASE = {
10+
"ENGINE": "plain.models.backends.sqlite3",
11+
"NAME": ":memory:",
12+
}
13+
MIDDLEWARE = [
14+
"plain.sessions.middleware.SessionMiddleware",
15+
"plain.auth.middleware.AuthenticationMiddleware",
16+
]
17+
AUTH_LOGIN_URL = "login"
18+
AUTH_USER_MODEL = "users.User"

plain-auth/tests/app/urls.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
from plain.auth.views import AuthViewMixin
2+
from plain.urls import Router, path
3+
from plain.views import View
4+
5+
6+
class LoginView(View):
7+
def get(self):
8+
return "login"
9+
10+
11+
class ProtectedView(AuthViewMixin, View):
12+
def get(self):
13+
return "protected"
14+
15+
16+
class OpenView(AuthViewMixin, View):
17+
login_required = False
18+
19+
def get(self):
20+
return "open"
21+
22+
23+
class AdminView(AuthViewMixin, View):
24+
admin_required = True
25+
26+
def get(self):
27+
return "admin"
28+
29+
30+
class NoLoginUrlView(AuthViewMixin, View):
31+
login_url = None
32+
33+
def get(self):
34+
return "none"
35+
36+
37+
class AppRouter(Router):
38+
namespace = ""
39+
urls = [
40+
path("login/", LoginView, name="login"),
41+
path("protected/", ProtectedView, name="protected"),
42+
path("open/", OpenView, name="open"),
43+
path("admin/", AdminView, name="admin"),
44+
path("nolink/", NoLoginUrlView, name="nolink"),
45+
]
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Generated by Plain 0.21.5 on 2025-02-17 04:12
2+
3+
from plain import models
4+
from plain.models import migrations
5+
6+
7+
class Migration(migrations.Migration):
8+
initial = True
9+
10+
dependencies = []
11+
12+
operations = [
13+
migrations.CreateModel(
14+
name="User",
15+
fields=[
16+
("id", models.BigAutoField(auto_created=True, primary_key=True)),
17+
("username", models.CharField(max_length=255)),
18+
("is_admin", models.BooleanField(default=False)),
19+
],
20+
),
21+
]

plain-auth/tests/app/users/migrations/__init__.py

Whitespace-only changes.

plain-auth/tests/app/users/models.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from plain import models
2+
3+
4+
@models.register_model
5+
class User(models.Model):
6+
username = models.CharField(max_length=255)
7+
is_admin = models.BooleanField(default=False)

plain-auth/tests/test_views.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
from plain.auth import get_user_model
2+
from plain.test import Client
3+
4+
5+
def test_login_required_redirect(db):
6+
client = Client()
7+
response = client.get("/protected/")
8+
assert response.status_code == 302
9+
assert response.url == "/login/?next=/protected/"
10+
11+
12+
def test_view_without_login_required(db):
13+
client = Client()
14+
response = client.get("/open/")
15+
assert response.status_code == 200
16+
assert response.content == b"open"
17+
assert response.headers["Cache-Control"] == "private"
18+
19+
20+
def test_admin_required(db):
21+
client = Client()
22+
# login required first
23+
assert client.get("/admin/").status_code == 302
24+
25+
user = get_user_model().objects.create(username="user")
26+
client.force_login(user)
27+
# not admin -> 404
28+
assert client.get("/admin/").status_code == 404
29+
30+
user.is_admin = True
31+
user.save()
32+
# now admin -> success
33+
resp = client.get("/admin/")
34+
assert resp.status_code == 200
35+
assert resp.content == b"admin"
36+
37+
38+
def test_no_login_url_forbidden(db):
39+
client = Client()
40+
response = client.get("/nolink/")
41+
assert response.status_code == 403

uv.lock

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)