added Go variant

- using homegrown approval test function
This commit is contained in:
Christian Haas
2019-10-06 19:57:02 +02:00
parent 09a3047087
commit b7d9ed303b
10 changed files with 280 additions and 0 deletions
+53
View File
@@ -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
}
+74
View File
@@ -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")
}
}
+78
View File
@@ -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)
}
}
})
}
}
+1
View File
@@ -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 }
]
}
}
+16
View File
@@ -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
}