├── .editorconfig ├── .github ├── FUNDING.yml └── workflows │ └── go.yml ├── .gitignore ├── .mdlrc ├── LICENSE.md ├── README.md ├── SUMMARY.md ├── TDD-outside-in.jpg ├── amazing-art.png ├── anti-patterns.md ├── app-intro.md ├── arrays-and-slices.md ├── arrays ├── v1 │ ├── sum.go │ └── sum_test.go ├── v2 │ ├── sum.go │ └── sum_test.go ├── v3 │ ├── sum.go │ └── sum_test.go ├── v4 │ ├── sum.go │ └── sum_test.go ├── v5 │ ├── sum.go │ └── sum_test.go ├── v6 │ ├── sum.go │ └── sum_test.go ├── v7 │ ├── sum.go │ └── sum_test.go └── v8 │ ├── assert.go │ ├── bad_bank.go │ ├── bad_bank_test.go │ ├── collection_fun.go │ ├── sum.go │ └── sum_test.go ├── blogrenderer ├── post.go ├── renderer.go ├── renderer_test.TestRender.it_converts_a_single_post_into_HTML.approved.txt ├── renderer_test.TestRender.it_renders_an_index_of_posts.approved.txt ├── renderer_test.go └── templates │ ├── blog.gohtml │ ├── bottom.gohtml │ ├── index.gohtml │ └── top.gohtml ├── book.json ├── build.books.sh ├── build.sh ├── command-line.md ├── command-line ├── v1 │ ├── cmd │ │ ├── cli │ │ │ └── main.go │ │ └── webserver │ │ │ ├── game.db.json │ │ │ └── main.go │ ├── file_system_store.go │ ├── file_system_store_test.go │ ├── league.go │ ├── server.go │ ├── server_integration_test.go │ ├── server_test.go │ ├── tape.go │ └── tape_test.go ├── v2 │ ├── CLI.go │ ├── CLI_test.go │ ├── cmd │ │ ├── cli │ │ │ └── main.go │ │ └── webserver │ │ │ ├── game.db.json │ │ │ └── main.go │ ├── file_system_store.go │ ├── file_system_store_test.go │ ├── league.go │ ├── server.go │ ├── server_integration_test.go │ ├── server_test.go │ ├── tape.go │ └── tape_test.go └── v3 │ ├── CLI.go │ ├── CLI_test.go │ ├── cmd │ ├── cli │ │ └── main.go │ └── webserver │ │ └── main.go │ ├── file_system_store.go │ ├── file_system_store_test.go │ ├── league.go │ ├── server.go │ ├── server_integration_test.go │ ├── server_test.go │ ├── tape.go │ ├── tape_test.go │ └── testing.go ├── concurrency.md ├── concurrency ├── v1 │ ├── check_website.go │ ├── check_websites.go │ ├── check_websites_benchmark_test.go │ └── check_websites_test.go ├── v2 │ ├── check_website.go │ ├── check_websites.go │ ├── check_websites_benchmark_test.go │ └── check_websites_test.go └── v3 │ ├── check_website.go │ ├── check_websites.go │ ├── check_websites_benchmark_test.go │ └── check_websites_test.go ├── context-aware-reader.md ├── context.md ├── context ├── v1 │ ├── context.go │ └── context_test.go ├── v2 │ ├── context.go │ ├── context_test.go │ └── testdoubles.go └── v3 │ ├── context.go │ ├── context_test.go │ └── testdoubles.go ├── contributing.md ├── dependency-injection.md ├── di ├── v1 │ ├── di.go │ └── di_test.go └── v2 │ ├── di.go │ └── di_test.go ├── epub-cover-small.png ├── epub-cover.png ├── epub-cover.pxm ├── error-types.md ├── for ├── v1 │ ├── repeat.go │ └── repeat_test.go ├── v2 │ ├── repeat.go │ └── repeat_test.go ├── v3 │ ├── repeat.go │ └── repeat_test.go └── vx │ ├── repeat.go │ └── repeat_test.go ├── gb-readme.md ├── generics.md ├── generics ├── assert.go ├── generics_test.go └── stack.go ├── go.mod ├── go.sum ├── hello-world.md ├── hello-world ├── .DS_Store ├── v1 │ └── hello.go ├── v2 │ ├── hello.go │ └── hello_test.go ├── v3 │ ├── hello.go │ └── hello_test.go ├── v4 │ ├── hello.go │ └── hello_test.go ├── v5 │ ├── hello.go │ └── hello_test.go ├── v6 │ ├── hello.go │ └── hello_test.go ├── v7 │ ├── hello.go │ └── hello_test.go └── v8 │ ├── hello.go │ └── hello_test.go ├── html-templates.md ├── http-handlers-revisited.md ├── http-server.md ├── http-server ├── v1 │ ├── main.go │ ├── server.go │ └── server_test.go ├── v2 │ ├── main.go │ ├── server.go │ └── server_test.go ├── v3 │ ├── main.go │ ├── server.go │ └── server_test.go ├── v4 │ ├── main.go │ ├── server.go │ └── server_test.go └── v5 │ ├── in_memory_player_store.go │ ├── main.go │ ├── server.go │ ├── server_integration_test.go │ └── server_test.go ├── install-go.md ├── integers.md ├── integers ├── v1 │ ├── adder.go │ └── adder_test.go └── v2 │ ├── adder.go │ └── adder_test.go ├── intro-to-acceptance-tests.md ├── io.md ├── io ├── v1 │ ├── file_system_store.go │ ├── file_system_store_test.go │ ├── in_memory_player_store.go │ ├── league.go │ ├── main.go │ ├── server.go │ ├── server_integration_test.go │ └── server_test.go ├── v2 │ ├── file_system_store.go │ ├── file_system_store_test.go │ ├── in_memory_player_store.go │ ├── league.go │ ├── main.go │ ├── server.go │ ├── server_integration_test.go │ └── server_test.go ├── v3 │ ├── file_system_store.go │ ├── file_system_store_test.go │ ├── in_memory_player_store.go │ ├── league.go │ ├── main.go │ ├── server.go │ ├── server_integration_test.go │ └── server_test.go ├── v4 │ ├── file_system_store.go │ ├── file_system_store_test.go │ ├── in_memory_player_store.go │ ├── league.go │ ├── main.go │ ├── server.go │ ├── server_integration_test.go │ └── server_test.go ├── v5 │ ├── file_system_store.go │ ├── file_system_store_test.go │ ├── league.go │ ├── main.go │ ├── server.go │ ├── server_integration_test.go │ └── server_test.go ├── v6 │ ├── file_system_store.go │ ├── file_system_store_test.go │ ├── league.go │ ├── main.go │ ├── server.go │ ├── server_integration_test.go │ └── server_test.go ├── v7 │ ├── file_system_store.go │ ├── file_system_store_test.go │ ├── league.go │ ├── main.go │ ├── server.go │ ├── server_integration_test.go │ ├── server_test.go │ ├── tape.go │ └── tape_test.go ├── v8 │ ├── file_system_store.go │ ├── file_system_store_test.go │ ├── league.go │ ├── main.go │ ├── server.go │ ├── server_integration_test.go │ ├── server_test.go │ ├── tape.go │ └── tape_test.go └── v9 │ ├── file_system_store.go │ ├── file_system_store_test.go │ ├── league.go │ ├── main.go │ ├── server.go │ ├── server_integration_test.go │ ├── server_test.go │ ├── tape.go │ └── tape_test.go ├── iteration.md ├── iterators └── iterators_test.go ├── json.md ├── json ├── v1 │ ├── in_memory_player_store.go │ ├── main.go │ ├── server.go │ ├── server_integration_test.go │ └── server_test.go ├── v2 │ ├── in_memory_player_store.go │ ├── main.go │ ├── server.go │ ├── server_integration_test.go │ └── server_test.go ├── v3 │ ├── in_memory_player_store.go │ ├── main.go │ ├── server.go │ ├── server_integration_test.go │ └── server_test.go ├── v4 │ ├── in_memory_player_store.go │ ├── main.go │ ├── server.go │ ├── server_integration_test.go │ └── server_test.go ├── v5 │ ├── in_memory_player_store.go │ ├── main.go │ ├── server.go │ ├── server_integration_test.go │ └── server_test.go └── v6 │ ├── in_memory_player_store.go │ ├── main.go │ ├── server.go │ ├── server_integration_test.go │ └── server_test.go ├── maps.md ├── maps ├── v1 │ ├── dictionary.go │ └── dictionary_test.go ├── v2 │ ├── dictionary.go │ └── dictionary_test.go ├── v3 │ ├── dictionary.go │ └── dictionary_test.go ├── v4 │ ├── dictionary.go │ └── dictionary_test.go ├── v5 │ ├── dictionary.go │ └── dictionary_test.go ├── v6 │ ├── dictionary.go │ └── dictionary_test.go └── v7 │ ├── dictionary.go │ └── dictionary_test.go ├── math.md ├── math ├── clock.template.svg ├── example_clock.svg ├── images │ ├── unit_circle.png │ ├── unit_circle_12_oclock.png │ ├── unit_circle_coords.png │ └── unit_circle_params.png ├── v1 │ └── clockface │ │ ├── clockface.go │ │ └── clockface_test.go ├── v10 │ └── clockface │ │ ├── clockface.go │ │ ├── clockface │ │ ├── clock.svg │ │ └── main.go │ │ ├── clockface_acceptance_test.go │ │ ├── clockface_test.go │ │ └── svgWriter.go ├── v11 │ └── clockface │ │ ├── clockface.go │ │ ├── clockface │ │ ├── clock.svg │ │ └── main.go │ │ ├── clockface_acceptance_test.go │ │ ├── clockface_test.go │ │ └── svgWriter.go ├── v12 │ └── clockface │ │ ├── clockface.go │ │ ├── clockface │ │ ├── clock.svg │ │ └── main.go │ │ ├── clockface_acceptance_test.go │ │ ├── clockface_test.go │ │ └── svgWriter.go ├── v2 │ └── clockface │ │ ├── clockface.go │ │ ├── clockface_acceptance_test.go │ │ └── clockface_test.go ├── v3 │ └── clockface │ │ ├── clockface.go │ │ ├── clockface_acceptance_test.go │ │ └── clockface_test.go ├── v4 │ └── clockface │ │ ├── clockface.go │ │ ├── clockface_acceptance_test.go │ │ └── clockface_test.go ├── v5 │ └── clockface │ │ ├── clockface.go │ │ ├── clockface_acceptance_test.go │ │ └── clockface_test.go ├── v6 │ └── clockface │ │ ├── clockface.go │ │ ├── clockface │ │ ├── clock.svg │ │ └── main.go │ │ ├── clockface_acceptance_test.go │ │ └── clockface_test.go ├── v7 │ └── clockface │ │ ├── clockface.go │ │ ├── clockface │ │ ├── clock.svg │ │ └── main.go │ │ ├── clockface_acceptance_test.go │ │ ├── clockface_test.go │ │ └── svgWriter.go ├── v7b │ └── clockface │ │ ├── clockface.go │ │ ├── clockface │ │ ├── clock.svg │ │ └── main.go │ │ ├── clockface_acceptance_test.go │ │ ├── clockface_test.go │ │ └── svgWriter.go ├── v7c │ └── clockface │ │ ├── clockface.go │ │ ├── clockface │ │ ├── clock.svg │ │ └── main.go │ │ ├── clockface_acceptance_test.go │ │ ├── clockface_test.go │ │ └── svgWriter.go ├── v8 │ └── clockface │ │ ├── clockface.go │ │ ├── clockface │ │ ├── clock.svg │ │ └── main.go │ │ ├── clockface_acceptance_test.go │ │ ├── clockface_test.go │ │ └── svgWriter.go ├── v9 │ └── clockface │ │ ├── clockface.go │ │ ├── clockface │ │ ├── clock.svg │ │ └── main.go │ │ ├── clockface_acceptance_test.go │ │ ├── clockface_test.go │ │ └── svgWriter.go └── vFinal │ └── clockface │ ├── clockface.go │ ├── clockface │ ├── clock.svg │ └── main.go │ ├── clockface_test.go │ └── svg │ ├── svg.go │ └── svg_test.go ├── meta.tmpl.tex ├── mocking.md ├── mocking ├── v1 │ ├── countdown_test.go │ └── main.go ├── v2 │ ├── countdown_test.go │ └── main.go ├── v3 │ ├── countdown_test.go │ └── main.go ├── v4 │ ├── countdown_test.go │ └── main.go ├── v5 │ ├── countdown_test.go │ └── main.go └── v6 │ ├── countdown_test.go │ └── main.go ├── os-exec.md ├── pdf-cover.md ├── pdf-cover.tex ├── pointers-and-errors.md ├── pointers ├── v1 │ ├── wallet.go │ └── wallet_test.go ├── v2 │ ├── wallet.go │ └── wallet_test.go ├── v3 │ ├── wallet.go │ └── wallet_test.go └── v4 │ ├── wallet.go │ └── wallet_test.go ├── q-and-a ├── context-aware-reader │ ├── context_aware_reader.go │ └── context_aware_reader_test.go ├── error-types │ ├── error-types_test.go │ └── v2 │ │ └── error-types_test.go ├── http-handlers-revisited │ ├── basic_test.go │ ├── still_basic.go │ └── still_basic_test.go └── os-exec │ ├── msg.xml │ └── os-exec_test.go ├── reading-files.md ├── reading-files ├── blogposts.go ├── blogposts_test.go └── post.go ├── red-green-blue-gophers-smaller.png ├── red-green-blue-gophers.png ├── refactoring-checklist.md ├── reflection.md ├── reflection ├── v1 │ ├── reflection.go │ └── reflection_test.go ├── v10 │ ├── reflection.go │ └── reflection_test.go ├── v2 │ ├── reflection.go │ └── reflection_test.go ├── v3 │ ├── reflection.go │ └── reflection_test.go ├── v4 │ ├── reflection.go │ └── reflection_test.go ├── v5 │ ├── reflection.go │ └── reflection_test.go ├── v6 │ ├── reflection.go │ └── reflection_test.go ├── v7 │ ├── reflection.go │ └── reflection_test.go ├── v8 │ ├── reflection.go │ └── reflection_test.go └── v9 │ ├── reflection.go │ └── reflection_test.go ├── revisiting-arrays-and-slices-with-generics.md ├── roman-numerals.md ├── roman-numerals ├── v1 │ └── numeral_test.go ├── v10 │ ├── numeral_test.go │ └── roman_numerals.go ├── v11 │ ├── numeral_test.go │ └── roman_numerals.go ├── v2 │ └── numeral_test.go ├── v3 │ └── numeral_test.go ├── v4 │ └── numeral_test.go ├── v5 │ └── numeral_test.go ├── v6 │ └── numeral_test.go ├── v7 │ └── numeral_test.go ├── v8 │ └── numeral_test.go └── v9 │ └── numeral_test.go ├── scaling-acceptance-tests.md ├── select.md ├── select ├── v1 │ ├── racer.go │ └── racer_test.go ├── v2 │ ├── racer.go │ └── racer_test.go └── v3 │ ├── racer.go │ └── racer_test.go ├── structs-methods-and-interfaces.md ├── structs ├── v1 │ ├── shapes.go │ └── shapes_test.go ├── v2 │ ├── shapes.go │ └── shapes_test.go ├── v3 │ ├── shapes.go │ └── shapes_test.go ├── v4 │ ├── shapes.go │ └── shapes_test.go ├── v5 │ ├── shapes.go │ └── shapes_test.go ├── v6 │ ├── shapes.go │ └── shapes_test.go ├── v7 │ ├── shapes.go │ └── shapes_test.go └── v8 │ ├── shapes.go │ └── shapes_test.go ├── sync.md ├── sync ├── v1 │ ├── sync.go │ └── sync_test.go └── v2 │ ├── sync.go │ └── sync_test.go ├── template.md ├── time.md ├── time ├── v1 │ ├── CLI.go │ ├── CLI_test.go │ ├── blind_alerter.go │ ├── cmd │ │ ├── cli │ │ │ └── main.go │ │ └── webserver │ │ │ └── main.go │ ├── file_system_store.go │ ├── file_system_store_test.go │ ├── league.go │ ├── server.go │ ├── server_integration_test.go │ ├── server_test.go │ ├── tape.go │ ├── tape_test.go │ └── testing.go ├── v2 │ ├── CLI.go │ ├── CLI_test.go │ ├── blind_alerter.go │ ├── cmd │ │ ├── cli │ │ │ └── main.go │ │ └── webserver │ │ │ └── main.go │ ├── file_system_store.go │ ├── file_system_store_test.go │ ├── league.go │ ├── server.go │ ├── server_integration_test.go │ ├── server_test.go │ ├── tape.go │ ├── tape_test.go │ ├── testing.go │ ├── texas_holdem.go │ └── texas_holdem_test.go └── v3 │ ├── BlindAlerter.go │ ├── CLI.go │ ├── CLI_test.go │ ├── cmd │ ├── cli │ │ └── main.go │ └── webserver │ │ └── main.go │ ├── file_system_store.go │ ├── file_system_store_test.go │ ├── game.go │ ├── league.go │ ├── server.go │ ├── server_integration_test.go │ ├── server_test.go │ ├── tape.go │ ├── tape_test.go │ ├── testing.go │ ├── texas_holdem.go │ └── texas_holdem_test.go ├── title.txt ├── todo └── todo1_test.go ├── websockets.md ├── websockets ├── v1 │ ├── CLI.go │ ├── CLI_test.go │ ├── Gopkg.lock │ ├── Gopkg.toml │ ├── blind_alerter.go │ ├── cmd │ │ ├── cli │ │ │ └── main.go │ │ └── webserver │ │ │ ├── game.html │ │ │ └── main.go │ ├── file_system_store.go │ ├── file_system_store_test.go │ ├── game.go │ ├── game.html │ ├── league.go │ ├── server.go │ ├── server_integration_test.go │ ├── server_test.go │ ├── tape.go │ ├── tape_test.go │ ├── testing.go │ ├── texas_holdem.go │ ├── texas_holdem_test.go │ └── vendor │ │ └── github.com │ │ └── gorilla │ │ └── websocket │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── AUTHORS │ │ ├── LICENSE │ │ ├── README.md │ │ ├── client.go │ │ ├── client_clone.go │ │ ├── client_clone_legacy.go │ │ ├── compression.go │ │ ├── conn.go │ │ ├── conn_write.go │ │ ├── conn_write_legacy.go │ │ ├── doc.go │ │ ├── json.go │ │ ├── mask.go │ │ ├── mask_safe.go │ │ ├── prepared.go │ │ ├── proxy.go │ │ ├── server.go │ │ ├── trace.go │ │ ├── trace_17.go │ │ ├── util.go │ │ └── x_net_proxy.go └── v2 │ ├── CLI.go │ ├── CLI_test.go │ ├── Gopkg.lock │ ├── Gopkg.toml │ ├── blind_alerter.go │ ├── cmd │ ├── cli │ │ └── main.go │ └── webserver │ │ ├── game.html │ │ └── main.go │ ├── file_system_store.go │ ├── file_system_store_test.go │ ├── game.go │ ├── game.html │ ├── league.go │ ├── player_server_ws.go │ ├── server.go │ ├── server_integration_test.go │ ├── server_test.go │ ├── tape.go │ ├── tape_test.go │ ├── testing.go │ ├── texas_holdem.go │ ├── texas_holdem_test.go │ └── vendor │ └── github.com │ └── gorilla │ └── websocket │ ├── .gitignore │ ├── .travis.yml │ ├── AUTHORS │ ├── LICENSE │ ├── README.md │ ├── client.go │ ├── client_clone.go │ ├── client_clone_legacy.go │ ├── compression.go │ ├── conn.go │ ├── conn_write.go │ ├── conn_write_legacy.go │ ├── doc.go │ ├── json.go │ ├── mask.go │ ├── mask_safe.go │ ├── prepared.go │ ├── proxy.go │ ├── server.go │ ├── trace.go │ ├── trace_17.go │ ├── util.go │ └── x_net_proxy.go ├── why.md └── working-without-mocks.md /.editorconfig: -------------------------------------------------------------------------------- 1 | # Top-most EditorConfig file 2 | root = true 3 | 4 | # Every file should according to these default configurations if not specified 5 | [*] 6 | # Use UNIX-style line endings 7 | end_of_line = LF 8 | # Use utf-8 file encoding 9 | charset = utf-8 10 | # 4 space indent 11 | indent_style = space 12 | indent_size = 4 13 | # Ensure file ends with a newline when saving(prevent `no newline at EOF`) 14 | insert_final_newline = true 15 | # Remove any whitespace characters preceding newline characters 16 | trim_trailing_whitespace = true 17 | 18 | # For YAML 19 | [*.{yml,yaml}] 20 | indent_size = 2 21 | 22 | # For Go files 23 | [*.go] 24 | # `gofmt` uses tabs for indentation 25 | indent_style = tab 26 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [quii] 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | *.iml 3 | 4 | # Book build output 5 | _book/ 6 | 7 | # eBook build output 8 | *.epub 9 | *.mobi 10 | *.pdf 11 | 12 | # templated files 13 | meta.tex 14 | 15 | game.db.json 16 | .DS_Store 17 | -------------------------------------------------------------------------------- /.mdlrc: -------------------------------------------------------------------------------- 1 | git_recurse true 2 | 3 | rules "MD001", "MD002", "MD003", "MD004", "MD005", "MD006", "MD007", "MD008", "MD009", "MD010", "MD011", "MD012", "MD014", "MD015", "MD016", "MD017", "MD018", "MD019", "MD020", "MD021", "MD023", "MD025", "MD028", "MD030", "MD035", "MD037", "MD038", "MD040", "MD041", "MD042" 4 | 5 | # exclude rules 6 | exclude "MD013" # Line length 7 | exclude "MD022" # Headers should be surrounded by blank lines 8 | exclude "MD024" # Multiple headers with the same content 9 | exclude "MD026" # Trailing punctuation in header 10 | exclude "MD027" # Multiple spaces after blockquote symbol 11 | exclude "MD029" # Ordered list item prefix 12 | exclude "MD031" # Fenced code blocks should be surrounded by blank lines 13 | exclude "MD032" # Lists should be surrounded by blank lines 14 | exclude "MD033" # Inline HTML 15 | exclude "MD034" # Bare URL used 16 | exclude "MD036" # Emphasis used instead of a header 17 | exclude "MD039" # Spaces inside link text 18 | -------------------------------------------------------------------------------- /TDD-outside-in.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quii/learn-go-with-tests/9980f1332d7ff3b6a047cf67658451107c4550a8/TDD-outside-in.jpg -------------------------------------------------------------------------------- /amazing-art.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quii/learn-go-with-tests/9980f1332d7ff3b6a047cf67658451107c4550a8/amazing-art.png -------------------------------------------------------------------------------- /arrays/v1/sum.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // Sum calculates the total from an array of numbers. 4 | func Sum(numbers [5]int) int { 5 | sum := 0 6 | for i := 0; i < 5; i++ { 7 | sum += numbers[i] 8 | } 9 | return sum 10 | } 11 | -------------------------------------------------------------------------------- /arrays/v1/sum_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | func TestSum(t *testing.T) { 6 | 7 | numbers := [5]int{1, 2, 3, 4, 5} 8 | 9 | got := Sum(numbers) 10 | want := 15 11 | 12 | if want != got { 13 | t.Errorf("got %d want %d given, %v", got, want, numbers) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /arrays/v2/sum.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // Sum calculates the total from an array of numbers. 4 | func Sum(numbers [5]int) int { 5 | sum := 0 6 | for _, number := range numbers { 7 | sum += number 8 | } 9 | return sum 10 | } 11 | -------------------------------------------------------------------------------- /arrays/v2/sum_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | func TestSum(t *testing.T) { 6 | 7 | numbers := [5]int{1, 2, 3, 4, 5} 8 | 9 | got := Sum(numbers) 10 | want := 15 11 | 12 | if got != want { 13 | t.Errorf("got %d want %d given, %v", got, want, numbers) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /arrays/v3/sum.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // Sum calculates the total from a slice of numbers. 4 | func Sum(numbers []int) int { 5 | sum := 0 6 | for _, number := range numbers { 7 | sum += number 8 | } 9 | return sum 10 | } 11 | -------------------------------------------------------------------------------- /arrays/v3/sum_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | func TestSum(t *testing.T) { 6 | 7 | t.Run("collections of any size", func(t *testing.T) { 8 | 9 | numbers := []int{1, 2, 3} 10 | 11 | got := Sum(numbers) 12 | want := 6 13 | 14 | if got != want { 15 | t.Errorf("got %d want %d given, %v", got, want, numbers) 16 | } 17 | }) 18 | 19 | } 20 | -------------------------------------------------------------------------------- /arrays/v4/sum.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // Sum calculates the total from a slice of numbers. 4 | func Sum(numbers []int) int { 5 | sum := 0 6 | for _, number := range numbers { 7 | sum += number 8 | } 9 | return sum 10 | } 11 | 12 | // SumAll calculates the respective sums of every slice passed in. 13 | func SumAll(numbersToSum ...[]int) []int { 14 | lengthOfNumbers := len(numbersToSum) 15 | sums := make([]int, lengthOfNumbers) 16 | 17 | for i, numbers := range numbersToSum { 18 | sums[i] = Sum(numbers) 19 | } 20 | 21 | return sums 22 | } 23 | -------------------------------------------------------------------------------- /arrays/v4/sum_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestSum(t *testing.T) { 9 | 10 | t.Run("collections of any size", func(t *testing.T) { 11 | 12 | numbers := []int{1, 2, 3} 13 | 14 | got := Sum(numbers) 15 | want := 6 16 | 17 | if got != want { 18 | t.Errorf("got %d want %d given, %v", got, want, numbers) 19 | } 20 | }) 21 | 22 | } 23 | 24 | func TestSumAll(t *testing.T) { 25 | 26 | got := SumAll([]int{1, 2}, []int{0, 9}) 27 | want := []int{3, 9} 28 | 29 | if !reflect.DeepEqual(got, want) { 30 | t.Errorf("got %v want %v", got, want) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /arrays/v5/sum.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // Sum calculates the total from a slice of numbers. 4 | func Sum(numbers []int) int { 5 | sum := 0 6 | for _, number := range numbers { 7 | sum += number 8 | } 9 | return sum 10 | } 11 | 12 | // SumAll calculates the respective sums of every slice passed in. 13 | func SumAll(numbersToSum ...[]int) []int { 14 | var sums []int 15 | for _, numbers := range numbersToSum { 16 | sums = append(sums, Sum(numbers)) 17 | } 18 | 19 | return sums 20 | } 21 | -------------------------------------------------------------------------------- /arrays/v5/sum_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestSum(t *testing.T) { 9 | 10 | t.Run("collections of any size", func(t *testing.T) { 11 | 12 | numbers := []int{1, 2, 3} 13 | 14 | got := Sum(numbers) 15 | want := 6 16 | 17 | if got != want { 18 | t.Errorf("got %d want %d given, %v", got, want, numbers) 19 | } 20 | }) 21 | 22 | } 23 | 24 | func TestSumAll(t *testing.T) { 25 | 26 | got := SumAll([]int{1, 2}, []int{0, 9}) 27 | want := []int{3, 9} 28 | 29 | if !reflect.DeepEqual(got, want) { 30 | t.Errorf("got %v want %v", got, want) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /arrays/v6/sum.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // Sum calculates the total from a slice of numbers. 4 | func Sum(numbers []int) int { 5 | sum := 0 6 | for _, number := range numbers { 7 | sum += number 8 | } 9 | return sum 10 | } 11 | 12 | // SumAllTails calculates the respective sums of every slice passed in. 13 | func SumAllTails(numbersToSum ...[]int) []int { 14 | var sums []int 15 | for _, numbers := range numbersToSum { 16 | tail := numbers[1:] 17 | sums = append(sums, Sum(tail)) 18 | } 19 | 20 | return sums 21 | } 22 | -------------------------------------------------------------------------------- /arrays/v6/sum_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestSum(t *testing.T) { 9 | 10 | t.Run("collections of any size", func(t *testing.T) { 11 | 12 | numbers := []int{1, 2, 3} 13 | 14 | got := Sum(numbers) 15 | want := 6 16 | 17 | if got != want { 18 | t.Errorf("got %d want %d given, %v", got, want, numbers) 19 | } 20 | }) 21 | 22 | } 23 | 24 | func TestSumAllTails(t *testing.T) { 25 | 26 | got := SumAllTails([]int{1, 2}, []int{0, 9}) 27 | want := []int{2, 9} 28 | 29 | if !reflect.DeepEqual(got, want) { 30 | t.Errorf("got %v want %v", got, want) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /arrays/v7/sum.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // Sum calculates the total from a slice of numbers. 4 | func Sum(numbers []int) int { 5 | sum := 0 6 | for _, number := range numbers { 7 | sum += number 8 | } 9 | return sum 10 | } 11 | 12 | // SumAllTails calculates the sums of all but the first number given a collection of slices. 13 | func SumAllTails(numbersToSum ...[]int) []int { 14 | var sums []int 15 | for _, numbers := range numbersToSum { 16 | if len(numbers) == 0 { 17 | sums = append(sums, 0) 18 | } else { 19 | tail := numbers[1:] 20 | sums = append(sums, Sum(tail)) 21 | } 22 | } 23 | 24 | return sums 25 | } 26 | -------------------------------------------------------------------------------- /arrays/v7/sum_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestSum(t *testing.T) { 9 | 10 | t.Run("collections of any size", func(t *testing.T) { 11 | 12 | numbers := []int{1, 2, 3} 13 | 14 | got := Sum(numbers) 15 | want := 6 16 | 17 | if got != want { 18 | t.Errorf("got %d want %d given, %v", got, want, numbers) 19 | } 20 | }) 21 | 22 | } 23 | 24 | func TestSumAllTails(t *testing.T) { 25 | 26 | checkSums := func(t *testing.T, got, want []int) { 27 | if !reflect.DeepEqual(got, want) { 28 | t.Errorf("got %v want %v", got, want) 29 | } 30 | } 31 | 32 | t.Run("make the sums of tails of", func(t *testing.T) { 33 | got := SumAllTails([]int{1, 2}, []int{0, 9}) 34 | want := []int{2, 9} 35 | checkSums(t, got, want) 36 | }) 37 | 38 | t.Run("safely sum empty slices", func(t *testing.T) { 39 | got := SumAllTails([]int{}, []int{3, 4, 5}) 40 | want := []int{0, 9} 41 | checkSums(t, got, want) 42 | }) 43 | 44 | } 45 | -------------------------------------------------------------------------------- /arrays/v8/assert.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | func AssertEqual[T comparable](t *testing.T, got, want T) { 6 | t.Helper() 7 | if got != want { 8 | t.Errorf("got %v, want %v", got, want) 9 | } 10 | } 11 | 12 | func AssertNotEqual[T comparable](t *testing.T, got, want T) { 13 | t.Helper() 14 | if got == want { 15 | t.Errorf("didn't want %v", got) 16 | } 17 | } 18 | 19 | func AssertTrue(t *testing.T, got bool) { 20 | t.Helper() 21 | if !got { 22 | t.Errorf("got %v, want true", got) 23 | } 24 | } 25 | 26 | func AssertFalse(t *testing.T, got bool) { 27 | t.Helper() 28 | if got { 29 | t.Errorf("got %v, want false", got) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /arrays/v8/bad_bank.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Transaction struct { 4 | From string 5 | To string 6 | Sum float64 7 | } 8 | 9 | func NewTransaction(from, to Account, sum float64) Transaction { 10 | return Transaction{From: from.Name, To: to.Name, Sum: sum} 11 | } 12 | 13 | type Account struct { 14 | Name string 15 | Balance float64 16 | } 17 | 18 | func NewBalanceFor(account Account, transactions []Transaction) Account { 19 | return Reduce( 20 | transactions, 21 | applyTransaction, 22 | account, 23 | ) 24 | } 25 | 26 | func applyTransaction(a Account, transaction Transaction) Account { 27 | if transaction.From == a.Name { 28 | a.Balance -= transaction.Sum 29 | } 30 | if transaction.To == a.Name { 31 | a.Balance += transaction.Sum 32 | } 33 | return a 34 | } 35 | -------------------------------------------------------------------------------- /arrays/v8/bad_bank_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | func TestBadBank(t *testing.T) { 6 | var ( 7 | riya = Account{Name: "Riya", Balance: 100} 8 | chris = Account{Name: "Chris", Balance: 75} 9 | adil = Account{Name: "Adil", Balance: 200} 10 | 11 | transactions = []Transaction{ 12 | NewTransaction(chris, riya, 100), 13 | NewTransaction(adil, chris, 25), 14 | } 15 | ) 16 | 17 | newBalanceFor := func(account Account) float64 { 18 | return NewBalanceFor(account, transactions).Balance 19 | } 20 | 21 | AssertEqual(t, newBalanceFor(riya), 200) 22 | AssertEqual(t, newBalanceFor(chris), 0) 23 | AssertEqual(t, newBalanceFor(adil), 175) 24 | } 25 | -------------------------------------------------------------------------------- /arrays/v8/collection_fun.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func Find[A any](items []A, predicate func(A) bool) (value A, found bool) { 4 | for _, v := range items { 5 | if predicate(v) { 6 | return v, true 7 | } 8 | } 9 | return 10 | } 11 | 12 | func Reduce[A, B any](collection []A, f func(B, A) B, initialValue B) B { 13 | var result = initialValue 14 | for _, x := range collection { 15 | result = f(result, x) 16 | } 17 | return result 18 | } 19 | -------------------------------------------------------------------------------- /arrays/v8/sum.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // Sum calculates the total from a slice of numbers. 4 | func Sum(numbers []int) int { 5 | add := func(acc, x int) int { return acc + x } 6 | return Reduce(numbers, add, 0) 7 | } 8 | 9 | // SumAllTails calculates the sums of all but the first number given a collection of slices. 10 | func SumAllTails(numbers ...[]int) []int { 11 | sumTail := func(acc, x []int) []int { 12 | if len(x) == 0 { 13 | return append(acc, 0) 14 | } else { 15 | tail := x[1:] 16 | return append(acc, Sum(tail)) 17 | } 18 | } 19 | 20 | return Reduce(numbers, sumTail, []int{}) 21 | } 22 | -------------------------------------------------------------------------------- /blogrenderer/post.go: -------------------------------------------------------------------------------- 1 | package blogrenderer 2 | 3 | import "strings" 4 | 5 | // Post is a representation of a post 6 | type Post struct { 7 | Title, Description, Body string 8 | Tags []string 9 | } 10 | 11 | // SanitisedTitle returns the title of the post with spaces replaced by dashes for pleasant URLs 12 | func (p Post) SanitisedTitle() string { 13 | return strings.ToLower(strings.Replace(p.Title, " ", "-", -1)) 14 | } 15 | -------------------------------------------------------------------------------- /blogrenderer/renderer_test.TestRender.it_renders_an_index_of_posts.approved.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |{{.Description}}
5 | 6 | Tags: