Add "echo" as logger

This allow to test ouput, and specify ouput other then os.Stdout
This commit is contained in:
Mikołaj Pęczkowski 2021-11-07 19:58:49 +01:00
parent 23e4547e52
commit 5fd9bc851b
6 changed files with 315 additions and 117 deletions

View File

@ -2,10 +2,13 @@ package app
import ( import (
"errors" "errors"
"fmt"
"io"
"os" "os"
"gitlab.com/revalus/grm/commands" "gitlab.com/revalus/grm/commands"
"gitlab.com/revalus/grm/config" "gitlab.com/revalus/grm/config"
"gitlab.com/revalus/grm/echo"
) )
const ( const (
@ -19,15 +22,12 @@ const (
type GitRepositoryManager struct { type GitRepositoryManager struct {
cliArguments config.CliArguments cliArguments config.CliArguments
configuration config.Configuration configuration config.Configuration
console ConsoleOutput
} }
func (g *GitRepositoryManager) Parse(args []string) { func (g *GitRepositoryManager) Parse(args []string) {
co := ConsoleOutput{}
checkCriticalError := func(err error) { checkCriticalError := func(err error) {
if err != nil { if err != nil {
co.ErrorfMsg("%v", err.Error()) fmt.Printf("Error: %v", err.Error())
os.Exit(2) os.Exit(2)
} }
} }
@ -43,21 +43,22 @@ func (g *GitRepositoryManager) Parse(args []string) {
configuration, err := config.GetRepositoryConfig(configFileContent, fileExcension) configuration, err := config.GetRepositoryConfig(configFileContent, fileExcension)
checkCriticalError(err) checkCriticalError(err)
co.Color = arguments.Color
g.console = co
g.cliArguments = arguments g.cliArguments = arguments
g.configuration = configuration g.configuration = configuration
} }
func (g *GitRepositoryManager) Run() int { func (g *GitRepositoryManager) Run(w io.Writer) int {
echo.Color(g.cliArguments.Color)
echo.Output(w)
exitCode := 0 exitCode := 0
if len(g.cliArguments.LimitTags) != 0 { if len(g.cliArguments.LimitTags) != 0 {
err := g.limitTags() err := g.limitTags()
if err != nil { if err != nil {
g.console.ErrorfMsg(err.Error()) echo.ErrorfMsg(err.Error())
exitCode = 1 exitCode = 1
} }
} }
@ -65,40 +66,40 @@ func (g *GitRepositoryManager) Run() int {
if g.cliArguments.LimitName != "" { if g.cliArguments.LimitName != "" {
err := g.limitName() err := g.limitName()
if err != nil { if err != nil {
g.console.ErrorfMsg(err.Error()) echo.ErrorfMsg(err.Error())
exitCode = 1 exitCode = 1
} }
} }
if g.cliArguments.Sync && exitCode == 0 { if g.cliArguments.Sync && exitCode == 0 {
g.console.InfoFMsg("Synchronizing repositories") echo.InfoFMsg("Synchronizing repositories")
sync := commands.NewSynchronizer(g.configuration.Workspace) sync := commands.NewSynchronizer(g.configuration.Workspace)
g.runCommand(sync) g.runCommand(sync)
g.console.InfoFMsg("All repositories are synced") echo.InfoFMsg("All repositories are synced")
} }
if g.cliArguments.Status && exitCode == 0 { if g.cliArguments.Status && exitCode == 0 {
g.console.InfoFMsg("Current status of repositories") echo.InfoFMsg("Current status of repositories")
status := commands.NewStatusChecker(g.configuration.Workspace) status := commands.NewStatusChecker(g.configuration.Workspace)
g.runCommand(status) g.runCommand(status)
} }
if g.cliArguments.Version { if g.cliArguments.Version {
g.console.InfoFMsg("Current version: %v", VERSION) echo.InfoFMsg("Current version: %v", VERSION)
} }
return exitCode return exitCode
} }
func (g GitRepositoryManager) describeStatus(status commands.CommandStatus) { func (g GitRepositoryManager) describeStatus(status commands.CommandStatus) {
if status.Error { if status.Error {
g.console.ErrorStatusF("Repository \"%v\": an error occurred: %v", status.Name, status.Message) echo.RedMessageF("Repository \"%v\": an error occurred: %v", status.Name, status.Message)
return return
} }
if status.Changed { if status.Changed {
g.console.ChangedStatusF("Repository \"%v\": %v", status.Name, status.Message) echo.YellowMessageF("Repository \"%v\": %v", status.Name, status.Message)
} else { } else {
g.console.UnchangedStatusF("Repository \"%v\": %v", status.Name, status.Message) echo.GreenMessageF("Repository \"%v\": %v", status.Name, status.Message)
} }
} }

View File

@ -8,6 +8,7 @@ import (
"gitlab.com/revalus/grm/commands" "gitlab.com/revalus/grm/commands"
"gitlab.com/revalus/grm/config" "gitlab.com/revalus/grm/config"
"gitlab.com/revalus/grm/echo"
) )
type FakeCommandToTest struct { type FakeCommandToTest struct {
@ -15,6 +16,21 @@ type FakeCommandToTest struct {
triggerChanged bool triggerChanged bool
} }
type ExpectedMessageTester struct {
expectedMessages []string
}
func (emt ExpectedMessageTester) Write(p []byte) (n int, err error) {
msg := string(p)
if !checkIsItemInSlice(msg, emt.expectedMessages) {
panic(fmt.Sprintf("the message \"%v\"does not match any of the given patterns: %#v", msg, emt.expectedMessages))
} else {
fmt.Println(msg)
}
return 0, nil
}
func (fk FakeCommandToTest) Command(repoCfg config.RepositoryConfig, cmdStatus chan commands.CommandStatus) { func (fk FakeCommandToTest) Command(repoCfg config.RepositoryConfig, cmdStatus chan commands.CommandStatus) {
status := commands.CommandStatus{ status := commands.CommandStatus{
Name: repoCfg.Name, Name: repoCfg.Name,
@ -100,7 +116,7 @@ func TestParseApplication(t *testing.T) {
} }
} }
func Example_test_sync_output() { func TestOutputFromSync(t *testing.T) {
grm := GitRepositoryManager{ grm := GitRepositoryManager{
configuration: config.Configuration{ configuration: config.Configuration{
Workspace: "/tmp", Workspace: "/tmp",
@ -108,19 +124,20 @@ func Example_test_sync_output() {
cliArguments: config.CliArguments{ cliArguments: config.CliArguments{
Sync: true, Sync: true,
Version: true, Version: true,
}, Color: false,
console: ConsoleOutput{
Color: false,
}, },
} }
grm.Run() emt := ExpectedMessageTester{
// Output: expectedMessages: []string{
// Info: Synchronizing repositories "Info: Synchronizing repositories\n",
// Info: All repositories are synced "Info: All repositories are synced\n",
// Info: Current version: 0.3.0 fmt.Sprintf("Info: Current version: %v\n", VERSION),
},
}
grm.Run(emt)
} }
func Example_limit_test_tags() { func TestLimitTags(t *testing.T) {
grm := GitRepositoryManager{ grm := GitRepositoryManager{
cliArguments: config.CliArguments{ cliArguments: config.CliArguments{
LimitTags: []string{"example"}, LimitTags: []string{"example"},
@ -132,22 +149,24 @@ func Example_limit_test_tags() {
{Name: "notExample"}, {Name: "notExample"},
}, },
}, },
console: ConsoleOutput{
Color: false,
},
} }
fakeCommand := FakeCommandToTest{ fakeCommand := FakeCommandToTest{
triggerError: false, triggerError: false,
triggerChanged: false, triggerChanged: false,
} }
emt := ExpectedMessageTester{
expectedMessages: []string{
"Repository \"example1\": response from fake command\n",
"Repository \"example2\": response from fake command\n",
},
}
echo.Color(false)
echo.Output(emt)
grm.limitTags() grm.limitTags()
grm.runCommand(fakeCommand) grm.runCommand(fakeCommand)
// Output:
// Repository "example1": response from fake command
// Repository "example2": response from fake command
} }
func Example_limit_name() { func TestLimitName(t *testing.T) {
grm := GitRepositoryManager{ grm := GitRepositoryManager{
cliArguments: config.CliArguments{ cliArguments: config.CliArguments{
LimitName: "notExample", LimitName: "notExample",
@ -159,21 +178,22 @@ func Example_limit_name() {
{Name: "notExample"}, {Name: "notExample"},
}, },
}, },
console: ConsoleOutput{
Color: false,
},
} }
fakeCommand := FakeCommandToTest{ fakeCommand := FakeCommandToTest{
triggerError: false, triggerError: false,
triggerChanged: false, triggerChanged: false,
} }
emt := ExpectedMessageTester{
expectedMessages: []string{
"Repository \"notExample\": response from fake command\n",
},
}
echo.Color(false)
echo.Output(emt)
grm.limitName() grm.limitName()
grm.runCommand(fakeCommand) grm.runCommand(fakeCommand)
// Output:
// Repository "notExample": response from fake command
} }
func TestRunWithNotExistingNameInLimit(t *testing.T) {
func Example_run_with_limit_not_existing_name() {
grm := GitRepositoryManager{ grm := GitRepositoryManager{
cliArguments: config.CliArguments{ cliArguments: config.CliArguments{
LimitName: "not-existing-name", LimitName: "not-existing-name",
@ -185,16 +205,20 @@ func Example_run_with_limit_not_existing_name() {
{Name: "notExample"}, {Name: "notExample"},
}, },
}, },
console: ConsoleOutput{ }
Color: false, emt := ExpectedMessageTester{
expectedMessages: []string{
"Error: no repository was found with the specified name\n",
}, },
} }
grm.Run() echo.Color(false)
// Output: status := grm.Run(emt)
// Error: no repository was found with the specified name if status != 1 {
t.Errorf("Expected to get status %v, instead o this got %v", 1, status)
}
} }
func Example_run_with_limit_not_existing_tags() { func TestRunWithNotExistingTagsInLimit(t *testing.T) {
grm := GitRepositoryManager{ grm := GitRepositoryManager{
cliArguments: config.CliArguments{ cliArguments: config.CliArguments{
LimitTags: []string{"not-existing-tag"}, LimitTags: []string{"not-existing-tag"},
@ -206,16 +230,20 @@ func Example_run_with_limit_not_existing_tags() {
{Name: "notExample"}, {Name: "notExample"},
}, },
}, },
console: ConsoleOutput{ }
Color: false, emt := ExpectedMessageTester{
expectedMessages: []string{
"Error: no repository was found with the specified tags\n",
}, },
} }
grm.Run() echo.Color(false)
// Output: status := grm.Run(emt)
// Error: no repository was found with the specified tags if status != 1 {
t.Errorf("Expected to get status %v, instead o this got %v", 1, status)
}
} }
func Example_test_status_output() { func TestGetStatusOutput(t *testing.T) {
grm := GitRepositoryManager{ grm := GitRepositoryManager{
configuration: config.Configuration{ configuration: config.Configuration{
Workspace: "/tmp", Workspace: "/tmp",
@ -223,11 +251,17 @@ func Example_test_status_output() {
cliArguments: config.CliArguments{ cliArguments: config.CliArguments{
Status: true, Status: true,
}, },
console: ConsoleOutput{ }
Color: false, emt := ExpectedMessageTester{
expectedMessages: []string{
"Info: Current status of repositories\n",
}, },
} }
grm.Run() echo.Color(false)
status := grm.Run(emt)
if status != 0 {
t.Errorf("Expected to get status %v, instead o this got %v", 1, status)
}
// Output: // Output:
// Info: Current status of repositories // Info: Current status of repositories
} }

View File

@ -1,61 +0,0 @@
package app
import (
"fmt"
)
const (
colorReset = "\033[0m"
colorRed = "\033[31m"
colorGreen = "\033[32m"
colorYellow = "\033[33m"
colorBlue = "\033[34m"
)
type ConsoleOutput struct {
Color bool
}
func (co ConsoleOutput) ErrorfMsg(format string, a ...interface{}) {
msg := fmt.Sprintf(format, a...)
if co.Color {
msg = fmt.Sprintf("%vError:%v %v", colorRed, colorReset, msg)
} else {
msg = fmt.Sprintf("Error: %v", msg)
}
fmt.Println(msg)
}
func (co ConsoleOutput) InfoFMsg(format string, a ...interface{}) {
msg := fmt.Sprintf(format, a...)
if co.Color {
msg = fmt.Sprintf("%vInfo:%v %v", colorBlue, colorReset, msg)
} else {
msg = fmt.Sprintf("Info: %v", msg)
}
fmt.Println(msg)
}
func (co ConsoleOutput) UnchangedStatusF(format string, a ...interface{}) {
msg := fmt.Sprintf(format, a...)
if co.Color {
msg = fmt.Sprintf("%v%v%v", colorGreen, msg, colorReset)
}
fmt.Println(msg)
}
func (co ConsoleOutput) ChangedStatusF(format string, a ...interface{}) {
msg := fmt.Sprintf(format, a...)
if co.Color {
msg = fmt.Sprintf("%v%v%v", colorYellow, msg, colorReset)
}
fmt.Println(msg)
}
func (co ConsoleOutput) ErrorStatusF(format string, a ...interface{}) {
msg := fmt.Sprintf(format, a...)
if co.Color {
msg = fmt.Sprintf("%v%v%v", colorRed, msg, colorReset)
}
fmt.Println(msg)
}

72
echo/echo.go Normal file
View File

@ -0,0 +1,72 @@
package echo
import (
"fmt"
"io"
"os"
)
const (
colorReset = "\033[0m"
colorRed = "\033[31m"
colorGreen = "\033[32m"
colorYellow = "\033[33m"
colorBlue = "\033[34m"
)
var (
useColor bool = false
output io.Writer = os.Stdout
)
func Color(enabled bool) {
useColor = enabled
}
func Output(writer io.Writer) {
output = writer
}
func ErrorfMsg(format string, a ...interface{}) error {
msg := fmt.Sprintf(format, a...)
if useColor {
msg = fmt.Sprintf("%vError:%v %v", colorRed, colorReset, msg)
} else {
msg = fmt.Sprintf("Error: %v", msg)
}
return write(msg)
}
func InfoFMsg(format string, a ...interface{}) error {
msg := fmt.Sprintf(format, a...)
if useColor {
msg = fmt.Sprintf("%vInfo:%v %v", colorBlue, colorReset, msg)
} else {
msg = fmt.Sprintf("Info: %v", msg)
}
return write(msg)
}
func GreenMessageF(format string, a ...interface{}) error {
return writeWithColor(fmt.Sprintf(format, a...), colorGreen)
}
func YellowMessageF(format string, a ...interface{}) error {
return writeWithColor(fmt.Sprintf(format, a...), colorYellow)
}
func RedMessageF(format string, a ...interface{}) error {
return writeWithColor(fmt.Sprintf(format, a...), colorRed)
}
func writeWithColor(msg string, color string) error {
if useColor {
return write(fmt.Sprintf("%v%v%v", color, msg, colorReset))
}
return write(msg)
}
func write(msg string) error {
_, err := fmt.Fprintln(output, msg)
return err
}

151
echo/echo_test.go Normal file
View File

@ -0,0 +1,151 @@
package echo
import (
"fmt"
"os"
"testing"
)
type ExpectedMessageTester struct {
expectedMessage string
}
func (emt ExpectedMessageTester) Write(p []byte) (n int, err error) {
msg := string(p)
if msg != emt.expectedMessage {
return 0, fmt.Errorf("expected to get \"%v\", instead of this got \"%v\"", msg, emt.expectedMessage)
}
return 0, nil
}
func TestOverwriteColor(t *testing.T) {
Color(false)
if useColor {
t.Error("Expected that \"useColor\" will be false")
}
Color(true)
if !useColor {
t.Error("Expected that \"useColor\" will be true")
}
}
func TestOverwriteWriter(t *testing.T) {
Output(os.Stderr)
if output != os.Stderr {
t.Error("Expected to receive addresses on os.Stderr")
}
Output(os.Stdout)
if output != os.Stdout {
t.Error("Expected to receive addresses on os.Stdout")
}
}
func TestErrorfMsgWithoutColor(t *testing.T) {
useColor = false
output = ExpectedMessageTester{
expectedMessage: "Error: test message\n",
}
err := ErrorfMsg("test message")
if err != nil {
t.Error(err)
}
}
func TestErrorfMsgWithColor(t *testing.T) {
useColor = true
output = ExpectedMessageTester{
expectedMessage: "\033[31mError:\033[0m test message\n",
}
err := ErrorfMsg("test message")
if err != nil {
t.Error(err)
}
}
func TestInfoMsgFWithoutColor(t *testing.T) {
useColor = false
output = ExpectedMessageTester{
expectedMessage: "Info: test message\n",
}
err := InfoFMsg("test message")
if err != nil {
t.Error(err)
}
}
func TestInfoMsgFWithColor(t *testing.T) {
useColor = true
output = ExpectedMessageTester{
expectedMessage: "\033[34mInfo:\033[0m test message\n",
}
err := InfoFMsg("test message")
if err != nil {
t.Error(err)
}
}
func TestGreenMessageWithoutColor(t *testing.T) {
useColor = false
output = ExpectedMessageTester{
expectedMessage: "test message\n",
}
err := GreenMessageF("test message")
if err != nil {
t.Error(err)
}
}
func TestGreenMessageWithColor(t *testing.T) {
useColor = true
output = ExpectedMessageTester{
expectedMessage: "\033[32mtest message\033[0m\n",
}
err := GreenMessageF("test message")
if err != nil {
t.Error(err)
}
}
func TestYellowMessageWithout(t *testing.T) {
useColor = false
output = ExpectedMessageTester{
expectedMessage: "test message\n",
}
err := YellowMessageF("test message")
if err != nil {
t.Error(err)
}
}
func TestYellowMessageWithColor(t *testing.T) {
useColor = true
output = ExpectedMessageTester{
expectedMessage: "\033[33mtest message\033[0m\n",
}
err := YellowMessageF("test message")
if err != nil {
t.Error(err)
}
}
func TestRedMessageWithout(t *testing.T) {
useColor = false
output = ExpectedMessageTester{
expectedMessage: "test message\n",
}
err := RedMessageF("test message")
if err != nil {
t.Error(err)
}
}
func TestRedMessageWithColor(t *testing.T) {
useColor = true
output = ExpectedMessageTester{
expectedMessage: "\033[31mtest message\033[0m\n",
}
err := RedMessageF("test message")
if err != nil {
t.Error(err)
}
}

View File

@ -12,5 +12,6 @@ func main() {
app := app.GitRepositoryManager{} app := app.GitRepositoryManager{}
app.Parse(os.Args) app.Parse(os.Args)
os.Exit(app.Run()) exitCode := app.Run(os.Stdout)
os.Exit(exitCode)
} }