Skip to content

Commit

Permalink
WIP: Add integration test for GraphQL API
Browse files Browse the repository at this point in the history
  • Loading branch information
varungandhi-src committed May 20, 2024
1 parent 11fb60b commit 979bc4f
Show file tree
Hide file tree
Showing 6 changed files with 215 additions and 2 deletions.
1 change: 1 addition & 0 deletions dev/gqltest/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ go_test(
"access_token_test.go",
"bitbucket_projects_perms_sync_test.go",
"code_insights_test.go",
"code_intel_test.go",
"compute_test.go",
"external_service_test.go",
"feature_flag_test.go",
Expand Down
67 changes: 67 additions & 0 deletions dev/gqltest/code_intel_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package main

import (
"fmt"
"testing"
"time"

"github.com/stretchr/testify/require"

"github.com/sourcegraph/sourcegraph/internal/extsvc"
"github.com/sourcegraph/sourcegraph/internal/gqltestutil"
"github.com/sourcegraph/sourcegraph/lib/pointers"
"github.com/sourcegraph/sourcegraph/schema"
)

func unwrap[T any](v T, err error) func(*testing.T) T {
return func(t *testing.T) T {
require.NoError(t, err)
return v
}
}

func TestCodeGraphAPIs(t *testing.T) {
if len(*githubToken) == 0 {
t.Skip("Environment variable GITHUB_TOKEN is not set")
}

reset, err := client.ModifySiteConfiguration(func(siteConfig *schema.SiteConfiguration) {
siteConfig.CodeIntelAutoIndexingEnabled = pointers.Ptr(true)
})
require.NoError(t, err)
if reset != nil {
t.Cleanup(func() {
require.NoError(t, reset())
})
}

extSvcID := unwrap(client.AddExternalService(gqltestutil.AddExternalServiceInput{
Kind: extsvc.KindGitHub,
DisplayName: "gqltest-code-graph-apis",
Config: mustMarshalJSONString(&schema.GitHubConnection{
Authorization: &schema.GitHubAuthorization{},
Url: "https://ghe.sgdev.org/",
Token: *githubToken,
Repos: []string{
"sgtest/go-diff",
},
RepositoryPathPattern: "github.com/{nameWithOwner}",
}),
}))(t)

removeExternalServiceAfterTest(t, extSvcID)
start := time.Now()
err = client.WaitForReposToBeCloned("github.com/sgtest/go-diff")
cloneTime := time.Since(start)
require.NoError(t, err)

jobs := unwrap(client.TriggerAutoIndexing("github.com/sgtest/go-diff"))(t)

timeout := 2 * time.Minute
start = time.Now()
jobStateMap, err := client.WaitForAutoIndexingJobsToComplete(jobs, timeout)
require.NoErrorf(t, err, "jobStateMap: %v", jobStateMap)
autoIndexingTime := time.Since(start)
panic(fmt.Sprintf("Auto-indexing timing: indexing time - %s, clone time - %s", autoIndexingTime, cloneTime))
panic("Auto-indexing job completed!")
}
1 change: 0 additions & 1 deletion dev/gqltest/site_config_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package main

import (
"github.com/stretchr/testify/require"
"strings"
"testing"
"time"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
uploadsgraphql "github.com/sourcegraph/sourcegraph/internal/codeintel/uploads/transport/graphql"
"github.com/sourcegraph/sourcegraph/internal/observation"
"github.com/sourcegraph/sourcegraph/lib/codeintel/autoindex/config"
"github.com/sourcegraph/sourcegraph/lib/errors"
"github.com/sourcegraph/sourcegraph/lib/pointers"
)

Expand Down Expand Up @@ -83,7 +84,7 @@ func (r *rootResolver) QueueAutoIndexJobsForRepo(ctx context.Context, args *reso

repositoryID, err := resolverstubs.UnmarshalID[api.RepoID](args.Repository)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "malformed repository ID")
}

rev := "HEAD"
Expand Down
2 changes: 2 additions & 0 deletions internal/gqltestutil/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ go_library(
"batch_specs.go",
"client.go",
"code_insights.go",
"code_intel.go",
"compute.go",
"external_service.go",
"git.go",
Expand Down Expand Up @@ -35,5 +36,6 @@ go_library(
"//schema",
"@com_github_graph_gophers_graphql_go//:graphql-go",
"@com_github_json_iterator_go//:go",
"@com_github_sourcegraph_conc//pool",
],
)
143 changes: 143 additions & 0 deletions internal/gqltestutil/code_intel.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package gqltestutil

