Skip to content

Add exact ratio distribution for deterministic test data generation - #186

Merged
layoutd merged 70 commits into
trunkfrom
exact-ratio-distribution
Feb 4, 2026
Merged

layoutd merged 70 commits into
trunkfrom
exact-ratio-distribution

Conversation

@layoutd

@layoutd layoutd commented Oct 27, 2025 •

Copy link
Copy Markdown
Contributor

NOTE: This PR branches from #182 and will need merge conflict resolution with #184 and #185 (all modify the batch() method). The conflicts are trivial - just combining initialization calls.

Builds on: #182

Summary

Adds exact ratio distribution for coupon and refund generation in batch mode, eliminating variance from probabilistic distribution. Also includes critical bug fixes for weighted selection and counter restoration.

Current Behavior (Probabilistic)

With 100 orders at 0.5 coupon ratio:

  • ❌ Get 45-55 coupons (varies due to random chance)
  • ❌ Can't predict exact counts for testing

New Behavior (Deterministic)

With 100 orders at 0.5 coupon ratio:

  • ✅ Get exactly 50 coupons (pre-calculated distribution)
  • ✅ Predictable counts for testing and verification

Key Changes

  1. Exact Ratio Distribution - Pre-generates boolean/integer arrays with exact counts before shuffling
  2. Fixed Weighted Selection Bug - Corrected threshold calculation that was causing incorrect distribution
  3. Counter Restoration - Restores orders_remaining and distribution arrays when order generation fails

Benefits

  1. Deterministic Testing - Exact ratios make test verification trivial
  2. No Variance - Eliminates probabilistic randomness in batch operations
  3. Backwards Compatible - Single orders still use probabilistic approach
  4. Memory Efficient - Integer constants instead of strings for large batches
  5. Correct Distribution - Fixed threshold bug ensures ratios work as expected

Testing

# Test exact coupon ratio
ddev wp wc generate orders 100 --coupon-ratio=0.5 --status=completed
# Verify: Query wp_wc_orders_meta for _coupon_lines_hash, should be exactly 50

# Test exact refund ratio  
ddev wp wc generate orders 100 --status=completed --refund-ratio=0.4
# Verify: Should have exactly 20 full, 10 partial, 10 multi-partial refunds (40 total)

# Test counter restoration on failure
ddev wp wc generate orders 100 --coupon-ratio=0.5 --refund-ratio=0.4
# Intentionally cause some failures, verify counters restore correctly
Original Implementation Details

Coupon Distribution

Pre-generates an array of boolean flags:

// 100 orders, 0.5 ratio → exactly 50 true, 50 false
[true, true, ..., false, false]
shuffle($flags);

Odd number handling: Uses round() for non-exact splits:

  • 11 orders @ 0.5 ratio → round(11 × 0.5) = round(5.5) = 6 with coupons, 5 without

Refund Distribution

Pre-generates refund type flags with exact split using integer constants for memory efficiency:

// Integer constants for memory optimization
const REFUND_TYPE_NONE = 0;
const REFUND_TYPE_FULL = 1;
const REFUND_TYPE_PARTIAL = 2;
const REFUND_TYPE_MULTI = 3;

// 100 orders, 0.4 refund ratio → 40 refunds total:
// - 20 full (50%)
// - 10 single partial (25%)
// - 10 multi-partial (25%)
[1, 1, ..., 2, 2, ..., 3, 3, ..., 0, 0, ...]
shuffle($flags);

Odd number handling: Remainder goes to multi-partial:

  • 11 orders @ 0.4 ratio → 4 total refunds (rounded)
    • round(4 × 0.5) = 2 full
    • round(4 × 0.25) = 1 partial
    • 4 - 2 - 1 = 1 multi (remainder)
    • 11 - 4 = 7 none

Memory efficiency: For 20,000 orders, integers use ~160 KB vs strings ~160-320 KB, plus better CPU cache locality.

layoutd and others added 30 commits October 23, 2025 01:13
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
- Add detailed logging when orders cannot be refunded due to empty line items
- Consolidate refund creation error logs into single formatted message
- Add error logging for invalid order instance check

These improvements will help diagnose why some completed orders aren't receiving refunds when --refund-ratio=1.0 is specified.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Adds check to ensure refund amount is greater than 0 before calling wc_create_refund(). This prevents "Invalid refund amount" errors that occur when:
- Orders have 100% discount coupons (total = $0)
- Line items have $0 totals
- Calculation results in 0 or negative amount

Logs order ID, calculated amount, and order total when skipping refund.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
The "Invalid refund amount" error occurred because calculated refund amounts slightly exceeded the available order total due to rounding errors in tax calculations.

Changes:
- Calculate maximum refundable amount (order total - already refunded)
- Cap refund amount to maximum available before calling wc_create_refund()
- Round both calculated refund and max refund to 2 decimal places
- Improve error logging to show order total and already refunded amounts

Example of the issue:
- Order total: $24851.03
- Calculated refund (with 3 decimal tax): $24851.04
- Result: $0.01 over limit → "Invalid refund amount" error

This fix ensures refunds never exceed the mathematically available amount, preventing WooCommerce validation errors.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
…partial refunds stay under 50%

For full refunds, use the order's actual total instead of summing line items to avoid rounding discrepancies that created tiny 0.01 refunds. For partial refunds, ensure the total stays below 50% of the order total by removing items if needed, preventing two partial refunds from fully refunding an order.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@layoutd
layoutd marked this pull request as ready for review October 30, 2025 00:53
Base automatically changed from add-order-coupon-refund-ratios to trunk October 30, 2025 12:33

@chihsuan chihsuan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work on this feature! @layoutd Confirmed that coupons/refunds are generated correctly. 👍

I wanted to add a potential enhancement to this feature. Instead of pre-generating arrays, maybe we can calculate probability dynamically using "selection without replacement", something like this:

  // Initialize counters (O(1) memory)
  $coupons_remaining = isset( $args['coupon-ratio'] )
      ? (int) round( $amount * floatval( $args['coupon-ratio'] ) )
      : 0;
  $orders_remaining = $amount;

  for ( $i = 1; $i <= $amount; $i++ ) {
      $include_coupon = null;

      if ( isset( $args['coupon-ratio'] ) ) {
          // Probability = remaining_coupons / remaining_orders
          // Guarantees exact count while maintaining random distribution
          $include_coupon = ( wp_rand( 1, $orders_remaining ) <= $coupons_remaining );
          if ( $include_coupon ) {
              $coupons_remaining--;
          }
      }

      $orders_remaining--;

      // ... generate order
  }

The approach might be worth considering for the elimination threshold.

@layoutd

layoutd commented Feb 4, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the suggestion @chihsuan, I've implemented the changes to improve . As a follow-up PR, it would be interesting to add unit tests to cover all generation and edge cases.

@layoutd
layoutd merged commit abfb15b into trunk Feb 4, 2026
@layoutd
layoutd deleted the exact-ratio-distribution branch February 4, 2026 12:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants