File/package naming convention conforming better to go standards - as ugly as it is

This commit is contained in:
bwilliams committed 2026-10-13 16:31:47 +01:00
1 parent be4770cbab
commit d6688a6754
32 files changed
+36 -36

No files matched your search

+209
View File
@@ -0,0 +1,209 @@
package mock
import (
"encoding/base64"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"path/filepath"
"strings"
"testing"
"time"
"git.avsdev.uk/Go/job-search/internal/mock/findatender/ocds"
)
func TestUpdatedFromIsLocalTime(t *testing.T) {
london, err := time.LoadLocation("Europe/London")
if err != nil {
t.Fatal(err)
}
store, err := OpenStore(filepath.Join(t.TempDir(), "r.jsonl"))
if err != nil {
t.Fatal(err)
}
defer store.Close()
// Two releases at 11:45Z and 12:15Z, i.e. 12:45 and 13:15 BST.
for i, at := range []time.Time{
time.Date(2026, 10, 8, 11, 45, 0, 0, time.UTC),
time.Date(2026, 10, 8, 12, 15, 0, 0, time.UTC),
} {
if err := store.Append(
ocds.Release{
OCID: "ocds-test",
ID: []string{"000001-2026", "000002-2026"}[i],
Date: at,
Tag: []string{"tender"},
},
); err != nil {
t.Fatal(err)
}
}
h := NewHandler(store, "", london)
count := func(query string) int {
rec := httptest.NewRecorder()
h(rec, httptest.NewRequest("GET", BasePath+"?"+query, nil))
var pkg ocds.ReleasePackage
if err := json.Unmarshal(rec.Body.Bytes(), &pkg); err != nil {
t.Fatalf("%s: %v (%s)", query, err, rec.Body.String())
}
return len(pkg.Releases)
}
cases := map[string]int{
"updatedFrom=2026-10-08T13:00:00": 1, // 13:00 BST = 12:00Z
"updatedFrom=2026-10-08T12:30:00": 2, // 12:30 BST = 11:30Z
"updatedFrom=2026-10-08T12:00:00Z": 1, // explicit UTC still honoured
"updatedFrom=2026-10-08T12:00:00%2B01:00": 2, // explicit offset still honoured
"updatedFrom=2026-10-08T12:00:00&updatedTo=2026-10-08T13:00:00": 1, // window in BST
}
for q, want := range cases {
if got := count(q); got != want {
t.Errorf("%s: got %d releases, want %d", q, got, want)
}
}
// In winter (GMT) London local time equals UTC.
winter := time.Date(2026, 12, 1, 9, 0, 0, 0, time.UTC)
if got, _ := parseTime("2026-12-01T09:00:00", london); !got.Equal(winter) {
t.Errorf("GMT: got %s, want %s", got, winter)
}
}
func openTestStore(t *testing.T) *Store {
t.Helper()
store, err := OpenStore(filepath.Join(t.TempDir(), "r.jsonl"))
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { store.Close() })
return store
}
func TestHandlerRejectsBadRequests(t *testing.T) {
h := NewHandler(openTestStore(t), "", nil)
rec := httptest.NewRecorder()
h(rec, httptest.NewRequest("POST", BasePath, nil))
if rec.Code != http.StatusMethodNotAllowed {
t.Errorf("POST: status %d, want 405", rec.Code)
}
cursor := func(s string) string { return base64.RawURLEncoding.EncodeToString([]byte(s)) }
cases := map[string]string{
"limit=0": "limit must be an integer between 1 and 100",
"limit=101": "limit must be an integer between 1 and 100",
"limit=ten": "limit must be an integer between 1 and 100",
"updatedFrom=yesterday": "updatedFrom must be in format yyyy-MM-ddTHH:mm:ss",
"updatedTo=2026-13-01": "updatedTo must be in format yyyy-MM-ddTHH:mm:ss",
"updatedFrom=2026-10-08T12:00:00&updatedTo=2026-10-08T11:00:00": "updatedTo must not be before updatedFrom",
"cursor=!!!": "invalid cursor",
"cursor=" + cursor("pos:1"): "invalid cursor",
"cursor=" + cursor("idx:one"): "invalid cursor",
"cursor=" + cursor("idx:-1"): "invalid cursor",
}
for q, want := range cases {
rec := httptest.NewRecorder()
h(rec, httptest.NewRequest("GET", BasePath+"?"+q, nil))
if rec.Code != http.StatusBadRequest {
t.Errorf("%s: status %d, want 400", q, rec.Code)
continue
}
if ct := rec.Header().Get("Content-Type"); ct != "application/json" {
t.Errorf("%s: Content-Type %q", q, ct)
}
var body map[string]string
if err := json.Unmarshal(rec.Body.Bytes(), &body); err != nil || body["error"] != want {
t.Errorf("%s: body %s, want error %q", q, rec.Body.String(), want)
}
}
}
func TestCursorRoundTrip(t *testing.T) {
for _, i := range []int{0, 1, 99, 123456} {
if got, err := decodeCursor(encodeCursor(i)); err != nil || got != i {
t.Errorf("decodeCursor(encodeCursor(%d)) = %d, %v", i, got, err)
}
}
}
func TestParseTimeFormats(t *testing.T) {
cases := map[string]time.Time{
"2026-10-08T12:00:00Z": time.Date(2026, 10, 8, 12, 0, 0, 0, time.UTC),
"2026-10-08T12:00:00+02:00": time.Date(2026, 10, 8, 10, 0, 0, 0, time.UTC),
"2026-10-08T12:00:00": time.Date(2026, 10, 8, 12, 0, 0, 0, time.UTC),
"2026-10-08": time.Date(2026, 10, 8, 0, 0, 0, 0, time.UTC),
}
for v, want := range cases {
got, err := parseTime(v, nil) // nil location means UTC
if err != nil || !got.Equal(want) {
t.Errorf("parseTime(%q) = %s, %v; want %s", v, got, err, want)
}
}
for _, v := range []string{"", "08/10/2026", "2026-10-08 12:00:00", "2026-10-08T25:00:00"} {
if _, err := parseTime(v, nil); err == nil {
t.Errorf("parseTime(%q): expected error", v)
}
}
}
// Following links.next walks every release exactly once, keeping the other
// query parameters, and the last page has no next link.
func TestHandlerPagination(t *testing.T) {
store := openTestStore(t)
base := time.Date(2026, 10, 8, 9, 0, 0, 0, time.UTC)
for i := 0; i < 5; i++ {
r := ocds.Release{
OCID: fmt.Sprintf("ocds-test-%d", i),
ID: fmt.Sprintf("%06d-2026", i+1),
Date: base.Add(time.Duration(i) * time.Minute),
Tag: []string{"tender"},
}
if err := store.Append(r); err != nil {
t.Fatal(err)
}
}
const baseURL = "http://mock.test"
h := NewHandler(store, baseURL, nil)
next := BasePath + "?limit=2&updatedFrom=2026-10-08T09:01:00"
var ids []string
for pages := 0; next != ""; pages++ {
if pages > 5 {
t.Fatal("too many pages")
}
rec := httptest.NewRecorder()
h(rec, httptest.NewRequest("GET", next, nil))
if rec.Code != http.StatusOK {
t.Fatalf("%s: status %d: %s", next, rec.Code, rec.Body.String())
}
var pkg ocds.ReleasePackage
if err := json.Unmarshal(rec.Body.Bytes(), &pkg); err != nil {
t.Fatal(err)
}
if pkg.URI != baseURL+next {
t.Errorf("uri = %q, want %q", pkg.URI, baseURL+next)
}
for _, r := range pkg.Releases {
ids = append(ids, r.ID)
}
next = ""
if pkg.Links != nil && pkg.Links.Next != "" {
if !strings.HasPrefix(pkg.Links.Next, baseURL+BasePath+"?") {
t.Fatalf("next = %q", pkg.Links.Next)
}
if !strings.Contains(pkg.Links.Next, "limit=2") ||
!strings.Contains(pkg.Links.Next, "updatedFrom=") {
t.Errorf("next lost query parameters: %q", pkg.Links.Next)
}
next = strings.TrimPrefix(pkg.Links.Next, baseURL)
}
}
want := "000002-2026,000003-2026,000004-2026,000005-2026"
if got := strings.Join(ids, ","); got != want {
t.Errorf("walked %s, want %s", got, want)
}
}