authentik.admin.files.tests.test_api

test file api

  1"""test file api"""
  2
  3from io import BytesIO
  4
  5from django.test import TestCase
  6from django.urls import reverse
  7
  8from authentik.admin.files.manager import FileManager
  9from authentik.admin.files.tests.utils import FileTestFileBackendMixin
 10from authentik.admin.files.usage import FileUsage
 11from authentik.core.tests.utils import create_test_admin_user
 12from authentik.events.models import Event, EventAction
 13
 14
 15class TestFileAPI(FileTestFileBackendMixin, TestCase):
 16    """test file api"""
 17
 18    def setUp(self) -> None:
 19        super().setUp()
 20        self.user = create_test_admin_user()
 21        self.client.force_login(self.user)
 22
 23    def test_upload_creates_event(self):
 24        """Test that uploading a file creates a FILE_UPLOADED event"""
 25        manager = FileManager(FileUsage.MEDIA)
 26        file_content = b"test file content"
 27        file_name = "test-upload.png"
 28
 29        # Upload file
 30        response = self.client.post(
 31            reverse("authentik_api:files"),
 32            {
 33                "file": BytesIO(file_content),
 34                "name": file_name,
 35                "usage": FileUsage.MEDIA.value,
 36            },
 37            format="multipart",
 38        )
 39
 40        self.assertEqual(response.status_code, 200)
 41
 42        # Verify event was created
 43        event = Event.objects.filter(action=EventAction.MODEL_CREATED).first()
 44
 45        self.assertIsNotNone(event)
 46        assert event is not None  # nosec
 47        self.assertEqual(event.context["model"]["name"], file_name)
 48        self.assertEqual(event.context["model"]["usage"], FileUsage.MEDIA.value)
 49        self.assertEqual(event.context["model"]["mime_type"], "image/png")
 50
 51        # Verify user is captured
 52        self.assertEqual(event.user["username"], self.user.username)
 53        self.assertEqual(event.user["pk"], self.user.pk)
 54
 55        manager.delete_file(file_name)
 56
 57    def test_delete_creates_event(self):
 58        """Test that deleting a file creates an event"""
 59        manager = FileManager(FileUsage.MEDIA)
 60        file_name = "test-delete.png"
 61        manager.save_file(file_name, b"test content")
 62
 63        # Delete file
 64        response = self.client.delete(
 65            reverse(
 66                "authentik_api:files",
 67                query={
 68                    "name": file_name,
 69                    "usage": FileUsage.MEDIA.value,
 70                },
 71            )
 72        )
 73
 74        self.assertEqual(response.status_code, 200)
 75
 76        # Verify event was created
 77        event = Event.objects.filter(action=EventAction.MODEL_DELETED).first()
 78
 79        self.assertIsNotNone(event)
 80        assert event is not None  # nosec
 81        self.assertEqual(event.context["model"]["name"], file_name)
 82        self.assertEqual(event.context["model"]["usage"], FileUsage.MEDIA.value)
 83
 84        # Verify user is captured
 85        self.assertEqual(event.user["username"], self.user.username)
 86        self.assertEqual(event.user["pk"], self.user.pk)
 87
 88    def test_list_files_basic(self):
 89        """Test listing files with default parameters"""
 90        response = self.client.get(reverse("authentik_api:files"))
 91
 92        self.assertEqual(response.status_code, 200)
 93        self.assertIn(
 94            {
 95                "name": "/static/authentik/sources/ldap.png",
 96                "url": "http://testserver/static/authentik/sources/ldap.png",
 97                "mime_type": "image/png",
 98                "themed_urls": None,
 99            },
100            response.data,
101        )
102
103    def test_list_files_invalid_usage(self):
104        """Test listing files with invalid usage parameter"""
105        response = self.client.get(
106            reverse(
107                "authentik_api:files",
108                query={
109                    "usage": "invalid",
110                },
111            )
112        )
113
114        self.assertEqual(response.status_code, 400)
115        self.assertIn("not a valid choice", str(response.data))
116
117    def test_list_files_with_search(self):
118        """Test listing files with search query"""
119        response = self.client.get(
120            reverse(
121                "authentik_api:files",
122                query={
123                    "search": "ldap.png",
124                },
125            )
126        )
127
128        self.assertEqual(response.status_code, 200)
129        self.assertIn(
130            {
131                "name": "/static/authentik/sources/ldap.png",
132                "url": "http://testserver/static/authentik/sources/ldap.png",
133                "mime_type": "image/png",
134                "themed_urls": None,
135            },
136            response.data,
137        )
138
139    def test_list_files_with_manageable_only(self):
140        """Test listing files with omit parameter"""
141        response = self.client.get(
142            reverse(
143                "authentik_api:files",
144                query={
145                    "manageableOnly": "true",
146                },
147            )
148        )
149
150        self.assertEqual(response.status_code, 200)
151        self.assertNotIn(
152            {
153                "name": "/static/dist/assets/images/flow_background.jpg",
154                "mime_type": "image/jpeg",
155            },
156            response.data,
157        )
158
159    def test_upload_file_with_custom_path(self):
160        """Test uploading file with custom path"""
161        manager = FileManager(FileUsage.MEDIA)
162        file_name = "custom/test"
163        file_content = b"test content"
164        response = self.client.post(
165            reverse("authentik_api:files"),
166            {
167                "file": BytesIO(file_content),
168                "name": file_name,
169                "usage": FileUsage.MEDIA.value,
170            },
171            format="multipart",
172        )
173
174        self.assertEqual(response.status_code, 200)
175        self.assertTrue(manager.file_exists(file_name))
176        manager.delete_file(file_name)
177
178    def test_upload_file_duplicate(self):
179        """Test uploading file that already exists"""
180        manager = FileManager(FileUsage.MEDIA)
181        file_name = "test-file.png"
182        file_content = b"test content"
183        manager.save_file(file_name, file_content)
184        response = self.client.post(
185            reverse("authentik_api:files"),
186            {
187                "file": BytesIO(file_content),
188                "name": file_name,
189            },
190            format="multipart",
191        )
192
193        self.assertEqual(response.status_code, 400)
194        self.assertIn("already exists", str(response.data))
195        manager.delete_file(file_name)
196
197    def test_delete_without_name_parameter(self):
198        """Test delete without name parameter"""
199        response = self.client.delete(reverse("authentik_api:files"))
200
201        self.assertEqual(response.status_code, 400)
202        self.assertIn("field is required", str(response.data))
203
204    def test_list_files_includes_themed_urls_none(self):
205        """Test listing files includes themed_urls as None for non-themed files"""
206        manager = FileManager(FileUsage.MEDIA)
207        file_name = "test-no-theme.png"
208        manager.save_file(file_name, b"test content")
209
210        response = self.client.get(
211            reverse("authentik_api:files", query={"search": file_name, "manageableOnly": "true"})
212        )
213
214        self.assertEqual(response.status_code, 200)
215        file_entry = next((f for f in response.data if f["name"] == file_name), None)
216        self.assertIsNotNone(file_entry)
217        self.assertIn("themed_urls", file_entry)
218        self.assertIsNone(file_entry["themed_urls"])
219
220        manager.delete_file(file_name)
221
222    def test_list_files_includes_themed_urls_dict(self):
223        """Test listing files includes themed_urls as dict for themed files"""
224        manager = FileManager(FileUsage.MEDIA)
225        file_name = "logo-%(theme)s.svg"
226        manager.save_file("logo-light.svg", b"<svg>light</svg>")
227        manager.save_file("logo-dark.svg", b"<svg>dark</svg>")
228        manager.save_file(file_name, b"<svg>placeholder</svg>")
229
230        response = self.client.get(
231            reverse("authentik_api:files", query={"search": "%(theme)s", "manageableOnly": "true"})
232        )
233
234        self.assertEqual(response.status_code, 200)
235        file_entry = next((f for f in response.data if f["name"] == file_name), None)
236        self.assertIsNotNone(file_entry)
237        self.assertIn("themed_urls", file_entry)
238        self.assertIsInstance(file_entry["themed_urls"], dict)
239        self.assertIn("light", file_entry["themed_urls"])
240        self.assertIn("dark", file_entry["themed_urls"])
241
242        manager.delete_file(file_name)
243        manager.delete_file("logo-light.svg")
244        manager.delete_file("logo-dark.svg")
245
246    def test_upload_file_with_theme_variable(self):
247        """Test uploading file with %(theme)s in name"""
248        manager = FileManager(FileUsage.MEDIA)
249        file_name = "brand-logo-%(theme)s.svg"
250        file_content = b"<svg></svg>"
251
252        response = self.client.post(
253            reverse("authentik_api:files"),
254            {
255                "file": BytesIO(file_content),
256                "name": file_name,
257                "usage": FileUsage.MEDIA.value,
258            },
259            format="multipart",
260        )
261
262        self.assertEqual(response.status_code, 200)
263        self.assertTrue(manager.file_exists(file_name))
264        manager.delete_file(file_name)
class TestFileAPI(authentik.admin.files.tests.utils.FileTestFileBackendMixin, django.test.testcases.TestCase):
 16class TestFileAPI(FileTestFileBackendMixin, TestCase):
 17    """test file api"""
 18
 19    def setUp(self) -> None:
 20        super().setUp()
 21        self.user = create_test_admin_user()
 22        self.client.force_login(self.user)
 23
 24    def test_upload_creates_event(self):
 25        """Test that uploading a file creates a FILE_UPLOADED event"""
 26        manager = FileManager(FileUsage.MEDIA)
 27        file_content = b"test file content"
 28        file_name = "test-upload.png"
 29
 30        # Upload file
 31        response = self.client.post(
 32            reverse("authentik_api:files"),
 33            {
 34                "file": BytesIO(file_content),
 35                "name": file_name,
 36                "usage": FileUsage.MEDIA.value,
 37            },
 38            format="multipart",
 39        )
 40
 41        self.assertEqual(response.status_code, 200)
 42
 43        # Verify event was created
 44        event = Event.objects.filter(action=EventAction.MODEL_CREATED).first()
 45
 46        self.assertIsNotNone(event)
 47        assert event is not None  # nosec
 48        self.assertEqual(event.context["model"]["name"], file_name)
 49        self.assertEqual(event.context["model"]["usage"], FileUsage.MEDIA.value)
 50        self.assertEqual(event.context["model"]["mime_type"], "image/png")
 51
 52        # Verify user is captured
 53        self.assertEqual(event.user["username"], self.user.username)
 54        self.assertEqual(event.user["pk"], self.user.pk)
 55
 56        manager.delete_file(file_name)
 57
 58    def test_delete_creates_event(self):
 59        """Test that deleting a file creates an event"""
 60        manager = FileManager(FileUsage.MEDIA)
 61        file_name = "test-delete.png"
 62        manager.save_file(file_name, b"test content")
 63
 64        # Delete file
 65        response = self.client.delete(
 66            reverse(
 67                "authentik_api:files",
 68                query={
 69                    "name": file_name,
 70                    "usage": FileUsage.MEDIA.value,
 71                },
 72            )
 73        )
 74
 75        self.assertEqual(response.status_code, 200)
 76
 77        # Verify event was created
 78        event = Event.objects.filter(action=EventAction.MODEL_DELETED).first()
 79
 80        self.assertIsNotNone(event)
 81        assert event is not None  # nosec
 82        self.assertEqual(event.context["model"]["name"], file_name)
 83        self.assertEqual(event.context["model"]["usage"], FileUsage.MEDIA.value)
 84
 85        # Verify user is captured
 86        self.assertEqual(event.user["username"], self.user.username)
 87        self.assertEqual(event.user["pk"], self.user.pk)
 88
 89    def test_list_files_basic(self):
 90        """Test listing files with default parameters"""
 91        response = self.client.get(reverse("authentik_api:files"))
 92
 93        self.assertEqual(response.status_code, 200)
 94        self.assertIn(
 95            {
 96                "name": "/static/authentik/sources/ldap.png",
 97                "url": "http://testserver/static/authentik/sources/ldap.png",
 98                "mime_type": "image/png",
 99                "themed_urls": None,
100            },
101            response.data,
102        )
103
104    def test_list_files_invalid_usage(self):
105        """Test listing files with invalid usage parameter"""
106        response = self.client.get(
107            reverse(
108                "authentik_api:files",
109                query={
110                    "usage": "invalid",
111                },
112            )
113        )
114
115        self.assertEqual(response.status_code, 400)
116        self.assertIn("not a valid choice", str(response.data))
117
118    def test_list_files_with_search(self):
119        """Test listing files with search query"""
120        response = self.client.get(
121            reverse(
122                "authentik_api:files",
123                query={
124                    "search": "ldap.png",
125                },
126            )
127        )
128
129        self.assertEqual(response.status_code, 200)
130        self.assertIn(
131            {
132                "name": "/static/authentik/sources/ldap.png",
133                "url": "http://testserver/static/authentik/sources/ldap.png",
134                "mime_type": "image/png",
135                "themed_urls": None,
136            },
137            response.data,
138        )
139
140    def test_list_files_with_manageable_only(self):
141        """Test listing files with omit parameter"""
142        response = self.client.get(
143            reverse(
144                "authentik_api:files",
145                query={
146                    "manageableOnly": "true",
147                },
148            )
149        )
150
151        self.assertEqual(response.status_code, 200)
152        self.assertNotIn(
153            {
154                "name": "/static/dist/assets/images/flow_background.jpg",
155                "mime_type": "image/jpeg",
156            },
157            response.data,
158        )
159
160    def test_upload_file_with_custom_path(self):
161        """Test uploading file with custom path"""
162        manager = FileManager(FileUsage.MEDIA)
163        file_name = "custom/test"
164        file_content = b"test content"
165        response = self.client.post(
166            reverse("authentik_api:files"),
167            {
168                "file": BytesIO(file_content),
169                "name": file_name,
170                "usage": FileUsage.MEDIA.value,
171            },
172            format="multipart",
173        )
174
175        self.assertEqual(response.status_code, 200)
176        self.assertTrue(manager.file_exists(file_name))
177        manager.delete_file(file_name)
178
179    def test_upload_file_duplicate(self):
180        """Test uploading file that already exists"""
181        manager = FileManager(FileUsage.MEDIA)
182        file_name = "test-file.png"
183        file_content = b"test content"
184        manager.save_file(file_name, file_content)
185        response = self.client.post(
186            reverse("authentik_api:files"),
187            {
188                "file": BytesIO(file_content),
189                "name": file_name,
190            },
191            format="multipart",
192        )
193
194        self.assertEqual(response.status_code, 400)
195        self.assertIn("already exists", str(response.data))
196        manager.delete_file(file_name)
197
198    def test_delete_without_name_parameter(self):
199        """Test delete without name parameter"""
200        response = self.client.delete(reverse("authentik_api:files"))
201
202        self.assertEqual(response.status_code, 400)
203        self.assertIn("field is required", str(response.data))
204
205    def test_list_files_includes_themed_urls_none(self):
206        """Test listing files includes themed_urls as None for non-themed files"""
207        manager = FileManager(FileUsage.MEDIA)
208        file_name = "test-no-theme.png"
209        manager.save_file(file_name, b"test content")
210
211        response = self.client.get(
212            reverse("authentik_api:files", query={"search": file_name, "manageableOnly": "true"})
213        )
214
215        self.assertEqual(response.status_code, 200)
216        file_entry = next((f for f in response.data if f["name"] == file_name), None)
217        self.assertIsNotNone(file_entry)
218        self.assertIn("themed_urls", file_entry)
219        self.assertIsNone(file_entry["themed_urls"])
220
221        manager.delete_file(file_name)
222
223    def test_list_files_includes_themed_urls_dict(self):
224        """Test listing files includes themed_urls as dict for themed files"""
225        manager = FileManager(FileUsage.MEDIA)
226        file_name = "logo-%(theme)s.svg"
227        manager.save_file("logo-light.svg", b"<svg>light</svg>")
228        manager.save_file("logo-dark.svg", b"<svg>dark</svg>")
229        manager.save_file(file_name, b"<svg>placeholder</svg>")
230
231        response = self.client.get(
232            reverse("authentik_api:files", query={"search": "%(theme)s", "manageableOnly": "true"})
233        )
234
235        self.assertEqual(response.status_code, 200)
236        file_entry = next((f for f in response.data if f["name"] == file_name), None)
237        self.assertIsNotNone(file_entry)
238        self.assertIn("themed_urls", file_entry)
239        self.assertIsInstance(file_entry["themed_urls"], dict)
240        self.assertIn("light", file_entry["themed_urls"])
241        self.assertIn("dark", file_entry["themed_urls"])
242
243        manager.delete_file(file_name)
244        manager.delete_file("logo-light.svg")
245        manager.delete_file("logo-dark.svg")
246
247    def test_upload_file_with_theme_variable(self):
248        """Test uploading file with %(theme)s in name"""
249        manager = FileManager(FileUsage.MEDIA)
250        file_name = "brand-logo-%(theme)s.svg"
251        file_content = b"<svg></svg>"
252
253        response = self.client.post(
254            reverse("authentik_api:files"),
255            {
256                "file": BytesIO(file_content),
257                "name": file_name,
258                "usage": FileUsage.MEDIA.value,
259            },
260            format="multipart",
261        )
262
263        self.assertEqual(response.status_code, 200)
264        self.assertTrue(manager.file_exists(file_name))
265        manager.delete_file(file_name)

