Ctrl K
ComunidadGuías…

test/mate-avatar-persistence.test.js

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
var test = require('node:test');
var assert = require('node:assert/strict');
var childProcess = require('node:child_process');
var fs = require('node:fs');
var os = require('node:os');
var path = require('node:path');
var EventEmitter = require('node:events');
var attachSettings = require('../lib/server-settings').attachSettings;

function uploadAvatar(settings, mateId, bytes) {
  return new Promise(function (resolve) {
    var req = new EventEmitter();
    req.method = 'POST';
    var result = { status: 0, body: '' };
    var res = {
      writeHead: function (status) { result.status = status; },
      end: function (body) { result.body = String(body || ''); resolve(result); },
    };
    settings.handleRequest(req, res, '/api/mate-avatar/' + mateId);
    req.emit('data', bytes);
    req.emit('end');
  });
}

function getGeneratedAvatar(settings, requestUrl) {
  return new Promise(function (resolve) {
    var result = { status: 0, headers: {}, body: '' };
    var res = {
      writeHead: function (status, headers) { result.status = status; result.headers = headers || {}; },
      end: function (body) { result.body = String(body || ''); resolve(result); },
    };
    settings.handleRequest({ method: 'GET', url: requestUrl }, res, requestUrl.split('?')[0]);
  });
}

test('Mate avatar profiles persist across reload and locked artwork cannot be replaced', function (t) {
  var clayHome = fs.mkdtempSync(path.join(os.tmpdir(), 'clay-mate-avatar-'));
  t.after(function () { fs.rmSync(clayHome, { recursive: true, force: true }); });
  var script = [
    "var mates = require('./lib/mates');",
    "var attachMates = require('./lib/server-mates').attachMates;",
    "var ctx = { userId: null, multiUser: false, linuxUser: null };",
    "var mate = mates.createMate(ctx, { vendor: 'codex' });",
    "var messages = [];",
    "var handler = attachMates({ users: { isMultiUser: function () { return false; } }, mates: mates, projects: new Map(), addProject: function () {}, removeProject: function () {} });",
    "handler.handleMessage({ readyState: 1, send: function (raw) { messages.push(JSON.parse(raw)); } }, { type: 'mate_update', mateId: mate.id, updates: { profile: Object.assign({}, mate.profile, { displayName: 'Ada', avatarStyle: 'bottts', avatarSeed: 'seed-a', avatarCustom: '' }) } });",
    "var saved = messages[0].mate;",
    "var reloaded = mates.getMate(ctx, mate.id);",
    "if (reloaded.profile.avatarStyle !== 'catalog' || !reloaded.profile.avatarCatalogId) process.exit(2);",
    "var locked = Object.assign({}, reloaded.profile, { avatarLocked: true, avatarCustom: '/locked.png' });",
    "mates.updateMate(ctx, mate.id, { profile: locked });",
    "mates.updateMate(ctx, mate.id, { profile: Object.assign({}, locked, { avatarStyle: 'pixel-art', avatarSeed: 'attacker', avatarCustom: '/replacement.png', avatarLocked: false }) });",
    "var protectedMate = mates.getMate(ctx, mate.id);",
    "process.stdout.write(JSON.stringify({ saved: saved.profile, reloaded: reloaded.profile, protected: protectedMate.profile }));",
  ].join('\n');
  var result = childProcess.spawnSync(process.execPath, ['-e', script], {
    cwd: path.join(__dirname, '..'),
    env: Object.assign({}, process.env, { CLAY_HOME: clayHome, CLAY_CONFIG: path.join(clayHome, 'daemon.json'), CLAY_DEV: '' }),
    encoding: 'utf8',
  });
  assert.strictEqual(result.status, 0, result.stderr || result.stdout);
  var output = JSON.parse(result.stdout);
  assert.strictEqual(output.reloaded.avatarStyle, 'catalog');
  assert.strictEqual(output.reloaded.avatarSeed, undefined);
  assert.strictEqual(output.protected.avatarStyle, 'catalog');
  assert.strictEqual(output.protected.avatarCatalogId, output.reloaded.avatarCatalogId);
  assert.strictEqual(output.protected.avatarCustom, '/locked.png');
  assert.strictEqual(output.protected.avatarLocked, true);
});

test('custom Mate avatar upload uses the existing bounded server path and refuses locked artwork', async function (t) {
  var clayHome = fs.mkdtempSync(path.join(os.tmpdir(), 'clay-mate-upload-'));
  t.after(function () { fs.rmSync(clayHome, { recursive: true, force: true }); });
  var records = {
    'mate-open': { id: 'mate-open', profile: { displayName: 'Open' } },
    'mate-locked': { id: 'mate-locked', profile: { displayName: 'Locked', avatarLocked: true, avatarCustom: '/locked.png' } },
  };
  var settings = attachSettings({
    CONFIG_DIR: clayHome,
    users: { isMultiUser: function () { return false; } },
    mates: {
      buildMateCtx: function () { return {}; },
      getMate: function (ctx, id) { return records[id] || null; },
      updateMate: function (ctx, id, updates) { records[id] = Object.assign({}, records[id], updates); return records[id]; },
    },
    getMultiUserFromReq: function () { return null; },
    projects: new Map(),
    opts: {},
  });
  var png = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
  var saved = await uploadAvatar(settings, 'mate-open', png);
  assert.strictEqual(saved.status, 200);
  assert.match(records['mate-open'].profile.avatarCustom, /^\/api\/mate-avatar\/mate-open\?v=\d+$/);
  assert.strictEqual(fs.existsSync(path.join(clayHome, 'mate-avatars', 'mate-open.png')), true);

  var locked = await uploadAvatar(settings, 'mate-locked', png);
  assert.strictEqual(locked.status, 403);
  assert.strictEqual(records['mate-locked'].profile.avatarCustom, '/locked.png');
  assert.strictEqual(fs.existsSync(path.join(clayHome, 'mate-avatars', 'mate-locked.png')), false);
});

test('historical avatars are generated through the local server without external requests', async function (t) {
  var clayHome = fs.mkdtempSync(path.join(os.tmpdir(), 'clay-generated-avatar-'));
  t.after(function () { fs.rmSync(clayHome, { recursive: true, force: true }); });
  var settings = attachSettings({
    CONFIG_DIR: clayHome,
    users: { isMultiUser: function () { return false; } },
    mates: {},
    getMultiUserFromReq: function () { return null; },
    projects: new Map(),
    opts: {},
  });
  var generated = await getGeneratedAvatar(settings, '/api/generated-avatar?style=bottts&seed=private-seed&size=52');
  assert.strictEqual(generated.status, 200);
  assert.strictEqual(generated.headers['Content-Type'], 'image/svg+xml; charset=utf-8');
  assert.match(generated.body, /^<svg[^>]+width="52" height="52"/);
  assert.doesNotMatch(generated.body, /private-seed|https?:\/\/api\.dicebear\.com/i);

  var unknown = await getGeneratedAvatar(settings, '/api/generated-avatar?style=lorelei&seed=x&size=52');
  assert.strictEqual(unknown.status, 404);
  assert.match(unknown.body, /Unknown avatar style/);
});