Skip to content

Commit ad132c4

Browse files
committed
integration
1 parent 598b04e commit ad132c4

7 files changed

Lines changed: 846 additions & 1 deletion

File tree

.github/workflows/release.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,21 @@ permissions:
1212
contents: write
1313

1414
jobs:
15+
integration:
16+
runs-on: ubuntu-latest
17+
steps:
18+
- uses: actions/checkout@v4
19+
20+
- name: Set up Go
21+
uses: actions/setup-go@v5
22+
with:
23+
go-version: '1.21'
24+
25+
- name: Integration tests
26+
run: make integration
27+
1528
release:
29+
needs: integration
1630
runs-on: ubuntu-latest
1731
strategy:
1832
matrix:

Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ format:
88
go fmt ./...
99

1010
test:
11-
go test ./... -v
11+
go test $(shell go list ./... | grep -v /integrations/) -v
12+
13+
integration:
14+
go test ./integrations/... -v -count=1 -timeout=120s
1215

1316
build: format lint test

integrations/batch/batch_test.go

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
package batch_test
2+
3+
import (
4+
"strings"
5+
"testing"
6+
7+
"github.com/vogo/namer/integrations/helper"
8+
)
9+
10+
func TestBatchWithConfigFile(t *testing.T) {
11+
configJSON := `{
12+
"xing": "王",
13+
"year": 2024,
14+
"month": 3,
15+
"day": 15,
16+
"hour": 10,
17+
"minute": 30,
18+
"gender": 1,
19+
"min_candidate_score": 60,
20+
"ming_keywords": "明,轩,浩"
21+
}`
22+
23+
cfgPath := helper.WriteConfigFile(t, configJSON)
24+
25+
stdout, _, err := helper.Run(t, "-c", cfgPath)
26+
if err != nil {
27+
t.Fatalf("batch scoring failed: %v", err)
28+
}
29+
30+
// Should show batch progress and top results
31+
checks := []string{
32+
"开始批量评分",
33+
"Top 10",
34+
"分",
35+
"高分名字详情",
36+
}
37+
38+
for _, check := range checks {
39+
if !strings.Contains(stdout, check) {
40+
t.Errorf("batch output missing %q", check)
41+
}
42+
}
43+
}
44+
45+
func TestBatchWithMinimalKeywords(t *testing.T) {
46+
configJSON := `{
47+
"xing": "李",
48+
"year": 2000,
49+
"month": 6,
50+
"day": 1,
51+
"hour": 8,
52+
"minute": 0,
53+
"gender": 2,
54+
"min_candidate_score": 60,
55+
"ming_keywords": "明"
56+
}`
57+
58+
cfgPath := helper.WriteConfigFile(t, configJSON)
59+
60+
stdout, _, err := helper.Run(t, "-c", cfgPath)
61+
if err != nil {
62+
t.Fatalf("batch with minimal keywords failed: %v", err)
63+
}
64+
65+
// With single keyword, should have 1 single-char + 1 double-char result
66+
if !strings.Contains(stdout, "评分完成") {
67+
t.Errorf("expected completion message, got: %s", stdout)
68+
}
69+
}
70+
71+
func TestBatchWithEmptyKeywords(t *testing.T) {
72+
configJSON := `{
73+
"xing": "王",
74+
"year": 2024,
75+
"month": 1,
76+
"day": 1,
77+
"hour": 12,
78+
"minute": 0,
79+
"gender": 1,
80+
"min_candidate_score": 60,
81+
"ming_keywords": ""
82+
}`
83+
84+
cfgPath := helper.WriteConfigFile(t, configJSON)
85+
86+
stdout, _, err := helper.Run(t, "-c", cfgPath)
87+
// Should handle empty keywords gracefully (may prompt interactively or skip)
88+
_ = err
89+
_ = stdout
90+
}
91+
92+
func TestBatchConfigFileNotFound(t *testing.T) {
93+
_, _, err := helper.Run(t, "-c", "/nonexistent/path/config.json")
94+
// Should handle missing config file - may prompt or error
95+
_ = err
96+
}
97+
98+
func TestBatchResultsSortedByScore(t *testing.T) {
99+
configJSON := `{
100+
"xing": "张",
101+
"year": 2024,
102+
"month": 5,
103+
"day": 10,
104+
"hour": 14,
105+
"minute": 0,
106+
"gender": 1,
107+
"min_candidate_score": 60,
108+
"ming_keywords": "明,轩,浩,然"
109+
}`
110+
111+
cfgPath := helper.WriteConfigFile(t, configJSON)
112+
113+
stdout, _, err := helper.Run(t, "-c", cfgPath)
114+
if err != nil {
115+
t.Fatalf("batch scoring failed: %v", err)
116+
}
117+
118+
// Find the Top 10 section and verify scores are descending
119+
lines := strings.Split(stdout, "\n")
120+
var scores []string
121+
inTop := false
122+
for _, line := range lines {
123+
if strings.Contains(line, "Top 10") {
124+
inTop = true
125+
continue
126+
}
127+
if inTop && strings.Contains(line, "====") {
128+
break
129+
}
130+
if inTop && strings.Contains(line, "分") {
131+
scores = append(scores, line)
132+
}
133+
}
134+
135+
if len(scores) == 0 {
136+
t.Error("no scores found in Top 10 section")
137+
}
138+
}

