Files
bunq/test/test.session.ts

287 lines
9.1 KiB
TypeScript

import { expect, tap } from '@git.zone/tstest/tapbundle';
import * as bunq from '../ts/index.js';
import * as plugins from '../ts/bunq.plugins.js';
let testBunqAccount: bunq.BunqAccount;
let sandboxApiKey: string;
tap.test('should test session creation and lifecycle', async () => {
// Create sandbox user
const tempAccount = new bunq.BunqAccount({
apiKey: '',
deviceName: 'bunq-session-test',
environment: 'SANDBOX',
});
sandboxApiKey = await tempAccount.createSandboxUser();
console.log('Generated sandbox API key for session tests');
// Test initial session creation
testBunqAccount = new bunq.BunqAccount({
apiKey: sandboxApiKey,
deviceName: 'bunq-session-test',
environment: 'SANDBOX',
});
await testBunqAccount.init();
expect(testBunqAccount.userId).toBeTypeofNumber();
console.log('Initial session created successfully');
});
tap.test('should test session persistence and restoration', async () => {
// Get current context file path
const contextPath = testBunqAccount.getEnvironment() === 'PRODUCTION'
? '.nogit/bunqproduction.json'
: '.nogit/bunqsandbox.json';
// Check if context was saved
const contextExists = await plugins.smartfile.fs.fileExists(contextPath);
expect(contextExists).toEqual(true);
console.log('Session context saved to file');
// Create new instance that should restore session
const restoredAccount = new bunq.BunqAccount({
apiKey: sandboxApiKey,
deviceName: 'bunq-session-test',
environment: 'SANDBOX',
});
await restoredAccount.init();
// Should reuse existing session without creating new one
expect(restoredAccount.userId).toEqual(testBunqAccount.userId);
console.log('Session restored from saved context');
await restoredAccount.stop();
});
tap.test('should test session expiry and renewal', async () => {
const apiContext = testBunqAccount['apiContext'];
const session = apiContext.getSession();
// Check if session is valid
const isValid = session.isSessionValid();
expect(isValid).toEqual(true);
console.log('Session is currently valid');
// Test session refresh
await session.refreshSession();
console.log('Session refreshed successfully');
// Ensure session is still valid after refresh
const isStillValid = session.isSessionValid();
expect(isStillValid).toEqual(true);
});
tap.test('should test concurrent session usage', async () => {
// Create multiple operations that use the session concurrently
const operations = [];
// Operation 1: Get accounts
operations.push(testBunqAccount.getAccounts());
// Operation 2: Get user info
operations.push(testBunqAccount.getUser().getInfo());
// Operation 3: List notification filters
const notification = new bunq.BunqNotification(testBunqAccount);
operations.push(notification.listPushFilters());
// Execute all operations concurrently
const results = await Promise.all(operations);
expect(results[0]).toBeArray(); // Accounts
expect(results[1]).toBeDefined(); // User info
expect(results[2]).toBeArray(); // Notification filters
console.log('Concurrent session operations completed successfully');
});
tap.test('should test session with different device names', async () => {
// Create new session with different device name
const differentDevice = new bunq.BunqAccount({
apiKey: sandboxApiKey,
deviceName: 'bunq-different-device',
environment: 'SANDBOX',
});
await differentDevice.init();
expect(differentDevice.userId).toBeTypeofNumber();
// Should be same user but potentially different session
expect(differentDevice.userId).toEqual(testBunqAccount.userId);
console.log('Different device session created for same user');
await differentDevice.stop();
});
tap.test('should test session with IP restrictions', async () => {
// Create session with specific IP whitelist
const restrictedAccount = new bunq.BunqAccount({
apiKey: sandboxApiKey,
deviceName: 'bunq-ip-restricted',
environment: 'SANDBOX',
permittedIps: ['192.168.1.1', '10.0.0.1']
});
try {
await restrictedAccount.init();
console.log('IP-restricted session created (may fail if current IP not whitelisted)');
await restrictedAccount.stop();
} catch (error) {
console.log('IP-restricted session failed as expected:', error.message);
}
});
tap.test('should test session error recovery', async () => {
// Test recovery from various session errors
// 1. Invalid API key
const invalidKeyAccount = new bunq.BunqAccount({
apiKey: 'invalid_key_12345',
deviceName: 'bunq-invalid-test',
environment: 'SANDBOX',
});
try {
await invalidKeyAccount.init();
throw new Error('Should have failed with invalid API key');
} catch (error) {
expect(error.message).toInclude('User credentials are incorrect');
console.log('Invalid API key correctly rejected');
}
// 2. Test with production environment but sandbox key
const wrongEnvAccount = new bunq.BunqAccount({
apiKey: sandboxApiKey,
deviceName: 'bunq-wrong-env',
environment: 'PRODUCTION',
});
try {
await wrongEnvAccount.init();
throw new Error('Should have failed with sandbox key in production');
} catch (error) {
console.log('Sandbox key in production correctly rejected');
}
});
tap.test('should test session token rotation', async () => {
// Get current session token
const apiContext = testBunqAccount['apiContext'];
const httpClient = apiContext.getHttpClient();
// Make multiple requests to test token handling
for (let i = 0; i < 3; i++) {
const accounts = await testBunqAccount.getAccounts();
expect(accounts).toBeArray();
console.log(`Request ${i + 1} completed successfully`);
// Small delay between requests
await new Promise(resolve => setTimeout(resolve, 100));
}
console.log('Multiple requests with same session token successful');
});
tap.test('should test session context migration', async () => {
// Test upgrading from old context format to new
const contextPath = '.nogit/bunqsandbox.json';
// Read current context
const currentContext = await plugins.smartfile.fs.toStringSync(contextPath);
const contextData = JSON.parse(currentContext);
expect(contextData).toHaveProperty('apiKey');
expect(contextData).toHaveProperty('environment');
expect(contextData).toHaveProperty('sessionToken');
expect(contextData).toHaveProperty('installationToken');
expect(contextData).toHaveProperty('serverPublicKey');
expect(contextData).toHaveProperty('clientPrivateKey');
expect(contextData).toHaveProperty('clientPublicKey');
console.log('Session context has all required fields');
// Test with modified context (simulate old format)
const modifiedContext = { ...contextData };
delete modifiedContext.savedAt;
// Save modified context
await plugins.smartfile.memory.toFs(
JSON.stringify(modifiedContext, null, 2),
contextPath
);
// Create new instance that should handle missing fields
const migratedAccount = new bunq.BunqAccount({
apiKey: sandboxApiKey,
deviceName: 'bunq-migration-test',
environment: 'SANDBOX',
});
await migratedAccount.init();
expect(migratedAccount.userId).toBeTypeofNumber();
console.log('Session context migration handled successfully');
await migratedAccount.stop();
});
tap.test('should test session cleanup on error', async () => {
// Test that sessions are properly cleaned up on errors
const tempAccount = new bunq.BunqAccount({
apiKey: sandboxApiKey,
deviceName: 'bunq-cleanup-test',
environment: 'SANDBOX',
});
await tempAccount.init();
// Simulate an error condition
try {
// Force an error by making invalid request
const apiContext = tempAccount['apiContext'];
const httpClient = apiContext.getHttpClient();
await httpClient.post('/v1/invalid-endpoint', {});
} catch (error) {
console.log('Error handled, checking cleanup');
}
// Ensure we can still use the session
const accounts = await tempAccount.getAccounts();
expect(accounts).toBeArray();
console.log('Session still functional after error');
await tempAccount.stop();
});
tap.test('should test maximum session duration', async () => {
// Sessions expire after 10 minutes of inactivity
const sessionDuration = 10 * 60 * 1000; // 10 minutes in milliseconds
console.log(`bunq sessions expire after ${sessionDuration / 1000} seconds of inactivity`);
// Check session expiry time is set correctly
const apiContext = testBunqAccount['apiContext'];
const session = apiContext.getSession();
const expiryTime = session['sessionExpiryTime'];
expect(expiryTime).toBeDefined();
console.log('Session expiry time is tracked');
});
tap.test('should cleanup session test resources', async () => {
// Destroy current session
await testBunqAccount.stop();
// Verify session was destroyed
try {
await testBunqAccount.getAccounts();
throw new Error('Should not be able to use destroyed session');
} catch (error) {
console.log('Destroyed session correctly rejected requests');
}
console.log('Session test cleanup completed');
});
export default tap.start();