package collaboration import ( "os" "path/filepath" "testing" "time" "github.com/limeleaf/diffdown/internal/db" "github.com/limeleaf/diffdown/internal/model" ) // openTestDB opens a temporary SQLite DB with migrations applied. func openTestDB(t *testing.T) *db.DB { t.Helper() tmp := t.TempDir() database, err := db.Open(filepath.Join(tmp, "test.db")) if err != nil { t.Fatalf("open test db: %v", err) } // Point migrations at the real migrations directory (relative to module root). db.SetMigrationsDir(filepath.Join("..", "..", "migrations")) if err := database.Migrate(); err != nil { t.Fatalf("migrate: %v", err) } t.Cleanup(func() { database.Close() os.RemoveAll(tmp) }) return database } // --- GenerateInviteToken --- func TestGenerateInviteToken_Length(t *testing.T) { token, err := GenerateInviteToken() if err != nil { t.Fatalf("unexpected error: %v", err) } // SHA-256 hex = 64 chars if len(token) != 64 { t.Errorf("expected token length 64, got %d", len(token)) } } func TestGenerateInviteToken_Unique(t *testing.T) { t1, err1 := GenerateInviteToken() t2, err2 := GenerateInviteToken() if err1 != nil || err2 != nil { t.Fatalf("unexpected errors: %v %v", err1, err2) } if t1 == t2 { t.Error("expected unique tokens, got duplicates") } } // --- CreateInvite --- func TestCreateInvite_ReturnsInvite(t *testing.T) { database := openTestDB(t) invite, err := CreateInvite(database, "rkey123", "did:plc:creator") if err != nil { t.Fatalf("CreateInvite: %v", err) } if invite.ID == "" { t.Error("expected non-empty ID") } if invite.Token == "" { t.Error("expected non-empty Token") } if invite.DocumentRKey != "rkey123" { t.Errorf("DocumentRKey: got %q, want %q", invite.DocumentRKey, "rkey123") } if invite.CreatedBy != "did:plc:creator" { t.Errorf("CreatedBy: got %q, want %q", invite.CreatedBy, "did:plc:creator") } } func TestCreateInvite_ExpiresInSevenDays(t *testing.T) { database := openTestDB(t) before := time.Now() invite, err := CreateInvite(database, "rkey123", "did:plc:creator") if err != nil { t.Fatalf("CreateInvite: %v", err) } after := time.Now() minExpiry := before.Add(7 * 24 * time.Hour) maxExpiry := after.Add(7 * 24 * time.Hour) if invite.ExpiresAt.Before(minExpiry) || invite.ExpiresAt.After(maxExpiry) { t.Errorf("ExpiresAt %v not in expected range [%v, %v]", invite.ExpiresAt, minExpiry, maxExpiry) } } func TestCreateInvite_PersistedInDB(t *testing.T) { database := openTestDB(t) invite, err := CreateInvite(database, "rkey123", "did:plc:creator") if err != nil { t.Fatalf("CreateInvite: %v", err) } fetched, err := database.GetInviteByToken(invite.Token) if err != nil { t.Fatalf("GetInviteByToken: %v", err) } if fetched.DocumentRKey != invite.DocumentRKey { t.Errorf("persisted DocumentRKey: got %q, want %q", fetched.DocumentRKey, invite.DocumentRKey) } } // --- ValidateInvite --- func TestValidateInvite_ValidToken(t *testing.T) { database := openTestDB(t) invite, err := CreateInvite(database, "rkey123", "did:plc:creator") if err != nil { t.Fatalf("CreateInvite: %v", err) } validated, err := ValidateInvite(database, invite.Token, "rkey123") if err != nil { t.Fatalf("ValidateInvite: %v", err) } if validated.Token != invite.Token { t.Errorf("got token %q, want %q", validated.Token, invite.Token) } } func TestValidateInvite_WrongRKey(t *testing.T) { database := openTestDB(t) invite, err := CreateInvite(database, "rkey123", "did:plc:creator") if err != nil { t.Fatalf("CreateInvite: %v", err) } _, err = ValidateInvite(database, invite.Token, "wrong-rkey") if err == nil { t.Error("expected error for mismatched rkey, got nil") } } func TestValidateInvite_UnknownToken(t *testing.T) { database := openTestDB(t) _, err := ValidateInvite(database, "nonexistent-token", "rkey123") if err == nil { t.Error("expected error for unknown token, got nil") } } func TestValidateInvite_ExpiredToken(t *testing.T) { database := openTestDB(t) // Insert an already-expired invite directly. expired := &model.Invite{ ID: db.NewID(), DocumentRKey: "rkey123", Token: "expiredtoken1234567890abcdef1234567890abcdef1234567890abcdef12", CreatedBy: "did:plc:creator", CreatedAt: time.Now().Add(-8 * 24 * time.Hour), ExpiresAt: time.Now().Add(-1 * time.Hour), } if err := database.CreateInvite(expired); err != nil { t.Fatalf("CreateInvite expired: %v", err) } _, err := ValidateInvite(database, expired.Token, "rkey123") if err == nil { t.Error("expected error for expired token, got nil") } }