Files
Agent-System-Dev/.task/artifacts/project-member-runtime/acceptance_http.py
T
weichenandCursor 3785408e71
ci / test-pg (pull_request) Successful in 9m3s
ci / test (pull_request) Successful in 7m48s
ci / svc-realcombo (pull_request) Successful in 2m34s
Archive the completed project-member-runtime task card
Why: the 8788 project-member runtime delivery finished (code merged via #526/#530) but its card and operation records lived only on an unpushed task branch, leaving the worktree impossible to recycle and the record invisible on main.
Evidence: card status "已完成(运行目标)" on feature/project-member-runtime 835d807; branch diff against merge-base touches only .task (card + 29 operation records); credential scan finds only variable-based Authorization headers and sha256 digests.
Impact: adds .task/archive/feature-project-member-runtime.md and .task/artifacts/project-member-runtime/**; no code, gitlink or policy change.
Refs: worktree cleanup 2026-10-18
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-18 09:01:23 +00:00

58 lines
5.0 KiB
Python

import hashlib,json,secrets,sqlite3
from pathlib import Path
from collections import Counter
import client,prepare
W=client.W;HOST=prepare.HOST
adopt=json.loads((W/'adoption-result.json').read_text());PID=adopt['canonicalProjectId']
state_path=W/'http-fixtures.json';state=json.loads(state_path.read_text()) if state_path.exists() else {'conversations':{},'artifacts':{}}
def state_save():client.save('http-fixtures.json',state)
def ok(owner,path,method='GET',data=None,raw=False,headers=None):
code,out=client.request(owner,path,method,data,raw,headers);assert code==200,f'{owner} {method} {path} status={code}';return out
def deny(owner,path,method='GET',data=None):
code,_=client.request(owner,path,method,data);assert code in (403,404),f'{owner} {path} not denied ({code})';return code
for owner in ['weichen','test']:
me=ok(owner,'/api/me');assert me['owner']==owner
projects=ok(owner,'/api/projects');p=next(x for x in projects if x['id']==HOST)
assert p['settings']['rdm.projectId']==PID and p['settings']['rdm.state']=='ready'
if owner not in state['conversations']:
conv=ok(owner,'/api/projects/'+HOST+'/conversations','POST',{'title':'成员协作复测-'+owner+'-20261013'})
assert conv['owner']==owner and conv['projectId']==HOST
state['conversations'][owner]=conv['id'];state_save()
own_id=state['conversations'][owner]
assert ok(owner,'/api/conversations/'+own_id)['conversation']['owner']==owner
if owner not in state['artifacts']:
marker=('PROJECT_MEMBER_RUNTIME_PRIVATE_'+owner+'_20261013\n').encode()
boundary='PMR'+secrets.token_hex(16)
raw=(f'--{boundary}\r\nContent-Disposition: form-data; name="file"; filename="成员协作复测-{owner}-私有.txt"\r\nContent-Type: text/plain\r\n\r\n').encode()+marker+(f'\r\n--{boundary}--\r\n').encode()
files=ok(owner,'/api/projects/'+HOST+'/uploads','POST',raw,headers={'Content-Type':'multipart/form-data; boundary='+boundary})
state['artifacts'][owner]={'id':files['artifactId'],'sha256':hashlib.sha256(marker).hexdigest()};state_save()
a=state['artifacts'][owner]
assert hashlib.sha256(ok(owner,'/api/artifacts/'+a['id']+'/content',raw=True)).hexdigest()==a['sha256']
with prepare.db(prepare.DATA/'agent-system.db') as c:
row=c.execute('select owner,project_id from artifacts where id=?',(a['id'],)).fetchone();assert tuple(row)==(owner,HOST)
private_status={}
for viewer,other in [('weichen','test'),('test','weichen')]:
private_status[viewer]={'other_conversation':deny(viewer,'/api/conversations/'+state['conversations'][other]),'other_original':deny(viewer,'/api/artifacts/'+state['artifacts'][other]['id']+'/content')}
assert state['conversations'][other] not in [x['id'] for x in ok(viewer,'/api/projects/'+HOST+'/conversations')]
assert state['artifacts'][other]['id'] not in [x['id'] for x in ok(viewer,'/api/projects/'+HOST+'/artifacts')]
foreign='prj_b32c62943fa3';foreign_pid='p-39dd859c3c99'
assert foreign not in [x['id'] for x in ok('test','/api/projects')]
foreign_status={'conversation_list':deny('test','/api/projects/'+foreign+'/conversations'),'conversation_create':deny('test','/api/projects/'+foreign+'/conversations','POST',{'title':'cross-project denial probe'}),'artifacts':deny('test','/api/projects/'+foreign+'/artifacts')}
code,denied=client.request('test','/api/commands/rdm.inspect.summary','POST',{'params':{'projectId':foreign_pid},'entrance':'workspace'})
assert code in (400,403,404) and denied.get('receipt',{}).get('state')=='rejected';foreign_status['domain_summary']=code
code,_=client.request('test','/api/projects','POST',{'name':'genui-e2e'});assert code==403,'member unexpectedly gained system project-create authority'
code,_=client.request('weichen','/api/projects','POST',{'name':'U7宿主项目'});assert code==409,'another owner project name was not globally reserved';duplicate_status=code
before=json.loads((W/'baseline-private.json').read_text());current=prepare.logical(prepare.DATA)
backup=Path(json.loads((W/'backup-location.json').read_text())['path'])
with prepare.db(backup/'dev/data/rdm.db') as c:
moved=Counter(prepare.digest(x) for x in c.execute('select * from rdm_members where project_id=?',(HOST,)))
preserved={}
for table,b in before['logical'].items():
expected=Counter(b['rows'])
if table=='rdm.db:rdm_members':expected-=moved
assert not expected-Counter(current[table]['rows']), 'old business rows changed: '+table
preserved[table]={'old_preserved':sum(expected.values()),'current':len(current[table]['rows'])}
for a in before['artifacts']:assert prepare.sha(Path(a['content_path']))==a['sha256']
result={'source':'SELF','hostProjectId':HOST,'canonicalProjectId':PID,'account_visibility_and_private_creation':'PASS','private_peer_status':private_status,'cross_project_status':foreign_status,'global_duplicate_name_status':duplicate_status,'preserved_business_rows':preserved,'original_artifact_bytes_preserved':len(before['artifacts']),'fixtures':state,'password_login':'NOT_RUN; user selected temporary API credentials'}
client.save('acceptance-http.json',result);print(json.dumps(result,ensure_ascii=False))