Download test/test_user_profile.py from ethonmax/picpocket: direct link, hf CLI and curl.
- Browser
- Download file 15.6 kB
-
https://huggingface.co/spaces/ethonmax/picpocket/resolve/main/test/test_user_profile.py
- Command line
-
hf download hf://spaces/ethonmax/picpocket/test/test_user_profile.py
-
curl -L -o test_user_profile.py https://huggingface.co/spaces/ethonmax/picpocket/resolve/main/test/test_user_profile.py
15.6 kB
| import unittest | |
| from unittest.mock import AsyncMock, patch | |
| from test_ranking_consistency import database | |
| from fastapi import FastAPI | |
| from fastapi.testclient import TestClient | |
| import profile_routes | |
| class UserProfileApiTest(unittest.TestCase): | |
| def setUp(self): | |
| app = FastAPI() | |
| app.include_router(profile_routes.profile_router, prefix="/facescore") | |
| self.client = TestClient(app) | |
| self.headers = {"x-device-id": "0123456789abcdef"} | |
| def test_new_user_is_empty_and_does_not_create_record(self): | |
| with patch.object(profile_routes, "get_user_profile", AsyncMock(return_value={"display_name": "", "nickname_initialized": False})) as read: | |
| response = self.client.get("/facescore/app/profile", headers=self.headers) | |
| self.assertEqual(200, response.status_code) | |
| self.assertEqual({"success": True, "display_name": "", "nickname_initialized": False}, response.json()) | |
| read.assert_awaited_once_with("0123456789abcdef") | |
| def test_save_trim_clear_and_unicode_limit(self): | |
| for name, expected in [(" 小星星 ", "小星星"), ("", ""), (" ", ""), ("😀" * 12, "😀" * 12), ("昵称" * 6, "昵称" * 6)]: | |
| with self.subTest(name=name), patch.object(profile_routes, "set_user_display_name", AsyncMock()) as write: | |
| response = self.client.put("/facescore/app/profile", headers=self.headers, json={"display_name": name}) | |
| self.assertEqual(200, response.status_code) | |
| self.assertEqual(expected, response.json()["display_name"]) | |
| self.assertTrue(response.json()["nickname_initialized"]) | |
| write.assert_awaited_once_with("0123456789abcdef", expected, | |
| device_type=None, device_model=None, os_version=None, app_version=None, | |
| timezone=None, region=None, language=None) | |
| def test_first_profile_save_writes_complete_device_in_one_statement(self): | |
| # 贯穿 HTTP -> 实际数据库函数,仅隔离最终数据库连接。 | |
| headers = {**self.headers, "x-device-type": "Android", "x-device-model": "Google Pixel 9", | |
| "x-os-version": "Android 15", "x-app-version": "1.8.11", | |
| "x-timezone": "Asia/Shanghai", "x-region": "CN", "Accept-Language": "zh-CN", | |
| "x-client-source": "wechat"} | |
| with patch.object(database, "execute", AsyncMock()) as write: | |
| response = self.client.put("/facescore/app/profile", headers=headers, | |
| json={"display_name": "GlowBoss4826"}) | |
| self.assertEqual(200, response.status_code) | |
| write.assert_awaited_once() | |
| sql, params = write.await_args.args | |
| self.assertEqual(("0123456789abcdef", "GlowBoss4826", "Android", "Google Pixel 9", | |
| "Android 15", "1.8.11", "Asia/Shanghai", "CN", "zh-CN"), params) | |
| self.assertIn("'android'", sql) # 来源仍由接口确定,不能被请求头伪造。 | |
| self.assertNotIn("tpl_app_processed_images", sql) | |
| def test_region_fallback_and_blank_headers(self): | |
| for extra, expected in [({"x-region": " US ", "cf-ipcountry": "JP"}, "US"), | |
| ({"x-region": " ", "cf-ipcountry": "JP"}, "JP"), | |
| ({"x-country-code": "DE", "x-geo-country": "FR"}, "DE"), | |
| ({"x-geo-country": "FR"}, "FR"), ({}, None)]: | |
| with self.subTest(extra=extra), patch.object(profile_routes, "set_user_display_name", AsyncMock()) as write: | |
| response = self.client.put("/facescore/app/profile", | |
| headers={**self.headers, **extra, "x-device-model": " ", "x-app-version": " 1.8.11 "}, | |
| json={"display_name": ""}) | |
| self.assertEqual(200, response.status_code) | |
| self.assertEqual(expected, write.await_args.kwargs["region"]) | |
| self.assertIsNone(write.await_args.kwargs["device_model"]) | |
| self.assertEqual("1.8.11", write.await_args.kwargs["app_version"]) | |
| self.assertEqual("", write.await_args.args[1]) | |
| def test_device_headers_fit_database_columns(self): | |
| fields = {"x-device-type": ("device_type", 32), "x-device-model": ("device_model", 64), | |
| "x-os-version": ("os_version", 32), "x-app-version": ("app_version", 32), | |
| "x-timezone": ("timezone", 64), "x-region": ("region", 16), | |
| "Accept-Language": ("language", 32)} | |
| with patch.object(profile_routes, "set_user_display_name", AsyncMock()) as write: | |
| response = self.client.put("/facescore/app/profile", | |
| headers={**self.headers, **{key: "x" * 200 for key in fields}}, | |
| json={"display_name": "GlowBoss4826"}) | |
| self.assertEqual(200, response.status_code) | |
| for field, limit in fields.values(): | |
| self.assertEqual("x" * limit, write.await_args.kwargs[field]) | |
| def test_invalid_input_never_writes(self): | |
| for body in [{}, {"display_name": None}, {"display_name": 12}, {"display_name": "😀" * 13}, | |
| {"display_name": "x\ny"}, {"display_name": "\u202eevil"}, {"display_name": "\u0000"}]: | |
| with self.subTest(body=body), patch.object(profile_routes, "set_user_display_name", AsyncMock()) as write: | |
| response = self.client.put("/facescore/app/profile", headers=self.headers, json=body) | |
| self.assertEqual(422, response.status_code) | |
| write.assert_not_awaited() | |
| def test_initialize_returns_actual_name_including_manual_clear(self): | |
| for actual in ["GlowBoss4826", "AlreadySet", ""]: | |
| state = {"display_name": actual, "nickname_initialized": True} | |
| with self.subTest(actual=actual), \ | |
| patch.object(profile_routes, "get_user_profile", AsyncMock(return_value=state)), \ | |
| patch.object(database, "execute", AsyncMock()) as write: | |
| response = self.client.post("/facescore/app/profile/initialize", | |
| headers={**self.headers, "x-app-version": "1.8.11", "x-region": "CN"}, | |
| json={"display_name": "GlowBoss4826"}) | |
| self.assertEqual(200, response.status_code) | |
| self.assertEqual({"success": True, **state}, response.json()) | |
| write.assert_awaited_once() | |
| sql, params = write.await_args.args | |
| self.assertIn("IF(display_name_initialized = 0 AND display_name = '', VALUES(display_name), display_name)", sql) | |
| self.assertEqual("1.8.11", params[5]) | |
| self.assertEqual("CN", params[7]) | |
| def test_initialize_rejects_empty_developer_and_invalid_names(self): | |
| for candidate in ["", "99", "9527", "小帅9527", "GlowBoss0000", "toolongtitle1234", "Name1234\n"]: | |
| with self.subTest(candidate=candidate), patch.object(profile_routes, "set_user_display_name", AsyncMock()) as write: | |
| response = self.client.post("/facescore/app/profile/initialize", headers=self.headers, | |
| json={"display_name": candidate}) | |
| self.assertEqual(422, response.status_code) | |
| write.assert_not_awaited() | |
| def test_initialize_requires_valid_device(self): | |
| for headers in [{}, {"x-device-id": "unknown"}, {"x-device-id": " "}]: | |
| with patch.object(profile_routes, "set_user_display_name", AsyncMock()) as write: | |
| response = self.client.post("/facescore/app/profile/initialize", headers=headers, | |
| json={"display_name": "GlowBoss4826"}) | |
| self.assertIn(response.status_code, (400, 422)) | |
| write.assert_not_awaited() | |
| def test_initialize_failed_read_after_write_is_safe_to_retry(self): | |
| state = {"display_name": "OtherName", "nickname_initialized": True} | |
| with patch.object(profile_routes, "get_user_profile", AsyncMock(side_effect=[RuntimeError("offline"), state])), \ | |
| patch.object(database, "execute", AsyncMock()) as write: | |
| first = self.client.post("/facescore/app/profile/initialize", headers=self.headers, | |
| json={"display_name": "GlowBoss4826"}) | |
| second = self.client.post("/facescore/app/profile/initialize", headers=self.headers, | |
| json={"display_name": "GlowBoss4826"}) | |
| self.assertEqual(503, first.status_code) | |
| self.assertEqual("OtherName", second.json()["display_name"]) | |
| self.assertEqual(2, write.await_count) | |
| for call in write.await_args_list: | |
| self.assertIn("IF(display_name_initialized = 0 AND display_name = ''", call.args[0]) | |
| def test_initialize_write_failure_never_reports_success(self): | |
| with patch.object(database, "execute", AsyncMock(side_effect=RuntimeError("offline"))), \ | |
| patch.object(profile_routes, "get_user_profile", AsyncMock()) as read: | |
| response = self.client.post("/facescore/app/profile/initialize", headers=self.headers, | |
| json={"display_name": "GlowBoss4826"}) | |
| self.assertEqual(503, response.status_code) | |
| read.assert_not_awaited() | |
| def test_missing_or_invalid_device_is_rejected(self): | |
| for headers in [{}, {"x-device-id": "unknown"}, {"x-device-id": " "}, {"x-device-id": "x" * 129}]: | |
| with patch.object(profile_routes, "set_user_display_name", AsyncMock()) as write: | |
| response = self.client.put("/facescore/app/profile", headers=headers, json={"display_name": "hello"}) | |
| self.assertIn(response.status_code, (400, 422)) | |
| write.assert_not_awaited() | |
| def test_database_failure_does_not_report_save_success(self): | |
| with patch.object(profile_routes, "set_user_display_name", AsyncMock(side_effect=RuntimeError("offline"))): | |
| response = self.client.put("/facescore/app/profile", headers=self.headers, json={"display_name": "hello"}) | |
| self.assertEqual(503, response.status_code) | |
| with patch.object(profile_routes, "get_user_profile", AsyncMock(side_effect=RuntimeError("offline"))): | |
| response = self.client.get("/facescore/app/profile", headers=self.headers) | |
| self.assertEqual(503, response.status_code) | |
| def test_device_and_nickname_write_failure_returns_error(self): | |
| with patch.object(database, "execute", AsyncMock(side_effect=RuntimeError("offline"))) as write: | |
| response = self.client.put("/facescore/app/profile", | |
| headers={**self.headers, "x-device-model": "Google Pixel 9"}, | |
| json={"display_name": "GlowBoss4826"}) | |
| self.assertEqual(503, response.status_code) | |
| write.assert_awaited_once() | |
| class DisplayNameDatabaseTest(unittest.IsolatedAsyncioTestCase): | |
| def setUp(self): | |
| database._LEADERBOARD_CACHE.clear() | |
| async def test_save_only_changes_device_profile(self): | |
| with patch.object(database, "execute", AsyncMock()) as write: | |
| await database.set_user_display_name("device", "小星星") | |
| sql, params = write.await_args.args | |
| self.assertEqual(("device", "小星星") + (None,) * 7, params) | |
| self.assertNotIn("tpl_app_processed_images", sql) | |
| self.assertNotIn("nickname", sql) | |
| self.assertIn("ON DUPLICATE KEY UPDATE display_name", sql) | |
| async def test_missing_device_fields_do_not_clear_existing_profile(self): | |
| with patch.object(database, "execute", AsyncMock()) as write: | |
| await database.set_user_display_name("device", "", app_version="1.8.11", region="") | |
| sql, params = write.await_args.args | |
| self.assertEqual(("device", "", None, None, None, "1.8.11", None, "", None), params) | |
| for field in ("device_type", "device_model", "os_version", "app_version", "timezone", "region", "language"): | |
| self.assertIn(f"{field} = COALESCE(NULLIF(VALUES({field}), ''), {field})", sql) | |
| self.assertIn("display_name = VALUES(display_name)", sql) # 清空昵称仍有效。 | |
| self.assertIn("IF(client_source = 'unknown', VALUES(client_source), client_source)", sql) | |
| async def test_device_telemetry_preserves_display_name(self): | |
| with patch.object(database, "execute", AsyncMock()) as write: | |
| await database.upsert_device_record(device_id="device", app_version="1.8.6") | |
| self.assertNotIn("display_name", write.await_args.args[0]) | |
| async def test_missing_profile_reads_empty(self): | |
| with patch.object(database, "fetch_one", AsyncMock(return_value=None)): | |
| self.assertEqual("", await database.get_user_display_name("device")) | |
| async def test_profile_distinguishes_unset_named_and_cleared(self): | |
| for row, expected in [(None, False), | |
| ({"display_name": "", "display_name_initialized": 0}, False), | |
| ({"display_name": "", "display_name_initialized": 1}, True), | |
| ({"display_name": "OldName", "display_name_initialized": 0}, True)]: | |
| with self.subTest(row=row), patch.object(database, "fetch_one", AsyncMock(return_value=row)): | |
| profile = await database.get_user_profile("device") | |
| self.assertEqual(expected, profile["nickname_initialized"]) | |
| async def test_manual_clear_marks_initialized_and_auto_guard_precedes_marker(self): | |
| with patch.object(database, "execute", AsyncMock()) as write: | |
| await database.set_user_display_name("device", "") | |
| await database.set_user_display_name("device", "GlowBoss4826", initialize_only=True) | |
| manual, automatic = [call.args[0] for call in write.await_args_list] | |
| self.assertIn("display_name = VALUES(display_name)", manual) | |
| self.assertIn("display_name_initialized = 1", manual) | |
| self.assertLess(automatic.index("display_name = IF("), automatic.index("display_name_initialized = 1")) | |
| async def test_cached_rank_gets_live_names_for_all_records_and_other_viewers(self): | |
| rows = [{"id": 1, "score": 90, "nickname": "android_release_device", "updated_at": "2026-09-24 10:00:00"}, | |
| {"id": 2, "score": 89, "nickname": "android_device", "updated_at": "2026-09-23 10:00:00"}, | |
| {"id": 3, "score": 88, "nickname": "android_release_other"}] | |
| with patch.object(database, "fetch_all", AsyncMock(side_effect=[rows, | |
| [{"device_id": "device", "display_name": "old"}], | |
| [{"device_id": "device", "display_name": "new"}], | |
| [{"device_id": "device", "display_name": ""}]])) as fetch: | |
| first = await database.get_face_score_leaderboard(None, 50, "device") | |
| second = await database.get_face_score_leaderboard(None, 50, "other") | |
| third = await database.get_face_score_leaderboard(None, 50, "device") | |
| self.assertEqual(["old", "old", ""], [i["display_name"] for i in first["items"]]) | |
| self.assertEqual(["new", "new", ""], [i["display_name"] for i in second["items"]]) | |
| self.assertEqual(["", "", ""], [i["display_name"] for i in third["items"]]) | |
| self.assertFalse(second["items"][0]["is_current_user"]) | |
| self.assertTrue(second["items"][2]["is_current_user"]) | |
| self.assertEqual(4, fetch.await_count) # 排名仅查一次;昵称每次实时读取。 | |
| for result in (second, third): | |
| self.assertEqual([1, 2, 3], [i["rank"] for i in result["items"]]) | |
| self.assertEqual(first["items"][0]["updated_at"], result["items"][0]["updated_at"]) | |
| if __name__ == "__main__": | |
| unittest.main() | |