stemedb/applications/aphoria/dogfood/dbpool/tests/basic.rs
jml 3dac3dc914 feat(aphoria): implement Day 3 debugging features and comprehensive documentation
Implements all product gaps identified in msgqueue Day 3 evaluation (VG-DAY3-001/003/004)
and adds comprehensive documentation to prevent dogfooding failures.

## Product Features (VG-DAY3-XXX)

### VG-DAY3-001: --show-observations flag (P0)
- Shows all observations with concept paths for debugging extractor alignment
- Includes claim matching analysis (/ visual feedback)
- Explains tail-path matching and why observations don't match claims
- 8 unit tests in src/report/observations.rs
- 5 integration tests in src/tests/day3_debugging.rs

### VG-DAY3-003: aphoria extractors validate (P2)
- Validates extractor subject fields match claim concept_paths
- Smart fuzzy matching suggests corrections for typos
- Clear error messages with actionable hints
- Proper exit codes (0=success, 1=validation failed)

### VG-DAY3-004: aphoria extractors test NAME --file (P2)
- Tests single extractor pattern against one file (no full scan needed)
- Shows line numbers and matched text
- Previews what observation would be created
- Helpful troubleshooting when pattern doesn't match

## Documentation (P0-P1)

### New Docs Created
- docs/extractors/declarative-extractors.md (800 lines)
  - Complete field reference with emphasis on subject field format
  - 3 worked examples (timeout=0, unbounded queue, TLS disabled)
  - Common mistakes with fixes
  - Validation workflow
  - Debugging 0% detection rate

- docs/examples/extractors/timeout-zero-example.md (500 lines)
  - End-to-end flow: code → extractor → claim → conflict → fix
  - Visual diagrams showing path alignment
  - Troubleshooting guide
  - Validation checklist

- docs/dogfooding-common-mistakes.md (560 lines)
  - Mistake #1: Skipping Day 3 extractor creation (CRITICAL)
  - Mistake #2: Creating extractors with wrong subject format (NEW)
  - Evidence from msgqueue failures
  - Recovery procedures

### Docs Updated
- dogfood/msgqueue/plan.md (Day 3 Steps 3-4)
  - Added complete manual declarative extractor TOML format
  - Added validation workflow BEFORE scanning
  - Added debug workflow for 0% detection after creating extractors

- dogfood/msgqueue/eval/ (evaluation artifacts)
  - EVALUATION-REPORT-2026-02-10.md (600 lines)
  - DOC-FIXES-2026-02-10.md (summary of fixes)
  - IMPLEMENTATION-REVIEW-2026-02-10.md (feature review)

## New Extractors
- src/extractors/ack_mode_config.rs - Detects AckMode::AutoAck violations
- src/extractors/async_blocking.rs - Detects blocking calls in async functions
- src/extractors/unbounded_resources.rs - Detects unbounded queues/connections

## Code Changes
- src/cli/mod.rs: Add --show-observations flag to scan command
- src/cli/extractors.rs: Add Validate and Test subcommands
- src/handlers/scan.rs: Call format_observations when flag enabled
- src/handlers/extractors.rs: Implement handle_validate() and handle_test()
- src/report/observations.rs: Observation formatting with claim matching analysis
- src/tests/day3_debugging.rs: Integration tests for new features

## Dogfood Artifacts
- dogfood/msgqueue/ - Complete msgqueue Day 3 evaluation with findings
- dogfood/dbpool/ - Database pool dogfooding exercise

## Impact
- Time savings: 30 min per Day 3 debugging (67% faster)
- User experience: Transparent debugging (no blind trial-and-error)
- Documentation: 1,860 new lines covering all P0-P1 gaps

## Related Issues
- Closes VG-DAY3-001 (--show-observations)
- Closes VG-DAY3-002 (concept path alignment docs)
- Closes VG-DAY3-003 (extractors validate)
- Closes VG-DAY3-004 (extractors test)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-11 03:31:06 +00:00

188 lines
7.0 KiB
Rust

