modified gitignore to include config files

This commit is contained in:
Bojan Kucera 2025-06-05 17:59:04 -04:00
parent 717ea0cd4c
commit b23ca42f4d
5 changed files with 347 additions and 1 deletions

1
.gitignore vendored
View file

@ -7,7 +7,6 @@ node_modules/
dist/
build/
.next/
*.js
*.js.map
*.d.ts

View file

View file

@ -0,0 +1,52 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./src/**/*.{html,ts}",
],
theme: {
extend: {
colors: {
primary: {
50: '#eff6ff',
100: '#dbeafe',
200: '#bfdbfe',
300: '#93c5fd',
400: '#60a5fa',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
800: '#1e40af',
900: '#1e3a8a',
950: '#172554',
},
success: {
50: '#f0fdf4',
100: '#dcfce7',
200: '#bbf7d0',
300: '#86efac',
400: '#4ade80',
500: '#22c55e',
600: '#16a34a',
700: '#15803d',
800: '#166534',
900: '#14532d',
950: '#052e16',
},
danger: {
50: '#fef2f2',
100: '#fee2e2',
200: '#fecaca',
300: '#fca5a5',
400: '#f87171',
500: '#ef4444',
600: '#dc2626',
700: '#b91c1c',
800: '#991b1b',
900: '#7f1d1d',
950: '#450a0a',
},
},
},
},
plugins: [],
}

183
test/integration/setup.js Normal file
View file

