Repository: remotemobprogramming/mob
Pull request #416 (2024-03-25 18:03:57)

344 timer bug
This is the new PR for #344. This PR supersedes #415 


Patch:
diff --git a/mob.go b/mob.go
index 876c859..cf61e19 100644
--- a/mob.go
+++ b/mob.go
@@ -2,19 +2,12 @@ package main
 
 import (
 	"bufio"
-	"bytes"
-	"crypto/tls"
-	x509 "crypto/x509"
-	"encoding/json"
 	"errors"
 	"fmt"
 	config "github.com/remotemobprogramming/mob/v4/configuration"
 	"github.com/remotemobprogramming/mob/v4/help"
 	"github.com/remotemobprogramming/mob/v4/open"
 	"github.com/remotemobprogramming/mob/v4/say"
-	"io"
-	"net/http"
-	"net/url"
 	"os"
 	"os/exec"
 	"path/filepath"
@@ -311,9 +304,9 @@ func execute(command string, parameter []string, configuration config.Configurat
 		}
 		if len(parameter) > 0 {
 			timer := parameter[0]
-			startTimer(timer, configuration)
+			StartTimer(timer, configuration)
 		} else if configuration.Timer != "" {
-			startTimer(configuration.Timer, configuration)
+			StartTimer(configuration.Timer, configuration)
 		} else {
 			say.Info("It's now " + currentTime() + ". Happy collaborating! :)")
 		}
@@ -341,16 +334,16 @@ func execute(command string, parameter []string, configuration config.Configurat
 				}
 			} else {
 				timer := parameter[0]
-				startTimer(timer, configuration)
+				StartTimer(timer, configuration)
 			}
 		} else if configuration.Timer != "" {
-			startTimer(configuration.Timer, configuration)
+			StartTimer(configuration.Timer, configuration)
 		} else {
 			help.Help(configuration)
 		}
 	case "break":
 		if len(parameter) > 0 {
-			startBreakTimer(parameter[0], configuration)
+			StartBreakTimer(parameter[0], configuration)
 		} else {
 			help.Help(configuration)
 		}
@@ -458,10 +451,6 @@ func determineBranches(currentBranch Branch, localBranches []string, configurati
 	return
 }
 
