n8n_node_librebooking/test/test-triggers.ts

486 lines
18 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Comprehensive Trigger Test Script for LibreBooking
*
* Tests:
* 1. Authentication
* 2. Get All Reservations
* 3. Create New Reservation (triggers "New" mode)
* 4. Update Reservation (triggers "Updated" mode)
* 5. Delete Reservation
* 6. Date Range calculations
* 7. Time Filter logic
*/
import * as https from 'https';
const BASE_URL = 'https://librebooking.zell-cloud.de';
const USERNAME = 'sebastian.zell@zell-aufmass.de';
const PASSWORD = 'wanUQ4uVqU6lfP';
// Test date: 7.2.2026
const TEST_DATE = '2026-02-07';
interface TestResult {
name: string;
success: boolean;
message: string;
data?: any;
}
interface Session {
sessionToken: string;
userId: number;
}
const results: TestResult[] = [];
function log(message: string) {
console.log(`[${new Date().toISOString()}] ${message}`);
}
function logSuccess(name: string, message: string, data?: any) {
log(`${name}: ${message}`);
results.push({ name, success: true, message, data });
}
function logError(name: string, message: string, data?: any) {
log(`${name}: ${message}`);
results.push({ name, success: false, message, data });
}
async function makeRequest(
method: string,
path: string,
body?: any,
headers?: Record<string, string>
): Promise<any> {
return new Promise((resolve, reject) => {
const url = new URL(`${BASE_URL}${path}`);
const options: https.RequestOptions = {
hostname: url.hostname,
port: 443,
path: url.pathname + url.search,
method,
headers: {
'Content-Type': 'application/json',
...headers,
},
};
const req = https.request(options, (res) => {
let data = '';
res.on('data', (chunk) => (data += chunk));
res.on('end', () => {
try {
const json = JSON.parse(data);
resolve(json);
} catch (e) {
resolve(data);
}
});
});
req.on('error', reject);
if (body) {
req.write(JSON.stringify(body));
}
req.end();
});
}
// ===== AUTHENTICATION =====
async function authenticate(): Promise<Session> {
const response = await makeRequest(
'POST',
'/Web/Services/index.php/Authentication/Authenticate',
{ username: USERNAME, password: PASSWORD }
);
if (!response.isAuthenticated) {
throw new Error('Authentication failed');
}
return {
sessionToken: response.sessionToken,
userId: response.userId,
};
}
async function signOut(session: Session): Promise<void> {
await makeRequest(
'POST',
'/Web/Services/index.php/Authentication/SignOut',
{ userId: session.userId, sessionToken: session.sessionToken }
);
}
// ===== API REQUESTS =====
async function getReservations(
session: Session,
startDate: string,
endDate: string
): Promise<any[]> {
const response = await makeRequest(
'GET',
`/Web/Services/index.php/Reservations/?startDateTime=${startDate}T00:00:00&endDateTime=${endDate}T23:59:59`,
undefined,
{
'X-Booked-SessionToken': session.sessionToken,
'X-Booked-UserId': session.userId.toString(),
}
);
return response.reservations || [];
}
async function createReservation(
session: Session,
data: {
title: string;
description: string;
resourceId: number;
startDateTime: string;
endDateTime: string;
}
): Promise<any> {
const response = await makeRequest(
'POST',
'/Web/Services/index.php/Reservations/',
{
...data,
userId: session.userId,
termsAccepted: true,
allowParticipation: false,
},
{
'X-Booked-SessionToken': session.sessionToken,
'X-Booked-UserId': session.userId.toString(),
}
);
return response;
}
async function updateReservation(
session: Session,
referenceNumber: string,
data: {
title: string;
description: string;
resourceId: number;
startDateTime: string;
endDateTime: string;
}
): Promise<any> {
const response = await makeRequest(
'POST',
`/Web/Services/index.php/Reservations/${referenceNumber}`,
{
...data,
userId: session.userId,
termsAccepted: true,
allowParticipation: false,
},
{
'X-Booked-SessionToken': session.sessionToken,
'X-Booked-UserId': session.userId.toString(),
}
);
return response;
}
async function deleteReservation(session: Session, referenceNumber: string): Promise<any> {
const response = await makeRequest(
'DELETE',
`/Web/Services/index.php/Reservations/${referenceNumber}`,
undefined,
{
'X-Booked-SessionToken': session.sessionToken,
'X-Booked-UserId': session.userId.toString(),
}
);
return response;
}
async function getResources(session: Session): Promise<any[]> {
const response = await makeRequest(
'GET',
'/Web/Services/index.php/Resources/',
undefined,
{
'X-Booked-SessionToken': session.sessionToken,
'X-Booked-UserId': session.userId.toString(),
}
);
return response.resources || [];
}
// ===== DATE RANGE TESTS =====
function testDateRange() {
log('\n📅 Testing Date Range Calculations...\n');
const now = new Date('2026-01-25'); // Simulate current date
// Test thisWeek
const dayOfWeek = now.getDay();
const diffToMonday = dayOfWeek === 0 ? -6 : 1 - dayOfWeek;
const monday = new Date(now);
monday.setDate(now.getDate() + diffToMonday);
monday.setHours(0, 0, 0, 0);
const sunday = new Date(monday);
sunday.setDate(monday.getDate() + 6);
logSuccess(
'thisWeek',
`Monday: ${monday.toISOString().split('T')[0]}, Sunday: ${sunday.toISOString().split('T')[0]}`
);
// Test next2Weeks
const next2Weeks = new Date(now);
next2Weeks.setDate(now.getDate() + 14);
logSuccess(
'next2Weeks',
`From: ${now.toISOString().split('T')[0]}, To: ${next2Weeks.toISOString().split('T')[0]}`
);
// Test thisMonth
const monthStart = new Date(now.getFullYear(), now.getMonth(), 1);
const monthEnd = new Date(now.getFullYear(), now.getMonth() + 1, 0);
logSuccess(
'thisMonth',
`From: ${monthStart.toISOString().split('T')[0]}, To: ${monthEnd.toISOString().split('T')[0]}`
);
// Test next2Months
const next2Months = new Date(now);
next2Months.setMonth(now.getMonth() + 2);
logSuccess(
'next2Months',
`From: ${now.toISOString().split('T')[0]}, To: ${next2Months.toISOString().split('T')[0]}`
);
// Test thisYear
const yearStart = new Date(now.getFullYear(), 0, 1);
const yearEnd = new Date(now.getFullYear(), 11, 31);
logSuccess(
'thisYear',
`From: ${yearStart.toISOString().split('T')[0]}, To: ${yearEnd.toISOString().split('T')[0]}`
);
}
// ===== TIME FILTER TESTS =====
function testTimeFilter() {
log('\n⏰ Testing Time Filter Logic...\n');
const today = new Date('2026-02-07');
today.setHours(0, 0, 0, 0);
const reservations = [
{ title: 'Today', startDateTime: '2026-02-07T10:00:00' },
{ title: 'Tomorrow', startDateTime: '2026-02-08T10:00:00' },
{ title: 'In 3 days', startDateTime: '2026-02-10T10:00:00' },
{ title: 'In 7 days', startDateTime: '2026-02-14T10:00:00' },
{ title: 'In 10 days', startDateTime: '2026-02-17T10:00:00' },
];
// Filter: today
const todayOnly = reservations.filter((r) => {
const startDate = new Date(r.startDateTime);
startDate.setHours(0, 0, 0, 0);
return startDate.getTime() === today.getTime();
});
logSuccess('timeFilter=today', `Found ${todayOnly.length} reservation(s): ${todayOnly.map(r => r.title).join(', ')}`);
// Filter: next3Days
const threeDays = new Date(today);
threeDays.setDate(today.getDate() + 3);
const next3Days = reservations.filter((r) => {
const startDate = new Date(r.startDateTime);
startDate.setHours(0, 0, 0, 0);
return startDate >= today && startDate <= threeDays;
});
logSuccess('timeFilter=next3Days', `Found ${next3Days.length} reservation(s): ${next3Days.map(r => r.title).join(', ')}`);
// Filter: next7Days
const sevenDays = new Date(today);
sevenDays.setDate(today.getDate() + 7);
const next7Days = reservations.filter((r) => {
const startDate = new Date(r.startDateTime);
startDate.setHours(0, 0, 0, 0);
return startDate >= today && startDate <= sevenDays;
});
logSuccess('timeFilter=next7Days', `Found ${next7Days.length} reservation(s): ${next7Days.map(r => r.title).join(', ')}`);
}
// ===== MAIN TEST =====
async function runTests() {
console.log('🧪 LibreBooking Trigger Test Suite\n');
console.log('═'.repeat(60) + '\n');
// Test date calculations first (no API needed)
testDateRange();
testTimeFilter();
log('\n🔌 Testing API Operations...\n');
let session: Session | null = null;
let createdRefNumber: string | null = null;
try {
// 1. Authentication
log('1⃣ Authenticating...');
session = await authenticate();
logSuccess('Authentication', `Session: ${session.sessionToken.substring(0, 20)}...`);
// 2. Get Resources (to find available resource)
log('\n2⃣ Getting Resources...');
const resources = await getResources(session);
if (resources.length === 0) {
logError('GetResources', 'No resources found');
return;
}
logSuccess('GetResources', `Found ${resources.length} resources`);
const testResourceId = resources[0].resourceId;
log(` Using resource: ${resources[0].name} (ID: ${testResourceId})`);
// 3. Get Initial Reservations
log('\n3⃣ Getting initial reservations...');
const initialReservations = await getReservations(session, TEST_DATE, '2026-02-14');
logSuccess('GetReservations', `Found ${initialReservations.length} reservations`);
// Store initial IDs (simulating first poll)
const seenIds = initialReservations.map((r: any) => r.referenceNumber);
log(` Stored ${seenIds.length} IDs (simulating first poll)`);
// 4. Create New Reservation
log('\n4⃣ Creating new reservation...');
const createResult = await createReservation(session, {
title: 'TEST: Neue Reservierung für Trigger-Test',
description: 'Diese Reservierung wird erstellt, um den "Neue Objekte" Trigger zu testen',
resourceId: testResourceId,
startDateTime: `${TEST_DATE}T14:00:00`,
endDateTime: `${TEST_DATE}T15:00:00`,
});
if (createResult.referenceNumber) {
createdRefNumber = createResult.referenceNumber;
logSuccess('CreateReservation', `Created: ${createdRefNumber}`);
} else {
logError('CreateReservation', `Failed: ${JSON.stringify(createResult)}`);
}
// 5. Get Reservations Again (simulate second poll)
log('\n5⃣ Simulating second poll...');
await new Promise((resolve) => setTimeout(resolve, 2000)); // Wait 2 seconds
const afterCreateReservations = await getReservations(session, TEST_DATE, '2026-02-14');
const currentIds = afterCreateReservations.map((r: any) => r.referenceNumber);
const newIds = currentIds.filter((id: string) => !seenIds.includes(id));
logSuccess(
'NewReservationDetection',
`Found ${newIds.length} new reservation(s): ${newIds.join(', ')}`
);
// 6. Update Reservation
if (createdRefNumber) {
log('\n6⃣ Updating reservation...');
// Store hash before update
const reservationBefore = afterCreateReservations.find(
(r: any) => r.referenceNumber === createdRefNumber
);
const hashBefore = JSON.stringify({
title: reservationBefore?.title,
description: reservationBefore?.description,
});
// Update - must include all required fields
const updateResult = await updateReservation(session, createdRefNumber, {
title: 'TEST: GEÄNDERTE Reservierung',
description: 'Diese Reservierung wurde geändert - Update-Trigger sollte feuern',
resourceId: testResourceId,
startDateTime: `${TEST_DATE}T14:00:00`,
endDateTime: `${TEST_DATE}T15:00:00`,
});
if (updateResult.referenceNumber) {
logSuccess('UpdateReservation', `Updated: ${createdRefNumber}`);
} else {
logError('UpdateReservation', `Failed: ${JSON.stringify(updateResult)}`);
}
// 7. Get Reservations Again (check for changes)
log('\n7⃣ Checking for changes...');
await new Promise((resolve) => setTimeout(resolve, 2000));
const afterUpdateReservations = await getReservations(session, TEST_DATE, '2026-02-14');
const reservationAfter = afterUpdateReservations.find(
(r: any) => r.referenceNumber === createdRefNumber
);
const hashAfter = JSON.stringify({
title: reservationAfter?.title,
description: reservationAfter?.description,
});
if (hashBefore !== hashAfter) {
logSuccess(
'ChangeDetection',
`Change detected! Title: "${reservationAfter?.title}"`
);
} else {
logError('ChangeDetection', 'No change detected (hash unchanged)');
}
// 8. Delete Reservation
log('\n8⃣ Deleting reservation...');
await deleteReservation(session, createdRefNumber);
logSuccess('DeleteReservation', `Deleted: ${createdRefNumber}`);
// Verify deletion
await new Promise((resolve) => setTimeout(resolve, 1000));
const afterDeleteReservations = await getReservations(session, TEST_DATE, '2026-02-14');
const stillExists = afterDeleteReservations.some(
(r: any) => r.referenceNumber === createdRefNumber
);
if (!stillExists) {
logSuccess('DeletionVerified', 'Reservation successfully deleted');
} else {
logError('DeletionVerified', 'Reservation still exists!');
}
}
// 9. Sign Out
log('\n9⃣ Signing out...');
await signOut(session);
logSuccess('SignOut', 'Successfully signed out');
} catch (error: any) {
logError('TestSuite', `Error: ${error.message}`);
}
// Summary
console.log('\n' + '═'.repeat(60));
console.log('📊 TEST SUMMARY\n');
const passed = results.filter((r) => r.success).length;
const failed = results.filter((r) => !r.success).length;
console.log(` ✅ Passed: ${passed}`);
console.log(` ❌ Failed: ${failed}`);
console.log(` 📝 Total: ${results.length}`);
if (failed > 0) {
console.log('\n Failed Tests:');
results
.filter((r) => !r.success)
.forEach((r) => console.log(` - ${r.name}: ${r.message}`));
}
console.log('\n' + '═'.repeat(60));
// Exit with appropriate code
process.exit(failed > 0 ? 1 : 0);
}
runTests();