Initial commit
This commit is contained in:
40
server/internal/domain/domain_test.go
Normal file
40
server/internal/domain/domain_test.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"calllinesystem/server/internal/model"
|
||||
)
|
||||
|
||||
func TestETAUsesIntervalForEveryNumber(t *testing.T) {
|
||||
result, err := CalculateETA(ETAInput{
|
||||
PeopleAhead: 9, IntervalPerNumber: 90 * time.Second, Running: true,
|
||||
})
|
||||
if err != nil || !result.Available {
|
||||
t.Fatalf("got %#v, %v", result, err)
|
||||
}
|
||||
if result.EstimateMinutes != 15 || result.MinMinutes != 15 || result.MaxMinutes != 15 {
|
||||
t.Fatalf("unexpected result %#v", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestETAUnavailableWhenPaused(t *testing.T) {
|
||||
result, err := CalculateETA(ETAInput{Running: false})
|
||||
if err != nil || result.Available || result.Reason != "queue_not_running" {
|
||||
t.Fatalf("got %#v, %v", result, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTicketRules(t *testing.T) {
|
||||
if !CanTransitionTicket(model.TicketCalled, model.TicketArrived) || !CanTransitionTicket(model.TicketArrived, model.TicketCompleted) {
|
||||
t.Fatal("expected normal transitions")
|
||||
}
|
||||
if CanTransitionTicket(model.TicketMissed, model.TicketWaiting) || CanTransitionTicket(model.TicketArrived, model.TicketMissed) {
|
||||
t.Fatal("missed tickets must be reissued at the tail")
|
||||
}
|
||||
number, err := DisplayNumber("", 12)
|
||||
if err != nil || number != "00012" {
|
||||
t.Fatalf("got %q, %v", number, err)
|
||||
}
|
||||
}
|
||||
48
server/internal/domain/eta.go
Normal file
48
server/internal/domain/eta.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ETAInput struct {
|
||||
PeopleAhead int
|
||||
IntervalPerNumber time.Duration
|
||||
Running bool
|
||||
}
|
||||
|
||||
type ETAResult struct {
|
||||
Available bool `json:"available"`
|
||||
EstimateMinutes int `json:"estimate_minutes,omitempty"`
|
||||
MinMinutes int `json:"min_minutes,omitempty"`
|
||||
MaxMinutes int `json:"max_minutes,omitempty"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
}
|
||||
|
||||
func CalculateETA(input ETAInput) (ETAResult, error) {
|
||||
if !input.Running {
|
||||
return ETAResult{Available: false, Reason: "queue_not_running"}, nil
|
||||
}
|
||||
if input.PeopleAhead < 0 {
|
||||
return ETAResult{}, errors.New("people ahead cannot be negative")
|
||||
}
|
||||
if input.IntervalPerNumber <= 0 {
|
||||
return ETAResult{Available: false, Reason: "missing_interval_configuration"}, nil
|
||||
}
|
||||
rawMinutes := float64(input.PeopleAhead+1) * input.IntervalPerNumber.Minutes()
|
||||
estimate := roundUpFive(rawMinutes)
|
||||
return ETAResult{
|
||||
Available: true,
|
||||
EstimateMinutes: estimate,
|
||||
MinMinutes: estimate,
|
||||
MaxMinutes: estimate,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func roundUpFive(value float64) int {
|
||||
if value <= 0 {
|
||||
return 0
|
||||
}
|
||||
return int(math.Ceil(value/5) * 5)
|
||||
}
|
||||
29
server/internal/domain/queue.go
Normal file
29
server/internal/domain/queue.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"calllinesystem/server/internal/model"
|
||||
)
|
||||
|
||||
func DisplayNumber(_ string, number int) (string, error) {
|
||||
if number <= 0 || number > 99999 {
|
||||
return "", fmt.Errorf("invalid ticket number")
|
||||
}
|
||||
return fmt.Sprintf("%05d", number), nil
|
||||
}
|
||||
|
||||
func CanTransitionTicket(from, to string) bool {
|
||||
switch to {
|
||||
case model.TicketArrived:
|
||||
return from == model.TicketCalled || from == model.TicketArrived
|
||||
case model.TicketCompleted:
|
||||
return from == model.TicketCalled || from == model.TicketArrived || from == model.TicketCompleted
|
||||
case model.TicketMissed:
|
||||
return from == model.TicketCalled || from == model.TicketMissed
|
||||
case model.TicketCanceled:
|
||||
return from == model.TicketWaiting || from == model.TicketCanceled
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user