const test = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); const os = require('node:os'); const path = require('node:path'); const express = require('express'); require('../src/common'); const { createUploadSyncService } = require('../src/web/lib/media'); test('multipart uploads accept large text fields used by slide and template forms', async () => { const uploadDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pulse-signage-upload-')); const uploadSyncService = createUploadSyncService({ common: {}, playerSnapshotCache: new Map(), notifyPlayerScreens: async () => {} }); const upload = uploadSyncService.createUploadMiddleware(uploadDir); const app = express(); app.post('/upload', upload.any(), function (_req, res) { res.sendStatus(204); }); app.use(function (error, _req, res, _next) { res.status(error.statusCode || 500).send(String(error && error.message ? error.message : error)); }); const server = app.listen(0); try { const address = server.address(); const largeValue = 'x'.repeat(1_100_000); const formData = new FormData(); formData.set('region_text_6', largeValue); const response = await fetch(`http://127.0.0.1:${address.port}/upload`, { method: 'POST', body: formData }); assert.equal(response.status, 204); } finally { await new Promise((resolve) => server.close(resolve)); fs.rmSync(uploadDir, { recursive: true, force: true }); } }); test('fqdn player registration wins over a local configured player target for media sync', async () => { const uploadDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pulse-signage-upload-sync-')); fs.mkdirSync(path.join(uploadDir, 'uploads'), { recursive: true }); fs.writeFileSync(path.join(uploadDir, 'uploads', 'sample.bin'), Buffer.from('hello world')); const fetchCalls = []; const originalFetch = global.fetch; global.fetch = async function (url, init) { fetchCalls.push({ url, init }); return { ok: true, status: 200, statusText: 'OK', headers: { get() { return null; } }, async text() { return JSON.stringify({ ok: true }); } }; }; const uploadSyncService = createUploadSyncService({ common: {}, pool: { async query() { return [[ { identifier: 'player-local', internal_base_url: 'http://player:8081' }, { identifier: 'player-remote', internal_base_url: 'https://pulse-dev-bridge.lzstealth.com' } ]]; } }, playerSnapshotCache: new Map(), notifyPlayerScreens: async () => {} }); try { const success = await uploadSyncService.pushUploadFileToPlayer('/media/uploads/sample.bin', uploadDir); assert.equal(success, true); assert.equal(fetchCalls.length, 1); assert.equal(fetchCalls[0].url, 'https://pulse-dev-bridge.lzstealth.com/api/media/uploads%2Fsample.bin'); } finally { global.fetch = originalFetch; fs.rmSync(uploadDir, { recursive: true, force: true }); } }); test('media sync retry warnings include the resolved player label', async () => { const uploadDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pulse-signage-upload-sync-log-')); fs.mkdirSync(path.join(uploadDir, 'uploads'), { recursive: true }); fs.writeFileSync(path.join(uploadDir, 'uploads', 'sample.bin'), Buffer.from('hello world')); const originalFetch = global.fetch; const originalWarn = console.warn; const warned = []; console.warn = function () { warned.push(Array.from(arguments).join(' ')); }; global.fetch = async function () { const error = new Error('getaddrinfo ENOTFOUND player-remote'); error.code = 'ENOTFOUND'; throw error; }; const uploadSyncService = createUploadSyncService({ common: {}, pool: { async query() { return [[ { identifier: 'player-remote', internal_base_url: 'https://pulse-dev-bridge.lzstealth.com' } ]]; } }, playerSnapshotCache: new Map(), notifyPlayerScreens: async () => {} }); try { await uploadSyncService.syncUploadRefsToPlayer(['/media/uploads/sample.bin'], uploadDir); await uploadSyncService.flushPendingPlayerUploadSyncs(); assert.ok(warned.some(function (message) { return message.includes('[media-sync] Player unavailable, retry queued for 1 upload for player-remote'); })); } finally { global.fetch = originalFetch; console.warn = originalWarn; fs.rmSync(uploadDir, { recursive: true, force: true }); } });