昨天我們用 fake ES 成功寫出第一個 E2E 測試。今天的目標更進一步:
啟動一個本地 Elasticsearch(Docker)並撰寫真正的 ESSearchService 實作,然後寫一個最小的 smoke test,確認 /search 能打通
先在 docker-compose.yml 裡加入 ES:
version: "3.8"
services:
es01:
image: docker.elastic.co/elasticsearch/elasticsearch:8.15.0
container_name: es01
environment:
- discovery.type=single-node
- xpack.security.enabled=false
ports:
- "9200:9200"
啟動:
docker compose up -d es01
確認是否正常:
curl http://localhost:9200/
應該會看到一個 JSON,顯示 cluster name 與版本。
我們替換掉昨天的 FakeSearchService,寫一個 ESSearchService:
// es_service.go
package search
import (
"context"
"encoding/json"
"fmt"
"net/http"
"strings"
)
type ESSearchService struct {
Endpoint string
}
func (es *ESSearchService) Search(ctx context.Context, query string) ([]SearchResult, error) {
body := fmt.Sprintf(`{"query":{"match":{"title":"%s"}}}`, query)
req, err := http.NewRequestWithContext(ctx, "GET",
es.Endpoint+"/_search",
strings.NewReader(body))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var raw struct {
Hits struct {
Hits []struct {
ID string `json:"_id"`
Source struct {
Title string `json:"title"`
} `json:"_source"`
} `json:"hits"`
} `json:"hits"`
}
if err := json.NewDecoder(resp.Body).Decode(&raw); err != nil {
return nil, err
}
results := []SearchResult{}
for _, h := range raw.Hits.Hits {
results = append(results, SearchResult{
ID: h.ID,
Title: h.Source.Title,
})
}
return results, nil
}
先插入一筆假文件:
curl -X POST "http://localhost:9200/books/_doc/1" \
-H 'Content-Type: application/json' \
-d '{"title":"Golang 101"}'
刷新 index
curl -X POST "http://localhost:9200/books/_refresh"
這裡我們不用 httptest,直接從 SearchService 驗證:
// es_service_test.go
func TestESSearchService_Smoke(t *testing.T) {
svc := &search.ESSearchService{Endpoint: "http://localhost:9200/books"}
ctx := context.Background()
results, err := svc.Search(ctx, "golang")
if err != nil {
t.Fatalf("search error: %v", err)
}
if len(results) == 0 {
t.Fatalf("expected at least 1 result, got 0")
}
t.Logf("got %+v", results[0])
}
執行:
go test -run TestESSearchService_Smoke ./...
會看到測試成功,並印出我們插入的 Golang 101。
今天我們完成了:
這樣,我們的 /search API 已經能夠打到真實資料庫 🎉
明天,我們要進行最後的 Golang Phase 穩定化:清理目錄、補測試、寫 README,為進入 Elasticsearch Phase 做準備。