github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/teams/list_test.go (about)

     1  package teams
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/keybase/client/go/protocol/keybase1"
     9  	"github.com/stretchr/testify/require"
    10  )
    11  
    12  func newSocialInviteMD(typ keybase1.TeamInviteType, status keybase1.TeamInviteMetadataStatus) validityInput {
    13  	return validityInput{
    14  		md: keybase1.TeamInviteMetadata{
    15  			Invite: keybase1.TeamInvite{
    16  				Type: typ,
    17  			},
    18  			Status: status,
    19  		},
    20  	}
    21  }
    22  
    23  // maxUses = nil -> infinite; otherwise finite
    24  func newInvitelinkMD(etime *time.Time, maxUses *int, usedTimes []time.Time, cancelled *time.Time) validityInput {
    25  	userLog := make(map[keybase1.UserVersion][]keybase1.UserLogPoint)
    26  	var usedInvites []keybase1.TeamUsedInviteLogPoint
    27  	for idx, usedTime := range usedTimes {
    28  		uv := keybase1.UserVersion{
    29  			Uid:         keybase1.UID(fmt.Sprintf("fakeuid%d", idx)),
    30  			EldestSeqno: 3,
    31  		}
    32  		// Joined first via some other way
    33  		userLog[uv] = append(userLog[uv], keybase1.UserLogPoint{
    34  			SigMeta: keybase1.SignatureMetadata{
    35  				Time: keybase1.ToTime(usedTime),
    36  			},
    37  		})
    38  		// Then was added by invitelink
    39  		userLog[uv] = append(userLog[uv], keybase1.UserLogPoint{
    40  			SigMeta: keybase1.SignatureMetadata{
    41  				Time: keybase1.ToTime(usedTime),
    42  			},
    43  		})
    44  		usedInvites = append(usedInvites, keybase1.TeamUsedInviteLogPoint{
    45  			Uv:       uv,
    46  			LogPoint: 1,
    47  		})
    48  	}
    49  	invite := keybase1.TeamInvite{
    50  		Type: keybase1.NewTeamInviteTypeDefault(keybase1.TeamInviteCategory_INVITELINK),
    51  	}
    52  	if etime != nil {
    53  		tmp := keybase1.ToUnixTime(*etime)
    54  		invite.Etime = &tmp
    55  	}
    56  	var m keybase1.TeamInviteMaxUses
    57  	if maxUses == nil {
    58  		m = keybase1.TeamMaxUsesInfinite
    59  	} else {
    60  		m, _ = keybase1.NewTeamInviteFiniteUses(*maxUses)
    61  	}
    62  	invite.MaxUses = &m
    63  	status := keybase1.NewTeamInviteMetadataStatusWithActive()
    64  	if cancelled != nil {
    65  		status = keybase1.NewTeamInviteMetadataStatusWithCancelled(keybase1.TeamInviteMetadataCancel{
    66  			TeamSigMeta: keybase1.TeamSignatureMetadata{
    67  				SigMeta: keybase1.SignatureMetadata{
    68  					Time: keybase1.ToTime(*cancelled),
    69  				},
    70  			},
    71  		})
    72  	}
    73  	md := keybase1.TeamInviteMetadata{
    74  		Invite:      invite,
    75  		Status:      status,
    76  		UsedInvites: usedInvites,
    77  	}
    78  	return validityInput{md, userLog}
    79  }
    80  
    81  type validityInput struct {
    82  	md      keybase1.TeamInviteMetadata
    83  	userLog map[keybase1.UserVersion][]keybase1.UserLogPoint
    84  }
    85  
    86  // Tests go/protocol/keybase1/extras.go:TeamInviteMetadata.ComputeValidity.
    87  // In this test we stub out fields of invites that are not needed/used by ComputeValidity, for
    88  // brevity.
    89  func TestComputeValidity(t *testing.T) {
    90  	now := time.Date(2020, time.January, 10, 14, 0, 0, 0, time.UTC)
    91  	hour := time.Hour
    92  	day := 24 * hour
    93  	week := 7 * day
    94  	month := 4 * week
    95  	year := 12 * month
    96  
    97  	intp := func(n int) *int {
    98  		return &n
    99  	}
   100  	timep := func(t time.Time) *time.Time {
   101  		return &t
   102  	}
   103  
   104  	var tests = []struct {
   105  		desc string
   106  
   107  		now time.Time
   108  		i   validityInput
   109  
   110  		expectedIsValid             bool
   111  		expectedValidityDescription string
   112  	}{
   113  		{
   114  			"active twitter",
   115  			now,
   116  			newSocialInviteMD(
   117  				keybase1.NewTeamInviteTypeWithSbs(keybase1.TeamInviteSocialNetwork("twitter")),
   118  				keybase1.NewTeamInviteMetadataStatusWithActive(),
   119  			),
   120  			true,
   121  			"Expires after 1 use",
   122  		},
   123  		{
   124  			"cancelled twitter",
   125  			now,
   126  			newSocialInviteMD(
   127  				keybase1.NewTeamInviteTypeWithSbs(keybase1.TeamInviteSocialNetwork("twitter")),
   128  				keybase1.NewTeamInviteMetadataStatusWithCancelled(keybase1.TeamInviteMetadataCancel{
   129  					TeamSigMeta: keybase1.TeamSignatureMetadata{
   130  						SigMeta: keybase1.SignatureMetadata{
   131  							Time: keybase1.ToTime(now.Add(-2 * week)),
   132  						},
   133  					},
   134  				}),
   135  			),
   136  			false,
   137  			"Cancelled 2 weeks ago",
   138  		},
   139  		{
   140  			"completed twitter",
   141  			now,
   142  			newSocialInviteMD(
   143  				keybase1.NewTeamInviteTypeWithSbs(keybase1.TeamInviteSocialNetwork("twitter")),
   144  				keybase1.NewTeamInviteMetadataStatusWithCompleted(keybase1.TeamInviteMetadataCompleted{
   145  					TeamSigMeta: keybase1.TeamSignatureMetadata{
   146  						SigMeta: keybase1.SignatureMetadata{
   147  							Time: keybase1.ToTime(now.Add(-month)),
   148  						},
   149  					},
   150  				}),
   151  			),
   152  			false,
   153  			"Completed 4 weeks ago",
   154  		},
   155  		{
   156  			"immortal invitelink",
   157  			now,
   158  			newInvitelinkMD(nil, nil, []time.Time{now.Add(-month), now.Add(-week), now, now.Add(day)}, nil),
   159  			true,
   160  			"Does not expire",
   161  		},
   162  		{
   163  			"cancelled immortal invitelink",
   164  			now,
   165  			newInvitelinkMD(nil, nil, []time.Time{now.Add(-month), now.Add(-week), now.Add(-3 * day), now.Add(-day)}, timep(now.Add(-hour))),
   166  			false,
   167  			"Cancelled 1 hour ago",
   168  		},
   169  		{
   170  			"expiring invitelink",
   171  			now,
   172  			newInvitelinkMD(timep(now.Add(12*day)), nil, []time.Time{}, nil),
   173  			true,
   174  			"Expires in 1 week",
   175  		},
   176  		{
   177  			"expiring limited invitelink",
   178  			now,
   179  			newInvitelinkMD(timep(now.Add(1*year+6*month)), intp(2), []time.Time{}, nil),
   180  			true,
   181  			"Expires in 1 year or after 2 uses",
   182  		},
   183  		{
   184  			"limited invitelink",
   185  			now,
   186  			newInvitelinkMD(nil, intp(123), []time.Time{}, nil),
   187  			true,
   188  			"Expires after 123 uses",
   189  		},
   190  		{
   191  			"expired invitelink, no uses",
   192  			now,
   193  			newInvitelinkMD(timep(now.Add(-16*day)), nil, []time.Time{}, nil),
   194  			false,
   195  			"Expired 2 weeks ago",
   196  		},
   197  		{
   198  			"expired invitelink, some uses",
   199  			now,
   200  			newInvitelinkMD(timep(now.Add(-16*day)), nil, []time.Time{now.Add(-month), now.Add(-year)}, nil),
   201  			false,
   202  			"Expired 2 weeks ago",
   203  		},
   204  		{
   205  			"expired invitelink, no uses, then cancelled",
   206  			now,
   207  			newInvitelinkMD(timep(now.Add(-16*day)), nil, []time.Time{}, timep(now.Add(-2*day))),
   208  			false,
   209  			"Cancelled 2 days ago",
   210  		},
   211  		{
   212  			"usedup invitelink one use",
   213  			now,
   214  			newInvitelinkMD(nil, intp(1), []time.Time{now.Add(-month)}, nil),
   215  			false,
   216  			"Expired 4 weeks ago",
   217  		},
   218  		{
   219  			"usedup invitelink multiuse",
   220  			now,
   221  			newInvitelinkMD(nil, intp(3), []time.Time{now.Add(-3 * month), now.Add(-2 * month), now.Add(-3 * day)}, nil),
   222  			false,
   223  			"Expired 3 days ago",
   224  		},
   225  		{
   226  			"usedup invitelink multiuse then cancelled",
   227  			now,
   228  			newInvitelinkMD(nil, intp(3), []time.Time{now.Add(-3 * month), now.Add(-2 * month), now.Add(-3 * day)}, timep(now.Add(-2*hour))),
   229  			false,
   230  			"Cancelled 2 hours ago",
   231  		},
   232  		{
   233  			"expiring partially used invitelink multiuse",
   234  			now,
   235  			newInvitelinkMD(timep(now.Add(3*day)), intp(3), []time.Time{now.Add(-3 * month), now.Add(-3 * day)}, nil),
   236  			true,
   237  			"Expires in 3 days or after 1 use",
   238  		},
   239  		{
   240  			"partially used invitelink multiuse",
   241  			now,
   242  			newInvitelinkMD(nil, intp(3), []time.Time{now.Add(-3 * month), now.Add(-3 * day)}, nil),
   243  			true,
   244  			"Expires after 1 use",
   245  		},
   246  		{
   247  			"expired and usedup invitelink, but the usedup happened first",
   248  			now,
   249  			newInvitelinkMD(timep(now.Add(-5*time.Minute)), intp(2), []time.Time{now.Add(-3 * month), now.Add(-1 * hour)}, nil),
   250  			false,
   251  			// i.e.; it was invalidated because it was used up, not because it later expired
   252  			"Expired 1 hour ago",
   253  		},
   254  	}
   255  	for _, tt := range tests {
   256  		tt := tt
   257  		t.Run(tt.desc, func(t *testing.T) {
   258  			gotIsValid, gotValidityDescription := tt.i.md.ComputeValidity(tt.now, tt.i.userLog)
   259  			require.Equal(t, tt.expectedIsValid, gotIsValid)
   260  			require.Equal(t, tt.expectedValidityDescription, gotValidityDescription)
   261  		})
   262  	}
   263  }