Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SQL support to push test results to database file #60

Merged
merged 22 commits into from
Jun 23, 2023
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
42d4ad6
Add sql (sqlLite) support to push test results to database file
JudahNour Jun 7, 2023
98e058a
Correct lint errors and handle database rollback errors
JudahNour Jun 8, 2023
5bc5edf
Create lint makefile target and remove redundant if statement
JudahNour Jun 8, 2023
2068e7d
Fix naming, adding documentation, and responding to comments
JudahNour Jun 13, 2023
7627054
correcting Makefile flags
JudahNour Jun 13, 2023
c73c360
Create commit table with summary information
JudahNour Jun 13, 2023
dd37879
correct SQLite typo
JudahNour Jun 13, 2023
7233384
correcting typos
JudahNour Jun 14, 2023
5461397
Refactored Code to use enviornment variables and to possibly introduc…
JudahNour Jun 16, 2023
2a60ae2
created testdb target for gopogh database testing
JudahNour Jun 16, 2023
197cdaa
refactor testdb target
JudahNour Jun 16, 2023
6cb2919
adding documentation to functions
JudahNour Jun 16, 2023
f10cf93
changing table/row struct names for database
JudahNour Jun 20, 2023
48e4ec0
correct typos/lint errors and add documentation
JudahNour Jun 20, 2023
20f0c28
trigger GitHub actions
JudahNour Jun 20, 2023
6253f6f
fix makefile removal on testdb target
JudahNour Jun 20, 2023
ecd348d
make testdb more comprehensive, add information to database, and modi…
JudahNour Jun 21, 2023
6a44a29
change sqlite driver to disable cgo. adds db_backend flag
JudahNour Jun 22, 2023
7be351f
change go version in workflow to allow for sqlite driver
JudahNour Jun 22, 2023
d1ede93
exit when database driver is specified and database path is not
JudahNour Jun 22, 2023
d65f46c
create function to check for db variables
JudahNour Jun 22, 2023
6537101
change function name
JudahNour Jun 22, 2023
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
33 changes: 24 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,24 @@ VERSION := v0.17.0
build: out/gopogh

out/gopogh:
CGO_ENABLED=0 go build -ldflags="$(LDFLAGS)" -a -o $@ github.com/medyagh/gopogh/cmd/gopogh
JudahNour marked this conversation as resolved.
Show resolved Hide resolved
go build -ldflags="$(LDFLAGS)" -a -o $@ github.com/medyagh/gopogh/cmd/gopogh

out/gopogh-darwin-arm64:
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags="$(LDFLAGS)" -a -o $@ github.com/medyagh/gopogh/cmd/gopogh
GOOS=darwin GOARCH=arm64 go build -ldflags="$(LDFLAGS)" -a -o $@ github.com/medyagh/gopogh/cmd/gopogh

out/gopogh-darwin-amd64:
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags="$(LDFLAGS)" -a -o $@ github.com/medyagh/gopogh/cmd/gopogh
GOOS=darwin GOARCH=amd64 go build -ldflags="$(LDFLAGS)" -a -o $@ github.com/medyagh/gopogh/cmd/gopogh

out/gopogh-linux-amd64:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="$(LDFLAGS)" -a -o $@ github.com/medyagh/gopogh/cmd/gopogh
GOOS=linux GOARCH=amd64 go build -ldflags="$(LDFLAGS)" -a -o $@ github.com/medyagh/gopogh/cmd/gopogh

out/gopogh-linux-arm:
CGO_ENABLED=0 GOOS=linux GOARCH=arm go build -ldflags="$(LDFLAGS)" -a -o $@ github.com/medyagh/gopogh/cmd/gopogh
GOOS=linux GOARCH=arm go build -ldflags="$(LDFLAGS)" -a -o $@ github.com/medyagh/gopogh/cmd/gopogh

out/gopogh-linux-arm64:
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="$(LDFLAGS)" -a -o $@ github.com/medyagh/gopogh/cmd/gopogh
GOOS=linux GOARCH=arm64 go build -ldflags="$(LDFLAGS)" -a -o $@ github.com/medyagh/gopogh/cmd/gopogh
out/gopogh.exe:
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags="$(LDFLAGS)" -a -o $@ github.com/medyagh/gopogh/cmd/gopogh
GOOS=windows GOARCH=amd64 go build -ldflags="$(LDFLAGS)" -a -o $@ github.com/medyagh/gopogh/cmd/gopogh

