Skip to content

Commit

Permalink
atlasaction: setup testscript for github action (#225)
Browse files Browse the repository at this point in the history
  • Loading branch information
giautm authored Sep 15, 2024
1 parent 0c06921 commit 28f4872
Show file tree
Hide file tree
Showing 6 changed files with 247 additions and 8 deletions.
136 changes: 136 additions & 0 deletions atlasaction/action_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ import (
"ariga.io/atlas/sql/sqlcheck"
"ariga.io/atlas/sql/sqlclient"
_ "github.com/mattn/go-sqlite3"
"github.com/rogpeppe/go-internal/diff"
"github.com/rogpeppe/go-internal/testscript"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -2163,3 +2165,137 @@ func (m *mockAction) WithFieldsMap(args map[string]string) atlasaction.Logger {
logger: m.logger.With(argPairs...),
}
}

func TestGitHubActions(t *testing.T) {
var (
actions = "actions"
output = filepath.Join(actions, "output.txt")
summary = filepath.Join(actions, "summary.txt")
)
testscript.Run(t, testscript.Params{
Dir: filepath.Join("testdata", "github"),
Setup: func(e *testscript.Env) (err error) {
dir := filepath.Join(e.WorkDir, actions)
if err := os.Mkdir(dir, 0700); err != nil {
return err
}
e.Setenv("GITHUB_ACTIONS", "true")
e.Setenv("GITHUB_ENV", filepath.Join(dir, "env.txt"))
e.Setenv("GITHUB_OUTPUT", filepath.Join(dir, "output.txt"))
e.Setenv("GITHUB_STEP_SUMMARY", filepath.Join(dir, "summary.txt"))
c, err := atlasexec.NewClient(e.WorkDir, "atlas")
if err != nil {
return err
}
// Create a new actions for each test.
e.Values[atlasKey{}] = &atlasClient{c}
return nil
},
Cmds: map[string]func(ts *testscript.TestScript, neg bool, args []string){
"atlas-action": atlasAction,
"mock-atlas": mockAtlasOutput,
"summary": func(ts *testscript.TestScript, neg bool, args []string) {
if len(args) == 0 {
_, err := os.Stat(ts.MkAbs(summary))
if neg {
if !os.IsNotExist(err) {
ts.Fatalf("expected no summary, but got some")
}
return
}
if err != nil {
ts.Fatalf("expected summary, but got none")
return
}
return
}
cmpFiles(ts, neg, args[0], summary)
},
"output": func(ts *testscript.TestScript, neg bool, args []string) {
if len(args) == 0 {
_, err := os.Stat(ts.MkAbs(output))
if neg {
if !os.IsNotExist(err) {
ts.Fatalf("expected no output, but got some")
}
return
}
if err != nil {
ts.Fatalf("expected output, but got none")
return
}
return
}
cmpFiles(ts, neg, args[0], output)
},
},
})
}

type (
atlasKey struct{}
atlasClient struct {
atlasaction.AtlasExec
}
)

func atlasAction(ts *testscript.TestScript, neg bool, args []string) {
if len(args) != 1 {
ts.Fatalf("usage: atlas-action <action>")
}
client, ok := ts.Value(atlasKey{}).(*atlasClient)
if !ok || client == nil {
ts.Fatalf("client not found")
}
// The action need to be create for each call to read correct inputs
act, err := atlasaction.New(ts.Getenv, ts.Stdout())
ts.Check(err)
act.Atlas = client.AtlasExec
act.Version = "testscript"
// Run the action!
switch err := act.Run(context.Background(), args[0]); {
case !neg:
ts.Check(err)
case err == nil:
ts.Fatalf("expected fail")
case neg:
// Print the error to asserting on the testscript
fmt.Fprint(ts.Stderr(), err.Error())
}
}

func mockAtlasOutput(ts *testscript.TestScript, neg bool, args []string) {
if len(args) != 1 {
ts.Fatalf("usage: mock-atlas <dir>")
}
client, ok := ts.Value(atlasKey{}).(*atlasClient)
if !ok || client == nil {
ts.Fatalf("client not found")
}
m, err := atlasexec.NewClient("", "./mock-atlas.sh")
ts.Check(err)
ts.Check(m.SetEnv(map[string]string{
"TEST_BATCH": args[0],
}))
// Replace the atlas client with a mock client.
client.AtlasExec = m
}

func cmpFiles(ts *testscript.TestScript, neg bool, name1, name2 string) {
text1 := ts.ReadFile(name1)
data, err := os.ReadFile(ts.MkAbs(name2))
ts.Check(err)
eq := text1 == string(data)
if neg {
if eq {
ts.Fatalf("%s and %s do not differ", name1, name2)
}
return // they differ, as expected
}
if eq {
return // they are equal, as expected
}
unifiedDiff := diff.Diff(name1, []byte(text1), name2, data)
ts.Logf("%s", unifiedDiff)
ts.Fatalf("%s and %s differ", name1, name2)
}
47 changes: 47 additions & 0 deletions atlasaction/mock-atlas.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/bin/bash

# TEST_BATCH provide the directory containts all
# outputs for multiple runs. The path should be absolulate
# or related to current working directory.
if [[ "$TEST_BATCH" != "" ]]; then
COUNTER_FILE=$TEST_BATCH/counter
COUNTER=$(cat $COUNTER_FILE 2>/dev/null)
COUNTER=$((COUNTER+1))
DIR_CUR="$TEST_BATCH/$COUNTER"
if [ ! -d "$DIR_CUR" ]; then
>&2 echo -n "$DIR_CUR does not exist, quitting..."
exit 1
fi
# Save counter for the next runs
echo -n $COUNTER > $COUNTER_FILE
if [ -f "$DIR_CUR/args" ]; then
TEST_ARGS=$(cat $DIR_CUR/args)
fi
if [ -f "$DIR_CUR/stderr" ]; then
TEST_STDERR=$(cat $DIR_CUR/stderr)
fi
if [ -f "$DIR_CUR/stdout" ]; then
TEST_STDOUT=$(cat $DIR_CUR/stdout)
fi
fi

if [[ "$TEST_ARGS" != "$@" ]]; then
>&2 echo "Receive unexpected args: $@"
exit 1
fi

if [[ "$TEST_STDOUT" != "" ]]; then
echo -n $TEST_STDOUT
if [[ "$TEST_STDERR" == "" ]]; then
exit 0 # No stderr
fi
# In some cases, Atlas will write the error in stderr
# when if the command is partially successful.
# eg. Run the apply commands with multiple environments.
>&2 echo -n $TEST_STDERR
exit 1
fi

TEST_STDERR="${TEST_STDERR:-Missing stderr either stdout input for the test}"
>&2 echo -n $TEST_STDERR
exit 1
28 changes: 28 additions & 0 deletions atlasaction/testdata/github/migrate-test.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Mock the atlas command outputs
mock-atlas $WORK/migrate-test
# Setup the action input variables
env INPUT_CONFIG=file://testdata/config/atlas.hcl
env INPUT_ENV=test
env INPUT_VARS='{"var1":"value1","var2":"value2"}'
env INPUT_DIR=file://testdata/migrations
env INPUT_DEV_URL=sqlite://file?mode=memory
env INPUT_RUN=example

atlas-action migrate/test
stdout '`atlas migrate test` completed successfully, no issues found'
stdout 'Success'
! output

! atlas-action migrate/test
stderr '`atlas migrate test` completed with errors:'
stderr 'Failure'
! output

-- migrate-test/1/args --
migrate test --env test --config file://testdata/config/atlas.hcl --dir file://testdata/migrations --run example --var var1=value1 --var var2=value2
-- migrate-test/1/stdout --
Success
-- migrate-test/2/args --
migrate test --env test --config file://testdata/config/atlas.hcl --dir file://testdata/migrations --run example --var var1=value1 --var var2=value2
-- migrate-test/2/stderr --
Failure
27 changes: 27 additions & 0 deletions atlasaction/testdata/github/schema-test.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Mock the atlas command outputs
mock-atlas $WORK/schema-test
# Setup the action input variables
env INPUT_CONFIG=file://testdata/config/atlas.hcl
env INPUT_ENV=test
env INPUT_VARS='{"var1":"value1","var2":"value2"}'
env INPUT_DEV_URL=sqlite://file?mode=memory
env INPUT_RUN=example

atlas-action schema/test
stdout '`atlas schema test` completed successfully, no issues found'
stdout 'Success'
! output

! atlas-action schema/test
stderr '`atlas schema test` completed with errors:'
stderr 'Failure'
! output

-- schema-test/1/args --
schema test --env test --config file://testdata/config/atlas.hcl --run example --var var1=value1 --var var2=value2
-- schema-test/1/stdout --
Success
-- schema-test/2/args --
schema test --env test --config file://testdata/config/atlas.hcl --run example --var var1=value1 --var var2=value2
-- schema-test/2/stderr --
Failure
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
github.com/alecthomas/kong v0.8.0
github.com/mattn/go-sqlite3 v1.14.17
github.com/mitchellh/mapstructure v1.1.2
github.com/rogpeppe/go-internal v1.12.1-0.20240709150035-ccf4b4329d21
github.com/sethvargo/go-githubactions v1.3.0
github.com/stretchr/testify v1.8.4
golang.org/x/oauth2 v0.22.0
Expand All @@ -22,8 +23,9 @@ require (
github.com/hashicorp/hcl/v2 v2.18.1 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rogpeppe/go-internal v1.6.1 // indirect
github.com/zclconf/go-cty v1.14.1 // indirect
golang.org/x/sys v0.21.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.22.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
13 changes: 6 additions & 7 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,8 @@ github.com/hashicorp/hcl/v2 v2.18.1 h1:6nxnOJFku1EuSawSD81fuviYUV8DxFr3fp2dUi3ZY
github.com/hashicorp/hcl/v2 v2.18.1/go.mod h1:ThLC89FV4p9MPW804KVbe/cEXoQ8NZEh+JtMeeGErHE=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
Expand All @@ -45,8 +42,8 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWb
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.12.1-0.20240709150035-ccf4b4329d21 h1:igWZJluD8KtEtAgRyF4x6lqcxDry1ULztksMJh2mnQE=
github.com/rogpeppe/go-internal v1.12.1-0.20240709150035-ccf4b4329d21/go.mod h1:RMRJLmBOqWacUkmJHRMiPKh1S1m3PA7Zh4W80/kWPpg=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/sethvargo/go-githubactions v1.3.0 h1:Kg633LIUV2IrJsqy2MfveiED/Ouo+H2P0itWS0eLh8A=
Expand All @@ -57,12 +54,14 @@ github.com/zclconf/go-cty v1.14.1 h1:t9fyA35fwjjUMcmL5hLER+e/rEPqrbCK1/OSE4SI9KA
github.com/zclconf/go-cty v1.14.1/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA=
golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

0 comments on commit 28f4872

Please sign in to comment.