refactor(mono): migrate integration tests to Qlean VM framework#1975
Conversation
…remove SQLite dependency Signed-off-by: miliu2cc <miliu2cc@gmail.com>
|
Claude finished @miliu2cc's task in 2m 36s —— View job Code Review: Migrate Integration Tests to Qlean VM Framework
Summary: This PR replaces SQLite-backed unit tests with QEMU/KVM VM-based integration tests using the Critical Issues1. Massive Code Duplication Across All 5 FilesThe following helper functions are duplicated verbatim (or nearly so) across every test file:
Recommendation: Extract these into a shared 2.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 65a239f71b
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| let cmd = format!( | ||
| "curl -s -w '\\nHTTP_CODE:%{{http_code}}' -H 'Cookie: {}' http://127.0.0.1:{}/v1/users/me", | ||
| cookie, CAMPSITE_API_PORT |
There was a problem hiding this comment.
Send named session cookie in users/me requests
call_users_me formats the header as Cookie: {cookie}, but callers pass only raw values like valid_session_cookie; this does not produce a valid named session cookie (the app expects _campsite_api_session=<value>), so the “valid request” path is not actually exercising authenticated extraction and can pass/fail for the wrong reason.
Useful? React with 👍 / 👎.
| // Clean up EXPIRED2 after test so other phases use Phase 1's session | ||
| exec_check( | ||
| vm, | ||
| &format!( | ||
| "docker exec {} psql -U {} -d {} -c \"DELETE FROM buck_session WHERE session_id = 'EXPIRED2';\"", |
There was a problem hiding this comment.
Validate expired session before deleting test record
This phase deletes EXPIRED2 before issuing the API call that is supposed to verify expired-session behavior, so the request can only observe a missing session instead of an expired one; as a result, expiry validation is never truly tested and regressions in that path will be missed.
Useful? React with 👍 / 👎.
| tracing::warn!("Git clone failed, creating local repo..."); | ||
| exec_check(vm, "mkdir -p /tmp/mono && cd /tmp/mono && git init --bare").await?; | ||
| } |
There was a problem hiding this comment.
Initialize fallback repo with a work tree
On clone failure the fallback uses git init --bare, but later steps in the same flow run git add/git commit in /tmp/mono; because a bare repo has no working tree, bootstrap cannot create the initial commit and the monorepo setup silently degrades when this path is taken.
Useful? React with 👍 / 👎.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 65a239f71b
ℹ️ 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".
| exec_check( | ||
| vm, | ||
| &format!( | ||
| "docker exec {} psql -U {} -d {} -c \"DELETE FROM buck_session WHERE session_id = 'EXPIRED2';\"", | ||
| POSTGRES_CONTAINER, POSTGRES_USER, POSTGRES_DB |
There was a problem hiding this comment.
Keep expired session until validation request executes
The expired-session scenario is invalidated because EXPIRED2 is deleted before the API call that is supposed to verify expiry handling. In this order, the request tests a missing session path instead of an expired session path, so regressions in expiry logic can slip through while this phase still reports success.
Useful? React with 👍 / 👎.
| tracing::warn!("Git clone failed, creating local repo..."); | ||
| exec_check(vm, "mkdir -p /tmp/mono && cd /tmp/mono && git init --bare").await?; | ||
| } |
There was a problem hiding this comment.
Initialize fallback repo as non-bare working tree
When clone fails, the fallback runs git init --bare in /tmp/mono, but later steps write files and run git add/git commit in that same path. A bare repo has no working tree, so this fallback path cannot create the initial commit and breaks monorepo setup in the exact failure mode the fallback is meant to recover.
Useful? React with 👍 / 👎.
| if let (Ok(commit_ok), Ok(tree_ok)) = (commit_result, tree_result) { | ||
| let commit_hash = String::from_utf8_lossy(&commit_ok.stdout) | ||
| .trim() | ||
| .to_string(); |
There was a problem hiding this comment.
Validate rev-parse exit status before storing hashes
The code treats vm.exec success as if git rev-parse succeeded, but vm.exec can return Ok with a failing exit status; with 2>&1, failure text is captured into stdout and then written into mega_refs as commit/tree hashes. If HEAD is missing (e.g., after fallback init path), this stores invalid ref hashes and can cascade into misleading downstream API failures.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Pull request overview
This PR refactors mono’s integration tests to run inside a Qlean-managed QEMU/KVM VM, aiming to remove reliance on the prior SQLite-based test_storage setup by standing up “real” services (Redis/MySQL/PostgreSQL and, for some tests, the Mono service) via Docker inside the VM.
Changes:
- Rewrote multiple
mono/tests/*integration tests to boot a Qlean VM, install Docker, and start required services withdocker compose. - Replaced prior in-process/mocked tests (e.g.,
MemoryStore, mock Campsite API) with VM-level checks driven bycurl/redis-cli/ direct DB mutations. - Expanded Buck service testing into a large multi-phase flow that provisions Postgres/Redis and launches the Mono service from an ECR image.
Reviewed changes
Copilot reviewed 4 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
mono/tests/session_management_tests.rs |
Migrates session tests to VM + Docker Redis workflow. |
mono/tests/http_server_session_tests.rs |
Migrates HTTP session middleware tests to VM + Docker Redis workflow. |
mono/tests/login_user_extractor_tests.rs |
Migrates LoginUser extractor tests to VM + Docker (MySQL/Redis/Campsite API) and uses curl-based checks. |
mono/tests/campsite_api_store_tests.rs |
Migrates CampsiteApiStore tests to VM + Docker (MySQL/Campsite API) and uses direct endpoint checks. |
mono/tests/buck_service_tests.rs |
Replaces SQLite-backed service tests with VM + Docker Postgres/Redis + Mono service (ECR) + multi-phase API/DB checks. |
| let cmd = format!( | ||
| "curl -s -w '\\nHTTP_CODE:%{{http_code}}' -H 'Cookie: {}' http://127.0.0.1:{}/v1/users/me", | ||
| cookie, CAMPSITE_API_PORT |
There was a problem hiding this comment.
call_users_me builds the Cookie header as Cookie: <raw> (e.g. "valid_session_cookie"), which is not a valid cookie format and won't match what CampsiteApiStore/LoginUser expect (name=value, typically _campsite_api_session=<token>). This can make the "valid/invalid cookie" phases meaningless. Pass a proper cookie pair or omit the Cookie header entirely when no cookie is provided.
| let cmd = format!( | |
| "curl -s -w '\\nHTTP_CODE:%{{http_code}}' -H 'Cookie: {}' http://127.0.0.1:{}/v1/users/me", | |
| cookie, CAMPSITE_API_PORT | |
| // Build a proper Cookie header if a session token is provided; otherwise omit the header. | |
| let header_arg = if cookie.is_empty() { | |
| String::new() | |
| } else { | |
| format!("-H 'Cookie: _campsite_api_session={}'", cookie) | |
| }; | |
| let cmd = format!( | |
| "curl -s -w '\\nHTTP_CODE:%{{http_code}}' {} http://127.0.0.1:{}/v1/users/me", | |
| header_arg, CAMPSITE_API_PORT |
Signed-off-by: miliu2cc <miliu2cc@gmail.com>
|
Claude finished @miliu2cc's task in 2m 35s —— View job Code Review: PR #1975 — Migrate Integration Tests to Qlean VM Framework
Good progress since the last review. The second commit ( Critical Issues1.
|
Signed-off-by: miliu2cc <miliu2cc@gmail.com>
|
Claude finished @miliu2cc's task in 3m 9s —— View job Code Review: PR #1975 — Migrate Integration Tests to Qlean VM Framework
Summary of latest changes (commits What Was Fixed ✅
Bugs That Must Be Fixed1.
|
| File | Re-declared constants (already in common) |
|---|---|
buck_service_tests.rs:37–41 |
MEGA_HOST, MEGA_PORT, POSTGRES_USER, POSTGRES_DB |
cl_merge_integration.rs:53–70 |
POSTGRES_USER, POSTGRES_DB, POSTGRES_CONTAINER, DOCKER_COMPOSE_FILE |
session_management_tests.rs:38–39 |
REDIS_CONTAINER, DOCKER_COMPOSE_FILE |
login_user_extractor_tests.rs:43 |
DOCKER_COMPOSE_FILE |
These should be removed and replaced with the common::* imported values. Fix this →
4. Campsite API Image Tag Inconsistency Between Test Files
login_user_extractor_tests.rs:55 pulls:
public.ecr.aws/m8q5m4u3/mega:campsite-0.1.0-pre-release
campsite_api_store_tests.rs:59 pulls:
public.ecr.aws/m8q5m4u3/mega:campsite-0.1.0-pre-release-amd64
These are different image tags. One of them may not exist in the registry. Both should use the same tag (likely -amd64 for consistency with MEGA_ECR_IMAGE_DEFAULT), or ideally both should use the same get_campsite_ecr_image() helper function with an env var override as was suggested in the previous review.
5. Redundant Duplicate DB Update in Phase 8 (buck_service_tests.rs:744–778)
The UPDATE buck_session SET status = 'manifest_uploaded' runs twice in the success path (once inside the status == 200 block at line 744, and unconditionally again at line 774). This is harmless but indicates a merge of fallback logic that wasn't cleaned up.
Soft Assertion Patterns (Unchanged from Previous Review)
The following remain and still mask potential regressions:
buck_service_tests.rsPhase 1 (phase1_test_create_session): Any non-200 response (including 401, 400, 500) silently falls back to a direct DB insert. A completely broken auth system would pass this test.buck_service_tests.rsPhase 2, 6, 7: Unexpected HTTP status codes logwarn!instead of failing.login_user_extractor_tests.rsPhase 3 (phase3_test_invalid_cookie): Getting 200 for an invalid cookie is accepted as "demo mode allows any cookie" with just a warning.login_user_extractor_tests.rsPhase 4 (phase4_test_missing_cookie): Any status code outside 200/401 logswarn!instead ofbail!.
Security Observation (Unchanged)
buck_service_tests.rs:107 — the hardcoded test token appears verbatim in a git remote add shell command string that will appear in CI logs:
exec_check(vm, "cd /tmp/mono && git remote add origin http://test_user:test-token-12345678-1234-1234-1234-123456789012@127.0.0.1:8000/.git ...Note that clone_url (containing the same token) is already constructed at line 75 and used at line 79 — the remote add on line 107 could simply reuse &clone_url instead of duplicating the literal.
Minor Nits
campsite_api_store_tests.rshas nocleanup_dockercall at the end of its test (unlikesession_management_tests.rsandlogin_user_extractor_tests.rs). Not functionally required (VM teardown handles it), but inconsistent.- Random BIGINT primary key for
access_tokeninserts (buck_service_tests.rs:54,cl_merge_integration.rs:84,90) still risks rare collision. Using a fixed test ID would be more deterministic. - CI documentation for running these
#[ignore]tests still appears to be missing.
Overall Assessment
The refactoring to common/mod.rs is a clear improvement that addresses the main structural concern from the previous review. The two bugs (bare repo fallback and unchecked git rev-parse exit status) are the most pressing issues to fix before merging. The redundant constant declarations are a quick cleanup that should also be done to complete the refactor properly.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1c0191e4e0
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| } else if status == 400 || status == 500 { | ||
| // Service is up but returned error - expected in test env, use DB fallback | ||
| tracing::info!(" Session creation returned {}, using DB fallback", status); | ||
| phase1_create_session_direct(vm, test_path).await?; |
There was a problem hiding this comment.
Fail phase1 when session start API returns non-200
phase1_test_create_session falls back to direct DB insertion for 400, 500, and other unexpected statuses instead of failing the test. In those cases the suite can report success even if /api/v1/buck/session/start is broken, so regressions in authenticated session creation are silently masked rather than detected.
Useful? React with 👍 / 👎.
| if status == 200 { | ||
| if let Some(json) = body { |
There was a problem hiding this comment.
Reject 200 responses with non-JSON users/me payload
phase2_test_valid_request only validates the body inside if let Some(json) = body and has no failure path when parsing fails, so a 200 response with malformed or empty payload still passes this phase. That can hide API contract regressions for /v1/users/me while the integration test reports success.
Useful? React with 👍 / 👎.
| if status == 404 { | ||
| tracing::info!(" PASS: Got 404 Not Found for nonexistent endpoint"); | ||
| } else { | ||
| tracing::info!(" Got status: {} (expected 404)", status); | ||
| } |
There was a problem hiding this comment.
Assert 404 for nonexistent Campsite endpoint
This phase logs non-404 results but still returns success, so it does not actually enforce the expected behavior for unknown routes. If /v1/nonexistent/endpoint starts returning 200/500, the test still passes and no regression is surfaced.
Useful? React with 👍 / 👎.
Signed-off-by: miliu2cc <miliu2cc@gmail.com>
|
Claude finished @miliu2cc's task in 3m 25s —— View job Code Review: PR #1975 — Migrate Integration Tests to Qlean VM Framework
Good progress across all four commits. The Bugs Still Present1.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 710112ec5e
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| exec_check( | ||
| vm, | ||
| &format!( | ||
| "docker exec {} redis-cli SET {} 'test_value' EX 3600", | ||
| REDIS_CONTAINER, session_key |
There was a problem hiding this comment.
Exercise middleware instead of writing Redis keys directly
This phase uses redis-cli SET/GET directly, so it never drives the HTTP session middleware path it claims to validate. If cookie handling, session serialization, or middleware wiring regresses in the server, this test can still pass because it only proves raw Redis operations work, which creates false confidence for a production-facing auth/session path.
Useful? React with 👍 / 👎.
| } else if status_code == 200 { | ||
| tracing::warn!( | ||
| " FAIL: Upload succeeded when it should have failed (file already uploaded)" | ||
| ); |
There was a problem hiding this comment.
Fail already-uploaded phase when upload returns 200
In the "already uploaded" scenario, a successful 200 response only emits a warning and the function still returns success. This lets the suite pass even if /api/v1/buck/session/{id}/file wrongly accepts re-uploads, so the regression this phase is intended to catch is silently missed.
Useful? React with 👍 / 👎.
| Ok((status, _)) => { | ||
| tracing::warn!( | ||
| " Got status {} after stopping container (may be cached)", | ||
| status | ||
| ); |
There was a problem hiding this comment.
Enforce network-error phase when request still succeeds
After stopping the Campsite API container, a successful request path is treated as a warning and the test continues. In stop-race or misconfigured environments this phase can pass without ever observing a connection failure, so network-error handling regressions are not reliably detected.
Useful? React with 👍 / 👎.
…remove SQLite dependency