Skip to content

CBG-5784: Move tests for channel cache, change cache and changes feed into their own sub-packages - #8774

Open
gregns1 wants to merge 14 commits into
mainfrom
CBG-5784
Open

CBG-5784: Move tests for channel cache, change cache and changes feed into their own sub-packages#8774
gregns1 wants to merge 14 commits into
mainfrom
CBG-5784

Conversation

@gregns1

@gregns1 gregns1 commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

CBG-5784

Moving tests into sub-packages for channel cache, change cache and changes feed.

Things to note:

  • Mostly accessors are made to access private things to the db package. This is apart from type ChannelCacheImplForTest = channelCacheImpl type SingleChannelCacheImplForTest = singleChannelCacheImpl This is to support type assertion for unexported types in db package in tests that have moved.
  • TestAddPendingLogs did not move because it holds changeCache.lock for _pushRangeToPending/_addPendingLogs
  • TestReadChangesOptionsFromJSON did not move also because it uses private method on private type
  • subpackages can't declare their own ChannelQueryHandler (its only method is unexported)

NOTE: decision to be made on whether this change is still wanted. With extra accessor work made etc.

Pre-review checklist

  • Logging sensitive data? Make sure it's tagged (e.g. base.UD(docID), base.MD(dbName))
  • Updated relevant information in the API specifications (such as endpoint descriptions, schemas, ...) in docs/api

Dependencies (if applicable)

  • Link upstream PRs
  • Update Go module dependencies when merged

Integration Tests

@gregns1
gregns1 requested review from a team and a lite review from Copilot September 9, 2026 12:21
@factory-droid

factory-droid Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Droid finished @gregns1's task —— View job


This is a straightforward test refactor. The main remaining risk is test-only helpers diverging from production behavior, so it would help to fail fast on ChangeEntry.Err in GetChangesForTest and make the QueryHandlerForTest double honor startSeq/endSeq bounds (including endSeq==0 semantics).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

New test helpers in db/util_testing.go have correctness gaps (not failing on feed entry errors and a query test double ignoring start/end bounds) that can mask real failures and diverge from production behaviour.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR reorganizes Sync Gateway’s cache and changes-feed tests into dedicated sub-packages (e.g. db/changesfeedtest, db/changecachetest, db/channelcachetest, rest/changestest) to isolate coverage and reduce reliance on in-package access to unexported symbols. To support out-of-package tests, it introduces a set of thin “for test” bridges and helpers in db/util_testing.go.

