package monitor import ( "context" "database/sql" "database/sql/driver" "errors" "io" "strings" "sync" "testing" "time" ) var registerSingleConnDriver sync.Once func openSingleConnRegressionDB(t *testing.T) *sql.DB { t.Helper() registerSingleConnDriver.Do(func() { sql.Register("dockwatch-singleconn-regression", singleConnDriver{}) }) db, err := sql.Open("dockwatch-singleconn-regression", "") if err != nil { t.Fatal(err) } db.SetMaxOpenConns(1) t.Cleanup(func() { _ = db.Close() }) return db } type singleConnDriver struct{} func (singleConnDriver) Open(string) (driver.Conn, error) { return &singleConn{}, nil } type singleConn struct{} func (*singleConn) Prepare(string) (driver.Stmt, error) { return nil, errors.New("prepare not supported") } func (*singleConn) Close() error { return nil } func (*singleConn) Begin() (driver.Tx, error) { return nil, errors.New("tx not supported") } func (*singleConn) QueryContext(_ context.Context, query string, _ []driver.NamedValue) (driver.Rows, error) { switch { case strings.Contains(query, "FROM monitor_services ORDER BY name"): return newStaticRows([]string{"id", "name", "description", "created_at", "updated_at"}, [][]driver.Value{{int64(1), "Website", "", int64(1), int64(1)}}), nil case strings.Contains(query, "FROM status_pages ORDER BY name"): return newStaticRows([]string{"id", "name", "slug", "description", "enabled", "created_at", "updated_at"}, [][]driver.Value{{int64(1), "Public", "public", "", int64(1), int64(1), int64(1)}}), nil case strings.Contains(query, "FROM status_pages WHERE slug="): return newStaticRows([]string{"id", "name", "slug", "description", "enabled", "created_at", "updated_at"}, [][]driver.Value{{int64(1), "Public", "public", "", int64(1), int64(1), int64(1)}}), nil case strings.Contains(query, "FROM status_page_services WHERE page_id="): return newStaticRows([]string{"service_id"}, [][]driver.Value{{int64(1)}}), nil case strings.Contains(query, "JOIN status_page_services"): return newStaticRows([]string{"id", "name", "description", "created_at", "updated_at"}, [][]driver.Value{{int64(1), "Website", "", int64(1), int64(1)}}), nil case strings.Contains(query, "FROM monitors m"): // The regression only needs to verify that this second query can start // after the service rows were released. No monitor row is required. return newStaticRows([]string{"monitor"}, nil), nil default: return nil, errors.New("unexpected query: " + query) } } var _ driver.QueryerContext = (*singleConn)(nil) type staticRows struct { cols []string data [][]driver.Value pos int } func newStaticRows(cols []string, data [][]driver.Value) *staticRows { return &staticRows{cols: cols, data: data} } func (r *staticRows) Columns() []string { return r.cols } func (r *staticRows) Close() error { return nil } func (r *staticRows) Next(dest []driver.Value) error { if r.pos >= len(r.data) { return io.EOF } copy(dest, r.data[r.pos]) r.pos++ return nil } func shortContext(t *testing.T) context.Context { t.Helper() ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond) t.Cleanup(cancel) return ctx } func TestListGroupsDoesNotNestQueriesOnSingleConnection(t *testing.T) { s := &Service{db: openSingleConnRegressionDB(t)} groups, err := s.ListGroups(shortContext(t)) if err != nil { t.Fatalf("ListGroups: %v", err) } if len(groups) != 1 || groups[0].Name != "Website" { t.Fatalf("unexpected groups: %#v", groups) } } func TestListStatusPagesDoesNotNestQueriesOnSingleConnection(t *testing.T) { s := &Service{db: openSingleConnRegressionDB(t)} pages, err := s.ListStatusPages(shortContext(t)) if err != nil { t.Fatalf("ListStatusPages: %v", err) } if len(pages) != 1 || len(pages[0].ServiceIDs) != 1 || pages[0].ServiceIDs[0] != 1 { t.Fatalf("unexpected pages: %#v", pages) } } func TestPublicStatusPageDoesNotNestQueriesOnSingleConnection(t *testing.T) { s := &Service{db: openSingleConnRegressionDB(t)} page, err := s.PublicStatusPage(shortContext(t), "public") if err != nil { t.Fatalf("PublicStatusPage: %v", err) } if len(page.Services) != 1 || page.Services[0].Name != "Website" { t.Fatalf("unexpected public page: %#v", page) } }