test file api

def setUp(self) -> None:
19    def setUp(self) -> None:
20        super().setUp()
21        self.user = create_test_admin_user()
22        self.client.force_login(self.user)

Hook method for setting up the test fixture before exercising it.

def test_upload_creates_event(self):
24    def test_upload_creates_event(self):
25        """Test that uploading a file creates a FILE_UPLOADED event"""
26        manager = FileManager(FileUsage.MEDIA)
27        file_content = b"test file content"
28        file_name = "test-upload.png"
29
30        # Upload file
31        response = self.client.post(
32            reverse("authentik_api:files"),
33            {
34                "file": BytesIO(file_content),
35                "name": file_name,
36                "usage": FileUsage.MEDIA.value,
37            },
38            format="multipart",
39        )
40
41        self.assertEqual(response.status_code, 200)
42
43        # Verify event was created
44        event = Event.objects.filter(action=EventAction.MODEL_CREATED).first()
45
46        self.assertIsNotNone(event)
47        assert event is not None  # nosec
48        self.assertEqual(event.context["model"]["name"], file_name)
49        self.assertEqual(event.context["model"]["usage"], FileUsage.MEDIA.value)
50        self.assertEqual(event.context["model"]["mime_type"], "image/png")
51
52        # Verify user is captured
53        self.assertEqual(event.user["username"], self.user.username)
54        self.assertEqual(event.user["pk"], self.user.pk)
55
56        manager.delete_file(file_name)

