main
10
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
6f65c744fe |
Pin how the plan-wide acts interleave with each other, with a rule change and with a conferral
The plan-wide acts (tier removal, default change, reorder, tier add and delete) are now sent against each other and against a conferral, each interleaving chosen by a lock a side transaction holds and releases once the requests wait on it. dbtest.AwaitLockWait proves a request is queued, and PostgreSQL grants a row lock to its waiters in the order they queued, so no test sleeps. The kit gains Backend.Waiters, how many backends must wait at once, with a test of its own; its first caller is these tests. TestTierRemovalAgainstDefaultChange runs a removal and a default change on one pool in each order: queued on the pool's row, and one after the other. Every case ends with no live position on a deleted tier. TestDefaultChangeTwiceSerializesOnThePool sends one default change twice and shows one request grandfathering the pool and the other finding the legacy grant the first wrote. TestPlanActsRefuseWhileARuleChangeHoldsTheRendezvous holds the exclusive rendezvous while all five acts run, and pins each act's contention answer: the removal's and the organization's "Another entitlement set change is in progress. Try again." and the generic sentence of the reorder, the add and the delete. It waits out the 5 s lock timeout once. TestRenumberingCommitsDuringAConferral commits a reorder, a tier delete and a tier removal while a default change's conferral waits between its rank reads; the files pin the transition as it is recorded today (upgrade 0 to 1 for the reorder where a transfer belongs; downgrade 1 to 0 for the delete and the removal), because a renumbering takes the rendezvous in shared mode only. Pinned with #187: TestReorderEnactsAgainstTheRankZeroItSaved sends two reorders whose enactments queue on one pool; the first enacts against the rank 0 it saved after the second has put the other product back at rank 0, so it grandfathers the holder onto a legacy grant of the product that is rank 0 again, and the second finds the holder on that grant and leaves it. No output changed: the commit adds tests, their golden files and one field of the test kit, and touches no production file. The names and results of ./internal/server and ./internal/testkit/..., run after a database reset at the parent and at this commit, differ by the 13 new tests and subtests, all passing; the new tests passed three runs in a row and 20 runs of the four that do not wait out the timeout. The edit to the plan-wide acts' golden test is its JSON encoder, split so the new tests encode their files the same way; its 44 files are unchanged. The kit's wait.go and wait_test.go and the AwaitLockWait bullet of docs/testing.md change beside it. 14 breaks, each caught by its new test (new side only): 12 edit production code unchanged from the parent and 2 edit this commit's wait.go. The section's tests take about 6 s, 5 s of it the one wait. Unprobed: - Interleavings the tests do not choose, such as a removal that arrives between the default change's save and its pool lock; the cases cover the two queue orders on the pool and the two serial orders. - A conferral other than the default change's during a renumbering (a reconcile, a grant act, the floor); it is the same database function, which the reorder case reaches. - The default change's own stale candidate: a renumbering between its save and its enactment. It uses the same fixed product the reorder case reaches; no case pauses the default change there. - The two new failure messages of AwaitLockWait for a count above one; they are text, read only when a wait fails. |
||
|
|
a36003910f |
Note each Stripe call among the statements around it
A test can now put a line of its own among the statements a Counted handle recorded, and have the Stripe mock hand it every call it receives, so one list shows each Stripe call between the statements it ran between, begin and commit included. Statements.Note places a line after the statements recorded so far. Names returns statements and notes in order; Total and ByName leave notes out; Reset forgets them. MockBackend.OnCall is called for every request as soon as it is recorded, before Err, PathErrs or Handle answers it, so a refused request is seen too. The recording moved from Call into a small record method, which keeps (*MockBackend).Call at its cognitive complexity of 65. stripetest.Line prints a call as "stripe <METHOD> <path>" and its form's pairs sorted by key, unescaped. docs/testing.md names OnCall, Line and Note. No production code changes, so no output changes: only test helper packages and docs/testing.md are edited, and the build, vet, gofmt, lint and deadcode checks pass with the lint snapshot unchanged. The names and results of ./internal/testkit/..., ./internal/stripetest, ./internal/fulfillment, ./internal/integrations/stripe/... and ./internal/server equal the parent's (1767 at the parent, 1771 here; every result pass), the four additions being the new tests TestLineNamesTheCallAndItsSortedForm, TestOnCallSeesEveryCallInOrder, TestOnCallSeesARequestErrRefuses and TestNotesTakeTheirPlaceAmongTheStatements, after a database reset before each side. Eleven breaks, each caught by the new test. Not probed: OnCall called from concurrent goroutines. The mock appends under its mutex and calls OnCall outside it, and no test of this series calls Stripe from two goroutines at once. |
||
|
|
ff04f2ee86 |
Print a query's rows as text for a golden file
dbtest.Q.Rows runs a query and returns a line of column names, then one line per row in the query's order, values separated by " | ". NULL reads NULL, a time reads in UTC as RFC 3339 with its fraction, bytes read as text, anything else as fmt prints it. The query names its own order. docs/testing.md lists Rows with the Q methods. No production file changes. Shown to change no output: the tests of ./internal/testkit/... listed by name and result before and after differ by the one new test, TestRowsPrintsColumnsThenRowsInQueryOrder, and every test passes; the kit's imports guard still passes, and the lint snapshot has no added or removed line. 5 breaks, each caught by the new test (a new test is proven on the new side only); none dropped. Unprobed: how the driver hands over each column type (uuid, numeric and json arrive as text or bytes, both printed as text; which one a type takes is the driver's choice, so the test does not name it), and the error branches (each fails the test with the whole query folded onto one line, as Strings does). |
||
|
|
dd38fce93a |
Name the statements a Counted handle ran, in the order they ran
Statements.Names returns a copy of the recorded names in run order. A transaction's begin, commit and rollback are among them, because pgx sends them as statements the tracer sees, so a test can read where each statement fell against the transactions around it. docs/testing.md lists it beside ByName, Total and Reset. No production file changes. The test packages under internal/testkit run with the same test and subtest names and results at the parent and at this commit, apart from the new TestCountedNamesStatementsInOrder (37 results before, 38 after, each run after a reset of the test databases). 4 breaks, each caught by the new test: sorted order, a dropped first name, the shared slice returned instead of a copy, and Reset leaving the names. Unprobed: the mutex, because every caller records on one goroutine. That rollback is recorded is pgx's behaviour; the test asserts it, but no one-line edit of the kit removes it, so no break covers it. |
||
|
|
2fd63eaca2 |
Characterize the recent-activity feed: its rows, its failed reads, its reads per event and the landing's paging
No production file changes. The feed's output is unchanged because nothing the page runs was edited; the commit adds tests, one kit function and the files the golden compares with. TestRecentActivityGolden builds nine organizations on one World, for ten cases (the floor, an upgrade and a revocation on a plan the type defaults to; an upgrade by a subscription that then ends and resumes the grant; a live extension; an extension of a grant on hold; a ladder of its own; a reorder after the tier changes; the console's own removal of a tier, which writes the only end row with no rank; invoices and payments around one instant; two organizations with one invoice each at one instant). It compares the events the feed lists for them, in order, as JSON, and the activity panel's render of those events, with files under testdata/operator_activity. Instants are named t01 and on, newest first, because only their order is a fact. The files were read against the fixture before they were committed. The feed's four existing readers now go through wholeFeed. TestRecentActivityReadFailures fails each of ten reads in turn (the four sources, the product, the tiers, the organization, the person, a handler without a database and a closed one) and pins the events the feed keeps and the warning it writes. TestRecentActivityReadsPerEvent grows the feed by two organizations on a scratch database and expects exactly the product, ladder, organization and person reads of two more of each, and no change in the four source reads or the raw ladder read. The landing page tests read GET /operator on a scratch database: each source is cut at 100 rows (the 101st invoice is on no page and the pager counts the 101 events the feed holds), and a feed of 30 events pages by act_page and act_per, with a page past the end showing page 1 and an unknown size reading as 10. dbtest.CountedAt is Counted over a given address. Shown to change no output: 33 breaks of the feed's code, each applied on its own, are all caught by the new tests; the tests of ./internal/server and ./internal/testkit/... pass three times in a row, each on a reset database, with no test or subtest of the parent missing or changed in result and 19 added in ./internal/server (the golden, the failure table and its ten subtests, the reads test, the cut test, the paging test and its four subtests); the golden passes a second run without -update and leaves the tree unchanged. Unprobed, and why: - Each source is read once per render: no one-line edit doubles a source read without also changing the rows. - The order of two warnings when two sources fail: no subtest fails two reads. - The When cells: the golden replaces them with tokens, and the test of billing events' times pins their text. - The template's id fallback for an unnamed organization or actor: the overview render tests, which build their events by hand, pin it. - An extension older than the transitions window shows as its grant row: pinning it needs about 100 later tier changes. |
||
|
|
2790c6bbc7 |
Count the statements a request runs, and pin the products page at none per row
dbtest.Counted opens the test database on a handle that records the name of every statement run on it (a generated query by its query name, anything else by its first line), with Reset, Total, ByName and Diff. A new table test, TestOperatorPagesReadAFixedNumberOfTimes, grows a page by one row, then by two more, and asserts that the second request runs 2*perRow more statements than the first. Its first row is the products page at 0 per row, which holds today. No production file changes, so no output changes. Tests of internal/testkit/... and internal/server pass on a reset database, and the read-count test passes three runs in a row. New tests: TestCountedRecordsEachStatementByName and TestDiffListsTheNamesWhoseCountsDiffer (dbtest), and TestOperatorPagesReadAFixedNumberOfTimes with its products subtest (server); every other name and result equals the parent's. 8 breaks, each caught by the read-count test (new side only). Unprobed: the mutex in Statements (every handler under test runs its statements on the request's goroutine, so nothing records concurrently); pings and prepares going unrecorded (a ping happens only after a second of idleness, and nothing in the module prepares a statement); Counted failing on a database behind the migrations (ledgerBehind, which the Open test pins). |
||
|
|
f8f9551ddd |
Build the FedWiki tests' fixtures through the kit
Adds internal/testkit/world (New, Providers, Person, Org, Workspace,
Product, Rule, Confer, Person.SignIn), internal/testkit/authtest
(NewSessionManager, SignIn) and internal/integrations/fedwiki/fedwikitest
(the environment, tenant, limit and usage, operator root, site rows), and
moves the FedWiki fixtures onto them: newReqEnv, setupQuotaFixture,
newReconcileTestEnv, siblingWorkspace, governFedwikiLimit (Product, Rule,
Confer, no materialization), newSiteEnv, newClaimTestDB (its raw provider
insert goes), seedOperatorSites, newMemberWorkspace, seedUnderstatedQuota,
newTestWorkspace, seedCountedWorkspace and addSiblingWorkspace.
newReconcileDomainsDB and setupReconcileRoot had nothing left to move. The
six sessions the FedWiki web tests build go through authtest. The import
guard covers the three packages, and scripts/deadcode-testonly.txt lists
them.
The six hand-written lock-wait loops become dbtest.AwaitLockWait
(lifecycle_db_test.go, registration_db_test.go, invoice_reconcile_test.go,
stamp_db_test.go, and the two record-swap tests in requests_db_test.go and
swap_record_db_test.go). Backend gains BlockedBy, Query and Abort. The
registration loop is not a verbatim move: its select matches by query text
within the current database and its done check becomes the Abort probe, as
do the two record-swap loops, each keeping its message ("registration
finished (%v) while the first key was held", "the record finished without
writing the activated site's status: %v", "the record finished without
waiting on the parked request: %v"). Kept weaker on purpose: the
never-waited messages are now the helper's, which names the selection
("backend N never waited on a lock" for the pid loops, "no backend waited on
a lock held by backend N" for the record swaps, "no backend running a
statement containing %q waited on a lock" for the registration and invoice
loops), the record-swap and stamp bounds are the helper's ten seconds (were
the tests' thirty), the stamp loop accepts any waiting count (was exactly
one for its pid), and the invoice loop matches the statement text anywhere
in the query (was its prefix). TestDispatchStampsLockRecordsInOneOrder drops
from cognitive complexity 39 to 31; its pin is re-pinned to 31.
Names and results of the three FedWiki packages, internal/integration,
internal/workflows/desiredstate and internal/integrations/stripe/workflows
equal the parent's apart from the new tests (the import guard's forbidden
list with its three subtests, and the lock helper's selection test with its
three subtests and its Abort test) and one change: the three
TestReconcileDomains* tests in internal/integrations/fedwiki now pass on a
freshly reset database. They failed there at the parent, alone and in the
package run, because nothing had registered FedWiki; the fixture now does
(#33).
Fixture equivalence (each fixture run alone on a reset database, every table
of core, fedwiki and domains dumped before and after). Differences, none
read by an assertion: names and addresses carry the builder's shape
(organization "<name> <suffix>", email "<name>-<suffix>@example.test",
subject "sub-<name>-<suffix>"; the display name is the label for tenant
fixtures); the pool is the organization's default pool (name "Default", key
default, auto-managed) where the fixtures wrote a bare one; a workspace now
sits in the organization's default pool through a pool assignment where
newMemberWorkspace, seedOperatorSites and the claim fixtures wrote neither
(seedOperatorSites gains two pools and two assignments); every FedWiki
fixture registers FedWiki and the System tenant, which adds the provider row
(or fills operator_surface_path on the row a claim fixture inserted), the
operations, connection, state and outbox rows, the System tenant's user,
person, 'system' organization type, organization and workspace, and
refreshes the fedwiki_sites resource key; archived sites carry archived_at,
as the status query sets it; sites are minted under a database-minted id;
governing a limit goes through the rule-change function, which adds a
set-change row. Claim tokens are random in both.
25 breaks: 19 failed the old test at the parent and the new test here (one
code break per FedWiki test file in the function it pins, and one per
converted lock wait with the lock removed; the three loops with an Abort
probe fail through it with their own message), and six on the kit alone
failed the new test. No assertion in a test body changed.
|
||
|
|
7c1867f0ab |
Lock pools through one helper
Queries.LockPools copies its ids, sorts them, removes repeats and runs
LockResourcePool once per id, returning the ids in that order with the
driver's error unwrapped; a missing pool is sql.ErrNoRows.
Queries.LockPoolOfGrant wraps LockGrantPool. No production Go file outside
internal/entitlements/tx.go and the generated queries now takes a pool row
lock.
Converted: RevokeGrantTx, ManageGrantTx, ResumeReplacedGrantsTx (q is created
before the lock) and enactOrgDisposition (q is created before the lock) from
the raw SELECT 1 ... FOR UPDATE; settlePool from LockResourcePool;
ExpireGrantTx and ExtendWaitingGrantTx from LockGrantPool to LockPoolOfGrant.
Each keeps its error wrap or dispositionFailure copy. CommitPlanLadderTierRemoval
passes the holders' pool ids to LockPools and walks the slice it returns in
place of the map dedupe, sort.Strings and lock loop; its refusal copy is
unchanged, and its gocognit pin goes from 61 to the measured 57. The raw
statement selected 1 and the helper selects pool_id; the row lock is the
same. The lockPool helper in concurrency_test.go is deleted; its three
callers use LockPools.
dbtest.AwaitLockWait(tb, d, Backend{PID}) polls pg_stat_activity until that
backend waits on a lock; its first caller is the new
TestLockPoolsTakesAscendingOrder, which holds a pool, asks a second
transaction for (high, low, high), and checks that a third connection is
refused the low pool with 55P03 while the second waits, that the ids come
back sorted and deduplicated, and that a missing pool is sql.ErrNoRows.
Breaks, six records, each caught: LockPools skips the sort, LockPools locks
nothing (both fail the new test); LockPoolOfGrant returns an empty id
without calling the query (TestExpireGrantTx and the four
TestExpireGrantActivity_ tests fail with "resume replaced grants: lock
pool" on the new commit, and on the parent when ExpireGrantTx's
LockGrantPool call is replaced the same way). No test name or result
changes apart from the new test.
|
||
|
|
11e0f6ee86 |
Run the migration tests on scratch databases
dbtest gains UpSQL, ApplyVersion, SimpleProtocol, Tx, On and Q (Exec, Scan, ID, Int, String, Strings, Attempt, Refuse), WantPgError, Name and the SQLState constants. internal/db's mustExec, mustScan, mustStrings, deliveryTx, attempt, mustRefuse, assertPgError, the sqlstate constants and deliveryName become their dbtest counterparts at every caller; the fx builders' id scans become Q.ID. The fx builders and the helpers built on them lose their unused ctx parameter. The two tests that open one transaction in the parent (TestProviderConnectionConstraints, TestDeliverySchemaFacts) rebind it in each subtest that runs a statement with dbtest.On(t, q.Tx()). TestTaxonomyMigrationBuckets runs on a scratch database through two connections (simple protocol to apply, extended to assert) instead of a docker container; its checks are tables of (query, want) in three named phases, and its gocognit pin is deleted. TestIntegrationDeliveryMigrationUpDownUp uses Scratch, UpTo and ApplyVersion; TestInvoiceIssuedPaymentPaidBackfill replays 00022 through UpSQL on a simple-protocol transaction; the reader tests in internal/migrate use Tx; TestFullMigrationSequence and the delivery benchmark migrate a scratch database with migrate.Sources(). The delivery benchmark's skip text is unified with the openers'. No test is added or renamed. 13 breaks, each failed the old and the new test: the 11 on the migration subjects, one on a WantPgError caller and one on a Q.Refuse caller. |
||
|
|
d1ac278e6b |
Open every test database through dbtest
internal/testkit/dbtest opens TEST_DATABASE_URL and E2E_DATABASE_URL, never migrates, and fails with "the test database is behind; run test/reset-test-db.sh" when the core ledger lacks the highest version of db.BaseSources(). The opener helpers except the scratch-database and container openers (26 of the 29) and the inline openers of the database tests call it; helpers that did more than open (providers, fake farms, the FedWiki provider row, cleanup of override rows) keep that setup. The stripe/workflows and workflows/entitlements openers keep their search_path through WithSearchPath. The closed-database seam tests use Closed. TestMigration18SetsExistingFedwikiRulesToForceReduce runs on a scratch database through Scratch and UpTo instead of rolling the shared core ledger back through goose's global state, because no opener migrates the shared database any more. The in-package forms tests of internal/server reached the integrations' forms only because an external test file imported internal/migrate; a test file now links internal/integrations explicitly. TestRegisterProviders_DB is re-pinned from gocognit 37 to 34. New tests: TestLedgerCheckRefusesAnEmptyDatabaseAndAcceptsTheTestDatabase (dbtest), TestOpenRefusesAnEmptyDatabaseAndAcceptsTheTestDatabase (dbtest, through opened, the path Open and OpenE2E share), TestKitPackagesReachOnlyTheirAllowedModulePackages (testkit). The skip messages of the openers are unified to "TEST_DATABASE_URL not set, skipping integration test". 4 breaks: Open skipping its ledger check and the ledger check accepting an empty database fail the new tests; a kit row that no longer allows internal/testkit fails the import guard; leaving the fedwiki rules as they were in migration 00018 fails the migration 18 test on both sides. |