metis-desk/scripts/application-coincidences-ui-audit.py

93 lines
11 KiB
Python
Raw Normal View History

2026-10-24 23:19:17 +00:00
"""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)