UNPKG

@idealic/poker-engine

Version:

Poker game engine and hand evaluator

228 lines (197 loc) 7.42 kB
#!/usr/bin/env node import { Command } from '@commander-js/extra-typings'; import { Pool } from 'pg'; import * as Poker from '../'; const program = new Command() .name('test-stats') .description('Test that poker_stats table has values for all HEADERS columns') .option( '-c, --connection <string>', 'PostgreSQL connection string (e.g., postgresql://user:pass@host:port/db)', process.env.DATABASE_URL ) .option('-t, --table <string>', 'Table name to check', 'poker_stats') .option('-l, --limit <number>', 'Maximum number of rows to check per column', parseInt, 1000) .option('-v, --verbose', 'Show detailed output') .version('1.0.0'); program.parse(); const options = program.opts(); if (!options.connection) { console.error('Error: PostgreSQL connection string is required'); console.error('Use -c option or set DATABASE_URL environment variable'); process.exit(1); } async function testStats() { const HEADERS = Poker.Stats.getColumnNames(); const pool = new Pool({ connectionString: options.connection }); try { console.log(`Connecting to database...`); const client = await pool.connect(); try { // First, check if table exists const tableCheckResult = await client.query( ` SELECT EXISTS ( SELECT FROM information_schema.tables WHERE table_name = $1 ) `, [options.table] ); if (!tableCheckResult.rows[0].exists) { console.error(`Error: Table '${options.table}' does not exist`); process.exit(1); } console.log(`Table '${options.table}' exists ✓`); // Get actual columns in the table const columnsResult = await client.query( ` SELECT column_name FROM information_schema.columns WHERE table_name = $1 ORDER BY ordinal_position `, [options.table] ); const actualColumns = new Set(columnsResult.rows.map(row => row.column_name)); console.log(`Found ${actualColumns.size} columns in table`); // Check which expected columns are missing from table schema const missingColumns: string[] = []; const presentColumns: string[] = []; for (const snakeCase of HEADERS) { if (actualColumns.has(snakeCase)) { presentColumns.push(snakeCase); } else { missingColumns.push(snakeCase); } } if (missingColumns.length > 0) { console.log(`\nMissing columns in table schema:`); missingColumns.forEach(col => console.log(` ❌ ${col}`)); } if (presentColumns.length > 0) { console.log(`\nColumns present in table: ${presentColumns.length}/${HEADERS.length}`); // Now check for non-zero values in present columns console.log( `\nChecking for non-zero values (checking first ${options.limit} rows per column)...` ); const columnsWithValues: Map<string, number> = new Map(); const columnsAllZero: string[] = []; for (const column of presentColumns) { // Skip non-numeric columns const nonNumericColumns = [ 'created_at', 'venue', 'table', 'street', 'player', 'currency', ]; if (nonNumericColumns.includes(column)) { const query = ` SELECT COUNT(*) as non_null_count FROM ( SELECT "${column}" FROM ${options.table} WHERE "${column}" IS NOT NULL LIMIT $1 ) sub `; try { const result = await client.query(query, [options.limit]); const count = parseInt(result.rows[0].non_null_count); if (count > 0) { columnsWithValues.set(column, count); } if (options.verbose) { console.log(` ${column}: ✓ (${count} non-null) [non-numeric]`); } } catch (err) { console.error(` Error checking column ${column}: ${err}`); } continue; } const query = ` SELECT COUNT(*) as non_zero_count FROM ( SELECT "${column}" FROM ${options.table} WHERE "${column}" IS NOT NULL AND "${column}" != 0 LIMIT $1 ) sub `; try { const result = await client.query(query, [options.limit]); const count = parseInt(result.rows[0].non_zero_count); if (count > 0) { columnsWithValues.set(column, count); } else { columnsAllZero.push(column); } if (options.verbose) { console.log(` ${column}: ${count > 0 ? `✓ (${count} non-zero)` : '✗ (all zero)'}`); } } catch (err) { console.error(` Error checking column ${column}: ${err}`); } } // Summary console.log(`\n=== SUMMARY ===`); console.log(`Total expected columns: ${HEADERS.length}`); console.log(`Columns in table: ${presentColumns.length}`); console.log(`Missing columns: ${missingColumns.length}`); console.log(`Columns with non-zero values: ${columnsWithValues.size}`); console.log(`Columns all zero: ${columnsAllZero.length}`); if (columnsAllZero.length > 0) { console.log(`\nColumns with no non-zero values (all zero or NULL):`); columnsAllZero.forEach(col => { const camelCase = HEADERS.find(h => h === col) || col; console.log(` ⚠️ ${col} (${camelCase})`); }); } // Check total row count const countResult = await client.query(`SELECT COUNT(*) FROM ${options.table}`); const totalRows = parseInt(countResult.rows[0].count); console.log(`\nTotal rows in table: ${totalRows.toLocaleString()}`); // Show sample of data if verbose if (options.verbose && totalRows > 0) { console.log(`\nSample row (first non-null values):`); const sampleQuery = ` SELECT ${presentColumns.map(c => `"${c}"`).join(', ')} FROM ${options.table} WHERE ${presentColumns.map(c => `"${c}" IS NOT NULL`).join(' OR ')} LIMIT 1 `; const sampleResult = await client.query(sampleQuery); if (sampleResult.rows.length > 0) { const sample = sampleResult.rows[0]; for (const [key, value] of Object.entries(sample)) { if (value !== null && value !== undefined) { const displayValue = typeof value === 'string' && value.length > 50 ? `${value.substring(0, 50)}...` : value; console.log(` ${key}: ${displayValue}`); } } } } // Exit code based on results if (missingColumns.length > 0 || columnsAllZero.length > 0) { process.exit(1); } } } finally { client.release(); } } catch (error) { console.error('Database error:', error); process.exit(1); } finally { await pool.end(); } } testStats().catch(error => { console.error('Error:', error); process.exit(1); });