mirror of
https://github.com/ysoftdevs/Theatrical-Players-Refactoring-Kata.git
synced 2026-05-19 06:26:58 +02:00
added Go variant
- using homegrown approval test function
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
package theatre
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/leekchan/accounting"
|
||||
)
|
||||
|
||||
type StatementPrinter struct{}
|
||||
|
||||
func (StatementPrinter) Print(invoice Invoice, plays map[string]Play) (string, error) {
|
||||
totalAmount := 0
|
||||
volumeCredits := 0
|
||||
result := fmt.Sprintf("Statement for %s\n", invoice.Customer)
|
||||
|
||||
ac := accounting.Accounting{Symbol: "$", Precision: 2}
|
||||
|
||||
for _, perf := range invoice.Performances {
|
||||
play := plays[perf.PlayID]
|
||||
thisAmount := 0
|
||||
|
||||
switch play.Type {
|
||||
case "tragedy":
|
||||
thisAmount = 40000
|
||||
if perf.Audience > 30 {
|
||||
thisAmount += 1000 * (perf.Audience - 30)
|
||||
}
|
||||
case "comedy":
|
||||
thisAmount = 30000
|
||||
if perf.Audience > 20 {
|
||||
thisAmount += 10000 + 500*(perf.Audience-20)
|
||||
}
|
||||
thisAmount += 300 * perf.Audience
|
||||
default:
|
||||
return "", fmt.Errorf("unknown type: %s", play.Type)
|
||||
}
|
||||
|
||||
// add volume credits
|
||||
volumeCredits += int(math.Max(float64(perf.Audience)-30, 0))
|
||||
// add extra credit for every ten comedy attendees
|
||||
if play.Type == "comedy" {
|
||||
volumeCredits += int(math.Floor(float64(perf.Audience) / 5))
|
||||
}
|
||||
|
||||
// print line for this order
|
||||
result += fmt.Sprintf(" %s: %s (%d seats)\n", play.Name, ac.FormatMoney(float64(thisAmount)/100), perf.Audience)
|
||||
totalAmount += thisAmount
|
||||
}
|
||||
result += fmt.Sprintf("Amount owed is %s\n", ac.FormatMoney(float64(totalAmount)/100))
|
||||
result += fmt.Sprintf("You earned %d credits\n", volumeCredits)
|
||||
return result, nil
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package theatre_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/emilybache/Theatrical-Players-Refactoring-Kata/go/theatre"
|
||||
)
|
||||
|
||||
func TestPrinterPrintByApproval(t *testing.T) {
|
||||
verify(t, "json", "txt", func(t testing.TB, data []byte) []byte {
|
||||
var in struct {
|
||||
Plays []struct {
|
||||
ID string
|
||||
Play struct {
|
||||
Name string
|
||||
Type string
|
||||
}
|
||||
}
|
||||
Invoice struct {
|
||||
Customer string
|
||||
Performances []struct {
|
||||
PlayID string
|
||||
Audience int
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &in); err != nil {
|
||||
t.Fatalf("failed to unmarshal input data: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// copy test-structure to production structure. Making use of matching types.
|
||||
plays := make(map[string]theatre.Play)
|
||||
invoice := theatre.Invoice{
|
||||
Customer: in.Invoice.Customer,
|
||||
Performances: make([]theatre.Performance, 0, len(in.Invoice.Performances)),
|
||||
}
|
||||
for _, perf := range in.Invoice.Performances {
|
||||
invoice.Performances = append(invoice.Performances, perf)
|
||||
}
|
||||
for _, identifiedPlay := range in.Plays {
|
||||
plays[identifiedPlay.ID] = identifiedPlay.Play
|
||||
}
|
||||
|
||||
var printer theatre.StatementPrinter
|
||||
statement, err := printer.Print(invoice, plays)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create statement, unexpected error: %v", err)
|
||||
}
|
||||
return []byte(statement)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStatementWithNewPlayTypes(t *testing.T) {
|
||||
plays := map[string]theatre.Play{
|
||||
"henry-v": {Name: "Henry V", Type: "history"},
|
||||
"as-like": {Name: "As You Like It", Type: "pastoral"},
|
||||
}
|
||||
invoice := theatre.Invoice{
|
||||
Customer: "BigCo",
|
||||
Performances: []theatre.Performance{
|
||||
{PlayID: "henry-v", Audience: 53},
|
||||
{PlayID: "as-like", Audience: 55},
|
||||
},
|
||||
}
|
||||
|
||||
var printer theatre.StatementPrinter
|
||||
_, err := printer.Print(invoice, plays)
|
||||
if err == nil {
|
||||
t.Errorf("Expected an error, got none")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package theatre_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// verify runs a list of sub-tests using input data from the "testdata" subdirectory named after the current test.
|
||||
// All files with the suffix ".in.<inType>" are used as input for the test case function.
|
||||
// The name before the suffix is used as the base name for the files this function uses.
|
||||
//
|
||||
// What the function returns will be compared to the file with suffix ".approved.<outType>".
|
||||
// If its content does not match the result, or the file does not exist, the test fails and a file with suffix ".out.<outType>" is created.
|
||||
//
|
||||
// In case the function encounters an error, it can call Fatalf() on the passed test instance.
|
||||
// To verify errors as approved data, be sure to include the error in your structured data.
|
||||
//
|
||||
// This function reports problems of file access, as well as a failure if no input files were found.
|
||||
func verify(t *testing.T, inType string, outType string, testCase func(testing.TB, []byte) []byte) {
|
||||
t.Helper()
|
||||
datadir := path.Join("testdata", t.Name())
|
||||
files, err := ioutil.ReadDir(datadir)
|
||||
if err != nil {
|
||||
t.Fatalf("could not read test directory: %v", err)
|
||||
}
|
||||
fullInSuffix := ".in." + inType
|
||||
fullApprovedSuffix := ".approved." + outType
|
||||
inputData := make(map[string][]byte)
|
||||
approvedData := make(map[string][]byte)
|
||||
for _, file := range files {
|
||||
switch {
|
||||
case file.IsDir():
|
||||
case strings.HasSuffix(file.Name(), fullInSuffix):
|
||||
data, err := ioutil.ReadFile(path.Join(datadir, file.Name()))
|
||||
if err != nil {
|
||||
t.Errorf("failed to read input file %s: %v", file.Name(), err)
|
||||
}
|
||||
inputData[file.Name()] = data
|
||||
case strings.HasSuffix(file.Name(), fullApprovedSuffix):
|
||||
data, err := ioutil.ReadFile(path.Join(datadir, file.Name()))
|
||||
if err != nil {
|
||||
t.Errorf("failed to read approved file %s: %v", file.Name(), err)
|
||||
}
|
||||
approvedData[file.Name()] = data
|
||||
}
|
||||
}
|
||||
if len(inputData) == 0 {
|
||||
t.Fatalf("no input files in %s - is this the intent?", datadir)
|
||||
}
|
||||
for rangedInFilename, rangedInData := range inputData {
|
||||
inFilename := rangedInFilename
|
||||
inData := rangedInData
|
||||
t.Run(t.Name()+"/"+inFilename, func(t *testing.T) {
|
||||
result := testCase(t, inData)
|
||||
|
||||
baseFilename := inFilename[:len(inFilename)-len(fullInSuffix)]
|
||||
expected, hasApproved := approvedData[baseFilename+fullApprovedSuffix]
|
||||
writeOutFile := false
|
||||
switch {
|
||||
case !hasApproved:
|
||||
t.Errorf("no approved data available (file %s not available)", baseFilename+fullApprovedSuffix)
|
||||
writeOutFile = true
|
||||
case !bytes.Equal(expected, result):
|
||||
t.Errorf("result does not match expectation")
|
||||
writeOutFile = true
|
||||
}
|
||||
if writeOutFile {
|
||||
err = ioutil.WriteFile(path.Join(datadir, baseFilename+".out."+outType), result, 0644)
|
||||
if err != nil {
|
||||
t.Errorf("failed to write output data: %v", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
*.out.*
|
||||
@@ -0,0 +1,6 @@
|
||||
Statement for BigCo
|
||||
Hamlet: $650.00 (55 seats)
|
||||
As You Like It: $580.00 (35 seats)
|
||||
Othello: $500.00 (40 seats)
|
||||
Amount owed is $1,730.00
|
||||
You earned 47 credits
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"plays": [
|
||||
{ "id": "hamlet", "play": { "name": "Hamlet", "type": "tragedy" } },
|
||||
{ "id": "as-like", "play": { "name": "As You Like It", "type": "comedy" } },
|
||||
{ "id": "othello", "play": { "name": "Othello", "type": "tragedy" } }
|
||||
],
|
||||
"invoice": {
|
||||
"customer": "BigCo",
|
||||
"performances": [
|
||||
{"playId": "hamlet", "audience": 55 },
|
||||
{"playId": "as-like", "audience": 35 },
|
||||
{"playId": "othello", "audience": 40 }
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package theatre
|
||||
|
||||
type Performance struct {
|
||||
PlayID string
|
||||
Audience int
|
||||
}
|
||||
|
||||
type Play struct {
|
||||
Name string
|
||||
Type string
|
||||
}
|
||||
|
||||
type Invoice struct {
|
||||
Customer string
|
||||
Performances []Performance
|
||||
}
|
||||
Reference in New Issue
Block a user