| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141 |
- 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))
- }
|