# gopogh requires a json input, uses go tool test2json to convert to json
generate_json:
Expand All @@ -37,13 +37,28 @@ test: build
rm ./out/output.html || true
rm ./out/output2.html || true
.${BINARY} -name "KVM Linux" -repo "github.com/kubernetes/minikube/" -pr "6096" -in "testdata/minikube-logs.json" -out_html "./out/output.html" -out_summary out/output_summary.json -details "0c07e808219403a7241ee5a0fc6a85a897594339"
.${BINARY} -name "KVM Linux" -repo "github.com/kubernetes/minikube/" -pr "6096" -in "testdata/Docker_Linux.json" -out_html "./out/output2.html" -out_summary out/output2_summary.json "0c07e808219403a7241ee5a0fc6a85a897594339"
.${BINARY} -name "KVM Linux" -repo "github.com/kubernetes/minikube/" -pr "6096" -in "testdata/Docker_Linux.json" -out_html "./out/output2NoSummary.html" "0c07e808219403a7241ee5a0fc6a85a897594339"
.${BINARY} -name "KVM Linux" -repo "github.com/kubernetes/minikube/" -pr "6096" -in "testdata/Docker_Linux.json" -out_html "./out/output2.html" -out_summary out/output2_summary.json -details "0c07e808219403a7241ee5a0fc6a85a897594339"
.${BINARY} -name "KVM Linux" -repo "github.com/kubernetes/minikube/" -pr "6096" -in "testdata/Docker_Linux.json" -out_html "./out/output2NoSummary.html" -details "0c07e808219403a7241ee5a0fc6a85a897594339"

.PHONY: testdb
testdb: export DB_BACKEND=sqlite
testdb: export DB_PATH=out/testdb/output2_sqlite_NoDBPATH.db
testdb: build
rm -f ./out/output.html
rm -f ./out/output2.html
rm -f ./out/testdb/output_sqlite_summary.db
rm -f ./out/testdb/output2_sqlite_summary.db
.${BINARY} -name "KVM Linux" -repo "github.com/kubernetes/minikube/" -pr "6096" -in "testdata/minikube-logs.json" -out_html "./out/output.html" -out_summary out/output_summary.json -db_path out/testdb/output_sqlite_summary.db -details "0c07e808219403a7241ee5a0fc6a85a897594339"
.${BINARY} -name "KVM Linux" -repo "github.com/kubernetes/minikube/" -pr "6096" -in "testdata/Docker_Linux.json" -out_html "./out/output2.html" -out_summary out/output2_summary.json -db_path out/testdb/output2_sqlite_summary.db -details "0c07e808219403a7241ee5a0fc6a85a897594339"
.${BINARY} -name "KVM Linux" -repo "github.com/kubernetes/minikube/" -pr "6096" -in "testdata/Docker_Linux.json" -out_html "./out/output2NoDBPath.html" -details "0c07e808219403a7241ee5a0fc6a85a897594339"


.PHONY: cross
cross: out/gopogh-linux-amd64 out/gopogh-darwin-amd64 out/gopogh-darwin-arm64 out/gopogh.exe out/gopogh-linux-arm64 out/gopogh-linux-arm

.PHONY: lint
lint:
golangci-lint run --enable gofmt,goimports,gocritic,revive,gocyclo,misspell,nakedret,stylecheck,unconvert,unparam,dogsled

.PHONY: clean
clean:
Expand Down
8 changes: 8 additions & 0 deletions cmd/gopogh/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
var Build string

