-
Notifications
You must be signed in to change notification settings - Fork 0
refactor: extract main() into testable App structure for E2E testing #111
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor: extract main() into testable App structure for E2E testing #111
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
This refactoring makes the application testable by extracting the 600+ line main() function into a reusable App structure with dependency injection. Changes: - Created App struct with all application components - Added AppOptions for dependency injection (mock clients, vault bypass, etc.) - Implemented lifecycle methods: NewApp, StartHTTPServer, WaitForVaultUnseal, Start, Shutdown - Reduced main() from 600+ lines to ~80 lines - Split helpers into separate files for better organization: - app.go (873 lines): Core App structure and lifecycle - workers.go (121 lines): Worker queue processing - hyperliquid_helpers.go (177 lines): Hyperliquid utilities - main.go (80 lines): Simplified entry point Benefits: - Can inject mock 3commas client for E2E tests - Can bypass vault authentication via AppOptions.VaultSecrets - Can use in-memory SQLite via AppOptions.StoragePath - Each lifecycle phase independently testable - Enables Phase 2-4 of E2E testing implementation Documentation: - Added E2E_TESTING_IMPLEMENTATION_PLAN.md: 4-phase implementation roadmap This is Phase 1 of the E2E testing implementation plan. Application verified to build and start successfully with no functional changes.
619910f to
616baf4
Compare
Restored original implementations that were inadvertently simplified: - SubscribeBBO: Restore proper try-function pattern with errors.Join for better error reporting and nil-safe client checks - decorateVenueFlags: Restore nil handling for non-primary empty flags - cloneVenueFlags: Restore distinction between nil and empty map These changes restore the original behavior from the pre-refactor code.
|
@codex review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The original runWorker wrapped each work item in a 2-hour timeout context to prevent stuck workers when 3Commas rate limiting causes long waits. The refactor accidentally removed this timeout, which could cause workers to block indefinitely. Restored: - context.WithTimeout(ctx, 2*time.Hour) per work item - Removed unnecessary select statement (original used simple for loop)
|
@codex review |
|
Codex Review: Didn't find any major issues. Swish! ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Added: - 3commas-mock v0.1.0 dependency to go.mod - app_integration_test.go with integration tests Phase 2 deliverables: - TestApp_With3CommasMock: Verifies App can be created with mock - TestApp_StartWithMock: Tests app lifecycle with mock dependencies These tests validate that: - 3commas-mock can be imported and used - Mock server starts and responds correctly - SDK client can communicate with mock - App accepts injected ThreeCommasClient via AppOptions - Vault secrets can be bypassed for testing Tests tagged with '//go:build integration' to run separately from unit tests. This completes Phase 2 of the E2E testing implementation plan.
Created comprehensive E2E testing infrastructure: Files added: - e2e_harness.go: Reusable E2E test harness - e2e_basic_test.go: Basic E2E tests demonstrating the harness E2ETestHarness features: - Manages both 3commas-mock and hyperliquid-mock servers - Creates App with injected test dependencies - Generates Hyperliquid test credentials automatically - Provides helper methods: - Start()/Shutdown(): App lifecycle management - WaitForHTTPServer(): Polls until server is ready - WaitForDealProcessing(): Waits for deal in database - WaitForOrderSubmission(): Waits for order in Hyperliquid mock - WaitForOrderInDatabase(): Waits for order in database - APIGet()/APIPost(): HTTP API testing utilities - TriggerDealProduction(): Manually trigger deal sync E2E tests included: - TestE2E_BasicHarnessLifecycle: Verifies harness start/shutdown - TestE2E_DealToOrderFlow: Full flow from 3commas → Hyperliquid → API - TestE2E_APIListBots: API endpoint testing Benefits: - All components wired together (3commas, hyperliquid, database, API) - Proper synchronization with WaitFor helpers (no flaky sleeps) - Tests run in parallel for speed - Tagged with //go:build integration This completes Phase 3 of the E2E testing implementation plan.
|
@codex review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Fixed two issues found in Phase 3: 1. Missing import in e2e_basic_test.go - Added github.com/recomma/recomma/internal/api import - Required for api.ListOrdersOptions usage 2. HTTPAddr() returning wrong address for random ports - Added serverAddr field to store actual bound address - Updated StartHTTPServer() to use net.Listen first - Captures actual OS-assigned port when using :0 - Updated HTTPAddr() to return actual bound address - Uses mutex for thread-safe access This fixes the issue where tests using random ports (127.0.0.1:0) would try to connect to port 0 instead of the actual bound port.
|
@codex review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Fixed exit code handling in main() when WaitForVaultUnseal returns an error. Before: Always exited with status 0 on any error After: - Exit 1 on startup failures (port in use, vault errors, etc.) - Exit 0 only on context cancellation (clean shutdown) This ensures service managers can detect failed startups, especially when HTTP server fails to bind (e.g., port already in use). The previous code would exit with 0 in all cases, hiding deployment failures.
|
@codex review |
|
Codex Review: Didn't find any major issues. Already looking forward to the next diff. ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Added go-test-coverage GitHub Action to track and enforce test coverage: Changes: - Created .testcoverage.yml configuration file - Set minimum thresholds: 50% per file/package, 60% total - Excluded webui/ directory - Excluded generated code (*.gen.go, storage/sqlcgen/) - Updated .github/workflows/test.yml - Added coverage profile generation with -coverprofile - Integrated vladopajic/go-test-coverage@v2 action - Uses atomic coverage mode for accuracy Benefits: - Automatic coverage reporting on PRs - Prevents merging code that reduces coverage below thresholds - Excludes generated code and webui from coverage requirements - Provides visibility into project test coverage The action will run on all PRs and pushes to main that modify Go files.
- Split test.yml into two jobs: test and coverage - test job: runs tests without coverage (blocking) - coverage job: runs tests with coverage tracking (non-blocking) - Added continue-on-error: true to coverage check for visibility only - Coverage can now be monitored without failing builds
Added comprehensive E2E test suite covering: - Order scaling with multipliers (5x, 10x max clamp) - Multi-deal concurrent processing (3 bots, 3 deals) - Order recording and metadata tracking - Edge cases: - Very small orders near minimum notional - Zero quantity orders - Take-profit stacks (3-level TP) - API endpoint comprehensive checks Tests verify end-to-end flows: - 3commas mock → deal production → order scaling → Hyperliquid submission - Concurrent deal processing across multiple bots - Database persistence of orders, deals, and scaled order audits - API endpoints returning correct data All tests use t.Parallel() for efficient execution.
Updated test coverage configuration and added tests for: Coverage config (.testcoverage.yml): - Exclude main.go (untestable entry point) - Exclude config/ and debugmode/ (env-based, hard to test) - Exclude simple logging wrappers (log/log.go, storage/logging.go) New test files: - internal/api/context_test.go (3 tests, 100% coverage) - RequestContextMiddleware functionality - Request attachment to context - Edge cases (missing, wrong type) - internal/api/system_status_test.go (8 tests, ~100% coverage) - SystemStatusTracker creation - 3Commas API error tracking - Engine status tracking - Venue status management - Complete snapshot generation - internal/api/system_stream_test.go (15 tests, ~95% coverage) - Event subscription and publishing - Level filtering (debug, info, warn, error) - Event history for late joiners - Context cancellation cleanup - Multi-subscriber fan-out - History pruning by size and age - Subscriber buffer overflow handling - cmd/recomma/workers_test.go (14 tests, ~80% coverage) - processWorkItem with various error scenarios - processOrderItem with various error scenarios - Queue retry logic (5 retries then forget) - Worker shutdown handling - Special error handling (ErrDealNotCached, ErrOrderAlreadySatisfied, etc.) All tests use mocks to avoid external dependencies and run quickly. Tests verify both happy paths and error handling paths.
Removed workers_test.go: - Testing workers.go in isolation makes no sense - it's just glue code - The engine is a concrete type (*engine.Engine), not an interface - Can't mock it without rewriting the workers functions - Workers are already tested indirectly through E2E tests Fixed e2e_comprehensive_test.go: - Removed non-E2E tests that didn't follow the pattern: 3commas mock -> app -> hyperliquid mock - Removed: TestE2E_FillTracking_OrderRecording (only checked database) - Removed: TestE2E_EdgeCase_VerySmallOrder (no e2e verification) - Removed: TestE2E_EdgeCase_ZeroQuantity (no e2e verification) - Removed: TestE2E_APIEndpoints_ComprehensiveCheck (just API tests) - Kept only proper E2E tests with full flow verification - Added comments explaining the E2E flow for each test Fixed system_stream_test.go: - Removed unused ctrl variable declaration at line 299 - Variable was shadowed in the loop, causing compilation error
|
@codex review, especially if the tests make sense. |
|
Codex Review: Didn't find any major issues. 🎉 ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Added handler_test.go with 20+ test cases covering main API endpoints: Endpoints tested: - ListBots (empty, with data, auth required) - ListDeals (empty, with data) - ListOrders (empty, with data) - ListVenues (empty, with data) - UpsertVenue (create) - DeleteVenue - ListVenueAssignments (empty, with data) - UpsertVenueAssignment - DeleteVenueAssignment - ListBotVenues (empty, with data) - GetSystemStatus - ListOrderScalers (empty, with data) Test pattern: - Created stubHandlerStore implementing Store interface - Used httptest for request/response testing - Tests both happy paths and edge cases - Verified authentication requirements - Type-asserted responses to check status codes (200, 204, 401, etc.) This should significantly improve coverage for internal/api/handler.go (from 14.8% to estimated 50-60%). Tests are fast (no DB, just in-memory stub) and cover: - Empty result sets - Results with data - CRUD operations - Authentication checks
- add the ListThreeCommasBotEventsForAPI query so sqlc handles filters/pagination used by ListOrders - update storage/api.go to call the generated helper and translate options (including cursor and prefix handling) into sqlc params - extend TestListOrdersSurfacesLatestModify to persist bot-event logs, fetch with IncludeLog, and assert both bot-event and submission log entries are emitted Co-authored-by: Codex <[email protected]>
- add TestE2E_VenueCRUDAndAssignments to drive venue CRUD + assignments through the harness with real sqlite/storage instead of stubs - extend the harness with a generic APIRequest helper so PUT/DELETE calls reuse the shared client - drop the old stub-based venue tests from internal/api/handler_test.go now that the API behavior is proven via the E2E flow Co-authored-by: Codex <[email protected]>
- expand the existing venue CRUD E2E to hit all 400/404/409 validation branches, including a dedicated unassigned venue to verify missing-assignment deletes - add a new E2E test that boots the harness, closes storage, and drills every venue endpoint to exercise the 500/error logging paths - reuse the harness in the basic E2E suite to seed deterministic bots/deals so `/api/bots` and `/api/deals` pagination + filters cover the storage cursor logic Co-authored-by: Codex <[email protected]>
- generalize the price-source multiplexer behind a tiny bboSubscriber interface (and adapt app.go to wrap the existing WS clients) so the helper can be unit-tested - add unit tests for the multiplexer failover logic, flag cloning/decorating, and the sentinel wallet heuristic to finally exercise all helper branches Co-authored-by: Codex <[email protected]>
- refactor `runWorker`/`processWorkItem` to depend on a tiny `dealHandler` interface so the logic can be exercised without spinning up a full engine - wire the real engine into the interface from `app.go` (behavior unchanged) - add a `stubDealHandler`-backed test that drives `processWorkItem` through the five retries + final forget path, covering the previously untested branch Co-authored-by: Codex <[email protected]>
- seed in-memory deal/order state with stale + active/recent entries and assert CleanupStaleDeals only removes the inactive, aged deal (and its orders) Co-authored-by: Codex <[email protected]>
- hook the fill tracker directly to the queue emitter so it can recreate take-profits immediately after Hyperliquid reports a base fill, and teach it to reconcile a single snapshot from UpdateStatus - have the engine skip emitting any take-profit action when the fill snapshot is missing or the net position is zero, preventing reduce-only submissions while flat - ensure the emission planner drops any targets once adjustActionWithTracker returns ActionNone, so replay/cancel scenarios no longer enqueue spurious work - update combo of E2E/unit tests (including the new regression) to require an actual submission before asserting TP presence and to confirm UpdateStatus emits a TP after a base fill Co-authored-by: Codex <[email protected]>
Co-authored-by: Codex <[email protected]>
- add a unit test that feeds Hyperliquid’s “A” sell side flag into ordersMatch to reproduce the take-profit modify churn from bug_2025-11-29 - teach ordersMatch to accept both “S” and “A” for sell orders so the test (and production modify verification) succeed Co-authored-by: Codex <[email protected]>
- Added reusable slog handler plumbing (pkg/sqllogger, fan-out/group-filter helpers, new config knobs) and wired it into the app so operators can persist logs to app_logs, filter by group, and control DB log level. - Storage now exposes/uses sqlc-backed log inserts (with tests), e2e harness disables DB logging to avoid flakiness, and specs/log_persistence.adoc documents the new flags/defaults. Co-authored-by: Codex <[email protected]>
- add regression test (TestUpdateStatus_RecreatesTakeProfitBeforeExistingStatusArrives) capturing the reboot/resync scenario where the base status replays before the TP status - skip `ensureTakeProfit` when `LoadHyperliquidStatus` reports `open`/`live` so we don’t enqueue duplicate reduce-only orders during restart or status refresh - document the incident in `bugs/bug_2025-11-30_tp_reconcile_reboot.md` Co-authored-by: Codex <[email protected]>
- log SignalComplete/Release at INFO only when freeing capacity or waking queued workflows, otherwise drop to DEBUG - return metadata from resetWindowIfNeeded/tryGrantWaiting so callers know when the limiter actually progressed and window watcher only wakes queues after a reset Co-authored-by: Codex <[email protected]>
- stop snapshot recompute from trusting 3Commas fills until HL statuses arrive and auto-close stale buys when the deal is finished - teach Hyperliquid cancel path to treat “already canceled/filled” responses as satisfied and document the regressions - add regression tests and bug reports covering the scenarios Co-authored-by: Codex <[email protected]>
- add optional error reporter w/ default stderr logging and wrap worker contexts with context.WithoutCancel - report insert errors instead of dropping them and add tests that cover detached contexts + error hooks Co-authored-by: Codex [email protected]
…failure - document the hydration gate regression under bugs/bug_2025-12-02_status_refresh_hydration.md - add TestReconcileTakeProfits_RecoversAfterStatusRefreshFailure that reproduces the startup-refresh failure - teach filltracker to track a hydration epoch per reduce-only order and automatically clear the gate once later Hyperliquid status replays cover every pending TP, so ReconcileTakeProfits resumes without restarting Co-authored-by: Codex <[email protected]>
- keep a reference to the multi handler used for the main slog logger so we can detach sinks dynamically - add MultiHandler.Remove and call it before closing the sqlite log handler, preventing post-close slog errors during shutdown Co-authored-by: Codex <[email protected]>
Co-authored-by: Codex <[email protected]>
- call a.RateLimiter.Stop() in App.Shutdown so the background window reset watcher is torn down cleanly Co-authored-by: Codex <[email protected]>
feat(logging): persist slog output to sqlite with filter controls
- split setup vs settings initialization so the API fetch stops looping - memoize loadVenuesFromAPI without onVenuesLoaded by using a ref, preventing perpetual apiLoading Co-authored-by: Codex <[email protected]>
|
@codex review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // Create engine | ||
| a.Engine = engine.NewEngine(a.ThreeCommasClient, | ||
| engine.WithStorage(a.Store), | ||
| engine.WithEmitter(a.OrderEmitter), | ||
| engine.WithFillTracker(a.FillTracker), | ||
| engine.WithOrderScaler(a.OrderScaler), | ||
| engine.WithRateLimiter(a.RateLimiter), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wire order scaler before engine creation
The engine is created with WithOrderScaler(a.OrderScaler) while a.OrderScaler is still nil, because the scaler is only instantiated later in Start (~line 765). As a result every engine run sees a nil scaler and applyScaling short-circuits, so order scaling is silently disabled for all requests (no size adjustments or venue minimum enforcement). Instantiate the scaler before calling engine.NewEngine or reattach it so scaling remains active.
Useful? React with 👍 / 👎.
This refactoring makes the application testable by extracting the 600+ line main() function into a reusable App structure with dependency injection.
Changes:
Benefits:
Documentation:
This is Phase 1 of the E2E testing implementation plan. Application verified to build and start successfully with no functional changes.