Skip to content
This repository has been archived by the owner on Aug 12, 2022. It is now read-only.

feat: Testing. More ways to verify table content. #221

Merged
merged 4 commits into from
Apr 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions provider/testing/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,15 @@ type ResourceTestCase struct {
ParallelFetchingLimit uint64
// SkipIgnoreInTest flag which detects if schema.Table or schema.Column should be ignored
SkipIgnoreInTest bool
// Verifiers are map from resource name to its verifiers.
// If no verifiers specified for resource (resource name is not in key set of map),
// non emptiness check of all columns in table and its relations will be performed.
Verifiers map[string][]Verifier
}

// Verifier verifies tables specified by table schema (main table and its relations).
type Verifier func(t *testing.T, table *schema.Table, conn pgxscan.Querier, shouldSkipIgnoreInTest bool)

func init() {
_ = faker.SetRandomMapAndSliceMinSize(1)
_ = faker.SetRandomMapAndSliceMaxSize(1)
Expand Down Expand Up @@ -67,10 +74,17 @@ func TestResource(t *testing.T, resource ResourceTestCase) {
if err = fetch(t, &resource); err != nil {
t.Fatal(err)
}
for _, table := range resource.Provider.ResourceMap {
verifyNoEmptyColumns(t, table, conn, resource.SkipIgnoreInTest)
}

for resourceName, table := range resource.Provider.ResourceMap {
if verifiers, ok := resource.Verifiers[resourceName]; ok {
for _, verifier := range verifiers {
verifier(t, table, conn, resource.SkipIgnoreInTest)
}
} else {
// fallback to default verification
verifyNoEmptyColumns(t, table, conn, resource.SkipIgnoreInTest)
}
}
}

// fetch - fetches resources from the cloud and puts them into database. database config can be specified via DATABASE_URL env variable
Expand Down
103 changes: 103 additions & 0 deletions provider/testing/verifiers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package testing
roneli marked this conversation as resolved.
Show resolved Hide resolved

import (
"context"
"fmt"
"testing"

"github.com/cloudquery/cq-provider-sdk/provider/schema"
"github.com/cloudquery/faker/v3/support/slice"
"github.com/georgysavva/scany/pgxscan"
)

type Row map[string]interface{}

// VerifyRowPredicateInTable is a base verifier accepting single row verifier for specific table from schema
func VerifyRowPredicateInTable(tableName string, rowVerifier func(*testing.T, Row)) Verifier {
var verifier Verifier
verifier = func(t *testing.T, table *schema.Table, conn pgxscan.Querier, shouldSkipIgnoreInTest bool) {
if tableName == table.Name {
rows := getRows(t, conn, table, shouldSkipIgnoreInTest)
for _, row := range rows {
rowVerifier(t, row)
}
}
for _, r := range table.Relations {
verifier(t, r, conn, shouldSkipIgnoreInTest)
}
}
return verifier
}

func getRows(t *testing.T, conn pgxscan.Querier, table *schema.Table, shouldSkipIgnoreInTest bool) []Row {
if shouldSkipIgnoreInTest && table.IgnoreInTests {
t.Skipf("table %s marked as IgnoreInTest. Skipping...", table.Name)
}

var rows []Row
err := pgxscan.Get(
context.Background(),
conn,
&rows,
fmt.Sprintf("select json_agg(%[1]s) from %[1]s", table.Name),
)
if err != nil {
t.Fatal(err)
}

for _, c := range table.Columns {
if shouldSkipIgnoreInTest && c.IgnoreInTests {
for _, row := range rows {
delete(row, c.Name)
}
}
}

return rows
}

// VerifyNoEmptyColumnsExcept verifies that for each row in table its columns are not empty except passed
func VerifyNoEmptyColumnsExcept(tableName string, except ...string) Verifier {
return VerifyRowPredicateInTable(tableName, func(t *testing.T, row Row) {
for k, v := range row {
if !slice.Contains(except, k) && v == nil {
t.Fatal("VerifyNoEmptyColumnsExcept failed: illegal row found")
}
}
})
}

// VerifyAtMostOneOf verifies that for each row in table at most one column from oneof is not empty
func VerifyAtMostOneOf(tableName string, oneof ...string) Verifier {
return VerifyRowPredicateInTable(tableName, func(t *testing.T, row Row) {
cnt := 0
for _, k := range oneof {
v, ok := row[k]
if !ok {
t.Fatalf("VerifyAtMostOneOf failed: column %s doesn't exist", k)
}
if v != nil {
cnt++
}
}

if cnt > 1 {
t.Fatal("VerifyAtMostOneOf failed: illegal row found")
}
})
}

// VerifyAtLeastOneRow verifies that main table from schema has at least one row
func VerifyAtLeastOneRow() Verifier {
return func(t *testing.T, table *schema.Table, conn pgxscan.Querier, _ bool) {
rows, err := conn.Query(context.Background(), fmt.Sprintf("select * from %s;", table.Name))
if err != nil {
t.Fatal(err)
}
if !rows.Next() {
t.Fatal("VerifyAtLeastOneRow failed: table is empty")
}

rows.Close()
}
}