Skip to content

Commit

Permalink
Add integration test workflow (#154)
Browse files Browse the repository at this point in the history
* Add integration test workflow

* Add integration tests
  • Loading branch information
adamyeats authored Apr 15, 2024
1 parent 3202bbd commit b00dc71
Show file tree
Hide file tree
Showing 4 changed files with 316 additions and 35 deletions.
58 changes: 58 additions & 0 deletions .github/workflows/integration.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Integration Tests

on:
workflow_run:
workflows: [CI]
types:
- completed

jobs:
e2e:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'yarn'

- name: Install dependencies
run: yarn install --frozen-lockfile

- name: Download plugin-dist artifact
uses: actions/download-artifact@v4
with:
name: plugin-dist
path: dist/
github-token: ${{ secrets.GH_PAT }}
repository: ${{ env.GITHUB_REPOSITORY }}
run-id: ${{ github.event.workflow_run.id }}

# actions/download-artifact@v4 does not keep executable permissions
# intact, so we need to restore them manually.
- name: Restore executable permissions
run: |
TARGET_DIR="dist/"
if [ -d "$TARGET_DIR" ]; then
for file in ${TARGET_DIR}gpx_*; do
if [ -f "$file" ] && [ ! -x "$file" ]; then
echo "Setting executable permission for $file"
chmod +x "$file"
fi
done
else
echo "Directory $TARGET_DIR does not exist."
fi
- name: Install and run Docker Compose
uses: isbang/compose-action@v1.5.1
with:
compose-file: './docker-compose.yaml'

- name: Run integration tests
run: go test -v ./e2e/datasource/**

timeout-minutes: 60
257 changes: 257 additions & 0 deletions e2e/datasource/datasource_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
package integration_test

import (
"context"
"encoding/json"
"os"
"testing"

"github.com/grafana-labs/surrealdb-datasource/pkg/client"
"github.com/grafana-labs/surrealdb-datasource/pkg/plugin"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt"
)

// table is a list of test cases for the QueryData method.
var cases = []struct {
input string
name string
}{
{
input: "SELECT * FROM person WHERE name = 'Richie Buck';",
name: "SELECT query with WHERE clause",
},
{
input: "SELECT id, name FROM person;",
name: "SELECT specific columns",
},
{
input: "SELECT count() FROM person GROUP all;",
name: "Aggregate function",
},
{
input: "SELECT * FROM person ORDER BY name DESC;",
name: "SELECT query with ORDER BY clause",
},
{
input: "SELECT * FROM person WHERE first_name ~ 'rich';",
name: "SELECT with fuzzy equality",
},
{
input: "SELECT * FROM person WHERE company_name == NONE;",
name: "SELECT with IS NONE",
},
{
input: "SELECT * FROM person WHERE company_name != NONE;",
name: "SELECT with IS NOT NONE",
},
{
input: "SELECT * FROM person LIMIT 10;",
name: "SELECT with LIMIT",
},
{
input: "SELECT product.name FROM review;",
name: "SELECT with Record links",
},
}

// getSurrealEndpoint returns the endpoint for the SurrealDB server.
func getSurrealEndpoint() string {
env := os.Getenv("SURREAL_DB_URL")

if env != "" {
return env
}

if os.Getenv("CI") != "true" {
return "ws://localhost:8000/rpc"
}

return "ws://surrealdb:8000/rpc"
}

// createTestInstance creates a new SurrealDatasource instance for testing.
func createTestInstance() (instancemgmt.Instance, error) {
config := client.SurrealConfig{
Database: "test",
Endpoint: getSurrealEndpoint(),
Namespace: "test",
Username: "root",
}

msg, err := json.Marshal(config)

if err != nil {
return nil, err
}

dsiConfig := backend.DataSourceInstanceSettings{
JSONData: msg,
DecryptedSecureJSONData: map[string]string{"password": "test"},
}

return plugin.NewDatasource(context.Background(), dsiConfig)
}

// createJsonRequest creates a new JSON request for testing.
func createJsonRequest(query string) json.RawMessage {
return json.RawMessage(`{"queryText":"` + query + `"}`)
}

func TestNewDatasource(t *testing.T) {
instance, err := createTestInstance()

if err != nil {
t.Errorf("unexpected error: %s", err)
}

if instance == nil {
t.Error("expected instance to be non-nil")
}

if _, ok := instance.(*plugin.SurrealDatasource); ok {
t.Error("expected instance to be the correct type")
}
}

func TestCheckHealth(t *testing.T) {
instance, err := createTestInstance()

if err != nil {
t.Errorf("unexpected error: %s", err)
}

res, err := (*instance.(*plugin.SurrealDatasource)).CheckHealth(context.Background(), &backend.CheckHealthRequest{})

if err != nil {
t.Errorf("unexpected error: %s", err)
}

if res == nil {
t.Error("expected response to be non-nil")
}
}

func TestQueryData_Success(t *testing.T) {
instance, err := createTestInstance()

if err != nil {
t.Errorf("unexpected error: %s", err)
}

for _, tt := range cases {
t.Run(tt.name, func(t *testing.T) {
dqs := []backend.DataQuery{
{
RefID: "A",
JSON: createJsonRequest(tt.input),
},
}

req := backend.QueryDataRequest{
PluginContext: backend.PluginContext{
OrgID: 1,
},
Queries: dqs,
}

res, err := (*instance.(*plugin.SurrealDatasource)).QueryData(context.Background(), &req)

if err != nil {
t.Errorf("unexpected error: %s", err)
}

if res == nil {
t.Error("expected response to be non-nil")
} else {
if len(res.Responses) != 1 {
t.Errorf("expected 1 response, got %d", len(res.Responses))
}

if res.Responses["A"].Frames == nil {
t.Error("expected frames to be non-nil")
}

if res.Responses["A"].Frames[0].Fields == nil {
t.Error("expected fields to be non-nil")
}
}
})
}
}

func TestQueryData_BadTableName(t *testing.T) {
instance, err := createTestInstance()

if err != nil {
t.Errorf("unexpected error: %s", err)
}

dqs := []backend.DataQuery{
{
RefID: "A",
JSON: createJsonRequest("SELECT * FROM does_not_exist;"),
},
}

req := backend.QueryDataRequest{
PluginContext: backend.PluginContext{
OrgID: 1,
},
Queries: dqs,
}

res, err := (*instance.(*plugin.SurrealDatasource)).QueryData(context.Background(), &req)

if err != nil {
t.Errorf("unexpected error: %s", err)
} else {
if len(res.Responses) != 1 {
t.Errorf("expected 1 response, got %d", len(res.Responses))
}

if res.Responses["A"].Frames != nil {
t.Error("expected frames to be nil")
}
}
}

func TestQueryData_BadQuerySyntax(t *testing.T) {
instance, err := createTestInstance()

if err != nil {
t.Errorf("unexpected error: %s", err)
}

dqs := []backend.DataQuery{
{
RefID: "A",
JSON: createJsonRequest("SELECT * FROM person JOIN address ON person.id = address.person_id;"),
},
}

req := backend.QueryDataRequest{
PluginContext: backend.PluginContext{
OrgID: 1,
},
Queries: dqs,
}

res, err := (*instance.(*plugin.SurrealDatasource)).QueryData(context.Background(), &req)

if err != nil {
t.Errorf("unexpected error: %s", err)
} else {
if len(res.Responses) != 1 {
t.Errorf("expected 1 response, got %d", len(res.Responses))
}

if res.Responses["A"].Frames != nil {
t.Error("expected frames to be nil")
}

if res.Responses["A"].Error == nil {
t.Error("expected error to be non-nil")
}
}
}
4 changes: 1 addition & 3 deletions pkg/plugin/datasource.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,7 @@ func NewDatasource(ctx context.Context, dsiConfig backend.DataSourceInstanceSett
return NewDatasourceInstance(db, &config), nil
}

// Dispose here tells plugin SDK that plugin wants to clean up resources when a new instance
// created. As soon as datasource settings change detected by SDK old datasource instance will
// be disposed and a new one will be created using NewSampleDatasource factory function.
// Dispose cleans up the datasource instance resources.
func (d *SurrealDatasource) Dispose() {
// Clean up datasource instance resources.
// d.db.Close()
Expand Down
32 changes: 0 additions & 32 deletions pkg/plugin/datasource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,6 @@ import (
"github.com/grafana/grafana-plugin-sdk-go/backend"
)

// IMPORTANT!
//
// As it stands, this test suite requires a running instance of SurrealDB. You
// can start one by bringing up the Docker containers with the following command:
//
// `docker-compose up -d`
//
// What can be tested in isolation at the moment, is being tested in isolation.

var mock = mocks.MockSurrealDBClient{}

var config = client.SurrealConfig{
Expand All @@ -30,29 +21,6 @@ var config = client.SurrealConfig{
Username: "grafana",
}

// @TODO: Move this test to an integration test suite.
// func TestNewDatasource_Success(t *testing.T) {
// msg, err := json.Marshal(config)

// if err != nil {
// t.Errorf("unexpected error: %s", err)
// }

// config := backend.DataSourceInstanceSettings{
// JSONData: msg,
// DecryptedSecureJSONData: map[string]string{"password": "password"},
// }

// instance, err := plugin.NewDatasource(context.Background(), config)

// if err != nil {
// t.Errorf("unexpected error: %s", err)
// }
// if instance == nil {
// t.Error("expected instance to be non-nil")
// }
// }

func TestNewDatasource_InvalidJSON(t *testing.T) {
config := backend.DataSourceInstanceSettings{
JSONData: json.RawMessage(`invalid json`),
Expand Down

0 comments on commit b00dc71

Please sign in to comment.