import (
"context"
"sync"
"time"

"github.com/graph-gophers/graphql-go"

"github.com/sourcegraph/conc/pool"
"github.com/sourcegraph/sourcegraph/lib/errors"
)

type IndexingJob struct {
ID graphql.ID
RepoID graphql.ID
}

type AutoIndexJobMap map[string]IndexingJob

// TriggerAutoIndexing enqueues auto-indexing jobs for the provided repos
func (c *Client) TriggerAutoIndexing(repos ...string) (AutoIndexJobMap, error) {
const query = `
query GetRepoIds($repoCount: Int!, $repos: [String!]!) {
repositories(first: $repoCount, names: $repos) {
nodes {
id
name
}
}
}
`
variables := map[string]any{
"repoCount": len(repos),
"repos": repos,
}
var resp struct {
Data struct {
NewRepositoryConnection struct {
Nodes []struct {
ID graphql.ID `json:"id"`
Name string `json:"name"`
} `json:"nodes"`
} `json:"repositories"`
} `json:"data"`
}

err := c.GraphQL("", query, variables, &resp)
if err != nil {
return AutoIndexJobMap{}, errors.Wrap(err, "request GraphQL")
}

mapping := map[string]IndexingJob{}
for _, repo := range resp.Data.NewRepositoryConnection.Nodes {
const mutation = `
mutation AutoIndexRepos($repoID: ID!) {
queueAutoIndexJobsForRepo(repository: $repoID) {
id
}
}
`
variables := map[string]any{
"repoID": repo.ID,
}
var resp struct {
Data struct {
QueueAutoIndexJobsForRepo []struct {
ID graphql.ID `json:"id"`
} `json:"queueAutoIndexJobsForRepo"`
} `json:"data"`
}
err := c.GraphQL("", mutation, variables, &resp)
if err != nil {
return AutoIndexJobMap{}, errors.Wrapf(err, "failed to queue auto-indexing job for repo: %v", repo.Name)
}
if len(resp.Data.QueueAutoIndexJobsForRepo) != 1 {
return AutoIndexJobMap{}, errors.Newf("unexpected number of auto-indexing jobs: %v", len(resp.Data.QueueAutoIndexJobsForRepo))
}
mapping[repo.Name] = IndexingJob{
ID: resp.Data.QueueAutoIndexJobsForRepo[0].ID,
RepoID: repo.ID,
}
}
return mapping, nil
}

type JobState struct {
State string `json:"state"`
PlaceInQueue int `json:"placeInQueue"`
}

type JobStateMap map[graphql.ID]JobState

func (c *Client) WaitForAutoIndexingJobsToComplete(jobMap AutoIndexJobMap, timeout time.Duration) (JobStateMap, error) {
ctx, cancelFunc := context.WithTimeout(context.Background(), timeout)
defer cancelFunc()

mtx := sync.Mutex{}
jobStateMap := JobStateMap{}

workPool := pool.New().WithErrors().WithContext(ctx)
for repoName, jobInfo := range jobMap {
jobId := jobInfo.ID
workPool.Go(func(ctx context.Context) error {
for {
if err := ctx.Err(); err != nil {
return err
}
const query = `
query GetJobById($jobID: ID!) {
node(id: $jobID) {
... on PreciseIndex {
state
placeInQueue
}
}
}
`
variables := map[string]any{"jobID": jobId}
var resp struct {
Data struct {
Node JobState `json:"node"`
} `json:"data"`
}
err := c.GraphQL("", query, variables, &resp)
if err != nil {
return errors.Wrapf(err, "when requesting index status for repo: %v, jobID: %v", repoName, jobId)
}
mtx.Lock()
jobStateMap[jobId] = resp.Data.Node
mtx.Unlock()
if resp.Data.Node.State == "COMPLETED" {
return nil
}
time.Sleep(100 * time.Millisecond)
}
})
}
if err := workPool.Wait(); err != nil {
return jobStateMap, errors.Wrap(err, "error in work pool")
}
return jobStateMap, nil
}

0 comments on commit 979bc4f

Please sign in to comment.