clean up old migration code. minor bug fixes with appview ui

This commit is contained in:
Evan Jarrett
2026-05-04 21:52:28 -05:00
parent 1ac8af74d5
commit b2d6842bb7
21 changed files with 445 additions and 385 deletions
+10
View File
@@ -30,6 +30,16 @@ type HoldAuthorizer interface {
// Called when user successfully becomes a crew member to ensure immediate access
// Returns nil if no denial cache exists or invalidation succeeds
ClearCrewDenial(ctx context.Context, holdDID, userDID string) error
// IsCachedCrewMember returns true only if there is a non-expired approval
// in the cache. It MUST NOT make any network calls. Cache miss returns (false, nil).
IsCachedCrewMember(ctx context.Context, holdDID, userDID string) (bool, error)
// RecordCrewApproval writes an approval to the cache with the implementation's
// standard TTL. Used to warm the cache after an out-of-band confirmation of crew
// membership (e.g. a successful requestCrew POST). No-op for implementations
// without a cache.
RecordCrewApproval(ctx context.Context, holdDID, userDID string) error
}
// CheckReadAccessWithCaptain implements the standard read authorization logic
+18
View File
@@ -401,6 +401,24 @@ func (a *RemoteHoldAuthorizer) isCrewMemberNoCache(ctx context.Context, holdDID,
return false, nil
}
// IsCachedCrewMember returns true if there is a non-expired approval row.
// Never makes network calls. Cache miss or no DB returns (false, nil).
func (a *RemoteHoldAuthorizer) IsCachedCrewMember(ctx context.Context, holdDID, userDID string) (bool, error) {
if a.db == nil {
return false, nil
}
return a.getCachedApproval(holdDID, userDID)
}
// RecordCrewApproval writes an approval to the cache with the standard 15-min TTL.
// No-op if there is no DB.
func (a *RemoteHoldAuthorizer) RecordCrewApproval(ctx context.Context, holdDID, userDID string) error {
if a.db == nil {
return nil
}
return a.cacheApproval(holdDID, userDID, 15*time.Minute)
}
// CheckReadAccess implements read authorization using shared logic
func (a *RemoteHoldAuthorizer) CheckReadAccess(ctx context.Context, holdDID, userDID string) (bool, error) {
captain, err := a.GetCaptainRecord(ctx, holdDID)
+126
View File
@@ -446,3 +446,129 @@ func TestClearAllDenials_OnStartup(t *testing.T) {
t.Error("Expected all denials to be cleared after ClearAllDenials")
}
}
func TestIsCachedCrewMember_Hit(t *testing.T) {
testDB := setupTestDB(t)
remote := NewRemoteHoldAuthorizer(testDB, false).(*RemoteHoldAuthorizer)
defer close(remote.stopCleanup)
holdDID := "did:web:hold01.atcr.io"
userDID := "did:plc:user123"
if err := remote.cacheApproval(holdDID, userDID, 15*time.Minute); err != nil {
t.Fatalf("cacheApproval failed: %v", err)
}
cached, err := remote.IsCachedCrewMember(context.Background(), holdDID, userDID)
if err != nil {
t.Fatalf("IsCachedCrewMember returned error: %v", err)
}
if !cached {
t.Error("Expected cache hit, got miss")
}
}
func TestIsCachedCrewMember_Miss(t *testing.T) {
testDB := setupTestDB(t)
remote := NewRemoteHoldAuthorizer(testDB, false).(*RemoteHoldAuthorizer)
defer close(remote.stopCleanup)
cached, err := remote.IsCachedCrewMember(context.Background(),
"did:web:hold01.atcr.io", "did:plc:nobody")
if err != nil {
t.Fatalf("IsCachedCrewMember returned error: %v", err)
}
if cached {
t.Error("Expected cache miss, got hit")
}
}
func TestIsCachedCrewMember_Expired(t *testing.T) {
testDB := setupTestDB(t)
remote := NewRemoteHoldAuthorizer(testDB, false).(*RemoteHoldAuthorizer)
defer close(remote.stopCleanup)
holdDID := "did:web:hold01.atcr.io"
userDID := "did:plc:user123"
// Insert a row that already expired one minute ago.
now := time.Now()
_, err := testDB.Exec(`
INSERT INTO hold_crew_approvals (hold_did, user_did, approved_at, expires_at)
VALUES (?, ?, ?, ?)
`, holdDID, userDID, now.Add(-2*time.Minute), now.Add(-1*time.Minute))
if err != nil {
t.Fatalf("seed insert failed: %v", err)
}
cached, err := remote.IsCachedCrewMember(context.Background(), holdDID, userDID)
if err != nil {
t.Fatalf("IsCachedCrewMember returned error: %v", err)
}
if cached {
t.Error("Expected expired entry to be treated as miss")
}
// Expired entry should have been cleaned up by getCachedApproval.
var count int
if err := testDB.QueryRow(`
SELECT COUNT(*) FROM hold_crew_approvals WHERE hold_did = ? AND user_did = ?
`, holdDID, userDID).Scan(&count); err != nil {
t.Fatalf("count query failed: %v", err)
}
if count != 0 {
t.Errorf("Expected expired row to be deleted, found %d", count)
}
}
func TestRecordCrewApproval_WritesAndReadsBack(t *testing.T) {
testDB := setupTestDB(t)
remote := NewRemoteHoldAuthorizer(testDB, false).(*RemoteHoldAuthorizer)
defer close(remote.stopCleanup)
holdDID := "did:web:hold01.atcr.io"
userDID := "did:plc:user123"
if err := remote.RecordCrewApproval(context.Background(), holdDID, userDID); err != nil {
t.Fatalf("RecordCrewApproval failed: %v", err)
}
cached, err := remote.IsCachedCrewMember(context.Background(), holdDID, userDID)
if err != nil {
t.Fatalf("IsCachedCrewMember returned error: %v", err)
}
if !cached {
t.Error("Expected RecordCrewApproval to populate the cache")
}
// Verify TTL is roughly 15 minutes from now.
var expiresAt time.Time
if err := testDB.QueryRow(`
SELECT expires_at FROM hold_crew_approvals WHERE hold_did = ? AND user_did = ?
`, holdDID, userDID).Scan(&expiresAt); err != nil {
t.Fatalf("expires_at query failed: %v", err)
}
ttl := time.Until(expiresAt)
if ttl < 14*time.Minute || ttl > 16*time.Minute {
t.Errorf("Expected TTL ~15min, got %v", ttl)
}
}
func TestIsCachedCrewMember_NoDB(t *testing.T) {
remote := NewRemoteHoldAuthorizer(nil, false).(*RemoteHoldAuthorizer)
defer close(remote.stopCleanup)
cached, err := remote.IsCachedCrewMember(context.Background(),
"did:web:hold01.atcr.io", "did:plc:user123")
if err != nil {
t.Errorf("Expected nil error with nil DB, got %v", err)
}
if cached {
t.Error("Expected false with nil DB")
}
if err := remote.RecordCrewApproval(context.Background(),
"did:web:hold01.atcr.io", "did:plc:user123"); err != nil {
t.Errorf("Expected nil error from RecordCrewApproval with nil DB, got %v", err)
}
}
+11
View File
@@ -102,3 +102,14 @@ func (a *Authorizer) CheckWriteAccess(ctx context.Context, holdDID, userDID stri
func (a *Authorizer) ClearCrewDenial(ctx context.Context, holdDID, userDID string) error {
return nil
}
// IsCachedCrewMember always returns (false, nil) for the local authorizer.
// There is no cache; callers fall through to the direct PDS lookup.
func (a *Authorizer) IsCachedCrewMember(ctx context.Context, holdDID, userDID string) (bool, error) {
return false, nil
}
// RecordCrewApproval is a no-op for the local authorizer (no cache to warm).
func (a *Authorizer) RecordCrewApproval(ctx context.Context, holdDID, userDID string) error {
return nil
}