<commit_before>package systests

import (
	"fmt"
	"strings"
	"testing"
	"time"

	"golang.org/x/net/context"

	client "github.com/keybase/client/go/client"
	libkb "github.com/keybase/client/go/libkb"
	chat1 "github.com/keybase/client/go/protocol/chat1"
	keybase1 "github.com/keybase/client/go/protocol/keybase1"
	teams "github.com/keybase/client/go/teams"
	"github.com/stretchr/testify/require"
)

func sendChat(t smuTeam, u *smuUser, msg string) {
	tctx := u.primaryDevice().popClone()
	runner := client.NewCmdChatSendRunner(tctx.G)
	runner.SetTeamChatForTest(t.name)
	runner.SetMessage(msg)
	err := runner.Run()
	if err != nil {
		u.ctx.t.Fatal(err)
	}
}

func divDebug(ctx *smuContext, fmt string, arg ...interface{}) {
	div := "------------"
	ctx.log.Debug(div+" "+fmt+" "+div, arg...)
}

func readChatsWithError(team smuTeam, u *smuUser) (messages []chat1.MessageUnboxed, err error) {
	return readChatsWithErrorAndDevice(team, u, u.primaryDevice())
}

func readChatsWithErrorAndDevice(team smuTeam, u *smuUser, dev *smuDeviceWrapper) (messages []chat1.MessageUnboxed, err error) {
	tctx := dev.popClone()

	wait := 100 * time.Millisecond
	var totalWait time.Duration
	for i := 0; i < 10; i++ {
		runner := client.NewCmdChatReadRunner(tctx.G)
		runner.SetTeamChatForTest(team.name)
		_, messages, err = runner.Fetch()

		if err == nil {
			if i != 0 {
				u.ctx.t.Logf("readChatsWithError success after retrying %d times, polling for %s", i, totalWait)
			}
			return messages, nil
		}

		if !strings.Contains(err.Error(), "KBFS client not found") {
			// Only retry on KBFS errors
			return messages, err
		}

		time.Sleep(wait)
		totalWait += wait
	}

	u.ctx.t.Logf("Failed to readChatsWithError after polling for %s", totalWait)
	return messages, err
}

func readChats(team smuTeam, u *smuUser, nMessages int) {
	readChatsWithDevice(team, u, u.primaryDevice(), nMessages)
}

func readChatsWithDevice(team smuTeam, u *smuUser, dev *smuDeviceWrapper, nMessages int) {
	messages, err := readChatsWithErrorAndDevice(team, u, dev)
	t := u.ctx.t
	if err != nil {
		u.ctx.t.Fatal(err)
	}
	require.Equal(t, nMessages, len(messages))
	for i, msg := range messages {
		require.Equal(t, msg.Valid().MessageBody.Text().Body, fmt.Sprintf("%d", len(messages)-i-1))
	}
	divDebug(u.ctx, "readChat success for %s", u.username)
}

func pollForMembershipUpdate(team smuTeam, ann *smuUser, bob *smuUser, cam *smuUser) {

	details := ann.pollForMembershipUpdate(team, keybase1.PerTeamKeyGeneration(2))
	for _, member := range details.Members.Admins {
		switch member.Username {
		case ann.username:
			require.True(ann.ctx.t, member.Active)
		default:
			ann.ctx.t.Fatalf("unknown admin: %s", member.Username)
		}
	}
	for _, member := range details.Members.Writers {
		switch member.Username {
		case bob.username:
			require.False(ann.ctx.t, member.Active)
		case cam.username:
			require.True(ann.ctx.t, member.Active)
		default:
			ann.ctx.t.Fatalf("unknown writer: %s (%+v)", member.Username, details)
		}
	}
	ann.ctx.log.Debug("team details checked out: %+v", details)
}

