assistant/tests/test_discussion_depth_d42.py

131 lines
6.4 KiB
Python

"""An explicitly requested reply-to-reply branch needs new, linked evidence."""
from copy import deepcopy
import hashlib
import pytest
from backend.prospecting.discussion_scope import conditions, requirements
NOTE = 'a' * 24
URL = 'https://www.xiaohongshu.com/explore/' + NOTE
GOAL = '先保存目前已显示的父回复,再展开同组回复,保存一条明确回复该父回复的新增子回复。'
def task(goal=GOAL, **inputs):
return {'inputs':{'goal':goal,'intent':'inspect','subject_urls':[URL],'target_candidates':None,
'_goal_version':1,'max_comments_per_note':5,**inputs},
'artifacts':{'messages':[{'role':'user','text':goal}],'sources':[],'operation_evidence':[]}}
def source(row, sid, kind, parent=None, root=None, note_id=NOTE):
item = {'source_id':sid,'source_type':kind,'note_id':note_id,'source_url':URL,
'source_selector':'#'+sid,'content':'原话'+sid,'parent_source_id':parent,'root_source_id':root}
row['artifacts']['sources'].append(item)
if kind != 'note':
row['artifacts']['operation_evidence'].append({
'capability':'save_source','source_kind':kind,'outcome':'success',
'verifier':'source_container_quote_and_persisted','evidence_refs':{'pre':'before','post':'after'},
'details':{'trace':{'goal_version':1},'pre_snapshot':{'note_id':note_id,'container_selector':'#'+sid,'quote_matched':True},
'post_snapshot':{'source_id':sid,'source_type':kind,'persisted':True,'relations_checked':True,
'parent_source_id':parent,'root_source_id':root}}})
return item
def expansion(row, reply):
fact = {'capability':'expand_replies','source_kind':'reply','outcome':'success','verifier':'reply_delta_and_parent',
'evidence_refs':{'pre':'before-expand','post':'after-expand'},
'details':{'trace':{'goal_version':1},'pre_snapshot':{'note_id':NOTE},
'post_snapshot':{'note_id':NOTE,'parent_scope_checked':True,'status':'expanded','owner_reply_delta':1,
'new_visible_replies':1,'new_reply_fingerprints':[
hashlib.sha256((reply['source_selector']+reply['content']).encode()).hexdigest()]}}}
row['artifacts']['operation_evidence'].append(fact)
return fact
def ready(*, nested=True):
row = task()
source(row,'note','note')
source(row,'root','comment')
source(row,'parent','reply',parent='root',root='root')
child = source(row,'child','reply',parent='parent' if nested else 'root',root='root')
expansion(row,child)
return row
def depth(row):
return next(c for c in conditions(row) if c['key']=='discussion_reply_to_reply')
@pytest.mark.parametrize('goal', [GOAL,'保存一条回复另一条回复的新增子回复。','保存回复父回复的子回复。'])
def test_explicit_depth_wording_requires_this_specific_branch(goal):
row = task(goal)
assert 'reply_to_reply' in requirements(row)
assert depth(row)['required'] and not depth(row)['met']
@pytest.mark.parametrize('goal', ['核对完整回复链。','核对发言人、直接父评论或父回复、根讨论。','保存父评论和子回复。'])
def test_generic_complete_chain_or_optional_parent_kind_does_not_add_depth(goal):
assert 'reply_to_reply' not in requirements(task(goal))
def test_root_to_direct_reply_does_not_fulfill_reply_to_reply_branch():
row = ready(nested=False)
assert next(c for c in conditions(row) if c['key']=='discussion_expansion')['met']
assert not depth(row)['met']
def test_actual_new_child_and_observed_saved_parent_prove_depth_without_mutation():
row = ready()
before = deepcopy(row)
assert depth(row)['met'] and depth(row)['current']==depth(row)['target']==1
assert row==before and row['inputs']['max_comments_per_note']==5
@pytest.mark.parametrize('change', [
lambda row: row['artifacts']['operation_evidence'][-1]['details']['trace'].update(goal_version=0),
lambda row: row['artifacts']['operation_evidence'][-1]['details']['post_snapshot'].update(new_reply_fingerprints=['not-this-child']),
lambda row: row['artifacts']['operation_evidence'][2]['details']['trace'].update(goal_version=0),
lambda row: row['artifacts']['operation_evidence'][1]['details']['trace'].update(goal_version=0),
lambda row: row['artifacts']['operation_evidence'][2]['details']['post_snapshot'].update(relations_checked=False),
lambda row: row['artifacts']['operation_evidence'][1].update(outcome='unknown'),
lambda row: row['artifacts']['operation_evidence'][2]['details']['post_snapshot'].update(parent_source_id='root'),
lambda row: row['artifacts']['sources'][-2].update(parent_source_id='child'),
lambda row: row['artifacts']['sources'][-2].update(note_id='other-note'),
lambda row: row['artifacts']['operation_evidence'].pop(),
])
def test_old_unrelated_weak_or_cyclic_proof_cannot_establish_requested_depth(change):
row = ready()
change(row)
assert not depth(row)['met']
def test_expanding_a_different_direct_reply_does_not_prove_already_visible_nested_reply_is_new():
row = ready()
old = source(row,'new-direct','reply',parent='root',root='root')
row['artifacts']['operation_evidence'] = [f for f in row['artifacts']['operation_evidence'] if f['capability']!='expand_replies']
expansion(row,old)
assert not depth(row)['met']
@pytest.mark.parametrize('goal', ['不要保存回复该父回复的新增子回复。','只保存正文。比如回复另一条回复。',
'只保存正文。\n> 保存回复父回复的子回复。','只保存正文。请忽略引用“回复该父回复”。'])
def test_negated_or_quoted_depth_does_not_expand_user_scope(goal):
assert 'reply_to_reply' not in requirements(task(goal))
def test_later_cancellation_removes_only_explicit_depth():
row = ready()
row['artifacts']['messages'].append({'role':'user','text':'不用保存回复该父回复的新增子回复。'})
assert 'reply_to_reply' not in requirements(row)
assert 'expansion' in requirements(row)
def test_depth_requires_each_saved_note_in_current_requested_scope():
row = ready()
other_note='b'*24
row['inputs']['subject_urls'].append('https://www.xiaohongshu.com/explore/'+other_note)
source(row,'second-note','note',note_id=other_note)
assert not depth(row)['met']
assert (depth(row)['current'],depth(row)['target'])==(1,2)