122 lines
4.2 KiB
Go
122 lines
4.2 KiB
Go
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)
|
|
}
|
|
}
|