refactor: centralizar pruebas de crypto/fechas y alias toIsoSql

Co-authored-by: aider (openrouter/openai/gpt-5) <aider@aider.chat>
main
brobert 1 month ago
parent ca06b85c48
commit 77e318e677

@ -0,0 +1,3 @@
import { sha256Hex as coreSha256Hex } from '../../src/utils/crypto';
export const sha256Hex = coreSha256Hex;

@ -0,0 +1,25 @@
import { toIsoSqlUTC } from '../../src/utils/datetime';
export function toIsoSql(d: Date = new Date()): string {
return toIsoSqlUTC(d);
}
export { toIsoSqlUTC };
export function ymdInTZ(d: Date, tz: string = 'Europe/Madrid'): string {
const parts = new Intl.DateTimeFormat('en-GB', {
timeZone: tz,
year: 'numeric',
month: '2-digit',
day: '2-digit',
}).formatToParts(d);
const get = (t: string) => parts.find(p => p.type === t)?.value || '';
return `${get('year')}-${get('month')}-${get('day')}`;
}
export function addDaysToYMD(ymd: string, days: number, tz: string = 'Europe/Madrid'): string {
const [Y, M, D] = ymd.split('-').map(n => parseInt(n, 10));
const base = new Date(Date.UTC(Y, (M || 1) - 1, D || 1));
base.setUTCDate(base.getUTCDate() + days);
return ymdInTZ(base, tz);
}

@ -0,0 +1,9 @@
let simulatedQueue: any[] = [];
export const SimulatedResponseQueue = {
async add(responses: any[]) {
simulatedQueue.push(...responses);
},
clear() { simulatedQueue = []; },
get() { return simulatedQueue; }
};

