@ -5,6 +5,7 @@ package cmd
import (
import (
"fmt"
"fmt"
"io"
"os"
"os"
"path/filepath"
"path/filepath"
"strings"
"strings"
@ -12,6 +13,7 @@ import (
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/assert"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v2"
@ -27,21 +29,38 @@ func makePathOutput(workPath, customPath, customConf string) string {
return fmt . Sprintf ( "WorkPath=%s\nCustomPath=%s\nCustomConf=%s" , workPath , customPath , customConf )
return fmt . Sprintf ( "WorkPath=%s\nCustomPath=%s\nCustomConf=%s" , workPath , customPath , customConf )
}
}
func newTestApp ( ) * cli . App {
func newTestApp ( testCmdAction func ( ctx * cli . Context ) error ) * cli . App {
app := NewMainApp ( )
app := NewMainApp ( "version" , "version-extra" )
testCmd := & cli . Command {
testCmd := & cli . Command { Name : "test-cmd" , Action : testCmdAction }
Name : "test-cmd" ,
Action : func ( ctx * cli . Context ) error {
_ , _ = fmt . Fprint ( app . Writer , makePathOutput ( setting . AppWorkPath , setting . CustomPath , setting . CustomConf ) )
return nil
} ,
}
prepareSubcommandWithConfig ( testCmd , appGlobalFlags ( ) )
prepareSubcommandWithConfig ( testCmd , appGlobalFlags ( ) )
app . Commands = append ( app . Commands , testCmd )
app . Commands = append ( app . Commands , testCmd )
app . DefaultCommand = testCmd . Name
app . DefaultCommand = testCmd . Name
return app
return app
}
}
type runResult struct {
Stdout string
Stderr string
ExitCode int
}
func runTestApp ( app * cli . App , args ... string ) ( runResult , error ) {
outBuf := new ( strings . Builder )
errBuf := new ( strings . Builder )
app . Writer = outBuf
app . ErrWriter = errBuf
exitCode := - 1
defer test . MockVariableValue ( & cli . ErrWriter , app . ErrWriter ) ( )
defer test . MockVariableValue ( & cli . OsExiter , func ( code int ) {
if exitCode == - 1 {
exitCode = code // save the exit code once and then reset the writer (to simulate the exit)
app . Writer , app . ErrWriter , cli . ErrWriter = io . Discard , io . Discard , io . Discard
}
} ) ( )
err := RunMainApp ( app , args ... )
return runResult { outBuf . String ( ) , errBuf . String ( ) , exitCode } , err
}
func TestCliCmd ( t * testing . T ) {
func TestCliCmd ( t * testing . T ) {
defaultWorkPath := filepath . Dir ( setting . AppPath )
defaultWorkPath := filepath . Dir ( setting . AppPath )
defaultCustomPath := filepath . Join ( defaultWorkPath , "custom" )
defaultCustomPath := filepath . Join ( defaultWorkPath , "custom" )
@ -92,7 +111,10 @@ func TestCliCmd(t *testing.T) {
} ,
} ,
}
}
app := newTestApp ( )
app := newTestApp ( func ( ctx * cli . Context ) error {
_ , _ = fmt . Fprint ( ctx . App . Writer , makePathOutput ( setting . AppWorkPath , setting . CustomPath , setting . CustomConf ) )
return nil
} )
var envBackup [ ] string
var envBackup [ ] string
for _ , s := range os . Environ ( ) {
for _ , s := range os . Environ ( ) {
if strings . HasPrefix ( s , "GITEA_" ) && strings . Contains ( s , "=" ) {
if strings . HasPrefix ( s , "GITEA_" ) && strings . Contains ( s , "=" ) {
@ -120,12 +142,39 @@ func TestCliCmd(t *testing.T) {
_ = os . Setenv ( k , v )
_ = os . Setenv ( k , v )
}
}
args := strings . Split ( c . cmd , " " ) // for test only, "split" is good enough
args := strings . Split ( c . cmd , " " ) // for test only, "split" is good enough
out := new ( strings . Builder )
r , err := runTestApp ( app , args ... )
app . Writer = out
err := app . Run ( args )
assert . NoError ( t , err , c . cmd )
assert . NoError ( t , err , c . cmd )
assert . NotEmpty ( t , c . exp , c . cmd )
assert . NotEmpty ( t , c . exp , c . cmd )
outStr := out . String ( )
assert . Contains ( t , r . Stdout , c . exp , c . cmd )
assert . Contains ( t , outStr , c . exp , c . cmd )
}
}
}
}
func TestCliCmdError ( t * testing . T ) {
app := newTestApp ( func ( ctx * cli . Context ) error { return fmt . Errorf ( "normal error" ) } )
r , err := runTestApp ( app , "./gitea" , "test-cmd" )
assert . Error ( t , err )
assert . Equal ( t , 1 , r . ExitCode )
assert . Equal ( t , "" , r . Stdout )
assert . Equal ( t , "Command error: normal error\n" , r . Stderr )
app = newTestApp ( func ( ctx * cli . Context ) error { return cli . Exit ( "exit error" , 2 ) } )
r , err = runTestApp ( app , "./gitea" , "test-cmd" )
assert . Error ( t , err )
assert . Equal ( t , 2 , r . ExitCode )
assert . Equal ( t , "" , r . Stdout )
assert . Equal ( t , "exit error\n" , r . Stderr )
app = newTestApp ( func ( ctx * cli . Context ) error { return nil } )
r , err = runTestApp ( app , "./gitea" , "test-cmd" , "--no-such" )
assert . Error ( t , err )
assert . Equal ( t , 1 , r . ExitCode )
assert . Equal ( t , "Incorrect Usage: flag provided but not defined: -no-such\n\n" , r . Stdout )
assert . Equal ( t , "" , r . Stderr ) // the cli package's strange behavior, the error message is not in stderr ....
app = newTestApp ( func ( ctx * cli . Context ) error { return nil } )
r , err = runTestApp ( app , "./gitea" , "test-cmd" )
assert . NoError ( t , err )
assert . Equal ( t , - 1 , r . ExitCode ) // the cli.OsExiter is not called
assert . Equal ( t , "" , r . Stdout )
assert . Equal ( t , "" , r . Stderr )
}