118 lines
4.4 KiB
TypeScript
118 lines
4.4 KiB
TypeScript
import { describe, it } from 'node:test';
|
|
import assert from 'node:assert';
|
|
import { sanitizeForRole, FORBIDDEN_CLIENT_FIELDS, applyVisibilityScope } from './creatives-visibility';
|
|
|
|
describe('Creatives Visibility Scope & Serializer', () => {
|
|
const sampleCreative = {
|
|
id: '11111111-1111-1111-1111-111111111111',
|
|
tenant_id: '8bec977a-6032-4759-aa7f-b5042081f450',
|
|
campaign_name: 'Campaña Matrículas',
|
|
format: 'estatico',
|
|
angle: 'prueba_social',
|
|
hook: 'Aprende inglés en 3 meses',
|
|
primary_text: 'El método que funciona para profesionales.',
|
|
headline: 'Clases online con nativos',
|
|
cta: 'Saber más',
|
|
asset_url: 'https://cdn.ejemplo.com/ad1.jpg',
|
|
thumbnail_url: 'https://cdn.ejemplo.com/thumb1.jpg',
|
|
status: 'publicada',
|
|
visibility: 'client',
|
|
parent_id: '00000000-0000-0000-0000-000000000000',
|
|
generation_request_id: '22222222-2222-2222-2222-222222222222',
|
|
created_by: 'claude',
|
|
created_at: '2026-10-12T10:00:00Z',
|
|
updated_at: '2026-10-12T10:00:00Z',
|
|
};
|
|
|
|
const sampleMetrics = {
|
|
id: '33333333-3333-3333-3333-333333333333',
|
|
tenant_id: '8bec977a-6032-4759-aa7f-b5042081f450',
|
|
creative_id: '11111111-1111-1111-1111-111111111111',
|
|
meta_ad_id: 'meta_123',
|
|
date: '2026-10-12',
|
|
impressions: 5000,
|
|
clicks: 120,
|
|
spend: 45.5,
|
|
raw: { meta_response_debug: true, internal_details: 'sensitive' },
|
|
};
|
|
|
|
const sampleGenerationRequest = {
|
|
id: '44444444-4444-4444-4444-444444444444',
|
|
tenant_id: '8bec977a-6032-4759-aa7f-b5042081f450',
|
|
reason: 'fatiga',
|
|
brief: { client_slug: 'bps', variants_requested: 2 },
|
|
cost_estimate: { tokens: 12000, credits: 1 },
|
|
};
|
|
|
|
it('Agency role receives all fields unmodified', () => {
|
|
const res = sanitizeForRole(sampleCreative, 'agency');
|
|
assert.strictEqual(res.angle, 'prueba_social');
|
|
assert.strictEqual(res.created_by, 'claude');
|
|
assert.strictEqual(res.parent_id, '00000000-0000-0000-0000-000000000000');
|
|
assert.strictEqual(res.generation_request_id, '22222222-2222-2222-2222-222222222222');
|
|
|
|
const metricsRes = sanitizeForRole(sampleMetrics, 'admin');
|
|
assert.ok(metricsRes.raw);
|
|
});
|
|
|
|
it('Client role NEVER receives forbidden fields in creatives', () => {
|
|
const res = sanitizeForRole(sampleCreative, 'client');
|
|
for (const field of FORBIDDEN_CLIENT_FIELDS) {
|
|
assert.strictEqual(
|
|
(res as any)[field],
|
|
undefined,
|
|
`Field "${field}" must not be present for client`
|
|
);
|
|
}
|
|
// Permitted fields remain
|
|
assert.strictEqual(res.headline, 'Clases online con nativos');
|
|
assert.strictEqual(res.hook, 'Aprende inglés en 3 meses');
|
|
assert.strictEqual(res.primary_text, 'El método que funciona para profesionales.');
|
|
assert.strictEqual(res.status, 'publicada');
|
|
});
|
|
|
|
it('Client role NEVER receives raw in metrics', () => {
|
|
const res = sanitizeForRole(sampleMetrics, 'client');
|
|
assert.strictEqual(res.raw, undefined);
|
|
assert.strictEqual(res.impressions, 5000);
|
|
assert.strictEqual(res.clicks, 120);
|
|
});
|
|
|
|
it('Client role NEVER receives brief or cost_estimate in generation requests', () => {
|
|
const res = sanitizeForRole(sampleGenerationRequest, 'client');
|
|
assert.strictEqual((res as any).brief, undefined);
|
|
assert.strictEqual((res as any).cost_estimate, undefined);
|
|
assert.strictEqual(res.reason, 'fatiga');
|
|
});
|
|
|
|
it('Sanitizes arrays of objects properly for client role', () => {
|
|
const list = [sampleCreative, sampleCreative];
|
|
const sanitizedList = sanitizeForRole(list, 'client');
|
|
assert.strictEqual(sanitizedList.length, 2);
|
|
sanitizedList.forEach((item: any) => {
|
|
assert.strictEqual(item.angle, undefined);
|
|
assert.strictEqual(item.created_by, undefined);
|
|
assert.strictEqual(item.hook, 'Aprende inglés en 3 meses');
|
|
});
|
|
});
|
|
|
|
it('Query builder applies strict client filters for creatives', () => {
|
|
const mockQuery = {
|
|
filters: [] as string[],
|
|
eq(k: string, v: any) {
|
|
this.filters.push(`${k}=${v}`);
|
|
return this;
|
|
},
|
|
in(k: string, values: any[]) {
|
|
this.filters.push(`${k} IN (${values.join(',')})`);
|
|
return this;
|
|
},
|
|
};
|
|
|
|
applyVisibilityScope(mockQuery, 'creatives', 'client', '8bec977a-6032-4759-aa7f-b5042081f450');
|
|
assert.ok(mockQuery.filters.includes('tenant_id=8bec977a-6032-4759-aa7f-b5042081f450'));
|
|
assert.ok(mockQuery.filters.includes('visibility=client'));
|
|
assert.ok(mockQuery.filters.some((f) => f.startsWith('status IN')));
|
|
});
|
|
});
|