var (
dbPath = flag.String("db_path", "", "path to sql summary output (file). Set DB_BACKEND env variable to 'sqlite' for file output")
JudahNour marked this conversation as resolved.
Show resolved Hide resolved
JudahNour marked this conversation as resolved.
Show resolved Hide resolved
reportName = flag.String("name", "", "report name")
reportPR = flag.String("pr", "", "Pull request number")
reportDetails = flag.String("details", "", "report details (for example test args...)")
Expand Down Expand Up @@ -59,6 +60,13 @@ func main() {
os.Exit(1)
}

if *dbPath != "" || os.Getenv("DB_PATH") != "" {
JudahNour marked this conversation as resolved.
Show resolved Hide resolved
if err := c.SQL(*dbPath); err != nil {
fmt.Println(err)
os.Exit(1)
}
}

html, err := c.HTML()
if err != nil {
fmt.Printf("failed to convert report to html: %v", err)
Expand Down
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
module github.com/medyagh/gopogh

go 1.20

require (
github.com/jmoiron/sqlx v1.3.5
github.com/mattn/go-sqlite3 v1.14.17
)
9 changes: 9 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
59 changes: 59 additions & 0 deletions pkg/db/db.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package db

import (
"fmt"
"os"

"github.com/medyagh/gopogh/pkg/models"
)

// Config is database configuration
type Config struct {
JudahNour marked this conversation as resolved.
Show resolved Hide resolved
Type string
Path string
}

// datab is the database interface we support
type datab interface {
Set(models.DBEnvironmentTest, []models.DBTestCase) error
JudahNour marked this conversation as resolved.
Show resolved Hide resolved

Initialize() error
}

// New handles which database driver to use and initializes the db
func New(cfg Config) (datab, error) {
switch cfg.Type {
case "sqlite":
return NewSQLite(cfg)
default:
return nil, fmt.Errorf("unknown backend: %q", cfg.Type)
}
}

// FromEnv configures and returns a database instance.
// backend and path parameters are default config, otherwise gets config from the environment variables DB_BACKEND and DB_PATH
func FromEnv(backend string, path string) (datab, error) {
if backend == "" {
backend = os.Getenv("DB_BACKEND")
}
if backend == "" {
return nil, fmt.Errorf("missing DB_BACKEND")
}

if path == "" {
path = os.Getenv("DB_PATH")
}
if path == "" {
return nil, fmt.Errorf("missing DB_PATH")
}

c, err := New(Config{
Type: backend,
Path: path,
})
if err != nil {
return nil, fmt.Errorf("new from %s: %s: %w", backend, path, err)
JudahNour marked this conversation as resolved.
Show resolved Hide resolved
}

return c, nil
}
108 changes: 108 additions & 0 deletions pkg/db/sqlite.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package db

import (
"fmt"
"os"
"path/filepath"

"github.com/jmoiron/sqlx"

_ "github.com/mattn/go-sqlite3" // Blank import used for registering SQLite driver as a database driver
"github.com/medyagh/gopogh/pkg/models"
)

var createEnvironmentTestsTableSQL = `
CREATE TABLE IF NOT EXISTS db_environment_tests (
CommitID TEXT,
EnvName TEXT,
GopoghTime TEXT,
TestTime TEXT,
NumberOfFail INTEGER,
NumberOfPass INTEGER,
NumberOfSkip INTEGER,
PRIMARY KEY (CommitID)
);
`
var createTestCasesTableSQL = `
CREATE TABLE IF NOT EXISTS db_test_cases (
PR TEXT,
CommitId TEXT,
TestName TEXT,
Result TEXT,
PRIMARY KEY (CommitId, TestName)
);
`

type SQLite struct {
JudahNour marked this conversation as resolved.
Show resolved Hide resolved
db *sqlx.DB
path string
}

// Set adds/updates rows to the database
func (m *SQLite) Set(commitRow models.DBEnvironmentTest, dbRows []models.DBTestCase) error {
tx, err := m.db.DB.Begin()
if err != nil {
return fmt.Errorf("failed to create SQL transaction: %v", err)
}

var rollbackError error
defer func() {
if rErr := tx.Rollback(); rErr != nil {
rollbackError = fmt.Errorf("error occurred during rollback: %v", rErr)
}
}()

sqlInsert := `INSERT OR REPLACE INTO db_test_cases (PR, CommitId, TestName, Result) VALUES (?, ?, ?, ?)`
stmt, err := tx.Prepare(sqlInsert)
if err != nil {
return fmt.Errorf("failed to prepare SQL insert statement: %v", err)
}
defer stmt.Close()

for _, r := range dbRows {
_, err := stmt.Exec(r.PR, r.CommitID, r.TestName, r.Result)
if err != nil {
return fmt.Errorf("failed to execute SQL insert: %v", err)
}
}

sqlInsert = `INSERT OR REPLACE INTO db_environment_tests (CommitID, EnvName, GopoghTime, TestTime, NumberOfFail, NumberOfPass, NumberOfSkip) VALUES (?, ?, ?, ?, ?, ?, ?)`
_, err = tx.Exec(sqlInsert, commitRow.CommitID, commitRow.EnvName, commitRow.GopoghTime, commitRow.TestTime, commitRow.NumberOfFail, commitRow.NumberOfPass, commitRow.NumberOfSkip)
if err != nil {
return fmt.Errorf("failed to execute SQL insert: %v", err)
}

err = tx.Commit()
if err != nil {
return fmt.Errorf("failed to commit SQL insert transaction: %v", err)
}
return rollbackError
}

// NewSQLite opens the database returning an SQLite database struct instance
func NewSQLite(cfg Config) (*SQLite, error) {
JudahNour marked this conversation as resolved.
Show resolved Hide resolved
if err := os.MkdirAll(filepath.Dir(cfg.Path), 0755); err != nil {
return nil, fmt.Errorf("failed to create directory: %v", err)
}
database, err := sqlx.Connect("sqlite3", cfg.Path)
if err != nil {
return nil, fmt.Errorf("failed to open database connection: %v", err)
}
m := &SQLite{
db: database,
path: cfg.Path,
}
return m, nil
}

// Initialize creates the tables within the SQLite database
func (m *SQLite) Initialize() error {
JudahNour marked this conversation as resolved.
Show resolved Hide resolved

if _, err := m.db.Exec(createEnvironmentTestsTableSQL); err != nil {
return fmt.Errorf("failed to initialize environment tests table: %w", err)
}
if _, err := m.db.Exec(createTestCasesTableSQL); err != nil {
return fmt.Errorf("failed to initialize test cases table: %w", err)
}
return nil
}
19 changes: 19 additions & 0 deletions pkg/models/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,22 @@ type TestGroup struct {
Duration float64
Events []TestEvent
}

// DBTestCase represents a row in db table that holds each individual subtest
type DBTestCase struct {
PR string
CommitID string
TestName string
Result string
}

// DBEnvironmentTest represents a row in db table that has finished tests in each environments
type DBEnvironmentTest struct {
CommitID string
EnvName string
GopoghTime string
TestTime string
JudahNour marked this conversation as resolved.
Show resolved Hide resolved
NumberOfFail int
NumberOfPass int
NumberOfSkip int
}
37 changes: 37 additions & 0 deletions pkg/report/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"math"
"time"

"github.com/medyagh/gopogh/pkg/db"
"github.com/medyagh/gopogh/pkg/models"
"github.com/medyagh/gopogh/pkg/templates"
)
Expand All @@ -19,6 +20,7 @@ type DisplayContent struct {
BuildVersion string
CreatedOn time.Time
Detail models.ReportDetail
TestTime time.Time
}

// ShortSummary returns only test names without logs
Expand Down Expand Up @@ -96,6 +98,40 @@ func (c DisplayContent) HTML() ([]byte, error) {
return b.Bytes(), nil
}

// SQL handles database creation and updatess
JudahNour marked this conversation as resolved.
Show resolved Hide resolved
func (c DisplayContent) SQL(dbPath string) error {
JudahNour marked this conversation as resolved.
Show resolved Hide resolved
database, err := db.FromEnv("", dbPath)
JudahNour marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return err
}
if err := database.Initialize(); err != nil {
return err
}

expectedRowNumber := 0
for _, g := range c.Results {
expectedRowNumber += len(g)
}
dbTestRows := make([]models.DBTestCase, 0, expectedRowNumber)
for resultType, testGroups := range c.Results {
for _, test := range testGroups {
r := models.DBTestCase{PR: c.Detail.PR, CommitID: c.Detail.Details, TestName: test.TestName, Result: resultType}
dbTestRows = append(dbTestRows, r)
}
}
dbEnvironmentRow := models.DBEnvironmentTest{
CommitID: c.Detail.Details,
EnvName: c.Detail.Name,
GopoghTime: time.Now().String(),
TestTime: c.TestTime.String(),
NumberOfFail: len(c.Results[fail]),
NumberOfPass: len(c.Results[pass]),
NumberOfSkip: len(c.Results[skip]),
}

return database.Set(dbEnvironmentRow, dbTestRows)
}

// Generate generates a report
func Generate(report models.ReportDetail, groups []models.TestGroup) (DisplayContent, error) {
var passedTests []models.TestGroup
Expand Down Expand Up @@ -146,6 +182,7 @@ func Generate(report models.ReportDetail, groups []models.TestGroup) (DisplayCon
BuildVersion: Version + "_" + Build,
CreatedOn: time.Now(),
Detail: report,
TestTime: startTime,
}, nil
}

Expand Down