django-workspaces 0.2.0__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/.github/workflows/test.yml +1 -1
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/CHANGELOG.md +17 -1
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/PKG-INFO +6 -3
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/pyproject.toml +15 -8
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/src/django_workspaces/__init__.py +31 -12
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/tests/test_utils.py +113 -120
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/.editorconfig +0 -0
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/.github/dependabot.yml +0 -0
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/.github/workflows/release.yml +0 -0
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/.gitignore +0 -0
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/.pre-commit-config.yaml +0 -0
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/.vscode/extensions.json +0 -0
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/.vscode/settings.json +0 -0
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/LICENSE.txt +0 -0
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/README.md +0 -0
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/demo/__init__.py +0 -0
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/demo/asgi.py +0 -0
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/demo/settings.py +0 -0
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/demo/urls.py +0 -0
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/demo/wsgi.py +0 -0
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/manage.py +0 -0
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/src/django_workspaces/_compat.py +0 -0
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/src/django_workspaces/apps.py +0 -0
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/src/django_workspaces/contrib/__init__.py +0 -0
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/src/django_workspaces/contrib/channels/__init__.py +0 -0
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/src/django_workspaces/contrib/channels/middleware.py +0 -0
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/src/django_workspaces/middleware.py +0 -0
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/src/django_workspaces/migrations/0001_initial.py +0 -0
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/src/django_workspaces/migrations/__init__.py +0 -0
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/src/django_workspaces/models.py +0 -0
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/src/django_workspaces/py.typed +0 -0
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/src/django_workspaces/signals.py +0 -0
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/src/django_workspaces/types.py +0 -0
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/tests/__init__.py +0 -0
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/tests/contrib/__init__.py +0 -0
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/tests/contrib/channels/__init__.py +0 -0
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/tests/contrib/channels/test_middleware.py +0 -0
- {django_workspaces-0.2.0 → django_workspaces-0.2.2}/tests/test_middleware.py +0 -0
|
@@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.2] - 2026-03-24
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- `aenter_workspace` and `aleave_workspace` now correctly resolve the user from async requests by awaiting `request.auser()`
|
|
15
|
+
- `resolve_workspace` and `aresolve_workspace` now correctly raise `Http404` when all `workspace_requested` signal receivers return `None`
|
|
16
|
+
|
|
17
|
+
## [0.2.1] - 2026-03-19
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- Support for Python 3.14
|
|
22
|
+
- Support for Django 6.0
|
|
23
|
+
|
|
10
24
|
## [0.2.0] - 2026-03-19
|
|
11
25
|
|
|
12
26
|
### Added
|
|
@@ -31,7 +45,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
31
45
|
- Workspace middleware for Django WSGI/ASGI applications
|
|
32
46
|
- `get_workspace` helper to retrieve the active workspace from a request
|
|
33
47
|
|
|
34
|
-
[Unreleased]: https://github.com/hartungstenio/django-workspaces/compare/0.2.
|
|
48
|
+
[Unreleased]: https://github.com/hartungstenio/django-workspaces/compare/0.2.2...HEAD
|
|
49
|
+
[0.2.2]: https://github.com/hartungstenio/django-workspaces/compare/0.2.1...0.2.2
|
|
50
|
+
[0.2.1]: https://github.com/hartungstenio/django-workspaces/compare/0.2.0...0.2.1
|
|
35
51
|
[0.2.0]: https://github.com/hartungstenio/django-workspaces/compare/0.1.0...0.2.0
|
|
36
52
|
[0.1.0]: https://github.com/hartungstenio/django-workspaces/compare/0.0.1a1...0.1.0
|
|
37
53
|
[0.0.1a1]: https://github.com/hartungstenio/django-workspaces/releases/tag/0.0.1a1
|
|
@@ -1,28 +1,31 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-workspaces
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Django reusable app to manage user workspaces
|
|
5
5
|
Project-URL: Documentation, https://github.com/hartungstenio/django-workspaces#readme
|
|
6
6
|
Project-URL: Issues, https://github.com/hartungstenio/django-workspaces/issues
|
|
7
7
|
Project-URL: Source, https://github.com/hartungstenio/django-workspaces
|
|
8
8
|
Author-email: Christian Hartung <hartung@live.com>
|
|
9
9
|
License-Expression: MIT
|
|
10
|
-
License-File: LICENSE.txt
|
|
11
10
|
Classifier: Development Status :: 4 - Beta
|
|
12
11
|
Classifier: Environment :: Web Environment
|
|
13
12
|
Classifier: Framework :: Django
|
|
14
13
|
Classifier: Framework :: Django :: 5.2
|
|
14
|
+
Classifier: Framework :: Django :: 6.0
|
|
15
15
|
Classifier: Intended Audience :: Developers
|
|
16
|
-
Classifier:
|
|
16
|
+
Classifier: Natural Language :: English
|
|
17
17
|
Classifier: Operating System :: OS Independent
|
|
18
18
|
Classifier: Programming Language :: Python
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.10
|
|
20
20
|
Classifier: Programming Language :: Python :: 3.11
|
|
21
21
|
Classifier: Programming Language :: Python :: 3.12
|
|
22
22
|
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
23
24
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
24
25
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
25
26
|
Classifier: Topic :: Internet :: WWW/HTTP
|
|
27
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
28
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
26
29
|
Classifier: Typing :: Typed
|
|
27
30
|
Requires-Python: >=3.10
|
|
28
31
|
Requires-Dist: django
|
|
@@ -4,6 +4,7 @@ description = "Django reusable app to manage user workspaces"
|
|
|
4
4
|
readme = "README.md"
|
|
5
5
|
requires-python = ">=3.10"
|
|
6
6
|
license = "MIT"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
7
8
|
authors = [
|
|
8
9
|
{ name = "Christian Hartung", email = "hartung@live.com" },
|
|
9
10
|
]
|
|
@@ -13,17 +14,21 @@ classifiers = [
|
|
|
13
14
|
"Environment :: Web Environment",
|
|
14
15
|
"Framework :: Django",
|
|
15
16
|
"Framework :: Django :: 5.2",
|
|
17
|
+
"Framework :: Django :: 6.0",
|
|
16
18
|
"Intended Audience :: Developers",
|
|
17
|
-
"License :: OSI Approved :: MIT License",
|
|
18
19
|
"Operating System :: OS Independent",
|
|
19
20
|
"Programming Language :: Python",
|
|
20
21
|
"Programming Language :: Python :: 3.10",
|
|
21
22
|
"Programming Language :: Python :: 3.11",
|
|
22
23
|
"Programming Language :: Python :: 3.12",
|
|
23
24
|
"Programming Language :: Python :: 3.13",
|
|
25
|
+
"Programming Language :: Python :: 3.14",
|
|
24
26
|
"Programming Language :: Python :: Implementation :: CPython",
|
|
25
27
|
"Programming Language :: Python :: Implementation :: PyPy",
|
|
28
|
+
"Natural Language :: English",
|
|
26
29
|
"Topic :: Internet :: WWW/HTTP",
|
|
30
|
+
"Topic :: Software Development :: Libraries :: Application Frameworks",
|
|
31
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
27
32
|
"Typing :: Typed",
|
|
28
33
|
]
|
|
29
34
|
dependencies = [
|
|
@@ -73,13 +78,10 @@ django_settings_module = "demo.settings"
|
|
|
73
78
|
[tool.hatch.version]
|
|
74
79
|
source = "vcs"
|
|
75
80
|
|
|
76
|
-
[[tool.hatch.envs.all.matrix]]
|
|
77
|
-
python = ["3.10", "3.11", "3.12", "3.13"]
|
|
78
|
-
|
|
79
81
|
[tool.hatch.envs.default]
|
|
80
82
|
features = ["channels"]
|
|
81
83
|
dependencies = [
|
|
82
|
-
"coverage[toml]>=6.5",
|
|
84
|
+
"coverage[toml] (>=6.5)",
|
|
83
85
|
"django-stubs",
|
|
84
86
|
"mypy",
|
|
85
87
|
"pytest",
|
|
@@ -108,16 +110,21 @@ features = ["channels"]
|
|
|
108
110
|
|
|
109
111
|
[tool.hatch.envs.hatch-test.overrides]
|
|
110
112
|
matrix.django.dependencies = [
|
|
111
|
-
{ value = "django
|
|
113
|
+
{ value = "django (~=5.2.0)", if = ["5.2"] },
|
|
114
|
+
{ value = "django (~=6.0.0)", if = ["6.0"] },
|
|
112
115
|
]
|
|
113
116
|
|
|
114
117
|
[[tool.hatch.envs.hatch-test.matrix]]
|
|
115
118
|
django = ["5.2"]
|
|
116
|
-
python = ["3.10", "3.11", "3.12", "3.13"]
|
|
119
|
+
python = ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
120
|
+
|
|
121
|
+
[[tool.hatch.envs.hatch-test.matrix]]
|
|
122
|
+
django = ["6.0"]
|
|
123
|
+
python = ["3.12", "3.13", "3.14"]
|
|
117
124
|
|
|
118
125
|
[tool.hatch.envs.hatch-static-analysis]
|
|
119
126
|
config-path = "none"
|
|
120
|
-
dependencies = ["ruff
|
|
127
|
+
dependencies = ["ruff (~=0.15.7)"]
|
|
121
128
|
|
|
122
129
|
[tool.mypy]
|
|
123
130
|
strict = true
|
|
@@ -63,6 +63,23 @@ def _resolve_user_session(
|
|
|
63
63
|
return cast("AbstractUser | AnonymousUser", obj), session
|
|
64
64
|
|
|
65
65
|
|
|
66
|
+
async def _aresolve_user_session(
|
|
67
|
+
obj: "AbstractUser | AnonymousUser | Mapping[str, Any] | HttpRequest",
|
|
68
|
+
session: SessionBase | None = None,
|
|
69
|
+
) -> tuple["AbstractUser | AnonymousUser", SessionBase]:
|
|
70
|
+
if isinstance(obj, HttpRequest):
|
|
71
|
+
return await obj.auser(), obj.session
|
|
72
|
+
|
|
73
|
+
if isinstance(obj, Mapping):
|
|
74
|
+
return obj["user"], obj["session"]
|
|
75
|
+
|
|
76
|
+
if not session:
|
|
77
|
+
msg = _("You must pass both a user and a session.")
|
|
78
|
+
raise ValueError(msg)
|
|
79
|
+
|
|
80
|
+
return cast("AbstractUser | AnonymousUser", obj), session
|
|
81
|
+
|
|
82
|
+
|
|
66
83
|
def _check_object_permission(user: "AbstractUser | AnonymousUser", workspace: _Workspace) -> None:
|
|
67
84
|
if getattr(settings, "WORKSPACE_CHECK_OBJECT_PERMISSIONS", False):
|
|
68
85
|
view_perm = f"{workspace._meta.app_label}.view_{workspace._meta.model_name}"
|
|
@@ -106,14 +123,15 @@ def resolve_workspace(user: "AbstractUser | AnonymousUser", session: SessionBase
|
|
|
106
123
|
|
|
107
124
|
try:
|
|
108
125
|
workspace_id = Workspace._meta.pk.to_python(session[SESSION_KEY])
|
|
109
|
-
except KeyError
|
|
126
|
+
except KeyError:
|
|
110
127
|
responses = workspace_requested.send(Workspace, user=user)
|
|
111
|
-
|
|
128
|
+
try:
|
|
129
|
+
workspace = next(cast("_Workspace", value) for response in responses if (value := response[1]))
|
|
130
|
+
except StopIteration as exc:
|
|
112
131
|
msg = "Could not find a workspace"
|
|
113
132
|
raise Http404(msg) from exc
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
enter_workspace(user, workspace, session)
|
|
133
|
+
else:
|
|
134
|
+
enter_workspace(user, workspace, session)
|
|
117
135
|
else:
|
|
118
136
|
workspace = get_object_or_404(Workspace, pk=workspace_id)
|
|
119
137
|
|
|
@@ -141,12 +159,13 @@ async def aresolve_workspace(user: "AbstractUser | AnonymousUser", session: Sess
|
|
|
141
159
|
session_workspace = await session.aget(SESSION_KEY)
|
|
142
160
|
if session_workspace is None:
|
|
143
161
|
responses = await workspace_requested.asend(Workspace, user=user)
|
|
144
|
-
|
|
162
|
+
try:
|
|
163
|
+
workspace = next(cast("_Workspace", value) for response in responses if (value := response[1]))
|
|
164
|
+
except StopIteration as exc:
|
|
145
165
|
msg = "Could not find a workspace"
|
|
146
|
-
raise Http404(msg)
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
await aenter_workspace(user, workspace, session)
|
|
166
|
+
raise Http404(msg) from exc
|
|
167
|
+
else:
|
|
168
|
+
await aenter_workspace(user, workspace, session)
|
|
150
169
|
else:
|
|
151
170
|
workspace_id = Workspace._meta.pk.to_python(session_workspace)
|
|
152
171
|
workspace = await aget_object_or_404(Workspace, pk=workspace_id)
|
|
@@ -316,7 +335,7 @@ async def aenter_workspace(
|
|
|
316
335
|
:exc:`django.core.exceptions.PermissionDenied`: if permission validation is enabled
|
|
317
336
|
and the user does not have view permission on the workspace.
|
|
318
337
|
"""
|
|
319
|
-
user, session =
|
|
338
|
+
user, session = await _aresolve_user_session(obj, session)
|
|
320
339
|
await _acheck_object_permission(user, workspace)
|
|
321
340
|
await session.aset(SESSION_KEY, workspace._meta.pk.value_to_string(workspace))
|
|
322
341
|
await workspace_entered.asend(workspace.__class__, user=user, workspace=workspace)
|
|
@@ -413,7 +432,7 @@ async def aleave_workspace(
|
|
|
413
432
|
user: the user leaving the workspace.
|
|
414
433
|
session: the current session.
|
|
415
434
|
"""
|
|
416
|
-
user, session =
|
|
435
|
+
user, session = await _aresolve_user_session(obj, session)
|
|
417
436
|
if await session.ahas_key(SESSION_KEY):
|
|
418
437
|
workspace = await aresolve_workspace(user, session)
|
|
419
438
|
await session.apop(SESSION_KEY)
|
|
@@ -16,11 +16,13 @@ from django_workspaces import (
|
|
|
16
16
|
aenter_workspace,
|
|
17
17
|
aget_workspace,
|
|
18
18
|
aleave_workspace,
|
|
19
|
+
aresolve_workspace,
|
|
19
20
|
aswitch_workspace,
|
|
20
21
|
enter_workspace,
|
|
21
22
|
get_workspace,
|
|
22
23
|
get_workspace_model,
|
|
23
24
|
leave_workspace,
|
|
25
|
+
resolve_workspace,
|
|
24
26
|
switch_workspace,
|
|
25
27
|
workspace_entered,
|
|
26
28
|
workspace_exited,
|
|
@@ -50,57 +52,48 @@ def test_get_workspace_model_swapped(settings: SettingsWrapper) -> None:
|
|
|
50
52
|
assert got is apps.get_model("sites", "Site")
|
|
51
53
|
|
|
52
54
|
|
|
53
|
-
def
|
|
54
|
-
"""Test if :func:`
|
|
55
|
+
def test_resolve_workspace_with_session_non_existing(settings: SettingsWrapper, client: Client) -> None:
|
|
56
|
+
"""Test if :func:`resolve_workspace` raises exception when session workspace does not exist."""
|
|
55
57
|
del settings.WORKSPACE_MODEL
|
|
56
58
|
|
|
57
59
|
user = User.objects.create(username="testuser", email="test@example.com", password="testpw") # noqa: S106
|
|
58
60
|
client.login(username="testuser", passworkd="testpw")
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
request.user = user
|
|
62
|
-
request.session = client.session
|
|
63
|
-
request.session[SESSION_KEY] = str(expected.pk)
|
|
64
|
-
|
|
65
|
-
got = get_workspace(request)
|
|
61
|
+
session = client.session
|
|
62
|
+
session[SESSION_KEY] = "0"
|
|
66
63
|
|
|
67
|
-
|
|
64
|
+
with pytest.raises(Http404):
|
|
65
|
+
resolve_workspace(user, session)
|
|
68
66
|
|
|
69
67
|
|
|
70
|
-
def
|
|
71
|
-
"""Test if :func:`
|
|
68
|
+
def test_resolve_workspace_no_signal(settings: SettingsWrapper, client: Client) -> None:
|
|
69
|
+
"""Test if :func:`resolve_workspace` raises exception when there are no signals to respond workspace requests."""
|
|
72
70
|
del settings.WORKSPACE_MODEL
|
|
73
71
|
|
|
74
72
|
user = User.objects.create(username="testuser", email="test@example.com", password="testpw") # noqa: S106
|
|
75
73
|
client.login(username="testuser", passworkd="testpw")
|
|
76
|
-
|
|
77
|
-
request.user = user
|
|
78
|
-
request.session = client.session
|
|
79
|
-
request.session[SESSION_KEY] = "0"
|
|
74
|
+
session = client.session
|
|
80
75
|
|
|
81
76
|
with pytest.raises(Http404):
|
|
82
|
-
|
|
77
|
+
resolve_workspace(user, session)
|
|
83
78
|
|
|
84
79
|
|
|
85
|
-
def
|
|
86
|
-
"""Test if :func:`
|
|
80
|
+
def test_resolve_workspace_requests_signal(settings: SettingsWrapper, client: Client) -> None:
|
|
81
|
+
"""Test if :func:`resolve_workspace` uses requested workspace when there is no workspace in session."""
|
|
87
82
|
del settings.WORKSPACE_MODEL
|
|
88
83
|
|
|
89
84
|
user = User.objects.create(username="testuser", email="test@example.com", password="testpw") # noqa: S106
|
|
90
85
|
client.login(username="testuser", passworkd="testpw")
|
|
91
86
|
expected: Workspace = Workspace.objects.create(name="test workspace")
|
|
92
|
-
|
|
93
|
-
request.user = user
|
|
94
|
-
request.session = client.session
|
|
87
|
+
session = client.session
|
|
95
88
|
mock_signal = mock.Mock(return_value=expected)
|
|
96
89
|
|
|
97
90
|
workspace_requested.connect(mock_signal)
|
|
98
91
|
try:
|
|
99
92
|
with mock.patch("django_workspaces.enter_workspace") as mock_enter:
|
|
100
|
-
got =
|
|
93
|
+
got = resolve_workspace(user, session)
|
|
101
94
|
|
|
102
95
|
assert got == expected
|
|
103
|
-
mock_enter.assert_called_once_with(user, got,
|
|
96
|
+
mock_enter.assert_called_once_with(user, got, session)
|
|
104
97
|
mock_signal.assert_called_once_with(
|
|
105
98
|
signal=workspace_requested,
|
|
106
99
|
sender=Workspace,
|
|
@@ -110,127 +103,81 @@ def test_get_workspace_requests_signal(settings: SettingsWrapper, rf: RequestFac
|
|
|
110
103
|
workspace_requested.disconnect(mock_signal)
|
|
111
104
|
|
|
112
105
|
|
|
113
|
-
def
|
|
114
|
-
"""Test if :func:`
|
|
106
|
+
def test_resolve_workspace_requests_signal_none(settings: SettingsWrapper, client: Client) -> None:
|
|
107
|
+
"""Test if :func:`resolve_workspace` raises exception when signal return None."""
|
|
115
108
|
del settings.WORKSPACE_MODEL
|
|
116
109
|
|
|
117
110
|
user = User.objects.create(username="testuser", email="test@example.com", password="testpw") # noqa: S106
|
|
118
111
|
client.login(username="testuser", passworkd="testpw")
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
request.session = client.session
|
|
112
|
+
session = client.session
|
|
113
|
+
mock_signal = mock.Mock(return_value=None)
|
|
122
114
|
|
|
123
|
-
|
|
124
|
-
|
|
115
|
+
workspace_requested.connect(mock_signal)
|
|
116
|
+
try:
|
|
117
|
+
with pytest.raises(Http404):
|
|
118
|
+
resolve_workspace(user, session)
|
|
119
|
+
finally:
|
|
120
|
+
workspace_requested.disconnect(mock_signal)
|
|
125
121
|
|
|
126
122
|
|
|
127
|
-
def
|
|
128
|
-
"""Test if :func:`
|
|
123
|
+
def test_aresolve_workspace_with_session(settings: SettingsWrapper, async_client: AsyncClient) -> None:
|
|
124
|
+
"""Test if :func:`aresolve_workspace` gets the session workspace."""
|
|
129
125
|
del settings.WORKSPACE_MODEL
|
|
130
126
|
|
|
131
127
|
user = User.objects.create(username="testuser", email="test@example.com", password="testpw") # noqa: S106
|
|
132
|
-
|
|
133
|
-
request = rf.get("/")
|
|
134
|
-
request.user = user
|
|
135
|
-
request.session = client.session
|
|
136
|
-
|
|
137
|
-
with pytest.raises(Http404):
|
|
138
|
-
get_workspace(request)
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
def test_get_workspace_with_header(settings: SettingsWrapper, rf: RequestFactory) -> None:
|
|
142
|
-
"""Test if :func:`get_workspace` returns the workspace identified by the request header."""
|
|
143
|
-
del settings.WORKSPACE_MODEL
|
|
144
|
-
settings.WORKSPACE_ID_HEADER = "x-workspace-id"
|
|
145
|
-
|
|
128
|
+
async_to_sync(async_client.alogin)(username="testuser", passworkd="testpw")
|
|
146
129
|
expected: Workspace = Workspace.objects.create(name="test workspace")
|
|
147
|
-
request = rf.get("/", headers={"x-workspace-id": str(expected.pk)})
|
|
148
130
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
assert got == expected
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
def test_get_workspace_with_header_non_existing(settings: SettingsWrapper, rf: RequestFactory) -> None:
|
|
155
|
-
"""Test if :func:`get_workspace` raises exception when the workspace from the header does not exist."""
|
|
156
|
-
del settings.WORKSPACE_MODEL
|
|
157
|
-
settings.WORKSPACE_ID_HEADER = "x-workspace-id"
|
|
131
|
+
session = async_client.session
|
|
132
|
+
session[SESSION_KEY] = str(expected.pk)
|
|
158
133
|
|
|
159
|
-
|
|
134
|
+
got = async_to_sync(aresolve_workspace)(user, session)
|
|
160
135
|
|
|
161
|
-
|
|
162
|
-
get_workspace(request)
|
|
136
|
+
assert got == expected
|
|
163
137
|
|
|
164
138
|
|
|
165
|
-
def
|
|
166
|
-
|
|
167
|
-
) -> None:
|
|
168
|
-
"""Test if :func:`aget_workspace` gets the session workspace."""
|
|
139
|
+
def test_aresolve_workspace_with_session_non_existing(settings: SettingsWrapper, async_client: AsyncClient) -> None:
|
|
140
|
+
"""Test if :func:`aresolve_workspace` raises exception when session workspace does not exist."""
|
|
169
141
|
del settings.WORKSPACE_MODEL
|
|
170
142
|
|
|
171
143
|
user = User.objects.create(username="testuser", email="test@example.com", password="testpw") # noqa: S106
|
|
172
144
|
async_to_sync(async_client.alogin)(username="testuser", passworkd="testpw")
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
async def auser() -> User:
|
|
177
|
-
return user
|
|
178
|
-
|
|
179
|
-
request.auser = auser
|
|
180
|
-
request.session = async_client.session
|
|
181
|
-
request.session[SESSION_KEY] = str(expected.pk)
|
|
182
|
-
|
|
183
|
-
got = async_to_sync(aget_workspace)(request)
|
|
145
|
+
session = async_client.session
|
|
146
|
+
session[SESSION_KEY] = "0"
|
|
184
147
|
|
|
185
|
-
|
|
148
|
+
with pytest.raises(Http404):
|
|
149
|
+
async_to_sync(aresolve_workspace)(user, session)
|
|
186
150
|
|
|
187
151
|
|
|
188
|
-
def
|
|
189
|
-
|
|
190
|
-
) -> None:
|
|
191
|
-
"""Test if :func:`aget_workspace` raises exception when session workspace does not exist."""
|
|
152
|
+
def test_aresolve_workspace_no_signal(settings: SettingsWrapper, async_client: AsyncClient) -> None:
|
|
153
|
+
"""Test if :func:`aresolve_workspace` raises exception when there are no signals to respond workspace requests."""
|
|
192
154
|
del settings.WORKSPACE_MODEL
|
|
193
155
|
|
|
194
156
|
user = User.objects.create(username="testuser", email="test@example.com", password="testpw") # noqa: S106
|
|
195
157
|
async_to_sync(async_client.alogin)(username="testuser", passworkd="testpw")
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
async def auser() -> User:
|
|
199
|
-
return user
|
|
200
|
-
|
|
201
|
-
request.auser = auser
|
|
202
|
-
request.session = async_client.session
|
|
203
|
-
request.session[SESSION_KEY] = "0"
|
|
158
|
+
session = async_client.session
|
|
204
159
|
|
|
205
160
|
with pytest.raises(Http404):
|
|
206
|
-
async_to_sync(
|
|
161
|
+
async_to_sync(aresolve_workspace)(user, session)
|
|
207
162
|
|
|
208
163
|
|
|
209
|
-
def
|
|
210
|
-
|
|
211
|
-
) -> None:
|
|
212
|
-
"""Test if :func:`aget_workspace` uses requested workspace when there is no workspace in session."""
|
|
164
|
+
def test_aresolve_workspace_requests_signal(settings: SettingsWrapper, async_client: AsyncClient) -> None:
|
|
165
|
+
"""Test if :func:`aresolve_workspace` uses requested workspace when there is no workspace in session."""
|
|
213
166
|
del settings.WORKSPACE_MODEL
|
|
214
167
|
|
|
215
168
|
user = User.objects.create(username="testuser", email="test@example.com", password="testpw") # noqa: S106
|
|
216
169
|
async_to_sync(async_client.alogin)(username="testuser", passworkd="testpw")
|
|
217
170
|
expected: Workspace = Workspace.objects.create(name="test workspace")
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
async def auser() -> User:
|
|
221
|
-
return user
|
|
222
|
-
|
|
223
|
-
request.auser = auser
|
|
224
|
-
request.session = async_client.session
|
|
171
|
+
session = async_client.session
|
|
225
172
|
mock_signal = mock.AsyncMock(return_value=expected)
|
|
226
173
|
|
|
227
174
|
workspace_requested.connect(mock_signal)
|
|
228
175
|
try:
|
|
229
176
|
with mock.patch("django_workspaces.aenter_workspace") as mock_aenter:
|
|
230
|
-
got = async_to_sync(
|
|
177
|
+
got = async_to_sync(aresolve_workspace)(user, session)
|
|
231
178
|
|
|
232
179
|
assert got == expected
|
|
233
|
-
mock_aenter.assert_called_once_with(user, got,
|
|
180
|
+
mock_aenter.assert_called_once_with(user, got, session)
|
|
234
181
|
mock_signal.assert_awaited_once_with(
|
|
235
182
|
signal=workspace_requested,
|
|
236
183
|
sender=Workspace,
|
|
@@ -240,44 +187,86 @@ def test_aget_workspace_requests_signal(
|
|
|
240
187
|
workspace_requested.disconnect(mock_signal)
|
|
241
188
|
|
|
242
189
|
|
|
243
|
-
def
|
|
244
|
-
|
|
245
|
-
) -> None:
|
|
246
|
-
"""Test if :func:`aget_workspace` raises exception when there are no signals to respond workspace requests."""
|
|
190
|
+
def test_aresolve_workspace_requests_signal_none(settings: SettingsWrapper, async_client: AsyncClient) -> None:
|
|
191
|
+
"""Test if :func:`aresolve_workspace` raises exception when signal return None."""
|
|
247
192
|
del settings.WORKSPACE_MODEL
|
|
248
193
|
|
|
249
194
|
user = User.objects.create(username="testuser", email="test@example.com", password="testpw") # noqa: S106
|
|
250
195
|
async_to_sync(async_client.alogin)(username="testuser", passworkd="testpw")
|
|
251
|
-
|
|
196
|
+
session = async_client.session
|
|
197
|
+
mock_signal = mock.AsyncMock(return_value=None)
|
|
252
198
|
|
|
253
|
-
|
|
254
|
-
|
|
199
|
+
workspace_requested.connect(mock_signal)
|
|
200
|
+
try:
|
|
201
|
+
with pytest.raises(Http404):
|
|
202
|
+
async_to_sync(aresolve_workspace)(user, session)
|
|
203
|
+
finally:
|
|
204
|
+
workspace_requested.disconnect(mock_signal)
|
|
255
205
|
|
|
256
|
-
|
|
257
|
-
|
|
206
|
+
|
|
207
|
+
def test_get_workspace_with_session(settings: SettingsWrapper, rf: RequestFactory, client: Client) -> None:
|
|
208
|
+
"""Test if :func:`get_workspace` gets the session workspace."""
|
|
209
|
+
del settings.WORKSPACE_MODEL
|
|
210
|
+
|
|
211
|
+
user = User.objects.create(username="testuser", email="test@example.com", password="testpw") # noqa: S106
|
|
212
|
+
client.login(username="testuser", passworkd="testpw")
|
|
213
|
+
expected: Workspace = Workspace.objects.create(name="test workspace")
|
|
214
|
+
request = rf.get("/")
|
|
215
|
+
request.user = user
|
|
216
|
+
request.session = client.session
|
|
217
|
+
request.session[SESSION_KEY] = str(expected.pk)
|
|
218
|
+
|
|
219
|
+
with mock.patch("django_workspaces.resolve_workspace", return_value=expected) as mock_resolve:
|
|
220
|
+
got = get_workspace(request)
|
|
221
|
+
|
|
222
|
+
assert got == expected
|
|
223
|
+
mock_resolve.assert_called_once_with(user, request.session)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def test_get_workspace_with_header(settings: SettingsWrapper, rf: RequestFactory) -> None:
|
|
227
|
+
"""Test if :func:`get_workspace` returns the workspace identified by the request header."""
|
|
228
|
+
del settings.WORKSPACE_MODEL
|
|
229
|
+
settings.WORKSPACE_ID_HEADER = "x-workspace-id"
|
|
230
|
+
|
|
231
|
+
expected: Workspace = Workspace.objects.create(name="test workspace")
|
|
232
|
+
request = rf.get("/", headers={"x-workspace-id": str(expected.pk)})
|
|
233
|
+
|
|
234
|
+
got = get_workspace(request)
|
|
235
|
+
|
|
236
|
+
assert got == expected
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def test_get_workspace_with_header_non_existing(settings: SettingsWrapper, rf: RequestFactory) -> None:
|
|
240
|
+
"""Test if :func:`get_workspace` raises exception when the workspace from the header does not exist."""
|
|
241
|
+
del settings.WORKSPACE_MODEL
|
|
242
|
+
settings.WORKSPACE_ID_HEADER = "x-workspace-id"
|
|
243
|
+
|
|
244
|
+
request = rf.get("/", headers={"x-workspace-id": "0"})
|
|
258
245
|
|
|
259
246
|
with pytest.raises(Http404):
|
|
260
|
-
|
|
247
|
+
get_workspace(request)
|
|
261
248
|
|
|
262
249
|
|
|
263
|
-
def
|
|
250
|
+
def test_aget_workspace_with_session(
|
|
264
251
|
settings: SettingsWrapper, async_rf: AsyncRequestFactory, async_client: AsyncClient
|
|
265
252
|
) -> None:
|
|
266
|
-
"""Test if :func:`aget_workspace`
|
|
253
|
+
"""Test if :func:`aget_workspace` gets the session workspace."""
|
|
267
254
|
del settings.WORKSPACE_MODEL
|
|
268
255
|
|
|
269
256
|
user = User.objects.create(username="testuser", email="test@example.com", password="testpw") # noqa: S106
|
|
270
257
|
async_to_sync(async_client.alogin)(username="testuser", passworkd="testpw")
|
|
271
258
|
request = async_rf.get("/")
|
|
259
|
+
request.user = user
|
|
260
|
+
request.auser = mock.AsyncMock(return_value=user)
|
|
261
|
+
request.session = async_client.session
|
|
272
262
|
|
|
273
|
-
|
|
274
|
-
return user
|
|
263
|
+
expected: Workspace = Workspace.objects.create(name="test workspace")
|
|
275
264
|
|
|
276
|
-
|
|
277
|
-
|
|
265
|
+
with mock.patch("django_workspaces.aresolve_workspace", return_value=expected) as mock_resolve:
|
|
266
|
+
got = async_to_sync(aget_workspace)(request)
|
|
278
267
|
|
|
279
|
-
|
|
280
|
-
|
|
268
|
+
assert got == expected
|
|
269
|
+
mock_resolve.assert_awaited_once_with(user, request.session)
|
|
281
270
|
|
|
282
271
|
|
|
283
272
|
def test_aget_workspace_with_header(settings: SettingsWrapper, async_rf: AsyncRequestFactory) -> None:
|
|
@@ -451,6 +440,7 @@ def test_aenter_workspace_request(
|
|
|
451
440
|
async_to_sync(async_client.alogin)(username="testuser", passworkd="testpw")
|
|
452
441
|
request = async_rf.get("/")
|
|
453
442
|
request.user = user
|
|
443
|
+
request.auser = mock.AsyncMock(return_value=user)
|
|
454
444
|
request.session = async_client.session
|
|
455
445
|
|
|
456
446
|
workspace: Workspace = Workspace.objects.create(name="test workspace")
|
|
@@ -515,6 +505,7 @@ def test_aenter_workspace_request_permission_denied(
|
|
|
515
505
|
user = User.objects.create(username="testuser", email="test@example.com", password="testpw") # noqa: S106
|
|
516
506
|
request = async_rf.get("/")
|
|
517
507
|
request.user = user
|
|
508
|
+
request.auser = mock.AsyncMock(return_value=user)
|
|
518
509
|
request.session = async_client.session
|
|
519
510
|
workspace: Workspace = Workspace.objects.create(name="test workspace")
|
|
520
511
|
|
|
@@ -675,6 +666,7 @@ def test_aleave_workspace_request(
|
|
|
675
666
|
workspace: Workspace = Workspace.objects.create(name="test workspace")
|
|
676
667
|
request = async_rf.get("/")
|
|
677
668
|
request.user = user
|
|
669
|
+
request.auser = mock.AsyncMock(return_value=user)
|
|
678
670
|
request.session = async_client.session
|
|
679
671
|
request.session[SESSION_KEY] = str(workspace.pk)
|
|
680
672
|
mock_signal = mock.AsyncMock()
|
|
@@ -814,6 +806,7 @@ def test_aswitch_workspace_request(
|
|
|
814
806
|
async_to_sync(async_client.alogin)(username="testuser", passworkd="testpw")
|
|
815
807
|
request = async_rf.get("/")
|
|
816
808
|
request.user = user
|
|
809
|
+
request.auser = mock.AsyncMock(return_value=user)
|
|
817
810
|
request.session = async_client.session
|
|
818
811
|
workspace: Workspace = Workspace.objects.create(name="test workspace")
|
|
819
812
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_workspaces-0.2.0 → django_workspaces-0.2.2}/src/django_workspaces/contrib/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_workspaces-0.2.0 → django_workspaces-0.2.2}/src/django_workspaces/migrations/0001_initial.py
RENAMED
|
File without changes
|
{django_workspaces-0.2.0 → django_workspaces-0.2.2}/src/django_workspaces/migrations/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_workspaces-0.2.0 → django_workspaces-0.2.2}/tests/contrib/channels/test_middleware.py
RENAMED
|
File without changes
|
|
File without changes
|