From f2f187fd45ee62f33718c3fc70009ec087d1c2b2 Mon Sep 17 00:00:00 2001 From: Stephen Gutekanst Date: Wed, 27 Jan 2021 01:43:45 -0700 Subject: [PATCH 1/2] enterprise/internal/insights: add basic store package + testing infrastructure This adds the basic store package implementation (just the groundowork, no actual implementation) based on what we do in campaigns, code monitoring, etc. I am sending this as a separate PR before implementing the store, as it is a non-trivial amount of setup code which is nice to track separately for purposes of documentation and for seeing how we can improve adding backends like this, campaigns, code monitoring, etc. in the future. Helps #17218 Signed-off-by: Stephen Gutekanst --- enterprise/internal/insights/insights.go | 5 +- .../internal/insights/resolvers/resolver.go | 17 +++++-- .../internal/insights/store/insights_test.go | 12 +++++ .../insights/store/integration_test.go | 46 +++++++++++++++++++ enterprise/internal/insights/store/store.go | 41 +++++++++++++++++ 5 files changed, 115 insertions(+), 6 deletions(-) create mode 100644 enterprise/internal/insights/store/insights_test.go create mode 100644 enterprise/internal/insights/store/integration_test.go create mode 100644 enterprise/internal/insights/store/store.go diff --git a/enterprise/internal/insights/insights.go b/enterprise/internal/insights/insights.go index b057410eccb8..b64c20555c13 100644 --- a/enterprise/internal/insights/insights.go +++ b/enterprise/internal/insights/insights.go @@ -19,11 +19,12 @@ func Init(ctx context.Context, enterpriseServices *enterprise.Services) error { // TimescaleDB in those deployments. https://github.com/sourcegraph/sourcegraph/issues/17218 return nil } - _, err := initializeCodeInsightsDB() + timescale, err := initializeCodeInsightsDB() if err != nil { return err } - enterpriseServices.InsightsResolver = resolvers.New() + postgres := dbconn.Global + enterpriseServices.InsightsResolver = resolvers.New(timescale, postgres) return nil } diff --git a/enterprise/internal/insights/resolvers/resolver.go b/enterprise/internal/insights/resolvers/resolver.go index 377553792d5d..f4660cba316d 100644 --- a/enterprise/internal/insights/resolvers/resolver.go +++ b/enterprise/internal/insights/resolvers/resolver.go @@ -5,14 +5,23 @@ import ( "errors" "github.com/sourcegraph/sourcegraph/cmd/frontend/graphqlbackend" + "github.com/sourcegraph/sourcegraph/enterprise/internal/campaigns/store" + "github.com/sourcegraph/sourcegraph/internal/database" + "github.com/sourcegraph/sourcegraph/internal/database/dbutil" ) // Resolver is the GraphQL resolver of all things related to Insights. -type Resolver struct{} +type Resolver struct { + store *store.Store + settingStore *database.SettingStore +} -// New returns a new Resolver whose store uses the given database -func New() graphqlbackend.InsightsResolver { - return &Resolver{} +// New returns a new Resolver whose store uses the given Timescale and Postgres DBs. +func New(timescale, postgres dbutil.DB) graphqlbackend.InsightsResolver { + return &Resolver{ + store: store.New(timescale), + settingStore: database.Settings(postgres), + } } func (r *Resolver) Insights(ctx context.Context) (graphqlbackend.InsightsResolver, error) { diff --git a/enterprise/internal/insights/store/insights_test.go b/enterprise/internal/insights/store/insights_test.go new file mode 100644 index 000000000000..ae5e39192d17 --- /dev/null +++ b/enterprise/internal/insights/store/insights_test.go @@ -0,0 +1,12 @@ +package store + +import ( + "context" + "testing" + "time" +) + +func testInsights(t *testing.T, ctx context.Context, s *Store, clock func() time.Time) { + // TODO: write tests against the store once it is implemented + // https://github.com/sourcegraph/sourcegraph/issues/17218 +} diff --git a/enterprise/internal/insights/store/integration_test.go b/enterprise/internal/insights/store/integration_test.go new file mode 100644 index 000000000000..610b510860d8 --- /dev/null +++ b/enterprise/internal/insights/store/integration_test.go @@ -0,0 +1,46 @@ +package store + +import ( + "context" + "database/sql" + "os" + "os/user" + "strings" + "testing" + + "github.com/sourcegraph/sourcegraph/internal/database/dbconn" + "github.com/sourcegraph/sourcegraph/internal/database/dbutil" + "github.com/sourcegraph/sourcegraph/internal/timeutil" +) + +func TestIntegration(t *testing.T) { + if testing.Short() { + t.Skip() + } + + t.Parallel() + + getTimescaleDB := func(t testing.TB) *sql.DB { + // Setup TimescaleDB for testing. + username := "" + if user, err := user.Current(); err == nil { + username = user.Username + } + timescaleDSN := dbutil.PostgresDSN("codeinsights", username, os.Getenv) + db, err := dbconn.New(timescaleDSN, "insights-test-"+strings.Replace(t.Name(), "/", "_", -1)) + if err != nil { + t.Fatalf("Failed to connect to codeinsights database: %s", err) + } + if err := dbconn.MigrateDB(db, dbconn.CodeInsights); err != nil { + t.Fatalf("Failed to perform codeinsights database migration: %s", err) + } + return db + } + + t.Run("Integration", func(t *testing.T) { + ctx := context.Background() + clock := timeutil.Now + store := NewWithClock(getTimescaleDB(t), clock) + t.Run("Insights", func(t *testing.T) { testInsights(t, ctx, store, clock) }) + }) +} diff --git a/enterprise/internal/insights/store/store.go b/enterprise/internal/insights/store/store.go new file mode 100644 index 000000000000..fc9a98f4bb04 --- /dev/null +++ b/enterprise/internal/insights/store/store.go @@ -0,0 +1,41 @@ +package store + +import ( + "database/sql" + "time" + + "github.com/sourcegraph/sourcegraph/internal/database/basestore" + "github.com/sourcegraph/sourcegraph/internal/database/dbutil" + "github.com/sourcegraph/sourcegraph/internal/timeutil" +) + +// Store exposes methods to read and write code insights domain models from +// persistent storage. +type Store struct { + *basestore.Store + now func() time.Time +} + +// New returns a new Store backed by the given Timescale db. +func New(db dbutil.DB) *Store { + return NewWithClock(db, timeutil.Now) +} + +// NewWithClock returns a new Store backed by the given db and +// clock for timestamps. +func NewWithClock(db dbutil.DB, clock func() time.Time) *Store { + return &Store{Store: basestore.NewWithDB(db, sql.TxOptions{}), now: clock} +} + +var _ basestore.ShareableStore = &Store{} + +// Handle returns the underlying transactable database handle. +// Needed to implement the ShareableStore interface. +func (s *Store) Handle() *basestore.TransactableHandle { return s.Store.Handle() } + +// With creates a new Store with the given basestore.Shareable store as the +// underlying basestore.Store. +// Needed to implement the basestore.Store interface +func (s *Store) With(other basestore.ShareableStore) *Store { + return &Store{Store: s.Store.With(other), now: s.now} +} From 199a53a1ed207a9a83f48dd065fa7eb794f74e8d Mon Sep 17 00:00:00 2001 From: Stephen Gutekanst Date: Wed, 27 Jan 2021 18:12:12 -0700 Subject: [PATCH 2/2] dev/ci: start TimescaleDB on CI Signed-off-by: Stephen Gutekanst --- dev/ci/go-test.sh | 4 ++++ enterprise/internal/insights/store/integration_test.go | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/dev/ci/go-test.sh b/dev/ci/go-test.sh index cdb41eeeeb90..d218eaead4ad 100755 --- a/dev/ci/go-test.sh +++ b/dev/ci/go-test.sh @@ -10,6 +10,10 @@ echo "--- build libsqlite" echo "--- comby install" ./dev/comby-install-or-upgrade.sh +# For code insights test +./dev/codeinsights-db.sh & +export CODEINSIGHTS_PGDATASOURCE=postgres://postgres:password@127.0.0.1:5435 + # Separate out time for go mod from go test echo "--- go mod download" go mod download diff --git a/enterprise/internal/insights/store/integration_test.go b/enterprise/internal/insights/store/integration_test.go index 610b510860d8..f314f3a16766 100644 --- a/enterprise/internal/insights/store/integration_test.go +++ b/enterprise/internal/insights/store/integration_test.go @@ -29,6 +29,14 @@ func TestIntegration(t *testing.T) { timescaleDSN := dbutil.PostgresDSN("codeinsights", username, os.Getenv) db, err := dbconn.New(timescaleDSN, "insights-test-"+strings.Replace(t.Name(), "/", "_", -1)) if err != nil { + t.Log("") + t.Log("README: To run these tests you need to have the codeinsights TimescaleDB running:") + t.Log("") + t.Log("$ ./dev/codeinsights-db.sh &") + t.Log("$ export CODEINSIGHTS_PGDATASOURCE=postgres://postgres:password@127.0.0.1:5435") + t.Log("") + t.Log("Or skip them with 'go test -short'") + t.Log("") t.Fatalf("Failed to connect to codeinsights database: %s", err) } if err := dbconn.MigrateDB(db, dbconn.CodeInsights); err != nil {