Test that uploading a file creates a FILE_UPLOADED event

def test_delete_creates_event(self):
58    def test_delete_creates_event(self):
59        """Test that deleting a file creates an event"""
60        manager = FileManager(FileUsage.MEDIA)
61        file_name = "test-delete.png"
62        manager.save_file(file_name, b"test content")
63
64        # Delete file
65        response = self.client.delete(
66            reverse(
67                "authentik_api:files",
68                query={
69                    "name": file_name,
70                    "usage": FileUsage.MEDIA.value,
71                },
72            )
73        )
74
75        self.assertEqual(response.status_code, 200)
76
77        # Verify event was created
78        event = Event.objects.filter(action=EventAction.MODEL_DELETED).first()
79
80        self.assertIsNotNone(event)
81        assert event is not None  # nosec
82        self.assertEqual(event.context["model"]["name"], file_name)
83        self.assertEqual(event.context["model"]["usage"], FileUsage.MEDIA.value)
84
85        # Verify user is captured
86        self.assertEqual(event.user["username"], self.user.username)
87        self.assertEqual(event.user["pk"], self.user.pk)

Test that deleting a file creates an event

def test_list_files_basic(self):
 89    def test_list_files_basic(self):
 90        """Test listing files with default parameters"""
 91        response = self.client.get(reverse("authentik_api:files"))
 92
 93        self.assertEqual(response.status_code, 200)
 94        self.assertIn(
 95            {
 96                "name": "/static/authentik/sources/ldap.png",
 97                "url": "http://testserver/static/authentik/sources/ldap.png",
 98                "mime_type": "image/png",
 99                "themed_urls": None,
100            },
101            response.data,
102        )

