import assert from "node:assert/strict" import test from "node:test" import { randomUUID } from "node:crypto" import sharp from "sharp" import Fastify from "fastify" import { chatContent } from "../src/modules/chat-content" import { CollaborationService } from "../src/modules/collaboration/service" import communicationRoutes from "../src/routes/communication" import { collaborationDatabase, testDatabaseUrl } from "./helpers/collaboration-db" const picture = async () => ({ name: "Baustelle.png", dataUrl: `data:image/png;base64,${(await sharp({ create: { width: 80, height: 40, channels: 3, background: '#206050' } }).png().toBuffer()).toString('base64')}` }) test("chat content accepts emoji and image-only messages, strips metadata and rejects unsafe input", async () => { assert.equal((await chatContent({ text: "Hallo 👋 ❤️" })).body, "Hallo 👋 ❤️") const normalized = await chatContent({ image: await picture() }) assert.equal(normalized.body, "") assert.equal(normalized.image?.width, 80) const metadata = await sharp(Buffer.from(normalized.image!.dataUrl.split(',')[1], 'base64')).metadata() assert.equal(metadata.format, 'webp') assert.equal(metadata.exif, undefined) for (const input of [{}, { text: ' '.repeat(5) }, { text: 'a'.repeat(10001) }, { image: { dataUrl: 'https://example.test/tracker.png' } }, { image: { dataUrl: `data:image/png;base64,${Buffer.from('').toString('base64')}` } }, { image: { dataUrl: 'data:image/png;base64,broken' } }, { image: { dataUrl: 'a'.repeat(700001) } }]) await assert.rejects(chatContent(input)) }) test("native chat images and reactions follow tenant, membership, project and message boundaries", { skip: !testDatabaseUrl }, async () => { const fixture = await collaborationDatabase(), app = Fastify() const owner = randomUUID(), colleague = randomUUID(), outsider = randomUUID(), room = randomUUID(), otherRoom = randomUUID() try { await fixture.pool.query("INSERT INTO tenants VALUES (1,'A',null),(2,'B',null)") for (const [id, tenant] of [[owner, 1], [colleague, 1], [outsider, 2]]) { await fixture.pool.query("INSERT INTO auth_users(id,email) VALUES ($1,$2)", [id, `${id}@test.example`]) await fixture.pool.query("INSERT INTO auth_tenant_users(tenant_id,user_id) VALUES ($1,$2)", [tenant, id]) } await fixture.pool.query("INSERT INTO projects(id,tenant,name,profiles,archived,chat_enabled) VALUES (1,1,'Project','{}',false,true)") await fixture.pool.query(`INSERT INTO communication_rooms(id,tenant_id,key,name,type,archived,entity_id) VALUES ($1,1,'project_1','Project','project',false,1),($2,1,'other','Other','room',false,null)`, [room, otherRoom]) await fixture.pool.query("INSERT INTO communication_room_members(room_id,user_id) VALUES ($1,$2),($1,$3),($4,$2)", [room, owner, colleague, otherRoom]) app.decorate('db', fixture.db) app.addHook('preHandler', async (req: any) => { req.user = { user_id: req.headers['x-user'] || owner, tenant_id: Number(req.headers['x-tenant'] || 1), is_admin: true } req.hasPermission = () => true }) await app.register(communicationRoutes) const base = '/communication/chat/rooms/project_1' const sent = await app.inject({ method: 'POST', url: `${base}/messages`, payload: { image: await picture() } }) assert.equal(sent.statusCode, 200, sent.body) const message = sent.json() assert.equal(message.body, '') assert.equal(message.image.width, 80) assert.equal(message.image.dataUrl, undefined) assert.equal((await app.inject(`${base}/messages`)).json().messages[0].image.dataUrl, undefined) const image = await app.inject(`${base}/image?messageId=${message.id}`) assert.equal(image.statusCode, 200, image.body) assert.equal(image.headers['cache-control'], 'no-store') assert.match(image.json().dataUrl, /^data:image\/webp;base64,/) const react = (headers = {}, active = true, emoji = '👍') => app.inject({ method: 'PUT', url: `${base}/reactions`, headers, payload: { messageId: message.id, emoji, active } }) assert.equal((await react()).statusCode, 200) assert.equal((await react()).json().reactions[message.id][0].count, 1) assert.equal((await react({ 'x-user': colleague })).json().reactions[message.id][0].count, 2) const removed = await react({}, false) assert.deepEqual(removed.json().reactions[message.id], [{ emoji: '👍', count: 1, own: false }]) assert.equal((await react({}, true, '