invoices/sql_store: add tests for targeted invoice query variants
Add testFetchPendingInvoicesAccepted to explicitly verify the state filtering behaviour of FetchPendingInvoices across all four contract states: ContractOpen (state 0) – must be returned ContractAccepted (state 3) – must be returned ContractSettled (state 1) – must be excluded ContractCanceled (state 2) – must be excluded This directly exercises the `state IN (0, 3)` predicate introduced in the FetchPendingInvoices SQL query and addresses the review request for explicit test coverage of the new targeted query variants. The test runs against the KV, SQLite, and Postgres backends.
This commit is contained in:
1 file changed
+113
@@ -166,6 +166,10 @@ func TestInvoices(t *testing.T) {
|
||||
name: "FetchPendingInvoices",
|
||||
test: testFetchPendingInvoices,
|
||||
},
|
||||
{
|
||||
name: "FetchPendingInvoicesAccepted",
|
||||
test: testFetchPendingInvoicesAccepted,
|
||||
},
|
||||
{
|
||||
name: "DuplicateSettleInvoice",
|
||||
test: testDuplicateSettleInvoice,
|
||||
@@ -1288,6 +1292,115 @@ func testFetchPendingInvoices(t *testing.T,
|
||||
require.Equal(t, pendingInvoices, pending)
|
||||
}
|
||||
|
||||
// testFetchPendingInvoicesAccepted verifies that FetchPendingInvoices returns
|
||||
// invoices in both ContractOpen (state 0) and ContractAccepted (state 3)
|
||||
// states, and that ContractSettled (state 1) and ContractCanceled (state 2)
|
||||
// invoices are excluded. This specifically exercises the `state IN (0, 3)`
|
||||
// predicate in the underlying SQL query.
|
||||
func testFetchPendingInvoicesAccepted(t *testing.T,
|
||||
makeDB func(t *testing.T) invpkg.InvoiceDB) {
|
||||
|
||||
t.Parallel()
|
||||
db := makeDB(t)
|
||||
ctxb := t.Context()
|
||||
|
||||
amt := lnwire.MilliSatoshi(1000)
|
||||
|
||||
// Add an invoice that stays in ContractOpen state.
|
||||
openInvoice, err := randInvoice(amt)
|
||||
require.NoError(t, err)
|
||||
openHash := openInvoice.Terms.PaymentPreimage.Hash()
|
||||
_, err = db.AddInvoice(ctxb, openInvoice, openHash)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Add a second invoice and transition it to ContractAccepted by
|
||||
// adding an HTLC while setting the new invoice state in a single
|
||||
// UpdateInvoice call (addHTLCs processes the HTLC list before
|
||||
// validating the state transition, so the empty-set check passes).
|
||||
acceptedInvoice, err := randInvoice(amt)
|
||||
require.NoError(t, err)
|
||||
acceptedHash := acceptedInvoice.Terms.PaymentPreimage.Hash()
|
||||
_, err = db.AddInvoice(ctxb, acceptedInvoice, acceptedHash)
|
||||
require.NoError(t, err)
|
||||
|
||||
acceptKey := models.CircuitKey{HtlcID: 1}
|
||||
acceptRef := invpkg.InvoiceRefByHash(acceptedHash)
|
||||
addHtlcs := map[models.CircuitKey]*invpkg.HtlcAcceptDesc{
|
||||
acceptKey: {
|
||||
Amt: amt,
|
||||
CustomRecords: make(
|
||||
record.CustomSet,
|
||||
),
|
||||
},
|
||||
}
|
||||
dbAccepted, err := db.UpdateInvoice(
|
||||
ctxb, acceptRef, nil,
|
||||
func(inv *invpkg.Invoice) (*invpkg.InvoiceUpdateDesc, error) {
|
||||
return &invpkg.InvoiceUpdateDesc{
|
||||
UpdateType: invpkg.AddHTLCsUpdate,
|
||||
State: &invpkg.InvoiceStateUpdateDesc{
|
||||
NewState: invpkg.ContractAccepted,
|
||||
},
|
||||
AddHtlcs: addHtlcs,
|
||||
}, nil
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, invpkg.ContractAccepted, dbAccepted.State)
|
||||
|
||||
// Add a settled invoice – it must NOT appear in the pending result.
|
||||
settledInvoice, err := randInvoice(amt)
|
||||
require.NoError(t, err)
|
||||
settledHash := settledInvoice.Terms.PaymentPreimage.Hash()
|
||||
_, err = db.AddInvoice(ctxb, settledInvoice, settledHash)
|
||||
require.NoError(t, err)
|
||||
_, err = db.UpdateInvoice(
|
||||
ctxb, invpkg.InvoiceRefByHash(settledHash), nil,
|
||||
getUpdateInvoice(2, amt),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Add a canceled invoice – it must also NOT appear in the pending
|
||||
// result, verifying that state 2 (ContractCanceled) is excluded by
|
||||
// the `state IN (0, 3)` SQL predicate.
|
||||
canceledInvoice, err := randInvoice(amt)
|
||||
require.NoError(t, err)
|
||||
canceledHash := canceledInvoice.Terms.PaymentPreimage.Hash()
|
||||
_, err = db.AddInvoice(ctxb, canceledInvoice, canceledHash)
|
||||
require.NoError(t, err)
|
||||
_, err = db.UpdateInvoice(
|
||||
ctxb, invpkg.InvoiceRefByHash(canceledHash), nil,
|
||||
func(inv *invpkg.Invoice) (*invpkg.InvoiceUpdateDesc, error) {
|
||||
return &invpkg.InvoiceUpdateDesc{
|
||||
UpdateType: invpkg.CancelInvoiceUpdate,
|
||||
State: &invpkg.InvoiceStateUpdateDesc{
|
||||
NewState: invpkg.ContractCanceled,
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
// FetchPendingInvoices must return exactly the two pending invoices.
|
||||
pending, err := db.FetchPendingInvoices(ctxb)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, pending, 2)
|
||||
|
||||
_, hasOpen := pending[openHash]
|
||||
require.True(t, hasOpen, "ContractOpen invoice missing from results")
|
||||
|
||||
_, hasAccepted := pending[acceptedHash]
|
||||
require.True(t, hasAccepted, "ContractAccepted invoice missing")
|
||||
|
||||
require.NotContains(t, pending, settledHash,
|
||||
"ContractSettled invoice should not appear in pending results")
|
||||
require.NotContains(t, pending, canceledHash,
|
||||
"ContractCanceled invoice should not appear in pending results")
|
||||
|
||||
require.Equal(t, invpkg.ContractOpen, pending[openHash].State)
|
||||
require.Equal(t, invpkg.ContractAccepted, pending[acceptedHash].State)
|
||||
}
|
||||
|
||||
// testDuplicateSettleInvoice tests that if we add a new invoice and settle it
|
||||
// twice, then the second time we also receive the invoice that we settled as a
|
||||
// return argument.
|
||||
|
||||
Reference in new issue
Block a user