测试修复: - 修复集成测试中的重复键冲突问题 - 移除 JSON 中多余的尾随逗号 - 新增 inprocess_transport_test.go - 更新 haiku 模型映射测试用例 数据库迁移: - 026: 运营指标聚合表 - 027: 使用量与计费一致性约束
142 lines
4.5 KiB
Go
142 lines
4.5 KiB
Go
package repository
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/Wei-Shaw/sub2api/internal/service"
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/stretchr/testify/suite"
|
|
)
|
|
|
|
type TurnstileServiceSuite struct {
|
|
suite.Suite
|
|
ctx context.Context
|
|
verifier *turnstileVerifier
|
|
received chan url.Values
|
|
}
|
|
|
|
func (s *TurnstileServiceSuite) SetupTest() {
|
|
s.ctx = context.Background()
|
|
s.received = make(chan url.Values, 1)
|
|
verifier, ok := NewTurnstileVerifier().(*turnstileVerifier)
|
|
require.True(s.T(), ok, "type assertion failed")
|
|
s.verifier = verifier
|
|
}
|
|
|
|
func (s *TurnstileServiceSuite) setupTransport(handler http.HandlerFunc) {
|
|
s.verifier.verifyURL = "http://in-process/turnstile"
|
|
s.verifier.httpClient = &http.Client{
|
|
Transport: newInProcessTransport(handler, nil),
|
|
}
|
|
}
|
|
|
|
func (s *TurnstileServiceSuite) TestVerifyToken_SendsFormAndDecodesJSON() {
|
|
s.setupTransport(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
// Capture form data in main goroutine context later
|
|
body, _ := io.ReadAll(r.Body)
|
|
values, _ := url.ParseQuery(string(body))
|
|
s.received <- values
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_ = json.NewEncoder(w).Encode(service.TurnstileVerifyResponse{Success: true})
|
|
}))
|
|
|
|
resp, err := s.verifier.VerifyToken(s.ctx, "sk", "token", "1.1.1.1")
|
|
require.NoError(s.T(), err, "VerifyToken")
|
|
require.NotNil(s.T(), resp)
|
|
require.True(s.T(), resp.Success, "expected success response")
|
|
|
|
// Assert form fields in main goroutine
|
|
select {
|
|
case values := <-s.received:
|
|
require.Equal(s.T(), "sk", values.Get("secret"))
|
|
require.Equal(s.T(), "token", values.Get("response"))
|
|
require.Equal(s.T(), "1.1.1.1", values.Get("remoteip"))
|
|
default:
|
|
require.Fail(s.T(), "expected server to receive request")
|
|
}
|
|
}
|
|
|
|
func (s *TurnstileServiceSuite) TestVerifyToken_ContentType() {
|
|
var contentType string
|
|
s.setupTransport(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
contentType = r.Header.Get("Content-Type")
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_ = json.NewEncoder(w).Encode(service.TurnstileVerifyResponse{Success: true})
|
|
}))
|
|
|
|
_, err := s.verifier.VerifyToken(s.ctx, "sk", "token", "1.1.1.1")
|
|
require.NoError(s.T(), err)
|
|
require.True(s.T(), strings.HasPrefix(contentType, "application/x-www-form-urlencoded"), "unexpected content-type: %s", contentType)
|
|
}
|
|
|
|
func (s *TurnstileServiceSuite) TestVerifyToken_EmptyRemoteIP_NotSent() {
|
|
s.setupTransport(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
body, _ := io.ReadAll(r.Body)
|
|
values, _ := url.ParseQuery(string(body))
|
|
s.received <- values
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_ = json.NewEncoder(w).Encode(service.TurnstileVerifyResponse{Success: true})
|
|
}))
|
|
|
|
_, err := s.verifier.VerifyToken(s.ctx, "sk", "token", "")
|
|
require.NoError(s.T(), err)
|
|
|
|
select {
|
|
case values := <-s.received:
|
|
require.Equal(s.T(), "", values.Get("remoteip"), "remoteip should be empty or not sent")
|
|
default:
|
|
require.Fail(s.T(), "expected server to receive request")
|
|
}
|
|
}
|
|
|
|
func (s *TurnstileServiceSuite) TestVerifyToken_RequestError() {
|
|
s.verifier.verifyURL = "http://in-process/turnstile"
|
|
s.verifier.httpClient = &http.Client{
|
|
Transport: roundTripFunc(func(*http.Request) (*http.Response, error) {
|
|
return nil, errors.New("dial failed")
|
|
}),
|
|
}
|
|
|
|
_, err := s.verifier.VerifyToken(s.ctx, "sk", "token", "1.1.1.1")
|
|
require.Error(s.T(), err, "expected error when server is closed")
|
|
}
|
|
|
|
func (s *TurnstileServiceSuite) TestVerifyToken_InvalidJSON() {
|
|
s.setupTransport(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_, _ = io.WriteString(w, "not-valid-json")
|
|
}))
|
|
|
|
_, err := s.verifier.VerifyToken(s.ctx, "sk", "token", "1.1.1.1")
|
|
require.Error(s.T(), err, "expected error for invalid JSON response")
|
|
}
|
|
|
|
func (s *TurnstileServiceSuite) TestVerifyToken_SuccessFalse() {
|
|
s.setupTransport(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_ = json.NewEncoder(w).Encode(service.TurnstileVerifyResponse{
|
|
Success: false,
|
|
ErrorCodes: []string{"invalid-input-response"},
|
|
})
|
|
}))
|
|
|
|
resp, err := s.verifier.VerifyToken(s.ctx, "sk", "token", "1.1.1.1")
|
|
require.NoError(s.T(), err, "VerifyToken should not error on success=false")
|
|
require.NotNil(s.T(), resp)
|
|
require.False(s.T(), resp.Success)
|
|
require.Contains(s.T(), resp.ErrorCodes, "invalid-input-response")
|
|
}
|
|
|
|
func TestTurnstileServiceSuite(t *testing.T) {
|
|
suite.Run(t, new(TurnstileServiceSuite))
|
|
}
|