forked from luopingyi/xc_agent_platform_demo
live: all-tasks foothold path (ALL_TASKS_FOOTHOLD, default off)
Wire onlyMine=false pass/fail/running slices into candidate merge; exclude waiting; demote queue-only pending text; keep onlyMine queue gate.
This commit is contained in:
125
tests/test_all_tasks_foothold.py
Normal file
125
tests/test_all_tasks_foothold.py
Normal file
@@ -0,0 +1,125 @@
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import unittest
|
||||
from unittest.mock import Mock, patch
|
||||
from urllib.parse import parse_qs, urlparse
|
||||
from src.all_tasks_foothold import (load_all_tasks_foothold, classify_api_record,
|
||||
build_candidates, merge_candidates, candidate_priority, TARGET_GPUS, FootholdSnapshot)
|
||||
from src import live_queue
|
||||
|
||||
|
||||
def record(status='success', result=1, model='org/model', gpu='壁砺', **extra):
|
||||
return dict(modelId=model, gpuType=gpu, status=status, verifyResult=result, **extra)
|
||||
|
||||
|
||||
def response(records=(), total=None, **body):
|
||||
result = Mock(status=200)
|
||||
result.read.return_value = json.dumps(body or dict(code=0, data=dict(records=list(records), total=len(records) if total is None else total))).encode()
|
||||
result.__enter__ = Mock(return_value=result)
|
||||
result.__exit__ = Mock(return_value=False)
|
||||
return result
|
||||
|
||||
|
||||
class TestFoothold(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.env = patch.dict(os.environ, {}, clear=True)
|
||||
self.env.start()
|
||||
self.addCleanup(self.env.stop)
|
||||
self.network = patch('urllib.request.urlopen', side_effect=AssertionError('network forbidden'))
|
||||
self.network.start()
|
||||
self.addCleanup(self.network.stop)
|
||||
|
||||
def load(self, responses, **kw):
|
||||
opener = Mock(side_effect=responses)
|
||||
snapshot = load_all_tasks_foothold(opener=opener, token_resolver=lambda: 'offline-credential', **kw)
|
||||
return snapshot, opener
|
||||
|
||||
def test_whitelist_rejects_waiting_conflicts_and_bad_types(self):
|
||||
for status, result, bucket in [('waiting', 1, 'pass'), ('failed', -1, 'fail'),
|
||||
('running', 1, 'running'), ('success', True, 'pass'),
|
||||
('success', {}, 'pass'), ('other', 1, 'pass'), ('success', 1.0, 'pass')]:
|
||||
self.assertIsNone(classify_api_record(record(status, result), bucket))
|
||||
for result in (1, '1'):
|
||||
self.assertIsNotNone(classify_api_record(record(result=result), 'pass'))
|
||||
for result in (None, 0, '0'):
|
||||
self.assertIsNotNone(classify_api_record(record(' RUNNING ', result), 'running'))
|
||||
for fields in (dict(modelId=True), dict(gpuType=''), dict(modelId='')):
|
||||
self.assertIsNone(classify_api_record(dict(record(), **fields), 'pass'))
|
||||
|
||||
def test_scope_buckets_aliases_order_and_queue_isolation(self):
|
||||
before = live_queue.last_stats()
|
||||
snap, opener = self.load([response([record()]), response([record(result=-1, model='org/fail')]),
|
||||
response([record('running', None, model='org/run')])])
|
||||
self.assertEqual(snap.reason, 'ok')
|
||||
self.assertEqual([r['foothold_class'] for r in snap.candidates], ['verified_reuse', 'failed_foothold', 'in_flight'])
|
||||
self.assertEqual(snap.candidates[0]['try_next_gpus'], list(TARGET_GPUS[1:]))
|
||||
for call in opener.call_args_list:
|
||||
query = parse_qs(urlparse(call.args[0].full_url).query)
|
||||
self.assertEqual(query['onlyMine'], ['false'])
|
||||
self.assertNotIn('waiting', query.get('status', []))
|
||||
self.assertLessEqual(call.kwargs['timeout'], 5)
|
||||
self.assertEqual(before, live_queue.last_stats())
|
||||
self.assertEqual(len(opener.call_args_list), 3)
|
||||
|
||||
def test_any_bucket_error_discards_whole_snapshot(self):
|
||||
for bad in (response([record('waiting', 1)]), response(data=None, code=0),
|
||||
response(code=403), OSError('private-response')):
|
||||
snap, _ = self.load([response([record()]), bad])
|
||||
self.assertNotEqual(snap.reason, 'ok')
|
||||
self.assertEqual(snap.candidates, [])
|
||||
snap, _ = self.load([response(), response(), response()])
|
||||
self.assertEqual(snap.reason, 'ok')
|
||||
self.assertEqual(snap.candidates, [])
|
||||
|
||||
def test_hard_caps_total_drift_and_invalid_env(self):
|
||||
with patch.dict(os.environ, {'ALL_TASKS_MAX_PAGES_PASS': '999', 'ALL_TASKS_MAX_PAGES_FAIL': 'bad',
|
||||
'ALL_TASKS_MAX_PAGES_RUNNING': '-1', 'ALL_TASKS_PAGE_SIZE': '999'}):
|
||||
pages = [response([record(model='org/p' + str(n))], total=1000+n) for n in range(3)]
|
||||
pages += [response([record(result=-1, model='org/f' + str(n))], total=1000+n) for n in range(3)]
|
||||
pages += [response([record('running', None, model='org/r' + str(n))], total=1000+n) for n in range(2)]
|
||||
snap, opener = self.load(pages)
|
||||
self.assertEqual(opener.call_count, 8)
|
||||
self.assertTrue(all(b['truncated'] for b in snap.buckets.values()))
|
||||
self.assertTrue(all('pageSize=100' in c.args[0].full_url for c in opener.call_args_list))
|
||||
snap, opener = self.load([], clock=Mock(side_effect=[0, 21]))
|
||||
self.assertEqual(snap.reason, 'deadline')
|
||||
opener.assert_not_called()
|
||||
|
||||
def test_repeated_tasks_pages_and_conflicts(self):
|
||||
r = record(taskId=1)
|
||||
snap, opener = self.load([response([r], total=999), response([r], total=1000), response(), response()])
|
||||
self.assertEqual(snap.buckets['pass']['unique'], 1)
|
||||
self.assertTrue(snap.buckets['pass']['truncated'])
|
||||
self.assertEqual(opener.call_count, 4)
|
||||
snap, _ = self.load([response([r]), response([record(result=-1, taskId=1)])])
|
||||
self.assertEqual(snap.reason, 'conflicting_task')
|
||||
self.assertEqual(snap.candidates, [])
|
||||
|
||||
def test_missing_token_never_opens(self):
|
||||
opener = Mock()
|
||||
snap = load_all_tasks_foothold(opener=opener, token_resolver=Mock(side_effect=ValueError()))
|
||||
self.assertEqual(snap.reason, 'missing/invalid_token')
|
||||
opener.assert_not_called()
|
||||
|
||||
def test_merge_constraints_empty_targets_and_uniform_priority(self):
|
||||
base = [dict(model_address='https://huggingface.co/ORG/Model/', framework='custom',
|
||||
skip_reason='manual', section='B', foothold_class='verified_reuse',
|
||||
try_next_gpus=['壁砺'], targetGpu='天垓150'),
|
||||
dict(model_address='org/static', section='A', foothold_class='verified_reuse', try_next_gpus=['天垓150'])]
|
||||
dynamic = build_candidates([('org/model', 'Biren_166m', 'in_flight'), ('org/run', 'MetaX_c-500', 'in_flight')])
|
||||
merged = merge_candidates(base, dynamic)
|
||||
row = next(r for r in merged if 'huggingface' in r['model_address'])
|
||||
self.assertEqual(row['framework'], 'custom')
|
||||
self.assertEqual(row['skip_reason'], 'manual')
|
||||
self.assertEqual(row['try_next_gpus'], [])
|
||||
self.assertEqual(row['foothold_class'], 'verified_reuse')
|
||||
self.assertEqual(len(merged), 3)
|
||||
self.assertEqual(merged[-1]['model_address'], 'org/run')
|
||||
self.assertEqual(merged[-1]['try_next_gpus'], list(TARGET_GPUS))
|
||||
self.assertEqual(candidate_priority(dict(foothold_class='in_flight'))[0], 3)
|
||||
|
||||
def test_pending_queue_only_text(self):
|
||||
from src.pending_pool import classify_foothold
|
||||
self.assertEqual(classify_foothold('验证排队中'), 'queued_only')
|
||||
self.assertEqual(classify_foothold('验证排队中; 验证通过'), 'verified_reuse')
|
||||
@@ -215,3 +215,63 @@ class TestLiveCycle(unittest.TestCase):
|
||||
def test_injected_queue_skips_stats_network(self):
|
||||
self.cycle()
|
||||
self.assertEqual(self.logs.getvalue().count('[live] stats unavailable reason=not_read'), 1)
|
||||
|
||||
def test_foothold_explicit_disabled_and_injected_queue_stay_offline(self):
|
||||
from src.all_tasks_foothold import FootholdSnapshot
|
||||
dynamic = Mock(return_value=FootholdSnapshot())
|
||||
with patch.dict(os.environ, {'ALL_TASKS_FOOTHOLD': '1'}):
|
||||
run_cycle([candidate('explicit')], self.client, lambda: (0, 'ok', set()), foothold_reader=dynamic)
|
||||
dynamic.assert_not_called()
|
||||
with patch('src.live_cycle.build_ordered_candidates', return_value=[candidate('base')]), patch.dict(os.environ, {'PENDING_SUBMIT_MD': 'offline'}):
|
||||
run_cycle(None, self.client, lambda: (0, 'ok', set()), foothold_reader=dynamic)
|
||||
with patch.dict(os.environ, {'ALL_TASKS_FOOTHOLD': '1'}):
|
||||
run_cycle(None, self.client, lambda: (0, 'ok', set()))
|
||||
dynamic.assert_not_called()
|
||||
|
||||
def test_foothold_merge_order_constraints_pending_and_fallback(self):
|
||||
from src.all_tasks_foothold import FootholdSnapshot, build_candidates
|
||||
base = [candidate('base', 'A', 'verified_reuse')]
|
||||
snapshot = FootholdSnapshot(build_candidates([('org/dynamic', 'Biren_166m', 'in_flight')]))
|
||||
with patch('src.live_cycle.build_ordered_candidates', return_value=base) as pending, patch.dict(os.environ, {'PENDING_SUBMIT_MD': 'offline', 'PENDING_SECTION': 'A', 'ALL_TASKS_FOOTHOLD': '1'}):
|
||||
run_cycle(None, self.client, lambda: (0, 'ok', set()), foothold_reader=lambda: snapshot)
|
||||
pending.assert_called_once_with()
|
||||
self.assertEqual([c.args[0]['modelAddress'] for c in self.client.submit.call_args_list], ['org/base', 'org/dynamic'])
|
||||
_submitted.clear()
|
||||
self.client.reset_mock()
|
||||
run_cycle(None, self.client, lambda: (0, 'ok', set()), foothold_reader=Mock(side_effect=OSError('private-response')))
|
||||
self.assertEqual(self.client.submit.call_args.args[0]['modelAddress'], 'org/base')
|
||||
self.assertIn('[foothold] reason=reader_error', self.logs.getvalue())
|
||||
self.assertNotIn('private-response', self.logs.getvalue())
|
||||
|
||||
def test_foothold_empty_intersection_never_revives_target(self):
|
||||
from src.all_tasks_foothold import FootholdSnapshot, build_candidates
|
||||
base = [dict(candidate('empty'), targetGpu='曦望')]
|
||||
snapshot = FootholdSnapshot(build_candidates([('org/empty', 'Biren_166m', 'verified_reuse')]))
|
||||
with patch('src.live_cycle.build_ordered_candidates', return_value=base), patch.dict(os.environ, {'PENDING_SUBMIT_MD': 'offline', 'ALL_TASKS_FOOTHOLD': '1'}):
|
||||
decisions = run_cycle(None, self.client, lambda: (0, 'ok', set()), foothold_reader=lambda: snapshot)
|
||||
self.client.submit.assert_not_called()
|
||||
self.assertEqual(decisions[-1]['reason'], 'all_gpus_done_or_unmapped')
|
||||
|
||||
def test_dynamic_still_respects_queue_marks_ledger_precheck(self):
|
||||
from src.all_tasks_foothold import FootholdSnapshot
|
||||
snapshot = FootholdSnapshot([dict(candidate('guarded'), evidence_source='all-tasks', foothold_class='verified_reuse')])
|
||||
with patch('src.live_cycle.build_ordered_candidates', return_value=[]), patch.dict(os.environ, {'PENDING_SUBMIT_MD': 'offline', 'ALL_TASKS_FOOTHOLD': '1'}):
|
||||
for count in (95, None):
|
||||
decisions = run_cycle(None, self.client, lambda: (count, 'ok', set()), foothold_reader=lambda: snapshot)
|
||||
self.assertEqual(decisions[-1]['action'], 'hold')
|
||||
with patch('src.live_cycle.load_baked', return_value=({('org/guarded', 'Biren_166m')}, set())):
|
||||
run_cycle(None, self.client, lambda: (0, 'ok', set()), foothold_reader=lambda: snapshot)
|
||||
_marked[('org/guarded', 'Biren_166m')] = 60028
|
||||
run_cycle(None, self.client, lambda: (0, 'ok', set()), foothold_reader=lambda: snapshot)
|
||||
_marked.clear()
|
||||
with patch.dict(os.environ, {'LIVE_PRECHECK': '1'}):
|
||||
run_cycle(None, self.client, lambda: (0, 'ok', set()), foothold_reader=lambda: snapshot,
|
||||
precheck=lambda _: ('ok', {'Biren_166m': 60027}))
|
||||
self.client.submit.assert_not_called()
|
||||
|
||||
def test_pending_section_b_does_not_gain_dynamic_section_a(self):
|
||||
from src.all_tasks_foothold import FootholdSnapshot
|
||||
dynamic = FootholdSnapshot([candidate('dynamic', 'A', 'verified_reuse')])
|
||||
with patch('src.live_cycle.build_ordered_candidates', return_value=[candidate('base')]), patch.dict(os.environ, {'PENDING_SUBMIT_MD': 'offline', 'PENDING_SECTION': 'B', 'ALL_TASKS_FOOTHOLD': '1'}):
|
||||
run_cycle(None, self.client, lambda: (0, 'ok', set()), foothold_reader=lambda: dynamic)
|
||||
self.assertEqual([c.args[0]['modelAddress'] for c in self.client.submit.call_args_list], ['org/base'])
|
||||
|
||||
@@ -59,7 +59,7 @@ class TestPendingPool(unittest.TestCase):
|
||||
'org/fail', 'org/queue', 'org/progress', 'org/b'])
|
||||
self.assertEqual([r['foothold_class'] for r in rows],
|
||||
['verified_reuse', 'verified_reuse', 'unknown', 'in_flight',
|
||||
'failed_foothold', 'in_flight', 'in_flight', 'unknown'])
|
||||
'failed_foothold', 'queued_only', 'in_flight', 'unknown'])
|
||||
for row in rows:
|
||||
self.assertEqual(row['evidence_source'], 'GAP' if row['foothold_class'] == 'unknown' else 'pending-foothold')
|
||||
self.assertEqual(rows[0]['foothold_text'], '他人:已验证成功; 正在验证')
|
||||
|
||||
Reference in New Issue
Block a user