mirror of
https://github.com/Buriburizaem0n/nezha_domains.git
synced 2026-09-19 09:40:12 +00:00
205 lines
5.2 KiB
Go
205 lines
5.2 KiB
Go
//go:build agentcompat && linux
|
|
|
|
package singleton
|
|
|
|
import (
|
|
"database/sql"
|
|
"database/sql/driver"
|
|
"errors"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
func TestSQLiteDriverAdapterRecordsExplicitInsert_when_AttributionEnabled(t *testing.T) {
|
|
resetSQLiteAttributionForTest()
|
|
databasePath := filepath.Join(t.TempDir(), "dashboard.sqlite")
|
|
database, err := openSQLiteAttributionTestDB(databasePath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() {
|
|
if closeErr := database.Close(); closeErr != nil {
|
|
t.Error(closeErr)
|
|
}
|
|
})
|
|
if _, err := database.Exec("CREATE TABLE settings (id INTEGER PRIMARY KEY, value TEXT)"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
enableSQLiteAttribution()
|
|
transaction, err := database.Begin()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := transaction.Exec("INSERT INTO settings (value) VALUES (?)", "opaque"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !sqliteAttributionTrackerHasWrite() {
|
|
t.Fatal("explicit insert did not record an atomic sqlite write")
|
|
}
|
|
if err := transaction.Commit(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestSQLiteDriverAdapterRecordsPreparedExplicitInsert_when_AttributionEnabled(t *testing.T) {
|
|
resetSQLiteAttributionForTest()
|
|
databasePath := filepath.Join(t.TempDir(), "dashboard.sqlite")
|
|
database, err := openSQLiteAttributionTestDB(databasePath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() {
|
|
if closeErr := database.Close(); closeErr != nil {
|
|
t.Error(closeErr)
|
|
}
|
|
})
|
|
if _, err := database.Exec("CREATE TABLE settings (id INTEGER PRIMARY KEY, value TEXT)"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
enableSQLiteAttribution()
|
|
transaction, err := database.Begin()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
statement, err := transaction.Prepare("INSERT INTO settings (value) VALUES (?)")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := statement.Exec("prepared"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := statement.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !sqliteAttributionTrackerHasWrite() {
|
|
t.Fatal("prepared explicit insert did not record an atomic sqlite write")
|
|
}
|
|
if err := transaction.Commit(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestSQLiteDriverAdapterRejectsUnboundWrite_when_AttributionEnabled(t *testing.T) {
|
|
database := openSQLiteAttributionTestDatabase(t)
|
|
enableSQLiteAttribution()
|
|
_, err := database.Exec("INSERT INTO settings (value) VALUES (?)", "unbound")
|
|
if !errors.Is(err, ErrSQLiteAttributionUnboundWrite) {
|
|
t.Fatalf("expected unbound write instrumentation error, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestSQLiteDriverAdapterRejectsUnsupportedDSN(t *testing.T) {
|
|
database, err := openSQLiteAttributionTestDB(":memory:")
|
|
if err == nil {
|
|
err = database.Ping()
|
|
}
|
|
if database != nil {
|
|
t.Cleanup(func() {
|
|
if closeErr := database.Close(); closeErr != nil {
|
|
t.Error(closeErr)
|
|
}
|
|
})
|
|
}
|
|
if !errors.Is(err, ErrSQLiteAttributionUnsupportedDSN) {
|
|
t.Fatalf("expected unsupported DSN error, got %v", err)
|
|
}
|
|
}
|
|
|
|
func openSQLiteAttributionTestDB(path string) (*sql.DB, error) {
|
|
registerSQLiteAttributionDriver()
|
|
return sql.Open(sqliteAttributionDriverName, path)
|
|
}
|
|
|
|
func openSQLiteAttributionTestDatabase(t *testing.T) *sql.DB {
|
|
t.Helper()
|
|
resetSQLiteAttributionForTest()
|
|
database, err := openSQLiteAttributionTestDB(sqliteAttributionTestDatabasePath(t))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() {
|
|
if closeErr := database.Close(); closeErr != nil {
|
|
t.Error(closeErr)
|
|
}
|
|
})
|
|
if _, err := database.Exec("CREATE TABLE settings (id INTEGER PRIMARY KEY, value TEXT)"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return database
|
|
}
|
|
|
|
func sqliteAttributionTestDatabasePath(t *testing.T) string {
|
|
t.Helper()
|
|
return filepath.Join(t.TempDir(), "dashboard.sqlite")
|
|
}
|
|
|
|
func sqliteAttributionSettingsCount(t *testing.T, database *sql.DB) int {
|
|
t.Helper()
|
|
var count int
|
|
if err := database.QueryRow("SELECT COUNT(*) FROM settings").Scan(&count); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return count
|
|
}
|
|
|
|
func sqliteAttributionSettingValue(t *testing.T, database *sql.DB) string {
|
|
t.Helper()
|
|
var value string
|
|
if err := database.QueryRow("SELECT value FROM settings LIMIT 1").Scan(&value); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return value
|
|
}
|
|
|
|
func sqliteAttributionConsumeRows(rows *sql.Rows) error {
|
|
if rows == nil {
|
|
return nil
|
|
}
|
|
var identifier int64
|
|
for rows.Next() {
|
|
if err := rows.Scan(&identifier); err != nil {
|
|
closeErr := rows.Close()
|
|
return errors.Join(err, closeErr)
|
|
}
|
|
}
|
|
err := rows.Err()
|
|
closeErr := rows.Close()
|
|
return errors.Join(err, closeErr)
|
|
}
|
|
|
|
func sqliteAttributionConsumeDriverRows(rows driver.Rows) error {
|
|
if rows == nil {
|
|
return nil
|
|
}
|
|
values := make([]driver.Value, len(rows.Columns()))
|
|
for {
|
|
err := rows.Next(values)
|
|
if err != nil {
|
|
closeErr := rows.Close()
|
|
return errors.Join(err, closeErr)
|
|
}
|
|
}
|
|
}
|
|
|
|
func sqliteAttributionTrackerHasWrite() bool {
|
|
return sqliteAttributionTrackerWriteEvidence().hasWrite
|
|
}
|
|
|
|
type sqliteAttributionWriteEvidence struct {
|
|
hasWrite bool
|
|
write SQLiteWriteObservation
|
|
}
|
|
|
|
func sqliteAttributionTrackerWriteEvidence() sqliteAttributionWriteEvidence {
|
|
tracker := sqliteAttributionTracker.Load()
|
|
tracker.mu.Lock()
|
|
defer tracker.mu.Unlock()
|
|
for _, held := range tracker.transactions {
|
|
if held.hasWrite {
|
|
return sqliteAttributionWriteEvidence{hasWrite: true, write: held.write}
|
|
}
|
|
}
|
|
return sqliteAttributionWriteEvidence{}
|
|
}
|