Files

130 lines
4.6 KiB
Go

package authgate
import (
"context"
"database/sql"
"net/http"
"net/http/httptest"
"strings"
"testing"
"atcr.io/pkg/appview/db"
"atcr.io/pkg/atproto"
"atcr.io/pkg/auth"
)
// newTestDB returns an in-memory libsql DB with the full appview schema
// applied. Tears down on test completion.
func newTestDB(t *testing.T) *sql.DB {
t.Helper()
testDB, err := db.InitDB(":memory:", db.LibsqlConfig{})
if err != nil {
t.Fatalf("InitDB: %v", err)
}
t.Cleanup(func() { _ = testDB.Close() })
return testDB
}
// seedUser upserts a users row and optionally sets default_hold_did.
// Pass "" for defaultHold to leave it NULL.
func seedUser(t *testing.T, d *sql.DB, did, handle, defaultHold string) {
t.Helper()
if err := db.UpsertUser(d, &db.User{DID: did, Handle: handle, PDSEndpoint: "https://pds.example/" + did}); err != nil {
t.Fatalf("UpsertUser(%s): %v", did, err)
}
if defaultHold != "" {
if err := db.UpdateUserDefaultHold(d, did, defaultHold); err != nil {
t.Fatalf("UpdateUserDefaultHold(%s, %s): %v", did, defaultHold, err)
}
}
}
// seedCaptain inserts a single hold_captain_records row.
func seedCaptain(t *testing.T, d *sql.DB, holdDID, ownerDID string) {
t.Helper()
if err := db.BatchUpsertCaptainRecords(d, []db.HoldCaptainRecord{
{HoldDID: holdDID, OwnerDID: ownerDID, Public: false, AllowAllCrew: false},
}); err != nil {
t.Fatalf("BatchUpsertCaptainRecords(%s, %s): %v", holdDID, ownerDID, err)
}
}
// seedCrewMember inserts a single hold_crew_members row with the given
// permissions JSON (pass "" to leave permissions NULL — note that the
// underlying schema may coerce empty strings; pass `"[]"` for an empty
// permissions array).
func seedCrewMember(t *testing.T, d *sql.DB, holdDID, memberDID, permsJSON string) {
t.Helper()
if err := db.BatchUpsertCrewMembers(d, []db.CrewMember{
{HoldDID: holdDID, MemberDID: memberDID, Rkey: "rkey-" + memberDID, Role: "crew", Permissions: permsJSON},
}); err != nil {
t.Fatalf("BatchUpsertCrewMembers(%s, %s): %v", holdDID, memberDID, err)
}
}
// quotaServerResult captures HTTP traffic the server saw, for assertions.
type quotaServerResult struct {
server *httptest.Server
holdDID string // did:web:127.0.0.1%3APORT form
hits int
lastURL string
}
// quotaServer spins up an httptest.Server that responds to every request
// with the given status + body, records hit count + last URL, and returns
// both the server URL and the did:web:HOST form that resolves to it under
// atproto.SetTestMode(true).
func quotaServer(t *testing.T, status int, body string) *quotaServerResult {
t.Helper()
res := &quotaServerResult{}
res.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
res.hits++
res.lastURL = r.URL.String()
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
_, _ = w.Write([]byte(body))
}))
t.Cleanup(res.server.Close)
// httptest.Server.URL has the form "http://127.0.0.1:PORT". The
// did:web equivalent percent-encodes the colon; didWebToURL reverses
// the encoding and re-derives the http://host:port form, which is
// what we need for atproto.SetTestMode(true) to route requests.
host := strings.TrimPrefix(res.server.URL, "http://")
res.holdDID = "did:web:" + strings.Replace(host, ":", "%3A", 1)
return res
}
// httpClient returns the server's client, which trusts its TLS cert (n/a
// here since httptest.NewServer is HTTP) and routes to the loopback.
func (r *quotaServerResult) httpClient() *http.Client {
return r.server.Client()
}
// fakeHoldAuthorizer is a no-op auth.HoldAuthorizer stub. The Authorize
// orchestration tests don't exercise the reconciliation closure (the
// closure is nil for our purposes because we don't supply a refresher
// and don't go through ResolveIdentity), so we don't need atomic
// counters — just zero-value returns.
type fakeHoldAuthorizer struct{}
func (fakeHoldAuthorizer) CheckReadAccess(_ context.Context, _, _ string) (bool, error) {
return true, nil
}
func (fakeHoldAuthorizer) CheckWriteAccess(_ context.Context, _, _ string) (bool, error) {
return true, nil
}
func (fakeHoldAuthorizer) GetCaptainRecord(_ context.Context, _ string) (*atproto.CaptainRecord, error) {
return nil, nil
}
func (fakeHoldAuthorizer) IsCrewMember(_ context.Context, _, _ string) (bool, error) {
return false, nil
}
func (fakeHoldAuthorizer) ClearCrewDenial(_ context.Context, _, _ string) error { return nil }
func (fakeHoldAuthorizer) IsCachedCrewMember(_ context.Context, _, _ string) (bool, error) {
return false, nil
}
func (fakeHoldAuthorizer) RecordCrewApproval(_ context.Context, _, _ string) error { return nil }
var _ auth.HoldAuthorizer = fakeHoldAuthorizer{}