queryd (D5) needs the same HTTP client to catalogd that ingestd uses, but the client lived in internal/ingestd — having queryd import from ingestd would invert the data-flow direction (ingestd is upstream of queryd; the package dep should not point back). Extract to a shared internal/catalogclient/ package now, before D5 forces it under implementation pressure. Adds the List(ctx) method queryd will need for view registration. Unit tests cover Register success/conflict and List success/error paths against an httptest.Server fake. ingestd's import flips from internal/ingestd → internal/catalogclient; the wire format and behavior are unchanged. All four smokes (D1/D2/D3/ D4) PASS unchanged. DuckDB cgo path re-verified with the official github.com/duckdb/duckdb-go/v2 (per ADR-001) on Go 1.25 + arrow-go. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
91 lines
2.4 KiB
Go
91 lines
2.4 KiB
Go
package catalogclient
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.agentview.dev/profit/golangLAKEHOUSE/internal/catalogd"
|
|
)
|
|
|
|
func TestRegister_Success(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path != "/catalog/register" || r.Method != http.MethodPost {
|
|
http.Error(w, "wrong route", http.StatusNotFound)
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_ = json.NewEncoder(w).Encode(RegisterResponse{
|
|
Manifest: &catalogd.Manifest{Name: "x", DatasetID: "id-1"},
|
|
Existing: false,
|
|
})
|
|
}))
|
|
defer srv.Close()
|
|
|
|
c := New(srv.URL)
|
|
resp, err := c.Register(context.Background(), &RegisterRequest{
|
|
Name: "x",
|
|
SchemaFingerprint: "sha256:abc",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if resp.Manifest.DatasetID != "id-1" {
|
|
t.Errorf("dataset_id: got %s, want id-1", resp.Manifest.DatasetID)
|
|
}
|
|
}
|
|
|
|
func TestRegister_ConflictMapsToSentinel(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
http.Error(w, "fingerprint conflict", http.StatusConflict)
|
|
}))
|
|
defer srv.Close()
|
|
|
|
c := New(srv.URL)
|
|
_, err := c.Register(context.Background(), &RegisterRequest{
|
|
Name: "x",
|
|
SchemaFingerprint: "sha256:abc",
|
|
})
|
|
if !errors.Is(err, ErrFingerprintConflict) {
|
|
t.Fatalf("expected ErrFingerprintConflict, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestList_Success(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path != "/catalog/list" || r.Method != http.MethodGet {
|
|
http.Error(w, "wrong route", http.StatusNotFound)
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_, _ = w.Write([]byte(`{"manifests":[{"name":"a"},{"name":"b"}],"count":2}`))
|
|
}))
|
|
defer srv.Close()
|
|
|
|
c := New(srv.URL)
|
|
got, err := c.List(context.Background())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(got) != 2 || got[0].Name != "a" || got[1].Name != "b" {
|
|
t.Errorf("List: got %+v, want [a b]", got)
|
|
}
|
|
}
|
|
|
|
func TestList_Non200Errors(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
http.Error(w, "boom", http.StatusInternalServerError)
|
|
}))
|
|
defer srv.Close()
|
|
|
|
c := New(srv.URL)
|
|
_, err := c.List(context.Background())
|
|
if err == nil || !strings.Contains(err.Error(), "list status 500") {
|
|
t.Fatalf("expected status 500 error, got %v", err)
|
|
}
|
|
}
|