From b23ca42f4d55acd1150b079d5cace066ef61244a Mon Sep 17 00:00:00 2001 From: Bojan Kucera Date: Thu, 5 Jun 2025 17:59:04 -0400 Subject: [PATCH] modified gitignore to include config files --- .gitignore | 1 - apps/dashboard/postcss.config.js | 0 apps/dashboard/tailwind.config.js | 52 +++++++++ test/integration/setup.js | 183 ++++++++++++++++++++++++++++++ test/setup.js | 112 ++++++++++++++++++ 5 files changed, 347 insertions(+), 1 deletion(-) create mode 100644 apps/dashboard/postcss.config.js create mode 100644 apps/dashboard/tailwind.config.js create mode 100644 test/integration/setup.js create mode 100644 test/setup.js diff --git a/.gitignore b/.gitignore index 4702ba0..911611e 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,6 @@ node_modules/ dist/ build/ .next/ -*.js *.js.map *.d.ts diff --git a/apps/dashboard/postcss.config.js b/apps/dashboard/postcss.config.js new file mode 100644 index 0000000..e69de29 diff --git a/apps/dashboard/tailwind.config.js b/apps/dashboard/tailwind.config.js new file mode 100644 index 0000000..00bc3e9 --- /dev/null +++ b/apps/dashboard/tailwind.config.js @@ -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: [], +} diff --git a/test/integration/setup.js b/test/integration/setup.js new file mode 100644 index 0000000..6495b25 --- /dev/null +++ b/test/integration/setup.js @@ -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 \ No newline at end of file diff --git a/test/setup.js b/test/setup.js new file mode 100644 index 0000000..cf52c79 --- /dev/null +++ b/test/setup.js @@ -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 \ No newline at end of file