XCode-Claude-Workflow/SoliCardsTests/GameEngine/MoveValidatorTests.swift
idev2025 0f989f5c86 feat: SoliCards v1.2.0 — native SwiftUI solitaire for iOS, iPadOS, macOS
Complete native rewrite of the web-based SoliCards game as a SwiftUI
multiplatform app targeting iOS 17+, iPadOS 17+, and macOS 14+.

Three solitaire variants (Klondike, Spider, FreeCell) with full game
rules, drag & drop, smart zoom layout, 6 themes, 4 difficulty levels,
SwiftData persistence, VoiceOver accessibility, and 57 unit tests.

Key features:
- MVVM + Protocol-Oriented Strategy architecture
- DragGesture with coordinate-space hit-testing (long press + drag)
- Smart zoom: cards auto-size to fit screen based on deepest column
- Landscape: 30% bigger cards with scrollable overflow (iOS)
- macOS: 120pt card cap, 92% height buffer for window resizing
- Auto-save, game resume, statistics tracking via SwiftData
- Privacy manifest, app icon, String Catalog, zero dependencies

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 07:33:52 -04:00

82 lines
3.3 KiB
Swift

import Testing
@testable import SoliCards
@Suite("MoveValidator Tests")
struct MoveValidatorTests {
@Test("Alternating color detection")
func alternatingColor() {
let redCard = Card(suit: .hearts, rank: .five, isFaceUp: true)
let blackCard = Card(suit: .spades, rank: .four, isFaceUp: true)
let anotherRed = Card(suit: .diamonds, rank: .three, isFaceUp: true)
#expect(MoveValidator.isAlternatingColor(redCard, with: blackCard))
#expect(MoveValidator.isAlternatingColor(blackCard, with: redCard))
#expect(!MoveValidator.isAlternatingColor(redCard, with: anotherRed))
}
@Test("Same suit detection")
func sameSuit() {
let spade1 = Card(suit: .spades, rank: .ace, isFaceUp: true)
let spade2 = Card(suit: .spades, rank: .king, isFaceUp: true)
let heart = Card(suit: .hearts, rank: .ace, isFaceUp: true)
#expect(MoveValidator.isSameSuit(spade1, as: spade2))
#expect(!MoveValidator.isSameSuit(spade1, as: heart))
}
@Test("Ascending rank (for foundations)")
func ascending() {
let ace = Card(suit: .hearts, rank: .ace, isFaceUp: true)
let two = Card(suit: .hearts, rank: .two, isFaceUp: true)
let three = Card(suit: .hearts, rank: .three, isFaceUp: true)
#expect(MoveValidator.isAscending(two, onto: ace))
#expect(MoveValidator.isAscending(three, onto: two))
#expect(!MoveValidator.isAscending(three, onto: ace))
#expect(!MoveValidator.isAscending(ace, onto: two))
}
@Test("Descending rank (for tableaus)")
func descending() {
let king = Card(suit: .spades, rank: .king, isFaceUp: true)
let queen = Card(suit: .hearts, rank: .queen, isFaceUp: true)
let jack = Card(suit: .spades, rank: .jack, isFaceUp: true)
#expect(MoveValidator.isDescending(queen, onto: king))
#expect(MoveValidator.isDescending(jack, onto: queen))
#expect(!MoveValidator.isDescending(king, onto: queen))
}
@Test("Can place on foundation")
func canPlaceOnFoundation() {
let ace = Card(suit: .hearts, rank: .ace, isFaceUp: true)
let two = Card(suit: .hearts, rank: .two, isFaceUp: true)
let twoSpades = Card(suit: .spades, rank: .two, isFaceUp: true)
let three = Card(suit: .hearts, rank: .three, isFaceUp: true)
// Ace on empty foundation
#expect(MoveValidator.canPlaceOnFoundation(ace, topCard: nil))
// Non-ace on empty foundation
#expect(!MoveValidator.canPlaceOnFoundation(two, topCard: nil))
// Two of hearts on ace of hearts
#expect(MoveValidator.canPlaceOnFoundation(two, topCard: ace))
// Two of spades on ace of hearts (wrong suit)
#expect(!MoveValidator.canPlaceOnFoundation(twoSpades, topCard: ace))
// Three on ace (skipping)
#expect(!MoveValidator.canPlaceOnFoundation(three, topCard: ace))
}
@Test("Ace and King detection")
func aceAndKing() {
let ace = Card(suit: .clubs, rank: .ace)
let king = Card(suit: .clubs, rank: .king)
let five = Card(suit: .clubs, rank: .five)
#expect(MoveValidator.isAce(ace))
#expect(!MoveValidator.isAce(king))
#expect(MoveValidator.isKing(king))
#expect(!MoveValidator.isKing(five))
}
}