2.0.0-rc2: Achievements Refactored & Admin improved.
This commit is contained in:
+163
-2
@@ -46,6 +46,7 @@ class ApplicationIntegrationTests(unittest.TestCase):
|
||||
'/matches/',
|
||||
'/reports/',
|
||||
'/awards/',
|
||||
'/discover/',
|
||||
'/players/',
|
||||
f'/players/{self.roster_ids[0]}',
|
||||
'/teams/',
|
||||
@@ -59,6 +60,35 @@ class ApplicationIntegrationTests(unittest.TestCase):
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertGreater(len(response.data), 100)
|
||||
|
||||
def test_discovery_filters_and_player_medals_render(self):
|
||||
response = self.client.get('/discover/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
for label in ('趣味发现', '表现勋章墙', '勋章总榜'):
|
||||
self.assertIn(label.encode('utf-8'), response.data)
|
||||
|
||||
response = self.client.get('/discover/?tone=negative')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIn('低谷收藏家'.encode('utf-8'), response.data)
|
||||
|
||||
response = self.client.get('/discover/?dimension=elo')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIn(b'ELO Medal', response.data)
|
||||
|
||||
l3 = sqlite3.connect(Config.DB_L3_PATH)
|
||||
try:
|
||||
medal_player = l3.execute(
|
||||
"""
|
||||
SELECT steam_id_64
|
||||
FROM dm_performance_medals
|
||||
ORDER BY medal_rank
|
||||
LIMIT 1
|
||||
"""
|
||||
).fetchone()[0]
|
||||
finally:
|
||||
l3.close()
|
||||
response = self.client.get(f'/players/{medal_player}')
|
||||
self.assertIn(b'Map & ELO Medals', response.data)
|
||||
|
||||
def test_match_detail_contains_post_match_report(self):
|
||||
l3 = sqlite3.connect(Config.DB_L3_PATH)
|
||||
try:
|
||||
@@ -103,10 +133,11 @@ class ApplicationIntegrationTests(unittest.TestCase):
|
||||
def test_admin_integrity_page_and_json_render(self):
|
||||
with self.client.session_transaction() as session:
|
||||
session['is_admin'] = True
|
||||
session['_csrf_token'] = 'test-csrf'
|
||||
|
||||
response = self.client.get('/admin/data-integrity')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIn('数据完整性中心'.encode('utf-8'), response.data)
|
||||
self.assertIn('数据质量中心'.encode('utf-8'), response.data)
|
||||
|
||||
response = self.client.get('/admin/data-integrity?format=json')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
@@ -120,13 +151,66 @@ class ApplicationIntegrationTests(unittest.TestCase):
|
||||
|
||||
response = self.client.get('/admin/import-match')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIn('比赛数据导入'.encode('utf-8'), response.data)
|
||||
self.assertIn('比赛导入'.encode('utf-8'), response.data)
|
||||
|
||||
def test_admin_console_pages_share_navigation_and_real_data(self):
|
||||
with self.client.session_transaction() as session:
|
||||
session['is_admin'] = True
|
||||
session['_csrf_token'] = 'test-csrf'
|
||||
pages = {
|
||||
'/admin/': ('运营总览', '完整数据流水线'),
|
||||
'/admin/jobs': ('作业中心', '执行记录'),
|
||||
'/admin/sql': ('只读查询控制台', 'Database Objects'),
|
||||
'/admin/system': ('系统与存储', '回滚快照'),
|
||||
}
|
||||
for path, labels in pages.items():
|
||||
with self.subTest(path=path):
|
||||
response = self.client.get(path)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIn('YRTV'.encode('utf-8'), response.data)
|
||||
for label in labels:
|
||||
self.assertIn(label.encode('utf-8'), response.data)
|
||||
|
||||
def test_admin_sql_is_read_only_and_csrf_protected(self):
|
||||
with self.client.session_transaction() as session:
|
||||
session['is_admin'] = True
|
||||
session['_csrf_token'] = 'test-csrf'
|
||||
|
||||
response = self.client.post(
|
||||
'/admin/sql',
|
||||
data={
|
||||
'_csrf_token': 'test-csrf',
|
||||
'db_name': 'l2',
|
||||
'query': 'SELECT COUNT(*) AS matches FROM fact_matches',
|
||||
},
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIn(b'matches', response.data)
|
||||
self.assertIn(b'208', response.data)
|
||||
|
||||
response = self.client.post(
|
||||
'/admin/sql',
|
||||
data={
|
||||
'_csrf_token': 'test-csrf',
|
||||
'db_name': 'l2',
|
||||
'query': 'DELETE FROM fact_matches',
|
||||
},
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIn(b'Only SELECT queries are allowed', response.data)
|
||||
|
||||
response = self.client.post(
|
||||
'/admin/trigger_etl',
|
||||
headers={'Accept': 'application/json'},
|
||||
)
|
||||
self.assertEqual(response.status_code, 400)
|
||||
|
||||
def test_duplicate_match_upload_is_rejected_without_starting_job(self):
|
||||
from database.paths import L1_DB
|
||||
|
||||
with self.client.session_transaction() as session:
|
||||
session['is_admin'] = True
|
||||
session['_csrf_token'] = 'test-csrf'
|
||||
with sqlite3.connect(str(L1_DB)) as db:
|
||||
raw = db.execute(
|
||||
"""
|
||||
@@ -142,6 +226,7 @@ class ApplicationIntegrationTests(unittest.TestCase):
|
||||
response = self.client.post(
|
||||
'/admin/import-match',
|
||||
data={
|
||||
'_csrf_token': 'test-csrf',
|
||||
'capture': (
|
||||
upload,
|
||||
'iframe_network.json',
|
||||
@@ -178,6 +263,8 @@ class ApplicationIntegrationTests(unittest.TestCase):
|
||||
):
|
||||
with self.subTest(label=label):
|
||||
self.assertIn(label.encode('utf-8'), response.data)
|
||||
self.assertIn(b'x-data="{ selected: \'last_20\' }"', response.data)
|
||||
self.assertNotIn(b'periods: [{', response.data)
|
||||
|
||||
def test_profile_period_api_and_trend_window(self):
|
||||
steam_id = self.roster_ids[0]
|
||||
@@ -699,6 +786,80 @@ class DatabaseGovernanceTests(unittest.TestCase):
|
||||
self.assertNotIn((award_type, period_key), seen)
|
||||
seen.add((award_type, period_key))
|
||||
|
||||
def test_discovery_tones_and_medal_rules(self):
|
||||
l3 = sqlite3.connect(Config.DB_L3_PATH)
|
||||
try:
|
||||
tones = {
|
||||
row[0] for row in l3.execute(
|
||||
'SELECT DISTINCT tone FROM dm_discovery_insights'
|
||||
)
|
||||
}
|
||||
medals = l3.execute(
|
||||
"""
|
||||
SELECT dimension_type, dimension_key, medal_rank,
|
||||
medal_tier, matches
|
||||
FROM dm_performance_medals
|
||||
"""
|
||||
).fetchall()
|
||||
finally:
|
||||
l3.close()
|
||||
|
||||
self.assertEqual(tones, {'positive', 'negative', 'fun'})
|
||||
self.assertGreater(len(medals), 0)
|
||||
seen = set()
|
||||
expected_tiers = {1: 'gold', 2: 'silver', 3: 'bronze'}
|
||||
for dimension_type, dimension_key, rank, tier, matches in medals:
|
||||
key = (dimension_type, dimension_key, rank)
|
||||
self.assertNotIn(key, seen)
|
||||
seen.add(key)
|
||||
self.assertIn(dimension_type, {'map', 'elo'})
|
||||
self.assertEqual(tier, expected_tiers[rank])
|
||||
self.assertGreaterEqual(matches, 5)
|
||||
|
||||
def test_map_gold_medal_matches_highest_eligible_rating(self):
|
||||
l3 = sqlite3.connect(Config.DB_L3_PATH)
|
||||
l2 = sqlite3.connect(Config.DB_L2_PATH)
|
||||
web = sqlite3.connect(Config.DB_WEB_PATH)
|
||||
try:
|
||||
medal = l3.execute(
|
||||
"""
|
||||
SELECT dimension_key, steam_id_64, avg_rating
|
||||
FROM dm_performance_medals
|
||||
WHERE dimension_type = 'map' AND medal_rank = 1
|
||||
ORDER BY dimension_key
|
||||
LIMIT 1
|
||||
"""
|
||||
).fetchone()
|
||||
roster_ids = json.loads(
|
||||
web.execute(
|
||||
"""
|
||||
SELECT player_ids_json FROM team_lineups
|
||||
WHERE is_active = 1
|
||||
"""
|
||||
).fetchone()[0]
|
||||
)
|
||||
placeholders = ','.join('?' for _ in roster_ids)
|
||||
expected = l2.execute(
|
||||
f"""
|
||||
SELECT p.steam_id_64, AVG(p.rating) AS avg_rating
|
||||
FROM fact_match_players p
|
||||
JOIN fact_matches m ON m.match_id = p.match_id
|
||||
WHERE p.steam_id_64 IN ({placeholders})
|
||||
AND m.map_name = ?
|
||||
GROUP BY p.steam_id_64
|
||||
HAVING COUNT(*) >= 5
|
||||
ORDER BY avg_rating DESC, AVG(p.adr) DESC, COUNT(*) DESC
|
||||
LIMIT 1
|
||||
""",
|
||||
roster_ids + [medal[0]],
|
||||
).fetchone()
|
||||
finally:
|
||||
l3.close()
|
||||
l2.close()
|
||||
web.close()
|
||||
self.assertEqual(medal[1], expected[0])
|
||||
self.assertAlmostEqual(medal[2], expected[1], places=8)
|
||||
|
||||
def test_seasons_only_include_team_matches(self):
|
||||
l2 = sqlite3.connect(Config.DB_L2_PATH)
|
||||
l3 = sqlite3.connect(Config.DB_L3_PATH)
|
||||
|
||||
Reference in New Issue
Block a user