func TestTeamDelete(t *testing.T) {
	ctx := newSMUContext(t)
	defer ctx.cleanup()

	ann := ctx.installKeybaseForUser("ann", 10)
	ann.signup()
	divDebug(ctx, "Signed up ann (%s)", ann.username)
	bob := ctx.installKeybaseForUser("bob", 10)
	bob.signup()
	divDebug(ctx, "Signed up bob (%s)", bob.username)
	cam := ctx.installKeybaseForUser("cam", 10)
	cam.signup()
	divDebug(ctx, "Signed up cam (%s)", cam.username)

	team := ann.createTeam([]*smuUser{bob, cam})
	divDebug(ctx, "team created (%s)", team.name)

	sendChat(team, ann, "0")
	divDebug(ctx, "Sent chat '0' (%s via %s)", team.name, ann.username)

	readChats(team, ann, 1)
	readChats(team, bob, 1)
	divDebug(ctx, "Ann and bob can read")

	ann.delete()
	divDebug(ctx, "Ann deleted her account")

	// It's important for cam to clear her cache right before the attempt to send,
	// since she might have received gregors that ann deleted her account,
	// and thefore might be trying to refresh and load the team.
	cam.primaryDevice().clearUPAKCache()
	sendChat(team, cam, "1")

	divDebug(ctx, "Cam sent a chat")
	readChats(team, bob, 2)
}