@ -6,23 +6,9 @@ import { GroupSyncService } from '../../src/services/group-sync';
import { initializeDatabase, ensureUserExists } from '../../src/db';
import { TaskService } from '../../src/tasks/service';
// Simulated ResponseQueue for testing (in-memory array)
let simulatedQueue: any[] = [];
let originalAdd: any;
class SimulatedResponseQueue {
static async add(responses: any[]) {
simulatedQueue.push(...responses);
}
static getQueue() {
return simulatedQueue;
}
static clear() {
simulatedQueue = [];
}
}
import { SimulatedResponseQueue } from '../helpers/queue';
// Test database instance
let testDb: Database;
@ -158,7 +144,7 @@ describe('WebhookServer', () => {
const request = createTestRequest(payload);
const response = await WebhookServer.handleRequest(request);
expect(response.status).toBe(200);
expect(SimulatedResponseQueue.getQueue().length).toBeGreaterThan(0);
expect(SimulatedResponseQueue.get().length).toBeGreaterThan(0);
});
test('should ignore empty message content', async () => {
@ -176,7 +162,7 @@ describe('WebhookServer', () => {
const request = createTestRequest(payload);
const response = await WebhookServer.handleRequest(request);
expect(response.status).toBe(200);
expect(SimulatedResponseQueue.getQueue().length).toBe(0);
expect(SimulatedResponseQueue.get().length).toBe(0);
});
test('should handle very long messages', async () => {
@ -195,7 +181,7 @@ describe('WebhookServer', () => {
const request = createTestRequest(payload);
const response = await WebhookServer.handleRequest(request);
expect(response.status).toBe(200);
expect(SimulatedResponseQueue.getQueue().length).toBeGreaterThan(0);
expect(SimulatedResponseQueue.get().length).toBeGreaterThan(0);
});
test('should handle messages with special characters and emojis', async () => {
@ -214,7 +200,7 @@ describe('WebhookServer', () => {
const request = createTestRequest(payload);
const response = await WebhookServer.handleRequest(request);
expect(response.status).toBe(200);
expect(SimulatedResponseQueue.getQueue().length).toBeGreaterThan(0);
expect(SimulatedResponseQueue.get().length).toBeGreaterThan(0);
});
test('should ignore non-/tarea commands', async () => {
@ -232,7 +218,7 @@ describe('WebhookServer', () => {
const request = createTestRequest(payload);
const response = await WebhookServer.handleRequest(request);
expect(response.status).toBe(200);
expect(SimulatedResponseQueue.getQueue().length).toBe(0);
expect(SimulatedResponseQueue.get().length).toBe(0);
});
test('should ignore message with mentions but no command', async () => {
@ -255,7 +241,7 @@ describe('WebhookServer', () => {
const request = createTestRequest(payload);
const response = await WebhookServer.handleRequest(request);
expect(response.status).toBe(200);
expect(SimulatedResponseQueue.getQueue().length).toBe(0);
expect(SimulatedResponseQueue.get().length).toBe(0);
});
test('should ignore media attachment messages', async () => {
@ -275,7 +261,7 @@ describe('WebhookServer', () => {
const request = createTestRequest(payload);
const response = await WebhookServer.handleRequest(request);
expect(response.status).toBe(200);
expect(SimulatedResponseQueue.getQueue().length).toBe(0);
expect(SimulatedResponseQueue.get().length).toBe(0);
});
test('should process command from extendedTextMessage', async () => {
@ -295,7 +281,7 @@ describe('WebhookServer', () => {
const request = createTestRequest(payload);
const response = await WebhookServer.handleRequest(request);
expect(response.status).toBe(200);
expect(SimulatedResponseQueue.getQueue().length).toBeGreaterThan(0);
expect(SimulatedResponseQueue.get().length).toBeGreaterThan(0);
});
test('should process command from image caption when caption starts with a command', async () => {
@ -315,7 +301,7 @@ describe('WebhookServer', () => {
const request = createTestRequest(payload);
const response = await WebhookServer.handleRequest(request);
expect(response.status).toBe(200);
expect(SimulatedResponseQueue.getQueue().length).toBeGreaterThan(0);
expect(SimulatedResponseQueue.get().length).toBeGreaterThan(0);
});
test('should handle requests on configured port', async () => {
@ -373,7 +359,7 @@ describe('WebhookServer', () => {
await WebhookServer.handleRequest(createTestRequest(payload));
// Check that a response was queued (indicating command processing)
expect(SimulatedResponseQueue.getQueue().length).toBeGreaterThan(0);
expect(SimulatedResponseQueue.get().length).toBeGreaterThan(0);
});
test('should log command with due date', async () => {
@ -398,7 +384,7 @@ describe('WebhookServer', () => {
await WebhookServer.handleRequest(createTestRequest(payload));
// Verify command processing by checking queue
expect(SimulatedResponseQueue.getQueue().length).toBeGreaterThan(0);
expect(SimulatedResponseQueue.get().length).toBeGreaterThan(0);
});
});
@ -418,7 +404,7 @@ describe('WebhookServer', () => {
const request = createTestRequest(payload);
const response = await WebhookServer.handleRequest(request);
expect(response.status).toBe(200);
expect(SimulatedResponseQueue.getQueue().length).toBeGreaterThan(0);
expect(SimulatedResponseQueue.get().length).toBeGreaterThan(0);
});
test('should handle multiple dates in command (use last one as due date)', async () => {
@ -438,7 +424,7 @@ describe('WebhookServer', () => {
await WebhookServer.handleRequest(createTestRequest(payload));
expect(SimulatedResponseQueue.getQueue().length).toBeGreaterThan(0);
expect(SimulatedResponseQueue.get().length).toBeGreaterThan(0);
});
test('should ignore past dates as due dates', async () => {
@ -598,7 +584,7 @@ describe('WebhookServer', () => {
const request = createTestRequest(payload);
const response = await WebhookServer.handleRequest(request);
expect(response.status).toBe(200);
expect(SimulatedResponseQueue.getQueue().length).toBeGreaterThan(0);
expect(SimulatedResponseQueue.get().length).toBeGreaterThan(0);
// Verify user was created in real database
const user = testDb.query("SELECT * FROM users WHERE id = ?").get('1234567890');
@ -621,7 +607,7 @@ describe('WebhookServer', () => {
const request = createTestRequest(payload);
const response = await WebhookServer.handleRequest(request);
expect(response.status).toBe(200);
expect(SimulatedResponseQueue.getQueue().length).toBe(0);
expect(SimulatedResponseQueue.get().length).toBe(0);
// Verify no user was created
const userCount = testDb.query("SELECT COUNT(*) as count FROM users").get();
@ -646,7 +632,7 @@ describe('WebhookServer', () => {
const request = createTestRequest(payload);
const response = await WebhookServer.handleRequest(request);
expect(response.status).toBe(200);
expect(SimulatedResponseQueue.getQueue().length).toBe(0);
expect(SimulatedResponseQueue.get().length).toBe(0);
// Reinitialize database for subsequent tests (force full migration)
testDb.exec('DROP TABLE IF EXISTS schema_migrations');
@ -668,7 +654,7 @@ describe('WebhookServer', () => {
const request = createTestRequest(payload);
const response = await WebhookServer.handleRequest(request);
expect(response.status).toBe(200);
expect(SimulatedResponseQueue.getQueue().length).toBeGreaterThan(0);
expect(SimulatedResponseQueue.get().length).toBeGreaterThan(0);
// Verify user was created/updated in database
const user = testDb.query("SELECT * FROM users WHERE id = ?").get('1234567890');
@ -695,7 +681,7 @@ describe('WebhookServer', () => {
await WebhookServer.handleRequest(request);
// Verify that a response was queued, indicating command processing
expect(SimulatedResponseQueue.getQueue().length).toBeGreaterThan(0);
expect(SimulatedResponseQueue.get().length).toBeGreaterThan(0);
});
test('should handle end-to-end flow with valid user and command processing', async () => {
@ -720,7 +706,7 @@ describe('WebhookServer', () => {
expect(user).toBeDefined();
// Verify that a response was queued
expect(SimulatedResponseQueue.getQueue().length).toBeGreaterThan(0);
expect(SimulatedResponseQueue.get().length).toBeGreaterThan(0);
});
});
@ -800,7 +786,7 @@ describe('WebhookServer', () => {
const request = createTestRequest(payload);
await WebhookServer.handleRequest(request);
const out = SimulatedResponseQueue.getQueue();
const out = SimulatedResponseQueue.get();
expect(out.length).toBeGreaterThan(0);
for (const r of out) {
expect(r.recipient.endsWith('@g.us')).toBe(false);
@ -848,7 +834,7 @@ describe('WebhookServer', () => {
const response = await WebhookServer.handleRequest(createTestRequest(payload));
expect(response.status).toBe(200);
const out = SimulatedResponseQueue.getQueue();
const out = SimulatedResponseQueue.get();
expect(out.length).toBeGreaterThan(0);
for (const r of out) {
expect(r.recipient.endsWith('@g.us')).toBe(false);

@ -7,14 +7,7 @@ import { ResponseQueue } from '../../../src/services/response-queue';
let testDb: Database;
let originalAdd: any;
let simulatedQueue: any[] = [];
const SimulatedResponseQueue = {
async add(responses: any[]) {
simulatedQueue.push(...responses);
},
clear() { simulatedQueue = []; },
get() { return simulatedQueue; }
};
import { SimulatedResponseQueue } from '../../helpers/queue';
const createTestRequest = (payload: any) =>
new Request('http://localhost:3007', {

@ -8,14 +8,7 @@ import { GroupSyncService } from '../../../src/services/group-sync';
let testDb: Database;
let originalAdd: any;
let simulatedQueue: any[] = [];
const SimulatedResponseQueue = {
async add(responses: any[]) {
simulatedQueue.push(...responses);
},
clear() { simulatedQueue = []; },
get() { return simulatedQueue; }
};
import { SimulatedResponseQueue } from '../../helpers/queue';
const createTestRequest = (payload: any) =>
new Request('http://localhost:3007', {

@ -7,14 +7,7 @@ import { ResponseQueue } from '../../../src/services/response-queue';
let testDb: Database;
let originalAdd: any;
let simulatedQueue: any[] = [];
const SimulatedResponseQueue = {
async add(responses: any[]) {
simulatedQueue.push(...responses);
},
clear() { simulatedQueue = []; },
get() { return simulatedQueue; }
};
import { SimulatedResponseQueue } from '../../helpers/queue';
const createTestRequest = (payload: any) =>
new Request('http://localhost:3007', {

@ -9,14 +9,7 @@ import { GroupSyncService } from '../../../src/services/group-sync';
let testDb: Database;
let originalAdd: any;
let simulatedQueue: any[] = [];
const SimulatedResponseQueue = {
async add(responses: any[]) {
simulatedQueue.push(...responses);
},
clear() { simulatedQueue = []; },
get() { return simulatedQueue; }
};
import { SimulatedResponseQueue } from '../../helpers/queue';
const createTestRequest = (payload: any) =>
new Request('http://localhost:3007', {

@ -7,14 +7,7 @@ import { ResponseQueue } from '../../../src/services/response-queue';
let testDb: Database;
let originalAdd: any;
let simulatedQueue: any[] = [];
const SimulatedResponseQueue = {
async add(responses: any[]) {
simulatedQueue.push(...responses);
},
clear() { simulatedQueue = []; },
get() { return simulatedQueue; }
};
import { SimulatedResponseQueue } from '../../helpers/queue';
const createTestRequest = (payload: any) =>
new Request('http://localhost:3007', {

@ -3,9 +3,7 @@ import { Database } from 'bun:sqlite';
import { initializeDatabase } from '../../../src/db';
import { MaintenanceService } from '../../../src/services/maintenance';
function toIso(d: Date): string {
return d.toISOString().replace('T', ' ').replace('Z', '');
}
import { toIsoSql as toIso } from '../../helpers/dates';
const envBackup = { ...process.env };
let memdb: Database;

@ -4,23 +4,8 @@ import { initializeDatabase } from '../../../src/db';
import { TaskService } from '../../../src/tasks/service';
import { CommandService } from '../../../src/services/command';
function ymdInTZ(d: Date, tz: string = 'Europe/Madrid'): string {
const parts = new Intl.DateTimeFormat('en-GB', {
timeZone: tz,
year: 'numeric',
month: '2-digit',
day: '2-digit',
}).formatToParts(d);
const get = (t: string) => parts.find(p => p.type === t)?.value || '';
return `${get('year')}-${get('month')}-${get('day')}`;
}
function addDaysToYMD(ymd: string, days: number, tz: string = 'Europe/Madrid'): string {
const [Y, M, D] = ymd.split('-').map(n => parseInt(n, 10));
const base = new Date(Date.UTC(Y, (M || 1) - 1, D || 1));
base.setUTCDate(base.getUTCDate() + days);
return ymdInTZ(base, tz);
}
import { ymdInTZ, addDaysToYMD } from '../../helpers/dates';
describe('CommandService - parser de fechas (hoy/mañana y formatos YYYY/YY-MM-DD)', () => {
let memdb: Database;

@ -4,9 +4,7 @@ import { Metrics } from '../../../src/services/metrics';
import { initializeDatabase } from '../../../src/db';
import { Database } from 'bun:sqlite';
function toIso(d: Date): string {
return d.toISOString().replace('T', ' ').replace('Z', '');
}
import { toIsoSql as toIso } from '../../helpers/dates';
const envBackup = { ...process.env };
let memdb: Database;

@ -6,9 +6,7 @@ import { ResponseQueue } from '../../../src/services/response-queue';
let testDb: Database;
let originalDbInstance: Database;
function toIso(dt: Date): string {
return dt.toISOString().replace('T', ' ').replace('Z', '');
}
import { toIsoSql as toIso } from '../../helpers/dates';
describe('ResponseQueue cleanup/retention', () => {
beforeAll(() => {

@ -5,9 +5,7 @@ import { TaskService } from '../../../src/tasks/service';
import { ResponseQueue } from '../../../src/services/response-queue';
import { AllowedGroups } from '../../../src/services/allowed-groups';
function toIsoSql(d: Date): string {
return d.toISOString().replace('T', ' ').replace('Z', '');
}
import { toIsoSql } from '../../helpers/dates';
describe('TaskService - reacción ✅ al completar (Fase 2)', () => {
let memdb: Database;

@ -3,18 +3,9 @@ import Database from 'bun:sqlite';
import { startWebServer } from './helpers/server';
import { createTempDb } from './helpers/db';
async function sha256Hex(input: string): Promise<string> {
const enc = new TextEncoder().encode(input);
const buf = await crypto.subtle.digest('SHA-256', enc);
const bytes = new Uint8Array(buf);
return Array.from(bytes)
.map((b) => b.toString(16).padStart(2, '0'))
.join('');
}
function toIsoSql(d = new Date()): string {
return d.toISOString().replace('T', ' ').replace('Z', '');
}
import { sha256Hex } from '../helpers/crypto';
import { toIsoSql } from '../helpers/dates';
describe('API - /api/integrations/feeds', () => {
const PORT = 19123;

@ -6,18 +6,9 @@ import { join } from 'path';
import { startWebServer } from './helpers/server';
import { initializeDatabase, ensureUserExists } from '../../src/db';
async function sha256Hex(input: string): Promise<string> {
const enc = new TextEncoder().encode(input);
const buf = await crypto.subtle.digest('SHA-256', enc);
const bytes = new Uint8Array(buf);
return Array.from(bytes)
.map((b) => b.toString(16).padStart(2, '0'))
.join('');
}
function toIsoSql(d = new Date()): string {
return d.toISOString().replace('T', ' ').replace('Z', '');
}
import { sha256Hex } from '../helpers/crypto';
import { toIsoSql } from '../helpers/dates';
describe('Web API - GET /api/me/preferences', () => {
const userId = '34600123456';

@ -6,18 +6,9 @@ import { join } from 'path';
import { startWebServer } from './helpers/server';
import { initializeDatabase, ensureUserExists } from '../../src/db';
async function sha256Hex(input: string): Promise<string> {
const enc = new TextEncoder().encode(input);
const buf = await crypto.subtle.digest('SHA-256', enc);
const bytes = new Uint8Array(buf);
return Array.from(bytes)
.map((b) => b.toString(16).padStart(2, '0'))
.join('');
}
function toIsoSql(d = new Date()): string {
return d.toISOString().replace('T', ' ').replace('Z', '');
}
import { sha256Hex } from '../helpers/crypto';
import { toIsoSql } from '../helpers/dates';
describe('Web API - GET /api/me/tasks', () => {
const USER = '34600123456';

@ -2,9 +2,7 @@ import { beforeEach, afterEach, describe, expect, it } from 'bun:test';
import { createTempDb } from './helpers/db';
// Los imports del handler y closeDb se hacen dinámicos dentro de cada test/teardown
function toIsoSql(d: Date): string {
return d.toISOString().replace('T', ' ').replace('Z', '');
}
import { toIsoSql } from '../helpers/dates';
describe('Web API - completar tarea encola reacción ✅', () => {
let cleanup: () => void;

@ -3,18 +3,9 @@ import Database from 'bun:sqlite';
import { startWebServer } from './helpers/server';
import { createTempDb } from './helpers/db';
async function sha256Hex(input: string): Promise<string> {
const enc = new TextEncoder().encode(input);
const buf = await crypto.subtle.digest('SHA-256', enc);
const bytes = new Uint8Array(buf);
return Array.from(bytes)
.map((b) => b.toString(16).padStart(2, '0'))
.join('');
}
import { sha256Hex } from '../helpers/crypto';
function toIsoSql(d = new Date()): string {
return d.toISOString().replace('T', ' ').replace('Z', '');
}
import { toIsoSql } from '../helpers/dates';
describe('Web page - /app/integrations', () => {
const PORT = 19124;

@ -6,18 +6,9 @@ import { join } from 'path';
import { startWebServer } from './helpers/server';
import { initializeDatabase, ensureUserExists } from '../../src/db';
async function sha256Hex(input: string): Promise<string> {
const enc = new TextEncoder().encode(input);
const buf = await crypto.subtle.digest('SHA-256', enc);
const bytes = new Uint8Array(buf);
return Array.from(bytes)
.map((b) => b.toString(16).padStart(2, '0'))
.join('');
}
import { sha256Hex } from '../helpers/crypto';
function toIsoSql(d = new Date()): string {
return d.toISOString().replace('T', ' ').replace('Z', '');
}
import { toIsoSql } from '../helpers/dates';
describe('Web UI - /app/preferences', () => {
const userId = '34600123456';

Loading…
Cancel
Save