Skip to content

Commit 461f7f0

Browse files
authored
test: cmd package (#95)
1 parent c3abe87 commit 461f7f0

File tree

4 files changed

+193
-53
lines changed

4 files changed

+193
-53
lines changed

cmd/jlv/main.go

Lines changed: 53 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ package main
22

33
import (
44
"context"
5+
"errors"
56
"flag"
67
"fmt"
8+
"io"
9+
"io/fs"
710
"os"
811
"path"
912

@@ -25,48 +28,80 @@ func main() {
2528
printVersion := flag.Bool("version", false, "Print version")
2629
flag.Parse()
2730

28-
if *printVersion {
31+
err := runApp(applicationArguments{
32+
Stdout: os.Stdout,
33+
Stdin: os.Stdin,
34+
35+
ConfigPath: *configPath,
36+
PrintVersion: *printVersion,
37+
Args: flag.Args(),
38+
39+
RunProgram: func(p *tea.Program) (tea.Model, error) {
40+
return p.Run()
41+
},
42+
})
43+
if err != nil {
44+
fmt.Fprintln(os.Stderr, "Error: "+err.Error())
45+
os.Exit(1)
46+
}
47+
}
48+
49+
type applicationArguments struct {
50+
Stdout io.Writer
51+
Stdin fs.File
52+
53+
ConfigPath string
54+
PrintVersion bool
55+
Args []string
56+
57+
RunProgram func(*tea.Program) (tea.Model, error)
58+
}
59+
60+
func runApp(args applicationArguments) (err error) {
61+
if args.PrintVersion {
2962
// nolint: forbidigo // Version command.
30-
print("github.com/hedhyw/json-log-viewer@" + version + "\n")
63+
fmt.Fprintln(args.Stdout, "github.com/hedhyw/json-log-viewer@"+version)
3164

32-
return
65+
return nil
3366
}
3467

35-
cfg, err := readConfig(*configPath)
68+
cfg, err := readConfig(args.ConfigPath)
3669
if err != nil {
37-
fatalf("Error reading config: %s\n", err)
70+
return fmt.Errorf("reading config: %w", err)
3871
}
3972

4073
fileName := ""
4174
var inputSource *source.Source
4275

43-
switch flag.NArg() {
76+
switch len(args.Args) {
4477
case 0:
4578
// Tee stdin to a temp file, so that we can
4679
// lazy load the log entries using random access.
4780
fileName = "-"
4881

49-
stdIn, err := getStdinReader(os.Stdin)
82+
stdin, err := getStdinReader(args.Stdin)
5083
if err != nil {
51-
fatalf("Stdin: %s\n", err)
84+
return fmt.Errorf("getting stdin: %w", err)
5285
}
5386

54-
inputSource, err = source.Reader(stdIn, cfg)
87+
inputSource, err = source.Reader(stdin, cfg)
5588
if err != nil {
56-
fatalf("Could not create temp flie: %s\n", err)
89+
return fmt.Errorf("creating a temporary file: %w", err)
5790
}
58-
defer inputSource.Close()
5991

92+
defer func() { err = errors.Join(err, inputSource.Close()) }()
6093
case 1:
61-
fileName = flag.Arg(0)
94+
fileName = args.Args[0]
95+
6296
inputSource, err = source.File(fileName, cfg)
6397
if err != nil {
64-
fatalf("Could not create temp flie: %s\n", err)
98+
return fmt.Errorf("reading file: %w", err)
6599
}
66-
defer inputSource.Close()
67100

101+
defer func() { err = errors.Join(err, inputSource.Close()) }()
68102
default:
69-
fatalf("Invalid arguments, usage: %s file.log\n", os.Args[0])
103+
// nolint: err113 // One time case.
104+
return fmt.Errorf("invalid arguments, usage: %s file.log", os.Args[0])
70105
}
71106

72107
appModel := app.NewModel(fileName, cfg, version)
@@ -80,14 +115,11 @@ func main() {
80115
}
81116
})
82117

83-
if _, err := program.Run(); err != nil {
84-
fatalf("Error running program: %s\n", err)
118+
if _, err := args.RunProgram(program); err != nil {
119+
return fmt.Errorf("running program: %w", err)
85120
}
86-
}
87121

88-
func fatalf(message string, args ...any) {
89-
fmt.Fprintf(os.Stderr, message, args...)
90-
os.Exit(1)
122+
return nil
91123
}
92124

93125
// readConfig tries to read config from working directory or home directory.

cmd/jlv/main_test.go

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,150 @@ import (
88
"os"
99
"testing"
1010

11+
"github.com/hedhyw/json-log-viewer/internal/app"
12+
"github.com/hedhyw/json-log-viewer/internal/pkg/config"
13+
"github.com/hedhyw/json-log-viewer/internal/pkg/tests"
14+
15+
tea "github.com/charmbracelet/bubbletea"
1116
"github.com/stretchr/testify/assert"
1217
"github.com/stretchr/testify/require"
1318
)
1419

20+
func TestRunAppVersion(t *testing.T) {
21+
t.Parallel()
22+
23+
var outputBuf bytes.Buffer
24+
25+
err := runApp(applicationArguments{
26+
Stdout: &outputBuf,
27+
PrintVersion: true,
28+
})
29+
require.NoError(t, err)
30+
31+
assert.Contains(t, outputBuf.String(), version)
32+
}
33+
34+
func TestRunAppRunProgramFailed(t *testing.T) {
35+
t.Parallel()
36+
37+
fileName := tests.RequireCreateFile(t, []byte(t.Name()))
38+
39+
err := runApp(applicationArguments{
40+
Args: []string{fileName},
41+
RunProgram: func(*tea.Program) (tea.Model, error) {
42+
return nil, tests.ErrTest
43+
},
44+
})
45+
require.ErrorIs(t, err, tests.ErrTest)
46+
}
47+
48+
func TestRunAppRunProgramReadConfigInvalid(t *testing.T) {
49+
t.Parallel()
50+
51+
configPath := tests.RequireCreateFile(t, []byte("invalid config"))
52+
53+
err := runApp(applicationArguments{
54+
ConfigPath: configPath,
55+
RunProgram: func(*tea.Program) (tea.Model, error) {
56+
t.Fatal("Should not run")
57+
58+
return app.NewModel("", config.GetDefaultConfig(), version), nil
59+
},
60+
})
61+
require.Error(t, err)
62+
}
63+
64+
func TestRunAppUnexpectedNumberOfArgs(t *testing.T) {
65+
t.Parallel()
66+
67+
err := runApp(applicationArguments{
68+
Args: []string{"1", "2", "3"},
69+
})
70+
require.Error(t, err)
71+
}
72+
73+
func TestRunAppReadFileSuccess(t *testing.T) {
74+
t.Parallel()
75+
76+
fileName := tests.RequireCreateFile(t, []byte(t.Name()))
77+
78+
var isStarted bool
79+
80+
err := runApp(applicationArguments{
81+
Args: []string{fileName},
82+
RunProgram: func(p *tea.Program) (tea.Model, error) {
83+
assert.NotNil(t, p)
84+
isStarted = true
85+
86+
return app.NewModel("", config.GetDefaultConfig(), version), nil
87+
},
88+
})
89+
require.NoError(t, err)
90+
91+
assert.True(t, isStarted)
92+
}
93+
94+
func TestRunAppReadFileNotFound(t *testing.T) {
95+
t.Parallel()
96+
97+
err := runApp(applicationArguments{
98+
Args: []string{t.Name() + "not found"},
99+
RunProgram: func(*tea.Program) (tea.Model, error) {
100+
t.Fatal("Should not run")
101+
102+
return app.NewModel("", config.GetDefaultConfig(), version), nil
103+
},
104+
})
105+
require.Error(t, err)
106+
}
107+
108+
func TestRunAppReadStdinSuccess(t *testing.T) {
109+
t.Parallel()
110+
111+
fakeStdin := fakeFile{
112+
Reader: bytes.NewReader([]byte(t.Name())),
113+
StatFileInfo: fakeFileInfo{
114+
FileMode: os.ModeNamedPipe,
115+
},
116+
}
117+
118+
var isStarted bool
119+
120+
err := runApp(applicationArguments{
121+
Args: []string{},
122+
Stdin: fakeStdin,
123+
RunProgram: func(p *tea.Program) (tea.Model, error) {
124+
assert.NotNil(t, p)
125+
isStarted = true
126+
127+
return app.NewModel("", config.GetDefaultConfig(), version), nil
128+
},
129+
})
130+
require.NoError(t, err)
131+
132+
assert.True(t, isStarted)
133+
}
134+
135+
func TestRunAppReadStdinStatFailed(t *testing.T) {
136+
t.Parallel()
137+
138+
fakeStdin := fakeFile{
139+
Reader: bytes.NewReader([]byte(t.Name())),
140+
ErrStat: tests.ErrTest,
141+
}
142+
143+
err := runApp(applicationArguments{
144+
Args: []string{},
145+
Stdin: fakeStdin,
146+
RunProgram: func(*tea.Program) (tea.Model, error) {
147+
t.Fatal("Should not run")
148+
149+
return app.NewModel("", config.GetDefaultConfig(), version), nil
150+
},
151+
})
152+
require.ErrorIs(t, err, tests.ErrTest)
153+
}
154+
15155
func TestGetStdinSource(t *testing.T) {
16156
t.Parallel()
17157

cmd/jlv/stdin_reader_mock.go

Lines changed: 0 additions & 30 deletions
This file was deleted.
Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
//go:build !mock_stdin
2-
31
package main
42

53
import (

0 commit comments

Comments
 (0)