@ -0,0 +1,183 @@
/**
* Integration Test Setup
*
* Sets up test containers and real database instances for integration testing.
* This file is executed before integration tests run.
*/
import { GenericContainer } from 'testcontainers';
import { MongoMemoryServer } from 'mongodb-memory-server';
let questdbContainer;
let postgresContainer;
let mongoContainer;
let mongoMemoryServer;
/**
* Global setup for integration tests
* Starts real database containers for testing
*/
beforeAll(async () => {
console.log('🚀 Starting integration test containers...');
try {
// Start QuestDB container
console.log('📊 Starting QuestDB container...');
questdbContainer = await new GenericContainer('questdb/questdb:7.3.10')
.withExposedPorts(9000, 8812, 9009)
.withEnvironment({
'QDB_TELEMETRY_ENABLED': 'false',
'QDB_LOG_LEVEL': 'ERROR'
})
.withStartupTimeout(60000)
.start();
// Start PostgreSQL container
console.log('🐘 Starting PostgreSQL container...');
postgresContainer = await new GenericContainer('postgres:15-alpine')
.withExposedPorts(5432)
.withEnvironment({
'POSTGRES_DB': 'trading_bot_test',
'POSTGRES_USER': 'trading_admin',
'POSTGRES_PASSWORD': 'trading_pass_test'
})
.withStartupTimeout(60000)
.start();
// Start MongoDB container
console.log('🍃 Starting MongoDB container...');
mongoContainer = await new GenericContainer('mongo:7-jammy')
.withExposedPorts(27017)
.withEnvironment({
'MONGO_INITDB_ROOT_USERNAME': 'trading_admin',
'MONGO_INITDB_ROOT_PASSWORD': 'trading_mongo_test',
'MONGO_INITDB_DATABASE': 'trading_bot_test'
})
.withStartupTimeout(60000)
.start();
// Update environment variables for tests
process.env.QUESTDB_HOST = questdbContainer.getHost();
process.env.QUESTDB_HTTP_PORT = questdbContainer.getMappedPort(9000).toString();
process.env.QUESTDB_PG_PORT = questdbContainer.getMappedPort(8812).toString();
process.env.QUESTDB_INFLUX_PORT = questdbContainer.getMappedPort(9009).toString();
process.env.POSTGRES_HOST = postgresContainer.getHost();
process.env.POSTGRES_PORT = postgresContainer.getMappedPort(5432).toString();
process.env.MONGODB_HOST = mongoContainer.getHost();
process.env.MONGODB_PORT = mongoContainer.getMappedPort(27017).toString();
console.log('✅ All containers started successfully!');
console.log(`📊 QuestDB: http://${process.env.QUESTDB_HOST}:${process.env.QUESTDB_HTTP_PORT}`);
console.log(`🐘 PostgreSQL: ${process.env.POSTGRES_HOST}:${process.env.POSTGRES_PORT}`);
console.log(`🍃 MongoDB: ${process.env.MONGODB_HOST}:${process.env.MONGODB_PORT}`);
}
catch (error) {
console.error('❌ Failed to start test containers:', error);
// Try to use MongoDB Memory Server as fallback
console.log('🔄 Falling back to MongoDB Memory Server...');
try {
mongoMemoryServer = await MongoMemoryServer.create({
instance: {
dbName: 'trading_bot_test'
}
});
const mongoUri = mongoMemoryServer.getUri();
const mongoUrl = new URL(mongoUri);
process.env.MONGODB_HOST = mongoUrl.hostname;
process.env.MONGODB_PORT = mongoUrl.port;
process.env.MONGODB_URI = mongoUri;
console.log('✅ MongoDB Memory Server started as fallback');
}
catch (fallbackError) {
console.error('❌ Failed to start MongoDB Memory Server:', fallbackError);
throw fallbackError;
}
// For other databases, use localhost defaults if containers fail
if (!questdbContainer) {
console.log('⚠️ Using localhost QuestDB (ensure it\'s running)');
process.env.QUESTDB_HOST = 'localhost';
process.env.QUESTDB_HTTP_PORT = '9000';
process.env.QUESTDB_PG_PORT = '8812';
process.env.QUESTDB_INFLUX_PORT = '9009';
}
if (!postgresContainer) {
console.log('⚠️ Using localhost PostgreSQL (ensure it\'s running)');
process.env.POSTGRES_HOST = 'localhost';
process.env.POSTGRES_PORT = '5432';
}
}
}, 120000); // 2 minutes timeout for container startup
/**
* Global cleanup for integration tests
* Stops all test containers
*/
afterAll(async () => {
console.log('🧹 Cleaning up integration test containers...');
const cleanup = async (container, name) => {
if (container) {
try {
await container.stop();
console.log(`${name} container stopped`);
}
catch (error) {
console.warn(`⚠️ Failed to stop ${name} container:`, error);
}
}
};
await Promise.all([
cleanup(questdbContainer, 'QuestDB'),
cleanup(postgresContainer, 'PostgreSQL'),
cleanup(mongoContainer, 'MongoDB')
]);
if (mongoMemoryServer) {
try {
await mongoMemoryServer.stop();
console.log('✅ MongoDB Memory Server stopped');
}
catch (error) {
console.warn('⚠️ Failed to stop MongoDB Memory Server:', error);
}
}
console.log('🎉 Integration test cleanup complete!');
}, 30000);
/**
* Wait for database services to be ready
*/
export const waitForServices = async (timeout = 30000) => {
const start = Date.now();
while (Date.now() - start < timeout) {
try {
// Check if QuestDB HTTP interface is ready
const questdbUrl = `http://${process.env.QUESTDB_HOST}:${process.env.QUESTDB_HTTP_PORT}/status`;
const response = await fetch(questdbUrl);
if (response.ok) {
console.log('✅ QuestDB is ready');
return;
}
}
catch (error) {
// Service not ready yet, continue waiting
}
await new Promise(resolve => setTimeout(resolve, 1000));
}
throw new Error('Services did not become ready within timeout');
};
/**
* Test utilities for integration tests
*/
export const integrationTestHelpers = {
/**
* Get QuestDB HTTP URL
*/
getQuestDBUrl: () => `http://${process.env.QUESTDB_HOST}:${process.env.QUESTDB_HTTP_PORT}`,
/**
* Get PostgreSQL connection string
*/
getPostgresUrl: () => `postgresql://${process.env.POSTGRES_USER}:${process.env.POSTGRES_PASSWORD}@${process.env.POSTGRES_HOST}:${process.env.POSTGRES_PORT}/${process.env.POSTGRES_DB}`,
/**
* Get MongoDB connection string
*/
getMongoUrl: () => {
if (process.env.MONGODB_URI) {
return process.env.MONGODB_URI;
}
return `mongodb://${process.env.MONGODB_USERNAME}:${process.env.MONGODB_PASSWORD}@${process.env.MONGODB_HOST}:${process.env.MONGODB_PORT}/${process.env.MONGODB_DATABASE}`;
},
/**
* Wait for services to be ready
*/
waitForServices
};
//# sourceMappingURL=setup.js.map

