Commit Graph

16 Commits

Author SHA1 Message Date
funman300 3ef4ecb747 test(data): client-side sync round-trip integration tests
CI / Test & Lint (push) Failing after 6m45s
CI / Release Build (push) Has been skipped
Server-side endpoint tests already exist in solitaire_server. This
adds the client-side counterpart: five integration tests in
solitaire_data/tests/sync_round_trip.rs that drive
SolitaireServerClient against an in-process axum::serve harness with
an in-memory SQLite database, covering:

- register_login_push_pull_round_trip — happy path: register, push
  non-default stats, pull from a fresh client, assert the merged
  payload reflects the pushed values
- pull_after_concurrent_pushes_merges_correctly — two clients on one
  user push different games_played values, verify the server-side
  merge returns the max
- unauthenticated_pull_returns_authentication_error — pull without
  tokens surfaces SyncError::Auth as expected
- jwt_refresh_on_401_succeeds — replace the access token with one
  whose exp is two hours stale (same signing key), pull triggers
  401 → /api/auth/refresh → retry, asserts the call ultimately
  succeeds
- pull_after_account_deletion_returns_default_or_error — register,
  push, delete via the trait, confirm the next push surfaces a
  result rather than panicking

keyring_core's mock store is installed once per process via Once;
each test uses a unique username so the shared store doesn't
cross-contaminate. Production code in sync_client.rs needed no
changes — the Box<dyn SyncProvider> design plus the mock keyring
were sufficient to drive every flow from outside.

solitaire_server is added as a path dev-dependency along with the
direct crates the harness needs (axum, sqlx, jsonwebtoken, uuid,
chrono, solitaire_sync); no runtime deps changed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 02:46:48 +00:00
funman300 e22fcadb22 feat(engine,assetgen): generate 52 individual card face PNGs
Replace the single shared face.png placeholder with 52 individual card
face images (120×168 px each), generated by the updated gen_art tool:

- solitaire_assetgen: add ab_glyph dep; rewrite gen_art to render each
  card with FiraMono rank characters, programmatic suit symbols (heart,
  spade, diamond, club drawn via circles/triangles), and standard pip
  layout for numbered cards (A–10) plus large face letter for J/Q/K.
- CardImageSet: replace single `face` handle with `faces: [[Handle; 13]; 4]`
  indexed by [suit][rank].
- card_sprite(): select the per-card face image by suit/rank indices.
- spawn/update_card_entity: suppress Text2d overlay when PNG faces are
  loaded (rank/suit baked into image); keep overlay in solid-colour
  fallback for tests.
- gen_sfx.rs: rename `gen` variable to `make` (reserved keyword in 2024).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 01:20:31 +00:00
funman300 66695683eb chore(workspace): upgrade rand 0.9, edition 2024, expand server tests
- rand "0.8" → "0.9": StdRng/SliceRandom API unchanged; 142 core tests pass
- edition "2021" → "2024" workspace-wide: no gen keyword conflicts found;
  204 tests (core + sync) pass clean with zero warnings
- ARCHITECTURE.md: Edition 2021 → Edition 2024 in header
- solitaire_server tests: add 5 new integration tests covering
  refresh-with-garbage-token, expired-refresh-token, push-without-token,
  delete-account-without-token, and leaderboard-authenticated-but-empty

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 00:36:12 +00:00
funman300 18ac5adef5 feat(engine): art pass — PNG assets, custom font, and keyring v4 upgrade
Art pass (Phase 4):
- Generate placeholder PNG assets: face.png, back_0–4.png, bg_0–4.png via
  solitaire_assetgen gen_art binary (16×16 RGBA, embedded via include_bytes!)
- Add FiraMono-Medium font (assets/fonts/main.ttf) embedded at compile time
- Add FontPlugin: loads font at startup, exposes FontResource; gracefully
  falls back to default handle when Assets<Font> absent (MinimalPlugins tests)
- Wire CardImageSet into card_plugin: face/back PNGs replace solid-colour
  sprites when available; tests continue using colour fallback via MinimalPlugins
