package calls import ( "bytes" "context" "errors" "testing" ) func TestShareReusesSealedInvitationAndPreservesLegacyLinks(t *testing.T) { s, _ := testStore(t) ctx := context.Background() m, original, err := s.createMeeting(ctx, "Share test", nil, "person:1") if err != nil { t.Fatal(err) } shared, err := s.shareLink(ctx, m.ID, "person:2") if err != nil || shared != original { t.Fatalf("share original: %v", err) } link, _, _, err := s.liveLink(ctx, shared) if err != nil || len(link.TokenSealed) == 0 || bytes.Contains(link.TokenSealed, []byte(shared)) { t.Fatal("invitation was not sealed") } // Model a migrated meeting: its original token cannot be recovered. if err := s.g.Model(&MeetingLink{}).Where("id = ?", link.ID).Update("token_sealed", nil).Error; err != nil { t.Fatal(err) } results := make(chan string, 8) failures := make(chan error, 8) for i := 0; i < 8; i++ { go func() { token, err := s.shareLink(ctx, m.ID, "person:1") results <- token failures <- err }() } replacement := "" for i := 0; i < 8; i++ { token := <-results if err := <-failures; err != nil { t.Fatal(err) } if replacement == "" { replacement = token } if token != replacement || token == original { t.Fatal("concurrent shares did not reuse one new invitation") } } for _, token := range []string{original, replacement} { if _, _, live, err := s.liveLink(ctx, token); err != nil || !live { t.Fatal("sharing invalidated a current invitation") } } var count int64 if err := s.g.Model(&MeetingLink{}).Where("meeting_id = ?", m.ID).Count(&count).Error; err != nil || count != 2 { t.Fatalf("links = %d, error %v", count, err) } fresh, err := s.replaceLink(ctx, m.ID, "person:1") if err != nil { t.Fatal(err) } for _, token := range []string{original, replacement} { if _, _, live, _ := s.liveLink(ctx, token); live { t.Fatal("replacement failed to revoke an invitation") } } shared, err = s.shareLink(ctx, m.ID, "person:1") if err != nil || shared != fresh { t.Fatal("share did not use the replacement") } if _, err := s.endMeeting(ctx, m.ID, "person:1"); err != nil { t.Fatal(err) } if _, err := s.shareLink(ctx, m.ID, "person:1"); !errors.Is(err, errMeetingEnded) { t.Fatalf("sharing ended meeting: %v", err) } } func TestShareRefusesCorruptedSealedInvitation(t *testing.T) { s, _ := testStore(t) ctx := context.Background() m, _, err := s.createMeeting(ctx, "Share test", nil, "person:1") if err != nil { t.Fatal(err) } if err := s.g.Model(&MeetingLink{}).Where("meeting_id = ?", m.ID).Update("token_sealed", []byte("broken")).Error; err != nil { t.Fatal(err) } if _, err := s.shareLink(ctx, m.ID, "person:1"); err == nil { t.Fatal("corrupt invitation was silently replaced") } }