Repository navigation
test(analytics): fold the hand-rolled ConfigStore mock onto mocks.MockConfigStore - #1254
Conversation
…kConfigStore The analytics collector test carried a 145-method copy of config.StoreInterface, so every new store method needed a stub there as well as in internal/mocks. The collector only reads GetActivePurchaseHistory, so the test double now embeds the shared MockConfigStore and overrides just that method; the four other func fields it carried were never set by any test. Add a repo-wide guard in internal/mocks that fails when a _test.go type defines more than half of StoreInterface's methods, so a new full copy cannot appear. internal/server's mockConfigStoreForHealth is allowlisted until it is folded the same way; the allowlist fails on stale entries, so it can only shrink. Refs LeanerCloud#76 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Warning Review limit reached
This review includes 2 billable files and costs up to $0.50. Or wait 59 minutes for your next included review. View limit details
Comment |
Bring the branch up to date with main. Conflicted files: - internal/analytics/collector_test.go: main kept adding stubs to the hand-rolled analytics mockConfigStore (HistorySummaryAggregate from LeanerCloud#1200, ClaimCollectionInvocation from LeanerCloud#1099, the ReplaceRecommendations removal from LeanerCloud#1209, and the pgx import they need). This branch replaces that hand-rolled mock with one that embeds mocks.MockConfigStore, which already carries all of those methods on main. Kept the branch's version; main's stub edits are covered by the shared mock and drop out. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bring the branch up to date with main again; main moved on while the previous merge was being checked. Conflicted files: - internal/analytics/collector_test.go: main (LeanerCloud#1242) added a CancelExecutionAtomicTokenPinned stub to the hand-rolled analytics mockConfigStore. This branch replaces that mock with one embedding mocks.MockConfigStore, which main already gives the same method. Kept the branch's version; the stub is not needed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Merged origin/main into this branch twice: first 8f186a8, then c1f1781, because main moved while the first merge was being checked. New head: 9e7fd37. Conflict resolution (both merges, same file):
Checks on the merged head:
|
|
Independent review (IAmJSD's agent, fresh context, given only the diff, the issue and the PR text): VERDICT: REQUEST_CHANGES Blocking:
Non-blocking:
Attacked:
Body-stale:
Reviewer: rB12. Bar while the maintainers are away (set by IAmJSD as acting owner): one approving independent review on the head, two for money, auth and security paths. |
Brings the branch up to date with main so the PR is mergeable again. Conflicted files: - internal/analytics/collector_test.go: main edited the hand-rolled mockConfigStore stubs this branch deletes. LeanerCloud#1299 added GetMarketplaceListingAttempt and ResolveMarketplaceListingAttempt, and LeanerCloud#1217 removed GetPendingExecutionsTx. The branch's version is kept, where mockConfigStore embeds mocks.MockConfigStore and overrides only GetActivePurchaseHistory. Main's two PRs made the matching changes to mocks.MockConfigStore, so the embedded mock already has the new methods and lacks the removed one. The file is identical to the branch head. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Merged current main into the branch. New head: 091c979.
Checks:
|
|
Independent review (IAmJSD's agent, fresh context, given only the diff, the issue and the PR text): VERDICT: APPROVE Blocking:
Non-blocking:
Attacked:
Body-stale:
Reviewer: rB18. Bar while the maintainers are away (set by IAmJSD as acting owner): one approving independent review on the head, two for money, auth and security paths. |
|
Local verification at |
|
Merged by IAmJSD's agent at IAmJSD's request (IAmJSD is acting owner while the maintainers are away), under the bar IAmJSD set: 1 approving independent review(s) on |
Conflict: internal/analytics/collector_test.go. LeanerCloud#1254 folded the hand-rolled analytics mock onto mocks.MockConfigStore, so took main's file and dropped this branch's two stubs there; the shared mock already carries them from this branch's internal/mocks/stores.go change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LeanerCloud#1254 (on main) replaced the hand-rolled config-store mock in collector_test.go and dropped the now-unused pkg/common import. This branch's new commitment-type assertion uses common.CommitmentType, so the analytics test build broke after main was merged in although the merge was textually clean. Same assertion. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LeanerCloud#974 was squash-merged into main as 97d04ec, whose recommendations handler files are byte-identical to the LeanerCloud#974 branch tip (11ddfea) this branch was stacked on. The handler and page files were re-merged with that tip as the base, which keeps this branch's chunked-scan changes and main's later LeanerCloud#1266 Cache-Control change. In openapi.yaml the recommendations parameters take main's LeanerCloud#1100 list. collector_test.go takes main's LeanerCloud#1254 version, whose embedded mocks.MockConfigStore already has ListStoredRecommendationsChunk. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
collector_test.go takes main's LeanerCloud#1254 version: its hand-rolled ConfigStore mock now embeds mocks.MockConfigStore, which this branch already extends with ListMarketplaceListingsToPoll, RecordMarketplaceListingActive and SettleMarketplaceListing, so the three local stubs the branch had added are no longer needed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…vice (#1302) * fix(purchase,server): skip the $0 purchase confirmation and report a failed lazy DB init on /health An execution with nothing selected never reaches a cloud API, but both the single-account and the per-account fan-out paths still sent a "purchase confirmed" email listing zero commitments. Gate the confirmation on anyRecPurchased / committed, so it goes out only when a real commitment was created (full or partial success). When reinitializeAfterConnect fails after setting app.Config, ensureDB closes the pool and clears app.DB but leaves app.Config, and the config_store health check reported healthy over the closed pool. Snapshot dbConnected with the store pointers under dbMu and report unhealthy while lazy PostgreSQL init has not completed. Audit findings A05-015 and A06-009. A06-019 (commitment_type labels) follows separately. Refs #339 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(analytics): label snapshot commitment types from provider and service commitmentTypeFor matched only a service literally named "SavingsPlans", which purchase_history never stores, so every commitment was stored as "RI": AWS Savings Plans ("savingsplans" / "savings-plans-<type>") and GCP committed-use discounts included. Derive the type from (provider, service) in the pkg/common CommitmentType vocabulary, and widen the savings_snapshots CHECK in migration 000145 to match. The CHECK still admits the legacy 'RI' / 'SavingsPlan' literals so an old replica can write during a rolling deploy; existing rows are relabeled. Audit finding A06-019. Refs #339 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(purchase): make the #339 no-confirmation assertions checkable by the mock guard TestNoUnfailableMockAssertions could not resolve the receiver of two AssertNotCalled sites (a mock returned by a helper). Declare the mock directly in the new test, and in the approve test rely on the missing SendPurchaseConfirmation expectation, which fails the test on any send. Refs #339 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(analytics): restore the pkg/common import after #1254 #1254 (on main) replaced the hand-rolled config-store mock in collector_test.go and dropped the now-unused pkg/common import. This branch's new commitment-type assertion uses common.CommitmentType, so the analytics test build broke after main was merged in although the merge was textually clean. Same assertion. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(db): renumber savings_snapshots_commitment_type_vocabulary migration 000145 -> 000141 golang-migrate only applies versions above the database's current one, so PRs must merge in migration-number order. 000140-000144 had been taken by PRs that are now held on owner decisions (#1239, #1241, #1253). The ready PRs move to 000140-000145 so they can merge ahead of them, and the held ones will take the next free numbers when decided. Rename only: the SQL statements are unchanged, and the test version constants and the comments naming the migration follow the new number. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Root cause
Three test doubles implemented the whole of
config.StoreInterface(145 methods) by hand:internal/mocks.MockConfigStore,internal/analytics.mockConfigStoreandinternal/server.mockConfigStoreForHealth. Each new store method therefore needed a stub in all three places. Nothing stopped a fourth copy from appearing.Fix (first slice)
internal/analytics/collector_test.go:mockConfigStorenow embedsmocks.MockConfigStoreand overrides onlyGetActivePurchaseHistory, the one config-store read the collector makes (collector.go:96). The default staysnil, nil, as before. 527 lines of stubs are gone. The four other func fields (getPurchaseHistoryFunc,getAllPurchaseHistoryFunc,getPurchaseHistoryByPurchaseIDFunc,markPurchaseRevokedFunc) were never set by any test, so they go too. No test call site changed.internal/mocks/config_store_copies_guard_test.go(new): a repo-wide syntactic guard,TestNoHandRolledConfigStoreMocks. It reads theStoreInterfacemethod set frominternal/config/interfaces.go. It fails when any type declared in a_test.gofile defines more than half of those methods. Today the largest partial override defines 24 of 145 (internal/server.mockConfigStoreForExchange), and the threshold is 73, so there is plenty of margin.internal/server.mockConfigStoreForHealthis allowlisted until it is folded. The allowlist can only shrink: the guard fails if an entry no longer hand-rolls the interface or no longer exists.No production code changed.
internal/mocks/stores.gois untouched, to keep conflicts with the open branches that add store methods to a minimum.Refs #76
Verification
All of this is unit/mock evidence. The change is test-only.
Fail-before / pass-after for the guard:
internal/analytics/collector_test.gorestored:go test ./internal/mocks/ -run TestNoHandRolledConfigStoreMocksFAILS and reportsinternal/analytics.mockConfigStoreas a hand-rolledconfig.StoreInterfacetest double.Mutation probes:
mockConfigStoreForHealthis now reported as a copy.GetActivePurchaseHistoryoverride from the analytics wrapper makesTestCollectorCollectFAIL with a panic. The shared mock dispatches that method throughm.Calledwithout a default, so the wrapper's override really is what the collector tests exercise.No non-test Go files changed, so gocyclo has nothing to check.
Combined-tree verification. This branch was tested together with six other queued branches on a local, never-pushed tree:
queue/platform@77d116c6= origin/maina14dc8f2plus these heads, merged in this order:Linux, Go 1.26.9,
AWS_CONFIG_FILE=/dev/null AWS_SHARED_CREDENTIALS_FILE=/dev/null AWS_EC2_METADATA_DISABLED=true; Docker Postgres via testcontainers:go build ./...,go vet ./...,go vet -tags integration ./...and the fullgo test -race -short -count=1 ./....--build-tags integration, andgocyclo -over 10is silent on the changed non-test files. These ran atb61a9c30, which differs from77d116c6only by one.gitallowedline on fix/issue-1233.-tags integration -count=1):cmd/cleanup-lambda,internal/analytics,internal/auth,internal/database/postgres/migrationsandinternal/purchase.internal/apipasses, except two tests whose Postgres container didn't start, or whose migrations hit a timeout, under machine load. Rerun alone on the same tree, both pass:TestDeleteManagedPlanByExecutionStatusandTestNotifiedRow_RescheduleDoesNotOverwriteConcurrentCancel.internal/configpasses exceptTestPostgresStoreDB_ResolveStaleRIExchange_ValidatesCloudCheck, which fails the same way on unmodified main (fixed by fix(config): compare resolve checked_at at the database's microsecond precision #1252).TESTCONTAINERS_RYUK_DISABLED=trueto avoid a testcontainers reaper race under load.scripts/setup-git-secrets.shpatterns in a fresh repo with an isolated HOME, found nothing in the changed files, and a planted fake key was flagged.scripts/test-git-secrets-allowlist.sh: 137 PASS, 0 FAIL.Out of scope / remaining for #76
internal/server/test_helpers_test.gomockConfigStoreForHealth(145 methods, about 520 lines) still needs folding. It is left for a follow-up to keep this diff small. It needs more care than the analytics copy:MockConfigStoremethods (for exampleGetServiceConfig,SaveGlobalConfig,GetPendingExecutions,GetRIExchangeDailySpend,ReserveRIExchange) callm.Calledunconditionally, so they panic when no expectation is set.handler_ladder_test.go,handler_ri_exchange_test.go) rely on that implicitly.handRolledConfigStoreAllowlistand change this toCloses #76.MockHTTPClient, underproviders/azure/...) belongs to thecloud-commitments-gorepository. That code does not exist in this repo, so it is not addressed here.Maintainer: please mirror the issue's triage labels on this PR:
impact/internal,type/chore,triaged,priority/p3,severity/low,urgency/this-quarter,effort/m.🤖 Generated with Claude Code