func TestTeamReset(t *testing.T) {
	t.Skip()
	ctx := newSMUContext(t)
	defer ctx.cleanup()

	ann := ctx.installKeybaseForUser("ann", 10)
	ann.signup()
	divDebug(ctx, "Signed up ann (%s)", ann.username)
	bob := ctx.installKeybaseForUser("bob", 10)
	bob.signup()
	divDebug(ctx, "Signed up bob (%s)", bob.username)
	cam := ctx.installKeybaseForUser("cam", 10)
	cam.signup()
	divDebug(ctx, "Signed up cam (%s)", cam.username)

	team := ann.createTeam([]*smuUser{bob, cam})
	divDebug(ctx, "team created (%s)", team.name)

	sendChat(team, ann, "0")
	divDebug(ctx, "Sent chat '0' (%s via %s)", team.name, ann.username)

	readChats(team, ann, 1)<commit_msg>retry if we dont get desired messages count (#8582)<commit_after>package systests

import (
	"fmt"
	"strings"
	"testing"
	"time"

	"golang.org/x/net/context"

	client "github.com/keybase/client/go/client"
	libkb "github.com/keybase/client/go/libkb"
	chat1 "github.com/keybase/client/go/protocol/chat1"
	keybase1 "github.com/keybase/client/go/protocol/keybase1"
	teams "github.com/keybase/client/go/teams"
	"github.com/stretchr/testify/require"
)

func sendChat(t smuTeam, u *smuUser, msg string) {
	tctx := u.primaryDevice().popClone()
	runner := client.NewCmdChatSendRunner(tctx.G)
	runner.SetTeamChatForTest(t.name)
	runner.SetMessage(msg)
	err := runner.Run()
	if err != nil {
		u.ctx.t.Fatal(err)
	}
}

func divDebug(ctx *smuContext, fmt string, arg ...interface{}) {
	div := "------------"
	ctx.log.Debug(div+" "+fmt+" "+div, arg...)
}

func readChatsWithError(team smuTeam, u *smuUser) (messages []chat1.MessageUnboxed, err error) {
	return readChatsWithErrorAndDevice(team, u, u.primaryDevice(), 0)
}

func readChatsWithErrorAndDevice(team smuTeam, u *smuUser, dev *smuDeviceWrapper, nMessages int) (messages []chat1.MessageUnboxed, err error) {
	tctx := dev.popClone()

	wait := time.Second
	var totalWait time.Duration
	for i := 0; i < 10; i++ {
		runner := client.NewCmdChatReadRunner(tctx.G)
		runner.SetTeamChatForTest(team.name)
		_, messages, err = runner.Fetch()

		if err == nil && len(messages) == nMessages {
			if i != 0 {
				u.ctx.t.Logf("readChatsWithError success after retrying %d times, polling for %s", i, totalWait)
			}
			return messages, nil
		}

		if err != nil && !strings.Contains(err.Error(), "KBFS client not found") {
			// Only retry on KBFS errors
			return messages, err
		}

		time.Sleep(wait)
		totalWait += wait
	}

	u.ctx.t.Logf("Failed to readChatsWithError after polling for %s", totalWait)
	return messages, err
}

func readChats(team smuTeam, u *smuUser, nMessages int) {
	readChatsWithDevice(team, u, u.primaryDevice(), nMessages)
}

func readChatsWithDevice(team smuTeam, u *smuUser, dev *smuDeviceWrapper, nMessages int) {
	messages, err := readChatsWithErrorAndDevice(team, u, dev, nMessages)
	t := u.ctx.t
	if err != nil {
		u.ctx.t.Fatal(err)
	}
	require.Equal(t, nMessages, len(messages))
	for i, msg := range messages {
		require.Equal(t, msg.Valid().MessageBody.Text().Body, fmt.Sprintf("%d", len(messages)-i-1))
	}
	divDebug(u.ctx, "readChat success for %s", u.username)
}

func pollForMembershipUpdate(team smuTeam, ann *smuUser, bob *smuUser, cam *smuUser) {

	details := ann.pollForMembershipUpdate(team, keybase1.PerTeamKeyGeneration(2))
	for _, member := range details.Members.Admins {
		switch member.Username {
		case ann.username:
			require.True(ann.ctx.t, member.Active)
		default:
			ann.ctx.t.Fatalf("unknown admin: %s", member.Username)
		}
	}
	for _, member := range details.Members.Writers {
		switch member.Username {
		case bob.username:
			require.False(ann.ctx.t, member.Active)
		case cam.username:
			require.True(ann.ctx.t, member.Active)
		default:
			ann.ctx.t.Fatalf("unknown writer: %s (%+v)", member.Username, details)
		}
	}
	ann.ctx.log.Debug("team details checked out: %+v", details)
}

func TestTeamDelete(t *testing.T) {
	ctx := newSMUContext(t)
	defer ctx.cleanup()

	ann := ctx.installKeybaseForUser("ann", 10)
	ann.signup()
	divDebug(ctx, "Signed up ann (%s)", ann.username)
	bob := ctx.installKeybaseForUser("bob", 10)
	bob.signup()
	divDebug(ctx, "Signed up bob (%s)", bob.username)
	cam := ctx.installKeybaseForUser("cam", 10)
	cam.signup()
	divDebug(ctx, "Signed up cam (%s)", cam.username)

	team := ann.createTeam([]*smuUser{bob, cam})
	divDebug(ctx, "team created (%s)", team.name)

	sendChat(team, ann, "0")
	divDebug(ctx, "Sent chat '0' (%s via %s)", team.name, ann.username)

	readChats(team, ann, 1)
	readChats(team, bob, 1)
	divDebug(ctx, "Ann and bob can read")

	ann.delete()
	divDebug(ctx, "Ann deleted her account")

	// It's important for cam to clear her cache right before the attempt to send,
	// since she might have received gregors that ann deleted her account,
	// and thefore might be trying to refresh and load the team.
	cam.primaryDevice().clearUPAKCache()
	sendChat(team, cam, "1")

	divDebug(ctx, "Cam sent a chat")
	readChats(team, bob, 2)
}

func TestTeamReset(t *testing.T) {
	ctx := newSMUContext(t)
	defer ctx.cleanup()

	ann := ctx.installKeybaseForUser("ann", 10)
	ann.signup()
	divDebug(ctx, "Signed up ann (%s)", ann.username)
	bob := ctx.installKeybaseForUser("bob", 10)
	bob.signup()
	divDebug(ctx, "Signed up bob (%s)", bob.username)
	cam := ctx.installKeybaseForUser("cam", 10)
	cam.signup()
	divDebug(ctx, "Signed up cam (%s)", cam.username)

	team := ann.createTeam([]*smuUser{bob, cam})
	divDebug(ctx, "team created (%s)", team.name)

	sendChat(team, ann, "0")
	divDebug(ctx, "Sent chat '0' (%s via %s)", team.name, ann.username)

	readChats(team, ann, 1)
{'fraction_non_alphanumeric': 0.15717821782178218, 'fraction_numerical': 0.007838283828382838, 'mean_word_length': 4.453880764904387, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '901920', 'n_tokens_mistral': 3759, 'n_tokens_neox': 3560, 'n_words': 871}