2024-09-08 10:09:54 +02:00
|
|
|
package main
|
2024-08-28 07:21:02 +02:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"log/slog"
|
|
|
|
"net/http"
|
|
|
|
"net/http/httptest"
|
|
|
|
"net/url"
|
|
|
|
"os"
|
|
|
|
"sort"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2024-09-07 12:10:48 +02:00
|
|
|
func TestServerServeHTTP(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
apiKey := "test"
|
2024-09-08 10:09:54 +02:00
|
|
|
srv := NewServer(NewMemory(), apiKey, slog.New(slog.NewJSONHandler(os.Stdout, nil)))
|
2024-09-07 12:10:48 +02:00
|
|
|
|
|
|
|
for _, tc := range []struct {
|
|
|
|
name string
|
|
|
|
key string
|
|
|
|
url string
|
|
|
|
method string
|
|
|
|
expStatus int
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "index always visible",
|
|
|
|
url: "/",
|
|
|
|
method: http.MethodGet,
|
|
|
|
expStatus: http.StatusOK,
|
|
|
|
},
|
|
|
|
} {
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
req, err := http.NewRequest(tc.method, tc.url, nil)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("exp nil, got %v", err)
|
|
|
|
}
|
|
|
|
res := httptest.NewRecorder()
|
|
|
|
srv.ServeHTTP(res, req)
|
|
|
|
if res.Result().StatusCode != tc.expStatus {
|
|
|
|
t.Errorf("exp %v, got %v", tc.expStatus, res.Result().StatusCode)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-28 07:21:02 +02:00
|
|
|
func TestSyncGet(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
now := time.Now()
|
2024-09-08 10:09:54 +02:00
|
|
|
mem := NewMemory()
|
2024-08-28 07:21:02 +02:00
|
|
|
|
2024-09-09 07:56:01 +02:00
|
|
|
items := []Item{
|
2024-08-28 07:21:02 +02:00
|
|
|
{ID: "id-0", Updated: now.Add(-10 * time.Minute)},
|
|
|
|
{ID: "id-1", Updated: now.Add(-5 * time.Minute)},
|
|
|
|
{ID: "id-2", Updated: now.Add(time.Minute)},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, item := range items {
|
|
|
|
if err := mem.Update(item); err != nil {
|
|
|
|
t.Errorf("exp nil, got %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-07 12:10:48 +02:00
|
|
|
apiKey := "test"
|
2024-09-08 10:09:54 +02:00
|
|
|
srv := NewServer(mem, apiKey, slog.New(slog.NewJSONHandler(os.Stdout, nil)))
|
2024-08-28 07:21:02 +02:00
|
|
|
|
|
|
|
for _, tc := range []struct {
|
|
|
|
name string
|
|
|
|
ts time.Time
|
|
|
|
expStatus int
|
2024-09-09 07:56:01 +02:00
|
|
|
expItems []Item
|
2024-08-28 07:21:02 +02:00
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "full",
|
|
|
|
expStatus: http.StatusOK,
|
|
|
|
expItems: items,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "normal",
|
|
|
|
ts: now.Add(-6 * time.Minute),
|
|
|
|
expStatus: http.StatusOK,
|
2024-09-09 07:56:01 +02:00
|
|
|
expItems: []Item{items[1], items[2]},
|
2024-08-28 07:21:02 +02:00
|
|
|
},
|
|
|
|
} {
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
url := fmt.Sprintf("/sync?ts=%s", url.QueryEscape(tc.ts.Format(time.RFC3339)))
|
|
|
|
req, err := http.NewRequest(http.MethodGet, url, nil)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("exp nil, got %v", err)
|
|
|
|
}
|
2024-09-07 12:10:48 +02:00
|
|
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", apiKey))
|
2024-08-28 07:21:02 +02:00
|
|
|
res := httptest.NewRecorder()
|
|
|
|
srv.ServeHTTP(res, req)
|
|
|
|
|
|
|
|
if res.Result().StatusCode != tc.expStatus {
|
|
|
|
t.Errorf("exp %v, got %v", tc.expStatus, res.Result().StatusCode)
|
|
|
|
}
|
2024-09-09 07:56:01 +02:00
|
|
|
var actItems []Item
|
2024-08-28 07:21:02 +02:00
|
|
|
actBody, err := io.ReadAll(res.Result().Body)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("exp nil, got %v", err)
|
|
|
|
}
|
|
|
|
defer res.Result().Body.Close()
|
|
|
|
|
|
|
|
if err := json.Unmarshal(actBody, &actItems); err != nil {
|
|
|
|
t.Errorf("exp nil, got %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(actItems) != len(tc.expItems) {
|
|
|
|
t.Errorf("exp %d, got %d", len(tc.expItems), len(actItems))
|
|
|
|
}
|
|
|
|
sort.Slice(actItems, func(i, j int) bool {
|
|
|
|
return actItems[i].ID < actItems[j].ID
|
|
|
|
})
|
|
|
|
for i := range actItems {
|
|
|
|
if actItems[i].ID != tc.expItems[i].ID {
|
|
|
|
t.Errorf("exp %v, got %v", tc.expItems[i].ID, actItems[i].ID)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSyncPost(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
2024-09-07 12:10:48 +02:00
|
|
|
apiKey := "test"
|
2024-08-28 07:21:02 +02:00
|
|
|
for _, tc := range []struct {
|
|
|
|
name string
|
|
|
|
reqBody []byte
|
|
|
|
expStatus int
|
2024-09-09 07:56:01 +02:00
|
|
|
expItems []Item
|
2024-08-28 07:21:02 +02:00
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "empty",
|
|
|
|
expStatus: http.StatusBadRequest,
|
|
|
|
},
|
|
|
|
{
|
2024-09-09 07:56:01 +02:00
|
|
|
name: "invalid json",
|
2024-08-28 07:21:02 +02:00
|
|
|
reqBody: []byte(`{"fail}`),
|
|
|
|
expStatus: http.StatusBadRequest,
|
|
|
|
},
|
2024-09-09 07:56:01 +02:00
|
|
|
{
|
|
|
|
name: "invalid item",
|
|
|
|
reqBody: []byte(`[
|
|
|
|
{"id":"id-1","kind":"test","updated":"2024-09-06T08:00:00Z"},
|
|
|
|
]`),
|
|
|
|
expStatus: http.StatusBadRequest,
|
|
|
|
},
|
2024-08-28 07:21:02 +02:00
|
|
|
{
|
|
|
|
name: "normal",
|
|
|
|
reqBody: []byte(`[
|
2024-09-09 07:56:01 +02:00
|
|
|
{"id":"id-1","kind":"test","updated":"2024-09-06T08:00:00Z","deleted":false,"body":"item"},
|
|
|
|
{"id":"id-2","kind":"test","updated":"2024-09-06T08:12:00Z","deleted":false,"body":"item2"}
|
2024-08-28 07:21:02 +02:00
|
|
|
]`),
|
|
|
|
expStatus: http.StatusNoContent,
|
2024-09-09 07:56:01 +02:00
|
|
|
expItems: []Item{
|
2024-08-28 07:21:02 +02:00
|
|
|
{ID: "id-1", Updated: time.Date(2024, 9, 6, 8, 0, 0, 0, time.UTC)},
|
|
|
|
{ID: "id-2", Updated: time.Date(2024, 9, 6, 12, 0, 0, 0, time.UTC)},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
} {
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
2024-09-08 10:09:54 +02:00
|
|
|
mem := NewMemory()
|
|
|
|
srv := NewServer(mem, apiKey, slog.New(slog.NewJSONHandler(os.Stdout, nil)))
|
2024-08-28 07:21:02 +02:00
|
|
|
req, err := http.NewRequest(http.MethodPost, "/sync", bytes.NewBuffer(tc.reqBody))
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("exp nil, got %v", err)
|
|
|
|
}
|
2024-09-07 12:10:48 +02:00
|
|
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", apiKey))
|
2024-08-28 07:21:02 +02:00
|
|
|
res := httptest.NewRecorder()
|
|
|
|
srv.ServeHTTP(res, req)
|
|
|
|
|
|
|
|
if res.Result().StatusCode != tc.expStatus {
|
|
|
|
t.Errorf("exp %v, got %v", tc.expStatus, res.Result().StatusCode)
|
|
|
|
}
|
|
|
|
|
|
|
|
actItems, err := mem.Updated(time.Time{})
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("exp nil, git %v", err)
|
|
|
|
}
|
|
|
|
if len(actItems) != len(tc.expItems) {
|
|
|
|
t.Errorf("exp %d, got %d", len(tc.expItems), len(actItems))
|
|
|
|
}
|
|
|
|
sort.Slice(actItems, func(i, j int) bool {
|
|
|
|
return actItems[i].ID < actItems[j].ID
|
|
|
|
})
|
|
|
|
for i := range actItems {
|
|
|
|
if actItems[i].ID != tc.expItems[i].ID {
|
|
|
|
t.Errorf("exp %v, got %v", tc.expItems[i].ID, actItems[i].ID)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|