agentIdentity.test.ts 3.3 KB

1234567891011121314151617181920212223242526272829303132333435363738
  1. import {describe,expect,it} from 'vitest'
  2. import {AGENT_MASCOT_SHAPES,AGENT_MASCOT_BODIES,AGENT_MASCOT_COLORS,AGENT_MASCOT_EXPRESSIONS,AGENT_IMAGE_CROPS,agentIdentityFromProfile,agentIdentityMetadata,defaultAgentIdentity,decodeAgentAvatar,encodeAgentAvatar,normalizeAvatar,YAOYAO_AGENT_IDENTITY_NAMESPACE} from '../../src/shared/agentIdentity'
  3. const photo='data:image/png;base64,aGVsbG8='
  4. describe('avatar v2',()=>{
  5. it('resets legacy internal appearance while retaining the configured name',()=>{
  6. const identity=agentIdentityFromProfile({name:'bot',ui_meta:{[YAOYAO_AGENT_IDENTITY_NAMESPACE]:{version:1,display_name:'瑶儿',avatar_mode:'mascot',shape:'triangle',color:'#ff0000',expression:'curious'}}})
  7. expect(identity).toMatchObject({displayName:'瑶儿',shape:'circle',color:'#00c875',expression:'idle',bodyId:null})
  8. expect(decodeAgentAvatar('yaoyao-mascot:v1:triangle:ff0000:curious')).toMatchObject({shape:'circle',color:'#00c875',expression:'idle'})
  9. })
  10. it('retains raw photo bytes and uses the prior rounded crop',()=>{
  11. const decoded=decodeAgentAvatar(normalizeAvatar(photo))
  12. expect(decoded).toMatchObject({avatarMode:'image',imageDataURL:photo,imageCrop:'rounded'})
  13. expect(agentIdentityFromProfile({name:'bot',ui_meta:{[YAOYAO_AGENT_IDENTITY_NAMESPACE]:{version:1,avatar_mode:'image',image_data_url:photo}}})).toMatchObject({avatarMode:'image',imageDataURL:photo})
  14. })
  15. it('retains hidden photos when resetting the old mascot',()=>{
  16. expect(agentIdentityFromProfile({name:'bot',ui_meta:{[YAOYAO_AGENT_IDENTITY_NAMESPACE]:{version:1,avatar_mode:'mascot',image_data_url:photo}}})).toMatchObject({avatarMode:'mascot',imageDataURL:photo,color:'#00c875'})
  17. })
  18. it('round trips every supported picker value and metadata without resetting v2',()=>{
  19. expect([AGENT_MASCOT_COLORS.length,AGENT_MASCOT_SHAPES.length,AGENT_MASCOT_BODIES.length,AGENT_MASCOT_EXPRESSIONS.length]).toEqual([11,8,10,10])
  20. for(const shape of AGENT_MASCOT_SHAPES) for(const bodyId of [null,...AGENT_MASCOT_BODIES]) {
  21. const identity={...defaultAgentIdentity('bot'),shape,bodyId,color:'#1488ff' as const,expression:'proud' as const}
  22. const encoded=encodeAgentAvatar(identity)
  23. expect(normalizeAvatar(encoded)).toBe(encoded)
  24. expect(decodeAgentAvatar(encoded)).toMatchObject({shape,bodyId,color:'#1488ff',expression:'proud'})
  25. expect(agentIdentityFromProfile({name:'bot',ui_meta:{[YAOYAO_AGENT_IDENTITY_NAMESPACE]:agentIdentityMetadata(identity)}})).toEqual(identity)
  26. }
  27. for(const imageCrop of AGENT_IMAGE_CROPS) expect(decodeAgentAvatar(encodeAgentAvatar({...defaultAgentIdentity('bot'),avatarMode:'image',imageDataURL:photo,imageCrop}))).toMatchObject({imageDataURL:photo,imageCrop})
  28. })
  29. it('uses the same green circle for all defaults, regardless of name or role',()=>{
  30. for(const name of ['samien','developer','研究员']) expect(defaultAgentIdentity(name)).toMatchObject({shape:'circle',color:'#00c875',expression:'idle'})
  31. })
  32. it('rejects untrusted geometry, invalid crops and external image URLs',()=>{
  33. const base={...defaultAgentIdentity('bot')}
  34. for(const patch of [{shape:'<script>'},{bodyId:'untrusted'},{color:'url(https://x)'},{imageCrop:'blob'},{avatarMode:'image',imageDataURL:'https://external.test/avatar.png'}]) {
  35. expect(decodeAgentAvatar('yaoyao-avatar:v2:'+JSON.stringify({...base,...patch}))).toBeNull()
  36. }
  37. })
  38. })