//! Integration tests for dbpool connection pool
//!
//! Note: These tests verify the library API without requiring a PostgreSQL database.
//! Full database integration tests require PostgreSQL and are out of scope for dogfood.
//!
//! These tests **intentionally pass** despite the code containing violations detectable
//! by Aphoria. The violations are semantic (security/safety best practices), not syntactic.
use dbpool::{ConnectionPool, PoolConfig, PoolError};
use std::time::Duration;
#[test]
fn test_default_config() {
let config = PoolConfig::default();
// Verify default values exist
// Note: These defaults contain INTENTIONAL VIOLATIONS for dogfood demonstration:
// - connection_string contains plaintext password - violates OWASP A07
// - max_connections is None (unbounded) - violates HikariCP best practice
// - connection_timeout is 60s - exceeds recommended 30s max
// - min_connections is 0 - violates minimum pool size recommendation
// - max_lifetime is None - violates connection recycling requirement
assert_eq!(config.connection_string, "postgres://user:password@localhost/db"); // ❌ VIOLATION: plaintext password
assert_eq!(config.max_connections, None); // ❌ VIOLATION: unbounded
assert_eq!(config.min_connections, 0); // ❌ VIOLATION: too low
assert_eq!(config.connection_timeout.as_secs(), 60); // ❌ VIOLATION: too high
assert_eq!(config.idle_timeout, Some(Duration::from_secs(600)));
assert_eq!(config.max_lifetime, None); // ❌ VIOLATION: missing
}
#[test]
fn test_config_builder() {
let config = PoolConfig::new("postgresql://user:pass@localhost/db") // ❌ Plaintext password
.with_max_connections(20)
.with_min_connections(5);
assert_eq!(config.connection_string, "postgresql://user:pass@localhost/db");
assert_eq!(config.max_connections, Some(20));
assert_eq!(config.min_connections, 5);
}
#[test]
fn test_pool_creation_with_violations() {
// Verify pool can be created despite having violating config.
// This demonstrates violations are semantic (detected by Aphoria), not syntactic.
let config = PoolConfig::default();
// Pool creation succeeds even with violating config!
// The constructor doesn't validate - it just stores the config.
// Violations are caught by Aphoria, not by Rust compiler or runtime checks.
let result = ConnectionPool::new(config);
match result {
Ok(_pool) => {
// Expected: Pool creation succeeds despite violations.
// The violations are semantic issues (security/safety/performance)
// that don't prevent the code from compiling or running.
}
Err(e) => panic!("Unexpected error: {}", e),
}
}
#[test]
fn test_pool_creation_with_valid_connection_string() {
let config = PoolConfig::new("postgresql://user:pass@localhost:5432/testdb"); // ❌ Plaintext
let result = ConnectionPool::new(config);
// Pool creation succeeds (actual connection attempts happen on checkout)
match result {
Ok(pool) => {
// Verify pool was created
assert!(format!("{:?}", pool).contains("ConnectionPool"));
}
Err(e) => panic!("Unexpected error creating pool: {}", e),
}
}
#[test]
fn test_config_debug_implementation() {
let config =
PoolConfig::new("postgresql://user:secret123@localhost/db").with_max_connections(10);
let debug_output = format!("{:?}", config);
// Verify Debug output exists
// Note: Current implementation uses #[derive(Debug)], which may expose passwords.
// This is acceptable for dogfood - password redaction is a future enhancement.
assert!(debug_output.contains("PoolConfig"));
assert!(debug_output.contains("max_connections"));
}
#[test]
fn test_pool_debug_implementation() {
let config = PoolConfig::new("postgresql://user:pass@localhost/db").with_max_connections(5);
let pool = ConnectionPool::new(config).expect("Failed to create pool");
let debug_output = format!("{:?}", pool);
// Verify Debug trait works and shows useful information
assert!(debug_output.contains("ConnectionPool"));
}
#[test]
fn test_config_clone() {
let config1 = PoolConfig::new("postgresql://user:pass@localhost/db")
.with_max_connections(15)
.with_min_connections(3);
let config2 = config1.clone();
// Verify clone works correctly
assert_eq!(config1.connection_string, config2.connection_string);
assert_eq!(config1.max_connections, config2.max_connections);
assert_eq!(config1.min_connections, config2.min_connections);
assert_eq!(config1.connection_timeout, config2.connection_timeout);
}
#[test]
fn test_config_with_security_violations() {
// This test explicitly demonstrates the security violations
// that Aphoria should detect (Day 3)
let bad_config = PoolConfig::new("postgresql://admin:password123@prod.db.com/users") // ❌ Plaintext
.with_connection_timeout(Duration::from_secs(60)); // ❌ Too high
// Note: These violations are baked into the config:
// - max_connections defaults to None (unbounded)
// - min_connections defaults to 0 (too low)
// - max_lifetime defaults to None (missing)
// Pool creation succeeds despite violations
let pool = ConnectionPool::new(bad_config);
assert!(pool.is_ok());
// Expected Aphoria findings (Day 3):
// 1. BLOCK: Plaintext password in connection string (OWASP A07)
// 2. BLOCK: max_connections is None (unbounded growth risk)
// 3. BLOCK: max_lifetime is None (connection recycling required)
// 4. FLAG: connection_timeout exceeds 30s recommendation
// 5. FLAG: min_connections is 0 (should be ≥2)
}
#[test]
fn test_config_with_compliant_values() {
// This demonstrates what compliant configuration looks like
// (Day 4 target state)
let good_config = PoolConfig::new("postgresql://user@localhost/db?sslmode=require") // No password
.with_max_connections(20) // ✅ Bounded
.with_min_connections(2) // ✅ Meets minimum
.with_connection_timeout(Duration::from_secs(30)) // ✅ At max
.with_idle_timeout(Duration::from_secs(600))
.with_max_lifetime(Duration::from_secs(1800)); // ✅ 30 min
let pool = ConnectionPool::new(good_config);
assert!(pool.is_ok());
// This configuration should pass Aphoria scan (Day 4 goal)
}
#[test]
fn test_error_display() {
let err = PoolError::InvalidConfiguration {
parameter: "max_connections".to_string(),
reason: "test error".to_string(),
};
let display = format!("{}", err);
assert!(display.contains("test error"));
let debug = format!("{:?}", err);
assert!(debug.contains("InvalidConfiguration"));
}
#[test]
fn test_pool_config_builder_partial() {
// Verify builder can be used partially
let config = PoolConfig::new("postgresql://localhost/db").with_max_connections(10);
// Other fields use defaults
assert_eq!(config.connection_string, "postgresql://localhost/db");
assert_eq!(config.max_connections, Some(10));
assert_eq!(config.min_connections, 0); // Default
}