93 lines
11 KiB
Python
93 lines
11 KiB
Python
|
|
"""R2.6: revisión contextual y coexistencia con datos sintéticos, sin adopción ni escrituras de detección."""
|
||
|
|
import json
|
||
|
|
import os
|
||
|
|
import secrets
|
||
|
|
import signal
|
||
|
|
import time
|
||
|
|
import uuid
|
||
|
|
|
||
|
|
|
||
|
|
def creation_id():
|
||
|
|
return str(uuid.UUID(f'{int(time.time()*1000):012x}'+'7'+secrets.token_hex(2)[1:]+'a'+secrets.token_hex(4)[1:]+secrets.token_hex(4)))
|
||
|
|
|
||
|
|
|
||
|
|
def verify(context,fixture,password):
|
||
|
|
api=context['api'];api.resource_contract='2'
|
||
|
|
api.request('/auth/login',{'email':'setup@audit.example.test','password':password})
|
||
|
|
company=fixture['company'];asset=fixture['softwareAssets'][0];profile=fixture['configurationProfiles'][0]
|
||
|
|
template=api.request('/resource-global-applications/template')['specification']['templateId']
|
||
|
|
provider=next(item for item in api.request('/resource-catalog/'+company)['items'] if item.get('builtinKey')=='field:provider')['id']
|
||
|
|
name='Chrome Ágil R2.6 — Aplicación de pruebas con nombre comercial extenso'
|
||
|
|
source=api.request('/resources/'+company,{'values':{'name':name,'templateId':template,'active':True,'notes':'Referencia particular conservada','characteristics':{provider:'Fábrica sintética'}}},expected=201)['resource']
|
||
|
|
empty=api.request('/resources/'+company,{'values':{'name':'Sin coincidencias sintéticas R2.6','templateId':template,'active':True,'notes':'','characteristics':{}}},expected=201)['resource']
|
||
|
|
for index in range(60):
|
||
|
|
api.request('/resource-global-applications',{'creationId':creation_id(),'values':{'name':(name.upper() if index==1 else name) if index<50 else name+' Pro','templateId':template,'active':index%5!=0,'notes':'Nota global privada','characteristics':{provider:'Fábrica sintética'} if index%3 else {}}},expected=201)
|
||
|
|
software=api.request('/software-installations/'+company,{'values':{'assetId':asset,'resourceId':source['id'],'licenseResourceId':None,'version':'LOCAL-126','state':'configured','notes':'Conservar UUID local'}},expected=201)['configuration']
|
||
|
|
configuration=api.request('/application-configurations/'+company,{'values':{'profileId':profile,'resourceId':source['id'],'delivery':'installed','version':'PERFIL-LOCAL-126','state':'pending','notes':'Conservar UUID local'}},expected=201)['configuration']
|
||
|
|
api.request('/auth/logout',{})
|
||
|
|
tool,browser,capture=context['tool'],context['browser'],context['capture']
|
||
|
|
directory,origin=context['directory'],context['origin'];screenshots=[];checks=[]
|
||
|
|
resource_url=origin+f'/companies/{company}/resources/{source["id"]}'
|
||
|
|
action='Buscar coincidencias en el catálogo global'
|
||
|
|
|
||
|
|
def code(client,source_code):
|
||
|
|
print(' R2.6 MCP: '+source_code[:105],flush=True)
|
||
|
|
try:return tool(client,'browser_run_code_unsafe',{'code':'async (page) => { '+source_code+' }'})
|
||
|
|
except RuntimeError:
|
||
|
|
capture(client,directory,'r26-failure');raise
|
||
|
|
|
||
|
|
with browser(context['config']) as client:
|
||
|
|
tool(client,'browser_resize',{'width':1440,'height':1000});tool(client,'browser_navigate',{'url':origin+'/login'})
|
||
|
|
code(client,"await page.getByRole('button',{name:'Entrar',exact:true}).waitFor();")
|
||
|
|
tool(client,'browser_fill_form',{'fields':[{'name':'Correo electrónico','type':'textbox','target':'input[type=email]','value':fixture['email']},{'name':'Contraseña','type':'textbox','target':'input[type=password]','value':fixture['password']}]})
|
||
|
|
tool(client,'browser_click',{'target':'button[type=submit]'});code(client,"await page.locator('[data-slot=workspace]').waitFor({timeout:30000});")
|
||
|
|
tool(client,'browser_navigate',{'url':resource_url})
|
||
|
|
code(client,"await page.getByRole('button',{name:'"+action+"',exact:true}).waitFor(); await page.getByRole('button',{name:'"+action+"',exact:true}).click(); await page.getByRole('dialog',{name:'Posibles coincidencias',exact:true}).getByRole('link',{name:'Abrir ficha global',exact:true}).first().waitFor(); await page.getByText('Global inactivo: no elegible para nuevas asociaciones.',{exact:true}).first().waitFor();")
|
||
|
|
screenshots.append(capture(client,directory,'r26-01-matches'))
|
||
|
|
checks.extend(['ordinary-login','contextual-source','explained-name-provider-and-missing-data','inactive-readable','no-equivalence-claim','global-link'])
|
||
|
|
code(client,"page.r26First=await page.getByRole('dialog',{name:'Posibles coincidencias',exact:true}).getByRole('link',{name:'Abrir ficha global',exact:true}).first().getAttribute('href'); await page.getByRole('dialog',{name:'Posibles coincidencias',exact:true}).getByRole('button',{name:'Siguiente',exact:true}).click(); await page.waitForFunction(previous=>Array.from(document.querySelectorAll('a')).find(a=>a.textContent==='Abrir ficha global')?.getAttribute('href')!==previous,page.r26First); await page.keyboard.press('Escape'); if(!(await page.evaluate(()=>document.activeElement?.textContent)).includes('Buscar coincidencias'))throw Error('Foco perdido');")
|
||
|
|
checks.extend(['cursor-pagination','keyboard-escape','focus-return'])
|
||
|
|
code(client,"page.r26Pattern='**/api/resource-application-coincidences/**'; await page.route(page.r26Pattern,r=>r.fulfill({status:500,contentType:'application/json',json:{code:'INTERNAL_ERROR'}})); await page.getByRole('button',{name:'"+action+"',exact:true}).click(); await page.getByRole('dialog',{name:'Posibles coincidencias',exact:true}).getByRole('alert').waitFor();")
|
||
|
|
screenshots.append(capture(client,directory,'r26-02-error'))
|
||
|
|
code(client,"await page.unroute(page.r26Pattern); await page.getByRole('dialog',{name:'Posibles coincidencias',exact:true}).getByRole('button',{name:'Reintentar',exact:true}).click(); await page.getByRole('link',{name:'Abrir ficha global',exact:true}).first().waitFor();")
|
||
|
|
checks.extend(['error-keeps-local-source','retry'])
|
||
|
|
for width in [1600,1440,1280,1024,768,390]:
|
||
|
|
tool(client,'browser_resize',{'width':width,'height':1000 if width>768 else 844})
|
||
|
|
code(client,"await page.getByRole('dialog',{name:'Posibles coincidencias',exact:true}).waitFor(); if(await page.evaluate(()=>document.documentElement.scrollWidth>innerWidth+1))throw Error('Desbordamiento global');")
|
||
|
|
screenshots.append(capture(client,directory,f'r26-dialog-{width}'))
|
||
|
|
code(client,"await page.keyboard.press('Escape');")
|
||
|
|
tool(client,'browser_resize',{'width':1440,'height':1000})
|
||
|
|
tool(client,'browser_navigate',{'url':resource_url})
|
||
|
|
code(client,"await page.getByRole('button',{name:'Consultar uso en esta compañía',exact:true}).click(); await page.getByRole('link').filter({hasText:'LOCAL-126'}).first().waitFor();")
|
||
|
|
screenshots.append(capture(client,directory,'r26-03-local-use'))
|
||
|
|
checks.extend(['six-viewports','local-software-use','local-profile-use'])
|
||
|
|
tool(client,'browser_navigate',{'url':origin+f'/companies/{company}/resources/{empty["id"]}'})
|
||
|
|
code(client,"await page.getByRole('button',{name:'"+action+"',exact:true}).click(); await page.getByText('No se han encontrado posibles coincidencias en el catálogo global con estos criterios. Puedes cambiar el filtro.',{exact:true}).waitFor();")
|
||
|
|
screenshots.append(capture(client,directory,'r26-04-empty'));checks.append('empty-not-partial')
|
||
|
|
code(client,"await page.keyboard.press('Escape');")
|
||
|
|
tool(client,'browser_navigate',{'url':origin+f'/companies/{company}/assets/{asset}'})
|
||
|
|
code(client,"await page.getByRole('button',{name:'Añadir instalación',exact:true}).waitFor(); await page.getByRole('button',{name:'Añadir instalación',exact:true}).click(); await page.getByRole('button',{name:'Software',exact:true}).click(); await page.getByRole('textbox',{name:'Buscar por nombre o proveedor',exact:true}).fill('Chrome Ágil R2.6'); await page.getByRole('cell',{name:'De la compañía · Fábrica sintética',exact:true}).waitFor(); await page.getByRole('cell').filter({hasText:'Global ·'}).first().waitFor(); if(await page.getByRole('radio',{checked:true}).count())throw Error('Selección automática');")
|
||
|
|
screenshots.append(capture(client,directory,'r26-05-software-homonyms'));checks.extend(['software-homonyms-distinct','no-auto-selection'])
|
||
|
|
code(client,"await page.keyboard.press('Escape'); await page.keyboard.press('Escape');")
|
||
|
|
tool(client,'browser_navigate',{'url':origin+f'/companies/{company}/work-profiles/{profile}/application-configurations'})
|
||
|
|
code(client,"await page.getByRole('button',{name:'Configurar aplicación',exact:true}).click(); await page.getByRole('button',{name:'Aplicación',exact:true}).click(); await page.getByRole('textbox',{name:'Buscar por nombre o proveedor',exact:true}).fill('Chrome Ágil R2.6'); await page.getByRole('cell',{name:'De la compañía · Fábrica sintética',exact:true}).waitFor(); await page.getByRole('cell').filter({hasText:'Global ·'}).first().waitFor();")
|
||
|
|
screenshots.append(capture(client,directory,'r26-06-profile-homonyms'));checks.append('profile-homonyms-distinct')
|
||
|
|
code(client,"await page.keyboard.press('Escape'); await page.keyboard.press('Escape');")
|
||
|
|
api.request('/auth/login',{'email':'setup@audit.example.test','password':password})
|
||
|
|
retained_software=next(item for item in api.request('/software-installations/'+company)['configurations'] if item['id']==software['id'])
|
||
|
|
retained_configuration=next(item for item in api.request('/application-configurations/'+company)['configurations'] if item['id']==configuration['id'])
|
||
|
|
if retained_software!=software or retained_configuration!=configuration:raise RuntimeError('La revisión modificó referencias operativas o historial.')
|
||
|
|
api.request('/auth/logout',{});checks.extend(['software-uuid-history-preserved','profile-uuid-history-preserved'])
|
||
|
|
with browser(context['config']) as anonymous:
|
||
|
|
tool(anonymous,'browser_navigate',{'url':resource_url});code(anonymous,"await page.getByRole('button',{name:'Entrar',exact:true}).waitFor();")
|
||
|
|
checks.append('anonymous-login-required')
|
||
|
|
backend=context['backend'];os.killpg(backend.pid,signal.SIGTERM);backend.wait(timeout=10)
|
||
|
|
closed={**context['active_environment'],'RESOURCE_GLOBAL_CATALOG_ENABLED':'false','RESOURCE_GLOBAL_SOFTWARE_ENABLED':'false','RESOURCE_GLOBAL_PROFILE_ENABLED':'false'}
|
||
|
|
with context['child_process'](['node','--import','tsx','apps/server/src/index.ts'],closed,directory/'api-coincidences-closed.log') as restarted:
|
||
|
|
context['wait_for'](api.ready,restarted);tool(client,'browser_navigate',{'url':resource_url})
|
||
|
|
code(client,"await page.getByRole('button',{name:'Consultar uso en esta compañía',exact:true}).waitFor(); if(await page.getByRole('button',{name:'"+action+"',exact:true}).count())throw Error('Detección abierta con catálogo cerrado');")
|
||
|
|
checks.append('catalog-closed-action-absent')
|
||
|
|
report={'checks':checks,'screenshots':screenshots,'companies':15,'principal':{'people':50,'assets':51,'profiles':50,'workstations':50,'localResourcesAtLeast':50},'globals':60,'credentialsSaved':False,'database':'metis_desk_test'}
|
||
|
|
(directory/'r26-report.json').write_text(json.dumps(report,ensure_ascii=False,indent=2)+'\n')
|
||
|
|
print('R2.6 recorrido verificado. Evidencia: '+str(directory.relative_to(context['ROOT'])),flush=True)
|