Files
bot-pontos/backend/internal/adapters/pontomais/client_test.go
2026-10-23 22:31:02 -03:00

142 lines
4.3 KiB
Go

package pontomais
import (
"context"
"encoding/json"
"errors"
"net/http"
"net/http/httptest"
"sync/atomic"
"testing"
"time"
"botpontos/internal/domain"
)
type plainKeys struct{}
func (plainKeys) Decrypt(data []byte, _ int16) (string, error) { return string(data), nil }
var saoPaulo = domain.LoadLocation("America/Sao_Paulo")
func newServer(t *testing.T, historyStatus func(call int32) int) (*httptest.Server, *int32, *int32) {
t.Helper()
var logins, histories int32
mux := http.NewServeMux()
mux.HandleFunc("POST /api/auth/sign_in", func(w http.ResponseWriter, r *http.Request) {
atomic.AddInt32(&logins, 1)
var body authRequest
_ = json.NewDecoder(r.Body).Decode(&body)
if body.Password != "certa" {
w.WriteHeader(http.StatusUnauthorized)
return
}
_ = json.NewEncoder(w).Encode(map[string]any{"token": "tok", "client_id": "cli", "data": map[string]string{"login": body.Login}})
})
mux.HandleFunc("GET /api/time_cards/current/last_cached", func(w http.ResponseWriter, r *http.Request) {
call := atomic.AddInt32(&histories, 1)
if status := historyStatus(call); status != http.StatusOK {
w.WriteHeader(status)
return
}
if r.Header.Get("access-token") != "tok" {
w.WriteHeader(http.StatusUnauthorized)
return
}
_ = json.NewEncoder(w).Encode(map[string]any{"time_cards": []map[string]any{
{"date": "2026-10-14", "time": "08:00:00"},
{"date": "2026-10-14", "time": "12:00:00"},
{"date": "2026-10-14", "time": "12:30:00", "disabled": true},
}})
})
server := httptest.NewServer(mux)
t.Cleanup(server.Close)
return server, &logins, &histories
}
func newClient(server *httptest.Server) *Client {
c := NewClient(server.URL+"/api/auth/sign_in", server.URL+"/api/time_cards/current/last_cached", 2*time.Second, plainKeys{})
c.retryDelay = time.Millisecond
return c
}
func cred(password string) domain.Credential {
return domain.Credential{UserID: 1, APIEmail: "a@b.c", EncryptedPassword: []byte(password), UpdatedAt: time.Unix(100, 0)}
}
func TestHistoryParsesAndCachesSession(t *testing.T) {
server, logins, _ := newServer(t, func(int32) int { return http.StatusOK })
c := newClient(server)
points, err := c.History(context.Background(), cred("certa"), saoPaulo)
if err != nil {
t.Fatal(err)
}
if len(points) != 2 {
t.Fatalf("esperados 2 pontos (o desabilitado é ignorado), vieram %d", len(points))
}
if got := points[0].OccurredAt.In(saoPaulo).Format("15:04"); got != "08:00" {
t.Errorf("primeiro ponto = %s", got)
}
if _, err := c.History(context.Background(), cred("certa"), saoPaulo); err != nil {
t.Fatal(err)
}
if *logins != 1 {
t.Errorf("a sessão deveria ser reaproveitada; logins = %d", *logins)
}
}
func TestHistoryRetriesTransientError(t *testing.T) {
server, _, histories := newServer(t, func(call int32) int {
if call == 1 {
return http.StatusBadGateway
}
return http.StatusOK
})
if _, err := newClient(server).History(context.Background(), cred("certa"), saoPaulo); err != nil {
t.Fatalf("a segunda tentativa deveria funcionar: %v", err)
}
if *histories != 2 {
t.Errorf("esperadas 2 chamadas ao histórico, vieram %d", *histories)
}
}
func TestHistoryRejectedCredential(t *testing.T) {
server, _, _ := newServer(t, func(int32) int { return http.StatusOK })
_, err := newClient(server).History(context.Background(), cred("errada"), saoPaulo)
if !errors.Is(err, ErrAuthRejected) {
t.Fatalf("esperado ErrAuthRejected, veio %v", err)
}
}
func TestHistoryRelogsAfterUnauthorized(t *testing.T) {
server, logins, _ := newServer(t, func(call int32) int {
if call == 2 {
return http.StatusUnauthorized
}
return http.StatusOK
})
c := newClient(server)
if _, err := c.History(context.Background(), cred("certa"), saoPaulo); err != nil {
t.Fatal(err)
}
if _, err := c.History(context.Background(), cred("certa"), saoPaulo); err != nil {
t.Fatalf("deveria refazer o login após 401: %v", err)
}
if *logins != 2 {
t.Errorf("esperados 2 logins, vieram %d", *logins)
}
}
func TestValidate(t *testing.T) {
server, _, _ := newServer(t, func(int32) int { return http.StatusOK })
c := newClient(server)
if err := c.Validate(context.Background(), "a@b.c", "certa", saoPaulo); err != nil {
t.Fatalf("credencial certa: %v", err)
}
if err := c.Validate(context.Background(), "a@b.c", "errada", saoPaulo); !errors.Is(err, ErrAuthRejected) {
t.Fatalf("credencial errada: %v", err)
}
}