Test listing files with default parameters

def test_list_files_invalid_usage(self):
104    def test_list_files_invalid_usage(self):
105        """Test listing files with invalid usage parameter"""
106        response = self.client.get(
107            reverse(
108                "authentik_api:files",
109                query={
110                    "usage": "invalid",
111                },
112            )
113        )
114
115        self.assertEqual(response.status_code, 400)
116        self.assertIn("not a valid choice", str(response.data))

Test listing files with invalid usage parameter

def test_list_files_with_manageable_only(self):
140    def test_list_files_with_manageable_only(self):
141        """Test listing files with omit parameter"""
142        response = self.client.get(
143            reverse(
144                "authentik_api:files",
145                query={
146                    "manageableOnly": "true",
147                },
148            )
149        )
150
151        self.assertEqual(response.status_code, 200)
152        self.assertNotIn(
153            {
154                "name": "/static/dist/assets/images/flow_background.jpg",
155                "mime_type": "image/jpeg",
156            },
157            response.data,
158        )

Test listing files with omit parameter

def test_upload_file_with_custom_path(self):
160    def test_upload_file_with_custom_path(self):
161        """Test uploading file with custom path"""
162        manager = FileManager(FileUsage.MEDIA)
163        file_name = "custom/test"
164        file_content = b"test content"
165        response = self.client.post(
166            reverse("authentik_api:files"),
167            {
168                "file": BytesIO(file_content),
169                "name": file_name,
170                "usage": FileUsage.MEDIA.value,
171            },
172            format="multipart",
173        )
174
175        self.assertEqual(response.status_code, 200)
176        self.assertTrue(manager.file_exists(file_name))
177        manager.delete_file(file_name)