-func getSleepCommand(timeoutInSeconds int) string {
-	return fmt.Sprintf("sleep %d", timeoutInSeconds)
-}
-
 func injectCommandWithMessage(command string, message string) string {
 	placeHolders := strings.Count(command, "%s")
 	if placeHolders > 1 {
@@ -474,20 +463,6 @@ func injectCommandWithMessage(command string, message string) string {
 	return fmt.Sprintf(command, message)
 }
 
-func getVoiceCommand(message string, voiceCommand string) string {
-	if len(voiceCommand) == 0 {
-		return ""
-	}
-	return injectCommandWithMessage(voiceCommand, message)
-}
-
-func getNotifyCommand(message string, notifyCommand string) string {
-	if len(notifyCommand) == 0 {
-		return ""
-	}
-	return injectCommandWithMessage(notifyCommand, message)
-}
-
 func executeCommandsInBackgroundProcess(commands ...string) (err error) {
 	cmds := make([]string, 0)
 	for _, c := range commands {
@@ -507,189 +482,6 @@ func executeCommandsInBackgroundProcess(commands ...string) (err error) {
 	return err
 }
 
-func startTimer(timerInMinutes string, configuration config.Configuration) {
-	timeoutInMinutes := toMinutes(timerInMinutes)
-
-	timeoutInSeconds := timeoutInMinutes * 60
-	timeOfTimeout := time.Now().Add(time.Minute * time.Duration(timeoutInMinutes)).Format("15:04")
-	say.Debug(fmt.Sprintf("Starting timer at %s for %d minutes = %d seconds (parsed from user input %s)", timeOfTimeout, timeoutInMinutes, timeoutInSeconds, timerInMinutes))
-
-	room := getMobTimerRoom(configuration)
-	startRemoteTimer := room != ""
-	startLocalTimer := configuration.TimerLocal
-
-	if !startRemoteTimer && !startLocalTimer {
-		say.Error("No timer configured, not starting timer")
-		exit(1)
-	}
-
-	if startRemoteTimer {
-		timerUser := getUserForMobTimer(configuration.TimerUser)
-		err := httpPutTimer(timeoutInMinutes, room, timerUser, configuration.TimerUrl, configuration.TimerInsecure)
-		if err != nil {
-			say.Error("remote timer couldn't be started")
-			say.Error(err.Error())
-			exit(1)
-		}
-	}
-
-	if startLocalTimer {
-		err := executeCommandsInBackgroundProcess(getSleepCommand(timeoutInSeconds), getVoiceCommand(configuration.VoiceMessage, configuration.VoiceCommand), getNotifyCommand(configuration.NotifyMessage, configuration.NotifyCommand), "echo \"mobTimer\"")
-
-		if err != nil {
-			say.Error(fmt.Sprintf("timer couldn't be started on your system (%s)", runtime.GOOS))
-			say.Error(err.Error())
-			exit(1)
-		}
-	}
-
-	say.Info("It's now " + currentTime() + ". " + fmt.Sprintf("%d min timer ends at approx. %s", timeoutInMinutes, timeOfTimeout) + ". Happy collaborating! :)")
-}
-
-func getMobTimerRoom(configuration config.Configuration) string {
-	if !isGit() {
-		say.Debug("timer not in git repository, using MOB_TIMER_ROOM for room name")
-		return configuration.TimerRoom
-	}
-
-	currentWipBranchQualifier := configuration.WipBranchQualifier
-	if currentWipBranchQualifier == "" {
-		currentBranch := gitCurrentBranch()
-		currentBaseBranch, _ := determineBranches(currentBranch, gitBranches(), configuration)
-
-		if currentBranch.IsWipBranch(configuration) {
-			wipBranchWithoutWipPrefix := currentBranch.removeWipPrefix(configuration).Name
-			currentWipBranchQualifier = removePrefix(removePrefix(wipBranchWithoutWipPrefix, currentBaseBranch.Name), configuration.WipBranchQualifierSeparator)
-		}
-	}
-
-	if configuration.TimerRoomUseWipBranchQualifier && currentWipBranchQualifier != "" {
-		say.Info("Using wip branch qualifier for room name")
-		return currentWipBranchQualifier
-	}
-
-	return configuration.TimerRoom
-}
-
-func startBreakTimer(timerInMinutes string, configuration config.Configuration) {
-	timeoutInMinutes := toMinutes(timerInMinutes)
-
-	timeoutInSeconds := timeoutInMinutes * 60
-	timeOfTimeout := time.Now().Add(time.Minute * time.Duration(timeoutInMinutes)).Format("15:04")
-	say.Debug(fmt.Sprintf("Starting break timer at %s for %d minutes = %d seconds (parsed from user input %s)", timeOfTimeout, timeoutInMinutes, timeoutInSeconds, timerInMinutes))
-
-	room := getMobTimerRoom(configuration)
-	startRemoteTimer := room != ""
-	startLocalTimer := configuration.TimerLocal
-
-	if !startRemoteTimer && !startLocalTimer {
-		say.Error("No break timer configured, not starting break timer")
-		exit(1)
-	}
-
-	if startRemoteTimer {
-		timerUser := getUserForMobTimer(configuration.TimerUser)
-		err := httpPutBreakTimer(timeoutInMinutes, room, timerUser, configuration.TimerUrl, configuration.TimerInsecure)
-
-		if err != nil {
-			say.Error("remote break timer couldn't be started")
-			say.Error(err.Error())
-			exit(1)
-		}
-	}
-
-	if startLocalTimer {
-		err := executeCommandsInBackgroundProcess(getSleepCommand(timeoutInSeconds), getVoiceCommand("mob start", configuration.VoiceCommand), getNotifyCommand("mob start", configuration.NotifyCommand), "echo \"mobTimer\"")
-
-		if err != nil {
-			say.Error(fmt.Sprintf("break timer couldn't be started on your system (%s)", runtime.GOOS))
-			say.Error(err.Error())
-			exit(1)
-		}
-	}
-
-	say.Info("It's now " + currentTime() + ". " + fmt.Sprintf("%d min break timer ends at approx. %s", timeoutInMinutes, timeOfTimeout) + ". Happy collaborating! :)")
-}
-
-func getUserForMobTimer(userOverride string) string {
-	if userOverride == "" {
-		return gitUserName()
-	}
-	return userOverride
-}
-
-func toMinutes(timerInMinutes string) int {
-	timeoutInMinutes, _ := strconv.Atoi(timerInMinutes)
-	if timeoutInMinutes < 0 {
-		timeoutInMinutes = 0
-	}
-	return timeoutInMinutes
-}
-
-func httpPutTimer(timeoutInMinutes int, room string, user string, timerService string, disableSSLVerification bool) error {
-	putBody, _ := json.Marshal(map[string]interface{}{
-		"timer": timeoutInMinutes,
-		"user":  user,
-	})
-	return sendRequest(putBody, "PUT", timerService+room, disableSSLVerification)
-}
-
-func httpPutBreakTimer(timeoutInMinutes int, room string, user string, timerService string, disableSSLVerification bool) error {
-	putBody, _ := json.Marshal(map[string]interface{}{
-		"breaktimer": timeoutInMinutes,
-		"user":       user,
-	})
-	return sendRequest(putBody, "PUT", timerService+room, disableSSLVerification)
-}
-
-func sendRequest(requestBody []byte, requestMethod string, requestUrl string, disableSSLVerification bool) error {
-	say.Info(requestMethod + " " + requestUrl + " " + string(requestBody))
-
-	responseBody := bytes.NewBuffer(requestBody)
-	request, requestCreationError := http.NewRequest(requestMethod, requestUrl, responseBody)
-
-	httpClient := http.DefaultClient
-	if disableSSLVerification {
-		transCfg := &http.Transport{
-			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
-		}
-		httpClient = &http.Client{Transport: transCfg}
-	}
-
-	if requestCreationError != nil {
-		return fmt.Errorf("failed to create the http request object: %w", requestCreationError)
-	}
-
-	request.Header.Set("Content-Type", "application/json")
-	response, responseErr := httpClient.Do(request)
-	if e, ok := responseErr.(*url.Error); ok {
-		switch e.Err.(type) {
-		case x509.UnknownAuthorityError:
-			say.Error("The timer.mob.sh SSL certificate is signed by an unknown authority!")
-			say.Fix("HINT: You can ignore that by adding MOB_TIMER_INSECURE=true to your configuration or environment.",
-				"echo MOB_TIMER_INSECURE=true >> ~/.mob")
-			return fmt.Errorf("failed, to amke the http request: %w", responseErr)
-
-		default:
-			return fmt.Errorf("failed to make the http request: %w", responseErr)
-
-		}
-	}
-
-	if responseErr != nil {
-		return fmt.Errorf("failed to make the http request: %w", responseErr)
-	}
-	defer response.Body.Close()
-	body, responseReadingErr := io.ReadAll(response.Body)
-	if responseReadingErr != nil {
-		return fmt.Errorf("failed to read the http response: %w", responseReadingErr)
-	}
-	if string(body) != "" {
-		say.Info(string(body))
-	}
-	return nil
-}
-
 func currentTime() string {
 	return time.Now().Format("15:04")
 }
diff --git a/timer.go b/timer.go
new file mode 100644
index 0000000..ecfb188
--- /dev/null
+++ b/timer.go
@@ -0,0 +1,240 @@
+package main
+
+import (
+	"bytes"
+	"crypto/tls"
+	"crypto/x509"
+	"encoding/json"
+	"errors"
+	"fmt"
+	config "github.com/remotemobprogramming/mob/v4/configuration"
+	"github.com/remotemobprogramming/mob/v4/say"
+	"io"
+	"net/http"
+	"net/url"
+	"runtime"
+	"strconv"
+	"time"
+)
+
+func StartTimer(timerInMinutes string, configuration config.Configuration) {
+	if err := startTimer(configuration.Timer, configuration); err != nil {
+		exit(1)
+	}
+}
+
+func startTimer(timerInMinutes string, configuration config.Configuration) error {
+	err, timeoutInMinutes := toMinutes(timerInMinutes)
+	if err != nil {
+		return err
+	}
+
+	timeoutInSeconds := timeoutInMinutes * 60
+	timeOfTimeout := time.Now().Add(time.Minute * time.Duration(timeoutInMinutes)).Format("15:04")
+	say.Debug(fmt.Sprintf("Starting timer at %s for %d minutes = %d seconds (parsed from user input %s)", timeOfTimeout, timeoutInMinutes, timeoutInSeconds, timerInMinutes))
+
+	room := getMobTimerRoom(configuration)
+	startRemoteTimer := room != ""
+	startLocalTimer := configuration.TimerLocal
+
+	if !startRemoteTimer && !startLocalTimer {
+		say.Error("No timer configured, not starting timer")
+		exit(1)
+	}
+
+	if startRemoteTimer {
+		timerUser := getUserForMobTimer(configuration.TimerUser)
+		err := httpPutTimer(timeoutInMinutes, room, timerUser, configuration.TimerUrl, configuration.TimerInsecure)
+		if err != nil {
+			say.Error("remote timer couldn't be started")
+			say.Error(err.Error())
+			exit(1)
+		}
+	}
+
+	if startLocalTimer {
+		err := executeCommandsInBackgroundProcess(getSleepCommand(timeoutInSeconds), getVoiceCommand(configuration.VoiceMessage, configuration.VoiceCommand), getNotifyCommand(configuration.NotifyMessage, configuration.NotifyCommand), "echo \"mobTimer\"")
+
+		if err != nil {
+			say.Error(fmt.Sprintf("timer couldn't be started on your system (%s)", runtime.GOOS))
+			say.Error(err.Error())
+			exit(1)
+		}
+	}
+
+	say.Info("It's now " + currentTime() + ". " + fmt.Sprintf("%d min timer ends at approx. %s", timeoutInMinutes, timeOfTimeout) + ". Happy collaborating! :)")
+	return nil
+}
+
+func getMobTimerRoom(configuration config.Configuration) string {
+	if !isGit() {
+		say.Debug("timer not in git repository, using MOB_TIMER_ROOM for room name")
+		return configuration.TimerRoom
+	}
+
+	currentWipBranchQualifier := configuration.WipBranchQualifier
+	if currentWipBranchQualifier == "" {
+		currentBranch := gitCurrentBranch()
+		currentBaseBranch, _ := determineBranches(currentBranch, gitBranches(), configuration)
+
+		if currentBranch.IsWipBranch(configuration) {
+			wipBranchWithoutWipPrefix := currentBranch.removeWipPrefix(configuration).Name
+			currentWipBranchQualifier = removePrefix(removePrefix(wipBranchWithoutWipPrefix, currentBaseBranch.Name), configuration.WipBranchQualifierSeparator)
+		}
+	}
+
+	if configuration.TimerRoomUseWipBranchQualifier && currentWipBranchQualifier != "" {
+		say.Info("Using wip branch qualifier for room name")
+		return currentWipBranchQualifier
+	}
+
+	return configuration.TimerRoom
+}
+
+func StartBreakTimer(timerInMinutes string, configuration config.Configuration) {
+	if err := startBreakTimer(configuration.Timer, configuration); err != nil {
+		exit(1)
+	}
+}
+
+func startBreakTimer(timerInMinutes string, configuration config.Configuration) error {
+	err, timeoutInMinutes := toMinutes(timerInMinutes)
+	if err != nil {
+		return err
+	}
+
+	timeoutInSeconds := timeoutInMinutes * 60
+	timeOfTimeout := time.Now().Add(time.Minute * time.Duration(timeoutInMinutes)).Format("15:04")
+	say.Debug(fmt.Sprintf("Starting break timer at %s for %d minutes = %d seconds (parsed from user input %s)", timeOfTimeout, timeoutInMinutes, timeoutInSeconds, timerInMinutes))
+
+	room := getMobTimerRoom(configuration)
+	startRemoteTimer := room != ""
+	startLocalTimer := configuration.TimerLocal
+
+	if !startRemoteTimer && !startLocalTimer {
+		say.Error("No break timer configured, not starting break timer")
+		exit(1)
+	}
+
+	if startRemoteTimer {
+		timerUser := getUserForMobTimer(configuration.TimerUser)
+		err := httpPutBreakTimer(timeoutInMinutes, room, timerUser, configuration.TimerUrl, configuration.TimerInsecure)
+
+		if err != nil {
+			say.Error("remote break timer couldn't be started")
+			say.Error(err.Error())
+			exit(1)
+		}
+	}
+
+	if startLocalTimer {
+		err := executeCommandsInBackgroundProcess(getSleepCommand(timeoutInSeconds), getVoiceCommand("mob start", configuration.VoiceCommand), getNotifyCommand("mob start", configuration.NotifyCommand), "echo \"mobTimer\"")
+
+		if err != nil {
+			say.Error(fmt.Sprintf("break timer couldn't be started on your system (%s)", runtime.GOOS))
+			say.Error(err.Error())
+			exit(1)
+		}
+	}
+
+	say.Info("It's now " + currentTime() + ". " + fmt.Sprintf("%d min break timer ends at approx. %s", timeoutInMinutes, timeOfTimeout) + ". So take a break now! :)")
+	return nil
+}
+
+func getUserForMobTimer(userOverride string) string {
+	if userOverride == "" {
+		return gitUserName()
+	}
+	return userOverride
+}
+
+func toMinutes(timerInMinutes string) (error, int) {
+	timeoutInMinutes, err := strconv.Atoi(timerInMinutes)
+	if err != nil || timeoutInMinutes < 1 {
+		say.Error(fmt.Sprintf("The parameter must be an integer number greater then zero"))
+		return errors.New("The parameter must be an integer number greater then zero"), 0
+	}
+	return nil, timeoutInMinutes
+}
+
+func httpPutTimer(timeoutInMinutes int, room string, user string, timerService string, disableSSLVerification bool) error {
+	putBody, _ := json.Marshal(map[string]interface{}{
+		"timer": timeoutInMinutes,
+		"user":  user,
+	})
+	return sendRequest(putBody, "PUT", timerService+room, disableSSLVerification)
+}
+
+func httpPutBreakTimer(timeoutInMinutes int, room string, user string, timerService string, disableSSLVerification bool) error {
+	putBody, _ := json.Marshal(map[string]interface{}{
+		"breaktimer": timeoutInMinutes,
+		"user":       user,
+	})
+	return sendRequest(putBody, "PUT", timerService+room, disableSSLVerification)
+}
+
+func sendRequest(requestBody []byte, requestMethod string, requestUrl string, disableSSLVerification bool) error {
+	say.Info(requestMethod + " " + requestUrl + " " + string(requestBody))
+
+	responseBody := bytes.NewBuffer(requestBody)
+	request, requestCreationError := http.NewRequest(requestMethod, requestUrl, responseBody)
+
+	httpClient := http.DefaultClient
+	if disableSSLVerification {
+		transCfg := &http.Transport{
+			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
+		}
+		httpClient = &http.Client{Transport: transCfg}
+	}
+
+	if requestCreationError != nil {
+		return fmt.Errorf("failed to create the http request object: %w", requestCreationError)
+	}
+
+	request.Header.Set("Content-Type", "application/json")
+	response, responseErr := httpClient.Do(request)
+	if e, ok := responseErr.(*url.Error); ok {
+		switch e.Err.(type) {
+		case x509.UnknownAuthorityError:
+			say.Error("The timer.mob.sh SSL certificate is signed by an unknown authority!")
+			say.Fix("HINT: You can ignore that by adding MOB_TIMER_INSECURE=true to your configuration or environment.",
+				"echo MOB_TIMER_INSECURE=true >> ~/.mob")
+			return fmt.Errorf("failed, to amke the http request: %w", responseErr)
+
+		default:
+			return fmt.Errorf("failed to make the http request: %w", responseErr)
+
+		}
+	}
+
+	if responseErr != nil {
+		return fmt.Errorf("failed to make the http request: %w", responseErr)
+	}
+	defer response.Body.Close()
+	body, responseReadingErr := io.ReadAll(response.Body)
+	if responseReadingErr != nil {
+		return fmt.Errorf("failed to read the http response: %w", responseReadingErr)
+	}
+	if string(body) != "" {
+		say.Info(string(body))
+	}
+	return nil
+}
+
+func getSleepCommand(timeoutInSeconds int) string {
+	return fmt.Sprintf("sleep %d", timeoutInSeconds)
+}
+
+func getVoiceCommand(message string, voiceCommand string) string {
+	if len(voiceCommand) == 0 {
+		return ""
+	}
+	return injectCommandWithMessage(voiceCommand, message)
+}
+
+func getNotifyCommand(message string, notifyCommand string) string {
+	if len(notifyCommand) == 0 {
+		return ""
+	}
+	return injectCommandWithMessage(notifyCommand, message)
+}


Test patch:
diff --git a/mob_test.go b/mob_test.go
index 7ae679d..31ab58a 100644
--- a/mob_test.go
+++ b/mob_test.go
@@ -1691,37 +1691,6 @@ func TestHelpRequested(t *testing.T) {
 	equals(t, true, helpRequested([]string{"s", "10", "-h"}))
 }
 
-func TestOpenTimerInBrowserWithTimerRoom(t *testing.T) {
-	mockOpenInBrowser()
-	output, configuration := setup(t)
-	configuration.TimerRoom = "testroom"
-
-	err := openTimerInBrowser(configuration)
-
-	assertOutputNotContains(t, output, "Timer Room is not configured.")
-	assertNoError(t, err)
-}
-
-func TestOpenTimerInBrowserWithoutTimerRoom(t *testing.T) {
-	mockOpenInBrowser()
-	output, configuration := setup(t)
-
-	err := openTimerInBrowser(configuration)
-
-	assertOutputContains(t, output, "Timer Room is not configured.")
-	assertNoError(t, err)
-}
-
-func TestOpenTimerInBrowserError(t *testing.T) {
-	mockOpenInBrowser()
-	_, configuration := setup(t)
-	configuration.TimerUrl = ""
-
-	err := openTimerInBrowser(configuration)
-
-	assertError(t, err, "Timer url is not configured")
-}
-
 func TestMobClean(t *testing.T) {
 	_, configuration := setup(t)
 
diff --git a/timer_test.go b/timer_test.go
new file mode 100644
index 0000000..3fa24d1
--- /dev/null
+++ b/timer_test.go
@@ -0,0 +1,94 @@
+package main
+
+import "testing"
+
+func TestOpenTimerInBrowserWithTimerRoom(t *testing.T) {
+	mockOpenInBrowser()
+	output, configuration := setup(t)
+	configuration.TimerRoom = "testroom"
+
+	err := openTimerInBrowser(configuration)
+
+	assertOutputNotContains(t, output, "Timer Room is not configured.")
+	assertNoError(t, err)
+}
+
+func TestOpenTimerInBrowserWithoutTimerRoom(t *testing.T) {
+	mockOpenInBrowser()
+	output, configuration := setup(t)
+
+	err := openTimerInBrowser(configuration)
+
+	assertOutputContains(t, output, "Timer Room is not configured.")
+	assertNoError(t, err)
+}
+
+func TestOpenTimerInBrowserError(t *testing.T) {
+	mockOpenInBrowser()
+	_, configuration := setup(t)
+	configuration.TimerUrl = ""
+
+	err := openTimerInBrowser(configuration)
+
+	assertError(t, err, "Timer url is not configured")
+}
+
+func TestTimerNumberLessThen1(t *testing.T) {
+	output, configuration := setup(t)
+
+	err := startTimer("0", configuration)
+
+	assertError(t, err, "The parameter must be an integer number greater then zero")
+	assertOutputContains(t, output, "The parameter must be an integer number greater then zero")
+}
+
+func TestTimerNotANumber(t *testing.T) {
+	output, configuration := setup(t)
+
+	err := startTimer("NotANumber", configuration)
+
+	assertError(t, err, "The parameter must be an integer number greater then zero")
+	assertOutputContains(t, output, "The parameter must be an integer number greater then zero")
+}
+
+func TestTimer(t *testing.T) {
+	output, configuration := setup(t)
+	configuration.NotifyCommand = ""
+	configuration.VoiceCommand = ""
+
+	err := startTimer("1", configuration)
+
+	assertNoError(t, err)
+	assertOutputContains(t, output, "1 min timer ends at approx.")
+	assertOutputContains(t, output, "Happy collaborating! :)")
+}
+
+func TestBreakTimerNumberLessThen1(t *testing.T) {
+	output, configuration := setup(t)
+
+	err := startBreakTimer("0", configuration)
+
+	assertError(t, err, "The parameter must be an integer number greater then zero")
+	assertOutputContains(t, output, "The parameter must be an integer number greater then zero")
+}
+
+func TestBreakTimerNotANumber(t *testing.T) {
+	output, configuration := setup(t)
+
+	err := startBreakTimer("NotANumber", configuration)
+
+	assertError(t, err, "The parameter must be an integer number greater then zero")
+	assertOutputContains(t, output, "The parameter must be an integer number greater then zero")
+}
+
+func TestBreakTimer(t *testing.T) {
+	output, configuration := setup(t)
+	configuration.NotifyCommand = ""
+	configuration.VoiceCommand = ""
+
+	err := startBreakTimer("1", configuration)
+
+	assertNoError(t, err)
+	assertOutputContains(t, output, "1 min break timer ends at approx.")
+	assertOutputContains(t, output, "So take a break now! :)")
+}