integrations/helper/helper.go

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package helper
2+
3+
import (
4+
"bytes"
5+
"os"
6+
"os/exec"
7+
"path/filepath"
8+
"runtime"
9+
"sync"
10+
"testing"
11+
)
12+
13+
var (
14+
buildOnce sync.Once
15+
binPath string
16+
buildErr error
17+
)
18+
19+
// NamerBinary compiles the namer binary to a temp directory (once) and returns its path.
20+
func NamerBinary(t *testing.T) string {
21+
t.Helper()
22+
23+
buildOnce.Do(func() {
24+
tmpDir, err := os.MkdirTemp("", "namer-integration-*")
25+
if err != nil {
26+
buildErr = err
27+
return
28+
}
29+
30+
bin := filepath.Join(tmpDir, "namer")
31+
if runtime.GOOS == "windows" {
32+
bin += ".exe"
33+
}
34+
35+
cmd := exec.Command("go", "build", "-o", bin, ".")
36+
cmd.Dir = projectRoot()
37+
out, err := cmd.CombinedOutput()
38+
if err != nil {
39+
buildErr = &BuildError{Output: string(out), Err: err}
40+
return
41+
}
42+
43+
binPath = bin
44+
})
45+
46+
if buildErr != nil {
47+
t.Fatalf("failed to build namer binary: %v", buildErr)
48+
}
49+
50+
return binPath
51+
}
52+
53+
// BuildError wraps a build failure with compiler output.
54+
type BuildError struct {
55+
Output string
56+
Err error
57+
}
58+
59+
func (e *BuildError) Error() string {
60+
return e.Err.Error() + "\n" + e.Output
61+
}
62+
63+
// Run executes the namer binary with the given args and returns stdout, stderr, and error.
64+
func Run(t *testing.T, args ...string) (stdout, stderr string, err error) {
65+
t.Helper()
66+
67+
bin := NamerBinary(t)
68+
cmd := exec.Command(bin, args...)
69+
cmd.Env = append(os.Environ(), "HOME="+t.TempDir()) // isolate from user config
70+
71+
var outBuf, errBuf bytes.Buffer
72+
cmd.Stdout = &outBuf
73+
cmd.Stderr = &errBuf
74+
75+
err = cmd.Run()
76+
return outBuf.String(), errBuf.String(), err
77+
}
78+
79+
// RunWithStdin executes the namer binary with the given args and stdin input.
80+
func RunWithStdin(t *testing.T, stdin string, args ...string) (stdout, stderr string, err error) {
81+
t.Helper()
82+
83+
bin := NamerBinary(t)
84+
cmd := exec.Command(bin, args...)
85+
cmd.Env = append(os.Environ(), "HOME="+t.TempDir())
86+
cmd.Stdin = bytes.NewBufferString(stdin)
87+
88+
var outBuf, errBuf bytes.Buffer
89+
cmd.Stdout = &outBuf
90+
cmd.Stderr = &errBuf
91+
92+
err = cmd.Run()
93+
return outBuf.String(), errBuf.String(), err
94+
}
95+
96+
// RunWithEnv executes the namer binary with extra environment variables.
97+
func RunWithEnv(t *testing.T, env []string, args ...string) (stdout, stderr string, err error) {
98+
t.Helper()
99+
100+
bin := NamerBinary(t)
101+
cmd := exec.Command(bin, args...)
102+
cmd.Env = append(os.Environ(), env...)
103+
104+
var outBuf, errBuf bytes.Buffer
105+
cmd.Stdout = &outBuf
106+
cmd.Stderr = &errBuf
107+
108+
err = cmd.Run()
109+
return outBuf.String(), errBuf.String(), err
110+
}
111+
112+
// WriteConfigFile creates a config file with the given JSON content and returns the path.
113+
func WriteConfigFile(t *testing.T, content string) string {
114+
t.Helper()
115+
116+
dir := t.TempDir()
117+
path := filepath.Join(dir, "namer.json")
118+
if err := os.WriteFile(path, []byte(content), 0644); err != nil {
119+
t.Fatalf("failed to write config file: %v", err)
120+
}
121+
122+
return path
123+
}
124+
125+
func projectRoot() string {
126+
// helper.go is at integrations/helper/helper.go
127+
_, f, _, _ := runtime.Caller(0)
128+
return filepath.Join(filepath.Dir(f), "..", "..")
129+
}

0 commit comments

Comments
 (0)