package authgate import ( "context" "database/sql" "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) } } // 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{}