hn-readcard-go/server_test.go

125 lines
4.9 KiB
Go
Raw Permalink Normal View History

2026-08-06 11:23:33 +08:00
package main
import (
"encoding/json"
"errors"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
type fakeReader struct {
status ReaderStatus
readResult DLLResult
readErr error
readPanic any
hmacResult DLLResult
hmacErr error
lastType string
lastNoPIN bool
}
func (f *fakeReader) Status() ReaderStatus { return f.status }
func (f *fakeReader) ReadCard(readerType string, noPIN bool) (DLLResult, error) {
f.lastType, f.lastNoPIN = readerType, noPIN
if f.readPanic != nil {
panic(f.readPanic)
}
return f.readResult, f.readErr
}
func (f *fakeReader) HMACSM3(_, _, _, _ string) (DLLResult, error) {
return f.hmacResult, f.hmacErr
}
func testHandler(reader *fakeReader) http.Handler {
cfg := defaultConfig(".")
return newHandler(cfg, reader, discardLogger())
}
func TestHealth(t *testing.T) {
reader := &fakeReader{status: ReaderStatus{Loaded: true, DLLDir: `C:\\dll`, Arch: "386", Platform: "windows"}}
recorder := httptest.NewRecorder()
testHandler(reader).ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/health", nil))
if recorder.Code != http.StatusOK {
t.Fatalf("status = %d, body = %s", recorder.Code, recorder.Body.String())
}
var response apiResponse
if err := json.Unmarshal(recorder.Body.Bytes(), &response); err != nil {
t.Fatal(err)
}
if response.Code != 0 {
t.Fatalf("unexpected response: %#v", response)
}
}
func TestReadNoPINRequiresReaderType(t *testing.T) {
recorder := httptest.NewRecorder()
testHandler(&fakeReader{}).ServeHTTP(recorder, httptest.NewRequest(http.MethodPost, "/api/card/read-nopin", strings.NewReader(`{}`)))
if recorder.Code != http.StatusBadRequest {
t.Fatalf("status = %d, body = %s", recorder.Code, recorder.Body.String())
}
}
func TestReadNoPINSuccess(t *testing.T) {
reader := &fakeReader{readResult: DLLResult{Code: 0, Success: true, Data: map[string]any{"Success": true, "Name": "测试"}}}
recorder := httptest.NewRecorder()
request := httptest.NewRequest(http.MethodPost, "/api/card/read-nopin", strings.NewReader(`{"reader_type":" ZJ_HZ_310000 "}`))
testHandler(reader).ServeHTTP(recorder, request)
if recorder.Code != http.StatusOK || reader.lastType != "ZJ_HZ_310000" || !reader.lastNoPIN {
t.Fatalf("status=%d type=%q noPIN=%v body=%s", recorder.Code, reader.lastType, reader.lastNoPIN, recorder.Body.String())
}
}
func TestReadFailure(t *testing.T) {
reader := &fakeReader{readResult: DLLResult{Code: -1, Success: false, Raw: `{"Success":false,"Message":"未检测到卡"}`, Data: map[string]any{"Success": false, "Message": "未检测到卡"}}}
recorder := httptest.NewRecorder()
testHandler(reader).ServeHTTP(recorder, httptest.NewRequest(http.MethodPost, "/api/card/read-nopin", strings.NewReader(`{"reader_type":"ZJ_HZ_310000"}`)))
if recorder.Code != http.StatusInternalServerError || !strings.Contains(recorder.Body.String(), "未检测到卡") {
t.Fatalf("status=%d body=%s", recorder.Code, recorder.Body.String())
}
}
func TestReadError(t *testing.T) {
reader := &fakeReader{readErr: errors.New("DLL unavailable")}
recorder := httptest.NewRecorder()
testHandler(reader).ServeHTTP(recorder, httptest.NewRequest(http.MethodPost, "/api/card/read-nopin", strings.NewReader(`{"reader_type":"ZJ_HZ_310000"}`)))
if recorder.Code != http.StatusInternalServerError {
t.Fatalf("status=%d body=%s", recorder.Code, recorder.Body.String())
}
}
func TestReadPanicIsRecovered(t *testing.T) {
reader := &fakeReader{readPanic: "driver panic"}
recorder := httptest.NewRecorder()
testHandler(reader).ServeHTTP(recorder, httptest.NewRequest(http.MethodPost, "/api/card/read-nopin", strings.NewReader(`{"reader_type":"ZJ_HZ_310000"}`)))
if recorder.Code != http.StatusInternalServerError || !strings.Contains(recorder.Body.String(), "服务内部异常") {
t.Fatalf("status=%d body=%s", recorder.Code, recorder.Body.String())
}
}
func TestWithPINRejectsOtherCity(t *testing.T) {
recorder := httptest.NewRecorder()
testHandler(&fakeReader{}).ServeHTTP(recorder, httptest.NewRequest(http.MethodPost, "/api/card/read", strings.NewReader(`{"reader_type":"ZJ_HZ_310000"}`)))
if recorder.Code != http.StatusBadRequest {
t.Fatalf("status=%d body=%s", recorder.Code, recorder.Body.String())
}
}
func TestHMACSuccess(t *testing.T) {
reader := &fakeReader{hmacResult: DLLResult{Code: 0, Success: true, Data: "123:ABC"}}
recorder := httptest.NewRecorder()
testHandler(reader).ServeHTTP(recorder, httptest.NewRequest(http.MethodPost, "/api/hmac-sm3", strings.NewReader(`{"key":"k","secret":"1234","request_body":"{}"}`)))
if recorder.Code != http.StatusOK || !strings.Contains(recorder.Body.String(), "123:ABC") {
t.Fatalf("status=%d body=%s", recorder.Code, recorder.Body.String())
}
}
func TestInvalidJSON(t *testing.T) {
recorder := httptest.NewRecorder()
testHandler(&fakeReader{}).ServeHTTP(recorder, httptest.NewRequest(http.MethodPost, "/api/card/read-nopin", strings.NewReader(`{`)))
if recorder.Code != http.StatusBadRequest {
t.Fatalf("status=%d body=%s", recorder.Code, recorder.Body.String())
}
}