Skip to main content
ThunderLang
← All articles
ai-engineering

Test AI-Generated Inventory Reservations for Partial Database Writes

6 min read · 2026-10-14 · Allen Codewell

Three wooden shapes bound by a brass band on a stone slab beside an empty pedestal.

AI-generated illustration. Conceptual illustration: the stock decrement, reservation, and outbox record must persist together, or leave no changes behind. A failure response alone cannot establish that outcome.

To test AI-generated code against a database transaction requirement, inject a persistence failure after an earlier write executes. Then inspect committed state through a fresh connection. An error response is not enough: the stock decrement, reservation row, and outbox row must persist together or leave no changes behind.

Check the outcome, not the completion message

The Hugging Face excerpt for The Agent Said It Was Done. The Database Disagreed. describes Microsoft ThinkingBox as available through Hugging Face. According to the excerpt, it grades agents on terminal backend state and side effects across repeated runs, rather than relying on their generated responses.

That motivates a useful testing principle, but agent evaluation is not application acceptance testing. The excerpt does not establish transaction correctness for your application or provide an inventory test. Your acceptance contract still needs to specify the persisted outcome.

Define the contract before implementation

Consider a hypothetical service that reserves one unit of inventory. Assume three writes belong to one transaction in the same transactional database:

  • Decrement the item's available stock.
  • Insert a reservation.
  • Insert a reservation-created outbox row for later processing.

The outbox row is a database record. It is not evidence of external message delivery, which sits outside this example's boundary.

Contract element Declared intent
Operation Reserve one unit of item-A for request-A, in one attempt.
Preconditions Stock is 1; no reservation or outbox row exists for request-A; no concurrent writer touches the fixture.
Permitted writes Update item-A stock, create one reservation, and create one corresponding reservation-created outbox row.
Success postcondition Committed stock is 0, with exactly one matching reservation and one matching outbox row.
Failure postcondition If persistence fails before commit, committed stock remains 1, with neither matching row present.
Side-effect boundary No unrelated application records change. Fixture identifiers are isolated from other tests.

“Matching” must mean more than a row count. Check the request identifier, item, quantity, event type, and outbox reference to the created reservation.

This contract assumes no separately committed audit or failure record. If your application intentionally writes one, declare the exception. Otherwise, “no partial writes” leaves the permitted side effects unclear.

Expose the defect an error assertion misses

A broken implementation could:

  1. Decrement stock and commit that update independently.
  2. Attempt to insert the reservation.
  3. Encounter a database error.
  4. Return a failure response.

A test that checks only the response passes. Yet stock is now zero, with no reservation explaining where the unit went.

That is intent drift: error handling appears to work while the implementation violates the all-or-nothing requirement. The missing assertion concerns committed state.

Paper arrow returns a square to its recess beside a blocked slip; a separate magnifying glass overlooks a square and two empty slots.

AI-generated illustration. In the proposed failure test, reject the reservation insert after the stock update executes. After cleanup, a fresh connection should find the original stock and neither matching record.

Inject the failure in an isolated database

Use a disposable database or isolated test resource, never production credentials or live customer data. Exercise the application's database engine and transaction path where practical. A mocked repository can check error handling, but it cannot establish actual database rollback behavior.

This is a test design, not an executed test.

Failure case: reject the reservation insert

  1. Commit the fixture. Seed stock at 1 and confirm that neither matching row exists. Record a baseline for other application records within the declared boundary.
  2. Arm a scoped failure. Use a test-only database fault, such as a conditional trigger in the disposable database, to reject the reservation insert for request-A. An endpoint error before persistence begins does not exercise this case.
  3. Call the real operation once. Instrument the test so it can confirm that the stock update executed before the reservation insert failed. Executed does not mean committed.
  4. Wait for cleanup. Let the operation finish its rollback or error handling.
  5. Read through a fresh connection. Query the authoritative database outside the operation's transaction, without a cache or read replica.
  6. Assert the contract. Require stock 1, zero matching reservations, zero matching outbox rows, and no changes to unrelated records within the declared boundary. Also assert the expected failure outcome.

Do not wrap the entire test in an outer transaction that automatically rolls everything back. That can hide an application transaction-boundary defect. The committed fixture and independent read expose what another database client can observe after the operation finishes.

Success control: permit all three writes

Reset the isolated fixture to the same preconditions and disable the injected failure. Run one reservation attempt, then read committed state through a fresh connection.

Require a successful outcome, stock 0, exactly one matching reservation, and exactly one correctly linked outbox row. Unrelated application records must remain unchanged.

Why include the control? Code that always returns an error without writing anything could satisfy the failure-state assertions alone.

Keep the conclusion narrower than the requirement

Passing these cases shows that the tested revision produced the expected committed state on the exercised paths and observed tables. It does not establish:

  • Rollback after later failures. Add an outbox-insert failure to check whether both earlier writes are undone.
  • Concurrency safety. Simultaneous reservations need separate checks for overselling and isolation behavior.
  • Retry behavior. One attempt does not cover duplicate requests or repeated side effects.
  • External delivery. An outbox row alone says nothing about delivery, ordering, or consumer behavior.
  • Every database failure. Connection loss during commit can leave the caller uncertain whether the transaction committed.
  • No transient partial commits. A final snapshot could miss an independently committed decrement that was later compensated. If intermediate visibility is forbidden, also review transaction ownership and database semantics.

The gate should say “the specified acceptance cases passed,” not “transaction atomicity is proven.” If your requirement covers every write boundary, the test plan needs more than the first injected failure.

Tie the evidence to the actual diff

Keep the contract beside the implementation and tests. An agent's explanation or a diff check cannot substitute for executing the integration test.

Retain an evidence record containing:

  • The contract and spec revision.
  • The code revision and diff checked.
  • The database configuration, fixture, and injection point.
  • The observed postconditions and test result.
  • Untested paths and side effects outside the boundary.

When the spec, implementation, or relevant database configuration changes, rerun the affected checks. An older passing result is not evidence for a new revision.

If permitted side effects remain ambiguous, clarify the contract before using it to gate the change.

Use this contract as a starting point to gate your first AI-assisted change with ThunderLang.