- Wire BackgroundImageSet into table_plugin: bg PNGs replace solid-colour
  background; empty set inserted when Assets<Image> absent in tests
- Fix hint highlight system (input_plugin): tint sprite.color directly instead
  of replacing the whole Sprite (which would discard the image handle)
- Export FontPlugin, FontResource, CardImageSet from solitaire_engine::lib
- Register FontPlugin in solitaire_app before other plugins

Dependency upgrades (latest releases):
- keyring "2" → keyring "4" + keyring-core "1" (v4 split architecture into
  separate core library crate)
- auth_tokens.rs: Entry::new now returns Result; delete_password →
  delete_credential; NoDefaultStore error variant handled
- solitaire_app: add keyring::use_native_store(true) at startup for Linux
  Secret Service / macOS Keychain / Windows Credential Store selection

ARCHITECTURE.md: fix Edition 2025→2021, update asset pipeline section,
add FontPlugin/CardImageSet/BackgroundImageSet to plugin and resource tables,
update Section 14 to reflect actual include_bytes!() rendering approach,
add Decision Log entries for embedded PNG and font decisions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 00:30:55 +00:00
funman300 4bd562671e chore(data,engine,docs): remove Google Play Games Services sync backend
Deletes the solitaire_gpgs crate and all GPGS references from settings,
sync client, profile plugin, CLAUDE.md, and ARCHITECTURE.md. The
self-hosted server covers all sync needs without the Android-only backend.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 23:22:25 +00:00
funman300 71c0c273a1 chore(deps): migrate kira 0.9 → 0.12
- Import paths simplified: manager/tween modules re-exported from kira root
  (AudioManager, AudioManagerSettings, DefaultBackend, Tween all via kira::*)
- Volume::Amplitude removed; replaced with Value<Decibels> using a new
  amplitude_to_decibels() helper (20*log10 conversion, clamps to SILENCE)
- output_destination field removed from StaticSoundSettings; sounds routed
  to sub-tracks by calling TrackHandle::play() directly instead of
  AudioManager::play()
- set_volume() now accepts f32 (Decibels) not f64
- start_ambient_loop signature updated to take &mut Option<TrackHandle>

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 13:54:01 -07:00
funman300 21d0c289b5 chore(deps): migrate to Bevy 0.18
- BorderRadius is no longer a Component; moved into Node.border_radius
  field at all 15 spawn sites across 6 plugin files
- Events<T> renamed to Messages<T> in test code (12 files)
- KeyboardEvents SystemParam renamed to KeyboardMessages to match the
  MessageWriter rename done in the 0.17 hop
- WindowResolution::from((f32,f32)) removed; use (u32,u32) tuple in main.rs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 13:48:41 -07:00
funman300 648cd44387 chore(deps): migrate to Bevy 0.17
- Event/EventReader/EventWriter renamed to Message/MessageReader/MessageWriter
- add_event → add_message for all 67 call sites
- ScrollPosition changed to tuple struct ScrollPosition(Vec2)
- CursorIcon import moved from bevy::winit::cursor to bevy::window
- WindowResolution::from((f32,f32)) removed — use (u32,u32) tuple
- World::send_event → World::write_message in test code

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 13:04:44 -07:00
funman300 c8553dc8c5 chore(deps): migrate to Bevy 0.16, axum 0.8, and other package updates
- Bump bevy 0.15 → 0.16; fixes all breaking API changes:
  ChildBuilder → ChildSpawnerCommands, Parent → ChildOf,
  despawn_descendants → despawn_related::<Children>(),
  despawn_recursive → despawn (now recursive by default),
  EventWriter::send → write, Query::{get_single,get_single_mut}
  → {single,single_mut}, ChildOf::get → parent()
- Bump axum 0.7 → 0.8; remove axum::async_trait from FromRequestParts
- Bump tower_governor 0.4 → 0.8; fix GovernorLayer::new() API
- Bump jsonwebtoken 9 → 10 with rust_crypto feature only
- Bump thiserror 1 → 2, dirs 5 → 6, bcrypt 0.15 → 0.19,
  reqwest 0.12 → 0.13 (rustls feature rename)