Test uploading file with custom path

def test_upload_file_duplicate(self):
179    def test_upload_file_duplicate(self):
180        """Test uploading file that already exists"""
181        manager = FileManager(FileUsage.MEDIA)
182        file_name = "test-file.png"
183        file_content = b"test content"
184        manager.save_file(file_name, file_content)
185        response = self.client.post(
186            reverse("authentik_api:files"),
187            {
188                "file": BytesIO(file_content),
189                "name": file_name,
190            },
191            format="multipart",
192        )
193
194        self.assertEqual(response.status_code, 400)
195        self.assertIn("already exists", str(response.data))
196        manager.delete_file(file_name)

Test uploading file that already exists

def test_delete_without_name_parameter(self):
198    def test_delete_without_name_parameter(self):
199        """Test delete without name parameter"""
200        response = self.client.delete(reverse("authentik_api:files"))
201
202        self.assertEqual(response.status_code, 400)
203        self.assertIn("field is required", str(response.data))

Test delete without name parameter

def test_list_files_includes_themed_urls_none(self):
205    def test_list_files_includes_themed_urls_none(self):
206        """Test listing files includes themed_urls as None for non-themed files"""
207        manager = FileManager(FileUsage.MEDIA)
208        file_name = "test-no-theme.png"
209        manager.save_file(file_name, b"test content")
210
211        response = self.client.get(
212            reverse("authentik_api:files", query={"search": file_name, "manageableOnly": "true"})
213        )
214
215        self.assertEqual(response.status_code, 200)
216        file_entry = next((f for f in response.data if f["name"] == file_name), None)
217        self.assertIsNotNone(file_entry)
218        self.assertIn("themed_urls", file_entry)
219        self.assertIsNone(file_entry["themed_urls"])
220
221        manager.delete_file(file_name)