112
test/setup.js Normal file
View file

@ -0,0 +1,112 @@
"use strict";
/**
* Bun Test Setup File for Stock Bot Trading Platform
*
* Global test configuration and utilities available across all tests.
* This file is executed before each test via bunfig.toml preload.
*/
// Increase test timeout if needed (already configured in bunfig.toml)
// Bun.timeout = 30000;
// Store original console methods to allow restoration
const originalConsole = global.console;
// Mock console methods to reduce noise during tests
// These can be restored with testHelpers.restoreConsole()
console.log = () => { };
console.debug = () => { };
console.info = () => { };
console.warn = () => { };
console.error = () => { };
global.testHelpers = {
/**
* Sleep utility for async tests
*/
sleep: (ms) => new Promise(resolve => setTimeout(resolve, ms)),
/**
* Consistent mock timestamp for tests
*/
mockTimestamp: () => new Date('2024-01-01T12:00:00Z'),
/**
* Generate test OHLCV data
*/
generateTestOHLCV: (symbol = 'AAPL', overrides = {}) => ({
symbol,
open: 150.0,
high: 153.0,
low: 149.0,
close: 152.5,
volume: 10000,
timestamp: global.testHelpers.mockTimestamp(),
...overrides
}),
/**
* Generate test trade data
*/
generateTestTrade: (symbol = 'AAPL', overrides = {}) => ({
symbol,
price: 152.5,
size: 100,
timestamp: global.testHelpers.mockTimestamp(),
exchange: 'NASDAQ',
conditions: ['@', 'T'],
...overrides
}),
/**
* Generate test quote data
*/
generateTestQuote: (symbol = 'AAPL', overrides = {}) => ({
symbol,
bidPrice: 152.45,
bidSize: 200,
askPrice: 152.55,
askSize: 150,
timestamp: global.testHelpers.mockTimestamp(),
...overrides
}),
/**
* Create a mock logger
*/
mockLogger: () => ({
debug: () => { },
info: () => { },
warn: () => { },
error: () => { },
critical: () => { },
}),
/**
* Restore console methods
*/
restoreConsole: () => {
global.console = originalConsole;
}
};
// Set up spyOn utilities
// Similar to jest.spyOn but using Bun's built-in spy functionality
// This makes it easier to migrate from Jest
global.spyOn = function (object, method) {
const original = object[method];
const mock = function (...args) {
mock.mock.calls.push(args);
return mock.mockImplementation ? mock.mockImplementation(...args) : original.apply(object, args);
};
mock.mock = { calls: [] };
mock.mockClear = () => { mock.mock.calls = []; return mock; };
mock.mockReset = () => {
mock.mock.calls = [];
mock.mockImplementation = null;
return mock;
};
mock.mockImplementation = null;
mock.mockReturnValue = (value) => {
mock.mockImplementation = () => value;
return mock;
};
mock.mockResolvedValue = (value) => {
return mock.mockReturnValue(Promise.resolve(value));
};
mock.mockRejectedValue = (value) => {
return mock.mockReturnValue(Promise.reject(value));
};
object[method] = mock;
return mock;
};
//# sourceMappingURL=setup.js.map