From 3981baf6738be86ee11a0595f630cc0f5a4bfe1b Mon Sep 17 00:00:00 2001 From: Luca Bernstein Date: Sat, 18 Dec 2021 09:56:01 +0100 Subject: [PATCH] build tx list message in controller, not in crud --- bot/controller.go | 25 +++++++++++++-- bot/controller_test.go | 62 +++++++++++++++++++++++++++++++++++++- db/crud/bot_transaction.go | 11 +++---- 3 files changed, 89 insertions(+), 9 deletions(-) diff --git a/bot/controller.go b/bot/controller.go index d8dd513..4b01fd9 100644 --- a/bot/controller.go +++ b/bot/controller.go @@ -209,7 +209,26 @@ func (bc *BotController) commandList(m *tb.Message) { } return } - if tx == "" { + if tx == nil { + bc.Logf(ERROR, m, "Tx unexpectedly was nil") + return + } + SEP := "\n" + TG_MAX_MSG_CHAR_LEN := 4096 + txMessages := []string{} + transactionsList := "" + for _, t := range tx { + if len(transactionsList)+len(t) >= TG_MAX_MSG_CHAR_LEN { + bc.Logf(TRACE, m, "Listed messages extend max message length. Splitting into multiple messages.") + txMessages = append(txMessages, transactionsList) + transactionsList = "" + } + transactionsList += t + SEP + } + if transactionsList != "" { + txMessages = append(txMessages, transactionsList) + } + if len(txMessages) == 0 { _, err := bc.Bot.Send(m.Sender, fmt.Sprintf("Your transaction list is empty. Create some first. Check /%s for commands to create a transaction."+ "\nYou might also be looking for archived transactions using '/list archived'.", CMD_HELP), clearKeyboard()) if err != nil { @@ -217,7 +236,9 @@ func (bc *BotController) commandList(m *tb.Message) { } return } - _, err = bc.Bot.Send(m.Sender, tx, clearKeyboard()) + for _, message := range txMessages { + _, err = bc.Bot.Send(m.Sender, message, clearKeyboard()) + } if err != nil { bc.Logf(ERROR, m, "Sending bot message failed: %s", err.Error()) } diff --git a/bot/controller_test.go b/bot/controller_test.go index 29d8a62..97c524d 100644 --- a/bot/controller_test.go +++ b/bot/controller_test.go @@ -10,22 +10,28 @@ import ( tb "gopkg.in/tucnak/telebot.v2" "github.com/DATA-DOG/go-sqlmock" + "github.com/LucaBernstein/beancount-bot-tg/db/crud" "github.com/LucaBernstein/beancount-bot-tg/helpers" ) type MockBot struct { - LastSentWhat interface{} + LastSentWhat interface{} + AllLastSentWhat []interface{} } func (b *MockBot) Start() {} func (b *MockBot) Handle(endpoint interface{}, handler interface{}) {} func (b *MockBot) Send(to tb.Recipient, what interface{}, options ...interface{}) (*tb.Message, error) { b.LastSentWhat = what + b.AllLastSentWhat = append(b.AllLastSentWhat, what) return nil, nil } func (b *MockBot) Me() *tb.User { return &tb.User{Username: "Test bot"} } +func (b *MockBot) reset() { + b.AllLastSentWhat = nil +} // GitHub-Issue #16: Panic if plain message without state arrives func TestTextHandlingWithoutPriorState(t *testing.T) { @@ -108,3 +114,57 @@ func TestTransactionDeletion(t *testing.T) { t.Errorf("there were unfulfilled expectations: %s", err) } } + +func TestTransactionListMaxLength(t *testing.T) { + // create test dependencies + chat := &tb.Chat{ID: 12345} + db, mock, err := sqlmock.New() + crud.TEST_MODE = true + if err != nil { + log.Fatal(err) + } + mock. + ExpectQuery(`SELECT "value" FROM "bot::transaction"`). + WithArgs(chat.ID, false). + WillReturnRows(sqlmock.NewRows([]string{"value"}).AddRow(strings.Repeat("**********", 100)).AddRow(strings.Repeat("**********", 100))) // 1000 + 1000 + mock. + ExpectQuery(`SELECT "value" FROM "bot::transaction"`). + WithArgs(chat.ID, false). + WillReturnRows(sqlmock.NewRows([]string{"value"}). + // 5 * 1000 + AddRow(strings.Repeat("**********", 100)). + AddRow(strings.Repeat("**********", 100)). + AddRow(strings.Repeat("**********", 100)). + AddRow(strings.Repeat("**********", 100)). + AddRow(strings.Repeat("**********", 100)), + ) + + bc := NewBotController(db) + bot := &MockBot{} + bc.AddBotAndStart(bot) + + // < 4096 chars tx + bc.commandList(&tb.Message{Chat: chat}) + if len(bot.AllLastSentWhat) != 1 { + t.Errorf("Expected exactly one message to be sent out: %v", bot.AllLastSentWhat) + } + + bot.reset() + + // > 4096 chars tx + bc.commandList(&tb.Message{Chat: chat}) + if len(bot.AllLastSentWhat) != 2 { + t.Errorf("Expected exactly two messages to be sent out: %v", strings.Join(stringArr(bot.AllLastSentWhat), ", ")) + } + if bot.LastSentWhat != strings.Repeat("**********", 100)+"\n" { + t.Errorf("Expected last message to contain last transaction as it flowed over the first message: %v", bot.LastSentWhat) + } +} + +func stringArr(i []interface{}) []string { + arr := []string{} + for _, e := range i { + arr = append(arr, fmt.Sprintf("%v", e)) + } + return arr +} diff --git a/db/crud/bot_transaction.go b/db/crud/bot_transaction.go index 2007fd0..14c3b7e 100644 --- a/db/crud/bot_transaction.go +++ b/db/crud/bot_transaction.go @@ -12,7 +12,7 @@ func (r *Repo) RecordTransaction(chatId int64, tx string) error { return err } -func (r *Repo) GetTransactions(m *tb.Message, isArchived bool) (string, error) { +func (r *Repo) GetTransactions(m *tb.Message, isArchived bool) ([]string, error) { LogDbf(r, helpers.TRACE, m, "Getting transactions") rows, err := r.db.Query(` SELECT "value" FROM "bot::transaction" @@ -20,19 +20,18 @@ func (r *Repo) GetTransactions(m *tb.Message, isArchived bool) (string, error) { ORDER BY "created" ASC `, m.Chat.ID, isArchived) if err != nil { - return "", err + return nil, err } defer rows.Close() - SEP := "\n" - allTransactionsMessage := "" + allTransactionsMessage := []string{} var transactionString string for rows.Next() { err = rows.Scan(&transactionString) if err != nil { - return "", err + return nil, err } - allTransactionsMessage += transactionString + SEP + allTransactionsMessage = append(allTransactionsMessage, transactionString) } return allTransactionsMessage, nil }