Compare commits
10 Commits
fc67cf14ff
...
05226985c7
Author | SHA1 | Date | |
---|---|---|---|
05226985c7 | |||
72e6ac9b77 | |||
56be76b275 | |||
d7840e9cb7 | |||
5f02a8fbde | |||
f573268be9 | |||
c04a2e1d65 | |||
4be80f4477 | |||
58efd33b20 | |||
7e713f54de |
45
countdown/countdown.go
Normal file
45
countdown/countdown.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package countdown
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
finalWord = "Go!"
|
||||||
|
countdownStart = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
type Sleeper interface {
|
||||||
|
Sleep()
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConfigurableSleeper struct {
|
||||||
|
duration time.Duration
|
||||||
|
sleep func(time.Duration)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConfigurableSleeper(
|
||||||
|
duration time.Duration,
|
||||||
|
sleep func(time.Duration),
|
||||||
|
) *ConfigurableSleeper {
|
||||||
|
return &ConfigurableSleeper{
|
||||||
|
duration: duration,
|
||||||
|
sleep: sleep,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ConfigurableSleeper) Sleep() {
|
||||||
|
s.sleep(s.duration)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Countdown(out io.Writer, sleeper Sleeper) {
|
||||||
|
// XXX: The sequence is not tested
|
||||||
|
for i := countdownStart; i > 0; i-- {
|
||||||
|
fmt.Fprintf(out, "%d\n", i)
|
||||||
|
// This is difficult to test!
|
||||||
|
sleeper.Sleep()
|
||||||
|
}
|
||||||
|
fmt.Fprint(out, finalWord)
|
||||||
|
}
|
77
countdown/countdown_test.go
Normal file
77
countdown/countdown_test.go
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
package countdown
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
write = "write"
|
||||||
|
sleep = "sleep"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SpyCountdownOperations struct {
|
||||||
|
Calls []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SpyCountdownOperations) Sleep() {
|
||||||
|
s.Calls = append(s.Calls, sleep)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SpyCountdownOperations) Write(p []byte) (n int, err error) {
|
||||||
|
s.Calls = append(s.Calls, write)
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type SpyTime struct {
|
||||||
|
durationSlept time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SpyTime) Sleep(duration time.Duration) {
|
||||||
|
s.durationSlept = duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCountdown(t *testing.T) {
|
||||||
|
t.Run("print 3 to Go!", func(t *testing.T) {
|
||||||
|
buffer := &bytes.Buffer{}
|
||||||
|
Countdown(buffer, &SpyCountdownOperations{})
|
||||||
|
|
||||||
|
got := buffer.String()
|
||||||
|
want := `3
|
||||||
|
2
|
||||||
|
1
|
||||||
|
Go!`
|
||||||
|
|
||||||
|
if got != want {
|
||||||
|
t.Errorf("got %q want %q", got, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("sleep before every print", func(t *testing.T) {
|
||||||
|
spySleepPrinter := &SpyCountdownOperations{}
|
||||||
|
|
||||||
|
Countdown(spySleepPrinter, spySleepPrinter)
|
||||||
|
|
||||||
|
want := []string{
|
||||||
|
write, sleep, write, sleep, write, sleep, write,
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(want, spySleepPrinter.Calls) {
|
||||||
|
t.Errorf("wanted %v got %v", want, spySleepPrinter.Calls)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigurableSleeper(t *testing.T) {
|
||||||
|
sleepTime := 5 * time.Second
|
||||||
|
|
||||||
|
spyTime := &SpyTime{}
|
||||||
|
sleeper := NewConfigurableSleeper(sleepTime, spyTime.Sleep)
|
||||||
|
sleeper.Sleep()
|
||||||
|
|
||||||
|
if spyTime.durationSlept != sleepTime {
|
||||||
|
t.Errorf("should have slept for %v but slept for %v", sleepTime, spyTime.durationSlept)
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,19 @@
|
|||||||
package dictionary
|
package dictionary
|
||||||
|
|
||||||
import "errors"
|
|
||||||
|
|
||||||
type Dictionary map[string]string
|
type Dictionary map[string]string
|
||||||
|
|
||||||
var ErrNotFound = errors.New("could not find the word you were looking for")
|
const (
|
||||||
|
ErrNotFound = DictionaryErr("could not find the word you were looking for")
|
||||||
|
ErrWordExists = DictionaryErr("word exists")
|
||||||
|
ErrWordDoesNotExist = DictionaryErr("word does not exist")
|
||||||
|
)
|
||||||
|
|
||||||
|
type DictionaryErr string
|
||||||
|
|
||||||
|
// implements the Error interface, makes it an "error"
|
||||||
|
func (e DictionaryErr) Error() string {
|
||||||
|
return string(e)
|
||||||
|
}
|
||||||
|
|
||||||
func (d Dictionary) Search(word string) (string, error) {
|
func (d Dictionary) Search(word string) (string, error) {
|
||||||
result, ok := d[word]
|
result, ok := d[word]
|
||||||
@ -13,3 +22,33 @@ func (d Dictionary) Search(word string) (string, error) {
|
|||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d Dictionary) Add(word, definition string) error {
|
||||||
|
_, err := d.Search(word)
|
||||||
|
switch err {
|
||||||
|
case ErrNotFound:
|
||||||
|
d[word] = definition
|
||||||
|
case nil:
|
||||||
|
return ErrWordExists
|
||||||
|
default:
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Dictionary) Update(word, definition string) error {
|
||||||
|
_, err := d.Search(word)
|
||||||
|
switch err {
|
||||||
|
case ErrNotFound:
|
||||||
|
return ErrWordDoesNotExist
|
||||||
|
case nil:
|
||||||
|
d[word] = definition
|
||||||
|
default:
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Dictionary) Delete(word string) {
|
||||||
|
delete(d, word)
|
||||||
|
}
|
||||||
|
@ -1,12 +1,22 @@
|
|||||||
package dictionary
|
package dictionary
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
func assertStrings(t testing.TB, got, want string) {
|
func assertStrings(t testing.TB, got, want, given string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
if got != want {
|
if got != want {
|
||||||
t.Errorf("got %q want %q given %q", got, want, "test")
|
t.Errorf("got %q want %q given %q", got, want, given)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertError(t testing.TB, got, want error) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
if got != want {
|
||||||
|
t.Errorf("got %q want %q", got, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -17,16 +27,82 @@ func TestSearch(t *testing.T) {
|
|||||||
got, _ := dictionary.Search("test")
|
got, _ := dictionary.Search("test")
|
||||||
want := "this is just a test"
|
want := "this is just a test"
|
||||||
|
|
||||||
assertStrings(t, got, want)
|
assertStrings(t, got, want, "test")
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("unknown word", func(t *testing.T) {
|
t.Run("unknown word", func(t *testing.T) {
|
||||||
_, err := dictionary.Search("undefined")
|
_, err := dictionary.Search("undefined")
|
||||||
want := "could not find the word you were looking for"
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("expected to get an error")
|
t.Fatal("expected to get an error")
|
||||||
}
|
}
|
||||||
|
assertError(t, err, ErrNotFound)
|
||||||
assertStrings(t, err.Error(), want)
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertDefinition(t testing.TB, dict Dictionary, word, def string) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
got, err := dict.Search(word)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Should find added word:", err)
|
||||||
|
}
|
||||||
|
assertStrings(t, got, def, word)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAdd(t *testing.T) {
|
||||||
|
t.Run("new word", func(t *testing.T) {
|
||||||
|
dict := Dictionary{}
|
||||||
|
|
||||||
|
word := "test"
|
||||||
|
def := "this is just a test"
|
||||||
|
|
||||||
|
dict.Add(word, def)
|
||||||
|
assertDefinition(t, dict, word, def)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("existing word", func(t *testing.T) {
|
||||||
|
word := "test"
|
||||||
|
def := "this is just a test"
|
||||||
|
dict := Dictionary{word: def}
|
||||||
|
|
||||||
|
err := dict.Add(word, def)
|
||||||
|
assertError(t, err, ErrWordExists)
|
||||||
|
assertDefinition(t, dict, word, def)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdate(t *testing.T) {
|
||||||
|
t.Run("update an existing word", func(t *testing.T) {
|
||||||
|
word := "test"
|
||||||
|
def := "this is just a test"
|
||||||
|
updated := "updated definition"
|
||||||
|
dict := Dictionary{word: def}
|
||||||
|
|
||||||
|
dict.Update(word, updated)
|
||||||
|
assertDefinition(t, dict, word, updated)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("update an non-existing word", func(t *testing.T) {
|
||||||
|
word := "test"
|
||||||
|
updated := "updated definition"
|
||||||
|
dict := Dictionary{}
|
||||||
|
|
||||||
|
err := dict.Update(word, updated)
|
||||||
|
assertError(t, err, ErrWordDoesNotExist)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDelete(t *testing.T) {
|
||||||
|
t.Run("delete a word", func(t *testing.T) {
|
||||||
|
word := "test"
|
||||||
|
def := "this is just a test"
|
||||||
|
dict := Dictionary{word: def}
|
||||||
|
|
||||||
|
dict.Delete(word)
|
||||||
|
_, err := dict.Search(word)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected to get an error")
|
||||||
|
}
|
||||||
|
assertError(t, err, ErrNotFound)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
17
greet/greet_test.go
Normal file
17
greet/greet_test.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package greet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGreet(t *testing.T) {
|
||||||
|
buffer := bytes.Buffer{}
|
||||||
|
Greet(&buffer, "Chris")
|
||||||
|
got := buffer.String()
|
||||||
|
want := "Hello, Chris"
|
||||||
|
|
||||||
|
if got != want {
|
||||||
|
t.Errorf("got %q want %q", got, want)
|
||||||
|
}
|
||||||
|
}
|
19
greet/main.go
Normal file
19
greet/main.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package greet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Greet(writer io.Writer, name string) {
|
||||||
|
fmt.Fprintf(writer, "Hello, %s", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MyGreeterHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
Greet(w, "world")
|
||||||
|
}
|
||||||
|
|
||||||
|
// func main() {
|
||||||
|
// log.Fatal(http.ListenAndServe(":5001", http.HandlerFunc(MyGreeterHandler)))
|
||||||
|
// }
|
Loading…
x
Reference in New Issue
Block a user