Compare commits

...

10 Commits

7 changed files with 297 additions and 10 deletions

45
countdown/countdown.go Normal file
View 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)
}

View 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)
}
}

View File

@ -1,10 +1,19 @@
package dictionary
import "errors"
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) {
result, ok := d[word]
@ -13,3 +22,33 @@ func (d Dictionary) Search(word string) (string, error) {
}
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)
}

View File

@ -1,12 +1,22 @@
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()
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")
want := "this is just a test"
assertStrings(t, got, want)
assertStrings(t, got, want, "test")
})
t.Run("unknown word", func(t *testing.T) {
_, err := dictionary.Search("undefined")
want := "could not find the word you were looking for"
if err == nil {
t.Fatal("expected to get an error")
}
assertStrings(t, err.Error(), want)
assertError(t, err, ErrNotFound)
})
}
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
View 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
View 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)))
// }

14
main.go Normal file
View File

@ -0,0 +1,14 @@
package main
import (
"gobytest/countdown"
"os"
"time"
)
func main() {
// greet.Greet(os.Stdout, "Elodie")
sleeper := countdown.NewConfigurableSleeper(1*time.Second, time.Sleep)
countdown.Countdown(os.Stdout, sleeper)
}