django-workspaces 0.2.1__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.
Files changed (38) hide show
  1. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/CHANGELOG.md +9 -1
  2. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/PKG-INFO +1 -1
  3. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/src/django_workspaces/__init__.py +31 -12
  4. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/tests/test_utils.py +113 -120
  5. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/.editorconfig +0 -0
  6. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/.github/dependabot.yml +0 -0
  7. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/.github/workflows/release.yml +0 -0
  8. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/.github/workflows/test.yml +0 -0
  9. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/.gitignore +0 -0
  10. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/.pre-commit-config.yaml +0 -0
  11. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/.vscode/extensions.json +0 -0
  12. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/.vscode/settings.json +0 -0
  13. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/LICENSE.txt +0 -0
  14. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/README.md +0 -0
  15. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/demo/__init__.py +0 -0
  16. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/demo/asgi.py +0 -0
  17. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/demo/settings.py +0 -0
  18. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/demo/urls.py +0 -0
  19. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/demo/wsgi.py +0 -0
  20. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/manage.py +0 -0
  21. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/pyproject.toml +0 -0
  22. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/src/django_workspaces/_compat.py +0 -0
  23. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/src/django_workspaces/apps.py +0 -0
  24. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/src/django_workspaces/contrib/__init__.py +0 -0
  25. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/src/django_workspaces/contrib/channels/__init__.py +0 -0
  26. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/src/django_workspaces/contrib/channels/middleware.py +0 -0
  27. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/src/django_workspaces/middleware.py +0 -0
  28. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/src/django_workspaces/migrations/0001_initial.py +0 -0
  29. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/src/django_workspaces/migrations/__init__.py +0 -0
  30. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/src/django_workspaces/models.py +0 -0
  31. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/src/django_workspaces/py.typed +0 -0
  32. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/src/django_workspaces/signals.py +0 -0
  33. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/src/django_workspaces/types.py +0 -0
  34. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/tests/__init__.py +0 -0
  35. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/tests/contrib/__init__.py +0 -0
  36. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/tests/contrib/channels/__init__.py +0 -0
  37. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/tests/contrib/channels/test_middleware.py +0 -0
  38. {django_workspaces-0.2.1 → django_workspaces-0.2.2}/tests/test_middleware.py +0 -0
@@ -7,6 +7,13 @@ 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
+
10
17
  ## [0.2.1] - 2026-03-19
11
18
 
12
19
  ### Added
@@ -38,7 +45,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
38
45
  - Workspace middleware for Django WSGI/ASGI applications
39
46
  - `get_workspace` helper to retrieve the active workspace from a request
40
47
 
41
- [Unreleased]: https://github.com/hartungstenio/django-workspaces/compare/0.2.1...HEAD
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
42
50
  [0.2.1]: https://github.com/hartungstenio/django-workspaces/compare/0.2.0...0.2.1
43
51
  [0.2.0]: https://github.com/hartungstenio/django-workspaces/compare/0.1.0...0.2.0
44
52
  [0.1.0]: https://github.com/hartungstenio/django-workspaces/compare/0.0.1a1...0.1.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-workspaces
3
- Version: 0.2.1
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
@@ -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 as exc:
126
+ except KeyError:
110
127
  responses = workspace_requested.send(Workspace, user=user)
111
- if not responses:
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
- _, workspace = cast("tuple[Any, _Workspace]", responses[0])
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
- if not responses:
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
- _, workspace = cast("tuple[Any, _Workspace]", responses[0])
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 = _resolve_user_session(obj, 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 = _resolve_user_session(obj, 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 test_get_workspace_with_session(settings: SettingsWrapper, rf: RequestFactory, client: Client) -> None:
54
- """Test if :func:`get_workspace` gets the session workspace."""
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
- expected: Workspace = Workspace.objects.create(name="test workspace")
60
- request = rf.get("/")
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
- assert got == expected
64
+ with pytest.raises(Http404):
65
+ resolve_workspace(user, session)
68
66
 
69
67
 
70
- def test_get_workspace_with_session_non_existing(settings: SettingsWrapper, rf: RequestFactory, client: Client) -> None:
71
- """Test if :func:`get_workspace` raises exception when session workspace does not exist."""
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
- request = rf.get("/")
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
- get_workspace(request)
77
+ resolve_workspace(user, session)
83
78
 
84
79
 
85
- def test_get_workspace_requests_signal(settings: SettingsWrapper, rf: RequestFactory, client: Client) -> None:
86
- """Test if :func:`get_workspace` uses requested workspace when there is no workspace in session."""
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
- request = rf.get("/")
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 = get_workspace(request)
93
+ got = resolve_workspace(user, session)
101
94
 
102
95
  assert got == expected
103
- mock_enter.assert_called_once_with(user, got, request.session)
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 test_get_workspace_no_signal(settings: SettingsWrapper, rf: RequestFactory, client: Client) -> None:
114
- """Test if :func:`get_workspace` raises exception when there are no signals to respond workspace requests."""
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
- request = rf.get("/")
120
- request.user = user
121
- request.session = client.session
112
+ session = client.session
113
+ mock_signal = mock.Mock(return_value=None)
122
114
 
123
- with pytest.raises(Http404):
124
- get_workspace(request)
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 test_get_workspace_requests_signal_none(settings: SettingsWrapper, rf: RequestFactory, client: Client) -> None:
128
- """Test if :func:`get_workspace` raises exception when signal return None."""
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
- client.login(username="testuser", passworkd="testpw")
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
- got = get_workspace(request)
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
- request = rf.get("/", headers={"x-workspace-id": "0"})
134
+ got = async_to_sync(aresolve_workspace)(user, session)
160
135
 
161
- with pytest.raises(Http404):
162
- get_workspace(request)
136
+ assert got == expected
163
137
 
164
138
 
165
- def test_aget_workspace_with_session(
166
- settings: SettingsWrapper, async_rf: AsyncRequestFactory, async_client: AsyncClient
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
- expected: Workspace = Workspace.objects.create(name="test workspace")
174
- request = async_rf.get("/")
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
- assert got == expected
148
+ with pytest.raises(Http404):
149
+ async_to_sync(aresolve_workspace)(user, session)
186
150
 
187
151
 
188
- def test_aget_workspace_with_session_non_existing(
189
- settings: SettingsWrapper, async_rf: AsyncRequestFactory, async_client: AsyncClient
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
- request = async_rf.get("/")
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(aget_workspace)(request)
161
+ async_to_sync(aresolve_workspace)(user, session)
207
162
 
208
163
 
209
- def test_aget_workspace_requests_signal(
210
- settings: SettingsWrapper, async_rf: AsyncRequestFactory, async_client: AsyncClient
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
- request = async_rf.get("/")
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(aget_workspace)(request)
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, request.session)
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 test_aget_workspace_no_signal(
244
- settings: SettingsWrapper, async_rf: AsyncRequestFactory, async_client: AsyncClient
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
- request = async_rf.get("/")
196
+ session = async_client.session
197
+ mock_signal = mock.AsyncMock(return_value=None)
252
198
 
253
- async def auser() -> User:
254
- return user
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
- request.auser = auser
257
- request.session = async_client.session
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
- async_to_sync(aget_workspace)(request)
247
+ get_workspace(request)
261
248
 
262
249
 
263
- def test_aget_workspace_requests_signal_none(
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` raises exception when signal return None."""
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
- async def auser() -> User:
274
- return user
263
+ expected: Workspace = Workspace.objects.create(name="test workspace")
275
264
 
276
- request.auser = auser
277
- request.session = async_client.session
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
- with pytest.raises(Http404):
280
- async_to_sync(aget_workspace)(request)
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