mirror of
https://github.com/Buriburizaem0n/nezha_domains.git
synced 2026-09-19 17:50:12 +00:00
212 lines
5.2 KiB
Go
212 lines
5.2 KiB
Go
//go:build agentcompat && linux
|
|
|
|
package singleton
|
|
|
|
import (
|
|
"context"
|
|
"database/sql/driver"
|
|
"errors"
|
|
"testing"
|
|
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
func TestSQLiteAttributionReturningCompletesExactlyOneRow(t *testing.T) {
|
|
// Given
|
|
database := openSQLiteAttributionTestDatabase(t)
|
|
enableSQLiteAttribution()
|
|
transaction, err := database.BeginTx(context.Background(), nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// When
|
|
rows, err := transaction.QueryContext(context.Background(), "INSERT INTO settings (value) VALUES (?) RETURNING id", "one-row")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !rows.Next() {
|
|
t.Fatal("RETURNING did not yield its inserted row")
|
|
}
|
|
var identifier int64
|
|
if err := rows.Scan(&identifier); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if rows.Next() {
|
|
t.Fatal("RETURNING yielded more than one row")
|
|
}
|
|
rowsErr := rows.Err()
|
|
closeErr := rows.Close()
|
|
commitErr := transaction.Commit()
|
|
count := sqliteAttributionSettingsCount(t, database)
|
|
|
|
// Then
|
|
if rowsErr != nil || closeErr != nil || commitErr != nil {
|
|
t.Fatal("completed RETURNING did not finish successfully")
|
|
}
|
|
if count != 1 {
|
|
t.Fatalf("completed RETURNING persisted %d rows", count)
|
|
}
|
|
}
|
|
|
|
func TestSQLiteAttributionEarlyReturningCloseRollsBackTransaction(t *testing.T) {
|
|
// Given
|
|
database := openSQLiteAttributionTestDatabase(t)
|
|
enableSQLiteAttribution()
|
|
transaction, err := database.BeginTx(context.Background(), nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// When
|
|
rows, err := transaction.QueryContext(context.Background(), "INSERT INTO settings (value) VALUES (?) RETURNING id", "early-close")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !rows.Next() {
|
|
t.Fatal("RETURNING did not yield its inserted row")
|
|
}
|
|
if err := rows.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
commitErr := transaction.Commit()
|
|
count := sqliteAttributionSettingsCount(t, database)
|
|
|
|
// Then
|
|
if commitErr == nil {
|
|
t.Fatal("early RETURNING Close allowed Commit")
|
|
}
|
|
if count != 0 {
|
|
t.Fatalf("early RETURNING Close persisted %d rows", count)
|
|
}
|
|
}
|
|
|
|
func TestSQLiteAttributionZeroRowUpdateDoesNotPublishEvidence(t *testing.T) {
|
|
// Given
|
|
database := openSQLiteAttributionTestDatabase(t)
|
|
enableSQLiteAttribution()
|
|
transaction, err := database.BeginTx(context.Background(), nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// When
|
|
if _, err := transaction.Exec("UPDATE settings SET value = ? WHERE id = ?", "zero", -1); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
commitErr := transaction.Commit()
|
|
evidence := sqliteAttributionTrackerWriteEvidence()
|
|
|
|
// Then
|
|
if commitErr != nil {
|
|
t.Fatal(commitErr)
|
|
}
|
|
if evidence.hasWrite {
|
|
t.Fatal("zero-row UPDATE published write evidence")
|
|
}
|
|
}
|
|
|
|
func TestSQLiteAttributionDirectReadQueryClosesOwnedStatement(t *testing.T) {
|
|
// Given
|
|
database := openSQLiteAttributionTestDatabase(t)
|
|
|
|
// When
|
|
rows, err := database.QueryContext(context.Background(), "SELECT id FROM settings")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
next := rows.Next()
|
|
rowsErr := rows.Err()
|
|
closeErr := rows.Close()
|
|
|
|
// Then
|
|
if next {
|
|
t.Fatal("empty SELECT returned a row")
|
|
}
|
|
if rowsErr != nil || closeErr != nil {
|
|
t.Fatal("direct readonly Query did not complete cleanly")
|
|
}
|
|
}
|
|
|
|
func TestSQLiteAttributionCommitClosesRetainedJournalDescriptor(t *testing.T) {
|
|
// Given
|
|
resetSQLiteAttributionForTest()
|
|
rawConnection, err := sqliteAttributionDriver{}.Open(sqliteAttributionTestDatabasePath(t))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
connection := rawConnection.(*sqliteAttributionConnection)
|
|
t.Cleanup(func() {
|
|
if closeErr := connection.Close(); closeErr != nil {
|
|
t.Error(closeErr)
|
|
}
|
|
})
|
|
create, err := connection.Prepare("CREATE TABLE settings (id INTEGER PRIMARY KEY, value TEXT)")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := create.Exec(nil); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := create.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
enableSQLiteAttribution()
|
|
transaction, err := connection.Begin()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
insert, err := connection.Prepare("INSERT INTO settings (value) VALUES (?)")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := insert.Exec([]driver.Value{"retained-descriptor"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := insert.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, descriptor, active := sqliteAttributionTransactionState(t, connection)
|
|
if !active {
|
|
t.Fatal("active transaction is missing before Commit")
|
|
}
|
|
|
|
// When
|
|
commitErr := transaction.Commit()
|
|
_, descriptorErr := unix.FcntlInt(uintptr(descriptor), unix.F_GETFD, 0)
|
|
|
|
// Then
|
|
if commitErr != nil {
|
|
t.Fatal(commitErr)
|
|
}
|
|
if !errors.Is(descriptorErr, unix.EBADF) {
|
|
t.Fatalf("retained journal descriptor remains open: %v", descriptorErr)
|
|
}
|
|
}
|
|
|
|
func TestSQLiteAttributionQueryRowReturningPoisonsTransaction(t *testing.T) {
|
|
// Given
|
|
database := openSQLiteAttributionTestDatabase(t)
|
|
enableSQLiteAttribution()
|
|
transaction, err := database.BeginTx(context.Background(), nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// When
|
|
var identifier int64
|
|
scanErr := transaction.QueryRowContext(context.Background(), "INSERT INTO settings (value) VALUES (?) RETURNING id", "query-row").Scan(&identifier)
|
|
commitErr := transaction.Commit()
|
|
|
|
// Then
|
|
if scanErr != nil {
|
|
t.Fatal(scanErr)
|
|
}
|
|
if commitErr == nil {
|
|
t.Fatal("QueryRowContext RETURNING committed without reaching EOF")
|
|
}
|
|
if sqliteAttributionTrackerHasWrite() {
|
|
t.Fatal("QueryRowContext RETURNING published evidence without EOF")
|
|
}
|
|
}
|