142 lines
4.3 KiB
Go
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)
|
|
}
|
|
}
|