diff --git a/apicore/views.py b/apicore/views.py index 1cca3d0..47f8da8 100644 --- a/apicore/views.py +++ b/apicore/views.py @@ -219,7 +219,7 @@ def list(self, request): cache_key = f"userfile:{user_id}" data = cache.get(cache_key) if data is None: - with user_obj.user_file.open("r") as f: + with user_obj.user_file.open("rb") as f: lines = [line.decode("utf-8") for line in f.readlines()] data = LuaParser(lines).parse() cache.set(cache_key, data, timeout=None) diff --git a/tests/test_views.py b/tests/test_views.py index 0b21c33..8d10494 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -156,6 +156,45 @@ def test_upload_does_not_500(self): ) assert resp.status_code == 200 + def test_addon_page_reads_uploaded_file(self): + """Regression test: page=addon (and any non-header page reading the + uploaded file) used to crash with 'str' object has no attribute 'decode' + because the file was opened in text mode ("r") despite the parser + expecting bytes to decode itself.""" + user_id = "u1" + ProfileUser.objects.create(user_id=user_id, user_file="", user_last_update=timezone.now()) + c = self._authed_client(user_id) + + body = ( + "FazzToolsScraperDB = {\n" + "preamble\n" + '["alts"] = {\n' + '["Testchar-Realm"] = {\n' + '["gold"] = 12345,\n' + "},\n" + "},\n" + "}\n" + ) + upload = SimpleUploadedFile( + "FazzToolsScraper.lua", body.encode(), content_type="text/plain" + ) + c.put( + f"/api/profile/users/{user_id}/", + data=encode_multipart( + BOUNDARY, + { + "user_id": user_id, + "user_file": upload, + "user_last_update": timezone.now().isoformat(), + }, + ), + content_type=MULTIPART_CONTENT, + ) + + resp = c.get(f"/api/profile/users/?user={user_id}&page=addon") + assert resp.status_code == 200 + assert resp.json() == [] + # --------------------------------------------------------------------------- # ProfileAltView — IsSessionUser enforcement