CasinoGames/Blackjack/BlackjackTests/BlackjackTests.swift

288 lines
10 KiB
Swift

//
// BlackjackTests.swift
// BlackjackTests
//
// Created by Matt Bruce on 12/17/25.
//
import Testing
@testable import Blackjack
import CasinoKit
struct BlackjackTests {
@Test func example() async throws {
// Write your test here and use APIs like `#expect(...)` to check expected conditions.
}
// MARK: - Hand Value Tests
@Test @MainActor func handValueCalculation() async throws {
var hand = BlackjackHand()
// Test empty hand
#expect(hand.value == 0, "Empty hand should have value 0")
// Test simple values
hand = BlackjackHand(cards: [
Card(suit: .spades, rank: .five),
Card(suit: .hearts, rank: .seven)
])
#expect(hand.value == 12, "5 + 7 should equal 12")
#expect(!hand.isSoft, "Hand without ace should not be soft")
// Test face cards
hand = BlackjackHand(cards: [
Card(suit: .clubs, rank: .king),
Card(suit: .diamonds, rank: .queen)
])
#expect(hand.value == 20, "King (10) + Queen (10) should equal 20")
// Test blackjack
hand = BlackjackHand(cards: [
Card(suit: .spades, rank: .ace),
Card(suit: .hearts, rank: .king)
])
#expect(hand.value == 21, "Ace + King should equal 21")
#expect(hand.isBlackjack, "Ace + King should be blackjack")
#expect(hand.isSoft, "Blackjack hand should be soft")
// Test soft hand (ace as 11)
hand = BlackjackHand(cards: [
Card(suit: .diamonds, rank: .ace),
Card(suit: .clubs, rank: .six)
])
#expect(hand.value == 17, "Ace + 6 should equal soft 17")
#expect(hand.isSoft, "Ace + 6 should be soft")
// Test hard hand (ace as 1)
hand = BlackjackHand(cards: [
Card(suit: .spades, rank: .ace),
Card(suit: .hearts, rank: .five),
Card(suit: .clubs, rank: .nine)
])
#expect(hand.value == 15, "Ace + 5 + 9 = 15 (ace counts as 1)")
#expect(!hand.isSoft, "Hand over 21 if ace=11 should be hard")
// Test bust
hand = BlackjackHand(cards: [
Card(suit: .diamonds, rank: .king),
Card(suit: .clubs, rank: .queen),
Card(suit: .hearts, rank: .five)
])
#expect(hand.value == 25, "10 + 10 + 5 = 25")
#expect(hand.isBusted, "Hand over 21 should be busted")
// Test multiple aces
hand = BlackjackHand(cards: [
Card(suit: .spades, rank: .ace),
Card(suit: .hearts, rank: .ace)
])
#expect(hand.value == 12, "Ace + Ace = 12 (one as 11, one as 1)")
#expect(hand.isSoft, "Two aces should be soft")
// Test three aces
hand = BlackjackHand(cards: [
Card(suit: .spades, rank: .ace),
Card(suit: .hearts, rank: .ace),
Card(suit: .clubs, rank: .ace)
])
#expect(hand.value == 13, "Three aces = 13 (one as 11, two as 1)")
#expect(hand.isSoft, "Three aces should be soft")
}
@Test @MainActor func handBlackjackDetection() async throws {
// Natural blackjack
var hand = BlackjackHand(cards: [
Card(suit: .spades, rank: .ace),
Card(suit: .hearts, rank: .ten)
])
#expect(hand.isBlackjack, "Ace + 10 should be blackjack")
// 21 with three cards is not blackjack
hand = BlackjackHand(cards: [
Card(suit: .spades, rank: .seven),
Card(suit: .hearts, rank: .seven),
Card(suit: .clubs, rank: .seven)
])
#expect(hand.value == 21, "Should have value 21")
#expect(!hand.isBlackjack, "21 with 3 cards is not blackjack")
// Split hand 21 is not blackjack
hand = BlackjackHand(cards: [
Card(suit: .spades, rank: .ace),
Card(suit: .hearts, rank: .king)
], bet: 100)
hand.isSplit = true
#expect(hand.value == 21, "Should have value 21")
#expect(!hand.isBlackjack, "21 after split is not blackjack")
}
@Test @MainActor func handSplitDetection() async throws {
// Splittable pair
var hand = BlackjackHand(cards: [
Card(suit: .spades, rank: .eight),
Card(suit: .hearts, rank: .eight)
])
#expect(hand.canSplit, "Pair of 8s should be splittable")
// Face cards of same rank can split
hand = BlackjackHand(cards: [
Card(suit: .spades, rank: .king),
Card(suit: .hearts, rank: .king)
])
#expect(hand.canSplit, "Pair of Kings should be splittable")
// Face cards of different ranks cannot split
hand = BlackjackHand(cards: [
Card(suit: .spades, rank: .king),
Card(suit: .hearts, rank: .queen)
])
#expect(!hand.canSplit, "King + Queen should not be splittable (different ranks)")
// Not splittable - different ranks
hand = BlackjackHand(cards: [
Card(suit: .spades, rank: .nine),
Card(suit: .hearts, rank: .ten)
])
#expect(!hand.canSplit, "9 + 10 should not be splittable")
// Not splittable - more than 2 cards
hand = BlackjackHand(cards: [
Card(suit: .spades, rank: .five),
Card(suit: .hearts, rank: .five),
Card(suit: .clubs, rank: .ace)
])
#expect(!hand.canSplit, "Hand with 3 cards should not be splittable")
}
@Test @MainActor func handDoubleDownDetection() async throws {
// Can double down with 2 cards
var hand = BlackjackHand(cards: [
Card(suit: .spades, rank: .five),
Card(suit: .hearts, rank: .six)
])
#expect(hand.canDoubleDown, "Should be able to double down with 2 cards")
// Cannot double down after already doubled
hand.isDoubledDown = true
#expect(!hand.canDoubleDown, "Should not be able to double down twice")
// Cannot double down with 3+ cards
hand = BlackjackHand(cards: [
Card(suit: .spades, rank: .five),
Card(suit: .hearts, rank: .six),
Card(suit: .clubs, rank: .two)
])
#expect(!hand.canDoubleDown, "Should not be able to double down with 3 cards")
}
@Test @MainActor func handHitDetection() async throws {
// Can hit with normal hand
var hand = BlackjackHand(cards: [
Card(suit: .spades, rank: .five),
Card(suit: .hearts, rank: .six)
])
#expect(hand.canHit, "Should be able to hit with 11")
// Cannot hit after standing
hand.isStanding = true
#expect(!hand.canHit, "Should not be able to hit after standing")
// Cannot hit when busted
hand = BlackjackHand(cards: [
Card(suit: .spades, rank: .king),
Card(suit: .hearts, rank: .queen),
Card(suit: .clubs, rank: .five)
])
#expect(!hand.canHit, "Should not be able to hit when busted")
// Cannot hit with blackjack
hand = BlackjackHand(cards: [
Card(suit: .spades, rank: .ace),
Card(suit: .hearts, rank: .king)
])
#expect(!hand.canHit, "Should not be able to hit with blackjack")
}
// MARK: - Card Value Tests
@Test func cardHiLoValues() async throws {
// Low cards (2-6) = +1
#expect(Card(suit: .spades, rank: .two).hiLoValue == 1)
#expect(Card(suit: .hearts, rank: .three).hiLoValue == 1)
#expect(Card(suit: .clubs, rank: .four).hiLoValue == 1)
#expect(Card(suit: .diamonds, rank: .five).hiLoValue == 1)
#expect(Card(suit: .spades, rank: .six).hiLoValue == 1)
// Neutral cards (7-9) = 0
#expect(Card(suit: .hearts, rank: .seven).hiLoValue == 0)
#expect(Card(suit: .clubs, rank: .eight).hiLoValue == 0)
#expect(Card(suit: .diamonds, rank: .nine).hiLoValue == 0)
// High cards (10, J, Q, K, A) = -1
#expect(Card(suit: .spades, rank: .ten).hiLoValue == -1)
#expect(Card(suit: .hearts, rank: .jack).hiLoValue == -1)
#expect(Card(suit: .clubs, rank: .queen).hiLoValue == -1)
#expect(Card(suit: .diamonds, rank: .king).hiLoValue == -1)
#expect(Card(suit: .spades, rank: .ace).hiLoValue == -1)
}
// MARK: - Game State Tests
@Test @MainActor func gameStateInitialization() async throws {
let state = GameState(settings: GameSettings())
// Initial state
#expect(state.balance == 10000, "Should start with $10,000")
#expect(state.currentBet == 0, "Should start with no bet")
#expect(state.playerHands.isEmpty, "Should start with no hands")
#expect(state.dealerHand.cards.isEmpty, "Should start with empty dealer hand")
// Check phase enum
switch state.currentPhase {
case .betting:
break // Expected
default:
Issue.record("Should start in betting phase")
}
}
@Test @MainActor func gameStateBetting() async throws {
let state = GameState(settings: GameSettings())
// Place a bet
state.placeBet(amount: 100)
#expect(state.currentBet == 100, "Bet should be placed")
#expect(state.balance == 9900, "Balance should decrease")
// Add to bet
state.placeBet(amount: 50)
#expect(state.currentBet == 150, "Bet should increase")
#expect(state.balance == 9850, "Balance should decrease more")
// Clear bet
state.clearBet()
#expect(state.currentBet == 0, "Bet should be cleared")
#expect(state.balance == 10000, "Balance should be restored")
}
@Test @MainActor func gameStateDealing() async throws {
let state = GameState(settings: GameSettings())
// Place bet and deal
state.placeBet(amount: 100)
await state.deal()
// Verify hands were dealt
#expect(state.playerHands.count == 1, "Should have 1 player hand")
#expect(state.playerHands[0].cards.count == 2, "Player should have 2 cards")
#expect(state.dealerHand.cards.count == 2, "Dealer should have 2 cards")
// Verify values are valid
#expect(state.playerHands[0].value >= 2, "Player hand should have valid value")
#expect(state.playerHands[0].value <= 21, "Player hand should not exceed 21 initially")
}
}