mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-08-29 04:06:58 +00:00
130 lines
4.6 KiB
Go
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 := "aServerResult{}
|
|
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{}
|