- Regenerate .sqlx offline cache for sqlx compile-time query checks

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 12:31:12 -07:00
funman300 59a023ed5e chore(workspace): fix all clippy warnings in test code
Resolves 15 violations found by `cargo clippy --workspace --tests -D warnings`:
- Remove unused imports (Card, Rank) in cursor_plugin tests
- Replace absurd i32::MAX comparison with a meaningful >= 0 check
- Use range .contains() instead of manual >= && <= (manual_range_contains)
- Move impl FromRequestParts before test module in middleware.rs (items_after_test_module)
- Move _VEC3_REFERENCED const before test module in input_plugin.rs
- Convert runtime assert on constant to const { assert!(...) }
- Use .contains() instead of .iter().any() for slice membership
- Replace .get(...).is_none() with !.contains_key(...) in HashMap checks
- Collapse Default::default() + field assignment into struct literal initializers
  across solitaire_sync, solitaire_data, and solitaire_engine test helpers

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 18:02:27 +00:00
root 34ba4dc6ed feat(workspace): full server + sync implementation, all tests green
- solitaire_server: Axum auth, sync push/pull, leaderboard, daily
  challenge, account deletion, JWT middleware, rate limiting via
  tower_governor, SQLite migrations, health endpoint
- solitaire_server: expose build_test_router (no rate limiting) so
  integration tests work without a peer IP in oneshot requests
- solitaire_sync: SyncPayload, merge logic, shared API types
- solitaire_data: SyncProvider trait, LocalOnlyProvider,
  SolitaireServerClient, auth_tokens keyring integration, blanket
  Box<dyn SyncProvider> impl
- solitaire_data/settings: derive Default on SyncBackend (clippy fix)
- .sqlx/: offline query cache so server compiles without a live DB
- sqlx: removed non-existent "offline" feature flag
- keyring v2: fixed Entry::new() returning Result<Entry>
- sqlx 0.8: all SQLite TEXT columns wrapped in Option<T>
- Integration tests: max_connections(1) on in-memory pool so all
  connections share the same schema

All 191 tests pass; cargo clippy -D warnings clean.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 23:32:56 +00:00
funman300 adacdf533c feat(engine,assetgen): synthesized SFX + kira AudioPlugin
- New solitaire_assetgen crate with gen_sfx binary: synthesizes
  five 44.1kHz mono 16-bit PCM WAVs (flip/place/deal/invalid/fanfare)
  from an LCG noise source + sine/square synths. Output committed
  under assets/audio/.
- AudioPlugin (engine): embeds the WAVs via include_bytes!, decodes
  once with kira::StaticSoundData, plays on Draw / Move / NewGame /
  GameWon events. card_invalid is loaded but unused — wiring it
  needs a MoveRejectedEvent.
- AudioManager kept on the main thread (NonSend) since cpal is !Send
  on some platforms; degrades gracefully if no audio device present.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 22:48:58 -07:00
Solitaire Quest a8a323c6c3 chore(deps): replace bevy_egui+bevy_kira_audio with bevy_ui+kira, drop AssetServer 2026-04-23 21:02:46 -07:00
funman300 c393eab17d feat(engine): add resources, events, and GamePlugin event routing
Introduces the plumbing layer for Phase 3: GameStateResource wraps
solitaire_core::GameState, DragState tracks in-progress drags, and
SyncStatusResource holds runtime sync status. GamePlugin routes
Draw/Move/Undo/NewGame request events into GameState and emits
StateChangedEvent and GameWonEvent for downstream systems.

Also adds the Phase 3 implementation plan under docs/superpowers/plans/.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-23 16:15:38 -07:00
Solitaire Quest f84d7c5849 fix(workspace): add derives/docs per code review, remove unused thiserror from solitaire_sync 2026-04-23 11:04:15 -07:00
Solitaire Quest 684f07746d feat(workspace): initialize all seven crates with stubs and blank Bevy window
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 11:00:42 -07:00