package sqlr import ( "context" "database/sql" "github.com/google/go-cmp/cmp" _ "github.com/mattn/go-sqlite3" "reflect" "testing" ) func TestScan(t *testing.T) { ctx := context.Background() for _, tc := range []struct { desc string db string query string want any }{{ desc: "match field names", db: ` CREATE TABLE Tbl (ColA, ColB); INSERT INTO Tbl VALUES (100, "test")`, query: "SELECT ColA, ColB FROM Tbl", want: &struct { ColA int ColB string }{100, "test"}, }, { desc: "select star", db: ` CREATE TABLE Tbl (ColA, ColB); INSERT INTO Tbl VALUES (100, "test")`, query: "SELECT * FROM Tbl", want: &struct { ColA int ColB string }{100, "test"}, }, { desc: "ignored field", db: ` CREATE TABLE Tbl (Col); INSERT INTO Tbl VALUES (100)`, query: "SELECT Col FROM Tbl", want: &struct{ Col, OtherField int }{Col: 100}, }, { desc: "tag", db: ` CREATE TABLE Tbl (col_a, col_b); INSERT INTO Tbl VALUES (100, "test")`, query: "SELECT col_a, col_b FROM Tbl", want: &struct { ColA int `sql:"col_a"` ColB string `sql:"col_b"` }{100, "test"}, }} { t.Run(tc.desc, func(t *testing.T) { db, err := sql.Open("sqlite3", ":memory:") if err != nil { t.Fatalf("Failed to create in-memory database: %s", err) } defer db.Close() if _, err := db.ExecContext(ctx, tc.db); err != nil { t.Fatalf("Failed to initialize test database: %s", err) } rows, err := db.QueryContext(ctx, tc.query) if err != nil { t.Fatalf("Failed to query test database: %s", err) } if !rows.Next() { t.Fatalf("No data") } got := reflect.New(reflect.TypeOf(tc.want).Elem()).Interface() if err := Scan(rows, got); err != nil { t.Fatalf("Scan failed: %s", err) } if err := rows.Close(); err != nil { t.Fatalf("Close rows: %s", err) } if err := rows.Err(); err != nil { t.Fatalf("Database read error: %s", err) } if diff := cmp.Diff(tc.want, got); diff != "" { t.Errorf("Scan returned unexpected diff (-want +got):\n%s", diff) } }) } } func TestScan_Errors(t *testing.T) { ctx := context.Background() for _, tc := range []struct { desc string db string query string out any }{{ desc: "wrong type", db: ` CREATE TABLE Tbl (Col); INSERT INTO Tbl VALUES (100)`, query: "SELECT Col FROM Tbl", out: 5, }, { desc: "nil pointer", db: ` CREATE TABLE Tbl (Col); INSERT INTO Tbl VALUES (100)`, query: "SELECT Col FROM Tbl", out: nil, }, { desc: "no field", db: ` CREATE TABLE Tbl (ColA, ColB); INSERT INTO Tbl VALUES (100, "test")`, query: "SELECT ColA, ColB FROM Tbl", out: new(struct{ ColA int }), }, { desc: "ignored field", db: ` CREATE TABLE Tbl (Col); INSERT INTO Tbl VALUES (100)`, query: "SELECT Col FROM Tbl", out: new(struct { Col int `sql:"-"` }), }} { t.Run(tc.desc, func(t *testing.T) { db, err := sql.Open("sqlite3", ":memory:") if err != nil { t.Fatalf("Failed to create in-memory database: %s", err) } defer db.Close() if _, err := db.ExecContext(ctx, tc.db); err != nil { t.Fatalf("Failed to initialize test database: %s", err) } rows, err := db.QueryContext(ctx, tc.query) if err != nil { t.Fatalf("Failed to query test database: %s", err) } defer rows.Close() if !rows.Next() { t.Fatalf("No data") } err = Scan(rows, tc.out) if err == nil { t.Errorf("Scan returned nil, want error") } }) } }