Skip to content

Commit d2e26b0

Browse files
author
nvima
committed
Improve error handling and add new test cases
- Change tplCommand from Run to RunE for better error handling - Modify error handling functions in util package - Add new test cases for empty data, empty output, and empty URL
1 parent f16b891 commit d2e26b0

File tree

6 files changed

+97
-38
lines changed

6 files changed

+97
-38
lines changed

cmd/root.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ For example, the following command:
2323
2424
reads from the YAML configuration file, processes the "translate" function, and sends an API request to an API.
2525
The response is then printed to stdout. For more Information about YAML Configuration, visit https://github.com/nvima/httpcli.`,
26-
Run: tplCommand,
26+
RunE: tplCommand,
2727
}
2828

2929
func Execute() {

cmd/testdata/config.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,15 @@ translate:
2626
env:
2727
- "TEST_API_KEY"
2828
- "TEST_SERVER_URL"
29+
emptydata:
30+
url: "${TEST_SERVER_URL}/emptydata"
31+
output: "response"
32+
env:
33+
- "TEST_SERVER_URL"
34+
emptyoutput:
35+
url: "${TEST_SERVER_URL}/emptydata"
36+
env:
37+
- "TEST_SERVER_URL"
38+
emptyurl:
39+
env:
40+
- "TEST_SERVER_URL"

cmd/tpl.go

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -28,37 +28,51 @@ type AppConfig struct {
2828
Functions map[string]FunctionConfig
2929
}
3030

31-
//TODO Better error handling for testing
32-
func tplCommand(cmd *cobra.Command, args []string) {
33-
fc := initFunctionConfig(cmd, args)
34-
output := fc.handleFunc(cmd)
31+
var fc FunctionConfig
32+
33+
func tplCommand(cmd *cobra.Command, args []string) error {
34+
err := initFunctionConfig(cmd, args)
35+
if err != nil {
36+
return err
37+
}
38+
39+
output, err := fc.handleFunc(cmd)
40+
if err != nil {
41+
return err
42+
}
43+
3544
fmt.Fprintf(cmd.OutOrStdout(), output)
45+
return nil
3646
}
3747

38-
func initFunctionConfig(cmd *cobra.Command, args []string) FunctionConfig {
39-
fc := FunctionConfig{}
48+
func initFunctionConfig(cmd *cobra.Command, args []string) error {
4049
config := viper.AllSettings()
4150

4251
if len(config) == 0 {
43-
util.HandleError(cmd, util.NO_FUNC_NAME_ERR.Err(), util.NO_CONFIG_FILE_ERR)
52+
return util.HandleError(cmd, util.NO_FUNC_NAME_ERR.Err(), util.NO_CONFIG_FILE_ERR)
4453
}
4554

4655
if len(args) == 0 {
47-
util.HandleError(cmd, util.NO_FUNC_NAME_ERR.Err(), util.NO_FUNC_NAME_ERR)
56+
return util.HandleError(cmd, util.NO_FUNC_NAME_ERR.Err(), util.NO_FUNC_NAME_ERR)
4857
}
4958

5059
var appConfig AppConfig
5160
err := mapstructure.Decode(config, &appConfig.Functions)
5261
if err != nil {
53-
util.HandleError(cmd, err, util.INVALID_CONFIG_ERR)
62+
return util.HandleError(cmd, err, util.INVALID_CONFIG_ERR)
5463
}
5564

56-
fc, ok := appConfig.Functions[args[0]]
65+
funcConfig, ok := appConfig.Functions[args[0]]
5766
if !ok {
58-
util.HandleError(cmd, util.NO_FUNC_FOUND_ERR.Err(), util.NO_FUNC_FOUND_ERR)
67+
return util.HandleError(cmd, util.NO_FUNC_FOUND_ERR.Err(), util.NO_FUNC_FOUND_ERR)
68+
}
69+
70+
if funcConfig.Url == "" {
71+
return util.HandleError(cmd, util.NO_URL_ERR.Err(), util.NO_URL_ERR)
5972
}
6073

61-
return fc
74+
fc = funcConfig
75+
return nil
6276
}
6377

6478
func (fc *FunctionConfig) makeHttpCall(jsonData []byte, cmd *cobra.Command) ([]byte, error) {
@@ -97,28 +111,28 @@ func (fc *FunctionConfig) makeHttpCall(jsonData []byte, cmd *cobra.Command) ([]b
97111
return body, nil
98112
}
99113

100-
func (fc *FunctionConfig) handleFunc(cmd *cobra.Command) string {
114+
func (fc *FunctionConfig) handleFunc(cmd *cobra.Command) (string, error) {
101115
jsonData, err := fc.getJSONData()
102116
if err != nil {
103-
util.HandleError(cmd, err, util.FAILED_TO_GET_DATA)
117+
return "", util.HandleError(cmd, err, util.FAILED_TO_GET_DATA)
104118
}
105119

106120
body, err := fc.makeHttpCall(jsonData, cmd)
107121
if err != nil {
108-
util.HandleError(cmd, err, util.FAILED_TO_MAKE_HTTP_CALL)
122+
return "", util.HandleError(cmd, err, util.FAILED_TO_MAKE_HTTP_CALL)
109123
}
110124

111125
responseData, err := util.ParseJSONResponse(body)
112126
if err != nil {
113-
util.HandleError(cmd, err, util.FAILED_TO_PARSE_JSON)
127+
return "", util.HandleError(cmd, err, util.FAILED_TO_PARSE_JSON)
114128
}
115129

116130
output, err := util.GetOutputField(responseData, fc.Output)
117131
if err != nil {
118-
util.HandleError(cmd, err, util.FAILED_TO_PARSE_OUTPUT_FIELD)
132+
return "", util.HandleError(cmd, err, util.FAILED_TO_PARSE_OUTPUT_FIELD)
119133
}
120134

121-
return output
135+
return output, nil
122136
}
123137

124138
func (fc *FunctionConfig) getJSONData() ([]byte, error) {

cmd/tpl_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"os"
1010
"testing"
1111

12+
"github.com/nvima/httpcli/util"
1213
)
1314

1415
var testServer *httptest.Server
@@ -38,6 +39,8 @@ func startTestHTTPServer() *httptest.Server {
3839
w.Write([]byte(`{"choices": [{"message": {"content": "` + data.Messages[0].Content + `"}}]}`))
3940
case "/v2/translate":
4041
w.Write([]byte(`{"translations": [{"text": "Pomme"}]}`))
42+
case "/emptydata":
43+
w.Write([]byte(`{"response":"Hello"}`))
4144
default:
4245
http.Error(w, "Not found", http.StatusNotFound)
4346
}
@@ -75,3 +78,43 @@ func TestTranslate(t *testing.T) {
7578
t.Fatalf("expected \"%s\" got \"%s\"", expected, string(out))
7679
}
7780
}
81+
82+
func TestEmptyData(t *testing.T) {
83+
b := bytes.NewBufferString("")
84+
rootCmd.SetOut(b)
85+
rootCmd.SetArgs([]string{"emptydata", "--config", "./testdata/config.yaml"})
86+
rootCmd.Execute()
87+
out, err := ioutil.ReadAll(b)
88+
if err != nil {
89+
t.Fatal(err)
90+
}
91+
expected := "Hello"
92+
if string(out) != expected {
93+
t.Fatalf("expected \"%s\" got \"%s\"", expected, string(out))
94+
}
95+
}
96+
97+
func TestEmptyOutput(t *testing.T) {
98+
b := bytes.NewBufferString("")
99+
rootCmd.SetOut(b)
100+
rootCmd.SetArgs([]string{"emptyoutput", "--config", "./testdata/config.yaml"})
101+
rootCmd.Execute()
102+
out, err := ioutil.ReadAll(b)
103+
if err != nil {
104+
t.Fatal(err)
105+
}
106+
expected := []byte(`{"response":"Hello"}`)
107+
if string(out) != string(expected) {
108+
t.Fatalf("expected \"%s\" got \"%s\"", expected, string(out))
109+
}
110+
}
111+
112+
func TestEmptyUrl(t *testing.T) {
113+
b := bytes.NewBufferString("")
114+
rootCmd.SetOut(b)
115+
rootCmd.SetArgs([]string{"emptyurl", "--config", "./testdata/config.yaml"})
116+
err := rootCmd.Execute()
117+
if err.Error() != util.NO_URL_ERR.Msg() {
118+
t.Fatalf("expected \"%s\" got \"%s\"", util.NO_URL_ERR.Msg(), err.Error())
119+
}
120+
}

util/errors.go

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
type TplError struct {
88
msg string
99
err error
10-
exitCode int
1110
}
1211

1312
func (te TplError) Err() error {
@@ -18,46 +17,36 @@ func (te TplError) Msg() string {
1817
return te.msg
1918
}
2019

21-
func (te TplError) ExitCode() int {
22-
return te.exitCode
23-
}
24-
2520
var NO_CONFIG_FILE_ERR = TplError{
2621
msg: "No config file found",
27-
exitCode: 1,
2822
}
2923

3024
var NO_FUNC_NAME_ERR = TplError{
3125
msg: "No function name provided",
32-
exitCode: 2,
3326
}
3427
var NO_FUNC_FOUND_ERR = TplError{
3528
msg: "Function not found in config",
36-
exitCode: 3,
3729
}
3830

3931
var INVALID_CONFIG_ERR = TplError{
4032
msg: "Invalid config file",
41-
exitCode: 4,
4233
}
4334

4435
var INVALID_RESP_CODE = TplError{
4536
msg: "Invalid response code",
46-
exitCode: 5,
4737
}
4838
var FAILED_TO_GET_DATA = TplError{
4939
msg: "Failed to get data",
50-
exitCode: 6,
5140
}
5241
var FAILED_TO_MAKE_HTTP_CALL = TplError{
5342
msg: "Failed to make http call",
54-
exitCode: 7,
5543
}
5644
var FAILED_TO_PARSE_JSON = TplError{
5745
msg: "Failed to parse JSON",
58-
exitCode: 8,
5946
}
6047
var FAILED_TO_PARSE_OUTPUT_FIELD = TplError{
6148
msg: "Failed to parse output field",
62-
exitCode: 9,
49+
}
50+
var NO_URL_ERR = TplError{
51+
msg: "No URL provided",
6352
}

util/handleError.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
package util
22

33
import (
4-
"fmt"
5-
"os"
4+
// "fmt"
65

76
"github.com/spf13/cobra"
87
)
98

10-
func HandleError(cmd *cobra.Command, err error, tplError TplError) {
9+
func HandleError(cmd *cobra.Command, err error, tplError TplError) error {
10+
//TODO:: Add Error Logging with Stacktraces
1111
// fmt.Println(err)
12-
fmt.Fprintf(cmd.OutOrStdout(), tplError.msg)
13-
os.Exit(tplError.exitCode)
12+
13+
// fmt.Fprintf(cmd.OutOrStdout(), tplError.msg)
14+
return err
1415
}

0 commit comments

Comments
 (0)