Changes:

  • Moved changes-feed, change-cache, and channel-cache tests into new sub-packages with their own TestMain bootstrapping.
  • Added exported test helpers/bridges in db/util_testing.go for accessing unexported internals from out-of-package tests.
  • Updated many existing db/*_test.go files to use the new exported setup/helpers (e.g. SetupTestDB, GetChangesForTest), and kept a small set of tests in-package where locks/private methods must be held/called directly.
File summaries
File Description
rest/changestest/changes_test.go Moved REST changes tests into changestest and updated references to rest.* helpers.
rest/changes_api_test.go Added back in-package handler test for readChangesOptionsFromJSON.
db/util_testing.go Added new exported test helpers/bridges and a query handler test double for out-of-package tests.
db/users_test.go Switched to exported SetupTestDB.
db/sg_replicate_cfg_test.go Switched to exported SetupTestDB.
db/revtree_test.go Switched to exported SetupTestDB.
db/revision_cache_test.go Switched to exported SetupTestDB.
db/query_test.go Switched to exported SetupTestDB.
db/import_test.go Switched to exported SetupTestDB / SetupTestLeakyDBWithCacheOptions.
db/hybrid_logical_vector_test.go Switched to exported SetupTestDB.
db/document_test.go Switched to exported SetupTestDB.
db/delta_cache_test.go Switched to exported SetupTestDB.
db/database_test.go Removed now-exported local setup helpers and updated call sites to exported versions.
db/crud_test.go Switched to exported SetupTestDB and exported changes helpers.
db/channelcachetest/main_test.go New subpackage TestMain bootstrapping for channel cache tests.
db/changesfeedtest/main_test.go New subpackage TestMain bootstrapping for changes feed tests.
db/changesfeedtest/sequence_id_test.go Moved sequence ID tests into changesfeedtest and updated to use exported APIs.
db/changesfeedtest/changes_test.go Moved changes feed tests into changesfeedtest and updated to use exported bridges/helpers.
db/changecachetest/main_test.go New subpackage TestMain bootstrapping for change cache tests.
db/changecachetest/skipped_sequence_test.go Moved skipped-sequence tests into changecachetest and updated to use exported bridges.
db/changecachetest/change_listener_test.go Moved change-listener tests into changecachetest and updated to use exported bridges.
db/changecachetest/change_listener_dual_metadata_test.go Moved caching-feed collections test into changecachetest and updated to use exported bridge.
db/change_cache_pending_test.go Kept lock-sensitive pending-log test in-package db.
db/blip_test.go Switched to exported SetupTestDB.
db/background_mgr_resync_dcp_test.go Switched to exported SetupTestDB.
db/background_mgr_attachment_migration_test.go Switched to exported SetupTestDB.
db/attachment_test.go Switched to exported SetupTestDB / SetupTestLeakyDBWithCacheOptions.
db/attachment_compaction_test.go Switched to exported SetupTestDB / SetupTestDBDefaultCollection.
db/access_test.go Switched to exported SetupTestDB.
Review details

Suppressed comments (1)

rest/changestest/changes_test.go:407

  • The second status assertion re-checks the previous PUT response (res) rather than the non-winning branch write. It’s redundant (PutNewEditsFalse already asserts 201) and makes the test intent unclear.
  • Files reviewed: 32/33 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread db/util_testing.go
Comment on lines +1319 to +1323
var changes = make([]*ChangeEntry, 0, 50)
for entry := range feed {
changes = append(changes, entry)
}
return changes

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TestChannelQueryCancellation fails with this change. Have removed the comment about the error instead

Comment thread db/util_testing.go
Comment thread db/util_testing.go
Comment on lines +1320 to +1322
for entry := range feed {
changes = append(changes, entry)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Fail tests when a changes feed emits an error

MultiChangesFeed can signal failures by sending a ChangeEntry with Err set (for example base.ErrChannelFeed) before closing. GetChangesForTest currently appends entries without checking entry.Err, which can let tests treat an error sentinel as a valid change and pass incorrectly.

Suggested change
for entry := range feed {
changes = append(changes, entry)
}
for entry := range feed {
require.NotNil(t, entry)
require.NoError(t, entry.Err)
changes = append(changes, entry)
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment above on copilot review

Comment thread db/util_testing.go
@gregns1 gregns1 self-assigned this Sep 9, 2026
gregns1 and others added 14 commits September 10, 2026 09:40
The seven helpers used by tests across package db were defined in
change_cache_test.go, which is about to move to db/changecachetest. Moving the
file would have taken them with it and broken 177 call sites in the tests that
stay behind.

Relocates the six phase-2 delegations to db/change_cache_helpers_test.go, and
exports the one real helper among them - the tombstoned-entry builder et - as
MakeDeletedTestLogEntry in util_testing.go, since it has consumers on both
sides of the boundary.

Also adds db/changesfeedtest/main_test.go, which was left untracked when the
changes feed tests moved, leaving that package with no TestMain and no bucket
pool.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
TestAddPendingLogs cannot move to db/changecachetest. It acquires
changeCache.lock and, while holding it, pushes directly onto the pendingLogs
heap and calls _pushRangeToPending and _addPendingLogs - underscore-prefixed
methods whose contract is that the caller already holds the lock. A held lock
cannot cross a package boundary, and an accessor that took the lock itself
would stop exercising the sequence-buffering path the test exists to pin down.

Moves it, with the four helpers only it uses, to change_cache_pending_test.go
so the remainder of change_cache_test.go can move as a whole file.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds the 24 accessors db/changecachetest needs to drive changeCache,
SkippedSequenceSkiplist, singleChannelCacheImpl and lateLogEntry from outside
package db. Derived by attempting the move and letting the compiler enumerate,
rather than by grep - a single pass finds only a third of them, because an
unnameable type masks every method call on values of that type.

All are thin pass-throughs. Two read fields without taking changeCache.lock -
NextSequence and GetMaxStableCached - because the tests they replace did the
same; production's only caller of _getMaxStableCached holds the read lock, so
the lock discipline there is worth revisiting on its own rather than as a side
effect of relocating tests.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Moves change_cache_test.go, skipped_sequence_test.go, change_listener_test.go
and change_listener_dual_metadata_test.go out of package db, so the change
cache can be tested and its coverage measured on its own.

54 tests and 10 benchmarks move; TestAddPendingLogs stays behind. Test names
are unchanged - 65 before, 65 after, verified by name rather than by count.

The move is mechanical apart from four edits the compiler cannot make safely:
the three ChangeEntry literals that set collectionID are hoisted so the
existing SetCollectionID accessor can be used; the seven user assignments are
restructured with a non-colliding variable name, since the obvious rewrite
reassigns a user already in scope; the two &Database{user: ...} literals keep
the literal and call SetUserForTest; and the two notifyChangeFunc assignments
become SetNotifyChangeFunc calls. The assertion message in verifyCacheSequences
no longer names singleChannelCacheImpl, which is not nameable from here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…n shims

Phase 2 exported each shared test helper and left a one-line unexported
delegation behind, to avoid renaming call sites in package db. The delegations
have no purpose: they sit in the same package as the exported implementations,
so the tests can call those directly.

Removes eleven of them - the four log-entry builders, the tombstoned-entry
helper, shortWaitCache, getChanges, and the four setup wrappers - and renames
their 220 call sites. Signatures were identical in every case, so this is a
pure rename; the diff is 178 changed lines against 178, and no comment or
string literal was touched.

Removing logEntry also resolves a name that meant both a helper function and a
local variable in the same package.

setupTestDB is left for a separate commit - 157 call sites across 20 files.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The final phase-2 delegation. 157 call sites across 20 files, all a pure
rename to the exported name in the same package - no comment or string
literal changed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Gives every accessor added for db/changecachetest a leading `_ testing.TB` and
a ForTest suffix, so a call from production code reads as obviously wrong and
the whole set is greppable by name.

The parameter is a signal, not a barrier: nil satisfies testing.TB, so
production code can still call these if it insists. The usual secondary
deterrent - that it forces a testing import - does not apply here either,
since db/util_testing.go, db/database.go and base/logging.go already make
testing a dependency of the production binary. Enforcement would need a
ruleguard rule; this commit does not add one.

The suffix is applied uniformly rather than case by case, so there is no
judgement call about which names look sufficiently like production API. It
also resolves a real collision: changeCache.NextSequence sat alongside the
production DatabaseContext.NextSequence.

Two benchmark helpers in skipped_sequence_test.go gained a testing.TB
parameter so they can reach the accessors they set up.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds the accessors db/channelcachetest needs to drive channelCacheImpl,
singleChannelCacheImpl and the bypass cache from outside package db. Derived by
attempting the move and compiling to closure - three probe rounds, then three
more accessors surfaced during the move itself, because newSingleChannelCache
and newChannelCache return unnameable types and mask every method call on their
results. A single pass finds six of them.

Notable shapes: singleChannelCacheImpl.options is already a
*ChannelCacheOptions, so OptionsForTest is a plain getter and the twelve sites
that adjust cache caps in place keep working. addChannelCache returns the
concrete cache and a capacity flag rather than an error, so
AddChannelCacheForTest mirrors that. WaitForChannelCacheCompactionForTest
absorbs the waitForCompaction helper from channel_cache_test.go, whose
*channelCacheImpl parameter cannot be named from another package.

Two exceptions to the accessor-only rule, both because test code has to name a
type rather than merely hold it - a type assertion, and a closure whose result
type is the concrete cache. ChannelCacheImplForTest and
SingleChannelCacheImplForTest are aliases for exactly those cases; no accessor
can stand in for a result type.

AddListenerToNewestLateLogForTest is the one accessor that is not a bare
pass-through: it takes lateLogLock for precisely the operation
TestLateLogsSpikeForcePrunedBoundsLateLogsAndForcesRollback performed inline,
so the critical section is unchanged. Encapsulating it avoids stranding that
test in package db the way TestAddPendingLogs is.

No wrapper struct: an exported constructor returning an unexported type is
usable by inference, so the 78 addToCache call sites need one exported method
rather than a parallel type.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Moves channel_cache_test.go, channel_cache_single_test.go and
channel_cache_latelog_test.go out of package db, so the channel cache can be
tested and its coverage measured on its own.

All 37 tests, 7 benchmarks and the fuzz target move; nothing stays behind.
Unlike the change cache, these three files are self-contained - compiling db's
remaining tests without them reports no undefined identifiers - so no helper
relocation was needed first. Test names are unchanged, 45 before and 45 after,
verified by name rather than by count.

The move is mechanical apart from edits the compiler cannot make safely: the
DatabaseCollectionWithUser literal carrying the unexported user field is
restructured around the existing setter, the eight user assignments use it too,
the change cache is reached through the database rather than the collection
router, and the two cache implementations are named through the aliases added
in the previous commit. The waitForCompaction helper is deleted in favour of
the accessor that absorbed it, which is why "time" leaves
channel_cache_test.go's imports - the only string literal that changes across
all three files.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
QueryHandlerForTest.getChangesInChannelFromQuery filtered on channel, activeOnly
and limit but ignored the sequence bounds, so it could return entries outside the
range the caller asked for. singleChannelCacheImpl.GetChanges feeds that result
to prependChanges with startSeq/resultValidTo as the cached range, and then
concatenates it with the cache result behind a de-dup that only drops a single
overlapping entry - so out-of-range rows could both poison the cached range and
duplicate. Pre-existing behaviour, carried over from testQueryHandler, but worth
fixing now the double is exported and will be reached for by new tests.

The bounds follow DatabaseCollection.QueryChannels: both inclusive (the N1QL
statement uses BETWEEN [$channelName, $startSeq] AND [$channelName, $endSeq],
and the views path uses startkey/endkey with the default inclusive_end), and an
endSeq of 0 means unbounded, which query.go turns into N1QLMaxInt64. They are
applied before the limit, so entries outside the range no longer consume it.

No existing test depended on either behaviour: instrumenting the double to
compute both results across db, db/channelcachetest, db/changecachetest and
db/changesfeedtest recorded 6250 calls and zero divergences. Every call used
startSeq=1 with endSeq of either 2 or MaxUint64, limit=0 and activeOnly=false,
so the bounds could not exclude anything - and the limit and activeOnly filters
were not exercised either.

Adds db/util_testing_test.go, which pins the filter contract directly. It has to
live in package db because getChangesInChannelFromQuery is unexported. The test
fails against an exclusive startSeq, an exclusive endSeq, a missing endSeq==0
sentinel, and a limit applied before the bounds.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants