Diffdown is a real-time collaborative Markdown editor/previewer built on the AT Protocol diffdown.com
at main 171 lines 4.6 kB view raw
1package collaboration 2 3import ( 4 "os" 5 "path/filepath" 6 "testing" 7 "time" 8 9 "github.com/limeleaf/diffdown/internal/db" 10 "github.com/limeleaf/diffdown/internal/model" 11) 12 13// openTestDB opens a temporary SQLite DB with migrations applied. 14func openTestDB(t *testing.T) *db.DB { 15 t.Helper() 16 tmp := t.TempDir() 17 database, err := db.Open(filepath.Join(tmp, "test.db")) 18 if err != nil { 19 t.Fatalf("open test db: %v", err) 20 } 21 // Point migrations at the real migrations directory (relative to module root). 22 db.SetMigrationsDir(filepath.Join("..", "..", "migrations")) 23 if err := database.Migrate(); err != nil { 24 t.Fatalf("migrate: %v", err) 25 } 26 t.Cleanup(func() { 27 database.Close() 28 os.RemoveAll(tmp) 29 }) 30 return database 31} 32 33// --- GenerateInviteToken --- 34 35func TestGenerateInviteToken_Length(t *testing.T) { 36 token, err := GenerateInviteToken() 37 if err != nil { 38 t.Fatalf("unexpected error: %v", err) 39 } 40 // SHA-256 hex = 64 chars 41 if len(token) != 64 { 42 t.Errorf("expected token length 64, got %d", len(token)) 43 } 44} 45 46func TestGenerateInviteToken_Unique(t *testing.T) { 47 t1, err1 := GenerateInviteToken() 48 t2, err2 := GenerateInviteToken() 49 if err1 != nil || err2 != nil { 50 t.Fatalf("unexpected errors: %v %v", err1, err2) 51 } 52 if t1 == t2 { 53 t.Error("expected unique tokens, got duplicates") 54 } 55} 56 57// --- CreateInvite --- 58 59func TestCreateInvite_ReturnsInvite(t *testing.T) { 60 database := openTestDB(t) 61 invite, err := CreateInvite(database, "rkey123", "did:plc:creator") 62 if err != nil { 63 t.Fatalf("CreateInvite: %v", err) 64 } 65 if invite.ID == "" { 66 t.Error("expected non-empty ID") 67 } 68 if invite.Token == "" { 69 t.Error("expected non-empty Token") 70 } 71 if invite.DocumentRKey != "rkey123" { 72 t.Errorf("DocumentRKey: got %q, want %q", invite.DocumentRKey, "rkey123") 73 } 74 if invite.CreatedBy != "did:plc:creator" { 75 t.Errorf("CreatedBy: got %q, want %q", invite.CreatedBy, "did:plc:creator") 76 } 77} 78 79func TestCreateInvite_ExpiresInSevenDays(t *testing.T) { 80 database := openTestDB(t) 81 before := time.Now() 82 invite, err := CreateInvite(database, "rkey123", "did:plc:creator") 83 if err != nil { 84 t.Fatalf("CreateInvite: %v", err) 85 } 86 after := time.Now() 87 88 minExpiry := before.Add(7 * 24 * time.Hour) 89 maxExpiry := after.Add(7 * 24 * time.Hour) 90 91 if invite.ExpiresAt.Before(minExpiry) || invite.ExpiresAt.After(maxExpiry) { 92 t.Errorf("ExpiresAt %v not in expected range [%v, %v]", invite.ExpiresAt, minExpiry, maxExpiry) 93 } 94} 95 96func TestCreateInvite_PersistedInDB(t *testing.T) { 97 database := openTestDB(t) 98 invite, err := CreateInvite(database, "rkey123", "did:plc:creator") 99 if err != nil { 100 t.Fatalf("CreateInvite: %v", err) 101 } 102 103 fetched, err := database.GetInviteByToken(invite.Token) 104 if err != nil { 105 t.Fatalf("GetInviteByToken: %v", err) 106 } 107 if fetched.DocumentRKey != invite.DocumentRKey { 108 t.Errorf("persisted DocumentRKey: got %q, want %q", fetched.DocumentRKey, invite.DocumentRKey) 109 } 110} 111 112// --- ValidateInvite --- 113 114func TestValidateInvite_ValidToken(t *testing.T) { 115 database := openTestDB(t) 116 invite, err := CreateInvite(database, "rkey123", "did:plc:creator") 117 if err != nil { 118 t.Fatalf("CreateInvite: %v", err) 119 } 120 121 validated, err := ValidateInvite(database, invite.Token, "rkey123") 122 if err != nil { 123 t.Fatalf("ValidateInvite: %v", err) 124 } 125 if validated.Token != invite.Token { 126 t.Errorf("got token %q, want %q", validated.Token, invite.Token) 127 } 128} 129 130func TestValidateInvite_WrongRKey(t *testing.T) { 131 database := openTestDB(t) 132 invite, err := CreateInvite(database, "rkey123", "did:plc:creator") 133 if err != nil { 134 t.Fatalf("CreateInvite: %v", err) 135 } 136 137 _, err = ValidateInvite(database, invite.Token, "wrong-rkey") 138 if err == nil { 139 t.Error("expected error for mismatched rkey, got nil") 140 } 141} 142 143func TestValidateInvite_UnknownToken(t *testing.T) { 144 database := openTestDB(t) 145 _, err := ValidateInvite(database, "nonexistent-token", "rkey123") 146 if err == nil { 147 t.Error("expected error for unknown token, got nil") 148 } 149} 150 151func TestValidateInvite_ExpiredToken(t *testing.T) { 152 database := openTestDB(t) 153 154 // Insert an already-expired invite directly. 155 expired := &model.Invite{ 156 ID: db.NewID(), 157 DocumentRKey: "rkey123", 158 Token: "expiredtoken1234567890abcdef1234567890abcdef1234567890abcdef12", 159 CreatedBy: "did:plc:creator", 160 CreatedAt: time.Now().Add(-8 * 24 * time.Hour), 161 ExpiresAt: time.Now().Add(-1 * time.Hour), 162 } 163 if err := database.CreateInvite(expired); err != nil { 164 t.Fatalf("CreateInvite expired: %v", err) 165 } 166 167 _, err := ValidateInvite(database, expired.Token, "rkey123") 168 if err == nil { 169 t.Error("expected error for expired token, got nil") 170 } 171}