Test listing files includes themed_urls as None for non-themed files

def test_list_files_includes_themed_urls_dict(self):
223    def test_list_files_includes_themed_urls_dict(self):
224        """Test listing files includes themed_urls as dict for themed files"""
225        manager = FileManager(FileUsage.MEDIA)
226        file_name = "logo-%(theme)s.svg"
227        manager.save_file("logo-light.svg", b"<svg>light</svg>")
228        manager.save_file("logo-dark.svg", b"<svg>dark</svg>")
229        manager.save_file(file_name, b"<svg>placeholder</svg>")
230
231        response = self.client.get(
232            reverse("authentik_api:files", query={"search": "%(theme)s", "manageableOnly": "true"})
233        )
234
235        self.assertEqual(response.status_code, 200)
236        file_entry = next((f for f in response.data if f["name"] == file_name), None)
237        self.assertIsNotNone(file_entry)
238        self.assertIn("themed_urls", file_entry)
239        self.assertIsInstance(file_entry["themed_urls"], dict)
240        self.assertIn("light", file_entry["themed_urls"])
241        self.assertIn("dark", file_entry["themed_urls"])
242
243        manager.delete_file(file_name)
244        manager.delete_file("logo-light.svg")
245        manager.delete_file("logo-dark.svg")

Test listing files includes themed_urls as dict for themed files

def test_upload_file_with_theme_variable(self):
247    def test_upload_file_with_theme_variable(self):
248        """Test uploading file with %(theme)s in name"""
249        manager = FileManager(FileUsage.MEDIA)
250        file_name = "brand-logo-%(theme)s.svg"
251        file_content = b"<svg></svg>"
252
253        response = self.client.post(
254            reverse("authentik_api:files"),
255            {
256                "file": BytesIO(file_content),
257                "name": file_name,
258                "usage": FileUsage.MEDIA.value,
259            },
260            format="multipart",
261        )
262
263        self.assertEqual(response.status_code, 200)
264        self.assertTrue(manager.file_exists(file_name))
265        manager.delete_file(file_name)

Test uploading file with %(theme)s in name