[
  {
    "path": ".editorconfig",
    "content": "# Top-most EditorConfig file\nroot = true\n\n# Every file should according to these default configurations if not specified\n[*]\n# Use UNIX-style line endings\nend_of_line = LF\n# Use utf-8 file encoding\ncharset = utf-8\n# 4 space indent\nindent_style = space\nindent_size = 4\n# Ensure file ends with a newline when saving(prevent `no newline at EOF`)\ninsert_final_newline = true\n# Remove any whitespace characters preceding newline characters\ntrim_trailing_whitespace = true\n\n# For YAML\n[*.{yml,yaml}]\nindent_size = 2\n\n# For Go files\n[*.go]\n# `gofmt` uses tabs for indentation\nindent_style = tab\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: [quii]\n"
  },
  {
    "path": ".github/workflows/go.yml",
    "content": "name: Go\n\non:\n  push:\n    branches: [ main ]\n  pull_request:\n    branches: [ main ]\n  release:\n    types: [ published ]\n\njobs:\n\n  build:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v4\n\n    - name: Set up Go\n      uses: actions/setup-go@v5\n      with:\n        go-version: 1.24\n\n    - name: Build\n      run: ./build.sh\n\n    - name: Build books\n      run: ./build.books.sh\n\n    - name: Get release\n      id: get_release\n      if: github.event_name == 'release' && github.event.action == 'published'\n      uses: bruceadams/get-release@v1.3.2\n      env:\n        GITHUB_TOKEN: ${{ github.token }}\n\n    - name: Attach generated epub upon release publish\n      if: github.event_name == 'release' && github.event.action == 'published'\n      with:\n        upload_url: ${{ steps.get_release.outputs.upload_url }}\n        asset_path: ./learn-go-with-tests.epub\n        asset_name: learn-go-with-tests.epub\n        asset_content_type: application/epub+zip\n      uses: actions/upload-release-asset@v1.0.2\n      env:\n        GITHUB_TOKEN: ${{ github.token }}\n\n    - name: Attach generated PDF upon release publish\n      if: github.event_name == 'release' && github.event.action == 'published'\n      with:\n        upload_url: ${{ steps.get_release.outputs.upload_url }}\n        asset_path: ./learn-go-with-tests.pdf\n        asset_name: learn-go-with-tests.pdf\n        asset_content_type: application/pdf\n      uses: actions/upload-release-asset@v1.0.2\n      env:\n        GITHUB_TOKEN: ${{ github.token }}\n\n"
  },
  {
    "path": ".gitignore",
    "content": ".idea/\n*.iml\n\n# Book build output\n_book/\n\n# eBook build output\n*.epub\n*.mobi\n*.pdf\n\n# templated files\nmeta.tex\n\ngame.db.json\n.DS_Store\n"
  },
  {
    "path": ".mdlrc",
    "content": "git_recurse true\n\nrules \"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\"\n\n# exclude rules\nexclude \"MD013\" # Line length\nexclude \"MD022\" # Headers should be surrounded by blank lines\nexclude \"MD024\" # Multiple headers with the same content\nexclude \"MD026\" # Trailing punctuation in header\nexclude \"MD027\" # Multiple spaces after blockquote symbol\nexclude \"MD029\" # Ordered list item prefix\nexclude \"MD031\" # Fenced code blocks should be surrounded by blank lines\nexclude \"MD032\" # Lists should be surrounded by blank lines\nexclude \"MD033\" # Inline HTML\nexclude \"MD034\" # Bare URL used\nexclude \"MD036\" # Emphasis used instead of a header\nexclude \"MD039\" # Spaces inside link text\n"
  },
  {
    "path": "LICENSE.md",
    "content": "MIT License\n\nCopyright (c) [2025] [Christopher James]\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Learn Go with Tests\n\n<p align=\"center\">\n  <img src=\"red-green-blue-gophers-smaller.png\" />\n</p>\n\n[Art by Denise](https://twitter.com/deniseyu21)\n\n[![Go Report Card](https://goreportcard.com/badge/github.com/quii/learn-go-with-tests)](https://goreportcard.com/report/github.com/quii/learn-go-with-tests)\n\n## Formats\n\n- [Gitbook](https://quii.gitbook.io/learn-go-with-tests)\n- [EPUB or PDF](https://github.com/quii/learn-go-with-tests/releases)\n\n## Translations\n\n- [中文](https://studygolang.gitbook.io/learn-go-with-tests)\n- [Português](https://larien.gitbook.io/aprenda-go-com-testes/)\n- [日本語](https://andmorefine.gitbook.io/learn-go-with-tests/)\n- [Français](https://goosegeesejeez.gitbook.io/apprendre-go-par-les-tests)\n- [한국어](https://miryang.gitbook.io/learn-go-with-tests/)\n- [Türkçe](https://halilkocaoz.gitbook.io/go-programlama-dilini-ogren/)\n- [فارسی](https://go-yaad-begir.gitbook.io/go-ba-test/)\n- [Nederlands](https://bobkosse.gitbook.io/leer-go-met-tests/)\n\n## Support me\n\nI am proud to offer this resource for free, but if you wish to give some appreciation:\n\n- [Tweet me @quii](https://twitter.com/quii)\n- <a rel=\"me\" href=\"https://mastodon.cloud/@quii\">Mastodon</a>\n- [Buy me a coffee :coffee:](https://www.buymeacoffee.com/quii)\n- [Sponsor me on GitHub](https://github.com/sponsors/quii)\n\n## Why\n\n* Explore the Go language by writing tests\n* **Get a grounding with TDD**. Go is a good language for learning TDD because it is a simple language to learn and testing is built-in\n* Be confident that you'll be able to start writing robust, well-tested systems in Go\n* [Watch a video, or read about why unit testing and TDD is important](why.md)\n\n## Table of contents\n\n### Go fundamentals\n\n1. [Install Go](install-go.md) - Set up environment for productivity.\n2. [Hello, world](hello-world.md) - Declaring variables, constants, if/else statements, switch, write your first go program and write your first test. Sub-test syntax and closures.\n3. [Integers](integers.md) - Further Explore function declaration syntax and learn new ways to improve the documentation of your code.\n4. [Iteration](iteration.md) - Learn about `for` and benchmarking.\n5. [Arrays and slices](arrays-and-slices.md) - Learn about arrays, slices, `len`, varargs, `range` and test coverage.\n6. [Structs, methods & interfaces](structs-methods-and-interfaces.md) - Learn about `struct`, methods, `interface` and table driven tests.\n7. [Pointers & errors](pointers-and-errors.md) - Learn about pointers and errors.\n8. [Maps](maps.md) - Learn about storing values in the map data structure.\n9. [Dependency Injection](dependency-injection.md) - Learn about dependency injection, how it relates to using interfaces and a primer on io.\n10. [Mocking](mocking.md) - Take some existing untested code and use DI with mocking to test it.\n11. [Concurrency](concurrency.md) - Learn how to write concurrent code to make your software faster.\n12. [Select](select.md) - Learn how to synchronise asynchronous processes elegantly.\n13. [Reflection](reflection.md) - Learn about reflection\n14. [Sync](sync.md) - Learn some functionality from the sync package including `WaitGroup` and `Mutex`\n15. [Context](context.md) - Use the context package to manage and cancel long-running processes\n16. [Intro to property based tests](roman-numerals.md) - Practice some TDD with the Roman Numerals kata and get a brief intro to property based tests\n17. [Maths](math.md) - Use the `math` package to draw an SVG clock\n18. [Reading files](reading-files.md) - Read files and process them\n19. [Templating](html-templates.md) - Use Go's html/template package to render html from data, and also learn about approval testing\n20. [Generics](generics.md) - Learn how to write functions that take generic arguments and make your own generic data-structure\n21. [Revisiting arrays and slices with generics](revisiting-arrays-and-slices-with-generics.md) - Generics are very useful when working with collections. Learn how to write your own `Reduce` function and tidy up some common patterns.\n\n### Build an application\n\nNow that you have hopefully digested the _Go Fundamentals_ section you have a solid grounding of a majority of Go's language features and how to do TDD.\n\nThis next section will involve building an application.\n\nEach chapter will iterate on the previous one, expanding the application's functionality as our product owner dictates.\n\nNew concepts will be introduced to help facilitate writing great code but most of the new material will be learning what can be accomplished from Go's standard library.\n\nBy the end of this, you should have a strong grasp as to how to iteratively write an application in Go, backed by tests.\n\n* [HTTP server](http-server.md) - We will create an application which listens to HTTP requests and responds to them.\n* [JSON, routing and embedding](json.md) - We will make our endpoints return JSON and explore how to do routing.\n* [IO and sorting](io.md) - We will persist and read our data from disk and we'll cover sorting data.\n* [Command line & project structure](command-line.md) - Support multiple applications from one code base and read input from command line.\n* [Time](time.md) - using the `time` package to schedule activities.\n* [WebSockets](websockets.md) - learn how to write and test a server that uses WebSockets.\n\n### Testing fundamentals\n\nCovering other subjects around testing.\n\n* [Introduction to acceptance tests](intro-to-acceptance-tests.md) - Learn how to write acceptance tests for your code, with a real-world example for gracefully shutting down a HTTP server\n* [Scaling acceptance tests](scaling-acceptance-tests.md) - Learn techniques to manage the complexity of writing acceptance tests for non-trivial systems.\n* [Working without mocks, stubs and spies](working-without-mocks.md) - Learn about how to use fakes and contracts to create more realistic and maintainable tests.\n* [Refactoring Checklist](refactoring-checklist.md) - Some discussion on what refactoring is, and some basic tips on how to do it.\n\n### Questions and answers\n\nI often run in to questions on the internets like\n\n> How do I test my amazing function that does x, y and z\n\nIf you have such a question raise it as an issue on github and I'll try and find time to write a short chapter to tackle the issue. I feel like content like this is valuable as it is tackling people's _real_ questions around testing.\n\n* [OS exec](os-exec.md) - An example of how we can reach out to the OS to execute commands to fetch data and keep our business logic testable/\n* [Error types](error-types.md) - Example of creating your own error types to improve your tests and make your code easier to work with.\n* [Context-aware Reader](context-aware-reader.md) - Learn how to TDD augmenting `io.Reader` with cancellation. Based on [Context-aware io.Reader for Go](https://pace.dev/blog/2020/02/03/context-aware-ioreader-for-golang-by-mat-ryer)\n* [Revisiting HTTP Handlers](http-handlers-revisited.md) - Testing HTTP handlers seems to be the bane of many a developer's existence. This chapter explores the issues around designing handlers correctly.\n\n### Meta / Discussion\n\n* [Why unit tests and how to make them work for you](why.md) - Watch a video, or read about why unit testing and TDD is important\n* [Anti-patterns](anti-patterns.md) - A short chapter on TDD and unit testing anti-patterns\n\n## Contributing\n\n* _This project is work in progress_ If you would like to contribute, please do get in touch.\n* Read [contributing.md](https://github.com/quii/learn-go-with-tests/tree/842f4f24d1f1c20ba3bb23cbc376c7ca6f7ca79a/contributing.md) for guidelines\n* Any ideas? Create an issue\n\n## Background\n\nI have some experience introducing Go to development teams and have tried different approaches as to how to grow a team from some people curious about Go into highly effective writers of Go systems.\n\n### What didn't work\n\n#### Read _the_ book\n\nAn approach we tried was to take [the blue book](https://www.amazon.co.uk/Programming-Language-Addison-Wesley-Professional-Computing/dp/0134190440) and every week discuss the next chapter along with the exercises.\n\nI love this book but it requires a high level of commitment. The book is very detailed in explaining concepts, which is obviously great but it means that the progress is slow and steady - this is not for everyone.\n\nI found that whilst a small number of people would read chapter X and do the exercises, many people didn't.\n\n#### Solve some problems\n\nKatas are fun but they are usually limited in their scope for learning a language; you're unlikely to use goroutines to solve a kata.\n\nAnother problem is when you have varying levels of enthusiasm. Some people just learn way more of the language than others and when demonstrating what they have done end up confusing people with features the others are not familiar with.\n\nThis ends up making the learning feel quite _unstructured_ and _ad hoc_.\n\n### What did work\n\nBy far the most effective way was by slowly introducing the fundamentals of the language by reading through [go by example](https://gobyexample.com/), exploring them with examples and discussing them as a group. This was a more interactive approach than \"read chapter x for homework\".\n\nOver time the team gained a solid foundation of the _grammar_ of the language so we could then start to build systems.\n\nThis to me seems analogous to practicing scales when trying to learn guitar.\n\nIt doesn't matter how artistic you think you are, you are unlikely to write good music without understanding the fundamentals and practicing the mechanics.\n\n### What works for me\n\nWhen _I_ learn a new programming language I usually start by messing around in a REPL but eventually, I need more structure.\n\nWhat I like to do is explore concepts and then solidify the ideas with tests. Tests verify the code I write is correct and documents the feature I have learned.\n\nTaking my experience of learning with a group and my own personal way I am going to try and create something that hopefully proves useful to other teams. Learning the fundamentals by writing small tests so that you can then take your existing software design skills and ship some great systems.\n\n## Who this is for\n\n* People who are interested in picking up Go.\n* People who already know some Go, but want to explore testing with TDD.\n\n## What you'll need\n\n* A computer!\n* [Installed Go](https://golang.org/)\n* A text editor\n* Some experience with programming. Understanding of concepts like `if`, variables, functions etc.\n* Comfortable using the terminal\n\n## Feedback\n\n* Add issues/submit PRs [here](https://github.com/quii/learn-go-with-tests) or [tweet me @quii](https://twitter.com/quii)\n\n[MIT license](LICENSE.md)\n\n[Logo is by egonelbre](https://github.com/egonelbre) What a star!\n"
  },
  {
    "path": "SUMMARY.md",
    "content": "# Table of contents\n\n* [Learn Go with Tests](gb-readme.md)\n\n## Go fundamentals\n\n* [Install Go](install-go.md)\n* [Hello, World](hello-world.md)\n* [Integers](integers.md)\n* [Iteration](iteration.md)\n* [Arrays and slices](arrays-and-slices.md)\n* [Structs, methods & interfaces](structs-methods-and-interfaces.md)\n* [Pointers & errors](pointers-and-errors.md)\n* [Maps](maps.md)\n* [Dependency Injection](dependency-injection.md)\n* [Mocking](mocking.md)\n* [Concurrency](concurrency.md)\n* [Select](select.md)\n* [Reflection](reflection.md)\n* [Sync](sync.md)\n* [Context](context.md)\n* [Intro to property based tests](roman-numerals.md)\n* [Maths](math.md)\n* [Reading files](reading-files.md)\n* [Templating](html-templates.md)\n* [Generics](generics.md)\n* [Revisiting arrays and slices with generics](revisiting-arrays-and-slices-with-generics.md)\n\n## Testing fundamentals\n\n* [Introduction to acceptance tests](intro-to-acceptance-tests.md)\n* [Scaling acceptance tests](scaling-acceptance-tests.md)\n* [Working without mocks](working-without-mocks.md)\n* [Refactoring Checklist](refactoring-checklist.md)\n\n## Build an application\n\n* [Intro](app-intro.md)\n* [HTTP server](http-server.md)\n* [JSON, routing and embedding](json.md)\n* [IO and sorting](io.md)\n* [Command line & package structure](command-line.md)\n* [Time](time.md)\n* [WebSockets](websockets.md)\n\n## Questions and answers\n\n* [OS Exec](os-exec.md)\n* [Error types](error-types.md)\n* [Context-aware Reader](context-aware-reader.md)\n* [Revisiting HTTP Handlers](http-handlers-revisited.md)\n\n## Meta\n\n* [Why unit tests and how to make them work for you](why.md)\n* [Anti-patterns](anti-patterns.md)\n* [Contributing](contributing.md)\n* [Chapter Template](template.md)\n"
  },
  {
    "path": "anti-patterns.md",
    "content": "# TDD Anti-patterns\n\nFrom time to time it's necessary to review your TDD techniques and remind yourself of behaviours to avoid.\n\nThe TDD process is conceptually simple to follow, but as you do it you'll find it challenging your design skills. **Don't mistake this for TDD being hard, it's design that's hard!**\n\nThis chapter lists a number of TDD and testing anti-patterns, and how to remedy them.\n\n## Not doing TDD at all\n\nOf course, it is possible to write great software without TDD but, a lot of problems I've seen with the design of code and the quality of tests would be very difficult to arrive at if a disciplined approach to TDD had been used.\n\nOne of the strengths of TDD is that it gives you a formal process to break down problems, understand what you're trying to achieve (red), get it done (green), then have a good think about how to make it right (blue/refactor).\n\nWithout this, the process is often ad-hoc and loose, which _can_ make engineering more difficult than it _could_ be.\n\n## Misunderstanding the constraints of the refactoring step\n\nI have been in a number of workshops, mobbing or pairing sessions where someone has made a test pass and is in the refactoring stage. After some thought, they think it would be good to abstract away some code into a new struct; a budding pedant yells:\n\n> You're not allowed to do this! You should write a test for this first, we're doing TDD!\n\nThis seems to be a common misunderstanding. **You can do whatever you like to the code when the tests are green**, the only thing you're not allowed to do is **add or change behaviour**.\n\nThe point of these tests are to give you the _freedom to refactor_, find the right abstractions and make the code easier to change and understand.\n\n## Having tests that won't fail (or, evergreen tests)\n\nIt's astonishing how often this comes up. You start debugging or changing some tests and realise: there are no scenarios where this test can fail. Or at least, it won't fail in the way the test is _supposed_ to be protecting against.\n\nThis is _next to impossible_ with TDD if you're following **the first step**,\n\n> Write a test, see it fail\n\nThis is almost always done when developers write tests _after_ code is written, and/or chasing test coverage rather than creating a useful test suite.\n\n## Useless assertions\n\nEver worked on a system, and you've broken a test, then you see this?\n\n> `false was not equal to true`\n\nI know that false is not equal to true. This is not a helpful message; it doesn't tell me what I've broken. This is a symptom of not following the TDD process and not reading the failure error message.\n\nGoing back to the drawing board,\n\n> Write a test, see it fail (and don't be ashamed of the error message)\n\n## Asserting on irrelevant detail\n\nAn example of this is making an assertion on a complex object, when in practice all you care about in the test is the value of one of the fields.\n\n```go\n// not this, now your test is tightly coupled to the whole object\nif !cmp.Equal(complexObject, want) {\n\tt.Error(\"got %+v, want %+v\", complexObject, want)\n}\n\n// be specific, and loosen the coupling\ngot := complexObject.fieldYouCareAboutForThisTest\nif got != want {\n\tt.Error(\"got %q, want %q\", got, want)\n}\n```\n\nAdditional assertions not only make your test more difficult to read by creating 'noise' in your documentation, but also needlessly couples the test with data it doesn't care about. This means if you happen to change the fields for your object, or the way they behave you may get unexpected compilation problems or failures with your tests.\n\nThis is an example of not following the red stage strictly enough.\n\n- Letting an existing design influence how you write your test **rather than thinking of the desired behaviour**\n- Not giving enough consideration to the failing test's error message\n\n## Lots of assertions within a single scenario for unit tests\n\nMany assertions can make tests difficult to read and challenging to debug when they fail.\n\nThey often creep in gradually, especially if test setup is complicated because you're reluctant to replicate the same horrible setup to assert on something else. Instead of this you should fix the problems in your design which are making it difficult to assert on new things.\n\nA helpful rule of thumb is to aim to make one assertion per test. In Go, take advantage of subtests to clearly delineate between assertions on the occasions where you need to. This is also a handy technique to separate assertions on behaviour vs implementation detail.\n\nFor other tests where setup or execution time may be a constraint (e.g an acceptance test driving a web browser), you need to weigh up the pros and cons of slightly trickier to debug tests against test execution time.\n\n## Not listening to your tests\n\n[Dave Farley in his video \"When TDD goes wrong\"](https://www.youtube.com/watch?v=UWtEVKVPBQ0&feature=youtu.be) points out,\n\n> TDD gives you the fastest feedback possible on your design\n\nFrom my own experience, a lot of developers are trying to practice TDD but frequently ignore the signals coming back to them from the TDD process. So they're still stuck with fragile, annoying systems, with a poor test suite.\n\nSimply put, if testing your code is difficult, then _using_ your code is difficult too. Treat your tests as the first user of your code and then you'll see if your code is pleasant to work with or not.\n\nI've emphasised this a lot in the book, and I'll say it again **listen to your tests**.\n\n### Excessive setup, too many test doubles, etc.\n\nEver looked at a test with 20, 50, 100, 200 lines of setup code before anything interesting in the test happens? Do you then have to change the code and revisit the mess and wish you had a different career?\n\nWhat are the signals here? _Listen_, complicated tests `==` complicated code. Why is your code complicated? Does it have to be?\n\n- When you have lots of test doubles in your tests, that means the code you're testing has lots of dependencies - which means your design needs work.\n- If your test is reliant on setting up various interactions with mocks, that means your code is making lots of interactions with its dependencies. Ask yourself whether these interactions could be simpler.\n\n#### Leaky interfaces\n\nIf you have declared an `interface` that has many methods, that points to a leaky abstraction. Think about how you could define that collaboration with a more consolidated set of methods, ideally one.\n\n#### Interface pollution\n\nAs a Go proverb says, *the bigger the interface, the weaker the abstraction*. If you expose a huge interface to the users of your package, you force them to create in their tests a stub/mock that matches the entire API, providing an implementation also for methods they do not use (sometimes, they just panic to make clear that they should not be used). This situation is an anti-pattern known as [interface pollution](https://rakyll.org/interface-pollution/) and this is the reason why the standard library offers you just tiny little interfaces. \n\nInstead, you should expose from your package a bare struct with all relevant methods exported, leaving to the clients of your API the freedom to declare their own interfaces abstracting over the subset of the methods they need: e.g [go-redis](https://github.com/redis/go-redis) exposes a struct (`redis.Client`) to the API clients.\n\nGenerally speaking, you should expose an interface to the clients only when:\n- the interface consists of a small and coherent set of functions.\n- the interface and its implementation need to be decoupled (e.g. because users can choose among multiple implementations or they need to mock an external dependency).\n\n#### Think about the types of test doubles you use\n\n- Mocks are sometimes helpful, but they're extremely powerful and therefore easy to misuse. Try giving yourself the constraint of using stubs instead.\n- Verifying implementation detail with spies is sometimes helpful, but try to avoid it. Remember your implementation detail is usually not important, and you don't want your tests coupled to them if possible. Look to couple your tests to **useful behaviour rather than incidental details**.\n- [Read my posts on naming test doubles](https://quii.dev/Start_naming_your_test_doubles_correctly) if the taxonomy of test doubles is a little unclear\n\n#### Consolidate dependencies\n\nHere is some code for a `http.HandlerFunc` to handle new user registrations for a website.\n\n```go\ntype User struct {\n\t// Some user fields\n}\n\ntype UserStore interface {\n\tCheckEmailExists(email string) (bool, error)\n\tStoreUser(newUser User) error\n}\n\ntype Emailer interface {\n\tSendEmail(to User, body string, subject string) error\n}\n\nfunc NewRegistrationHandler(userStore UserStore, emailer Emailer) http.HandlerFunc {\n\treturn func(writer http.ResponseWriter, request *http.Request) {\n\t\t// extract out the user from the request body (handle error)\n\t\t// check user exists (handle duplicates, errors)\n\t\t// store user (handle errors)\n\t\t// compose and send confirmation email (handle error)\n\t\t// if we got this far, return 2xx response\n\t}\n}\n```\n\nAt first pass it's reasonable to say the design isn't so bad. It only has 2 dependencies!\n\nRe-evaluate the design by considering the handler's responsibilities:\n\n- Parse the request body into a `User` :white_check_mark:\n- Use `UserStore` to check if the user exists :question:\n- Use `UserStore` to store the user :question:\n- Compose an email :question:\n- Use `Emailer` to send the email :question:\n- Return an appropriate http response, depending on success, errors, etc :white_check_mark:\n\nTo exercise this code, you're going to have to write many tests with varying degrees of test double setups, spies, etc\n\n- What if the requirements expand? Translations for the emails? Sending an SMS confirmation too? Does it make sense to you that you have to change a HTTP handler to accommodate this change?\n- Does it feel right that the important rule of \"we should send an email\" resides within a HTTP handler?\n    - Why do you have to go through the ceremony of creating HTTP requests and reading responses to verify that rule?\n\n**Listen to your tests**. Writing tests for this code in a TDD fashion should quickly make you feel uncomfortable (or at least, make the lazy developer in you be annoyed). If it feels painful, stop and think.\n\nWhat if the design was like this instead?\n\n```go\ntype UserService interface {\n\tRegister(newUser User) error\n}\n\nfunc NewRegistrationHandler(userService UserService) http.HandlerFunc {\n\treturn func(writer http.ResponseWriter, request *http.Request) {\n\t\t// parse user\n\t\t// register user\n\t\t// check error, send response\n\t}\n}\n```\n\n- Simple to test the handler ✅\n- Changes to the rules around registration are isolated away from HTTP, so they are also simpler to test ✅\n\n## Violating encapsulation\n\nEncapsulation is very important. There's a reason we don't make everything in a package exported (or public). We want coherent APIs with a small surface area to avoid tight coupling.\n\nPeople will sometimes be tempted to make a function or method public in order to test something. By doing this you make your design worse and send confusing messages to maintainers and users of your code.\n\nA result of this can be developers trying to debug a test and then eventually realising the function being tested is _only called from tests_. Which is obviously **a terrible outcome, and a waste of time**.\n\nIn Go, consider your default position for writing tests as _from the perspective of a consumer of your package_. You can make this a compile-time constraint by having your tests live in a test package e.g `package gocoin_test`. If you do this, you'll only have access to the exported members of the package so it won't be possible to couple yourself to implementation detail.\n\n## Complicated table tests\n\nTable tests are a great way of exercising a number of different scenarios when the test setup is the same, and you only wish to vary the inputs.\n\n_But_ they can be messy to read and understand when you try to shoehorn other kinds of tests under the name of having one, glorious table.\n\n```go\ncases := []struct {\n\tX                int\n\tY                int\n\tZ                int\n\terr              error\n\tIsFullMoon       bool\n\tIsLeapYear       bool\n\tAtWarWithEurasia bool\n}{}\n```\n\n**Don't be afraid to break out of your table and write new tests** rather than adding new fields and booleans to the table `struct`.\n\nA thing to bear in mind when writing software is,\n\n> [Simple is not easy](https://www.infoq.com/presentations/Simple-Made-Easy/)\n\n\"Just\" adding a field to a table might be easy, but it can make things far from simple.\n\n## Summary\n\nMost problems with unit tests can normally be traced to:\n\n- Developers not following the TDD process\n- Poor design\n\nSo, learn about good software design!\n\nThe good news is TDD can help you _improve your design skills_ because as stated in the beginning:\n\n**TDD's main purpose is to provide feedback on your design.** For the millionth time, listen to your tests, they are reflecting your design back at you.\n\nBe honest about the quality of your tests by listening to the feedback they give you, and you'll become a better developer for it.\n"
  },
  {
    "path": "app-intro.md",
    "content": "# Build an application\n\nNow that you have hopefully digested the _Go Fundamentals_ section you have a solid grounding of a majority of Go's language features and how to do TDD.\n\nThis next section will involve building an application.\n\nEach chapter will iterate on the previous one, expanding the application's functionality as our product owner dictates.\n\nNew concepts will be introduced to help facilitate writing great code but most of the new material will be learning what can be accomplished from Go's standard library.\n\nBy the end of this, you should have a strong grasp as to how to iteratively write an application in Go, backed by tests.\n\n- [HTTP server](http-server.md) - We will create an application which listens to HTTP requests and responds to them.\n- [JSON, routing and embedding](json.md) - We will make our endpoints return JSON and explore how to do routing.\n- [IO and sorting](io.md) - We will persist and read our data from disk and we'll cover sorting data.\n- [Command line & project structure](command-line.md) - Support multiple applications from one code base and read input from command line.\n- [Time](time.md) - using the `time` package to schedule activities.\n- [WebSockets](websockets.md) - learn how to write and test a server that uses WebSockets.\n"
  },
  {
    "path": "arrays/v1/sum.go",
    "content": "package main\n\n// Sum calculates the total from an array of numbers.\nfunc Sum(numbers [5]int) int {\n\tsum := 0\n\tfor i := 0; i < 5; i++ {\n\t\tsum += numbers[i]\n\t}\n\treturn sum\n}\n"
  },
  {
    "path": "arrays/v1/sum_test.go",
    "content": "package main\n\nimport \"testing\"\n\nfunc TestSum(t *testing.T) {\n\n\tnumbers := [5]int{1, 2, 3, 4, 5}\n\n\tgot := Sum(numbers)\n\twant := 15\n\n\tif want != got {\n\t\tt.Errorf(\"got %d want %d given, %v\", got, want, numbers)\n\t}\n}\n"
  },
  {
    "path": "arrays/v2/sum.go",
    "content": "package main\n\n// Sum calculates the total from an array of numbers.\nfunc Sum(numbers [5]int) int {\n\tsum := 0\n\tfor _, number := range numbers {\n\t\tsum += number\n\t}\n\treturn sum\n}\n"
  },
  {
    "path": "arrays/v2/sum_test.go",
    "content": "package main\n\nimport \"testing\"\n\nfunc TestSum(t *testing.T) {\n\n\tnumbers := [5]int{1, 2, 3, 4, 5}\n\n\tgot := Sum(numbers)\n\twant := 15\n\n\tif got != want {\n\t\tt.Errorf(\"got %d want %d given, %v\", got, want, numbers)\n\t}\n}\n"
  },
  {
    "path": "arrays/v3/sum.go",
    "content": "package main\n\n// Sum calculates the total from a slice of numbers.\nfunc Sum(numbers []int) int {\n\tsum := 0\n\tfor _, number := range numbers {\n\t\tsum += number\n\t}\n\treturn sum\n}\n"
  },
  {
    "path": "arrays/v3/sum_test.go",
    "content": "package main\n\nimport \"testing\"\n\nfunc TestSum(t *testing.T) {\n\n\tt.Run(\"collections of any size\", func(t *testing.T) {\n\n\t\tnumbers := []int{1, 2, 3}\n\n\t\tgot := Sum(numbers)\n\t\twant := 6\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %d want %d given, %v\", got, want, numbers)\n\t\t}\n\t})\n\n}\n"
  },
  {
    "path": "arrays/v4/sum.go",
    "content": "package main\n\n// Sum calculates the total from a slice of numbers.\nfunc Sum(numbers []int) int {\n\tsum := 0\n\tfor _, number := range numbers {\n\t\tsum += number\n\t}\n\treturn sum\n}\n\n// SumAll calculates the respective sums of every slice passed in.\nfunc SumAll(numbersToSum ...[]int) []int {\n\tlengthOfNumbers := len(numbersToSum)\n\tsums := make([]int, lengthOfNumbers)\n\n\tfor i, numbers := range numbersToSum {\n\t\tsums[i] = Sum(numbers)\n\t}\n\n\treturn sums\n}\n"
  },
  {
    "path": "arrays/v4/sum_test.go",
    "content": "package main\n\nimport (\n\t\"slices\"\n\t\"testing\"\n)\n\nfunc TestSum(t *testing.T) {\n\n\tt.Run(\"collections of any size\", func(t *testing.T) {\n\n\t\tnumbers := []int{1, 2, 3}\n\n\t\tgot := Sum(numbers)\n\t\twant := 6\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %d want %d given, %v\", got, want, numbers)\n\t\t}\n\t})\n}\n\nfunc TestSumAll(t *testing.T) {\n\n\tgot := SumAll([]int{1, 2}, []int{0, 9})\n\twant := []int{3, 9}\n\n\tif !slices.Equal(got, want) {\n\t\tt.Errorf(\"got %v want %v\", got, want)\n\t}\n}\n"
  },
  {
    "path": "arrays/v5/sum.go",
    "content": "package main\n\n// Sum calculates the total from a slice of numbers.\nfunc Sum(numbers []int) int {\n\tsum := 0\n\tfor _, number := range numbers {\n\t\tsum += number\n\t}\n\treturn sum\n}\n\n// SumAll calculates the respective sums of every slice passed in.\nfunc SumAll(numbersToSum ...[]int) []int {\n\tvar sums []int\n\tfor _, numbers := range numbersToSum {\n\t\tsums = append(sums, Sum(numbers))\n\t}\n\n\treturn sums\n}\n"
  },
  {
    "path": "arrays/v5/sum_test.go",
    "content": "package main\n\nimport (\n\t\"slices\"\n\t\"testing\"\n)\n\nfunc TestSum(t *testing.T) {\n\n\tt.Run(\"collections of any size\", func(t *testing.T) {\n\n\t\tnumbers := []int{1, 2, 3}\n\n\t\tgot := Sum(numbers)\n\t\twant := 6\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %d want %d given, %v\", got, want, numbers)\n\t\t}\n\t})\n\n}\n\nfunc TestSumAll(t *testing.T) {\n\n\tgot := SumAll([]int{1, 2}, []int{0, 9})\n\twant := []int{3, 9}\n\n\tif !slices.Equal(got, want) {\n\t\tt.Errorf(\"got %v want %v\", got, want)\n\t}\n}\n"
  },
  {
    "path": "arrays/v6/sum.go",
    "content": "package main\n\n// Sum calculates the total from a slice of numbers.\nfunc Sum(numbers []int) int {\n\tsum := 0\n\tfor _, number := range numbers {\n\t\tsum += number\n\t}\n\treturn sum\n}\n\n// SumAllTails calculates the respective sums of every slice passed in.\nfunc SumAllTails(numbersToSum ...[]int) []int {\n\tvar sums []int\n\tfor _, numbers := range numbersToSum {\n\t\ttail := numbers[1:]\n\t\tsums = append(sums, Sum(tail))\n\t}\n\n\treturn sums\n}\n"
  },
  {
    "path": "arrays/v6/sum_test.go",
    "content": "package main\n\nimport (\n\t\"slices\"\n\t\"testing\"\n)\n\nfunc TestSum(t *testing.T) {\n\n\tt.Run(\"collections of any size\", func(t *testing.T) {\n\n\t\tnumbers := []int{1, 2, 3}\n\n\t\tgot := Sum(numbers)\n\t\twant := 6\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %d want %d given, %v\", got, want, numbers)\n\t\t}\n\t})\n\n}\n\nfunc TestSumAllTails(t *testing.T) {\n\n\tgot := SumAllTails([]int{1, 2}, []int{0, 9})\n\twant := []int{2, 9}\n\n\tif !slices.Equal(got, want) {\n\t\tt.Errorf(\"got %v want %v\", got, want)\n\t}\n}\n"
  },
  {
    "path": "arrays/v7/sum.go",
    "content": "package main\n\n// Sum calculates the total from a slice of numbers.\nfunc Sum(numbers []int) int {\n\tsum := 0\n\tfor _, number := range numbers {\n\t\tsum += number\n\t}\n\treturn sum\n}\n\n// SumAllTails calculates the sums of all but the first number given a collection of slices.\nfunc SumAllTails(numbersToSum ...[]int) []int {\n\tvar sums []int\n\tfor _, numbers := range numbersToSum {\n\t\tif len(numbers) == 0 {\n\t\t\tsums = append(sums, 0)\n\t\t} else {\n\t\t\ttail := numbers[1:]\n\t\t\tsums = append(sums, Sum(tail))\n\t\t}\n\t}\n\n\treturn sums\n}\n"
  },
  {
    "path": "arrays/v7/sum_test.go",
    "content": "package main\n\nimport (\n\t\"slices\"\n\t\"testing\"\n)\n\nfunc TestSum(t *testing.T) {\n\n\tt.Run(\"collections of any size\", func(t *testing.T) {\n\n\t\tnumbers := []int{1, 2, 3}\n\n\t\tgot := Sum(numbers)\n\t\twant := 6\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %d want %d given, %v\", got, want, numbers)\n\t\t}\n\t})\n\n}\n\nfunc TestSumAllTails(t *testing.T) {\n\n\tcheckSums := func(t *testing.T, got, want []int) {\n\t\tif !slices.Equal(got, want) {\n\t\t\tt.Errorf(\"got %v want %v\", got, want)\n\t\t}\n\t}\n\n\tt.Run(\"make the sums of tails of\", func(t *testing.T) {\n\t\tgot := SumAllTails([]int{1, 2}, []int{0, 9})\n\t\twant := []int{2, 9}\n\t\tcheckSums(t, got, want)\n\t})\n\n\tt.Run(\"safely sum empty slices\", func(t *testing.T) {\n\t\tgot := SumAllTails([]int{}, []int{3, 4, 5})\n\t\twant := []int{0, 9}\n\t\tcheckSums(t, got, want)\n\t})\n\n}\n"
  },
  {
    "path": "arrays/v8/assert.go",
    "content": "package main\n\nimport \"testing\"\n\nfunc AssertEqual[T comparable](t *testing.T, got, want T) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"got %v, want %v\", got, want)\n\t}\n}\n\nfunc AssertNotEqual[T comparable](t *testing.T, got, want T) {\n\tt.Helper()\n\tif got == want {\n\t\tt.Errorf(\"didn't want %v\", got)\n\t}\n}\n\nfunc AssertTrue(t *testing.T, got bool) {\n\tt.Helper()\n\tif !got {\n\t\tt.Errorf(\"got %v, want true\", got)\n\t}\n}\n\nfunc AssertFalse(t *testing.T, got bool) {\n\tt.Helper()\n\tif got {\n\t\tt.Errorf(\"got %v, want false\", got)\n\t}\n}\n"
  },
  {
    "path": "arrays/v8/bad_bank.go",
    "content": "package main\n\ntype Transaction struct {\n\tFrom string\n\tTo   string\n\tSum  float64\n}\n\nfunc NewTransaction(from, to Account, sum float64) Transaction {\n\treturn Transaction{From: from.Name, To: to.Name, Sum: sum}\n}\n\ntype Account struct {\n\tName    string\n\tBalance float64\n}\n\nfunc NewBalanceFor(account Account, transactions []Transaction) Account {\n\treturn Reduce(\n\t\ttransactions,\n\t\tapplyTransaction,\n\t\taccount,\n\t)\n}\n\nfunc applyTransaction(a Account, transaction Transaction) Account {\n\tif transaction.From == a.Name {\n\t\ta.Balance -= transaction.Sum\n\t}\n\tif transaction.To == a.Name {\n\t\ta.Balance += transaction.Sum\n\t}\n\treturn a\n}\n"
  },
  {
    "path": "arrays/v8/bad_bank_test.go",
    "content": "package main\n\nimport \"testing\"\n\nfunc TestBadBank(t *testing.T) {\n\tvar (\n\t\triya  = Account{Name: \"Riya\", Balance: 100}\n\t\tchris = Account{Name: \"Chris\", Balance: 75}\n\t\tadil  = Account{Name: \"Adil\", Balance: 200}\n\n\t\ttransactions = []Transaction{\n\t\t\tNewTransaction(chris, riya, 100),\n\t\t\tNewTransaction(adil, chris, 25),\n\t\t}\n\t)\n\n\tnewBalanceFor := func(account Account) float64 {\n\t\treturn NewBalanceFor(account, transactions).Balance\n\t}\n\n\tAssertEqual(t, newBalanceFor(riya), 200)\n\tAssertEqual(t, newBalanceFor(chris), 0)\n\tAssertEqual(t, newBalanceFor(adil), 175)\n}\n"
  },
  {
    "path": "arrays/v8/collection_fun.go",
    "content": "package main\n\nfunc Find[A any](items []A, predicate func(A) bool) (value A, found bool) {\n\tfor _, v := range items {\n\t\tif predicate(v) {\n\t\t\treturn v, true\n\t\t}\n\t}\n\treturn\n}\n\nfunc Reduce[A, B any](collection []A, f func(B, A) B, initialValue B) B {\n\tvar result = initialValue\n\tfor _, x := range collection {\n\t\tresult = f(result, x)\n\t}\n\treturn result\n}\n"
  },
  {
    "path": "arrays/v8/sum.go",
    "content": "package main\n\n// Sum calculates the total from a slice of numbers.\nfunc Sum(numbers []int) int {\n\tadd := func(acc, x int) int { return acc + x }\n\treturn Reduce(numbers, add, 0)\n}\n\n// SumAllTails calculates the sums of all but the first number given a collection of slices.\nfunc SumAllTails(numbers ...[]int) []int {\n\tsumTail := func(acc, x []int) []int {\n\t\tif len(x) == 0 {\n\t\t\treturn append(acc, 0)\n\t\t} else {\n\t\t\ttail := x[1:]\n\t\t\treturn append(acc, Sum(tail))\n\t\t}\n\t}\n\n\treturn Reduce(numbers, sumTail, []int{})\n}\n"
  },
  {
    "path": "arrays/v8/sum_test.go",
    "content": "package main\n\nimport (\n\t\"slices\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestSum(t *testing.T) {\n\n\tt.Run(\"collections of any size\", func(t *testing.T) {\n\n\t\tnumbers := []int{1, 2, 3}\n\n\t\tgot := Sum(numbers)\n\t\twant := 6\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %d want %d given, %v\", got, want, numbers)\n\t\t}\n\t})\n\n}\n\nfunc TestSumAllTails(t *testing.T) {\n\n\tcheckSums := func(t *testing.T, got, want []int) {\n\t\tif !slices.Equal(got, want) {\n\t\t\tt.Errorf(\"got %v want %v\", got, want)\n\t\t}\n\t}\n\n\tt.Run(\"make the sums of tails of\", func(t *testing.T) {\n\t\tgot := SumAllTails([]int{1, 2}, []int{0, 9})\n\t\twant := []int{2, 9}\n\t\tcheckSums(t, got, want)\n\t})\n\n\tt.Run(\"safely sum empty slices\", func(t *testing.T) {\n\t\tgot := SumAllTails([]int{}, []int{3, 4, 5})\n\t\twant := []int{0, 9}\n\t\tcheckSums(t, got, want)\n\t})\n\n}\n\nfunc TestReduce(t *testing.T) {\n\tt.Run(\"multiplication of all elements\", func(t *testing.T) {\n\t\tmultiply := func(x, y int) int {\n\t\t\treturn x * y\n\t\t}\n\n\t\tAssertEqual(t, Reduce([]int{1, 2, 3}, multiply, 1), 6)\n\t})\n\n\tt.Run(\"concatenate strings\", func(t *testing.T) {\n\t\tconcatenate := func(x, y string) string {\n\t\t\treturn x + y\n\t\t}\n\n\t\tAssertEqual(t, Reduce([]string{\"a\", \"b\", \"c\"}, concatenate, \"\"), \"abc\")\n\t})\n}\n\nfunc TestFind(t *testing.T) {\n\tt.Run(\"find first even number\", func(t *testing.T) {\n\t\tnumbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}\n\n\t\tfirstEvenNumber, found := Find(numbers, func(x int) bool {\n\t\t\treturn x%2 == 0\n\t\t})\n\t\tAssertTrue(t, found)\n\t\tAssertEqual(t, firstEvenNumber, 2)\n\t})\n\n\ttype Person struct {\n\t\tName string\n\t}\n\n\tt.Run(\"Find the best programmer\", func(t *testing.T) {\n\t\tpeople := []Person{\n\t\t\tPerson{Name: \"Kent Beck\"},\n\t\t\tPerson{Name: \"Martin Fowler\"},\n\t\t\tPerson{Name: \"Chris James\"},\n\t\t}\n\n\t\tking, found := Find(people, func(p Person) bool {\n\t\t\treturn strings.Contains(p.Name, \"Chris\")\n\t\t})\n\n\t\tAssertTrue(t, found)\n\t\tAssertEqual(t, king, Person{Name: \"Chris James\"})\n\t})\n}\n"
  },
  {
    "path": "arrays-and-slices.md",
    "content": "# Arrays and slices\n\n**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/main/arrays)**\n\nArrays allow you to store multiple elements of the same type in a variable in\na particular order.\n\nWhen you have arrays, it is very common to have to iterate over them. So let's\nuse [our new-found knowledge of `for`](iteration.md) to make a `Sum` function. `Sum` will\ntake an array of numbers and return the total.\n\nLet's use our TDD skills\n\n## Write the test first\n\nCreate a new folder to work in. Create a new file called `sum_test.go` and insert the following:\n\n```go\npackage main\n\nimport \"testing\"\n\nfunc TestSum(t *testing.T) {\n\n\tnumbers := [5]int{1, 2, 3, 4, 5}\n\n\tgot := Sum(numbers)\n\twant := 15\n\n\tif got != want {\n\t\tt.Errorf(\"got %d want %d given, %v\", got, want, numbers)\n\t}\n}\n```\n\nArrays have a _fixed capacity_ which you define when you declare the variable.\nWe can initialize an array in two ways:\n\n* \\[N\\]type{value1, value2, ..., valueN} e.g. `numbers := [5]int{1, 2, 3, 4, 5}`\n* \\[...\\]type{value1, value2, ..., valueN} e.g. `numbers := [...]int{1, 2, 3, 4, 5}`\n\nIt is sometimes useful to also print the inputs to the function in the error message.\nHere, we are using the `%v` placeholder to print the \"default\" format, which works well for arrays.\n\n[Read more about the format strings](https://golang.org/pkg/fmt/)\n\n## Try to run the test\n\nIf you had initialized go mod with `go mod init main` you will be presented with an error\n`_testmain.go:13:2: cannot import \"main\"`. This is because according to common practice,\npackage main will only contain integration of other packages and not unit-testable code and\nhence Go will not allow you to import a package with name `main`.\n\nTo fix this, you can rename the main module in `go.mod` to any other name.\n\nOnce the above error is fixed, if you run `go test` the compiler will fail with the familiar\n`./sum_test.go:10:15: undefined: Sum` error. Now we can proceed with writing the actual method\nto be tested.\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\nIn `sum.go`\n\n```go\npackage main\n\nfunc Sum(numbers [5]int) int {\n\treturn 0\n}\n```\n\nYour test should now fail with _a clear error message_\n\n`sum_test.go:13: got 0 want 15 given, [1 2 3 4 5]`\n\n## Write enough code to make it pass\n\n```go\nfunc Sum(numbers [5]int) int {\n\tsum := 0\n\tfor i := 0; i < 5; i++ {\n\t\tsum += numbers[i]\n\t}\n\treturn sum\n}\n```\n\nTo get the value out of an array at a particular index, just use `array[index]`\nsyntax. In this case, we are using `for` to iterate 5 times to work through the\narray and add each item onto `sum`.\n\n## Refactor\n\nLet's introduce [`range`](https://gobyexample.com/range) to help clean up our code\n\n```go\nfunc Sum(numbers [5]int) int {\n\tsum := 0\n\tfor _, number := range numbers {\n\t\tsum += number\n\t}\n\treturn sum\n}\n```\n\n`range` lets you iterate over an array. On each iteration, `range` returns two values - the index and the value.\nWe are choosing to ignore the index value by using `_` [blank identifier](https://golang.org/doc/effective_go.html#blank).\n\n### Arrays and their type\n\nAn interesting property of arrays is that the size is encoded in its type. If you try\nto pass an `[4]int` into a function that expects `[5]int`, it won't compile.\nThey are different types so it's just the same as trying to pass a `string` into\na function that wants an `int`.\n\nYou may be thinking it's quite cumbersome that arrays have a fixed length, and most\nof the time you probably won't be using them!\n\nGo has _slices_ which do not encode the size of the collection and instead can\nhave any size.\n\nThe next requirement will be to sum collections of varying sizes.\n\n## Write the test first\n\nWe will now use the [slice type][slice] which allows us to have collections of\nany size. The syntax is very similar to arrays, you just omit the size when\ndeclaring them\n\n`mySlice := []int{1,2,3}` rather than `myArray := [3]int{1,2,3}`\n\n```go\nfunc TestSum(t *testing.T) {\n\n\tt.Run(\"collection of 5 numbers\", func(t *testing.T) {\n\t\tnumbers := [5]int{1, 2, 3, 4, 5}\n\n\t\tgot := Sum(numbers)\n\t\twant := 15\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %d want %d given, %v\", got, want, numbers)\n\t\t}\n\t})\n\n\tt.Run(\"collection of any size\", func(t *testing.T) {\n\t\tnumbers := []int{1, 2, 3}\n\n\t\tgot := Sum(numbers)\n\t\twant := 6\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %d want %d given, %v\", got, want, numbers)\n\t\t}\n\t})\n\n}\n```\n\n## Try and run the test\n\nThis does not compile\n\n`./sum_test.go:22:13: cannot use numbers (type []int) as type [5]int in argument to Sum`\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\nThe problem here is we can either\n\n* Break the existing API by changing the argument to `Sum` to be a slice rather\n  than an array. When we do this, we will potentially ruin\n  someone's day because our _other_ test will no longer compile!\n* Create a new function\n\nIn our case, no one else is using our function, so rather than having two functions to maintain, let's have just one.\n\n```go\nfunc Sum(numbers []int) int {\n\tsum := 0\n\tfor _, number := range numbers {\n\t\tsum += number\n\t}\n\treturn sum\n}\n```\n\nIf you try to run the tests they will still not compile, you will have to change the first test to pass in a slice rather than an array.\n\n## Write enough code to make it pass\n\nIt turns out that fixing the compiler problems were all we need to do here and the tests pass!\n\n## Refactor\n\nWe already refactored `Sum` - all we did was replace arrays with slices, so no extra changes are required.\nRemember that we must not neglect our test code in the refactoring stage - we can further improve our `Sum` tests.\n\n```go\nfunc TestSum(t *testing.T) {\n\n\tt.Run(\"collection of 5 numbers\", func(t *testing.T) {\n\t\tnumbers := []int{1, 2, 3, 4, 5}\n\n\t\tgot := Sum(numbers)\n\t\twant := 15\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %d want %d given, %v\", got, want, numbers)\n\t\t}\n\t})\n\n\tt.Run(\"collection of any size\", func(t *testing.T) {\n\t\tnumbers := []int{1, 2, 3}\n\n\t\tgot := Sum(numbers)\n\t\twant := 6\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %d want %d given, %v\", got, want, numbers)\n\t\t}\n\t})\n\n}\n```\n\nIt is important to question the value of your tests. It should not be a goal to\nhave as many tests as possible, but rather to have as much _confidence_ as\npossible in your code base. Having too many tests can turn in to a real problem\nand it just adds more overhead in maintenance. **Every test has a cost**.\n\nIn our case, you can see that having two tests for this function is redundant.\nIf it works for a slice of one size it's very likely it'll work for a slice of\nany size \\(within reason\\).\n\nGo's built-in testing toolkit features a [coverage tool](https://blog.golang.org/cover).\nWhilst striving for 100% coverage should not be your end goal, the coverage tool can help\nidentify areas of your code not covered by tests. If you have been strict with TDD,\nit's quite likely you'll have close to 100% coverage anyway.\n\nTry running\n\n`go test -cover`\n\nYou should see\n\n```bash\nPASS\ncoverage: 100.0% of statements\n```\n\nNow delete one of the tests and check the coverage again.\n\nNow that we are happy we have a well-tested function you should commit your\ngreat work before taking on the next challenge.\n\nWe need a new function called `SumAll` which will take a varying number of\nslices, returning a new slice containing the totals for each slice passed in.\n\nFor example\n\n`SumAll([]int{1,2}, []int{0,9})` would return `[]int{3, 9}`\n\nor\n\n`SumAll([]int{1,1,1})` would return `[]int{3}`\n\n## Write the test first\n\n```go\nfunc TestSumAll(t *testing.T) {\n\n\tgot := SumAll([]int{1, 2}, []int{0, 9})\n\twant := []int{3, 9}\n\n\tif got != want {\n\t\tt.Errorf(\"got %v want %v\", got, want)\n\t}\n}\n```\n\n## Try and run the test\n\n`./sum_test.go:23:9: undefined: SumAll`\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\nWe need to define `SumAll` according to what our test wants.\n\nGo can let you write [_variadic functions_](https://gobyexample.com/variadic-functions) that can take a variable number of arguments.\n\n```go\nfunc SumAll(numbersToSum ...[]int) []int {\n\treturn nil\n}\n```\n\nThis is valid, but our tests still won't compile!\n\n`./sum_test.go:26:9: invalid operation: got != want (slice can only be compared to nil)`\n\nGo does not let you use equality operators with slices. You _could_ write\na function to iterate over each `got` and `want` slice and check their values,\nbut what if we had a more convenient way to do this?\n\nFrom Go 1.21, [slices](https://pkg.go.dev/slices#pkg-overview) standard package is available, which has [slices.Equal](https://pkg.go.dev/slices#Equal) function to do a simple shallow compare on slices, where you don't need to worry about the types like the above case.\nNote that this function expects the elements to be [comparable](https://pkg.go.dev/builtin#comparable).\nSo, it can't be applied to slices with non-comparable elements like 2D slices.\n\nLet's go ahead and put this into practice!\n\n```go\nfunc TestSumAll(t *testing.T) {\n\n\tgot := SumAll([]int{1, 2}, []int{0, 9})\n\twant := []int{3, 9}\n\n\tif !slices.Equal(got, want) {\n\t\tt.Errorf(\"got %v want %v\", got, want)\n\t}\n}\n```\n\nYou should have test output like the following:\n`sum_test.go:30: got [] want [3 9]`\n\n## Write enough code to make it pass\n\nWhat we need to do is iterate over the varargs, calculate the sum using our\nexisting `Sum` function, then add it to the slice we will return\n\n```go\nfunc SumAll(numbersToSum ...[]int) []int {\n\tlengthOfNumbers := len(numbersToSum)\n\tsums := make([]int, lengthOfNumbers)\n\n\tfor i, numbers := range numbersToSum {\n\t\tsums[i] = Sum(numbers)\n\t}\n\n\treturn sums\n}\n```\n\nLots of new things to learn!\n\nThere's a new way to create a slice. `make` allows you to create a slice with\na starting capacity of the `len` of the `numbersToSum` we need to work through. The length of a slice is the number of elements it holds `len(mySlice)`, while the capacity is the number of elements it can hold in the underlying array `cap(mySlice)`, e.g., `make([]int, 0, 5)` creates a slice with length 0 and capacity 5.\n\nYou can index slices like arrays with `mySlice[N]` to get the value out or\nassign it a new value with `=`\n\nThe tests should now pass.\n\n## Refactor\n\nAs mentioned, slices have a capacity. If you have a slice with a capacity of\n2 and try to do `mySlice[10] = 1` you will get a _runtime_ error.\n\nHowever, you can use the `append` function which takes a slice and a new value,\nthen returns a new slice with all the items in it.\n\n```go\nfunc SumAll(numbersToSum ...[]int) []int {\n\tvar sums []int\n\tfor _, numbers := range numbersToSum {\n\t\tsums = append(sums, Sum(numbers))\n\t}\n\n\treturn sums\n}\n```\n\nIn this implementation, we are worrying less about capacity. We start with an\nempty slice `sums` and append to it the result of `Sum` as we work through the varargs.\n\nOur next requirement is to change `SumAll` to `SumAllTails`, where it will\ncalculate the totals of the \"tails\" of each slice. The tail of a collection is\nall items in the collection except the first one \\(the \"head\"\\).\n\n## Write the test first\n\n```go\nfunc TestSumAllTails(t *testing.T) {\n\tgot := SumAllTails([]int{1, 2}, []int{0, 9})\n\twant := []int{2, 9}\n\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %v want %v\", got, want)\n\t}\n}\n```\n\n## Try and run the test\n\n`./sum_test.go:26:9: undefined: SumAllTails`\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\nRename the function to `SumAllTails` and re-run the test\n\n`sum_test.go:30: got [3 9] want [2 9]`\n\n## Write enough code to make it pass\n\n```go\nfunc SumAllTails(numbersToSum ...[]int) []int {\n\tvar sums []int\n\tfor _, numbers := range numbersToSum {\n\t\ttail := numbers[1:]\n\t\tsums = append(sums, Sum(tail))\n\t}\n\n\treturn sums\n}\n```\n\nSlices can be sliced! The syntax is `slice[low:high]`. If you omit the value on\none of the sides of the `:` it captures everything to that side of it. In our\ncase, we are saying \"take from 1 to the end\" with `numbers[1:]`. You may wish to\nspend some time writing other tests around slices and experiment with the\nslice operator to get more familiar with it.\n\n## Refactor\n\nNot a lot to refactor this time.\n\nWhat do you think would happen if you passed in an empty slice into our\nfunction? What is the \"tail\" of an empty slice? What happens when you tell Go to\ncapture all elements from `myEmptySlice[1:]`?\n\n## Write the test first\n\n```go\nfunc TestSumAllTails(t *testing.T) {\n\n\tt.Run(\"make the sums of some slices\", func(t *testing.T) {\n\t\tgot := SumAllTails([]int{1, 2}, []int{0, 9})\n\t\twant := []int{2, 9}\n\n\t\tif !reflect.DeepEqual(got, want) {\n\t\t\tt.Errorf(\"got %v want %v\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"safely sum empty slices\", func(t *testing.T) {\n\t\tgot := SumAllTails([]int{}, []int{3, 4, 5})\n\t\twant := []int{0, 9}\n\n\t\tif !reflect.DeepEqual(got, want) {\n\t\t\tt.Errorf(\"got %v want %v\", got, want)\n\t\t}\n\t})\n\n}\n```\n\n## Try and run the test\n\n```text\npanic: runtime error: slice bounds out of range [recovered]\n    panic: runtime error: slice bounds out of range\n```\n\nOh no! It's important to note that while the test _has compiled_, it _has a runtime error_.\n\nCompile time errors are our friend because they help us write software that works,\nruntime errors are our enemies because they affect our users.\n\n## Write enough code to make it pass\n\n```go\nfunc SumAllTails(numbersToSum ...[]int) []int {\n\tvar sums []int\n\tfor _, numbers := range numbersToSum {\n\t\tif len(numbers) == 0 {\n\t\t\tsums = append(sums, 0)\n\t\t} else {\n\t\t\ttail := numbers[1:]\n\t\t\tsums = append(sums, Sum(tail))\n\t\t}\n\t}\n\n\treturn sums\n}\n```\n\n## Refactor\n\nOur tests have some repeated code around the assertions again, so let's extract those into a function.\n\n```go\nfunc TestSumAllTails(t *testing.T) {\n\n\tcheckSums := func(t testing.TB, got, want []int) {\n\t\tt.Helper()\n\t\tif !reflect.DeepEqual(got, want) {\n\t\t\tt.Errorf(\"got %v want %v\", got, want)\n\t\t}\n\t}\n\n\tt.Run(\"make the sums of tails of\", func(t *testing.T) {\n\t\tgot := SumAllTails([]int{1, 2}, []int{0, 9})\n\t\twant := []int{2, 9}\n\t\tcheckSums(t, got, want)\n\t})\n\n\tt.Run(\"safely sum empty slices\", func(t *testing.T) {\n\t\tgot := SumAllTails([]int{}, []int{3, 4, 5})\n\t\twant := []int{0, 9}\n\t\tcheckSums(t, got, want)\n\t})\n\n}\n```\n\nWe could've created a new function `checkSums` like we normally do, but in this case, we're showing a new technique, assigning a function to a variable. It might look strange but, it's no different to assigning a variable to a `string`, or an `int`, functions in effect are values too.\n\nIt's not shown here, but this technique can be useful when you want to bind a function to other local variables in \"scope\" (e.g between some `{}`). It also allows you to reduce the surface area of your API.\n\nBy defining this function inside the test, it cannot be used by other functions in this package. Hiding variables and functions that don't need to be exported is an important design consideration.\n\nA handy side-effect of this is this adds a little type-safety to our code. If\na developer mistakenly adds a new test with `checkSums(t, got, \"dave\")` the compiler\nwill stop them in their tracks.\n\n```bash\n$ go test\n./sum_test.go:52:21: cannot use \"dave\" (type string) as type []int in argument to checkSums\n```\n\n## Wrapping up\n\nWe have covered\n\n* Arrays\n* Slices\n  * The various ways to make them\n  * How they have a _fixed_ capacity but you can create new slices from old ones\n    using `append`\n  * How to slice, slices!\n* `len` to get the length of an array or slice\n* Test coverage tool\n* `reflect.DeepEqual` and why it's useful but can reduce the type-safety of your code\n\nWe've used slices and arrays with integers but they work with any other type\ntoo, including arrays/slices themselves. So you can declare a variable of\n`[][]string` if you need to.\n\n[Check out the Go blog post on slices][blog-slice] for an in-depth look into\nslices. Try writing more tests to solidify what you learn from reading it.\n\nAnother handy way to experiment with Go other than writing tests is the Go\nplayground. You can try most things out and you can easily share your code if\nyou need to ask questions. [I have made a go playground with a slice in it for you to experiment with.](https://play.golang.org/p/ICCWcRGIO68)\n\n[Here is an example](https://play.golang.org/p/bTrRmYfNYCp) of slicing an array\nand how changing the slice affects the original array; but a \"copy\" of the slice\nwill not affect the original array.\n[Another example](https://play.golang.org/p/Poth8JS28sc) of why it's a good idea\nto make a copy of a slice after slicing a very large slice.\n\n[for]: ../iteration.md#\n[blog-slice]: https://blog.golang.org/go-slices-usage-and-internals\n[deepEqual]: https://golang.org/pkg/reflect/#DeepEqual\n[slice]: https://golang.org/doc/effective_go.html#slices\n"
  },
  {
    "path": "blogrenderer/post.go",
    "content": "package blogrenderer\n\nimport \"strings\"\n\n// Post is a representation of a post\ntype Post struct {\n\tTitle, Description, Body string\n\tTags                     []string\n}\n\n// SanitisedTitle returns the title of the post with spaces replaced by dashes for pleasant URLs\nfunc (p Post) SanitisedTitle() string {\n\treturn strings.ToLower(strings.Replace(p.Title, \" \", \"-\", -1))\n}\n"
  },
  {
    "path": "blogrenderer/renderer.go",
    "content": "package blogrenderer\n\nimport (\n\t\"embed\"\n\t\"github.com/gomarkdown/markdown\"\n\t\"github.com/gomarkdown/markdown/parser\"\n\t\"html/template\"\n\t\"io\"\n)\n\nvar (\n\t//go:embed \"templates/*\"\n\tpostTemplates embed.FS\n)\n\n// PostRenderer renders data into HTML\ntype PostRenderer struct {\n\ttempl    *template.Template\n\tmdParser *parser.Parser\n}\n\n// NewPostRenderer creates a new PostRenderer\nfunc NewPostRenderer() (*PostRenderer, error) {\n\ttempl, err := template.ParseFS(postTemplates, \"templates/*.gohtml\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\textensions := parser.CommonExtensions | parser.AutoHeadingIDs\n\tparser := parser.NewWithExtensions(extensions)\n\n\treturn &PostRenderer{templ: templ, mdParser: parser}, nil\n}\n\n// Render renders post into HTML\nfunc (r *PostRenderer) Render(w io.Writer, p Post) error {\n\treturn r.templ.ExecuteTemplate(w, \"blog.gohtml\", newPostVM(p, r))\n}\n\n// RenderIndex creates an HTML index page given a collection of posts\nfunc (r *PostRenderer) RenderIndex(w io.Writer, posts []Post) error {\n\treturn r.templ.ExecuteTemplate(w, \"index.gohtml\", posts)\n}\n\ntype postViewModel struct {\n\tPost\n\tHTMLBody template.HTML\n}\n\nfunc newPostVM(p Post, r *PostRenderer) postViewModel {\n\tvm := postViewModel{Post: p}\n\tvm.HTMLBody = template.HTML(markdown.ToHTML([]byte(p.Body), r.mdParser, nil))\n\treturn vm\n}\n"
  },
  {
    "path": "blogrenderer/renderer_test.TestRender.it_converts_a_single_post_into_HTML.approved.txt",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <title>My amazing blog!</title>\n    <meta charset=\"UTF-8\"/>\n    <meta name=\"description\" content=\"Wow, like and subscribe, it really helps the channel guys\" lang=\"en\"/>\n</head>\n<body>\n<nav role=\"navigation\">\n    <div>\n        <h1>Budding Gopher's blog</h1>\n        <ul>\n            <li><a href=\"/\">home</a></li>\n            <li><a href=\"about\">about</a></li>\n            <li><a href=\"archive\">archive</a></li>\n        </ul>\n    </div>\n</nav>\n<main>\n\n<h1>hello world</h1>\n\n<p>This is a description</p>\n\nTags: <ul><li>go</li><li>tdd</li></ul>\n<h1 id=\"first-recipe\">First recipe!</h1>\n\n<p>Welcome to my <strong>amazing blog</strong>. I am going to write about my family recipes, and make sure I write a long, irrelevant and boring story about my family before you get to the actual instructions.</p>\n\n\n</main>\n<footer>\n    <ul>\n        <li><a href=\"https://twitter.com/quii\">Twitter</a></li>\n        <li><a href=\"https://github.com/quii\">GitHub</a></li>\n    </ul>\n</footer>\n</body>\n</html>\n\n"
  },
  {
    "path": "blogrenderer/renderer_test.TestRender.it_renders_an_index_of_posts.approved.txt",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <title>My amazing blog!</title>\n    <meta charset=\"UTF-8\"/>\n    <meta name=\"description\" content=\"Wow, like and subscribe, it really helps the channel guys\" lang=\"en\"/>\n</head>\n<body>\n<nav role=\"navigation\">\n    <div>\n        <h1>Budding Gopher's blog</h1>\n        <ul>\n            <li><a href=\"/\">home</a></li>\n            <li><a href=\"about\">about</a></li>\n            <li><a href=\"archive\">archive</a></li>\n        </ul>\n    </div>\n</nav>\n<main>\n\n<ol><li><a href=\"/post/hello-world\">Hello World</a></li><li><a href=\"/post/hello-world-2\">Hello World 2</a></li></ol>\n\n</main>\n<footer>\n    <ul>\n        <li><a href=\"https://twitter.com/quii\">Twitter</a></li>\n        <li><a href=\"https://github.com/quii\">GitHub</a></li>\n    </ul>\n</footer>\n</body>\n</html>\n\n"
  },
  {
    "path": "blogrenderer/renderer_test.go",
    "content": "package blogrenderer_test\n\nimport (\n\t\"bytes\"\n\tapprovals \"github.com/approvals/go-approval-tests\"\n\t\"github.com/quii/learn-go-with-tests/blogrenderer\"\n\t\"io\"\n\t\"testing\"\n)\n\nfunc TestRender(t *testing.T) {\n\tvar (\n\t\taPost = blogrenderer.Post{\n\t\t\tTitle: \"hello world\",\n\t\t\tBody: `# First recipe!\nWelcome to my **amazing blog**. I am going to write about my family recipes, and make sure I write a long, irrelevant and boring story about my family before you get to the actual instructions.`,\n\t\t\tDescription: \"This is a description\",\n\t\t\tTags:        []string{\"go\", \"tdd\"},\n\t\t}\n\t)\n\n\tpostRenderer, err := blogrenderer.NewPostRenderer()\n\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tt.Run(\"it converts a single post into HTML\", func(t *testing.T) {\n\t\tbuf := bytes.Buffer{}\n\n\t\tif err := postRenderer.Render(&buf, aPost); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tapprovals.VerifyString(t, buf.String())\n\t})\n\n\tt.Run(\"it renders an index of posts\", func(t *testing.T) {\n\t\tbuf := bytes.Buffer{}\n\t\tposts := []blogrenderer.Post{{Title: \"Hello World\"}, {Title: \"Hello World 2\"}}\n\n\t\tif err := postRenderer.RenderIndex(&buf, posts); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tapprovals.VerifyString(t, buf.String())\n\t})\n}\n\nfunc BenchmarkRender(b *testing.B) {\n\tvar (\n\t\taPost = blogrenderer.Post{\n\t\t\tTitle:       \"hello world\",\n\t\t\tBody:        \"This is a post\",\n\t\t\tDescription: \"This is a description\",\n\t\t\tTags:        []string{\"go\", \"tdd\"},\n\t\t}\n\t)\n\n\tpostRenderer, err := blogrenderer.NewPostRenderer()\n\n\tif err != nil {\n\t\tb.Fatal(err)\n\t}\n\n\tfor b.Loop() {\n\t\tpostRenderer.Render(io.Discard, aPost)\n\t}\n}\n"
  },
  {
    "path": "blogrenderer/templates/blog.gohtml",
    "content": "{{template \"top\" .}}\n<h1>{{.Title}}</h1>\n\n<p>{{.Description}}</p>\n\nTags: <ul>{{range .Tags}}<li>{{.}}</li>{{end}}</ul>\n{{.HTMLBody}}\n{{template \"bottom\" .}}\n"
  },
  {
    "path": "blogrenderer/templates/bottom.gohtml",
    "content": "{{define \"bottom\"}}\n</main>\n<footer>\n    <ul>\n        <li><a href=\"https://twitter.com/quii\">Twitter</a></li>\n        <li><a href=\"https://github.com/quii\">GitHub</a></li>\n    </ul>\n</footer>\n</body>\n</html>\n{{end}}\n"
  },
  {
    "path": "blogrenderer/templates/index.gohtml",
    "content": "{{template \"top\" .}}\n<ol>{{range .}}<li><a href=\"/post/{{.SanitisedTitle}}\">{{.Title}}</a></li>{{end}}</ol>\n{{template \"bottom\" .}}\n"
  },
  {
    "path": "blogrenderer/templates/top.gohtml",
    "content": "{{define \"top\"}}<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <title>My amazing blog!</title>\n    <meta charset=\"UTF-8\"/>\n    <meta name=\"description\" content=\"Wow, like and subscribe, it really helps the channel guys\" lang=\"en\"/>\n</head>\n<body>\n<nav role=\"navigation\">\n    <div>\n        <h1>Budding Gopher's blog</h1>\n        <ul>\n            <li><a href=\"/\">home</a></li>\n            <li><a href=\"about\">about</a></li>\n            <li><a href=\"archive\">archive</a></li>\n        </ul>\n    </div>\n</nav>\n<main>\n{{end}}\n"
  },
  {
    "path": "book.json",
    "content": "{\n    \"structure\": {\n        \"readme\": \"gb-readme.md\"\n    }\n}\n"
  },
  {
    "path": "build.books.sh",
    "content": "#!/usr/bin/env bash\n\nset -e\n\n# safer separator for sed\nsep=$'\\001'\n\nif [ -v GITHUB_REF_NAME ]; then\n    sed \"s${sep}%%FOOTER_VERSION%%${sep}${GITHUB_REF_NAME}${sep}\" meta.tmpl.tex > meta.tex\nelse\n    sed \"s${sep}%%FOOTER_VERSION%%${sep}UNDEFINED VERSION${sep}\" meta.tmpl.tex > meta.tex\nfi\n\ndocker run --rm -v `pwd`:/data uppalabharath/pandoc-latex-cjk:latest --from=gfm+rebase_relative_paths -o learn-go-with-tests.pdf \\\n    -H meta.tex --pdf-engine=xelatex --variable urlcolor=blue --toc --toc-depth=1 \\\n    -B pdf-cover.tex \\\n    gb-readme.md \\\n    why.md \\\n    hello-world.md \\\n    integers.md \\\n    iteration.md \\\n    arrays-and-slices.md \\\n    structs-methods-and-interfaces.md \\\n    pointers-and-errors.md \\\n    maps.md \\\n    dependency-injection.md \\\n    mocking.md \\\n    concurrency.md \\\n    select.md \\\n    reflection.md \\\n    sync.md \\\n    context.md \\\n    roman-numerals.md \\\n    math.md \\\n    reading-files.md \\\n    html-templates.md \\\n    generics.md \\\n    revisiting-arrays-and-slices-with-generics.md \\\n    intro-to-acceptance-tests.md \\\n    scaling-acceptance-tests.md \\\n    working-without-mocks.md \\\n    refactoring-checklist.md \\\n    app-intro.md \\\n    http-server.md \\\n    json.md \\\n    io.md \\\n    command-line.md \\\n    time.md \\\n    websockets.md \\\n    os-exec.md \\\n    error-types.md \\\n    context-aware-reader.md \\\n    http-handlers-revisited.md \\\n    anti-patterns.md\n\ndocker run --rm -v `pwd`:/data pandoc/latex:latest --from=gfm+rebase_relative_paths --to=epub --file-scope title.txt -o learn-go-with-tests.epub --pdf-engine=xelatex --toc --toc-depth=1  \\\n    gb-readme.md \\\n    why.md \\\n    hello-world.md \\\n    integers.md \\\n    iteration.md \\\n    arrays-and-slices.md \\\n    structs-methods-and-interfaces.md \\\n    pointers-and-errors.md \\\n    maps.md \\\n    dependency-injection.md \\\n    mocking.md \\\n    concurrency.md \\\n    select.md \\\n    reflection.md \\\n    sync.md \\\n    context.md \\\n    roman-numerals.md \\\n    math.md \\\n    reading-files.md \\\n    html-templates.md \\\n    generics.md \\\n    revisiting-arrays-and-slices-with-generics.md \\\n    intro-to-acceptance-tests.md \\\n    scaling-acceptance-tests.md \\\n    working-without-mocks.md \\\n    refactoring-checklist.md \\\n    app-intro.md \\\n    http-server.md \\\n    json.md \\\n    io.md \\\n    command-line.md \\\n    time.md \\\n    websockets.md \\\n    os-exec.md \\\n    error-types.md \\\n    context-aware-reader.md \\\n    http-handlers-revisited.md \\\n    anti-patterns.md\n"
  },
  {
    "path": "build.sh",
    "content": "#!/usr/bin/env bash\n\nset -e\n\ngo install github.com/client9/misspell/cmd/misspell@latest\ngo install github.com/po3rin/gofmtmd/cmd/gofmtmd@latest\n\nls *.md | xargs misspell -error\n\nfor md_file in ./*.md; do\n    echo \"formatting  file: $md_file\"\n    gofmtmd  \"$md_file\" -r\ndone\n\ngo test ./...\ngo vet ./...\ngo fmt ./...\n"
  },
  {
    "path": "command-line/v1/cmd/cli/main.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(\"Let's play poker\")\n}\n"
  },
  {
    "path": "command-line/v1/cmd/webserver/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/quii/learn-go-with-tests/command-line/v1\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n)\n\nconst dbFileName = \"game.db.json\"\n\nfunc main() {\n\tdb, err := os.OpenFile(dbFileName, os.O_RDWR|os.O_CREATE, 0666)\n\n\tif err != nil {\n\t\tlog.Fatalf(\"problem opening %s %v\", dbFileName, err)\n\t}\n\n\tstore, err := poker.NewFileSystemPlayerStore(db)\n\n\tif err != nil {\n\t\tlog.Fatalf(\"problem creating file system player store, %v \", err)\n\t}\n\n\tserver := poker.NewPlayerServer(store)\n\n\tlog.Fatal(http.ListenAndServe(\":5000\", server))\n}\n"
  },
  {
    "path": "command-line/v1/file_system_store.go",
    "content": "package poker\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"sort\"\n)\n\n// FileSystemPlayerStore stores players in the filesystem.\ntype FileSystemPlayerStore struct {\n\tdatabase *json.Encoder\n\tleague   League\n}\n\n// NewFileSystemPlayerStore creates a FileSystemPlayerStore initialising the store if needed.\nfunc NewFileSystemPlayerStore(file *os.File) (*FileSystemPlayerStore, error) {\n\n\terr := initialisePlayerDBFile(file)\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"problem initialising player db file, %v\", err)\n\t}\n\n\tleague, err := NewLeague(file)\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"problem loading player store from file %s, %v\", file.Name(), err)\n\t}\n\n\treturn &FileSystemPlayerStore{\n\t\tdatabase: json.NewEncoder(&tape{file}),\n\t\tleague:   league,\n\t}, nil\n}\n\nfunc initialisePlayerDBFile(file *os.File) error {\n\tfile.Seek(0, io.SeekStart)\n\n\tinfo, err := file.Stat()\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"problem getting file info from file %s, %v\", file.Name(), err)\n\t}\n\n\tif info.Size() == 0 {\n\t\tfile.Write([]byte(\"[]\"))\n\t\tfile.Seek(0, io.SeekStart)\n\t}\n\n\treturn nil\n}\n\n// GetLeague returns the scores of all the players.\nfunc (f *FileSystemPlayerStore) GetLeague() League {\n\tsort.Slice(f.league, func(i, j int) bool {\n\t\treturn f.league[i].Wins > f.league[j].Wins\n\t})\n\treturn f.league\n}\n\n// GetPlayerScore retrieves a player's score.\nfunc (f *FileSystemPlayerStore) GetPlayerScore(name string) int {\n\n\tplayer := f.league.Find(name)\n\n\tif player != nil {\n\t\treturn player.Wins\n\t}\n\n\treturn 0\n}\n\n// RecordWin will store a win for a player, incrementing wins if already known.\nfunc (f *FileSystemPlayerStore) RecordWin(name string) {\n\tplayer := f.league.Find(name)\n\n\tif player != nil {\n\t\tplayer.Wins++\n\t} else {\n\t\tf.league = append(f.league, Player{name, 1})\n\t}\n\n\tf.database.Encode(f.league)\n}\n"
  },
  {
    "path": "command-line/v1/file_system_store_test.go",
    "content": "package poker\n\nimport (\n\t\"os\"\n\t\"testing\"\n)\n\nfunc createTempFile(t testing.TB, initialData string) (*os.File, func()) {\n\tt.Helper()\n\n\ttmpfile, err := os.CreateTemp(\"\", \"db\")\n\n\tif err != nil {\n\t\tt.Fatalf(\"could not create temp file %v\", err)\n\t}\n\n\ttmpfile.Write([]byte(initialData))\n\n\tremoveFile := func() {\n\t\tos.Remove(tmpfile.Name())\n\t}\n\n\treturn tmpfile, removeFile\n}\n\nfunc TestFileSystemStore(t *testing.T) {\n\n\tt.Run(\"league sorted\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tgot := store.GetLeague()\n\n\t\twant := []Player{\n\t\t\t{\"Chris\", 33},\n\t\t\t{\"Cleo\", 10},\n\t\t}\n\n\t\tassertLeague(t, got, want)\n\n\t\t// read again\n\t\tgot = store.GetLeague()\n\t\tassertLeague(t, got, want)\n\t})\n\n\tt.Run(\"get player score\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tgot := store.GetPlayerScore(\"Chris\")\n\t\twant := 33\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"store wins for existing players\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tstore.RecordWin(\"Chris\")\n\n\t\tgot := store.GetPlayerScore(\"Chris\")\n\t\twant := 34\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"store wins for existing players\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tstore.RecordWin(\"Pepper\")\n\n\t\tgot := store.GetPlayerScore(\"Pepper\")\n\t\twant := 1\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"works with an empty file\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, \"\")\n\t\tdefer cleanDatabase()\n\n\t\t_, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\t})\n}\n\nfunc assertScoreEquals(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"got %d want %d\", got, want)\n\t}\n}\n\nfunc assertNoError(t testing.TB, err error) {\n\tt.Helper()\n\tif err != nil {\n\t\tt.Fatalf(\"didn't expect an error but got one, %v\", err)\n\t}\n}\n"
  },
  {
    "path": "command-line/v1/league.go",
    "content": "package poker\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n)\n\n// League stores a collection of players.\ntype League []Player\n\n// Find tries to return a player from a league.\nfunc (l League) Find(name string) *Player {\n\tfor i, p := range l {\n\t\tif p.Name == name {\n\t\t\treturn &l[i]\n\t\t}\n\t}\n\treturn nil\n}\n\n// NewLeague creates a league from JSON.\nfunc NewLeague(rdr io.Reader) (League, error) {\n\tvar league []Player\n\terr := json.NewDecoder(rdr).Decode(&league)\n\n\tif err != nil {\n\t\terr = fmt.Errorf(\"problem parsing league, %v\", err)\n\t}\n\n\treturn league, err\n}\n"
  },
  {
    "path": "command-line/v1/server.go",
    "content": "package poker\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n)\n\n// PlayerStore stores score information about players.\ntype PlayerStore interface {\n\tGetPlayerScore(name string) int\n\tRecordWin(name string)\n\tGetLeague() League\n}\n\n// Player stores a name with a number of wins.\ntype Player struct {\n\tName string\n\tWins int\n}\n\n// PlayerServer is a HTTP interface for player information.\ntype PlayerServer struct {\n\tstore PlayerStore\n\thttp.Handler\n}\n\nconst jsonContentType = \"application/json\"\n\n// NewPlayerServer creates a PlayerServer with routing configured.\nfunc NewPlayerServer(store PlayerStore) *PlayerServer {\n\tp := new(PlayerServer)\n\n\tp.store = store\n\n\trouter := http.NewServeMux()\n\trouter.Handle(\"/league\", http.HandlerFunc(p.leagueHandler))\n\trouter.Handle(\"/players/\", http.HandlerFunc(p.playersHandler))\n\n\tp.Handler = router\n\n\treturn p\n}\n\nfunc (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) {\n\tw.Header().Set(\"content-type\", jsonContentType)\n\tjson.NewEncoder(w).Encode(p.store.GetLeague())\n}\n\nfunc (p *PlayerServer) playersHandler(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\n\tswitch r.Method {\n\tcase http.MethodPost:\n\t\tp.processWin(w, player)\n\tcase http.MethodGet:\n\t\tp.showScore(w, player)\n\t}\n}\n\nfunc (p *PlayerServer) showScore(w http.ResponseWriter, player string) {\n\tscore := p.store.GetPlayerScore(player)\n\n\tif score == 0 {\n\t\tw.WriteHeader(http.StatusNotFound)\n\t}\n\n\tfmt.Fprint(w, score)\n}\n\nfunc (p *PlayerServer) processWin(w http.ResponseWriter, player string) {\n\tp.store.RecordWin(player)\n\tw.WriteHeader(http.StatusAccepted)\n}\n"
  },
  {
    "path": "command-line/v1/server_integration_test.go",
    "content": "package poker\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestRecordingWinsAndRetrievingThem(t *testing.T) {\n\tdatabase, cleanDatabase := createTempFile(t, `[]`)\n\tdefer cleanDatabase()\n\tstore, err := NewFileSystemPlayerStore(database)\n\n\tassertNoError(t, err)\n\n\tserver := NewPlayerServer(store)\n\tplayer := \"Pepper\"\n\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\n\tt.Run(\"get score\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newGetScoreRequest(player))\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\n\t\tassertResponseBody(t, response.Body.String(), \"3\")\n\t})\n\n\tt.Run(\"get league\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newLeagueRequest())\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\t\twant := []Player{\n\t\t\t{\"Pepper\", 3},\n\t\t}\n\t\tassertLeague(t, got, want)\n\t})\n}\n"
  },
  {
    "path": "command-line/v1/server_test.go",
    "content": "package poker\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"reflect\"\n\t\"testing\"\n)\n\ntype StubPlayerStore struct {\n\tscores   map[string]int\n\twinCalls []string\n\tleague   []Player\n}\n\nfunc (s *StubPlayerStore) GetPlayerScore(name string) int {\n\tscore := s.scores[name]\n\treturn score\n}\n\nfunc (s *StubPlayerStore) RecordWin(name string) {\n\ts.winCalls = append(s.winCalls, name)\n}\n\nfunc (s *StubPlayerStore) GetLeague() League {\n\treturn s.league\n}\n\nfunc TestGETPlayers(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{\n\t\t\t\"Pepper\": 20,\n\t\t\t\"Floyd\":  10,\n\t\t},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"returns Pepper's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Pepper\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"20\")\n\t})\n\n\tt.Run(\"returns Floyd's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Floyd\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"10\")\n\t})\n\n\tt.Run(\"returns 404 on missing players\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Apollo\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusNotFound)\n\t})\n}\n\nfunc TestStoreWins(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"it records wins on POST\", func(t *testing.T) {\n\t\tplayer := \"Pepper\"\n\n\t\trequest := newPostWinRequest(player)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusAccepted)\n\n\t\tif len(store.winCalls) != 1 {\n\t\t\tt.Fatalf(\"got %d calls to RecordWin want %d\", len(store.winCalls), 1)\n\t\t}\n\n\t\tif store.winCalls[0] != player {\n\t\t\tt.Errorf(\"did not store the correct winner got %q want %q\", store.winCalls[0], player)\n\t\t}\n\t})\n}\n\nfunc TestLeague(t *testing.T) {\n\n\tt.Run(\"it returns the league table as JSON\", func(t *testing.T) {\n\t\twantedLeague := []Player{\n\t\t\t{\"Cleo\", 32},\n\t\t\t{\"Chris\", 20},\n\t\t\t{\"Tiest\", 14},\n\t\t}\n\n\t\tstore := StubPlayerStore{nil, nil, wantedLeague}\n\t\tserver := NewPlayerServer(&store)\n\n\t\trequest := newLeagueRequest()\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertLeague(t, got, wantedLeague)\n\t\tassertContentType(t, response, jsonContentType)\n\n\t})\n}\n\nfunc assertContentType(t testing.TB, response *httptest.ResponseRecorder, want string) {\n\tt.Helper()\n\tif response.Header().Get(\"content-type\") != want {\n\t\tt.Errorf(\"response did not have content-type of %s, got %v\", want, response.Result().Header)\n\t}\n}\n\nfunc getLeagueFromResponse(t testing.TB, body io.Reader) []Player {\n\tt.Helper()\n\tleague, err := NewLeague(body)\n\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to parse response from server %q into slice of Player, '%v'\", body, err)\n\t}\n\n\treturn league\n}\n\nfunc assertLeague(t testing.TB, got, want []Player) {\n\tt.Helper()\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %v want %v\", got, want)\n\t}\n}\n\nfunc assertStatus(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"did not get correct status, got %d, want %d\", got, want)\n\t}\n}\n\nfunc newLeagueRequest() *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, \"/league\", nil)\n\treturn req\n}\n\nfunc newGetScoreRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc newPostWinRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodPost, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc assertResponseBody(t testing.TB, got, want string) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"response body is wrong, got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "command-line/v1/tape.go",
    "content": "package poker\n\nimport (\n\t\"io\"\n\t\"os\"\n)\n\ntype tape struct {\n\tfile *os.File\n}\n\nfunc (t *tape) Write(p []byte) (n int, err error) {\n\tt.file.Truncate(0)\n\tt.file.Seek(0, io.SeekStart)\n\treturn t.file.Write(p)\n}\n"
  },
  {
    "path": "command-line/v1/tape_test.go",
    "content": "package poker\n\nimport (\n\t\"io\"\n\t\"testing\"\n)\n\nfunc TestTape_Write(t *testing.T) {\n\tfile, clean := createTempFile(t, \"12345\")\n\tdefer clean()\n\n\ttape := &tape{file}\n\n\ttape.Write([]byte(\"abc\"))\n\n\tfile.Seek(0, io.SeekStart)\n\tnewFileContents, _ := io.ReadAll(file)\n\n\tgot := string(newFileContents)\n\twant := \"abc\"\n\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "command-line/v2/CLI.go",
    "content": "package poker\n\n// CLI helps players through a game of poker.\ntype CLI struct {\n\tplayerStore PlayerStore\n}\n\n// PlayPoker starts the game.\nfunc (cli *CLI) PlayPoker() {\n\tcli.playerStore.RecordWin(\"Cleo\")\n}\n"
  },
  {
    "path": "command-line/v2/CLI_test.go",
    "content": "package poker\n\nimport (\n\t\"testing\"\n)\n\nfunc TestCLI(t *testing.T) {\n\tplayerStore := &StubPlayerStore{}\n\n\tcli := &CLI{playerStore}\n\tcli.PlayPoker()\n\n\tif len(playerStore.winCalls) != 1 {\n\t\tt.Fatal(\"expected a win call but didn't get any\")\n\t}\n}\n"
  },
  {
    "path": "command-line/v2/cmd/cli/main.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(\"Let's play poker\")\n}\n"
  },
  {
    "path": "command-line/v2/cmd/webserver/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/quii/learn-go-with-tests/command-line/v1\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n)\n\nconst dbFileName = \"game.db.json\"\n\nfunc main() {\n\tdb, err := os.OpenFile(dbFileName, os.O_RDWR|os.O_CREATE, 0666)\n\n\tif err != nil {\n\t\tlog.Fatalf(\"problem opening %s %v\", dbFileName, err)\n\t}\n\n\tstore, err := poker.NewFileSystemPlayerStore(db)\n\n\tif err != nil {\n\t\tlog.Fatalf(\"problem creating file system player store, %v \", err)\n\t}\n\n\tserver := poker.NewPlayerServer(store)\n\n\tlog.Fatal(http.ListenAndServe(\":5000\", server))\n}\n"
  },
  {
    "path": "command-line/v2/file_system_store.go",
    "content": "package poker\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"sort\"\n)\n\n// FileSystemPlayerStore stores players in the filesystem.\ntype FileSystemPlayerStore struct {\n\tdatabase *json.Encoder\n\tleague   League\n}\n\n// NewFileSystemPlayerStore creates a FileSystemPlayerStore initialising the store if needed.\nfunc NewFileSystemPlayerStore(file *os.File) (*FileSystemPlayerStore, error) {\n\n\terr := initialisePlayerDBFile(file)\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"problem initialising player db file, %v\", err)\n\t}\n\n\tleague, err := NewLeague(file)\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"problem loading player store from file %s, %v\", file.Name(), err)\n\t}\n\n\treturn &FileSystemPlayerStore{\n\t\tdatabase: json.NewEncoder(&tape{file}),\n\t\tleague:   league,\n\t}, nil\n}\n\nfunc initialisePlayerDBFile(file *os.File) error {\n\tfile.Seek(0, io.SeekStart)\n\n\tinfo, err := file.Stat()\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"problem getting file info from file %s, %v\", file.Name(), err)\n\t}\n\n\tif info.Size() == 0 {\n\t\tfile.Write([]byte(\"[]\"))\n\t\tfile.Seek(0, io.SeekStart)\n\t}\n\n\treturn nil\n}\n\n// GetLeague returns the scores of all the players.\nfunc (f *FileSystemPlayerStore) GetLeague() League {\n\tsort.Slice(f.league, func(i, j int) bool {\n\t\treturn f.league[i].Wins > f.league[j].Wins\n\t})\n\treturn f.league\n}\n\n// GetPlayerScore retrieves a player's score.\nfunc (f *FileSystemPlayerStore) GetPlayerScore(name string) int {\n\n\tplayer := f.league.Find(name)\n\n\tif player != nil {\n\t\treturn player.Wins\n\t}\n\n\treturn 0\n}\n\n// RecordWin will store a win for a player, incrementing wins if already known.\nfunc (f *FileSystemPlayerStore) RecordWin(name string) {\n\tplayer := f.league.Find(name)\n\n\tif player != nil {\n\t\tplayer.Wins++\n\t} else {\n\t\tf.league = append(f.league, Player{name, 1})\n\t}\n\n\tf.database.Encode(f.league)\n}\n"
  },
  {
    "path": "command-line/v2/file_system_store_test.go",
    "content": "package poker\n\nimport (\n\t\"os\"\n\t\"testing\"\n)\n\nfunc createTempFile(t testing.TB, initialData string) (*os.File, func()) {\n\tt.Helper()\n\n\ttmpfile, err := os.CreateTemp(\"\", \"db\")\n\n\tif err != nil {\n\t\tt.Fatalf(\"could not create temp file %v\", err)\n\t}\n\n\ttmpfile.Write([]byte(initialData))\n\n\tremoveFile := func() {\n\t\tos.Remove(tmpfile.Name())\n\t}\n\n\treturn tmpfile, removeFile\n}\n\nfunc TestFileSystemStore(t *testing.T) {\n\n\tt.Run(\"league sorted\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tgot := store.GetLeague()\n\n\t\twant := []Player{\n\t\t\t{\"Chris\", 33},\n\t\t\t{\"Cleo\", 10},\n\t\t}\n\n\t\tassertLeague(t, got, want)\n\n\t\t// read again\n\t\tgot = store.GetLeague()\n\t\tassertLeague(t, got, want)\n\t})\n\n\tt.Run(\"get player score\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tgot := store.GetPlayerScore(\"Chris\")\n\t\twant := 33\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"store wins for existing players\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tstore.RecordWin(\"Chris\")\n\n\t\tgot := store.GetPlayerScore(\"Chris\")\n\t\twant := 34\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"store wins for existing players\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tstore.RecordWin(\"Pepper\")\n\n\t\tgot := store.GetPlayerScore(\"Pepper\")\n\t\twant := 1\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"works with an empty file\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, \"\")\n\t\tdefer cleanDatabase()\n\n\t\t_, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\t})\n}\n\nfunc assertScoreEquals(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"got %d want %d\", got, want)\n\t}\n}\n\nfunc assertNoError(t testing.TB, err error) {\n\tt.Helper()\n\tif err != nil {\n\t\tt.Fatalf(\"didn't expect an error but got one, %v\", err)\n\t}\n}\n"
  },
  {
    "path": "command-line/v2/league.go",
    "content": "package poker\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n)\n\n// League stores a collection of players.\ntype League []Player\n\n// Find tries to return a player from a league.\nfunc (l League) Find(name string) *Player {\n\tfor i, p := range l {\n\t\tif p.Name == name {\n\t\t\treturn &l[i]\n\t\t}\n\t}\n\treturn nil\n}\n\n// NewLeague creates a league from JSON.\nfunc NewLeague(rdr io.Reader) (League, error) {\n\tvar league []Player\n\terr := json.NewDecoder(rdr).Decode(&league)\n\n\tif err != nil {\n\t\terr = fmt.Errorf(\"problem parsing league, %v\", err)\n\t}\n\n\treturn league, err\n}\n"
  },
  {
    "path": "command-line/v2/server.go",
    "content": "package poker\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n)\n\n// PlayerStore stores score information about players.\ntype PlayerStore interface {\n\tGetPlayerScore(name string) int\n\tRecordWin(name string)\n\tGetLeague() League\n}\n\n// Player stores a name with a number of wins.\ntype Player struct {\n\tName string\n\tWins int\n}\n\n// PlayerServer is a HTTP interface for player information.\ntype PlayerServer struct {\n\tstore PlayerStore\n\thttp.Handler\n}\n\nconst jsonContentType = \"application/json\"\n\n// NewPlayerServer creates a PlayerServer with routing configured.\nfunc NewPlayerServer(store PlayerStore) *PlayerServer {\n\tp := new(PlayerServer)\n\n\tp.store = store\n\n\trouter := http.NewServeMux()\n\trouter.Handle(\"/league\", http.HandlerFunc(p.leagueHandler))\n\trouter.Handle(\"/players/\", http.HandlerFunc(p.playersHandler))\n\n\tp.Handler = router\n\n\treturn p\n}\n\nfunc (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) {\n\tw.Header().Set(\"content-type\", jsonContentType)\n\tjson.NewEncoder(w).Encode(p.store.GetLeague())\n}\n\nfunc (p *PlayerServer) playersHandler(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\n\tswitch r.Method {\n\tcase http.MethodPost:\n\t\tp.processWin(w, player)\n\tcase http.MethodGet:\n\t\tp.showScore(w, player)\n\t}\n}\n\nfunc (p *PlayerServer) showScore(w http.ResponseWriter, player string) {\n\tscore := p.store.GetPlayerScore(player)\n\n\tif score == 0 {\n\t\tw.WriteHeader(http.StatusNotFound)\n\t}\n\n\tfmt.Fprint(w, score)\n}\n\nfunc (p *PlayerServer) processWin(w http.ResponseWriter, player string) {\n\tp.store.RecordWin(player)\n\tw.WriteHeader(http.StatusAccepted)\n}\n"
  },
  {
    "path": "command-line/v2/server_integration_test.go",
    "content": "package poker\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestRecordingWinsAndRetrievingThem(t *testing.T) {\n\tdatabase, cleanDatabase := createTempFile(t, `[]`)\n\tdefer cleanDatabase()\n\tstore, err := NewFileSystemPlayerStore(database)\n\n\tassertNoError(t, err)\n\n\tserver := NewPlayerServer(store)\n\tplayer := \"Pepper\"\n\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\n\tt.Run(\"get score\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newGetScoreRequest(player))\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\n\t\tassertResponseBody(t, response.Body.String(), \"3\")\n\t})\n\n\tt.Run(\"get league\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newLeagueRequest())\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\t\twant := []Player{\n\t\t\t{\"Pepper\", 3},\n\t\t}\n\t\tassertLeague(t, got, want)\n\t})\n}\n"
  },
  {
    "path": "command-line/v2/server_test.go",
    "content": "package poker\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"reflect\"\n\t\"testing\"\n)\n\ntype StubPlayerStore struct {\n\tscores   map[string]int\n\twinCalls []string\n\tleague   []Player\n}\n\nfunc (s *StubPlayerStore) GetPlayerScore(name string) int {\n\tscore := s.scores[name]\n\treturn score\n}\n\nfunc (s *StubPlayerStore) RecordWin(name string) {\n\ts.winCalls = append(s.winCalls, name)\n}\n\nfunc (s *StubPlayerStore) GetLeague() League {\n\treturn s.league\n}\n\nfunc TestGETPlayers(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{\n\t\t\t\"Pepper\": 20,\n\t\t\t\"Floyd\":  10,\n\t\t},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"returns Pepper's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Pepper\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"20\")\n\t})\n\n\tt.Run(\"returns Floyd's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Floyd\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"10\")\n\t})\n\n\tt.Run(\"returns 404 on missing players\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Apollo\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusNotFound)\n\t})\n}\n\nfunc TestStoreWins(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"it records wins on POST\", func(t *testing.T) {\n\t\tplayer := \"Pepper\"\n\n\t\trequest := newPostWinRequest(player)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusAccepted)\n\n\t\tif len(store.winCalls) != 1 {\n\t\t\tt.Fatalf(\"got %d calls to RecordWin want %d\", len(store.winCalls), 1)\n\t\t}\n\n\t\tif store.winCalls[0] != player {\n\t\t\tt.Errorf(\"did not store the correct winner got %q want %q\", store.winCalls[0], player)\n\t\t}\n\t})\n}\n\nfunc TestLeague(t *testing.T) {\n\n\tt.Run(\"it returns the league table as JSON\", func(t *testing.T) {\n\t\twantedLeague := []Player{\n\t\t\t{\"Cleo\", 32},\n\t\t\t{\"Chris\", 20},\n\t\t\t{\"Tiest\", 14},\n\t\t}\n\n\t\tstore := StubPlayerStore{nil, nil, wantedLeague}\n\t\tserver := NewPlayerServer(&store)\n\n\t\trequest := newLeagueRequest()\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertLeague(t, got, wantedLeague)\n\t\tassertContentType(t, response, jsonContentType)\n\n\t})\n}\n\nfunc assertContentType(t testing.TB, response *httptest.ResponseRecorder, want string) {\n\tt.Helper()\n\tif response.Header().Get(\"content-type\") != want {\n\t\tt.Errorf(\"response did not have content-type of %s, got %v\", want, response.Result().Header)\n\t}\n}\n\nfunc getLeagueFromResponse(t testing.TB, body io.Reader) []Player {\n\tt.Helper()\n\tleague, err := NewLeague(body)\n\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to parse response from server %q into slice of Player, '%v'\", body, err)\n\t}\n\n\treturn league\n}\n\nfunc assertLeague(t testing.TB, got, want []Player) {\n\tt.Helper()\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %v want %v\", got, want)\n\t}\n}\n\nfunc assertStatus(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"did not get correct status, got %d, want %d\", got, want)\n\t}\n}\n\nfunc newLeagueRequest() *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, \"/league\", nil)\n\treturn req\n}\n\nfunc newGetScoreRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc newPostWinRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodPost, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc assertResponseBody(t testing.TB, got, want string) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"response body is wrong, got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "command-line/v2/tape.go",
    "content": "package poker\n\nimport (\n\t\"io\"\n\t\"os\"\n)\n\ntype tape struct {\n\tfile *os.File\n}\n\nfunc (t *tape) Write(p []byte) (n int, err error) {\n\tt.file.Truncate(0)\n\tt.file.Seek(0, io.SeekStart)\n\treturn t.file.Write(p)\n}\n"
  },
  {
    "path": "command-line/v2/tape_test.go",
    "content": "package poker\n\nimport (\n\t\"io\"\n\t\"testing\"\n)\n\nfunc TestTape_Write(t *testing.T) {\n\tfile, clean := createTempFile(t, \"12345\")\n\tdefer clean()\n\n\ttape := &tape{file}\n\n\ttape.Write([]byte(\"abc\"))\n\n\tfile.Seek(0, io.SeekStart)\n\tnewFileContents, _ := io.ReadAll(file)\n\n\tgot := string(newFileContents)\n\twant := \"abc\"\n\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "command-line/v3/CLI.go",
    "content": "package poker\n\nimport (\n\t\"bufio\"\n\t\"io\"\n\t\"strings\"\n)\n\n// CLI helps players through a game of poker.\ntype CLI struct {\n\tplayerStore PlayerStore\n\tin          *bufio.Scanner\n}\n\n// NewCLI creates a CLI for playing poker.\nfunc NewCLI(store PlayerStore, in io.Reader) *CLI {\n\treturn &CLI{\n\t\tplayerStore: store,\n\t\tin:          bufio.NewScanner(in),\n\t}\n}\n\n// PlayPoker starts the game.\nfunc (cli *CLI) PlayPoker() {\n\tuserInput := cli.readLine()\n\tcli.playerStore.RecordWin(extractWinner(userInput))\n}\n\nfunc extractWinner(userInput string) string {\n\treturn strings.Replace(userInput, \" wins\", \"\", 1)\n}\n\nfunc (cli *CLI) readLine() string {\n\tcli.in.Scan()\n\treturn cli.in.Text()\n}\n"
  },
  {
    "path": "command-line/v3/CLI_test.go",
    "content": "package poker_test\n\nimport (\n\t\"github.com/quii/learn-go-with-tests/command-line/v3\"\n\t\"io\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestCLI(t *testing.T) {\n\n\tt.Run(\"record chris win from user input\", func(t *testing.T) {\n\t\tin := strings.NewReader(\"Chris wins\\n\")\n\t\tplayerStore := &poker.StubPlayerStore{}\n\n\t\tcli := poker.NewCLI(playerStore, in)\n\t\tcli.PlayPoker()\n\n\t\tpoker.AssertPlayerWin(t, playerStore, \"Chris\")\n\t})\n\n\tt.Run(\"record cleo win from user input\", func(t *testing.T) {\n\t\tin := strings.NewReader(\"Cleo wins\\n\")\n\t\tplayerStore := &poker.StubPlayerStore{}\n\n\t\tcli := poker.NewCLI(playerStore, in)\n\t\tcli.PlayPoker()\n\n\t\tpoker.AssertPlayerWin(t, playerStore, \"Cleo\")\n\t})\n\n\tt.Run(\"do not read beyond the first newline\", func(t *testing.T) {\n\t\tin := failOnEndReader{\n\t\t\tt,\n\t\t\tstrings.NewReader(\"Chris wins\\n hello there\"),\n\t\t}\n\n\t\tplayerStore := &poker.StubPlayerStore{}\n\n\t\tcli := poker.NewCLI(playerStore, in)\n\t\tcli.PlayPoker()\n\t})\n\n}\n\ntype failOnEndReader struct {\n\tt   *testing.T\n\trdr io.Reader\n}\n\nfunc (m failOnEndReader) Read(p []byte) (n int, err error) {\n\n\tn, err = m.rdr.Read(p)\n\n\tif n == 0 || err == io.EOF {\n\t\tm.t.Fatal(\"Read to the end when you shouldn't have\")\n\t}\n\n\treturn n, err\n}\n"
  },
  {
    "path": "command-line/v3/cmd/cli/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tpoker \"github.com/quii/learn-go-with-tests/command-line/v3\"\n)\n\nconst dbFileName = \"game.db.json\"\n\nfunc main() {\n\tstore, close, err := poker.FileSystemPlayerStoreFromFile(dbFileName)\n\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer close()\n\n\tfmt.Println(\"Let's play poker\")\n\tfmt.Println(\"Type {Name} wins to record a win\")\n\tpoker.NewCLI(store, os.Stdin).PlayPoker()\n}\n"
  },
  {
    "path": "command-line/v3/cmd/webserver/main.go",
    "content": "package main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n\n\tpoker \"github.com/quii/learn-go-with-tests/command-line/v3\"\n)\n\nconst dbFileName = \"game.db.json\"\n\nfunc main() {\n\tstore, close, err := poker.FileSystemPlayerStoreFromFile(dbFileName)\n\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer close()\n\n\tserver := poker.NewPlayerServer(store)\n\n\tlog.Fatal(http.ListenAndServe(\":5000\", server))\n}\n"
  },
  {
    "path": "command-line/v3/file_system_store.go",
    "content": "package poker\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"sort\"\n)\n\n// FileSystemPlayerStore stores players in the filesystem.\ntype FileSystemPlayerStore struct {\n\tdatabase *json.Encoder\n\tleague   League\n}\n\n// NewFileSystemPlayerStore creates a FileSystemPlayerStore initialising the store if needed.\nfunc NewFileSystemPlayerStore(file *os.File) (*FileSystemPlayerStore, error) {\n\n\terr := initialisePlayerDBFile(file)\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"problem initialising player db file, %v\", err)\n\t}\n\n\tleague, err := NewLeague(file)\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"problem loading player store from file %s, %v\", file.Name(), err)\n\t}\n\n\treturn &FileSystemPlayerStore{\n\t\tdatabase: json.NewEncoder(&tape{file}),\n\t\tleague:   league,\n\t}, nil\n}\n\n// FileSystemPlayerStoreFromFile creates a PlayerStore from the contents of a JSON file found at path.\nfunc FileSystemPlayerStoreFromFile(path string) (*FileSystemPlayerStore, func(), error) {\n\tdb, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0666)\n\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"problem opening %s, %v\", path, err)\n\t}\n\n\tcloseFunc := func() {\n\t\tdb.Close()\n\t}\n\n\tstore, err := NewFileSystemPlayerStore(db)\n\n\tif err != nil {\n\t\tdb.Close()\n\t\treturn nil, nil, fmt.Errorf(\"problem creating file system player store, %v \", err)\n\t}\n\n\treturn store, closeFunc, nil\n}\n\nfunc initialisePlayerDBFile(file *os.File) error {\n\tfile.Seek(0, io.SeekStart)\n\n\tinfo, err := file.Stat()\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"problem getting file info from file %s, %v\", file.Name(), err)\n\t}\n\n\tif info.Size() == 0 {\n\t\tfile.Write([]byte(\"[]\"))\n\t\tfile.Seek(0, io.SeekStart)\n\t}\n\n\treturn nil\n}\n\n// GetLeague returns the scores of all the players.\nfunc (f *FileSystemPlayerStore) GetLeague() League {\n\tsort.Slice(f.league, func(i, j int) bool {\n\t\treturn f.league[i].Wins > f.league[j].Wins\n\t})\n\treturn f.league\n}\n\n// GetPlayerScore retrieves a player's score.\nfunc (f *FileSystemPlayerStore) GetPlayerScore(name string) int {\n\n\tplayer := f.league.Find(name)\n\n\tif player != nil {\n\t\treturn player.Wins\n\t}\n\n\treturn 0\n}\n\n// RecordWin will store a win for a player, incrementing wins if already known.\nfunc (f *FileSystemPlayerStore) RecordWin(name string) {\n\tplayer := f.league.Find(name)\n\n\tif player != nil {\n\t\tplayer.Wins++\n\t} else {\n\t\tf.league = append(f.league, Player{name, 1})\n\t}\n\n\tf.database.Encode(f.league)\n}\n"
  },
  {
    "path": "command-line/v3/file_system_store_test.go",
    "content": "package poker\n\nimport (\n\t\"os\"\n\t\"testing\"\n)\n\nfunc createTempFile(t testing.TB, initialData string) (*os.File, func()) {\n\tt.Helper()\n\n\ttmpfile, err := os.CreateTemp(\"\", \"db\")\n\n\tif err != nil {\n\t\tt.Fatalf(\"could not create temp file %v\", err)\n\t}\n\n\ttmpfile.Write([]byte(initialData))\n\n\tremoveFile := func() {\n\t\tos.Remove(tmpfile.Name())\n\t}\n\n\treturn tmpfile, removeFile\n}\n\nfunc TestFileSystemStore(t *testing.T) {\n\n\tt.Run(\"league sorted\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tgot := store.GetLeague()\n\n\t\twant := []Player{\n\t\t\t{\"Chris\", 33},\n\t\t\t{\"Cleo\", 10},\n\t\t}\n\n\t\tassertLeague(t, got, want)\n\n\t\t// read again\n\t\tgot = store.GetLeague()\n\t\tassertLeague(t, got, want)\n\t})\n\n\tt.Run(\"get player score\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tgot := store.GetPlayerScore(\"Chris\")\n\t\twant := 33\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"store wins for existing players\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tstore.RecordWin(\"Chris\")\n\n\t\tgot := store.GetPlayerScore(\"Chris\")\n\t\twant := 34\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"store wins for existing players\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tstore.RecordWin(\"Pepper\")\n\n\t\tgot := store.GetPlayerScore(\"Pepper\")\n\t\twant := 1\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"works with an empty file\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, \"\")\n\t\tdefer cleanDatabase()\n\n\t\t_, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\t})\n}\n\nfunc assertScoreEquals(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"got %d want %d\", got, want)\n\t}\n}\n\nfunc assertNoError(t testing.TB, err error) {\n\tt.Helper()\n\tif err != nil {\n\t\tt.Fatalf(\"didn't expect an error but got one, %v\", err)\n\t}\n}\n"
  },
  {
    "path": "command-line/v3/league.go",
    "content": "package poker\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n)\n\n// League stores a collection of players.\ntype League []Player\n\n// Find tries to return a player from a league.\nfunc (l League) Find(name string) *Player {\n\tfor i, p := range l {\n\t\tif p.Name == name {\n\t\t\treturn &l[i]\n\t\t}\n\t}\n\treturn nil\n}\n\n// NewLeague creates a league from JSON.\nfunc NewLeague(rdr io.Reader) (League, error) {\n\tvar league []Player\n\terr := json.NewDecoder(rdr).Decode(&league)\n\n\tif err != nil {\n\t\terr = fmt.Errorf(\"problem parsing league, %v\", err)\n\t}\n\n\treturn league, err\n}\n"
  },
  {
    "path": "command-line/v3/server.go",
    "content": "package poker\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n)\n\n// PlayerStore stores score information about players.\ntype PlayerStore interface {\n\tGetPlayerScore(name string) int\n\tRecordWin(name string)\n\tGetLeague() League\n}\n\n// Player stores a name with a number of wins.\ntype Player struct {\n\tName string\n\tWins int\n}\n\n// PlayerServer is a HTTP interface for player information.\ntype PlayerServer struct {\n\tstore PlayerStore\n\thttp.Handler\n}\n\nconst jsonContentType = \"application/json\"\n\n// NewPlayerServer creates a PlayerServer with routing configured.\nfunc NewPlayerServer(store PlayerStore) *PlayerServer {\n\tp := new(PlayerServer)\n\n\tp.store = store\n\n\trouter := http.NewServeMux()\n\trouter.Handle(\"/league\", http.HandlerFunc(p.leagueHandler))\n\trouter.Handle(\"/players/\", http.HandlerFunc(p.playersHandler))\n\n\tp.Handler = router\n\n\treturn p\n}\n\nfunc (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) {\n\tw.Header().Set(\"content-type\", jsonContentType)\n\tjson.NewEncoder(w).Encode(p.store.GetLeague())\n}\n\nfunc (p *PlayerServer) playersHandler(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\n\tswitch r.Method {\n\tcase http.MethodPost:\n\t\tp.processWin(w, player)\n\tcase http.MethodGet:\n\t\tp.showScore(w, player)\n\t}\n}\n\nfunc (p *PlayerServer) showScore(w http.ResponseWriter, player string) {\n\tscore := p.store.GetPlayerScore(player)\n\n\tif score == 0 {\n\t\tw.WriteHeader(http.StatusNotFound)\n\t}\n\n\tfmt.Fprint(w, score)\n}\n\nfunc (p *PlayerServer) processWin(w http.ResponseWriter, player string) {\n\tp.store.RecordWin(player)\n\tw.WriteHeader(http.StatusAccepted)\n}\n"
  },
  {
    "path": "command-line/v3/server_integration_test.go",
    "content": "package poker\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestRecordingWinsAndRetrievingThem(t *testing.T) {\n\tdatabase, cleanDatabase := createTempFile(t, `[]`)\n\tdefer cleanDatabase()\n\tstore, err := NewFileSystemPlayerStore(database)\n\n\tassertNoError(t, err)\n\n\tserver := NewPlayerServer(store)\n\tplayer := \"Pepper\"\n\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\n\tt.Run(\"get score\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newGetScoreRequest(player))\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\n\t\tassertResponseBody(t, response.Body.String(), \"3\")\n\t})\n\n\tt.Run(\"get league\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newLeagueRequest())\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\t\twant := []Player{\n\t\t\t{\"Pepper\", 3},\n\t\t}\n\t\tassertLeague(t, got, want)\n\t})\n}\n"
  },
  {
    "path": "command-line/v3/server_test.go",
    "content": "package poker\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestGETPlayers(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{\n\t\t\t\"Pepper\": 20,\n\t\t\t\"Floyd\":  10,\n\t\t},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"returns Pepper's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Pepper\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"20\")\n\t})\n\n\tt.Run(\"returns Floyd's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Floyd\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"10\")\n\t})\n\n\tt.Run(\"returns 404 on missing players\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Apollo\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusNotFound)\n\t})\n}\n\nfunc TestStoreWins(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"it records wins on POST\", func(t *testing.T) {\n\t\tplayer := \"Pepper\"\n\n\t\trequest := newPostWinRequest(player)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusAccepted)\n\t\tAssertPlayerWin(t, &store, player)\n\t})\n}\n\nfunc TestLeague(t *testing.T) {\n\n\tt.Run(\"it returns the league table as JSON\", func(t *testing.T) {\n\t\twantedLeague := []Player{\n\t\t\t{\"Cleo\", 32},\n\t\t\t{\"Chris\", 20},\n\t\t\t{\"Tiest\", 14},\n\t\t}\n\n\t\tstore := StubPlayerStore{nil, nil, wantedLeague}\n\t\tserver := NewPlayerServer(&store)\n\n\t\trequest := newLeagueRequest()\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertLeague(t, got, wantedLeague)\n\t\tassertContentType(t, response, jsonContentType)\n\n\t})\n}\n\nfunc assertContentType(t testing.TB, response *httptest.ResponseRecorder, want string) {\n\tt.Helper()\n\tif response.Header().Get(\"content-type\") != want {\n\t\tt.Errorf(\"response did not have content-type of %s, got %v\", want, response.Result().Header)\n\t}\n}\n\nfunc getLeagueFromResponse(t testing.TB, body io.Reader) []Player {\n\tt.Helper()\n\tleague, err := NewLeague(body)\n\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to parse response from server %q into slice of Player, '%v'\", body, err)\n\t}\n\n\treturn league\n}\n\nfunc assertLeague(t testing.TB, got, want []Player) {\n\tt.Helper()\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %v want %v\", got, want)\n\t}\n}\n\nfunc assertStatus(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"did not get correct status, got %d, want %d\", got, want)\n\t}\n}\n\nfunc newLeagueRequest() *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, \"/league\", nil)\n\treturn req\n}\n\nfunc newGetScoreRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc newPostWinRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodPost, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc assertResponseBody(t testing.TB, got, want string) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"response body is wrong, got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "command-line/v3/tape.go",
    "content": "package poker\n\nimport (\n\t\"io\"\n\t\"os\"\n)\n\ntype tape struct {\n\tfile *os.File\n}\n\nfunc (t *tape) Write(p []byte) (n int, err error) {\n\tt.file.Truncate(0)\n\tt.file.Seek(0, io.SeekStart)\n\treturn t.file.Write(p)\n}\n"
  },
  {
    "path": "command-line/v3/tape_test.go",
    "content": "package poker\n\nimport (\n\t\"io\"\n\t\"testing\"\n)\n\nfunc TestTape_Write(t *testing.T) {\n\tfile, clean := createTempFile(t, \"12345\")\n\tdefer clean()\n\n\ttape := &tape{file}\n\n\ttape.Write([]byte(\"abc\"))\n\n\tfile.Seek(0, io.SeekStart)\n\tnewFileContents, _ := io.ReadAll(file)\n\n\tgot := string(newFileContents)\n\twant := \"abc\"\n\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "command-line/v3/testing.go",
    "content": "package poker\n\nimport \"testing\"\n\n// StubPlayerStore implements PlayerStore for testing purposes.\ntype StubPlayerStore struct {\n\tScores   map[string]int\n\tWinCalls []string\n\tLeague   []Player\n}\n\n// GetPlayerScore returns a score from Scores.\nfunc (s *StubPlayerStore) GetPlayerScore(name string) int {\n\tscore := s.Scores[name]\n\treturn score\n}\n\n// RecordWin will record a win to WinCalls.\nfunc (s *StubPlayerStore) RecordWin(name string) {\n\ts.WinCalls = append(s.WinCalls, name)\n}\n\n// GetLeague returns League.\nfunc (s *StubPlayerStore) GetLeague() League {\n\treturn s.League\n}\n\n// AssertPlayerWin allows you to spy on the store's calls to RecordWin.\nfunc AssertPlayerWin(t testing.TB, store *StubPlayerStore, winner string) {\n\tt.Helper()\n\n\tif len(store.WinCalls) != 1 {\n\t\tt.Fatalf(\"got %d calls to RecordWin want %d\", len(store.WinCalls), 1)\n\t}\n\n\tif store.WinCalls[0] != winner {\n\t\tt.Errorf(\"did not store the correct winner got %q want %q\", store.WinCalls[0], winner)\n\t}\n}\n"
  },
  {
    "path": "command-line.md",
    "content": "# Command line and project structure\n\n**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/main/command-line)**\n\nOur product owner now wants to _pivot_ by introducing a second application - a command line application.\n\nFor now, it will just need to be able to record a player's win when the user types `Ruth wins`. The intention is to eventually be a tool for helping users play poker.\n\nThe product owner wants the database to be shared amongst the two applications so that the league updates according to wins recorded in the new application.\n\n## A reminder of the code\n\nWe have an application with a `main.go` file that launches an HTTP server. The HTTP server won't be interesting to us for this exercise but the abstraction it uses will. It depends on a `PlayerStore`.\n\n```go\ntype PlayerStore interface {\n\tGetPlayerScore(name string) int\n\tRecordWin(name string)\n\tGetLeague() League\n}\n```\n\nIn the previous chapter, we made a `FileSystemPlayerStore` which implements that interface. We should be able to re-use some of this for our new application.\n\n## Some project refactoring first\n\nOur project now needs to create two binaries, our existing web server and the command line app.\n\nBefore we get stuck into our new work we should structure our project to accommodate this.\n\nSo far all the code has lived in one folder, in a path looking like this\n\n`$GOPATH/src/github.com/your-name/my-app`\n\nIn order for you to make an application in Go, you need a `main` function inside a `package main`. So far all of our \"domain\" code has lived inside `package main` and our `func main` can reference everything.\n\nThis was fine so far and it is good practice not to go over-the-top with package structure. If you take the time to look through the standard library you will see very little in the way of lots of folders and structure.\n\nThankfully it's pretty straightforward to add structure _when you need it_.\n\nInside the existing project create a `cmd` directory with a `webserver` directory inside that (e.g `mkdir -p cmd/webserver`).\n\nMove the `main.go` inside there.\n\nIf you have `tree` installed you should run it and your structure should look like this\n\n```\n.\n|-- file_system_store.go\n|-- file_system_store_test.go\n|-- cmd\n|   |-- webserver\n|       |-- main.go\n|-- league.go\n|-- server.go\n|-- server_integration_test.go\n|-- server_test.go\n|-- tape.go\n|-- tape_test.go\n```\n\nWe now effectively have a separation between our application and the library code but we now need to change some package names. Remember when you build a Go application its package _must_ be `main`.\n\nChange all the other code to have a package called `poker`.\n\nFinally, we need to import this package into `main.go` so we can use it to create our web server. Then we can use our library code by using `poker.FunctionName`.\n\nThe paths will be different on your computer, but it should be similar to this:\n\n```go\n// cmd/webserver/main.go\npackage main\n\nimport (\n\t\"github.com/quii/learn-go-with-tests/command-line/v1\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n)\n\nconst dbFileName = \"game.db.json\"\n\nfunc main() {\n\tdb, err := os.OpenFile(dbFileName, os.O_RDWR|os.O_CREATE, 0666)\n\n\tif err != nil {\n\t\tlog.Fatalf(\"problem opening %s %v\", dbFileName, err)\n\t}\n\n\tstore, err := poker.NewFileSystemPlayerStore(db)\n\n\tif err != nil {\n\t\tlog.Fatalf(\"problem creating file system player store, %v \", err)\n\t}\n\n\tserver := poker.NewPlayerServer(store)\n\n\tlog.Fatal(http.ListenAndServe(\":5000\", server))\n}\n```\n\nThe full path may seem a bit jarring, but this is how you can import _any_ publicly available library into your code.\n\nBy separating our domain code into a separate package and committing it to a public repo like GitHub any Go developer can write their own code which imports that package the features we've written available. The first time you try and run it will complain it is not existing but all you need to do is run `go get`.\n\nIn addition, users can view [the documentation at pkg.go.dev](https://pkg.go.dev/github.com/quii/learn-go-with-tests/command-line/v1).\n\n### Final checks\n\n- Inside the root run `go test` and check they're still passing\n- Go inside our `cmd/webserver` and do `go run main.go`\n  - Visit `http://localhost:5000/league` and you should see it's still working\n\n### Walking skeleton\n\nBefore we get stuck into writing tests, let's add a new application that our project will build. Create another directory inside `cmd` called `cli` (command line interface) and add a `main.go` with the following\n\n```go\n// cmd/cli/main.go\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(\"Let's play poker\")\n}\n```\n\nThe first requirement we'll tackle is recording a win when the user types `{PlayerName} wins`.\n\n## Write the test first\n\nWe know we need to make something called `CLI` which will allow us to `Play` poker. It'll need to read user input and then record wins to a `PlayerStore`.\n\nBefore we jump too far ahead though, let's just write a test to check it integrates with the `PlayerStore` how we'd like.\n\nInside `CLI_test.go` (in the root of the project, not inside `cmd`)\n\n```go\n// CLI_test.go\npackage poker\n\nimport \"testing\"\n\nfunc TestCLI(t *testing.T) {\n\tplayerStore := &StubPlayerStore{}\n\tcli := &CLI{playerStore}\n\tcli.PlayPoker()\n\n\tif len(playerStore.winCalls) != 1 {\n\t\tt.Fatal(\"expected a win call but didn't get any\")\n\t}\n}\n```\n\n- We can use our `StubPlayerStore` from other tests\n- We pass in our dependency into our not yet existing `CLI` type\n- Trigger the game by an unwritten `PlayPoker` method\n- Check that a win is recorded\n\n## Try to run the test\n\n```\n# github.com/quii/learn-go-with-tests/command-line/v2\n./cli_test.go:25:10: undefined: CLI\n```\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\nAt this point, you should be comfortable enough to create our new `CLI` struct with the respective field for our dependency and add a method.\n\nYou should end up with code like this\n\n```go\n// CLI.go\npackage poker\n\ntype CLI struct {\n\tplayerStore PlayerStore\n}\n\nfunc (cli *CLI) PlayPoker() {}\n```\n\nRemember we're just trying to get the test running so we can check the test fails how we'd hope\n\n```\n--- FAIL: TestCLI (0.00s)\n    cli_test.go:30: expected a win call but didn't get any\nFAIL\n```\n\n## Write enough code to make it pass\n\n```go\n//CLI.go\nfunc (cli *CLI) PlayPoker() {\n\tcli.playerStore.RecordWin(\"Cleo\")\n}\n```\n\nThat should make it pass.\n\nNext, we need to simulate reading from `Stdin` (the input from the user) so that we can record wins for specific players.\n\nLet's extend our test to exercise this.\n\n## Write the test first\n\n```go\n//CLI_test.go\nfunc TestCLI(t *testing.T) {\n\tin := strings.NewReader(\"Chris wins\\n\")\n\tplayerStore := &StubPlayerStore{}\n\n\tcli := &CLI{playerStore, in}\n\tcli.PlayPoker()\n\n\tif len(playerStore.winCalls) != 1 {\n\t\tt.Fatal(\"expected a win call but didn't get any\")\n\t}\n\n\tgot := playerStore.winCalls[0]\n\twant := \"Chris\"\n\n\tif got != want {\n\t\tt.Errorf(\"didn't record correct winner, got %q, want %q\", got, want)\n\t}\n}\n```\n\n`os.Stdin` is what we'll use in `main` to capture the user's input. It is a `*File` under the hood which means it implements `io.Reader` which as we know by now is a handy way of capturing text.\n\nWe create an `io.Reader` in our test using the handy `strings.NewReader`, filling it with what we expect the user to type.\n\n## Try to run the test\n\n`./CLI_test.go:12:32: too many values in struct initializer`\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\nWe need to add our new dependency into `CLI`.\n\n```go\n//CLI.go\ntype CLI struct {\n\tplayerStore PlayerStore\n\tin          io.Reader\n}\n```\n\n```\n--- FAIL: TestCLI (0.00s)\n    CLI_test.go:23: didn't record the correct winner, got 'Cleo', want 'Chris'\nFAIL\n```\n\n## Write enough code to make it pass\n\nRemember to do the strictly easiest thing first\n\n```go\nfunc (cli *CLI) PlayPoker() {\n\tcli.playerStore.RecordWin(\"Chris\")\n}\n```\n\nThe test passes. We'll add another test to force us to write some real code next, but first, let's refactor.\n\n## Refactor\n\nIn `server_test` we earlier did checks to see if wins are recorded as we have here. Let's DRY that assertion up into a helper\n\n```go\n//server_test.go\nfunc assertPlayerWin(t testing.TB, store *StubPlayerStore, winner string) {\n\tt.Helper()\n\n\tif len(store.winCalls) != 1 {\n\t\tt.Fatalf(\"got %d calls to RecordWin want %d\", len(store.winCalls), 1)\n\t}\n\n\tif store.winCalls[0] != winner {\n\t\tt.Errorf(\"did not store correct winner got %q want %q\", store.winCalls[0], winner)\n\t}\n}\n```\n\nNow replace the assertions in both `server_test.go` and `CLI_test.go`.\n\nThe test should now read like so\n\n```go\n//CLI_test.go\nfunc TestCLI(t *testing.T) {\n\tin := strings.NewReader(\"Chris wins\\n\")\n\tplayerStore := &StubPlayerStore{}\n\n\tcli := &CLI{playerStore, in}\n\tcli.PlayPoker()\n\n\tassertPlayerWin(t, playerStore, \"Chris\")\n}\n```\n\nNow let's write _another_ test with different user input to force us into actually reading it.\n\n## Write the test first\n\n```go\n//CLI_test.go\nfunc TestCLI(t *testing.T) {\n\n\tt.Run(\"record chris win from user input\", func(t *testing.T) {\n\t\tin := strings.NewReader(\"Chris wins\\n\")\n\t\tplayerStore := &StubPlayerStore{}\n\n\t\tcli := &CLI{playerStore, in}\n\t\tcli.PlayPoker()\n\n\t\tassertPlayerWin(t, playerStore, \"Chris\")\n\t})\n\n\tt.Run(\"record cleo win from user input\", func(t *testing.T) {\n\t\tin := strings.NewReader(\"Cleo wins\\n\")\n\t\tplayerStore := &StubPlayerStore{}\n\n\t\tcli := &CLI{playerStore, in}\n\t\tcli.PlayPoker()\n\n\t\tassertPlayerWin(t, playerStore, \"Cleo\")\n\t})\n\n}\n```\n\n## Try to run the test\n\n```\n=== RUN   TestCLI\n--- FAIL: TestCLI (0.00s)\n=== RUN   TestCLI/record_chris_win_from_user_input\n    --- PASS: TestCLI/record_chris_win_from_user_input (0.00s)\n=== RUN   TestCLI/record_cleo_win_from_user_input\n    --- FAIL: TestCLI/record_cleo_win_from_user_input (0.00s)\n        CLI_test.go:27: did not store correct winner got 'Chris' want 'Cleo'\nFAIL\n```\n\n## Write enough code to make it pass\n\nWe'll use a [`bufio.Scanner`](https://golang.org/pkg/bufio/) to read the input from the `io.Reader`.\n\n> Package bufio implements buffered I/O. It wraps an io.Reader or io.Writer object, creating another object (Reader or Writer) that also implements the interface but provides buffering and some help for textual I/O.\n\nUpdate the code to the following\n\n```go\n//CLI.go\ntype CLI struct {\n\tplayerStore PlayerStore\n\tin          io.Reader\n}\n\nfunc (cli *CLI) PlayPoker() {\n\treader := bufio.NewScanner(cli.in)\n\treader.Scan()\n\tcli.playerStore.RecordWin(extractWinner(reader.Text()))\n}\n\nfunc extractWinner(userInput string) string {\n\treturn strings.Replace(userInput, \" wins\", \"\", 1)\n}\n```\n\nThe tests will now pass.\n\n- `Scanner.Scan()` will read up to a newline.\n- We then use `Scanner.Text()` to return the `string` the scanner read to.\n\nNow that we have some passing tests, we should wire this up into `main`. Remember we should always strive to have fully-integrated working software as quickly as we can.\n\nIn `main.go` add the following and run it. (you may have to adjust the path of the second dependency to match what's on your computer)\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"github.com/quii/learn-go-with-tests/command-line/v3\"\n\t\"log\"\n\t\"os\"\n)\n\nconst dbFileName = \"game.db.json\"\n\nfunc main() {\n\tfmt.Println(\"Let's play poker\")\n\tfmt.Println(\"Type {Name} wins to record a win\")\n\n\tdb, err := os.OpenFile(dbFileName, os.O_RDWR|os.O_CREATE, 0666)\n\n\tif err != nil {\n\t\tlog.Fatalf(\"problem opening %s %v\", dbFileName, err)\n\t}\n\n\tstore, err := poker.NewFileSystemPlayerStore(db)\n\n\tif err != nil {\n\t\tlog.Fatalf(\"problem creating file system player store, %v \", err)\n\t}\n\n\tgame := poker.CLI{store, os.Stdin}\n\tgame.PlayPoker()\n}\n```\n\nYou should get an error\n\n```\ncommand-line/v3/cmd/cli/main.go:32:25: implicit assignment of unexported field 'playerStore' in poker.CLI literal\ncommand-line/v3/cmd/cli/main.go:32:34: implicit assignment of unexported field 'in' in poker.CLI literal\n```\n\nWhat's happening here is because we are trying to assign to the fields `playerStore` and `in` in `CLI`. These are unexported (private) fields. We _could_ do this in our test code because our test is in the same package as `CLI` (`poker`). But our `main` is in package `main` so it does not have access.\n\nThis highlights the importance of _integrating your work_. We rightfully made the dependencies of our `CLI` private (because we don't want them exposed to users of `CLI`s) but haven't made a way for users to construct it.\n\nIs there a way to have caught this problem earlier?\n\n### `package mypackage_test`\n\nIn all other examples so far, when we make a test file we declare it as being in the same package that we are testing.\n\nThis is fine and it means on the odd occasion where we want to test something internal to the package we have access to the unexported types.\n\nBut given we have advocated for _not_ testing internal things _generally_, can Go help enforce that? What if we could test our code where we only have access to the exported types (like our `main` does)?\n\nWhen you're writing a project with multiple packages I would strongly recommend that your test package name has `_test` at the end. When you do this you will only be able to have access to the public types in your package. This would help with this specific case but also helps enforce the discipline of only testing public APIs. If you still wish to test internals you can make a separate test with the package you want to test.\n\nAn adage with TDD is that if you cannot test your code then it is probably hard for users of your code to integrate with it. Using `package foo_test` will help with this by forcing you to test your code as if you are importing it like users of your package will.\n\nBefore fixing `main` let's change the package of our test inside `CLI_test.go` to `poker_test`.\n\nIf you have a well-configured IDE you will suddenly see a lot of red! If you run the compiler you'll get the following errors\n\n```\n./CLI_test.go:12:19: undefined: StubPlayerStore\n./CLI_test.go:17:3: undefined: assertPlayerWin\n./CLI_test.go:22:19: undefined: StubPlayerStore\n./CLI_test.go:27:3: undefined: assertPlayerWin\n```\n\nWe have now stumbled into more questions on package design. In order to test our software we made unexported stubs and helper functions which are no longer available for us to use in our `CLI_test` because the helpers are defined in the `_test.go` files in the `poker` package.\n\n#### Do we want to have our stubs and helpers 'public'?\n\nThis is a subjective discussion. One could argue that you do not want to pollute your package's API with code to facilitate tests.\n\nIn the presentation [\"Advanced Testing with Go\"](https://speakerdeck.com/mitchellh/advanced-testing-with-go?slide=53) by Mitchell Hashimoto, it is described how at HashiCorp they advocate doing this so that users of the package can write tests without having to re-invent the wheel writing stubs. In our case, this would mean anyone using our `poker` package won't have to create their own stub `PlayerStore` if they wish to work with our code.\n\nAnecdotally I have used this technique in other shared packages and it has proved extremely useful in terms of users saving time when integrating with our packages.\n\nSo let's create a file called `testing.go` and add our stub and our helpers.\n\n```go\n// testing.go\npackage poker\n\nimport \"testing\"\n\ntype StubPlayerStore struct {\n\tscores   map[string]int\n\twinCalls []string\n\tleague   []Player\n}\n\nfunc (s *StubPlayerStore) GetPlayerScore(name string) int {\n\tscore := s.scores[name]\n\treturn score\n}\n\nfunc (s *StubPlayerStore) RecordWin(name string) {\n\ts.winCalls = append(s.winCalls, name)\n}\n\nfunc (s *StubPlayerStore) GetLeague() League {\n\treturn s.league\n}\n\nfunc AssertPlayerWin(t testing.TB, store *StubPlayerStore, winner string) {\n\tt.Helper()\n\n\tif len(store.winCalls) != 1 {\n\t\tt.Fatalf(\"got %d calls to RecordWin want %d\", len(store.winCalls), 1)\n\t}\n\n\tif store.winCalls[0] != winner {\n\t\tt.Errorf(\"did not store correct winner got %q want %q\", store.winCalls[0], winner)\n\t}\n}\n\n// todo for you - the rest of the helpers\n```\n\nYou'll need to make the helpers public (remember exporting is done with a capital letter at the start) if you want them to be exposed to importers of our package.\n\nIn our `CLI` test you'll need to call the code as if you were using it within a different package.\n\n```go\n//CLI_test.go\nfunc TestCLI(t *testing.T) {\n\n\tt.Run(\"record chris win from user input\", func(t *testing.T) {\n\t\tin := strings.NewReader(\"Chris wins\\n\")\n\t\tplayerStore := &poker.StubPlayerStore{}\n\n\t\tcli := &poker.CLI{playerStore, in}\n\t\tcli.PlayPoker()\n\n\t\tpoker.AssertPlayerWin(t, playerStore, \"Chris\")\n\t})\n\n\tt.Run(\"record cleo win from user input\", func(t *testing.T) {\n\t\tin := strings.NewReader(\"Cleo wins\\n\")\n\t\tplayerStore := &poker.StubPlayerStore{}\n\n\t\tcli := &poker.CLI{playerStore, in}\n\t\tcli.PlayPoker()\n\n\t\tpoker.AssertPlayerWin(t, playerStore, \"Cleo\")\n\t})\n\n}\n```\n\nYou'll now see we have the same problems as we had in `main`\n\n```\n./CLI_test.go:15:26: implicit assignment of unexported field 'playerStore' in poker.CLI literal\n./CLI_test.go:15:39: implicit assignment of unexported field 'in' in poker.CLI literal\n./CLI_test.go:25:26: implicit assignment of unexported field 'playerStore' in poker.CLI literal\n./CLI_test.go:25:39: implicit assignment of unexported field 'in' in poker.CLI literal\n```\n\nThe easiest way to get around this is to make a constructor as we have for other types. We'll also change `CLI` so it stores a `bufio.Scanner` instead of the reader as it's now automatically wrapped at construction time.\n\n```go\n//CLI.go\ntype CLI struct {\n\tplayerStore PlayerStore\n\tin          *bufio.Scanner\n}\n\nfunc NewCLI(store PlayerStore, in io.Reader) *CLI {\n\treturn &CLI{\n\t\tplayerStore: store,\n\t\tin:          bufio.NewScanner(in),\n\t}\n}\n```\n\nBy doing this, we can then simplify and refactor our reading code\n\n```go\n//CLI.go\nfunc (cli *CLI) PlayPoker() {\n\tuserInput := cli.readLine()\n\tcli.playerStore.RecordWin(extractWinner(userInput))\n}\n\nfunc extractWinner(userInput string) string {\n\treturn strings.Replace(userInput, \" wins\", \"\", 1)\n}\n\nfunc (cli *CLI) readLine() string {\n\tcli.in.Scan()\n\treturn cli.in.Text()\n}\n```\n\nChange the test to use the constructor instead and we should be back to the tests passing.\n\nFinally, we can go back to our new `main.go` and use the constructor we just made\n\n```go\n//cmd/cli/main.go\ngame := poker.NewCLI(store, os.Stdin)\n```\n\nTry and run it, type \"Bob wins\".\n\n### Refactor\n\nWe have some repetition in our respective applications where we are opening a file and creating a `file_system_store` from its contents. This feels like a slight weakness in our package's design so we should make a function in it to encapsulate opening a file from a path and returning you the `PlayerStore`.\n\n```go\n//file_system_store.go\nfunc FileSystemPlayerStoreFromFile(path string) (*FileSystemPlayerStore, func(), error) {\n\tdb, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0666)\n\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"problem opening %s %v\", path, err)\n\t}\n\n\tcloseFunc := func() {\n\t\tdb.Close()\n\t}\n\n\tstore, err := NewFileSystemPlayerStore(db)\n\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"problem creating file system player store, %v \", err)\n\t}\n\n\treturn store, closeFunc, nil\n}\n```\n\nNow refactor both of our applications to use this function to create the store.\n\n#### CLI application code\n\n```go\n// cmd/cli/main.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"github.com/quii/learn-go-with-tests/command-line/v3\"\n\t\"log\"\n\t\"os\"\n)\n\nconst dbFileName = \"game.db.json\"\n\nfunc main() {\n\tstore, close, err := poker.FileSystemPlayerStoreFromFile(dbFileName)\n\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer close()\n\n\tfmt.Println(\"Let's play poker\")\n\tfmt.Println(\"Type {Name} wins to record a win\")\n\tpoker.NewCLI(store, os.Stdin).PlayPoker()\n}\n```\n\n#### Web server application code\n\n```go\n// cmd/webserver/main.go\npackage main\n\nimport (\n\t\"github.com/quii/learn-go-with-tests/command-line/v3\"\n\t\"log\"\n\t\"net/http\"\n)\n\nconst dbFileName = \"game.db.json\"\n\nfunc main() {\n\tstore, close, err := poker.FileSystemPlayerStoreFromFile(dbFileName)\n\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer close()\n\n\tserver := poker.NewPlayerServer(store)\n\n\tif err := http.ListenAndServe(\":5000\", server); err != nil {\n\t\tlog.Fatalf(\"could not listen on port 5000 %v\", err)\n\t}\n}\n```\n\nNotice the symmetry: despite being different user interfaces the setup is almost identical. This feels like good validation of our design so far.\nAnd notice also that `FileSystemPlayerStoreFromFile` returns a closing function, so we can close the underlying file once we are done using the Store.\n\n## Wrapping up\n\n### Package structure\n\nThis chapter meant we wanted to create two applications, re-using the domain code we've written so far. In order to do this, we needed to update our package structure so that we had separate folders for our respective `main`s.\n\nBy doing this we ran into integration problems due to unexported values so this further demonstrates the value of working in small \"slices\" and integrating often.\n\nWe learned how `mypackage_test` helps us create a testing environment which is the same experience for other packages integrating with your code, to help you catch integration problems and see how easy (or not!) your code is to work with.\n\n### Reading user input\n\nWe saw how reading from `os.Stdin` is very easy for us to work with as it implements `io.Reader`. We used `bufio.Scanner` to easily read line by line user input.\n\n### Simple abstractions leads to simpler code re-use\n\nIt was almost no effort to integrate `PlayerStore` into our new application (once we had made the package adjustments) and subsequently testing was very easy too because we decided to expose our stub version too.\n"
  },
  {
    "path": "concurrency/v1/check_website.go",
    "content": "package concurrency\n\nimport \"net/http\"\n\n// CheckWebsite returns true if the URL returns a 200 status code, false otherwise.\nfunc CheckWebsite(url string) bool {\n\tresponse, err := http.Head(url)\n\tif err != nil {\n\t\treturn false\n\t}\n\n\treturn response.StatusCode == http.StatusOK\n}\n"
  },
  {
    "path": "concurrency/v1/check_websites.go",
    "content": "package concurrency\n\n// WebsiteChecker checks a url, returning a bool.\ntype WebsiteChecker func(string) bool\n\n// CheckWebsites takes a WebsiteChecker and a slice of urls and returns  a map.\n// of urls to the result of checking each url with the WebsiteChecker function.\nfunc CheckWebsites(wc WebsiteChecker, urls []string) map[string]bool {\n\tresults := make(map[string]bool)\n\n\tfor _, url := range urls {\n\t\tresults[url] = wc(url)\n\t}\n\n\treturn results\n}\n"
  },
  {
    "path": "concurrency/v1/check_websites_benchmark_test.go",
    "content": "package concurrency\n\nimport (\n\t\"testing\"\n\t\"time\"\n)\n\nfunc slowStubWebsiteChecker(_ string) bool {\n\ttime.Sleep(20 * time.Millisecond)\n\treturn true\n}\n\nfunc BenchmarkCheckWebsites(b *testing.B) {\n\turls := make([]string, 100)\n\tfor i := 0; i < len(urls); i++ {\n\t\turls[i] = \"a url\"\n\t}\n\n\tfor b.Loop() {\n\t\tCheckWebsites(slowStubWebsiteChecker, urls)\n\t}\n}\n"
  },
  {
    "path": "concurrency/v1/check_websites_test.go",
    "content": "package concurrency\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc mockWebsiteChecker(url string) bool {\n\tif url == \"waat://furhurterwe.geds\" {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc TestCheckWebsites(t *testing.T) {\n\twebsites := []string{\n\t\t\"http://google.com\",\n\t\t\"http://blog.gypsydave5.com\",\n\t\t\"waat://furhurterwe.geds\",\n\t}\n\n\twant := map[string]bool{\n\t\t\"http://google.com\":          true,\n\t\t\"http://blog.gypsydave5.com\": true,\n\t\t\"waat://furhurterwe.geds\":    false,\n\t}\n\n\tgot := CheckWebsites(mockWebsiteChecker, websites)\n\n\tif !reflect.DeepEqual(want, got) {\n\t\tt.Fatalf(\"wanted %v, got %v\", want, got)\n\t}\n}\n"
  },
  {
    "path": "concurrency/v2/check_website.go",
    "content": "package concurrency\n\nimport \"net/http\"\n\n// CheckWebsite returns true if the URL returns a 200 status code, false otherwise.\nfunc CheckWebsite(url string) bool {\n\tresponse, err := http.Head(url)\n\tif err != nil {\n\t\treturn false\n\t}\n\n\treturn response.StatusCode == http.StatusOK\n}\n"
  },
  {
    "path": "concurrency/v2/check_websites.go",
    "content": "package concurrency\n\nimport (\n\t\"time\"\n)\n\n// WebsiteChecker checks a url, returning a bool.\ntype WebsiteChecker func(string) bool\n\n// CheckWebsites takes a WebsiteChecker and a slice of urls and returns  a map.\n// of urls to the result of checking each url with the WebsiteChecker function.\nfunc CheckWebsites(wc WebsiteChecker, urls []string) map[string]bool {\n\tresults := make(map[string]bool)\n\n\tfor _, url := range urls {\n\t\tgo func() {\n\t\t\tresults[url] = wc(url)\n\t\t}()\n\t}\n\n\ttime.Sleep(2 * time.Second)\n\n\treturn results\n}\n"
  },
  {
    "path": "concurrency/v2/check_websites_benchmark_test.go",
    "content": "package concurrency\n\nimport (\n\t\"testing\"\n\t\"time\"\n)\n\nfunc slowStubWebsiteChecker(_ string) bool {\n\ttime.Sleep(20 * time.Millisecond)\n\treturn true\n}\n\nfunc BenchmarkCheckWebsites(b *testing.B) {\n\turls := make([]string, 100)\n\tfor i := 0; i < len(urls); i++ {\n\t\turls[i] = \"a url\"\n\t}\n\n\tfor b.Loop() {\n\t\tCheckWebsites(slowStubWebsiteChecker, urls)\n\t}\n}\n"
  },
  {
    "path": "concurrency/v2/check_websites_test.go",
    "content": "package concurrency\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc mockWebsiteChecker(url string) bool {\n\tif url == \"waat://furhurterwe.geds\" {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc TestCheckWebsites(t *testing.T) {\n\twebsites := []string{\n\t\t\"http://google.com\",\n\t\t\"http://blog.gypsydave5.com\",\n\t\t\"waat://furhurterwe.geds\",\n\t}\n\n\twant := map[string]bool{\n\t\t\"http://google.com\":          true,\n\t\t\"http://blog.gypsydave5.com\": true,\n\t\t\"waat://furhurterwe.geds\":    false,\n\t}\n\n\tgot := CheckWebsites(mockWebsiteChecker, websites)\n\n\tif !reflect.DeepEqual(want, got) {\n\t\tt.Fatalf(\"wanted %v, got %v\", want, got)\n\t}\n}\n"
  },
  {
    "path": "concurrency/v3/check_website.go",
    "content": "package concurrency\n\nimport \"net/http\"\n\n// CheckWebsite returns true if the URL returns a 200 status code, false otherwise.\nfunc CheckWebsite(url string) bool {\n\tresponse, err := http.Head(url)\n\tif err != nil {\n\t\treturn false\n\t}\n\n\treturn response.StatusCode == http.StatusOK\n}\n"
  },
  {
    "path": "concurrency/v3/check_websites.go",
    "content": "package concurrency\n\n// WebsiteChecker checks a url, returning a bool.\ntype WebsiteChecker func(string) bool\ntype result struct {\n\tstring\n\tbool\n}\n\n// CheckWebsites takes a WebsiteChecker and a slice of urls and returns a map.\n// of urls to the result of checking each url with the WebsiteChecker function.\nfunc CheckWebsites(wc WebsiteChecker, urls []string) map[string]bool {\n\tresults := make(map[string]bool)\n\tresultChannel := make(chan result)\n\n\tfor _, url := range urls {\n\t\tgo func() {\n\t\t\tresultChannel <- result{url, wc(url)}\n\t\t}()\n\t}\n\n\tfor i := 0; i < len(urls); i++ {\n\t\tr := <-resultChannel\n\t\tresults[r.string] = r.bool\n\t}\n\n\treturn results\n}\n"
  },
  {
    "path": "concurrency/v3/check_websites_benchmark_test.go",
    "content": "package concurrency\n\nimport (\n\t\"testing\"\n\t\"time\"\n)\n\nfunc slowStubWebsiteChecker(_ string) bool {\n\ttime.Sleep(20 * time.Millisecond)\n\treturn true\n}\n\nfunc BenchmarkCheckWebsites(b *testing.B) {\n\turls := make([]string, 100)\n\tfor i := 0; i < len(urls); i++ {\n\t\turls[i] = \"a url\"\n\t}\n\n\tfor b.Loop() {\n\t\tCheckWebsites(slowStubWebsiteChecker, urls)\n\t}\n}\n"
  },
  {
    "path": "concurrency/v3/check_websites_test.go",
    "content": "package concurrency\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc mockWebsiteChecker(url string) bool {\n\tif url == \"waat://furhurterwe.geds\" {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc TestCheckWebsites(t *testing.T) {\n\twebsites := []string{\n\t\t\"http://google.com\",\n\t\t\"http://blog.gypsydave5.com\",\n\t\t\"waat://furhurterwe.geds\",\n\t}\n\n\twant := map[string]bool{\n\t\t\"http://google.com\":          true,\n\t\t\"http://blog.gypsydave5.com\": true,\n\t\t\"waat://furhurterwe.geds\":    false,\n\t}\n\n\tgot := CheckWebsites(mockWebsiteChecker, websites)\n\n\tif !reflect.DeepEqual(want, got) {\n\t\tt.Fatalf(\"wanted %v, got %v\", want, got)\n\t}\n}\n"
  },
  {
    "path": "concurrency.md",
    "content": "# Concurrency\n\n**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/main/concurrency)**\n\nHere's the setup: a colleague has written a function, `CheckWebsites`, that\nchecks the status of a list of URLs.\n\n```go\npackage concurrency\n\ntype WebsiteChecker func(string) bool\n\nfunc CheckWebsites(wc WebsiteChecker, urls []string) map[string]bool {\n\tresults := make(map[string]bool)\n\n\tfor _, url := range urls {\n\t\tresults[url] = wc(url)\n\t}\n\n\treturn results\n}\n```\n\nIt returns a map of each URL checked to a boolean value: `true` for a good\nresponse; `false` for a bad response.\n\nYou also have to pass in a `WebsiteChecker` which takes a single URL and returns\na boolean. This is used by the function to check all the websites.\n\nUsing [dependency injection][DI] has allowed them to test the function without\nmaking real HTTP calls, making it reliable and fast.\n\nHere's the test they've written:\n\n```go\npackage concurrency\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc mockWebsiteChecker(url string) bool {\n\treturn url != \"waat://furhurterwe.geds\"\n}\n\nfunc TestCheckWebsites(t *testing.T) {\n\twebsites := []string{\n\t\t\"http://google.com\",\n\t\t\"http://blog.gypsydave5.com\",\n\t\t\"waat://furhurterwe.geds\",\n\t}\n\n\twant := map[string]bool{\n\t\t\"http://google.com\":          true,\n\t\t\"http://blog.gypsydave5.com\": true,\n\t\t\"waat://furhurterwe.geds\":    false,\n\t}\n\n\tgot := CheckWebsites(mockWebsiteChecker, websites)\n\n\tif !reflect.DeepEqual(want, got) {\n\t\tt.Fatalf(\"wanted %v, got %v\", want, got)\n\t}\n}\n```\n\nThe function is in production and being used to check hundreds of websites. But\nyour colleague has started to get complaints that it's slow, so they've asked\nyou to help speed it up.\n\n## Write a test\n\nLet's use a benchmark to test the speed of `CheckWebsites` so that we can see the\neffect of our changes.\n\n```go\npackage concurrency\n\nimport (\n\t\"testing\"\n\t\"time\"\n)\n\nfunc slowStubWebsiteChecker(_ string) bool {\n\ttime.Sleep(20 * time.Millisecond)\n\treturn true\n}\n\nfunc BenchmarkCheckWebsites(b *testing.B) {\n\turls := make([]string, 100)\n\tfor i := 0; i < len(urls); i++ {\n\t\turls[i] = \"a url\"\n\t}\n\n\tfor b.Loop() {\n\t\tCheckWebsites(slowStubWebsiteChecker, urls)\n\t}\n}\n```\n\nThe benchmark tests `CheckWebsites` using a slice of one hundred urls and uses\na new fake implementation of `WebsiteChecker`. `slowStubWebsiteChecker` is\ndeliberately slow. It uses `time.Sleep` to wait exactly twenty milliseconds and\nthen it returns true.\n\n\nWhen we run the benchmark using `go test -bench=.` (or if you're in Windows Powershell `go test -bench=\".\"`):\n\n```sh\npkg: github.com/gypsydave5/learn-go-with-tests/concurrency/v0\nBenchmarkCheckWebsites-4               1        2249228637 ns/op\nPASS\nok      github.com/gypsydave5/learn-go-with-tests/concurrency/v0        2.268s\n```\n\n`CheckWebsites` has been benchmarked at 2249228637 nanoseconds - about two and\na quarter seconds.\n\nLet's try and make this faster.\n\n### Write enough code to make it pass\n\nNow we can finally talk about concurrency which, for the purposes of the\nfollowing, means \"having more than one thing in progress.\" This is something\nthat we do naturally everyday.\n\nFor instance, this morning I made a cup of tea. I put the kettle on and then,\nwhile I was waiting for it to boil, I got the milk out of the fridge, got the\ntea out of the cupboard, found my favourite mug, put the teabag into the cup and\nthen, when the kettle had boiled, I put the water in the cup.\n\nWhat I _didn't_ do was put the kettle on and then stand there blankly staring at\nthe kettle until it boiled, then do everything else once the kettle had boiled.\n\nIf you can understand why it's faster to make tea the first way, then you can\nunderstand how we will make `CheckWebsites` faster. Instead of waiting for\na website to respond before sending a request to the next website, we will tell\nour computer to make the next request while it is waiting.\n\nNormally in Go when we call a function `doSomething()` we wait for it to return\n(even if it has no value to return, we still wait for it to finish). We say that\nthis operation is *blocking* - it makes us wait for it to finish. An operation\nthat does not block in Go will run in a separate *process* called a *goroutine*.\nThink of a process as reading down the page of Go code from top to bottom, going\n'inside' each function when it gets called to read what it does. When a separate\nprocess starts, it's like another reader begins reading inside the function,\nleaving the original reader to carry on going down the page.\n\nTo tell Go to start a new goroutine we turn a function call into a `go`\nstatement by putting the keyword `go` in front of it: `go doSomething()`.\n\n```go\npackage concurrency\n\ntype WebsiteChecker func(string) bool\n\nfunc CheckWebsites(wc WebsiteChecker, urls []string) map[string]bool {\n\tresults := make(map[string]bool)\n\n\tfor _, url := range urls {\n\t\tgo func() {\n\t\t\tresults[url] = wc(url)\n\t\t}()\n\t}\n\n\treturn results\n}\n```\n\nBecause the only way to start a goroutine is to put `go` in front of a function\ncall, we often use *anonymous functions* when we want to start a goroutine. An\nanonymous function literal looks just the same as a normal function declaration,\nbut without a name (unsurprisingly). You can see one above in the body of the\n`for` loop.\n\nAnonymous functions have a number of features which make them useful, two of\nwhich we're using above. Firstly, they can be executed at the same time that\nthey're declared - this is what the `()` at the end of the anonymous function is\ndoing. Secondly they maintain access to the lexical scope in which they are\ndefined - all the variables that are available at the point when you declare the\nanonymous function are also available in the body of the function.\n\nThe body of the anonymous function above is just the same as the loop body was\nbefore. The only difference is that each iteration of the loop will start a new\ngoroutine, concurrent with the current process (the `WebsiteChecker` function).\nEach goroutine will add its result to the results map.\n\nBut when we run `go test`:\n\n```sh\n--- FAIL: TestCheckWebsites (0.00s)\n        CheckWebsites_test.go:31: Wanted map[http://google.com:true http://blog.gypsydave5.com:true waat://furhurterwe.geds:false], got map[]\nFAIL\nexit status 1\nFAIL    github.com/gypsydave5/learn-go-with-tests/concurrency/v1        0.010s\n\n```\n\n### A quick aside into the concurrency universe...\n\nYou might not get this result. You might get a panic message that\nwe're going to talk about in a bit. Don't worry if you got that, just keep\nrunning the test until you _do_ get the result above. Or pretend that you did.\nUp to you. Welcome to concurrency: when it's not handled correctly it's hard to\npredict what's going to happen. Don't worry - that's why we're writing tests, to\nhelp us know when we're handling concurrency predictably.\n\n### ... and we're back.\n\nWe are caught by the original test `CheckWebsites`, it's now returning an\nempty map. What went wrong?\n\nNone of the goroutines that our `for` loop started had enough time to add\ntheir result to the `results` map; the `CheckWebsites` function is too fast for\nthem, and it returns the still empty map.\n\nTo fix this we can just wait while all the goroutines do their work, and then\nreturn. Two seconds ought to do it, right?\n\n```go\npackage concurrency\n\nimport \"time\"\n\ntype WebsiteChecker func(string) bool\n\nfunc CheckWebsites(wc WebsiteChecker, urls []string) map[string]bool {\n\tresults := make(map[string]bool)\n\n\tfor _, url := range urls {\n\t\tgo func() {\n\t\t\tresults[url] = wc(url)\n\t\t}()\n\t}\n\n\ttime.Sleep(2 * time.Second)\n\n\treturn results\n}\n```\n\nNow if you're lucky you'll get:\n\n```sh\nPASS\nok      github.com/gypsydave5/learn-go-with-tests/concurrency/v1        2.012s\n```\n\nBut if you're unlucky (this is more likely if you run them with the benchmark as you'll get more tries)\n\n```sh\nfatal error: concurrent map writes\n\ngoroutine 8 [running]:\nruntime.throw(0x12c5895, 0x15)\n        /usr/local/Cellar/go/1.9.3/libexec/src/runtime/panic.go:605 +0x95 fp=0xc420037700 sp=0xc4200376e0 pc=0x102d395\nruntime.mapassign_faststr(0x1271d80, 0xc42007acf0, 0x12c6634, 0x17, 0x0)\n        /usr/local/Cellar/go/1.9.3/libexec/src/runtime/hashmap_fast.go:783 +0x4f5 fp=0xc420037780 sp=0xc420037700 pc=0x100eb65\ngithub.com/gypsydave5/learn-go-with-tests/concurrency/v3.WebsiteChecker.func1(0xc42007acf0, 0x12d3938, 0x12c6634, 0x17)\n        /Users/gypsydave5/go/src/github.com/gypsydave5/learn-go-with-tests/concurrency/v3/websiteChecker.go:12 +0x71 fp=0xc4200377c0 sp=0xc420037780 pc=0x12308f1\nruntime.goexit()\n        /usr/local/Cellar/go/1.9.3/libexec/src/runtime/asm_amd64.s:2337 +0x1 fp=0xc4200377c8 sp=0xc4200377c0 pc=0x105cf01\ncreated by github.com/gypsydave5/learn-go-with-tests/concurrency/v3.WebsiteChecker\n        /Users/gypsydave5/go/src/github.com/gypsydave5/learn-go-with-tests/concurrency/v3/websiteChecker.go:11 +0xa1\n\n        ... many more scary lines of text ...\n```\n\nThis is long and scary, but all we need to do is take a breath and read the\nstacktrace: `fatal error: concurrent map writes`. Sometimes, when we run our\ntests, two of the goroutines write to the results map at exactly the same time.\nMaps in Go don't like it when more than one thing tries to write to them at\nonce, and so `fatal error`.\n\nThis is a _race condition_, a bug that occurs when the output of our software is\ndependent on the timing and sequence of events that we have no control over.\nBecause we cannot control exactly when each goroutine writes to the results map,\nwe are vulnerable to two goroutines writing to it at the same time.\n\nGo can help us to spot race conditions with its built in [_race detector_][godoc_race_detector].\nTo enable this feature, run the tests with the `race` flag: `go test -race`.\n\nYou should get some output that looks like this:\n\n```sh\n==================\nWARNING: DATA RACE\nWrite at 0x00c420084d20 by goroutine 8:\n  runtime.mapassign_faststr()\n      /usr/local/Cellar/go/1.9.3/libexec/src/runtime/hashmap_fast.go:774 +0x0\n  github.com/gypsydave5/learn-go-with-tests/concurrency/v3.WebsiteChecker.func1()\n      /Users/gypsydave5/go/src/github.com/gypsydave5/learn-go-with-tests/concurrency/v3/websiteChecker.go:12 +0x82\n\nPrevious write at 0x00c420084d20 by goroutine 7:\n  runtime.mapassign_faststr()\n      /usr/local/Cellar/go/1.9.3/libexec/src/runtime/hashmap_fast.go:774 +0x0\n  github.com/gypsydave5/learn-go-with-tests/concurrency/v3.WebsiteChecker.func1()\n      /Users/gypsydave5/go/src/github.com/gypsydave5/learn-go-with-tests/concurrency/v3/websiteChecker.go:12 +0x82\n\nGoroutine 8 (running) created at:\n  github.com/gypsydave5/learn-go-with-tests/concurrency/v3.WebsiteChecker()\n      /Users/gypsydave5/go/src/github.com/gypsydave5/learn-go-with-tests/concurrency/v3/websiteChecker.go:11 +0xc4\n  github.com/gypsydave5/learn-go-with-tests/concurrency/v3.TestWebsiteChecker()\n      /Users/gypsydave5/go/src/github.com/gypsydave5/learn-go-with-tests/concurrency/v3/websiteChecker_test.go:27 +0xad\n  testing.tRunner()\n      /usr/local/Cellar/go/1.9.3/libexec/src/testing/testing.go:746 +0x16c\n\nGoroutine 7 (finished) created at:\n  github.com/gypsydave5/learn-go-with-tests/concurrency/v3.WebsiteChecker()\n      /Users/gypsydave5/go/src/github.com/gypsydave5/learn-go-with-tests/concurrency/v3/websiteChecker.go:11 +0xc4\n  github.com/gypsydave5/learn-go-with-tests/concurrency/v3.TestWebsiteChecker()\n      /Users/gypsydave5/go/src/github.com/gypsydave5/learn-go-with-tests/concurrency/v3/websiteChecker_test.go:27 +0xad\n  testing.tRunner()\n      /usr/local/Cellar/go/1.9.3/libexec/src/testing/testing.go:746 +0x16c\n==================\n```\n\nThe details are, again, hard to read - but `WARNING: DATA RACE` is pretty\nunambiguous. Reading into the body of the error we can see two different\ngoroutines performing writes on a map:\n\n`Write at 0x00c420084d20 by goroutine 8:`\n\nis writing to the same block of memory as\n\n`Previous write at 0x00c420084d20 by goroutine 7:`\n\nOn top of that, we can see the line of code where the write is happening:\n\n`/Users/gypsydave5/go/src/github.com/gypsydave5/learn-go-with-tests/concurrency/v3/websiteChecker.go:12`\n\nand the line of code where goroutines 7 and 8 are started:\n\n`/Users/gypsydave5/go/src/github.com/gypsydave5/learn-go-with-tests/concurrency/v3/websiteChecker.go:11`\n\nEverything you need to know is printed to your terminal - all you have to do is\nbe patient enough to read it.\n\n### Channels\n\nWe can solve this data race by coordinating our goroutines using _channels_.\nChannels are a Go data structure that can both receive and send values. These\noperations, along with their details, allow communication between different\nprocesses.\n\nIn this case we want to think about the communication between the parent process\nand each of the goroutines that it makes to do the work of running the\n`WebsiteChecker` function with the url.\n\n```go\npackage concurrency\n\ntype WebsiteChecker func(string) bool\ntype result struct {\n\tstring\n\tbool\n}\n\nfunc CheckWebsites(wc WebsiteChecker, urls []string) map[string]bool {\n\tresults := make(map[string]bool)\n\tresultChannel := make(chan result)\n\n\tfor _, url := range urls {\n\t\tgo func() {\n\t\t\tresultChannel <- result{url, wc(url)}\n\t\t}()\n\t}\n\n\tfor i := 0; i < len(urls); i++ {\n\t\tr := <-resultChannel\n\t\tresults[r.string] = r.bool\n\t}\n\n\treturn results\n}\n```\n\nAlongside the `results` map we now have a `resultChannel`, which we `make` in\nthe same way. `chan result` is the type of the channel - a channel of `result`.\nThe new type, `result` has been made to associate the return value of the\n`WebsiteChecker` with the url being checked - it's a struct of `string` and\n`bool`. As we don't need either value to be named, each of them is anonymous\nwithin the struct; this can be useful when it's hard to know what to name\na value.\n\nNow when we iterate over the urls, instead of writing to the `map` directly\nwe're sending a `result` struct for each call to `wc` to the `resultChannel`\nwith a _send statement_. This uses the `<-` operator, taking a channel on the\nleft and a value on the right:\n\n```go\n// Send statement\nresultChannel <- result{url, wc(url)}\n```\n\nThe next `for` loop iterates once for each of the urls. Inside we're using\na _receive expression_, which assigns a value received from a channel to\na variable. This also uses the `<-` operator, but with the two operands now\nreversed: the channel is now on the right and the variable that\nwe're assigning to is on the left:\n\n```go\n// Receive expression\nr := <-resultChannel\n```\n\nWe then use the `result` received to update the map.\n\nBy sending the results into a channel, we can control the timing of each write\ninto the results map, ensuring that it happens one at a time. Although each of\nthe calls of `wc`, and each send to the result channel, is happening concurrently\ninside its own process, each of the results is being dealt with one at a time as\nwe take values out of the result channel with the receive expression.\n\nWe have used concurrency for the part of the code that we wanted to make faster, while\nmaking sure that the part that cannot happen simultaneously still happens linearly.\nAnd we have communicated across the multiple processes involved by using\nchannels.\n\nWhen we run the benchmark:\n\n```sh\npkg: github.com/gypsydave5/learn-go-with-tests/concurrency/v2\nBenchmarkCheckWebsites-8             100          23406615 ns/op\nPASS\nok      github.com/gypsydave5/learn-go-with-tests/concurrency/v2        2.377s\n```\n23406615 nanoseconds - 0.023 seconds, about one hundred times as fast as\noriginal function. A great success.\n\n## Wrapping up\n\nThis exercise has been a little lighter on the TDD than usual. In a way we've\nbeen taking part in one long refactoring of the `CheckWebsites` function; the\ninputs and outputs never changed, it just got faster. But the tests we had in\nplace, as well as the benchmark we wrote, allowed us to refactor `CheckWebsites`\nin a way that maintained confidence that the software was still working, while\ndemonstrating that it had actually become faster.\n\nIn making it faster we learned about\n\n- *goroutines*, the basic unit of concurrency in Go, which let us manage more\n  than one website check request.\n- *anonymous functions*, which we used to start each of the concurrent processes\n  that check websites.\n- *channels*, to help organize and control the communication between the\n  different processes, allowing us to avoid a *race condition* bug.\n- *the race detector* which helped us debug problems with concurrent code\n\n### Make it fast\n\nOne formulation of an agile way of building software, often misattributed to Kent\nBeck, is:\n\n> [Make it work, make it right, make it fast][wrf]\n\nWhere 'work' is making the tests pass, 'right' is refactoring the code, and\n'fast' is optimizing the code to make it, for example, run quickly. We can only\n'make it fast' once we've made it work and made it right. We were lucky that the\ncode we were given was already demonstrated to be working, and didn't need to be\nrefactored. We should never try to 'make it fast' before the other two steps\nhave been performed because\n\n> [Premature optimization is the root of all evil][popt]\n> -- Donald Knuth\n\n[DI]: dependency-injection.md\n[wrf]: http://wiki.c2.com/?MakeItWorkMakeItRightMakeItFast\n[godoc_race_detector]: https://blog.golang.org/race-detector\n[popt]: http://wiki.c2.com/?PrematureOptimization\n"
  },
  {
    "path": "context/v1/context.go",
    "content": "package context1\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n)\n\n// Store fetches data.\ntype Store interface {\n\tFetch() string\n}\n\n// Server returns a handler for calling Store.\nfunc Server(store Store) http.HandlerFunc {\n\treturn func(w http.ResponseWriter, r *http.Request) {\n\t\tfmt.Fprint(w, store.Fetch())\n\t}\n}\n"
  },
  {
    "path": "context/v1/context_test.go",
    "content": "package context1\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\ntype StubStore struct {\n\tresponse string\n}\n\nfunc (s *StubStore) Fetch() string {\n\treturn s.response\n}\n\nfunc TestServer(t *testing.T) {\n\tdata := \"hello, world\"\n\tsvr := Server(&StubStore{data})\n\n\trequest := httptest.NewRequest(http.MethodGet, \"/\", nil)\n\tresponse := httptest.NewRecorder()\n\n\tsvr.ServeHTTP(response, request)\n\n\tif response.Body.String() != data {\n\t\tt.Errorf(`got \"%s\", want \"%s\"`, response.Body.String(), data)\n\t}\n}\n"
  },
  {
    "path": "context/v2/context.go",
    "content": "package context2\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n)\n\n// Store fetches data.\ntype Store interface {\n\tFetch() string\n\tCancel()\n}\n\n// Server returns a handler for calling Store.\nfunc Server(store Store) http.HandlerFunc {\n\treturn func(w http.ResponseWriter, r *http.Request) {\n\t\tctx := r.Context()\n\n\t\tdata := make(chan string, 1)\n\n\t\tgo func() {\n\t\t\tdata <- store.Fetch()\n\t\t}()\n\n\t\tselect {\n\t\tcase d := <-data:\n\t\t\tfmt.Fprint(w, d)\n\t\tcase <-ctx.Done():\n\t\t\tstore.Cancel()\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "context/v2/context_test.go",
    "content": "package context2\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestServer(t *testing.T) {\n\tdata := \"hello, world\"\n\n\tt.Run(\"returns data from store\", func(t *testing.T) {\n\t\tstore := &SpyStore{response: data, t: t}\n\t\tsvr := Server(store)\n\n\t\trequest := httptest.NewRequest(http.MethodGet, \"/\", nil)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tsvr.ServeHTTP(response, request)\n\n\t\tif response.Body.String() != data {\n\t\t\tt.Errorf(`got \"%s\", want \"%s\"`, response.Body.String(), data)\n\t\t}\n\n\t\tstore.assertWasNotCancelled()\n\t})\n\n\tt.Run(\"tells store to cancel work if request is cancelled\", func(t *testing.T) {\n\t\tstore := &SpyStore{response: data, t: t}\n\t\tsvr := Server(store)\n\n\t\trequest := httptest.NewRequest(http.MethodGet, \"/\", nil)\n\n\t\tcancellingCtx, cancel := context.WithCancel(request.Context())\n\t\ttime.AfterFunc(5*time.Millisecond, cancel)\n\t\trequest = request.WithContext(cancellingCtx)\n\n\t\tresponse := httptest.NewRecorder()\n\n\t\tsvr.ServeHTTP(response, request)\n\n\t\tstore.assertWasCancelled()\n\t})\n}\n"
  },
  {
    "path": "context/v2/testdoubles.go",
    "content": "package context2\n\nimport (\n\t\"testing\"\n\t\"time\"\n)\n\n// SpyStore allows you to simulate a store and see how its used.\ntype SpyStore struct {\n\tresponse  string\n\tcancelled bool\n\tt         *testing.T\n}\n\n// Fetch returns response after a short delay.\nfunc (s *SpyStore) Fetch() string {\n\ttime.Sleep(100 * time.Millisecond)\n\treturn s.response\n}\n\n// Cancel will record the call.\nfunc (s *SpyStore) Cancel() {\n\ts.cancelled = true\n}\n\nfunc (s *SpyStore) assertWasCancelled() {\n\ts.t.Helper()\n\tif !s.cancelled {\n\t\ts.t.Error(\"store was not told to cancel\")\n\t}\n}\n\nfunc (s *SpyStore) assertWasNotCancelled() {\n\ts.t.Helper()\n\tif s.cancelled {\n\t\ts.t.Error(\"store was told to cancel\")\n\t}\n}\n"
  },
  {
    "path": "context/v3/context.go",
    "content": "package context3\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n)\n\n// Store fetches data.\ntype Store interface {\n\tFetch(ctx context.Context) (string, error)\n}\n\n// Server returns a handler for calling Store.\nfunc Server(store Store) http.HandlerFunc {\n\treturn func(w http.ResponseWriter, r *http.Request) {\n\t\tdata, err := store.Fetch(r.Context())\n\n\t\tif err != nil {\n\t\t\treturn // todo: log error however you like\n\t\t}\n\n\t\tfmt.Fprint(w, data)\n\t}\n}\n"
  },
  {
    "path": "context/v3/context_test.go",
    "content": "package context3\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestServer(t *testing.T) {\n\tdata := \"hello, world\"\n\n\tt.Run(\"returns data from store\", func(t *testing.T) {\n\t\tstore := &SpyStore{response: data}\n\t\tsvr := Server(store)\n\n\t\trequest := httptest.NewRequest(http.MethodGet, \"/\", nil)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tsvr.ServeHTTP(response, request)\n\n\t\tif response.Body.String() != data {\n\t\t\tt.Errorf(`got \"%s\", want \"%s\"`, response.Body.String(), data)\n\t\t}\n\t})\n\n\tt.Run(\"tells store to cancel work if request is cancelled\", func(t *testing.T) {\n\t\tstore := &SpyStore{response: data}\n\t\tsvr := Server(store)\n\n\t\trequest := httptest.NewRequest(http.MethodGet, \"/\", nil)\n\n\t\tcancellingCtx, cancel := context.WithCancel(request.Context())\n\t\ttime.AfterFunc(5*time.Millisecond, cancel)\n\t\trequest = request.WithContext(cancellingCtx)\n\n\t\tresponse := &SpyResponseWriter{}\n\n\t\tsvr.ServeHTTP(response, request)\n\n\t\tif response.written {\n\t\t\tt.Error(\"a response should not have been written\")\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "context/v3/testdoubles.go",
    "content": "package context3\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"log\"\n\t\"net/http\"\n\t\"time\"\n)\n\n// SpyStore allows you to simulate a store and see how its used.\ntype SpyStore struct {\n\tresponse string\n}\n\n// Fetch returns response after a short delay.\nfunc (s *SpyStore) Fetch(ctx context.Context) (string, error) {\n\tdata := make(chan string, 1)\n\n\tgo func() {\n\t\tvar result string\n\t\tfor _, c := range s.response {\n\t\t\tselect {\n\t\t\tcase <-ctx.Done():\n\t\t\t\tlog.Println(\"spy store got cancelled\")\n\t\t\t\treturn\n\t\t\tdefault:\n\t\t\t\ttime.Sleep(10 * time.Millisecond)\n\t\t\t\tresult += string(c)\n\t\t\t}\n\t\t}\n\t\tdata <- result\n\t}()\n\n\tselect {\n\tcase <-ctx.Done():\n\t\treturn \"\", ctx.Err()\n\tcase res := <-data:\n\t\treturn res, nil\n\t}\n}\n\n// SpyResponseWriter checks whether a response has been written.\ntype SpyResponseWriter struct {\n\twritten bool\n}\n\n// Header will mark written to true.\nfunc (s *SpyResponseWriter) Header() http.Header {\n\ts.written = true\n\treturn nil\n}\n\n// Write will mark written to true.\nfunc (s *SpyResponseWriter) Write([]byte) (int, error) {\n\ts.written = true\n\treturn 0, errors.New(\"not implemented\")\n}\n\n// WriteHeader will mark written to true.\nfunc (s *SpyResponseWriter) WriteHeader(statusCode int) {\n\ts.written = true\n}\n"
  },
  {
    "path": "context-aware-reader.md",
    "content": "# Context-aware readers\n\n**[You can find all the code here](https://github.com/quii/learn-go-with-tests/tree/main/q-and-a/context-aware-reader)**\n\nThis chapter demonstrates how to test-drive a context aware `io.Reader` as written by Mat Ryer and David Hernandez in [The Pace Dev Blog](https://pace.dev/blog/2020/02/03/context-aware-ioreader-for-golang-by-mat-ryer).\n\n## Context aware reader?\n\nFirst of all, a quick primer on `io.Reader`.\n\nIf you've read other chapters in this book you will have ran into `io.Reader` when we've opened files, encoded JSON and various other common tasks. It's a simple abstraction over reading data from _something_\n\n```go\ntype Reader interface {\n\tRead(p []byte) (n int, err error)\n}\n```\n\nBy using `io.Reader` you can gain a lot of re-use from the standard library, it's a very commonly used abstraction (along with its counterpart `io.Writer`)\n\n### Context aware?\n\n[In a previous chapter](context.md) we discussed how we can use `context` to provide cancellation. This is especially useful if you're performing tasks which may be computationally expensive and you want to be able to stop them.\n\nWhen you're using an `io.Reader` you have no guarantees over speed, it could take 1 nanosecond or hundreds of hours. You might find it useful to be able to cancel these kind of tasks in your own application and that's what Mat and David wrote about.\n\nThey combined two simple abstractions (`context.Context` and `io.Reader`) to solve this problem.\n\nLet's try and TDD some functionality so that we can wrap an `io.Reader` so it can be cancelled.\n\nTesting this poses an interesting challenge. Normally when using an `io.Reader` you're usually supplying it to some other function and you don't really concern yourself with the details; such as `json.NewDecoder` or `io.ReadAll`.\n\nWhat we want to demonstrate is something like\n\n> Given an `io.Reader` with \"ABCDEF\", when I send a cancel signal half-way through I when I try to continue to read I get nothing else so all I get is \"ABC\"\n\nLet's look at the interface again.\n\n```go\ntype Reader interface {\n\tRead(p []byte) (n int, err error)\n}\n```\n\nThe `Reader`'s `Read` method will read the contents it has into a `[]byte` that we supply.\n\nSo rather than reading everything, we could:\n\n - Supply a fixed-size byte array that doesn't fit all the contents\n - Send a cancel signal\n - Try and read again and this should return an error with 0 bytes read\n\nFor now, let's just write a \"happy path\" test where there is no cancellation, just so we can get familiar with the problem without having to write any production code yet.\n\n```go\nfunc TestContextAwareReader(t *testing.T) {\n\tt.Run(\"lets just see how a normal reader works\", func(t *testing.T) {\n\t\trdr := strings.NewReader(\"123456\")\n\t\tgot := make([]byte, 3)\n\t\t_, err := rdr.Read(got)\n\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tassertBufferHas(t, got, \"123\")\n\n\t\t_, err = rdr.Read(got)\n\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tassertBufferHas(t, got, \"456\")\n\t})\n}\n\nfunc assertBufferHas(t testing.TB, buf []byte, want string) {\n\tt.Helper()\n\tgot := string(buf)\n\tif got != want {\n\t\tt.Errorf(\"got %q, want %q\", got, want)\n\t}\n}\n```\n\n- Make an `io.Reader` from a string with some data\n- A byte array to read into which is smaller than the contents of the reader\n- Call read, check the contents, repeat.\n\nFrom this we can imagine sending some kind of cancel signal before the second read to change behaviour.\n\nNow we've seen how it works we'll TDD the rest of the functionality.\n\n## Write the test first\n\nWe want to be able to compose an `io.Reader` with a `context.Context`.\n\nWith TDD it's best to start with imagining your desired API and write a test for it.\n\nFrom there let the compiler and failing test output can guide us to a solution\n\n```go\nt.Run(\"behaves like a normal reader\", func(t *testing.T) {\n\trdr := NewCancellableReader(strings.NewReader(\"123456\"))\n\tgot := make([]byte, 3)\n\t_, err := rdr.Read(got)\n\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tassertBufferHas(t, got, \"123\")\n\n\t_, err = rdr.Read(got)\n\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tassertBufferHas(t, got, \"456\")\n})\n```\n\n## Try to run the test\n\n```\n./cancel_readers_test.go:12:10: undefined: NewCancellableReader\n```\n## Write the minimal amount of code for the test to run and check the failing test output\n\nWe'll need to define this function and it should return an `io.Reader`\n\n```go\nfunc NewCancellableReader(rdr io.Reader) io.Reader {\n\treturn nil\n}\n```\n\nIf you try and run it\n\n```\n=== RUN   TestCancelReaders\n=== RUN   TestCancelReaders/behaves_like_a_normal_reader\npanic: runtime error: invalid memory address or nil pointer dereference [recovered]\n\tpanic: runtime error: invalid memory address or nil pointer dereference\n[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x10f8fb5]\n```\n\nAs expected\n\n## Write enough code to make it pass\n\nFor now, we'll just return the `io.Reader` we pass in\n\n```go\nfunc NewCancellableReader(rdr io.Reader) io.Reader {\n\treturn rdr\n}\n```\n\nThe test should now pass.\n\nI know, I know, this seems silly and pedantic but before charging in to the fancy work it is important that we have _some_ verification that we haven't broken the \"normal\" behaviour of an `io.Reader` and this test will give us confidence as we move forward.\n\n## Write the test first\n\nNext we need to try and cancel.\n\n```go\nt.Run(\"stops reading when cancelled\", func(t *testing.T) {\n\tctx, cancel := context.WithCancel(context.Background())\n\trdr := NewCancellableReader(ctx, strings.NewReader(\"123456\"))\n\tgot := make([]byte, 3)\n\t_, err := rdr.Read(got)\n\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tassertBufferHas(t, got, \"123\")\n\n\tcancel()\n\n\tn, err := rdr.Read(got)\n\n\tif err == nil {\n\t\tt.Error(\"expected an error after cancellation but didn't get one\")\n\t}\n\n\tif n > 0 {\n\t\tt.Errorf(\"expected 0 bytes to be read after cancellation but %d were read\", n)\n\t}\n})\n```\n\nWe can more or less copy the first test but now we're:\n- Creating a `context.Context` with cancellation so we can `cancel` after the first read\n- For our code to work we'll need to pass `ctx` to our function\n- We then assert that post-`cancel` nothing was read\n\n## Try to run the test\n\n```\n./cancel_readers_test.go:33:30: too many arguments in call to NewCancellableReader\n\thave (context.Context, *strings.Reader)\n\twant (io.Reader)\n```\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\nThe compiler is telling us what to do; update our signature to accept a context\n\n```go\nfunc NewCancellableReader(ctx context.Context, rdr io.Reader) io.Reader {\n\treturn rdr\n}\n```\n\n(You'll need to update the first test to pass in `context.Background` too)\n\nYou should now see a very clear failing test output\n\n```\n=== RUN   TestCancelReaders\n=== RUN   TestCancelReaders/stops_reading_when_cancelled\n--- FAIL: TestCancelReaders (0.00s)\n    --- FAIL: TestCancelReaders/stops_reading_when_cancelled (0.00s)\n        cancel_readers_test.go:48: expected an error but didn't get one\n        cancel_readers_test.go:52: expected 0 bytes to be read after cancellation but 3 were read\n```\n\n## Write enough code to make it pass\n\nAt this point, it's copy and paste from the original post by Mat and David but we'll still take it slowly and iteratively.\n\nWe know we need to have a type that encapsulates the `io.Reader` that we read from and the `context.Context` so let's create that and try and return it from our function instead of the original `io.Reader`\n\n```go\nfunc NewCancellableReader(ctx context.Context, rdr io.Reader) io.Reader {\n\treturn &readerCtx{\n\t\tctx:      ctx,\n\t\tdelegate: rdr,\n\t}\n}\n\ntype readerCtx struct {\n\tctx      context.Context\n\tdelegate io.Reader\n}\n```\n\nAs I have stressed many times in this book, go slowly and let the compiler help you\n\n```\n./cancel_readers_test.go:60:3: cannot use &readerCtx literal (type *readerCtx) as type io.Reader in return argument:\n\t*readerCtx does not implement io.Reader (missing Read method)\n```\n\nThe abstraction feels right, but it doesn't implement the interface we need (`io.Reader`) so let's add the method.\n\n```go\nfunc (r *readerCtx) Read(p []byte) (n int, err error) {\n\tpanic(\"implement me\")\n}\n```\n\nRun the tests and they should _compile_ but panic. This is still progress.\n\nLet's make the first test pass by just _delegating_ the call to our underlying `io.Reader`\n\n```go\nfunc (r readerCtx) Read(p []byte) (n int, err error) {\n\treturn r.delegate.Read(p)\n}\n```\n\nAt this point we have our happy path test passing again and it feels like we have our stuff abstracted nicely\n\nTo make our second test pass we need to check the `context.Context` to see if it has been cancelled.\n\n```go\nfunc (r readerCtx) Read(p []byte) (n int, err error) {\n\tif err := r.ctx.Err(); err != nil {\n\t\treturn 0, err\n\t}\n\treturn r.delegate.Read(p)\n}\n```\n\nAll tests should now pass. You'll notice how we return the error from the `context.Context`. This allows callers of the code to inspect the various reasons cancellation has occurred and this is covered more in the original post.\n\n## Wrapping up\n\n- Small interfaces are good and are easily composed\n- When you're trying to augment one thing (e.g `io.Reader`) with another you usually want to reach for the [delegation pattern](https://en.wikipedia.org/wiki/Delegation_pattern)\n\n> In software engineering, the delegation pattern is an object-oriented design pattern that allows object composition to achieve the same code reuse as inheritance.\n\n- An easy way to start this kind of work is to wrap your delegate and write a test that asserts it behaves how the delegate normally does before you start composing other parts to change behaviour. This will help you to keep things working correctly as you code toward your goal\n"
  },
  {
    "path": "context.md",
    "content": "# Context\n\n**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/main/context)**\n\nSoftware often kicks off long-running, resource-intensive processes (often in goroutines). If the action that caused this gets cancelled or fails for some reason you need to stop these processes in a consistent way through your application.\n\nIf you don't manage this your snappy Go application that you're so proud of could start having difficult to debug performance problems.\n\nIn this chapter we'll use the package `context` to help us manage long-running processes.\n\nWe're going to start with a classic example of a web server that when hit kicks off a potentially long-running process to fetch some data for it to return in the response.\n\nWe will exercise a scenario where a user cancels the request before the data can be retrieved and we'll make sure the process is told to give up.\n\nI've set up some code on the happy path to get us started. Here is our server code.\n\n```go\nfunc Server(store Store) http.HandlerFunc {\n\treturn func(w http.ResponseWriter, r *http.Request) {\n\t\tfmt.Fprint(w, store.Fetch())\n\t}\n}\n```\n\nThe function `Server` takes a `Store` and returns us a `http.HandlerFunc`. Store is defined as:\n\n```go\ntype Store interface {\n\tFetch() string\n}\n```\n\nThe returned function calls the `store`'s `Fetch` method to get the data and writes it to the response.\n\nWe have a corresponding spy for `Store` which we use in a test.\n\n```go\ntype SpyStore struct {\n\tresponse string\n}\n\nfunc (s *SpyStore) Fetch() string {\n\treturn s.response\n}\n\nfunc TestServer(t *testing.T) {\n\tdata := \"hello, world\"\n\tsvr := Server(&SpyStore{data})\n\n\trequest := httptest.NewRequest(http.MethodGet, \"/\", nil)\n\tresponse := httptest.NewRecorder()\n\n\tsvr.ServeHTTP(response, request)\n\n\tif response.Body.String() != data {\n\t\tt.Errorf(`got \"%s\", want \"%s\"`, response.Body.String(), data)\n\t}\n}\n```\n\nNow that we have a happy path, we want to make a more realistic scenario where the `Store` can't finish a`Fetch` before the user cancels the request.\n\n## Write the test first\n\nOur handler will need a way of telling the `Store` to cancel the work so update the interface.\n\n```go\ntype Store interface {\n\tFetch() string\n\tCancel()\n}\n```\n\nWe will need to adjust our spy so it takes some time to return `data` and a way of knowing it has been told to cancel. It'll have to add `Cancel` as a method to implement the `Store` interface.\n\n```go\ntype SpyStore struct {\n\tresponse  string\n\tcancelled bool\n}\n\nfunc (s *SpyStore) Fetch() string {\n\ttime.Sleep(100 * time.Millisecond)\n\treturn s.response\n}\n\nfunc (s *SpyStore) Cancel() {\n\ts.cancelled = true\n}\n```\n\nLet's add a new test where we cancel the request before 100 milliseconds and check the store to see if it gets cancelled.\n\n```go\nt.Run(\"tells store to cancel work if request is cancelled\", func(t *testing.T) {\n\tdata := \"hello, world\"\n\tstore := &SpyStore{response: data}\n\tsvr := Server(store)\n\n\trequest := httptest.NewRequest(http.MethodGet, \"/\", nil)\n\n\tcancellingCtx, cancel := context.WithCancel(request.Context())\n\ttime.AfterFunc(5*time.Millisecond, cancel)\n\trequest = request.WithContext(cancellingCtx)\n\n\tresponse := httptest.NewRecorder()\n\n\tsvr.ServeHTTP(response, request)\n\n\tif !store.cancelled {\n\t\tt.Error(\"store was not told to cancel\")\n\t}\n})\n```\n\nFrom the [Go Blog: Context](https://blog.golang.org/context)\n\n> The context package provides functions to derive new Context values from existing ones. These values form a tree: when a Context is canceled, all Contexts derived from it are also canceled.\n\nIt's important that you derive your contexts so that cancellations are propagated throughout the call stack for a given request.\n\nWhat we do is derive a new `cancellingCtx` from our `request` which returns us a `cancel` function. We then schedule that function to be called in 5 milliseconds by using `time.AfterFunc`. Finally we use this new context in our request by calling `request.WithContext`.\n\n## Try to run the test\n\nThe test fails as we'd expect.\n\n```\n--- FAIL: TestServer (0.00s)\n    --- FAIL: TestServer/tells_store_to_cancel_work_if_request_is_cancelled (0.00s)\n    \tcontext_test.go:62: store was not told to cancel\n```\n\n## Write enough code to make it pass\n\nRemember to be disciplined with TDD. Write the _minimal_ amount of code to make our test pass.\n\n```go\nfunc Server(store Store) http.HandlerFunc {\n\treturn func(w http.ResponseWriter, r *http.Request) {\n\t\tstore.Cancel()\n\t\tfmt.Fprint(w, store.Fetch())\n\t}\n}\n```\n\nThis makes this test pass but it doesn't feel good does it! We surely shouldn't be cancelling `Cancel()` before we fetch on _every request_.\n\nBy being disciplined it highlighted a flaw in our tests, this is a good thing!\n\nWe'll need to update our happy path test to assert that it does not get cancelled.\n\n```go\nt.Run(\"returns data from store\", func(t *testing.T) {\n\tdata := \"hello, world\"\n\tstore := &SpyStore{response: data}\n\tsvr := Server(store)\n\n\trequest := httptest.NewRequest(http.MethodGet, \"/\", nil)\n\tresponse := httptest.NewRecorder()\n\n\tsvr.ServeHTTP(response, request)\n\n\tif response.Body.String() != data {\n\t\tt.Errorf(`got \"%s\", want \"%s\"`, response.Body.String(), data)\n\t}\n\n\tif store.cancelled {\n\t\tt.Error(\"it should not have cancelled the store\")\n\t}\n})\n```\n\nRun both tests and the happy path test should now be failing and now we're forced to do a more sensible implementation.\n\n```go\nfunc Server(store Store) http.HandlerFunc {\n\treturn func(w http.ResponseWriter, r *http.Request) {\n\t\tctx := r.Context()\n\n\t\tdata := make(chan string, 1)\n\n\t\tgo func() {\n\t\t\tdata <- store.Fetch()\n\t\t}()\n\n\t\tselect {\n\t\tcase d := <-data:\n\t\t\tfmt.Fprint(w, d)\n\t\tcase <-ctx.Done():\n\t\t\tstore.Cancel()\n\t\t}\n\t}\n}\n```\n\nWhat have we done here?\n\n`context` has a method `Done()` which returns a channel which gets sent a signal when the context is \"done\" or \"cancelled\". We want to listen to that signal and call `store.Cancel` if we get it but we want to ignore it if our `Store` manages to `Fetch` before it.\n\nTo manage this we run `Fetch` in a goroutine and it will write the result into a new channel `data`. We then use `select` to effectively race to the two asynchronous processes and then we either write a response or `Cancel`.\n\n## Refactor\n\nWe can refactor our test code a bit by making assertion methods on our spy\n\n```go\ntype SpyStore struct {\n\tresponse  string\n\tcancelled bool\n\tt         *testing.T\n}\n\nfunc (s *SpyStore) assertWasCancelled() {\n\ts.t.Helper()\n\tif !s.cancelled {\n\t\ts.t.Error(\"store was not told to cancel\")\n\t}\n}\n\nfunc (s *SpyStore) assertWasNotCancelled() {\n\ts.t.Helper()\n\tif s.cancelled {\n\t\ts.t.Error(\"store was told to cancel\")\n\t}\n}\n```\n\nRemember to pass in the `*testing.T` when creating the spy.\n\n```go\nfunc TestServer(t *testing.T) {\n\tdata := \"hello, world\"\n\n\tt.Run(\"returns data from store\", func(t *testing.T) {\n\t\tstore := &SpyStore{response: data, t: t}\n\t\tsvr := Server(store)\n\n\t\trequest := httptest.NewRequest(http.MethodGet, \"/\", nil)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tsvr.ServeHTTP(response, request)\n\n\t\tif response.Body.String() != data {\n\t\t\tt.Errorf(`got \"%s\", want \"%s\"`, response.Body.String(), data)\n\t\t}\n\n\t\tstore.assertWasNotCancelled()\n\t})\n\n\tt.Run(\"tells store to cancel work if request is cancelled\", func(t *testing.T) {\n\t\tstore := &SpyStore{response: data, t: t}\n\t\tsvr := Server(store)\n\n\t\trequest := httptest.NewRequest(http.MethodGet, \"/\", nil)\n\n\t\tcancellingCtx, cancel := context.WithCancel(request.Context())\n\t\ttime.AfterFunc(5*time.Millisecond, cancel)\n\t\trequest = request.WithContext(cancellingCtx)\n\n\t\tresponse := httptest.NewRecorder()\n\n\t\tsvr.ServeHTTP(response, request)\n\n\t\tstore.assertWasCancelled()\n\t})\n}\n```\n\nThis approach is ok, but is it idiomatic?\n\nDoes it make sense for our web server to be concerned with manually cancelling `Store`? What if `Store` also happens to depend on other slow-running processes? We'll have to make sure that `Store.Cancel` correctly propagates the cancellation to all of its dependants.\n\nOne of the main points of `context` is that it is a consistent way of offering cancellation.\n\n[From the go doc](https://golang.org/pkg/context/)\n\n> Incoming requests to a server should create a Context, and outgoing calls to servers should accept a Context. The chain of function calls between them must propagate the Context, optionally replacing it with a derived Context created using WithCancel, WithDeadline, WithTimeout, or WithValue. When a Context is canceled, all Contexts derived from it are also canceled.\n\nFrom the [Go Blog: Context](https://blog.golang.org/context) again:\n\n> At Google, we require that Go programmers pass a Context parameter as the first argument to every function on the call path between incoming and outgoing requests. This allows Go code developed by many different teams to interoperate well. It provides simple control over timeouts and cancellation and ensures that critical values like security credentials transit Go programs properly.\n\n(Pause for a moment and think of the ramifications of every function having to send in a context, and the ergonomics of that.)\n\nFeeling a bit uneasy? Good. Let's try and follow that approach though and instead pass through the `context` to our `Store` and let it be responsible. That way it can also pass the `context` through to its dependants and they too can be responsible for stopping themselves.\n\n## Write the test first\n\nWe'll have to change our existing tests as their responsibilities are changing. The only thing our handler is responsible for now is making sure it sends a context through to the downstream `Store` and that it handles the error that will come from the `Store` when it is cancelled.\n\nLet's update our `Store` interface to show the new responsibilities.\n\n```go\ntype Store interface {\n\tFetch(ctx context.Context) (string, error)\n}\n```\n\nDelete the code inside our handler for now\n\n```go\nfunc Server(store Store) http.HandlerFunc {\n\treturn func(w http.ResponseWriter, r *http.Request) {\n\t}\n}\n```\n\nUpdate our `SpyStore`\n\n```go\ntype SpyStore struct {\n\tresponse string\n\tt        *testing.T\n}\n\nfunc (s *SpyStore) Fetch(ctx context.Context) (string, error) {\n\tdata := make(chan string, 1)\n\n\tgo func() {\n\t\tvar result string\n\t\tfor _, c := range s.response {\n\t\t\tselect {\n\t\t\tcase <-ctx.Done():\n\t\t\t\tlog.Println(\"spy store got cancelled\")\n\t\t\t\treturn\n\t\t\tdefault:\n\t\t\t\ttime.Sleep(10 * time.Millisecond)\n\t\t\t\tresult += string(c)\n\t\t\t}\n\t\t}\n\t\tdata <- result\n\t}()\n\n\tselect {\n\tcase <-ctx.Done():\n\t\treturn \"\", ctx.Err()\n\tcase res := <-data:\n\t\treturn res, nil\n\t}\n}\n```\n\nWe have to make our spy act like a real method that works with `context`.\n\nWe are simulating a slow process where we build the result slowly by appending the string, character by character in a goroutine. When the goroutine finishes its work it writes the string to the `data` channel. The goroutine listens for the `ctx.Done` and will stop the work if a signal is sent in that channel.\n\nFinally the code uses another `select` to wait for that goroutine to finish its work or for the cancellation to occur.\n\nIt's similar to our approach from before, we use Go's concurrency primitives to make two asynchronous processes race each other to determine what we return.\n\nYou'll take a similar approach when writing your own functions and methods that accept a `context` so make sure you understand what's going on.\n\nFinally we can update our tests. Comment out our cancellation test so we can fix the happy path test first.\n\n```go\nt.Run(\"returns data from store\", func(t *testing.T) {\n\tdata := \"hello, world\"\n\tstore := &SpyStore{response: data, t: t}\n\tsvr := Server(store)\n\n\trequest := httptest.NewRequest(http.MethodGet, \"/\", nil)\n\tresponse := httptest.NewRecorder()\n\n\tsvr.ServeHTTP(response, request)\n\n\tif response.Body.String() != data {\n\t\tt.Errorf(`got \"%s\", want \"%s\"`, response.Body.String(), data)\n\t}\n})\n```\n\n## Try to run the test\n\n```\n=== RUN   TestServer/returns_data_from_store\n--- FAIL: TestServer (0.00s)\n    --- FAIL: TestServer/returns_data_from_store (0.00s)\n    \tcontext_test.go:22: got \"\", want \"hello, world\"\n```\n\n## Write enough code to make it pass\n\n```go\nfunc Server(store Store) http.HandlerFunc {\n\treturn func(w http.ResponseWriter, r *http.Request) {\n\t\tdata, _ := store.Fetch(r.Context())\n\t\tfmt.Fprint(w, data)\n\t}\n}\n```\n\nOur happy path should be... happy. Now we can fix the other test.\n\n## Write the test first\n\nWe need to test that we do not write any kind of response on the error case. Sadly `httptest.ResponseRecorder` doesn't have a way of figuring this out so we'll have to roll our own spy to test for this.\n\n```go\ntype SpyResponseWriter struct {\n\twritten bool\n}\n\nfunc (s *SpyResponseWriter) Header() http.Header {\n\ts.written = true\n\treturn nil\n}\n\nfunc (s *SpyResponseWriter) Write([]byte) (int, error) {\n\ts.written = true\n\treturn 0, errors.New(\"not implemented\")\n}\n\nfunc (s *SpyResponseWriter) WriteHeader(statusCode int) {\n\ts.written = true\n}\n```\n\nOur `SpyResponseWriter` implements `http.ResponseWriter` so we can use it in the test.\n\n```go\nt.Run(\"tells store to cancel work if request is cancelled\", func(t *testing.T) {\n\tdata := \"hello, world\"\n\tstore := &SpyStore{response: data, t: t}\n\tsvr := Server(store)\n\n\trequest := httptest.NewRequest(http.MethodGet, \"/\", nil)\n\n\tcancellingCtx, cancel := context.WithCancel(request.Context())\n\ttime.AfterFunc(5*time.Millisecond, cancel)\n\trequest = request.WithContext(cancellingCtx)\n\n\tresponse := &SpyResponseWriter{}\n\n\tsvr.ServeHTTP(response, request)\n\n\tif response.written {\n\t\tt.Error(\"a response should not have been written\")\n\t}\n})\n```\n\n## Try to run the test\n\n```\n=== RUN   TestServer\n=== RUN   TestServer/tells_store_to_cancel_work_if_request_is_cancelled\n--- FAIL: TestServer (0.01s)\n    --- FAIL: TestServer/tells_store_to_cancel_work_if_request_is_cancelled (0.01s)\n    \tcontext_test.go:47: a response should not have been written\n```\n\n## Write enough code to make it pass\n\n```go\nfunc Server(store Store) http.HandlerFunc {\n\treturn func(w http.ResponseWriter, r *http.Request) {\n\t\tdata, err := store.Fetch(r.Context())\n\n\t\tif err != nil {\n\t\t\treturn // todo: log error however you like\n\t\t}\n\n\t\tfmt.Fprint(w, data)\n\t}\n}\n```\n\nWe can see after this that the server code has become simplified as it's no longer explicitly responsible for cancellation, it simply passes through `context` and relies on the downstream functions to respect any cancellations that may occur.\n\n## Wrapping up\n\n### What we've covered\n\n- How to test a HTTP handler that has had the request cancelled by the client.\n- How to use context to manage cancellation.\n- How to write a function that accepts `context` and uses it to cancel itself by using goroutines, `select` and channels.\n- Follow Google's guidelines as to how to manage cancellation by propagating request scoped context through your call-stack.\n- How to roll your own spy for `http.ResponseWriter` if you need it.\n\n### What about context.Value ?\n\n[Michal Štrba](https://faiface.github.io/post/context-should-go-away-go2/) and I have a similar opinion.\n\n> If you use ctx.Value in my (non-existent) company, you’re fired\n\nSome engineers have advocated passing values through `context` as it _feels convenient_.\n\nConvenience is often the cause of bad code.\n\nThe problem with `context.Values` is that it's just an untyped map so you have no type-safety and you have to handle it not actually containing your value. You have to create a coupling of map keys from one module to another and if someone changes something things start breaking.\n\nIn short, **if a function needs some values, put them as typed parameters rather than trying to fetch them from `context.Value`**. This makes it statically checked and documented for everyone to see.\n\n#### But...\n\nOn other hand, it can be helpful to include information that is orthogonal to a request in a context, such as a trace id. Potentially this information would not be needed by every function in your call-stack and would make your functional signatures very messy.\n\n[Jack Lindamood says **Context.Value should inform, not control**](https://medium.com/@cep21/how-to-correctly-use-context-context-in-go-1-7-8f2c0fafdf39)\n\n> The content of context.Value is for maintainers not users. It should never be required input for documented or expected results.\n\n### Additional material\n\n- I really enjoyed reading [Context should go away for Go 2 by Michal Štrba](https://faiface.github.io/post/context-should-go-away-go2/). His argument is that having to pass `context` everywhere is a smell, that it's pointing to a deficiency in the language in respect to cancellation. He says it would better if this was somehow solved at the language level, rather than at a library level. Until that happens, you will need `context` if you want to manage long running processes.\n- The [Go blog further describes the motivation for working with `context` and has some examples](https://blog.golang.org/context)\n"
  },
  {
    "path": "contributing.md",
    "content": "# Contributing\n\nContributions are very welcome. I hope for this to become a great home for guides of how to learn Go by writing tests. Consider submitting a PR or creating an issue which you can do [here](https://github.com/quii/learn-go-with-tests/issues).\n\n## What we're looking for\n\n* Teaching Go features \\(e.g things like `if`, `select`, structs, methods, etc\\).\n* Showcase interesting functionality within the standard library. Show off how easy it is to TDD a HTTP server for instance.\n* Show how Go's tooling, like benchmarking, race detectors, etc can help you arrive at great software.\n\nIf you don't feel confident to submit your own guide, submitting an issue for something you want to learn is still a valuable contribution.\n\n### ⚠️ Get feedback quickly for new content ⚠️\n\n- TDD teaches us to work iteratively and get feedback and I strongly suggest you do the same if you wish to contribute\n    - Open a PR with your first test and implementation, discuss your approach so I can offer feedback and course correct\n- This is of course open-source but I do have strong opinions on the content. The sooner you talk to me the better.\n\n## Style guide\n\n* Always be reinforcing the TDD cycle. Take a look at the [Chapter Template](template.md).\n* Emphasis on iterating over functionality driven by tests. The Hello, world example works well because we gradually make it more sophisticated and learning new techniques _driven_ by the tests. For example:\n  * `Hello()` &lt;- how to write functions, return types.\n  * `Hello(name string)` &lt;- arguments, constants.\n  * `Hello(name string)` &lt;- default to \"world\" using `if`.\n  * `Hello(name, language string)` &lt;- `switch`.\n* Try and minimise the surface area of required knowledge.\n  * Thinking of examples that showcase what you're trying to teach without confusing the reader with other features is important.\n  * For example you can learn about `struct`s without understanding pointers.\n  * Brevity is king.\n* Follow the [Code Review Comments style guide](https://go.dev/wiki/CodeReviewComments). It's important for a consistent style across all the sections.\n* Your section should have a runnable application at the end \\(e.g `package main` with a `main` func\\) so users can see it in action and play with it.\n* All tests should pass.\n* Run `./build.sh` before raising PR.\n"
  },
  {
    "path": "dependency-injection.md",
    "content": "# Dependency Injection\n\n**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/main/di)**\n\nIt is assumed that you have read the [structs section](./structs-methods-and-interfaces.md) before as some understanding of interfaces will be needed for this.\n\nThere are _a lot_ of misunderstandings around dependency injection around the programming community. Hopefully, this guide will show you how\n\n* You don't need a framework\n* It does not overcomplicate your design\n* It facilitates testing\n* It allows you to write great, general-purpose functions.\n\nWe want to write a function that greets someone, just like we did in the hello-world chapter but this time we are going to be testing the _actual printing_.\n\nJust to recap, here is what that function could look like\n\n```go\nfunc Greet(name string) {\n\tfmt.Printf(\"Hello, %s\", name)\n}\n```\n\nBut how can we test this? Calling `fmt.Printf` prints to stdout, which is pretty hard for us to capture using the testing framework.\n\nWhat we need to do is to be able to **inject** \\(which is just a fancy word for pass in\\) the dependency of printing.\n\n**Our function doesn't need to care _where_ or _how_ the printing happens, so we should accept an _interface_ rather than a concrete type.**\n\nIf we do that, we can then change the implementation to print to something we control so that we can test it. In \"real life\" you would inject in something that writes to stdout.\n\nIf you look at the source code of [`fmt.Printf`](https://pkg.go.dev/fmt#Printf) you can see a way for us to hook in\n\n```go\n// It returns the number of bytes written and any write error encountered.\nfunc Printf(format string, a ...interface{}) (n int, err error) {\n\treturn Fprintf(os.Stdout, format, a...)\n}\n```\n\nInteresting! Under the hood `Printf` just calls `Fprintf` passing in `os.Stdout`.\n\nWhat exactly _is_ an `os.Stdout`? What does `Fprintf` expect to get passed to it for the 1st argument?\n\n```go\nfunc Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {\n\tp := newPrinter()\n\tp.doPrintf(format, a)\n\tn, err = w.Write(p.buf)\n\tp.free()\n\treturn\n}\n```\n\nAn `io.Writer`\n\n```go\ntype Writer interface {\n\tWrite(p []byte) (n int, err error)\n}\n```\n\nFrom this we can infer that `os.Stdout` implements `io.Writer`; `Printf` passes `os.Stdout` to `Fprintf` which expects an `io.Writer`.\n\nAs you write more Go code you will find this interface popping up a lot because it's a great general purpose interface for \"put this data somewhere\".\n\nSo we know under the covers we're ultimately using `Writer` to send our greeting somewhere. Let's use this existing abstraction to make our code testable and more reusable.\n\n## Write the test first\n\n```go\nfunc TestGreet(t *testing.T) {\n\tbuffer := bytes.Buffer{}\n\tGreet(&buffer, \"Chris\")\n\n\tgot := buffer.String()\n\twant := \"Hello, Chris\"\n\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n```\n\nThe `Buffer` type from the `bytes` package implements the `Writer` interface, because it has the method `Write(p []byte) (n int, err error)`.\n\nSo we'll use it in our test to send in as our `Writer` and then we can check what was written to it after we invoke `Greet`\n\n## Try and run the test\n\nThe test will not compile\n\n```text\n./di_test.go:10:2: undefined: Greet\n```\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\n_Listen to the compiler_ and fix the problem.\n\n```go\nfunc Greet(writer *bytes.Buffer, name string) {\n\tfmt.Printf(\"Hello, %s\", name)\n}\n```\n\n`Hello, Chris di_test.go:16: got '' want 'Hello, Chris'`\n\nThe test fails. Notice that the name is getting printed out, but it's going to stdout.\n\n## Write enough code to make it pass\n\nUse the writer to send the greeting to the buffer in our test. Remember `fmt.Fprintf` is like `fmt.Printf` but instead takes a `Writer` to send the string to, whereas `fmt.Printf` defaults to stdout.\n\n```go\nfunc Greet(writer *bytes.Buffer, name string) {\n\tfmt.Fprintf(writer, \"Hello, %s\", name)\n}\n```\n\nThe test now passes.\n\n## Refactor\n\nEarlier the compiler told us to pass in a pointer to a `bytes.Buffer`. This is technically correct but not very useful.\n\nTo demonstrate this, try wiring up the `Greet` function into a Go application where we want it to print to stdout.\n\n```go\nfunc main() {\n\tGreet(os.Stdout, \"Elodie\")\n}\n```\n\n`./di.go:14:7: cannot use os.Stdout (type *os.File) as type *bytes.Buffer in argument to Greet`\n\nAs discussed earlier `fmt.Fprintf` allows you to pass in an `io.Writer` which we know both `os.Stdout` and `bytes.Buffer` implement.\n\nIf we change our code to use the more general purpose interface we can now use it in both tests and in our application.\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n)\n\nfunc Greet(writer io.Writer, name string) {\n\tfmt.Fprintf(writer, \"Hello, %s\", name)\n}\n\nfunc main() {\n\tGreet(os.Stdout, \"Elodie\")\n}\n```\n\n## More on io.Writer\n\nWhat other places can we write data to using `io.Writer`? Just how general purpose is our `Greet` function?\n\n### The Internet\n\nRun the following\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc Greet(writer io.Writer, name string) {\n\tfmt.Fprintf(writer, \"Hello, %s\", name)\n}\n\nfunc MyGreeterHandler(w http.ResponseWriter, r *http.Request) {\n\tGreet(w, \"world\")\n}\n\nfunc main() {\n\tlog.Fatal(http.ListenAndServe(\":5001\", http.HandlerFunc(MyGreeterHandler)))\n}\n```\n\nRun the program and go to [http://localhost:5001](http://localhost:5001). You'll see your greeting function being used.\n\nHTTP servers will be covered in a later chapter so don't worry too much about the details.\n\nWhen you write an HTTP handler, you are given an `http.ResponseWriter` and the `http.Request` that was used to make the request. When you implement your server you _write_ your response using the writer.\n\nYou can probably guess that `http.ResponseWriter` also implements `io.Writer` so this is why we could re-use our `Greet` function inside our handler.\n\n## Wrapping up\n\nOur first round of code was not easy to test because it wrote data to somewhere we couldn't control.\n\n_Motivated by our tests_ we refactored the code so we could control _where_ the data was written by **injecting a dependency** which allowed us to:\n\n* **Test our code** If you can't test a function _easily_, it's usually because of dependencies hard-wired into a function _or_ global state. If you have a global database connection pool for instance that is used by some kind of service layer, it is likely going to be difficult to test and they will be slow to run. DI will motivate you to inject in a database dependency \\(via an interface\\) which you can then mock out with something you can control in your tests.\n* **Separate our concerns**, decoupling _where the data goes_ from _how to generate it_. If you ever feel like a method/function has too many responsibilities \\(generating data _and_ writing to a db? handling HTTP requests _and_ doing domain level logic?\\) DI is probably going to be the tool you need.\n* **Allow our code to be re-used in different contexts** The first \"new\" context our code can be used in is inside tests. But further on if someone wants to try something new with your function they can inject their own dependencies.\n\n### What about mocking? I hear you need that for DI and also it's evil\n\nMocking will be covered in detail later \\(and it's not evil\\). You use mocking to replace real things you inject with a pretend version that you can control and inspect in your tests. In our case though, the standard library had something ready for us to use.\n\n### The Go standard library is really good, take time to study it\n\nBy having some familiarity with the `io.Writer` interface we are able to use `bytes.Buffer` in our test as our `Writer` and then we can use other `Writer`s from the standard library to use our function in a command line app or in web server.\n\nThe more familiar you are with the standard library the more you'll see these general purpose interfaces which you can then re-use in your own code to make your software reusable in a number of contexts.\n\nThis example is heavily influenced by a chapter in [The Go Programming language](https://www.amazon.co.uk/Programming-Language-Addison-Wesley-Professional-Computing/dp/0134190440), so if you enjoyed this, go buy it!\n"
  },
  {
    "path": "di/v1/di.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n)\n\n// Greet sends a personalised greeting to writer.\nfunc Greet(writer io.Writer, name string) {\n\tfmt.Fprintf(writer, \"Hello, %s\", name)\n}\n\nfunc main() {\n\tGreet(os.Stdout, \"Elodie\")\n}\n"
  },
  {
    "path": "di/v1/di_test.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n)\n\nfunc TestGreet(t *testing.T) {\n\tbuffer := bytes.Buffer{}\n\tGreet(&buffer, \"Chris\")\n\n\tgot := buffer.String()\n\twant := \"Hello, Chris\"\n\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "di/v2/di.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"net/http\"\n)\n\n// Greet sends a personalised greeting to writer.\nfunc Greet(writer io.Writer, name string) {\n\tfmt.Fprintf(writer, \"Hello, %s\", name)\n}\n\n// MyGreeterHandler says Hello, world over HTTP.\nfunc MyGreeterHandler(w http.ResponseWriter, r *http.Request) {\n\tGreet(w, \"world\")\n}\n\nfunc main() {\n\tlog.Fatal(http.ListenAndServe(\":5000\", http.HandlerFunc(MyGreeterHandler)))\n}\n"
  },
  {
    "path": "di/v2/di_test.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n)\n\nfunc TestGreet(t *testing.T) {\n\tbuffer := bytes.Buffer{}\n\tGreet(&buffer, \"Chris\")\n\n\tgot := buffer.String()\n\twant := \"Hello, Chris\"\n\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "error-types.md",
    "content": "# Error types\n\n**[You can find all the code here](https://github.com/quii/learn-go-with-tests/tree/main/q-and-a/error-types)**\n\n**Creating your own types for errors can be an elegant way of tidying up your code, making your code easier to use and test.**\n\nPedro on the Gopher Slack asks\n\n> If I’m creating an error like `fmt.Errorf(\"%s must be foo, got %s\", bar, baz)`, is there a way to test equality without comparing the string value?\n\nLet's make up a function to help explore this idea.\n\n```go\n// DumbGetter will get the string body of url if it gets a 200\nfunc DumbGetter(url string) (string, error) {\n\tres, err := http.Get(url)\n\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"problem fetching from %s, %v\", url, err)\n\t}\n\n\tif res.StatusCode != http.StatusOK {\n\t\treturn \"\", fmt.Errorf(\"did not get 200 from %s, got %d\", url, res.StatusCode)\n\t}\n\n\tdefer res.Body.Close()\n\tbody, _ := io.ReadAll(res.Body) // ignoring err for brevity\n\n\treturn string(body), nil\n}\n```\n\nIt's not uncommon to write a function that might fail for different reasons and we want to make sure we handle each scenario correctly.\n\nAs Pedro says, we _could_ write a test for the status error like so.\n\n```go\nt.Run(\"when you don't get a 200 you get a status error\", func(t *testing.T) {\n\n\tsvr := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {\n\t\tres.WriteHeader(http.StatusTeapot)\n\t}))\n\tdefer svr.Close()\n\n\t_, err := DumbGetter(svr.URL)\n\n\tif err == nil {\n\t\tt.Fatal(\"expected an error\")\n\t}\n\n\twant := fmt.Sprintf(\"did not get 200 from %s, got %d\", svr.URL, http.StatusTeapot)\n\tgot := err.Error()\n\n\tif got != want {\n\t\tt.Errorf(`got \"%v\", want \"%v\"`, got, want)\n\t}\n})\n```\n\nThis test creates a server which always returns `StatusTeapot` and then we use its URL as the argument to `DumbGetter` so we can see it handles non `200` responses correctly.\n\n## Problems with this way of testing\n\nThis book tries to emphasise _listen to your tests_ and this test doesn't _feel_ good:\n\n- We're constructing the same string as production code does to test it\n- It's annoying to read and write\n- Is the exact error message string what we're _actually concerned with_ ?\n\nWhat does this tell us? The ergonomics of our test would be reflected on another bit of code trying to use our code.\n\nHow does a user of our code react to the specific kind of errors we return? The best they can do is look at the error string which is extremely error prone and horrible to write.\n\n## What we should do\n\nWith TDD we have the benefit of getting into the mindset of:\n\n> How would _I_ want to use this code?\n\nWhat we could do for `DumbGetter` is provide a way for users to use the type system to understand what kind of error has happened.\n\nWhat if `DumbGetter` could return us something like\n\n```go\ntype BadStatusError struct {\n\tURL    string\n\tStatus int\n}\n```\n\nRather than a magical string, we have actual _data_ to work with.\n\nLet's change our existing test to reflect this need\n\n```go\nt.Run(\"when you don't get a 200 you get a status error\", func(t *testing.T) {\n\n\tsvr := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {\n\t\tres.WriteHeader(http.StatusTeapot)\n\t}))\n\tdefer svr.Close()\n\n\t_, err := DumbGetter(svr.URL)\n\n\tif err == nil {\n\t\tt.Fatal(\"expected an error\")\n\t}\n\n\tgot, isStatusErr := err.(BadStatusError)\n\n\tif !isStatusErr {\n\t\tt.Fatalf(\"was not a BadStatusError, got %T\", err)\n\t}\n\n\twant := BadStatusError{URL: svr.URL, Status: http.StatusTeapot}\n\n\tif got != want {\n\t\tt.Errorf(\"got %v, want %v\", got, want)\n\t}\n})\n```\n\nWe'll have to make `BadStatusError` implement the error interface.\n\n```go\nfunc (b BadStatusError) Error() string {\n\treturn fmt.Sprintf(\"did not get 200 from %s, got %d\", b.URL, b.Status)\n}\n```\n\n### What does the test do?\n\nInstead of checking the exact string of the error, we are doing a [type assertion](https://tour.golang.org/methods/15) on the error to see if it is a `BadStatusError`. This reflects our desire for the _kind_ of error clearer. Assuming the assertion passes we can then check the properties of the error are correct.\n\nWhen we run the test, it tells us we didn't return the right kind of error\n\n```\n--- FAIL: TestDumbGetter (0.00s)\n    --- FAIL: TestDumbGetter/when_you_dont_get_a_200_you_get_a_status_error (0.00s)\n    \terror-types_test.go:56: was not a BadStatusError, got *errors.errorString\n```\n\nLet's fix `DumbGetter` by updating our error handling code to use our type\n\n```go\nif res.StatusCode != http.StatusOK {\n\treturn \"\", BadStatusError{URL: url, Status: res.StatusCode}\n}\n```\n\nThis change has had some _real positive effects_\n\n- Our `DumbGetter` function has become simpler, it's no longer concerned with the intricacies of an error string, it just creates a `BadStatusError`.\n- Our tests now reflect (and document) what a user of our code _could_ do if they decided they wanted to do some more sophisticated error handling than just logging. Just do a type assertion and then you get easy access to the properties of the error.\n- It is still \"just\" an `error`, so if they choose to they can pass it up the call stack or log it like any other `error`.\n\n## Wrapping up\n\nIf you find yourself testing for multiple error conditions don't fall in to the trap of comparing the error messages.\n\nThis leads to flaky and difficult to read/write tests and it reflects the difficulties the users of your code will have if they also need to start doing things differently depending on the kind of errors that have occurred.\n\nAlways make sure your tests reflect how _you'd_ like to use your code, so in this respect consider creating error types to encapsulate your kinds of errors. This makes handling different kinds of errors easier for users of your code and also makes writing your error handling code simpler and easier to read.\n\n## Addendum\n\nAs of Go 1.13 there are new ways to work with errors in the standard library which is covered in the [Go Blog](https://blog.golang.org/go1.13-errors)\n\n```go\nt.Run(\"when you don't get a 200 you get a status error\", func(t *testing.T) {\n\n\tsvr := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {\n\t\tres.WriteHeader(http.StatusTeapot)\n\t}))\n\tdefer svr.Close()\n\n\t_, err := DumbGetter(svr.URL)\n\n\tif err == nil {\n\t\tt.Fatal(\"expected an error\")\n\t}\n\n\tvar got BadStatusError\n\tisBadStatusError := errors.As(err, &got)\n\twant := BadStatusError{URL: svr.URL, Status: http.StatusTeapot}\n\n\tif !isBadStatusError {\n\t\tt.Fatalf(\"was not a BadStatusError, got %T\", err)\n\t}\n\n\tif got != want {\n\t\tt.Errorf(\"got %v, want %v\", got, want)\n\t}\n})\n```\n\nIn this case we are using [`errors.As`](https://pkg.go.dev/errors#example-As) to try and extract our error into our custom type. It returns a `bool` to denote success and extracts it into `got` for us.\n"
  },
  {
    "path": "for/v1/repeat.go",
    "content": "package iteration\n\n// Repeat returns character repeated 5 times.\nfunc Repeat(character string) string {\n\tvar repeated string\n\tfor i := 0; i < 5; i++ {\n\t\trepeated = repeated + character\n\t}\n\treturn repeated\n}\n"
  },
  {
    "path": "for/v1/repeat_test.go",
    "content": "package iteration\n\nimport \"testing\"\n\nfunc TestRepeat(t *testing.T) {\n\trepeated := Repeat(\"a\")\n\texpected := \"aaaaa\"\n\n\tif repeated != expected {\n\t\tt.Errorf(\"expected %q but got %q\", expected, repeated)\n\t}\n}\n"
  },
  {
    "path": "for/v2/repeat.go",
    "content": "package iteration\n\nconst repeatCount = 5\n\n// Repeat returns character repeated 5 times.\nfunc Repeat(character string) string {\n\tvar repeated string\n\tfor i := 0; i < repeatCount; i++ {\n\t\trepeated += character\n\t}\n\treturn repeated\n}\n"
  },
  {
    "path": "for/v2/repeat_test.go",
    "content": "package iteration\n\nimport \"testing\"\n\nfunc TestRepeat(t *testing.T) {\n\trepeated := Repeat(\"a\")\n\texpected := \"aaaaa\"\n\n\tif repeated != expected {\n\t\tt.Errorf(\"expected %q but got %q\", expected, repeated)\n\t}\n}\n"
  },
  {
    "path": "for/v3/repeat.go",
    "content": "package iteration\n\nimport \"strings\"\n\nconst repeatCount = 5\n\n// Repeat returns character repeated 5 times.\nfunc Repeat(character string) string {\n\tvar repeated strings.Builder\n\tfor i := 0; i < repeatCount; i++ {\n\t\trepeated.WriteString(character)\n\t}\n\treturn repeated.String()\n}\n"
  },
  {
    "path": "for/v3/repeat_test.go",
    "content": "package iteration\n\nimport \"testing\"\n\nfunc TestRepeat(t *testing.T) {\n\trepeated := Repeat(\"a\")\n\texpected := \"aaaaa\"\n\n\tif repeated != expected {\n\t\tt.Errorf(\"expected %q but got %q\", expected, repeated)\n\t}\n}\n"
  },
  {
    "path": "for/vx/repeat.go",
    "content": "package iteration\n\nconst repeatCount = 5\n\n// Repeat returns character repeated 5 times.\nfunc Repeat(character string) string {\n\tvar repeated string\n\tfor i := 0; i < repeatCount; i++ {\n\t\trepeated += character\n\t}\n\treturn repeated\n}\n"
  },
  {
    "path": "for/vx/repeat_test.go",
    "content": "package iteration\n\nimport \"testing\"\n\nfunc TestRepeat(t *testing.T) {\n\trepeated := Repeat(\"a\")\n\texpected := \"aaaaa\"\n\n\tif repeated != expected {\n\t\tt.Errorf(\"expected %q but got %q\", expected, repeated)\n\t}\n}\n\nfunc BenchmarkRepeat(b *testing.B) {\n\tfor b.Loop() {\n\t\tRepeat(\"a\")\n\t}\n}\n"
  },
  {
    "path": "gb-readme.md",
    "content": "# Learn Go with Tests\n\n![](.gitbook/assets/red-green-blue-gophers-smaller.png)\n\n[Art by Denise](https://twitter.com/deniseyu21)\n\n## Support me\n\nI am proud to offer this resource for free, but if you wish to give some appreciation\n\n* [Tweet me @quii](https://twitter.com/quii)\n* [Mastodon](https://mastodon.cloud/@quii)\n* [Buy me a coffee](https://www.buymeacoffee.com/quii)\n* [Sponsor me on GitHub](https://github.com/sponsors/quii)\n\n## Learn test-driven development with Go\n\n* Explore the Go language by writing tests\n* **Get a grounding with TDD**. Go is a good language for learning TDD because it is a simple language to learn and testing is built-in\n* Be confident that you'll be able to start writing robust, well-tested systems in Go\n\nTranslations:\n\n* [中文](https://studygolang.gitbook.io/learn-go-with-tests)\n* [Português](https://larien.gitbook.io/aprenda-go-com-testes/)\n* [日本語](https://andmorefine.gitbook.io/learn-go-with-tests/)\n* [Français](https://goosegeesejeez.gitbook.io/apprendre-go-par-les-tests)\n* [한국어](https://miryang.gitbook.io/learn-go-with-tests/)\n* [Türkçe](https://halilkocaoz.gitbook.io/go-programlama-dilini-ogren/)\n* [Nederlands](https://bobkosse.gitbook.io/leer-go-met-tests)\n\n## Background\n\nI have some experience introducing Go to development teams and have tried different approaches as to how to grow a team from some people curious about Go into highly effective writers of Go systems.\n\n### What didn't work\n\n#### Read _the_ book\n\nAn approach we tried was to take [the blue book](https://www.amazon.co.uk/Programming-Language-Addison-Wesley-Professional-Computing/dp/0134190440) and every week discuss the next chapter along with the exercises.\n\nI love this book but it requires a high level of commitment. The book is very detailed in explaining concepts, which is obviously great but it means that the progress is slow and steady - this is not for everyone.\n\nI found that whilst a small number of people would read chapter X and do the exercises, many people didn't.\n\n#### Solve some problems\n\nKatas are fun but they are usually limited in their scope for learning a language; you're unlikely to use goroutines to solve a kata.\n\nAnother problem is when you have varying levels of enthusiasm. Some people just learn way more of the language than others and when demonstrating what they have done end up confusing people with features the others are not familiar with.\n\nThis ends up making the learning feel quite _unstructured_ and _ad hoc_.\n\n### What did work\n\nBy far the most effective way was by slowly introducing the fundamentals of the language by reading through [go by example](https://gobyexample.com/), exploring them with examples and discussing them as a group. This was a more interactive approach than \"read chapter x for homework\".\n\nOver time the team gained a solid foundation of the _grammar_ of the language so we could then start to build systems.\n\nThis to me seems analogous to practicing scales when trying to learn guitar.\n\nIt doesn't matter how artistic you think you are, you are unlikely to write good music without understanding the fundamentals and practicing the mechanics.\n\n### What works for me\n\nWhen _I_ learn a new programming language I usually start by messing around in a REPL but eventually, I need more structure.\n\nWhat I like to do is explore concepts and then solidify the ideas with tests. Tests verify the code I write is correct and documents the feature I have learned.\n\nTaking my experience of learning with a group and my own personal way I am going to try and create something that hopefully proves useful to other teams. Learning the fundamentals by writing small tests so that you can then take your existing software design skills and ship some great systems.\n\n## Who this is for\n\n* People who are interested in picking up Go\n* People who already know some Go, but want to explore testing more\n\n## What you'll need\n\n* A computer!\n* [Installed Go](https://golang.org/)\n* A text editor\n* Some experience with programming. Understanding of concepts like `if`, variables, functions etc.\n* Comfortable using the terminal\n\n## Feedback\n\n* Add issues/submit PRs [here](https://github.com/quii/learn-go-with-tests) or [tweet me @quii](https://twitter.com/quii)\n\n[MIT license](LICENSE.md)\n"
  },
  {
    "path": "generics/assert.go",
    "content": "package generics\n\nimport \"testing\"\n\nfunc AssertEqual[T comparable](t *testing.T, got, want T) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"got %v, want %v\", got, want)\n\t}\n}\n\nfunc AssertNotEqual[T comparable](t *testing.T, got, want T) {\n\tt.Helper()\n\tif got == want {\n\t\tt.Errorf(\"didn't want %v\", got)\n\t}\n}\n\nfunc AssertTrue(t *testing.T, got bool) {\n\tt.Helper()\n\tif !got {\n\t\tt.Errorf(\"got %v, want true\", got)\n\t}\n}\n\nfunc AssertFalse(t *testing.T, got bool) {\n\tt.Helper()\n\tif got {\n\t\tt.Errorf(\"got %v, want false\", got)\n\t}\n}\n"
  },
  {
    "path": "generics/generics_test.go",
    "content": "package generics\n\nimport \"testing\"\n\nfunc TestAssertFunctions(t *testing.T) {\n\tt.Run(\"asserting on integers\", func(t *testing.T) {\n\t\tAssertEqual(t, 1, 1)\n\t\tAssertNotEqual(t, 1, 2)\n\t})\n\n\tt.Run(\"asserting on strings\", func(t *testing.T) {\n\t\tAssertEqual(t, \"hello\", \"hello\")\n\t\tAssertNotEqual(t, \"hello\", \"Grace\")\n\t})\n\n\t// AssertEqual(t, 1, \"1\") // uncomment to see the compilation error\n}\n\nfunc TestStack(t *testing.T) {\n\tt.Run(\"integer stack\", func(t *testing.T) {\n\t\tmyStackOfInts := NewStack[int]()\n\n\t\t// check stack is empty\n\t\tAssertTrue(t, myStackOfInts.IsEmpty())\n\n\t\t// add a thing, then check it's not empty\n\t\tmyStackOfInts.Push(123)\n\t\tAssertFalse(t, myStackOfInts.IsEmpty())\n\n\t\t// add another thing, pop it back again\n\t\tmyStackOfInts.Push(456)\n\t\tvalue, _ := myStackOfInts.Pop()\n\t\tAssertEqual(t, value, 456)\n\t\tvalue, _ = myStackOfInts.Pop()\n\t\tAssertEqual(t, value, 123)\n\t\tAssertTrue(t, myStackOfInts.IsEmpty())\n\n\t\t// can get the numbers we put in as numbers, not untyped interface{}\n\t\tmyStackOfInts.Push(1)\n\t\tmyStackOfInts.Push(2)\n\t\tfirstNum, _ := myStackOfInts.Pop()\n\t\tsecondNum, _ := myStackOfInts.Pop()\n\t\tAssertEqual(t, firstNum+secondNum, 3)\n\t})\n}\n"
  },
  {
    "path": "generics/stack.go",
    "content": "package generics\n\ntype Stack[T any] struct {\n\tvalues []T\n}\n\nfunc NewStack[T any]() *Stack[T] {\n\treturn new(Stack[T])\n}\n\nfunc (s *Stack[T]) Push(value T) {\n\ts.values = append(s.values, value)\n}\n\nfunc (s *Stack[T]) IsEmpty() bool {\n\treturn len(s.values) == 0\n}\n\nfunc (s *Stack[T]) Pop() (T, bool) {\n\tif s.IsEmpty() {\n\t\tvar zero T\n\t\treturn zero, false\n\t}\n\n\tindex := len(s.values) - 1\n\tel := s.values[index]\n\ts.values = s.values[:index]\n\treturn el, true\n}\n"
  },
  {
    "path": "generics.md",
    "content": "# Generics\n\n**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/main/generics)**\n\nThis chapter will give you an introduction to generics, dispel reservations you may have about them, and give you an idea how to simplify some of your code in the future. After reading this you'll know how to write:\n\n- A function that takes generic arguments\n- A generic data-structure\n\n\n## Our own test helpers (`AssertEqual`, `AssertNotEqual`)\n\nTo explore generics we'll write some test helpers.\n\n### Assert on integers\n\nLet's start with something basic and iterate toward our goal\n\n```go\nimport \"testing\"\n\nfunc TestAssertFunctions(t *testing.T) {\n\tt.Run(\"asserting on integers\", func(t *testing.T) {\n\t\tAssertEqual(t, 1, 1)\n\t\tAssertNotEqual(t, 1, 2)\n\t})\n}\n\nfunc AssertEqual(t *testing.T, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"got %d, want %d\", got, want)\n\t}\n}\n\nfunc AssertNotEqual(t *testing.T, got, want int) {\n\tt.Helper()\n\tif got == want {\n\t\tt.Errorf(\"didn't want %d\", got)\n\t}\n}\n```\n\n\n### Assert on strings\n\nBeing able to assert on the equality of integers is great but what if we want to assert on `string` ?\n\n```go\nt.Run(\"asserting on strings\", func(t *testing.T) {\n\tAssertEqual(t, \"hello\", \"hello\")\n\tAssertNotEqual(t, \"hello\", \"Grace\")\n})\n```\n\nYou'll get an error\n\n```\n# github.com/quii/learn-go-with-tests/generics [github.com/quii/learn-go-with-tests/generics.test]\n./generics_test.go:12:18: cannot use \"hello\" (untyped string constant) as int value in argument to AssertEqual\n./generics_test.go:13:21: cannot use \"hello\" (untyped string constant) as int value in argument to AssertNotEqual\n./generics_test.go:13:30: cannot use \"Grace\" (untyped string constant) as int value in argument to AssertNotEqual\n```\n\nIf you take your time to read the error, you'll see the compiler is complaining that we're trying to pass a `string` to a function that expects an `integer`.\n\n#### Recap on type-safety\n\nIf you've read the previous chapters of this book, or have experience with statically typed languages, this should not surprise you. The Go compiler expects you to write your functions, structs e.t.c. by describing what types you wish to work with.\n\nYou can't pass a `string` to a function that expects an `integer`.\n\nWhilst this can feel like ceremony, it can be extremely helpful. By describing these constraints you,\n\n- Make function implementation simpler. By describing to the compiler what types you work with, you **constrain the number of possible valid implementations**. You can't \"add\" a `Person` and a `BankAccount`. You can't capitalise an `integer`. In software, constraints are often extremely helpful.\n- Are prevented from accidentally passing data to a function you didn't mean to.\n\nGo offers you a way to be more abstract with your types with [interfaces](./structs-methods-and-interfaces.md), so that you can design functions that do not take concrete types but instead, types that offer the behaviour you need. This gives you some flexibility whilst maintaining type-safety.\n\n### A function that takes a string or an integer? (or indeed, other things)\n\nAnother option Go has to make your functions more flexible is by declaring the type of your argument as `interface{}` which means \"anything\".\n\nTry changing the signatures to use this type instead.\n\n```go\nfunc AssertEqual(got, want interface{})\n\nfunc AssertNotEqual(got, want interface{})\n\n```\n\nThe tests should now compile and pass. If you try making them fail you'll see the output is a bit ropey because we're using the integer `%d` format string to print our messages, so change them to the general `%+v` format for a better output of any kind of value.\n\n### The problem with `interface{}`\n\nOur `AssertX` functions are quite naive but conceptually aren't too different to how other [popular libraries offer this functionality](https://github.com/matryer/is/blob/master/is.go#L150)\n\n```go\nfunc (is *I) Equal(a, b interface{})\n```\n\nSo what's the problem?\n\nBy using `interface{}` the compiler can't help us when writing our code, because we're not telling it anything useful about the types of things passed to the function. Try comparing two different types.\n\n```go\nAssertEqual(1, \"1\")\n```\n\nIn this case, we get away with it; the test compiles, and it fails as we'd hope, although the error message `got 1, want 1` is unclear; but do we want to be able to compare strings with integers? What about comparing a `Person` with an `Airport`?\n\nWriting functions that take `interface{}` can be extremely challenging and bug-prone because we've _lost_ our constraints, and we have no information at compile time as to what kinds of data we're dealing with.\n\nThis means **the compiler can't help us** and we're instead more likely to have **runtime errors** which could affect our users, cause outages, or worse.\n\nOften developers have to use reflection to implement these *ahem* generic functions, which can get complicated to read and write, and can hurt the performance of your program.\n\n## Our own test helpers with generics\n\nIdeally, we don't want to have to make specific `AssertX` functions for every type we ever deal with. We'd like to be able to have _one_ `AssertEqual` function that works with _any_ type but does not let you compare [apples and oranges](https://en.wikipedia.org/wiki/Apples_and_oranges).\n\nGenerics offer us a way to make abstractions (like interfaces) by letting us **describe our constraints**. They allow us to write functions that have a similar level of flexibility that `interface{}` offers but retain type-safety and provide a better developer experience for callers.\n\n```go\nfunc TestAssertFunctions(t *testing.T) {\n\tt.Run(\"asserting on integers\", func(t *testing.T) {\n\t\tAssertEqual(t, 1, 1)\n\t\tAssertNotEqual(t, 1, 2)\n\t})\n\n\tt.Run(\"asserting on strings\", func(t *testing.T) {\n\t\tAssertEqual(t, \"hello\", \"hello\")\n\t\tAssertNotEqual(t, \"hello\", \"Grace\")\n\t})\n\n\t// AssertEqual(t, 1, \"1\") // uncomment to see the error\n}\n\nfunc AssertEqual[T comparable](t *testing.T, got, want T) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"got %v, want %v\", got, want)\n\t}\n}\n\nfunc AssertNotEqual[T comparable](t *testing.T, got, want T) {\n\tt.Helper()\n\tif got == want {\n\t\tt.Errorf(\"didn't want %v\", got)\n\t}\n}\n```\n\nTo write generic functions in Go, you need to provide \"type parameters\" which is just a fancy way of saying \"describe your generic type and give it a label\".\n\nIn our case the type of our type parameter is `comparable` and we've given it the label of `T`. This label then lets us describe the types for the arguments to our function (`got, want T`).\n\nWe're using `comparable` because we want to describe to the compiler that we wish to use the `==` and `!=` operators on things of type `T` in our function, we want to compare! If you try changing the type to `any`,\n\n```go\nfunc AssertNotEqual[T any](got, want T)\n```\n\nYou'll get the following error:\n\n```\nprog.go2:15:5: cannot compare got != want (operator != not defined for T)\n```\n\nWhich makes a lot of sense, because you can't use those operators on every (or `any`) type.\n\n### Is a generic function with [`T any`](https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-type-parameters.md#the-constraint) the same as `interface{}` ?\n\nConsider two functions\n\n```go\nfunc GenericFoo[T any](x, y T)\n```\n\n```go\nfunc InterfaceyFoo(x, y interface{})\n```\n\nWhat's the point of generics here? Doesn't `any` describe... anything?\n\nIn terms of constraints, `any` does mean \"anything\" and so does `interface{}`. In fact, `any` was added in 1.18 and is _just an alias for `interface{}`_.\n\nThe difference with the generic version is _you're still describing a specific type_ and what that means is we've still constrained this function to only work with _one_ type.\n\nWhat this means is you can call `InterfaceyFoo` with any combination of types (e.g `InterfaceyFoo(apple, orange)`). However `GenericFoo` still offers some constraints because we've said that it only works with _one_ type, `T`.\n\nValid:\n\n- `GenericFoo(apple1, apple2)`\n- `GenericFoo(orange1, orange2)`\n- `GenericFoo(1, 2)`\n- `GenericFoo(\"one\", \"two\")`\n\nNot valid (fails compilation):\n\n- `GenericFoo(apple1, orange1)`\n- `GenericFoo(\"1\", 1)`\n\nIf your function returns the generic type, the caller can also use the type as it was, rather than having to make a type assertion because when a function returns `interface{}` the compiler cannot make any guarantees about the type.\n\n## Next: Generic data types\n\nWe're going to create a [stack](https://en.wikipedia.org/wiki/Stack_(abstract_data_type)) data type. Stacks should be fairly straightforward to understand from a requirements point of view. They're a collection of items where you can `Push` items to the \"top\" and to get items back again you `Pop` items from the top (LIFO - last in, first out).\n\nFor the sake of brevity I've omitted the TDD process that arrived me at the following code for a stack of `int`s, and a stack of `string`s.\n\n```go\ntype StackOfInts struct {\n\tvalues []int\n}\n\nfunc (s *StackOfInts) Push(value int) {\n\ts.values = append(s.values, value)\n}\n\nfunc (s *StackOfInts) IsEmpty() bool {\n\treturn len(s.values) == 0\n}\n\nfunc (s *StackOfInts) Pop() (int, bool) {\n\tif s.IsEmpty() {\n\t\treturn 0, false\n\t}\n\n\tindex := len(s.values) - 1\n\tel := s.values[index]\n\ts.values = s.values[:index]\n\treturn el, true\n}\n\ntype StackOfStrings struct {\n\tvalues []string\n}\n\nfunc (s *StackOfStrings) Push(value string) {\n\ts.values = append(s.values, value)\n}\n\nfunc (s *StackOfStrings) IsEmpty() bool {\n\treturn len(s.values) == 0\n}\n\nfunc (s *StackOfStrings) Pop() (string, bool) {\n\tif s.IsEmpty() {\n\t\treturn \"\", false\n\t}\n\n\tindex := len(s.values) - 1\n\tel := s.values[index]\n\ts.values = s.values[:index]\n\treturn el, true\n}\n```\n\nI've created a couple of other assertion functions to help out\n\n```go\nfunc AssertTrue(t *testing.T, got bool) {\n\tt.Helper()\n\tif !got {\n\t\tt.Errorf(\"got %v, want true\", got)\n\t}\n}\n\nfunc AssertFalse(t *testing.T, got bool) {\n\tt.Helper()\n\tif got {\n\t\tt.Errorf(\"got %v, want false\", got)\n\t}\n}\n```\n\nAnd here's the tests\n\n```go\nfunc TestStack(t *testing.T) {\n\tt.Run(\"integer stack\", func(t *testing.T) {\n\t\tmyStackOfInts := new(StackOfInts)\n\n\t\t// check stack is empty\n\t\tAssertTrue(t, myStackOfInts.IsEmpty())\n\n\t\t// add a thing, then check it's not empty\n\t\tmyStackOfInts.Push(123)\n\t\tAssertFalse(t, myStackOfInts.IsEmpty())\n\n\t\t// add another thing, pop it back again\n\t\tmyStackOfInts.Push(456)\n\t\tvalue, _ := myStackOfInts.Pop()\n\t\tAssertEqual(t, value, 456)\n\t\tvalue, _ = myStackOfInts.Pop()\n\t\tAssertEqual(t, value, 123)\n\t\tAssertTrue(t, myStackOfInts.IsEmpty())\n\t})\n\n\tt.Run(\"string stack\", func(t *testing.T) {\n\t\tmyStackOfStrings := new(StackOfStrings)\n\n\t\t// check stack is empty\n\t\tAssertTrue(t, myStackOfStrings.IsEmpty())\n\n\t\t// add a thing, then check it's not empty\n\t\tmyStackOfStrings.Push(\"123\")\n\t\tAssertFalse(t, myStackOfStrings.IsEmpty())\n\n\t\t// add another thing, pop it back again\n\t\tmyStackOfStrings.Push(\"456\")\n\t\tvalue, _ := myStackOfStrings.Pop()\n\t\tAssertEqual(t, value, \"456\")\n\t\tvalue, _ = myStackOfStrings.Pop()\n\t\tAssertEqual(t, value, \"123\")\n\t\tAssertTrue(t, myStackOfStrings.IsEmpty())\n\t})\n}\n```\n\n### Problems\n\n- The code for both `StackOfStrings` and `StackOfInts` is almost identical. Whilst duplication isn't always the end of the world, it's more code to read, write and maintain.\n- As we're duplicating the logic across two types, we've had to duplicate the tests too.\n\nWe really want to capture the _idea_ of a stack in one type, and have one set of tests for them. We should be wearing our refactoring hat right now which means we should not be changing the tests because we want to maintain the same behaviour.\n\nWithout generics, this is what we _could_ do\n\n```go\ntype StackOfInts = Stack\ntype StackOfStrings = Stack\n\ntype Stack struct {\n\tvalues []interface{}\n}\n\nfunc (s *Stack) Push(value interface{}) {\n\ts.values = append(s.values, value)\n}\n\nfunc (s *Stack) IsEmpty() bool {\n\treturn len(s.values) == 0\n}\n\nfunc (s *Stack) Pop() (interface{}, bool) {\n\tif s.IsEmpty() {\n\t\tvar zero interface{}\n\t\treturn zero, false\n\t}\n\n\tindex := len(s.values) - 1\n\tel := s.values[index]\n\ts.values = s.values[:index]\n\treturn el, true\n}\n```\n\n- We're aliasing our previous implementations of `StackOfInts` and `StackOfStrings` to a new unified type `Stack`\n- We've removed the type safety from the `Stack` by making it so `values` is a [slice](https://github.com/quii/learn-go-with-tests/blob/main/arrays-and-slices.md) of `interface{}`\n\nTo try this code, you'll have to remove the type constraints from our assert functions:\n\n```go\nfunc AssertEqual(t *testing.T, got, want interface{})\n```\n\nIf you do this, our tests still pass. Who needs generics?\n\n### The problem with throwing out type safety\n\nThe first problem is the same as we saw with our `AssertEquals` - we've lost type safety. I can now `Push` apples onto a stack of oranges.\n\nEven if we have the discipline not to do this, the code is still unpleasant to work with because when methods **return `interface{}` they are horrible to work with**.\n\nAdd the following test,\n\n```go\nt.Run(\"interface stack DX is horrid\", func(t *testing.T) {\n\tmyStackOfInts := new(StackOfInts)\n\n\tmyStackOfInts.Push(1)\n\tmyStackOfInts.Push(2)\n\tfirstNum, _ := myStackOfInts.Pop()\n\tsecondNum, _ := myStackOfInts.Pop()\n\tAssertEqual(t, firstNum+secondNum, 3)\n})\n```\n\nYou get a compiler error, showing the weakness of losing type-safety:\n\n```\ninvalid operation: operator + not defined on firstNum (variable of type interface{})\n```\n\nWhen `Pop` returns `interface{}` it means the compiler has no information about what the data is and therefore severely limits what we can do. It can't know that it should be an integer, so it does not let us use the `+` operator.\n\nTo get around this, the caller has to do a [type assertion](https://golang.org/ref/spec#Type_assertions) for each value.\n\n```go\nt.Run(\"interface stack dx is horrid\", func(t *testing.T) {\n\tmyStackOfInts := new(StackOfInts)\n\n\tmyStackOfInts.Push(1)\n\tmyStackOfInts.Push(2)\n\tfirstNum, _ := myStackOfInts.Pop()\n\tsecondNum, _ := myStackOfInts.Pop()\n\n\t// get our ints from out interface{}\n\treallyFirstNum, ok := firstNum.(int)\n\tAssertTrue(t, ok) // need to check we definitely got an int out of the interface{}\n\n\treallySecondNum, ok := secondNum.(int)\n\tAssertTrue(t, ok) // and again!\n\n\tAssertEqual(t, reallyFirstNum+reallySecondNum, 3)\n})\n```\n\nThe unpleasantness radiating from this test would be repeated for every potential user of our `Stack` implementation, yuck.\n\n### Generic data structures to the rescue\n\nJust like you can define generic arguments to functions, you can define generic data structures.\n\nHere's our new `Stack` implementation, featuring a generic data type.\n\n```go\ntype Stack[T any] struct {\n\tvalues []T\n}\n\nfunc (s *Stack[T]) Push(value T) {\n\ts.values = append(s.values, value)\n}\n\nfunc (s *Stack[T]) IsEmpty() bool {\n\treturn len(s.values) == 0\n}\n\nfunc (s *Stack[T]) Pop() (T, bool) {\n\tif s.IsEmpty() {\n\t\tvar zero T\n\t\treturn zero, false\n\t}\n\n\tindex := len(s.values) - 1\n\tel := s.values[index]\n\ts.values = s.values[:index]\n\treturn el, true\n}\n```\n\nHere's the tests, showing them working how we'd like them to work, with full type-safety.\n\n```go\nfunc TestStack(t *testing.T) {\n\tt.Run(\"integer stack\", func(t *testing.T) {\n\t\tmyStackOfInts := new(Stack[int])\n\n\t\t// check stack is empty\n\t\tAssertTrue(t, myStackOfInts.IsEmpty())\n\n\t\t// add a thing, then check it's not empty\n\t\tmyStackOfInts.Push(123)\n\t\tAssertFalse(t, myStackOfInts.IsEmpty())\n\n\t\t// add another thing, pop it back again\n\t\tmyStackOfInts.Push(456)\n\t\tvalue, _ := myStackOfInts.Pop()\n\t\tAssertEqual(t, value, 456)\n\t\tvalue, _ = myStackOfInts.Pop()\n\t\tAssertEqual(t, value, 123)\n\t\tAssertTrue(t, myStackOfInts.IsEmpty())\n\n\t\t// can get the numbers we put in as numbers, not untyped interface{}\n\t\tmyStackOfInts.Push(1)\n\t\tmyStackOfInts.Push(2)\n\t\tfirstNum, _ := myStackOfInts.Pop()\n\t\tsecondNum, _ := myStackOfInts.Pop()\n\t\tAssertEqual(t, firstNum+secondNum, 3)\n\t})\n}\n```\n\nYou'll notice the syntax for defining generic data structures is consistent with defining generic arguments to functions.\n\n```go\ntype Stack[T any] struct {\n\tvalues []T\n}\n```\n\nIt's _almost_ the same as before, it's just that what we're saying is the **type of the stack constrains what type of values you can work with**.\n\nOnce you create a `Stack[Orange]` or a `Stack[Apple]` the methods defined on our stack will only let you pass in and will only return the particular type of the stack you're working with:\n\n```go\nfunc (s *Stack[T]) Pop() (T, bool)\n```\n\nYou can imagine the types of implementation being somehow generated for you, depending on what type of stack you create:\n\n```go\nfunc (s *Stack[Orange]) Pop() (Orange, bool)\n```\n\n```go\nfunc (s *Stack[Apple]) Pop() (Apple, bool)\n```\n\nNow that we have done this refactoring, we can safely remove the string stack test because we don't need to prove the same logic over and over.\n\nNote that so far in the examples of calling generic functions, we have not needed to specify the generic types. For example, to call `AssertEqual[T]`, we do not need to specify what the type `T` is since it can be inferred from the arguments. In cases where the generic types cannot be inferred, you need to specify the types when calling the function. The syntax is the same as when defining the function, i.e. you specify the types inside square brackets before the arguments.\n\nFor a concrete example, consider making a constructor for `Stack[T]`.\n```go\nfunc NewStack[T any]() *Stack[T] {\n\treturn new(Stack[T])\n}\n```\nTo use this constructor to create a stack of ints and a stack of strings for example, you call it like this:\n```go\nmyStackOfInts := NewStack[int]()\nmyStackOfStrings := NewStack[string]()\n```\n\nHere is the `Stack` implementation and the tests after adding the constructor.\n\n```go\ntype Stack[T any] struct {\n\tvalues []T\n}\n\nfunc NewStack[T any]() *Stack[T] {\n\treturn new(Stack[T])\n}\n\nfunc (s *Stack[T]) Push(value T) {\n\ts.values = append(s.values, value)\n}\n\nfunc (s *Stack[T]) IsEmpty() bool {\n\treturn len(s.values) == 0\n}\n\nfunc (s *Stack[T]) Pop() (T, bool) {\n\tif s.IsEmpty() {\n\t\tvar zero T\n\t\treturn zero, false\n\t}\n\n\tindex := len(s.values) - 1\n\tel := s.values[index]\n\ts.values = s.values[:index]\n\treturn el, true\n}\n```\n\n```go\nfunc TestStack(t *testing.T) {\n\tt.Run(\"integer stack\", func(t *testing.T) {\n\t\tmyStackOfInts := NewStack[int]()\n\n\t\t// check stack is empty\n\t\tAssertTrue(t, myStackOfInts.IsEmpty())\n\n\t\t// add a thing, then check it's not empty\n\t\tmyStackOfInts.Push(123)\n\t\tAssertFalse(t, myStackOfInts.IsEmpty())\n\n\t\t// add another thing, pop it back again\n\t\tmyStackOfInts.Push(456)\n\t\tvalue, _ := myStackOfInts.Pop()\n\t\tAssertEqual(t, value, 456)\n\t\tvalue, _ = myStackOfInts.Pop()\n\t\tAssertEqual(t, value, 123)\n\t\tAssertTrue(t, myStackOfInts.IsEmpty())\n\n\t\t// can get the numbers we put in as numbers, not untyped interface{}\n\t\tmyStackOfInts.Push(1)\n\t\tmyStackOfInts.Push(2)\n\t\tfirstNum, _ := myStackOfInts.Pop()\n\t\tsecondNum, _ := myStackOfInts.Pop()\n\t\tAssertEqual(t, firstNum+secondNum, 3)\n\t})\n}\n```\n\n\nUsing a generic data type we have:\n\n- Reduced duplication of important logic.\n- Made `Pop` return `T` so that if we create a `Stack[int]` we in practice get back `int` from `Pop`; we can now use `+` without the need for type assertion gymnastics.\n- Prevented misuse at compile time. You cannot `Push` oranges to an apple stack.\n\n## Wrapping up\n\nThis chapter should have given you a taste of generics syntax, and some ideas as to why generics might be helpful. We've written our own `Assert` functions which we can safely re-use to experiment with other ideas around generics, and we've implemented a simple data structure to store any type of data we wish, in a type-safe manner.\n\n### Generics are simpler than using `interface{}` in most cases\n\nIf you're inexperienced with statically-typed languages, the point of generics may not be immediately obvious, but I hope the examples in this chapter have illustrated where the Go language isn't as expressive as we'd like. In particular using `interface{}` makes your code:\n\n- Less safe (mix apples and oranges), requires more error handling\n- Less expressive, `interface{}` tells you nothing about the data\n- More likely to rely on [reflection](https://github.com/quii/learn-go-with-tests/blob/main/reflection.md), type-assertions etc which makes your code more difficult to work with and more error prone as it pushes checks from compile-time to runtime\n\nUsing statically typed languages is an act of describing constraints. If you do it well, you create code that is not only safe and simple to use but also simpler to write because the possible solution space is smaller.\n\nGenerics gives us a new way to express constraints in our code, which as demonstrated will allow us to consolidate and simplify code that was not possible until Go 1.18.\n\n### Will generics turn Go into Java?\n\n- No.\n\nThere's a lot of [FUD (fear, uncertainty and doubt)](https://en.wikipedia.org/wiki/Fear,_uncertainty,_and_doubt) in the Go community about generics leading to nightmare abstractions and baffling code bases. This is usually caveatted with \"they must be used carefully\".\n\nWhilst this is true, it's not especially useful advice because this is true of any language feature.\n\nNot many people complain about our ability to define interfaces which, like generics is a way of describing constraints within our code. When you describe an interface you are making a design choice that _could be poor_, generics are not unique in their ability to make confusing, annoying to use code.\n\n### You're already using generics\n\nWhen you consider that if you've used arrays, slices or maps; you've _already been a consumer of generic code_.\n\n```\nvar myApples []Apple\n// You can't do this!\nappend(myApples, Orange{})\n```\n\n### Abstraction is not a dirty word\n\nIt's easy to dunk on [AbstractSingletonProxyFactoryBean](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/aop/framework/AbstractSingletonProxyFactoryBean.html) but let's not pretend a code base with no abstraction at all isn't also bad. It's your job to _gather_ related concepts when appropriate, so your system is easier to understand and change; rather than being a collection of disparate functions and types with a lack of clarity.\n\n### [Make it work, make it right, make it fast](https://wiki.c2.com/?MakeItWorkMakeItRightMakeItFast#:~:text=%22Make%20it%20work%2C%20make%20it,to%20DesignForPerformance%20ahead%20of%20time.)\n\nPeople run in to problems with generics when they're abstracting too quickly without enough information to make good design decisions.\n\nThe TDD cycle of red, green, refactor means that you have more guidance as to what code you _actually need_ to deliver your behaviour, **rather than imagining abstractions up front**; but you still need to be careful.\n\nThere's no hard and fast rules here but resist making things generic until you can see that you have a useful generalisation. When we created the various `Stack` implementations we importantly started with _concrete_ behaviour like `StackOfStrings` and `StackOfInts` backed by tests. From our _real_ code we could start to see real patterns, and backed by our tests, we could explore refactoring toward a more general-purpose solution.\n\nPeople often advise you to only generalise when you see the same code three times, which seems like a good starting rule of thumb.\n\nA common path I've taken in other programming languages has been:\n\n- One TDD cycle to drive some behaviour\n- Another TDD cycle to exercise some other related scenarios\n\n> Hmm, these things look similar - but a little duplication is better than coupling to a bad abstraction\n\n- Sleep on it\n- Another TDD cycle\n\n> OK, I'd like to try to see if I can generalise this thing. Thank goodness I am so smart and good-looking because I use TDD, so I can refactor whenever I wish, and the process has helped me understand what behaviour I actually need before designing too much.\n\n- This abstraction feels nice! The tests are still passing, and the code is simpler\n- I can now delete a number of tests, I've captured the _essence_ of the behaviour and removed unnecessary detail\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/quii/learn-go-with-tests\n\ngo 1.24\n\nrequire (\n\tgithub.com/approvals/go-approval-tests v0.0.0-20211008131110-0c40b30e0000\n\tgithub.com/gomarkdown/markdown v0.0.0-20240626202925-2eda941fd024\n\tgithub.com/gorilla/websocket v1.5.3\n)\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/approvals/go-approval-tests v0.0.0-20211008131110-0c40b30e0000 h1:H152l3O+2XIXQu8IrqEXeqJOFCvSShUXs7+x0lw8V1k=\ngithub.com/approvals/go-approval-tests v0.0.0-20211008131110-0c40b30e0000/go.mod h1:PJOqSY8IofNv3heAD6k8E7EfFS6okiSS9bSAasaAUME=\ngithub.com/gomarkdown/markdown v0.0.0-20240626202925-2eda941fd024 h1:saBP362Qm7zDdDXqv61kI4rzhmLFq3Z1gx34xpl6cWE=\ngithub.com/gomarkdown/markdown v0.0.0-20240626202925-2eda941fd024/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\n"
  },
  {
    "path": "hello-world/v1/hello.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(\"Hello, world\")\n}\n"
  },
  {
    "path": "hello-world/v2/hello.go",
    "content": "package main\n\nimport \"fmt\"\n\n// Hello returns a greeting.\nfunc Hello() string {\n\treturn \"Hello, world\"\n}\n\nfunc main() {\n\tfmt.Println(Hello())\n}\n"
  },
  {
    "path": "hello-world/v2/hello_test.go",
    "content": "package main\n\nimport \"testing\"\n\nfunc TestHello(t *testing.T) {\n\tgot := Hello()\n\twant := \"Hello, world\"\n\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "hello-world/v3/hello.go",
    "content": "package main\n\nimport \"fmt\"\n\n// Hello returns a personalised greeting.\nfunc Hello(name string) string {\n\treturn \"Hello, \" + name\n}\n\nfunc main() {\n\tfmt.Println(Hello(\"world\"))\n}\n"
  },
  {
    "path": "hello-world/v3/hello_test.go",
    "content": "package main\n\nimport \"testing\"\n\nfunc TestHello(t *testing.T) {\n\tgot := Hello(\"Chris\")\n\twant := \"Hello, Chris\"\n\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "hello-world/v4/hello.go",
    "content": "package main\n\nimport \"fmt\"\n\nconst englishHelloPrefix = \"Hello, \"\n\n// Hello returns a personalised greeting.\nfunc Hello(name string) string {\n\treturn englishHelloPrefix + name\n}\n\nfunc main() {\n\tfmt.Println(Hello(\"world\"))\n}\n"
  },
  {
    "path": "hello-world/v4/hello_test.go",
    "content": "package main\n\nimport \"testing\"\n\nfunc TestHello(t *testing.T) {\n\tgot := Hello(\"Chris\")\n\twant := \"Hello, Chris\"\n\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "hello-world/v5/hello.go",
    "content": "package main\n\nimport \"fmt\"\n\nconst englishHelloPrefix = \"Hello, \"\n\n// Hello returns a personalised greeting, defaulting to Hello, world if an empty name is passed.\nfunc Hello(name string) string {\n\tif name == \"\" {\n\t\tname = \"World\"\n\t}\n\treturn englishHelloPrefix + name\n}\n\nfunc main() {\n\tfmt.Println(Hello(\"world\"))\n}\n"
  },
  {
    "path": "hello-world/v5/hello_test.go",
    "content": "package main\n\nimport \"testing\"\n\nfunc TestHello(t *testing.T) {\n\tt.Run(\"saying hello to people\", func(t *testing.T) {\n\t\tgot := Hello(\"Chris\")\n\t\twant := \"Hello, Chris\"\n\t\tassertCorrectMessage(t, got, want)\n\t})\n\n\tt.Run(\"empty string defaults to 'world'\", func(t *testing.T) {\n\t\tgot := Hello(\"\")\n\t\twant := \"Hello, World\"\n\t\tassertCorrectMessage(t, got, want)\n\t})\n\n}\n\nfunc assertCorrectMessage(t testing.TB, got, want string) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "hello-world/v6/hello.go",
    "content": "package main\n\nimport \"fmt\"\n\nconst spanish = \"Spanish\"\nconst french = \"French\"\nconst englishHelloPrefix = \"Hello, \"\nconst spanishHelloPrefix = \"Hola, \"\nconst frenchHelloPrefix = \"Bonjour, \"\n\n// Hello returns a personalised greeting in a given language.\nfunc Hello(name string, language string) string {\n\tif name == \"\" {\n\t\tname = \"World\"\n\t}\n\n\tif language == spanish {\n\t\treturn spanishHelloPrefix + name\n\t}\n\n\tif language == french {\n\t\treturn frenchHelloPrefix + name\n\t}\n\n\treturn englishHelloPrefix + name\n}\n\nfunc main() {\n\tfmt.Println(Hello(\"world\", \"\"))\n}\n"
  },
  {
    "path": "hello-world/v6/hello_test.go",
    "content": "package main\n\nimport \"testing\"\n\nfunc TestHello(t *testing.T) {\n\tt.Run(\"to a person\", func(t *testing.T) {\n\t\tgot := Hello(\"Chris\", \"\")\n\t\twant := \"Hello, Chris\"\n\t\tassertCorrectMessage(t, got, want)\n\t})\n\n\tt.Run(\"empty string\", func(t *testing.T) {\n\t\tgot := Hello(\"\", \"\")\n\t\twant := \"Hello, World\"\n\t\tassertCorrectMessage(t, got, want)\n\t})\n\n\tt.Run(\"in Spanish\", func(t *testing.T) {\n\t\tgot := Hello(\"Elodie\", spanish)\n\t\twant := \"Hola, Elodie\"\n\t\tassertCorrectMessage(t, got, want)\n\t})\n\n\tt.Run(\"in French\", func(t *testing.T) {\n\t\tgot := Hello(\"Lauren\", french)\n\t\twant := \"Bonjour, Lauren\"\n\t\tassertCorrectMessage(t, got, want)\n\t})\n\n}\n\nfunc assertCorrectMessage(t testing.TB, got, want string) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "hello-world/v7/hello.go",
    "content": "package main\n\nimport \"fmt\"\n\nconst spanish = \"Spanish\"\nconst french = \"French\"\nconst englishHelloPrefix = \"Hello, \"\nconst spanishHelloPrefix = \"Hola, \"\nconst frenchHelloPrefix = \"Bonjour, \"\n\n// Hello returns a personalised greeting in a given language.\nfunc Hello(name string, language string) string {\n\tif name == \"\" {\n\t\tname = \"World\"\n\t}\n\n\tprefix := englishHelloPrefix\n\n\tswitch language {\n\tcase spanish:\n\t\tprefix = spanishHelloPrefix\n\tcase french:\n\t\tprefix = frenchHelloPrefix\n\t}\n\n\treturn prefix + name\n}\n\nfunc main() {\n\tfmt.Println(Hello(\"world\", \"\"))\n}\n"
  },
  {
    "path": "hello-world/v7/hello_test.go",
    "content": "package main\n\nimport \"testing\"\n\nfunc TestHello(t *testing.T) {\n\tt.Run(\"saying hello to people\", func(t *testing.T) {\n\t\tgot := Hello(\"Chris\", \"\")\n\t\twant := \"Hello, Chris\"\n\t\tassertCorrectMessage(t, got, want)\n\t})\n\n\tt.Run(\"say hello world when an empty string is supplied\", func(t *testing.T) {\n\t\tgot := Hello(\"\", \"\")\n\t\twant := \"Hello, World\"\n\t\tassertCorrectMessage(t, got, want)\n\t})\n\n\tt.Run(\"say hello in Spanish\", func(t *testing.T) {\n\t\tgot := Hello(\"Elodie\", spanish)\n\t\twant := \"Hola, Elodie\"\n\t\tassertCorrectMessage(t, got, want)\n\t})\n\n\tt.Run(\"say hello in French\", func(t *testing.T) {\n\t\tgot := Hello(\"Lauren\", french)\n\t\twant := \"Bonjour, Lauren\"\n\t\tassertCorrectMessage(t, got, want)\n\t})\n}\n\nfunc assertCorrectMessage(t testing.TB, got, want string) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "hello-world/v8/hello.go",
    "content": "package main\n\nimport \"fmt\"\n\nconst spanish = \"Spanish\"\nconst french = \"French\"\nconst englishHelloPrefix = \"Hello, \"\nconst spanishHelloPrefix = \"Hola, \"\nconst frenchHelloPrefix = \"Bonjour, \"\n\n// Hello returns a personalised greeting in a given language.\nfunc Hello(name string, language string) string {\n\tif name == \"\" {\n\t\tname = \"World\"\n\t}\n\n\treturn greetingPrefix(language) + name\n}\n\nfunc greetingPrefix(language string) (prefix string) {\n\tswitch language {\n\tcase french:\n\t\tprefix = frenchHelloPrefix\n\tcase spanish:\n\t\tprefix = spanishHelloPrefix\n\tdefault:\n\t\tprefix = englishHelloPrefix\n\t}\n\treturn\n}\n\nfunc main() {\n\tfmt.Println(Hello(\"world\", \"\"))\n}\n"
  },
  {
    "path": "hello-world/v8/hello_test.go",
    "content": "package main\n\nimport \"testing\"\n\nfunc TestHello(t *testing.T) {\n\tt.Run(\"to a person\", func(t *testing.T) {\n\t\tgot := Hello(\"Chris\", \"\")\n\t\twant := \"Hello, Chris\"\n\t\tassertCorrectMessage(t, got, want)\n\t})\n\n\tt.Run(\"empty string\", func(t *testing.T) {\n\t\tgot := Hello(\"\", \"\")\n\t\twant := \"Hello, World\"\n\t\tassertCorrectMessage(t, got, want)\n\t})\n\n\tt.Run(\"in Spanish\", func(t *testing.T) {\n\t\tgot := Hello(\"Elodie\", spanish)\n\t\twant := \"Hola, Elodie\"\n\t\tassertCorrectMessage(t, got, want)\n\t})\n\n\tt.Run(\"in French\", func(t *testing.T) {\n\t\tgot := Hello(\"Lauren\", french)\n\t\twant := \"Bonjour, Lauren\"\n\t\tassertCorrectMessage(t, got, want)\n\t})\n}\n\nfunc assertCorrectMessage(t testing.TB, got, want string) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "hello-world.md",
    "content": "# Hello, World\n\n**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/main/hello-world)**\n\nIt is traditional for your first program in a new language to be [Hello, World](https://en.m.wikipedia.org/wiki/%22Hello,_World!%22_program).\n\n- Create a folder wherever you like\n- Put a new file in it called `hello.go` and put the following code inside it\n\n```go\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(\"Hello, world\")\n}\n```\n\nTo run it, type `go run hello.go`.\n\n## How it works\n\nWhen you write a program in Go, you will have a `main` package defined with a `main` func inside it. Packages are ways of grouping up related Go code together.\n\nThe `func` keyword defines a function with a name and a body.\n\nWith `import \"fmt\"` we are importing a package which contains the `Println` function that we use to print.\n\n## How to test\n\nHow do you test this? It is good to separate your \"domain\" code from the outside world \\(side-effects\\). The `fmt.Println` is a side effect \\(printing to stdout\\), and the string we send in is our domain.\n\nSo let's separate these concerns so it's easier to test\n\n```go\npackage main\n\nimport \"fmt\"\n\nfunc Hello() string {\n\treturn \"Hello, world\"\n}\n\nfunc main() {\n\tfmt.Println(Hello())\n}\n```\n\nWe have created a new function with `func`, but this time, we've added another keyword, `string,` to the definition. This means this function returns a `string`.\n\nNow create a new file called `hello_test.go` where we are going to write a test for our `Hello` function\n\n```go\npackage main\n\nimport \"testing\"\n\nfunc TestHello(t *testing.T) {\n\tgot := Hello()\n\twant := \"Hello, world\"\n\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n```\n\n## Go modules?\n\nThe next step is to run the tests. Enter `go test` in your terminal. If the tests pass, then you are probably using an earlier version of Go. However, if you are using Go 1.16 or later, the tests will likely not run. Instead, you will see an error message like this in the terminal:\n\n```shell\n$ go test\ngo: cannot find main module; see 'go help modules'\n```\n\nWhat's the problem? In a word, [modules](https://blog.golang.org/go116-module-changes). Luckily, the problem is easy to fix. Enter `go mod init example.com/hello` in your terminal. That will create a new file with the following contents:\n\n```\nmodule example.com/hello\n\ngo 1.16\n```\n\nThis file tells the `go` tools essential information about your code. If you planned to distribute your application, you would include where the code was available for download as well as information about dependencies.  The name of the module, example\\.com\\/hello, usually refers to a URL where the module can be found and downloaded. For compatibility with tools we'll start using soon, make sure your module's name has a dot somewhere in it, like the dot in .com of example\\.com/hello. For now, your module file is minimal, and you can leave it that way. To read more about modules, [you can check out the reference in the Golang documentation](https://golang.org/doc/modules/gomod-ref). We can get back to testing and learning Go now since the tests should run, even on Go 1.16.\n\nIn future chapters, you will need to run `go mod init SOMENAME` in each new folder before running commands like `go test` or `go build`.\n\n## Back to Testing\n\nRun `go test` in your terminal. It should've passed! Just to check, try deliberately breaking the test by changing the `want` string.\n\nNotice how you have not had to pick between multiple testing frameworks and then figure out how to install them. Everything you need is built into the language, and the syntax is the same as the rest of the code you will write.\n\n### Writing tests\n\nWriting a test is just like writing a function, with a few rules\n\n* It needs to be in a file with a name like `xxx_test.go`\n* The test function must start with the word `Test`\n* The test function takes one argument only `t *testing.T`\n* To use the `*testing.T` type, you need to `import \"testing\"`, like we did with `fmt` in the other file\n\nFor now, it's enough to know that your `t` of type `*testing.T` is your \"hook\" into the testing framework so you can do things like `t.Fail()` when you want to fail.\n\nWe've covered some new topics:\n\n#### `if`\nIf statements in Go are very much like other programming languages.\n\n#### Declaring variables\n\nWe're declaring some variables with the syntax `varName := value`, which lets us reuse some values in our test for readability.\n\n#### `t.Errorf`\n\nWe are calling the `Errorf` _method_ on our `t`, which will print out a message and fail the test. The `f` stands for format, which allows us to build a string with values inserted into the placeholder values `%q`. When you make the test fail, it should be clear how it works.\n\nYou can read more about the placeholder strings in the [fmt documentation](https://pkg.go.dev/fmt#hdr-Printing). For tests, `%q` is very useful as it wraps your values in double quotes.\n\nWe will later explore the difference between methods and functions.\n\n### Go's documentation\n\nAnother quality-of-life feature of Go is the documentation.  We just saw the documentation for the fmt package at the official package viewing website, and Go also provides ways for quickly getting at the documentation offline.\n\nGo has a built-in tool, doc, which lets you examine any package installed on your system, or the module you're currently working on. To view that same documentation for the Printing verbs:\n\n```\n$ go doc fmt\npackage fmt // import \"fmt\"\n\nPackage fmt implements formatted I/O with functions analogous to C's printf and\nscanf. The format 'verbs' are derived from C's but are simpler.\n\n# Printing\n\nThe verbs:\n\nGeneral:\n\n    %v\tthe value in a default format\n    \twhen printing structs, the plus flag (%+v) adds field names\n    %#v\ta Go-syntax representation of the value\n    %T\ta Go-syntax representation of the type of the value\n    %%\ta literal percent sign; consumes no value\n...\n```\n\nGo's second tool for viewing documentation is the pkgsite command, which powers Go's official package viewing website.  You can install pkgsite with `go install golang.org/x/pkgsite/cmd/pkgsite@latest`, then run it with `pkgsite -open .`.  Go's install command will download the source files from that repository and build them into an executable binary.  For a default installation of Go, that executable will be in `$HOME/go/bin` for Linux and macOS, and `%USERPROFILE%\\go\\bin` for Windows.  If you have not already added those paths to your $PATH var, you might want to do so to make running go-installed commands easier.\n\nThe vast majority of the standard library has excellent documentation with examples. Navigating to [http://localhost:8080/testing](http://localhost:8080/testing) would be worthwhile to see what's available to you.\n\n\n### Hello, YOU\n\nNow that we have a test, we can iterate on our software safely.\n\nIn the last example, we wrote the test _after_ the code had been written so that you could get an example of how to write a test and declare a function. From this point on, we will be _writing tests first_.\n\nOur next requirement is to let us specify the recipient of the greeting.\n\nLet's start by capturing these requirements in a test. This is basic test-driven development and allows us to make sure our test is _actually_ testing what we want. When you retrospectively write tests, there is the risk that your test may continue to pass even if the code doesn't work as intended.\n\n```go\npackage main\n\nimport \"testing\"\n\nfunc TestHello(t *testing.T) {\n\tgot := Hello(\"Chris\")\n\twant := \"Hello, Chris\"\n\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n```\n\nNow run `go test`, you should have a compilation error\n\n```text\n./hello_test.go:6:18: too many arguments in call to Hello\n    have (string)\n    want ()\n```\n\nWhen using a statically typed language like Go it is important to _listen to the compiler_. The compiler understands how your code should snap together and work so you don't have to.\n\nIn this case the compiler is telling you what you need to do to continue. We have to change our function `Hello` to accept an argument.\n\nEdit the `Hello` function to accept an argument of type string\n\n```go\nfunc Hello(name string) string {\n\treturn \"Hello, world\"\n}\n```\n\nIf you try and run your tests again your `hello.go` will fail to compile because you're not passing an argument. Send in \"world\" to make it compile.\n\n```go\nfunc main() {\n\tfmt.Println(Hello(\"world\"))\n}\n```\n\nNow when you run your tests, you should see something like\n\n```text\nhello_test.go:10: got 'Hello, world' want 'Hello, Chris''\n```\n\nWe finally have a compiling program but it is not meeting our requirements according to the test.\n\nLet's make the test pass by using the name argument and concatenate it with `Hello,`\n\n```go\nfunc Hello(name string) string {\n\treturn \"Hello, \" + name\n}\n```\n\nWhen you run the tests, they should now pass. Normally, as part of the TDD cycle, we should now _refactor_.\n\n### A note on source control\n\nAt this point, if you are using source control \\(which you should!\\) I would\n`commit` the code as it is. We have working software backed by a test.\n\nI _wouldn't_ push to main though, because I plan to refactor next. It is nice\nto commit at this point in case you somehow get into a mess with refactoring - you can always go back to the working version.\n\nThere's not a lot to refactor here, but we can introduce another language feature, _constants_.\n\n### Constants\n\nConstants are defined like so\n\n```go\nconst englishHelloPrefix = \"Hello, \"\n```\n\nWe can now refactor our code\n\n```go\nconst englishHelloPrefix = \"Hello, \"\n\nfunc Hello(name string) string {\n\treturn englishHelloPrefix + name\n}\n```\n\nAfter refactoring, re-run your tests to make sure you haven't broken anything.\n\nIt's worth thinking about creating constants to capture the meaning of values and sometimes to aid performance.\n\n## Hello, world... again\n\nThe next requirement is when our function is called with an empty string it defaults to printing \"Hello, World\", rather than \"Hello, \".\n\nStart by writing a new failing test\n\n```go\nfunc TestHello(t *testing.T) {\n\tt.Run(\"saying hello to people\", func(t *testing.T) {\n\t\tgot := Hello(\"Chris\")\n\t\twant := \"Hello, Chris\"\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %q want %q\", got, want)\n\t\t}\n\t})\n\tt.Run(\"say 'Hello, World' when an empty string is supplied\", func(t *testing.T) {\n\t\tgot := Hello(\"\")\n\t\twant := \"Hello, World\"\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %q want %q\", got, want)\n\t\t}\n\t})\n}\n```\n\nHere, we are introducing another tool in our testing arsenal: subtests. Sometimes, it is useful to group tests around a \"thing\" and then have subtests describing different scenarios.\n\nA benefit of this approach is you can set up shared code that can be used in the other tests.\n\nWhile we have a failing test, let's fix the code, using an `if`.\n\n```go\nconst englishHelloPrefix = \"Hello, \"\n\nfunc Hello(name string) string {\n\tif name == \"\" {\n\t\tname = \"World\"\n\t}\n\treturn englishHelloPrefix + name\n}\n```\n\nIf we run our tests we should see it satisfies the new requirement and we haven't accidentally broken the other functionality.\n\nIt is important that your tests _are clear specifications_ of what the code needs to do. But there is repeated code when we check if the message is what we expect.\n\nRefactoring is not _just_ for the production code!\n\nNow that the tests are passing, we can and should refactor our tests.\n\n```go\nfunc TestHello(t *testing.T) {\n\tt.Run(\"saying hello to people\", func(t *testing.T) {\n\t\tgot := Hello(\"Chris\")\n\t\twant := \"Hello, Chris\"\n\t\tassertCorrectMessage(t, got, want)\n\t})\n\n\tt.Run(\"empty string defaults to 'world'\", func(t *testing.T) {\n\t\tgot := Hello(\"\")\n\t\twant := \"Hello, World\"\n\t\tassertCorrectMessage(t, got, want)\n\t})\n\n}\n\nfunc assertCorrectMessage(t testing.TB, got, want string) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n```\n\nWhat have we done here?\n\nWe've refactored our assertion into a new function. This reduces duplication and improves the readability of our tests. We need to pass in `t *testing.T` so that we can tell the test code to fail when we need to.\n\nFor helper functions, it's a good idea to accept a `testing.TB` which is an interface that `*testing.T` and `*testing.B` both satisfy, so you can call helper functions from a test, or a benchmark (don't worry if words like \"interface\" mean nothing to you right now, it will be covered later).\n\n`t.Helper()` is needed to tell the test suite that this method is a helper. By doing this, when it fails, the line number reported will be in our _function call_ rather than inside our test helper. This will help other developers track down problems more easily. If you still don't understand, comment it out, make a test fail and observe the test output. Comments in Go are a great way to add additional information to your code, or in this case, a quick way to tell the compiler to ignore a line. You can comment out the `t.Helper()` code by adding two forward slashes `//` at the beginning of the line. You should see that line turn grey or change to another color than the rest of your code to indicate it's now commented out.\n\nWhen you have more than one argument of the same type \\(in our case two strings\\) rather than having `(got string, want string)` you can shorten it to `(got, want string)`.\n\n### Back to source control\n\nNow that we are happy with the code, I would amend the previous commit so that we only check in the lovely version of our code with its test.\n\n### Discipline\n\nLet's go over the cycle again\n\n* Write a test\n* Make the compiler pass\n* Run the test, see that it fails and check the error message is meaningful\n* Write enough code to make the test pass\n* Refactor\n\nOn the face of it this may seem tedious but sticking to the feedback loop is important.\n\nNot only does it ensure that you have _relevant tests_, it helps ensure _you design good software_ by refactoring with the safety of tests.\n\nSeeing the test fail is an important check because it also lets you see what the error message looks like. As a developer it can be very hard to work with a codebase when failing tests do not give a clear idea as to what the problem is.\n\nBy ensuring your tests are _fast_ and setting up your tools so that running tests is simple you can get in to a state of flow when writing your code.\n\nBy not writing tests, you are committing to manually checking your code by running your software, which breaks your state of flow. You won't be saving yourself any time, especially in the long run.\n\n## Keep going! More requirements\n\nGoodness me, we have more requirements. We now need to support a second parameter, specifying the language of the greeting. If a language is passed in that we do not recognise, just default to English.\n\nWe should be confident that we can easily use TDD to flesh out this functionality!\n\nWrite a test for a user passing in Spanish. Add it to the existing suite.\n\n```go\n\tt.Run(\"in Spanish\", func(t *testing.T) {\n\t\tgot := Hello(\"Elodie\", \"Spanish\")\n\t\twant := \"Hola, Elodie\"\n\t\tassertCorrectMessage(t, got, want)\n\t})\n```\n\nRemember not to cheat! _Test first_. When you try to run the test, the compiler _should_ complain because you are calling `Hello` with two arguments rather than one.\n\n```text\n./hello_test.go:27:19: too many arguments in call to Hello\n    have (string, string)\n    want (string)\n```\n\nFix the compilation problems by adding another string argument to `Hello`\n\n```go\nfunc Hello(name string, language string) string {\n\tif name == \"\" {\n\t\tname = \"World\"\n\t}\n\treturn englishHelloPrefix + name\n}\n```\n\nWhen you try and run the test again it will complain about not passing through enough arguments to `Hello` in your other tests and in `hello.go`\n\n```text\n./hello.go:15:19: not enough arguments in call to Hello\n    have (string)\n    want (string, string)\n```\n\nFix them by passing through empty strings. Now all your tests should compile _and_ pass, apart from our new scenario\n\n```text\nhello_test.go:29: got 'Hello, Elodie' want 'Hola, Elodie'\n```\n\nWe can use `if` here to check the language is equal to \"Spanish\" and if so change the message\n\n```go\nfunc Hello(name string, language string) string {\n\tif name == \"\" {\n\t\tname = \"World\"\n\t}\n\n\tif language == \"Spanish\" {\n\t\treturn \"Hola, \" + name\n\t}\n\treturn englishHelloPrefix + name\n}\n```\n\nThe tests should now pass.\n\nNow it is time to _refactor_. You should see some problems in the code, \"magic\" strings, some of which are repeated. Try and refactor it yourself, with every change make sure you re-run the tests to make sure your refactoring isn't breaking anything.\n\n```go\n\tconst spanish = \"Spanish\"\n\tconst englishHelloPrefix = \"Hello, \"\n\tconst spanishHelloPrefix = \"Hola, \"\n\n\tfunc Hello(name string, language string) string {\n\t\tif name == \"\" {\n\t\t\tname = \"World\"\n\t\t}\n\n\t\tif language == spanish {\n\t\t\treturn spanishHelloPrefix + name\n\t\t}\n\t\treturn englishHelloPrefix + name\n\t}\n```\n\n### French\n\n* Write a test asserting that if you pass in `\"French\"` you get `\"Bonjour, \"`\n* See it fail, check the error message is easy to read\n* Do the smallest reasonable change in the code\n\nYou may have written something that looks roughly like this\n\n```go\nfunc Hello(name string, language string) string {\n\tif name == \"\" {\n\t\tname = \"World\"\n\t}\n\n\tif language == spanish {\n\t\treturn spanishHelloPrefix + name\n\t}\n\tif language == french {\n\t\treturn frenchHelloPrefix + name\n\t}\n\treturn englishHelloPrefix + name\n}\n```\n\n## `switch`\n\nWhen you have lots of `if` statements checking a particular value it is common to use a `switch` statement instead. We can use `switch` to refactor the code to make it easier to read and more extensible if we wish to add more language support later\n\n```go\nfunc Hello(name string, language string) string {\n\tif name == \"\" {\n\t\tname = \"World\"\n\t}\n\n\tprefix := englishHelloPrefix\n\n\tswitch language {\n\tcase spanish:\n\t\tprefix = spanishHelloPrefix\n\tcase french:\n\t\tprefix = frenchHelloPrefix\n\t}\n\n\treturn prefix + name\n}\n```\n\nWrite a test to now include a greeting in the language of your choice and you should see how simple it is to extend our _amazing_ function.\n\n### one...last...refactor?\n\nYou could argue that maybe our function is getting a little big. The simplest refactor for this would be to extract out some functionality into another function.\n\n```go\n\nconst (\n\tspanish = \"Spanish\"\n\tfrench  = \"French\"\n\n\tenglishHelloPrefix = \"Hello, \"\n\tspanishHelloPrefix = \"Hola, \"\n\tfrenchHelloPrefix  = \"Bonjour, \"\n)\n\nfunc Hello(name string, language string) string {\n\tif name == \"\" {\n\t\tname = \"World\"\n\t}\n\n\treturn greetingPrefix(language) + name\n}\n\nfunc greetingPrefix(language string) (prefix string) {\n\tswitch language {\n\tcase french:\n\t\tprefix = frenchHelloPrefix\n\tcase spanish:\n\t\tprefix = spanishHelloPrefix\n\tdefault:\n\t\tprefix = englishHelloPrefix\n\t}\n\treturn\n}\n```\n\nA few new concepts:\n\n* In our function signature we have made a _named return value_ `(prefix string)`.\n* This will create a variable called `prefix` in your function.\n  * It will be assigned the \"zero\" value. This depends on the type, for example `int`s are 0 and for `string`s it is `\"\"`.\n    * You can return whatever it's set to by just calling `return` rather than `return prefix`.\n  * This will display in the Go Doc for your function so it can make the intent of your code clearer.\n* `default` in the switch case will be branched to if none of the other `case` statements match.\n* The function name starts with a lowercase letter. In Go, public functions start with a capital letter, and private ones start with a lowercase letter. We don't want the internals of our algorithm exposed to the world, so we made this function private.\n* Also, we can group constants in a block instead of declaring them on their own line. For readability, it's a good idea to use a line between sets of related constants.\n\n## Wrapping up\n\nWho knew you could get so much out of `Hello, world`?\n\nBy now you should have some understanding of:\n\n### Some of Go's syntax around\n\n* Writing tests\n* Declaring functions, with arguments and return types\n* `if`, `const` and `switch`\n* Declaring variables and constants\n\n### The TDD process and _why_ the steps are important\n\n* _Write a failing test and see it fail_ so we know we have written a _relevant_ test for our requirements and seen that it produces an _easy to understand description of the failure_\n* Writing the smallest amount of code to make it pass so we know we have working software\n* _Then_ refactor, backed with the safety of our tests to ensure we have well-crafted code that is easy to work with\n\nIn our case, we've gone from `Hello()` to `Hello(\"name\")` and then to `Hello(\"name\", \"French\")` in small, easy-to-understand steps.\n\nOf course, this is trivial compared to \"real-world\" software, but the principles still stand. TDD is a skill that needs practice to develop, but by breaking problems down into smaller components that you can test, you will have a much easier time writing software.\n"
  },
  {
    "path": "html-templates.md",
    "content": "# HTML Templates\n\n**[You can find all the code here](https://github.com/quii/learn-go-with-tests/tree/main/blogrenderer)**\n\nWe live in a world where everyone wants to build web applications with the latest flavour of the month frontend framework built upon gigabytes of transpiled JavaScript, working with a Byzantine build system; [but maybe that's not always necessary](https://quii.dev/The_Web_I_Want).  \n\nI'd say most Go developers value a simple, stable & fast toolchain but the frontend world frequently fails to deliver on this front.\n\nMany websites do not need to be an [SPA](https://en.wikipedia.org/wiki/Single-page_application). **HTML and CSS are fantastic ways of delivering content** and you can use Go to make a website to deliver HTML. \n\nIf you wish to still have some dynamic elements, you can still sprinkle in some client side JavaScript, or you may even want to try experimenting with [Hotwire](https://hotwired.dev) which allows you to deliver a dynamic experience with a server-side approach. \n\nYou can generate your HTML in Go with elaborate usage of [`fmt.Fprintf`](https://pkg.go.dev/fmt#Fprintf), but in this chapter you'll learn that Go's standard library has some tools to generate HTML in a simpler and more maintainable way. You'll also learn more effective ways of testing this kind of code that you may not have run in to before.\n\n## What we're going to build\n\nIn the [Reading Files](/reading-files.md) chapter we wrote some code that would take an [`fs.FS`](https://pkg.go.dev/io/fs)  (a file-system), and return a slice of `Post` for each markdown file it encountered.\n\n```go\nposts, err := blogposts.NewPostsFromFS(os.DirFS(\"posts\"))\n```\n\nHere is how we defined `Post`\n\n```go\ntype Post struct {\n\tTitle, Description, Body string\n\tTags                     []string\n}\n```\n\nHere's an example of one of the markdown files that can be parsed.\n\n```markdown\nTitle: Welcome to my blog\nDescription: Introduction to my blog\nTags: cooking, family, live-laugh-love\n---\n# First recipe!\nWelcome to my **amazing recipe blog**. I am going to write about my family recipes, and make sure I write a long, irrelevant and boring story about my family before you get to the actual instructions.\n```\n\nIf we continue our journey of writing blog software, we'd take this data and generate HTML from it for our web server to return in response to HTTP requests.\n\nFor our blog, we want to generate two kinds of page:\n\n1. **View post**. Renders a specific post. The `Body` field in `Post` is a string containing markdown so that should be converted to HTML. \n2. **Index**. Lists all of the posts, with hyperlinks to view the specific post.\n\nWe'll also want a consistent look and feel across our site, so for each page we'll have the usual HTML furniture like `<html>` and a `<head>` containing links to CSS stylesheets and whatever else we may want.\n\nWhen you're building blog software you have a few options in terms of approach of how you build and send HTML to the user's browser. \n\nWe'll design our code so it accepts an `io.Writer`. This means the caller of our code has the flexibility to:\n\n- Write them to an [os.File](https://pkg.go.dev/os#File) , so they can be statically served\n- Write out the HTML directly to a [`http.ResponseWriter`](https://pkg.go.dev/net/http#ResponseWriter)\n- Or just write them to anything really! So long as it implements `io.Writer` the user can generate some HTML from a `Post`\n\n## Write the test first\n\nAs always, it's important to think about requirements before diving in too fast. How can we take this large-ish set of requirements and break it down in to a small, achievable step that we can focus on?\n\nIn my view, actually viewing content is higher priority than an index page. We could launch this product and share direct links to our wonderful content. An index page which can't link to the actual content isn't useful.\n\nStill, rendering a post as described earlier still feels big. All the HTML furniture, converting the body markdown into HTML, listing tags, e.t.c. \n\nAt this stage I'm not overly concerned with the specific markup, and an easy first step would be just to check we can render the post's title as an `<h1>`. This *feels* like the smallest first step that can move us forward a bit.\n\n```go\npackage blogrenderer_test\n\nimport (\n\t\"bytes\"\n\t\"github.com/quii/learn-go-with-tests/blogrenderer\"\n\t\"testing\"\n)\n\nfunc TestRender(t *testing.T) {\n\tvar (\n\t\taPost = blogrenderer.Post{\n\t\t\tTitle:       \"hello world\",\n\t\t\tBody:        \"This is a post\",\n\t\t\tDescription: \"This is a description\",\n\t\t\tTags:        []string{\"go\", \"tdd\"},\n\t\t}\n\t)\n\n\tt.Run(\"it converts a single post into HTML\", func(t *testing.T) {\n\t\tbuf := bytes.Buffer{}\n\t\terr := blogrenderer.Render(&buf, aPost)\n\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tgot := buf.String()\n\t\twant := `<h1>hello world</h1>`\n\t\tif got != want {\n\t\t\tt.Errorf(\"got '%s' want '%s'\", got, want)\n\t\t}\n\t})\n}\n```\n\nOur decision to accept an `io.Writer` also makes testing simple, in this case we're writing to a [`bytes.Buffer`](https://pkg.go.dev/bytes#Buffer) which we can then later inspect the contents.\n\n## Try to run the test\n\nIf you've read the previous chapters of this book you should be well-practiced at this now. You won't be able to run the test because we don't have the package defined or the `Render` function. Try and follow the compiler messages yourself and get to a state where you can run the test and see that it fails with a clear message. \n\nIt's really important that you exercise your tests failing, you'll thank yourself when you accidentally make a test fail 6 months later that you put in the effort *now* to check it fails with a clear message.\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\nThis is the minimal code to get the test running\n\n```go\npackage blogrenderer\n\n// if you're continuing from the read files chapter, you shouldn't redefine this\ntype Post struct {\n\tTitle, Description, Body string\n\tTags                     []string\n}\n\nfunc Render(w io.Writer, p Post) error {\n\treturn nil\n}\n```\n\nThe test should complain that an empty string doesn't equal what we want.\n\n## Write enough code to make it pass\n\n```go\nfunc Render(w io.Writer, p Post) error {\n\t_, err := fmt.Fprintf(w, \"<h1>%s</h1>\", p.Title)\n\treturn err\n}\n```\n\nRemember, software development is primarily a learning activity. In order to discover and learn as we work, we need to work in a way that gives us frequent, high-quality feedback loops, and the easiest way to do that is work in small steps. \n\nSo we're not worrying about using any templating libraries right now. You can make HTML just with \"normal\" string templating just fine, and by skipping the template part we can validate a small bit of useful behaviour and we've done a small bit of design work for our package's API.\n\n## Refactor\n\nNot much to refactor yet, so let's move to the next iteration\n\n## Write the test first\n\nNow we have a very basic version working, we can now iterate on the test to expand on the functionality. In this case, rendering more information from the `Post`.\n\n```go\n\tt.Run(\"it converts a single post into HTML\", func(t *testing.T) {\n\t\tbuf := bytes.Buffer{}\n\t\terr := blogrenderer.Render(&buf, aPost)\n\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tgot := buf.String()\n\t\twant := `<h1>hello world</h1>\n<p>This is a description</p>\nTags: <ul><li>go</li><li>tdd</li></ul>`\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"got '%s' want '%s'\", got, want)\n\t\t}\n\t})\n```\n\nNotice that writing this, *feels* awkward. Seeing all that markup in the test feels bad, and we haven't even put the body in, or the actual HTML we'd want with all of the `<head>` content and whatever page furniture we need.\n\nNonetheless, let's put up with the pain *for now*.\n\n## Try to run the test\n\nIt should fail, complaining it doesn't have the string we expect, as we're not rendering the description and tags. \n\n## Write enough code to make it pass\n\nTry and do this yourself rather than copying the code. What you should find is that making this test pass _is a bit annoying_! When I tried, my first attempt got this error\n\n```\n=== RUN   TestRender\n=== RUN   TestRender/it_converts_a_single_post_into_HTML\n    renderer_test.go:32: got '<h1>hello world</h1><p>This is a description</p><ul><li>go</li><li>tdd</li></ul>' want '<h1>hello world</h1>\n        <p>This is a description</p>\n        Tags: <ul><li>go</li><li></li></ul>'\n```\n\nNew lines! Who cares? Well, our test does, because it's matching on an exact string value. Should it? I removed the newlines for now just to get the test passing.\n\n```go\nfunc Render(w io.Writer, p Post) error {\n\t_, err := fmt.Fprintf(w, \"<h1>%s</h1><p>%s</p>\", p.Title, p.Description)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t_, err = fmt.Fprint(w, \"Tags: <ul>\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, tag := range p.Tags {\n\t\t_, err = fmt.Fprintf(w, \"<li>%s</li>\", tag)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\t_, err = fmt.Fprint(w, \"</ul>\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n```\n\n**Yikes**. Not the nicest code i've written, and we're still only at a very early implementation of our markup. We'll need so much more content and things on our page, we're quickly seeing that this approach is not appropriate. \n\nCrucially though, we have a passing test; we have working software.\n\n## Refactor\n\nWith the safety-net of a passing test for working code, we can now think about changing our implementation approach at the refactoring stage. \n\n### Introducing templates\n\nGo has two templating packages [text/template](https://pkg.go.dev/text/template) and [html/template](https://pkg.go.dev/html/template) and they share the same interface.  What they both do is allow you to combine a template and some data to produce a string. \n\nWhat's the difference with the HTML version?\n\n> Package template (html/template) implements data-driven templates for generating HTML output safe against code injection. It provides the same interface as package text/template and should be used instead of text/template whenever the output is HTML.\n\nThe templating language is very similar to [Mustache](https://mustache.github.io) and allows you to dynamically generate content in a very clean fashion with a nice separation of concerns. Compared to other templating languages you may have used, it is very constrained or \"logic-less\" as Mustache likes to say. This is an important, **and deliberate** design decision.\n\nWhilst we're focusing on generating HTML here, if your project is doing complex string concatenations and incantations, you might want to reach for `text/template` to clean up your code.\n\n### Back to the code\n\nHere is a template for our blog: \n\n`<h1>{{.Title}}</h1><p>{{.Description}}</p>Tags: <ul>{{range .Tags}}<li>{{.}}</li>{{end}}</ul>`\n\nWhere do we define this string? Well, we have a few options, but to keep the steps small, let's just start with a plain old string\n\n```go\npackage blogrenderer\n\nimport (\n\t\"html/template\"\n\t\"io\"\n)\n\nconst (\n\tpostTemplate = `<h1>{{.Title}}</h1><p>{{.Description}}</p>Tags: <ul>{{range .Tags}}<li>{{.}}</li>{{end}}</ul>`\n)\n\nfunc Render(w io.Writer, p Post) error {\n\ttempl, err := template.New(\"blog\").Parse(postTemplate)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif err := templ.Execute(w, p); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n```\n\nWe create a new template with a name, and then parse our template string. We can then use the `Execute` method on it, passing in our data, in this case the `Post`. \n\nThe template will substitute things like `{{.Description}}` with the content of `p.Description`. Templates also give you some programming primitives like `range` to loop over values, and `if`. You can find more details in the [text/template documentation](https://pkg.go.dev/text/template).\n\n*This should be a pure refactor.* We shouldn't need to change our tests and they should continue to pass. Importantly, our code is easier to read and has far less annoying error handling to contend with. \n\nFrequently people complain about the verbosity of error handling in Go, but you might find you can find better ways to write your code so it's less error-prone in the first place, like here.\n\n### More refactoring\n\nUsing the `html/template` has definitely been an improvement, but having it as a string constant in our code isn't great:\n\n- It's still quite difficult to read.\n- It's not IDE/editor friendly. No syntax highlighting, ability to reformat, refactor, e.t.c.\n- It looks like HTML, but you can't really work with it like you could a \"normal\" HTML file\n\nWhat we'd like to do is have our templates live in separate files so we can better organise them, and work with them as if they're HTML files.\n\nCreate a folder called \"templates\" and inside it make a file called `blog.gohtml`, paste our template into the file.\n\nNow change our code to embed the file systems using the [embedding functionality included in go 1.16](https://pkg.go.dev/embed).\n\n```go\npackage blogrenderer\n\nimport (\n\t\"embed\"\n\t\"html/template\"\n\t\"io\"\n)\n\nvar (\n\t//go:embed \"templates/*\"\n\tpostTemplates embed.FS\n)\n\nfunc Render(w io.Writer, p Post) error {\n\ttempl, err := template.ParseFS(postTemplates, \"templates/*.gohtml\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif err := templ.Execute(w, p); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n```\n\nBy embedding a \"file system\" into our code, we can load multiple templates and combine them freely. This will become useful when we want to share rendering logic across different templates, such as a header for the top of the HTML page and a footer.\n\n### Embed?\n\nEmbed was lightly touched on in [reading files](reading-files.md). The [documentation from the standard library explains](https://pkg.go.dev/embed)\n\n> Package embed provides access to files embedded in the running Go program.\n>\n> Go source files that import \"embed\" can use the //go:embed directive to initialize a variable of type string, []byte, or FS with the contents of files read from the package directory or subdirectories at compile time.\n\nWhy would we want to use this? Well the alternative is that we _can_ load our templates from a \"normal\" file system. However this means we'd have to make sure that the templates are in the correct file path wherever we want to use this software. In your job you may have various environments like development, staging and live. For this to work, you'd need to make sure your templates are copied to the correct place. \n\nWith embed, the files are included in your Go program when you build it. This means once you've built your program (which you should only do once), the files are always available to you. \n\nWhat's handy is you can not only embed individual files, but also file systems; and that filesystem implements [io/fs](https://pkg.go.dev/io/fs) which means your code doesn't need to care what kind of file system it is working with.\n\nIf you wish to use different templates depending on configuration though, you may wish to stick to loading templates from disk in the more conventional way.\n\n## Next: Make the template \"nice\"\n\nWe don't really want our template to be defined as a one line string. We want to be able to space it out to make it easier to read and work with, something like this:\n\n```handlebars\n<h1>{{.Title}}</h1>\n\n<p>{{.Description}}</p>\n\nTags: <ul>{{range .Tags}}<li>{{.}}</li>{{end}}</ul>\n```\n\nBut if we do this, our test fails. This is because our test is expecting a very specific string to be returned. \n\nBut really, we don't actually care about whitespace. Maintaining this test will become a nightmare if we have to keep painstakingly updating the assertion string every time we make minor changes to the markup. As the template grows, these kind of edits become harder to manage and the costs of work will spiral out of control.\n\n## Introducing Approval Tests\n\n[Go Approval Tests](https://github.com/approvals/go-approval-tests)\n\n> ApprovalTests allows for easy testing of larger objects, strings and anything else that can be saved to a file (images, sounds, CSV, etc...)\n\nThe idea is similar to \"golden\" files, or snapshot testing. Rather than awkwardly maintaining strings within a test file, the approval tool can compare the output for you with an \"approved\" file you created. You then simply copy over the new version if you approve it. Re-run the test and you're back to green.\n\nAdd a dependency to `\"github.com/approvals/go-approval-tests\"` to your project and edit the test to the following\n\n```go\nfunc TestRender(t *testing.T) {\n\tvar (\n\t\taPost = blogrenderer.Post{\n\t\t\tTitle:       \"hello world\",\n\t\t\tBody:        \"This is a post\",\n\t\t\tDescription: \"This is a description\",\n\t\t\tTags:        []string{\"go\", \"tdd\"},\n\t\t}\n\t)\n\n\tt.Run(\"it converts a single post into HTML\", func(t *testing.T) {\n\t\tbuf := bytes.Buffer{}\n\n\t\tif err := blogrenderer.Render(&buf, aPost); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tapprovals.VerifyString(t, buf.String())\n\t})\n}\n```\n\nThe first time you run it, it will fail because we haven't approved anything yet\n\n```\n=== RUN   TestRender\n=== RUN   TestRender/it_converts_a_single_post_into_HTML\n    renderer_test.go:29: Failed Approval: received does not match approved.\n```\n\nIt will have created two files, that look like the following\n\n- `renderer_test.TestRender.it_converts_a_single_post_into_HTML.received.txt`\n- `renderer_test.TestRender.it_converts_a_single_post_into_HTML.approved.txt`\n\nThe received file has the new, unapproved version of the output. Copy that into the empty approved file and re-run the test.\n\nBy copying the new version you have \"approved\" the change, and the test now passes.\n\nTo see the workflow in action, edit the template to how we discussed to make it easier to read (but semantically, it's the same).\n\n```handlebars\n<h1>{{.Title}}</h1>\n\n<p>{{.Description}}</p>\n\nTags: <ul>{{range .Tags}}<li>{{.}}</li>{{end}}</ul>\n```\n\nRe-run the test. A new \"received\" file will be generated because the output of our code differs to the approved version. Give them a look, and if you're happy with the changes, simply copy over the new version and re-run the test. Be sure to commit the approved files to source control.\n\nThis approach makes managing changes to big ugly things like HTML far simpler. You can use a diff tool to view and manage the differences, and it keeps your test code cleaner.\n\n![Use diff tool to manage changes](https://i.imgur.com/0MoNdva.png)\n\nThis is actually a fairly minor usage of approval tests, which are an extremely useful tool in your testing arsenal. [Emily Bache](https://twitter.com/emilybache) has an [interesting video where she uses approval tests to add an incredibly extensive set of tests to a complicated codebase that has zero tests](https://www.youtube.com/watch?v=zyM2Ep28ED8). \"Combinatorial Testing\" is definitely something worth looking into.\n\nNow that we have made this change, we still benefit from having our code well-tested, but the tests won't get in the way too much when we're tinkering with the markup.\n\n### Are we still doing TDD?\n\nAn interesting side-effect of this approach is it takes us away from TDD. Of course you _could_ manually edit the approved files to the state you want, run your tests and then fix the templates so they output what you defined. \n\nBut that's just silly! TDD is a method for doing work, specifically designing; but that doesn't mean we have to dogmatically use it for **everything**. \n\nThe important thing is, we've done the right thing and used TDD as a **design tool** to design our package's API. For templates changes our process can be:\n\n- Make a small change to the template\n- Run the approval test\n- Eyeball the output to check it looks correct\n- Make the approval\n- Repeat\n\nWe still shouldn't give up the value of working in small achievable steps. Try to find ways to make the changes small and keep re-running the tests to get real feedback on what you're doing.\n\nIf we start doing things like changing the code _around_ the templates, then of course that may warrant going back to our TDD method of work. \n\n## Expand the markup\n\nMost websites have richer HTML than we have right now. For starters, a `html` element, along with a `head`, perhaps some `nav` too. Usually there's an idea of a footer too.\n\nIf our site is going to have different pages, we'd want to define these things in one place to keep our site looking consistent. Go templates support us defining sections which we can then import in to other templates.\n\nEdit our existing template to import a top and bottom template\n\n```handlebars\n{{template \"top\" .}}\n<h1>{{.Title}}</h1>\n\n<p>{{.Description}}</p>\n\nTags: <ul>{{range .Tags}}<li>{{.}}</li>{{end}}</ul>\n{{template \"bottom\" .}}\n```\n\nThen create `top.gohtml` with the following\n\n```handlebars\n{{define \"top\"}}\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <title>My amazing blog!</title>\n    <meta charset=\"UTF-8\"/>\n    <meta name=\"description\" content=\"Wow, like and subscribe, it really helps the channel guys\" lang=\"en\"/>\n</head>\n<body>\n<nav role=\"navigation\">\n    <div>\n        <h1>Budding Gopher's blog</h1>\n        <ul>\n            <li><a href=\"/\">home</a></li>\n            <li><a href=\"about\">about</a></li>\n            <li><a href=\"archive\">archive</a></li>\n        </ul>\n    </div>\n</nav>\n<main>\n{{end}}\n```\n\nAnd `bottom.gohtml`\n\n```handlebars\n{{define \"bottom\"}}\n</main>\n<footer>\n    <ul>\n        <li><a href=\"https://twitter.com/quii\">Twitter</a></li>\n        <li><a href=\"https://github.com/quii\">GitHub</a></li>\n    </ul>\n</footer>\n</body>\n</html>\n{{end}}\n```\n\n(Obviously, feel free to put whatever markup you like!)\n\nWe now need to specify a specific template to run. In the blog renderer, change the `Execute` command to `ExecuteTemplate`\n\n```go\nif err := templ.ExecuteTemplate(w, \"blog.gohtml\", p); err != nil {\n\treturn err\n}\n```\n\nRe-run your test. A new \"received\" file should be made and the test will fail. Check it over and if you're happy, approve it by copying it over the old version. Re-run the test again and it should pass.\n\n## An excuse to mess around with Benchmarking\n\nBefore pressing on, let's consider what our code does.\n\n```go\nfunc Render(w io.Writer, p Post) error {\n\ttempl, err := template.ParseFS(postTemplates, \"templates/*.gohtml\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif err := templ.ExecuteTemplate(w, \"blog.gohtml\", p); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n```\n\n- Parse the templates\n- Use the template to render a post to an `io.Writer`\n\nWhilst the performance impact of re-parsing the templates for each post in most cases will be fairly negligible, the effort to *not* do this is also pretty negligible and should tidy the code up a bit too.\n\nTo see the impact of not doing this parsing over and over, we can use the benchmarking tool to see how fast our function is.\n\n```go\nfunc BenchmarkRender(b *testing.B) {\n\tvar (\n\t\taPost = blogrenderer.Post{\n\t\t\tTitle:       \"hello world\",\n\t\t\tBody:        \"This is a post\",\n\t\t\tDescription: \"This is a description\",\n\t\t\tTags:        []string{\"go\", \"tdd\"},\n\t\t}\n\t)\n\n\tfor b.Loop() {\n\t\tblogrenderer.Render(io.Discard, aPost)\n\t}\n}\n```\n\nOn my computer, here are the results\n\n```\nBenchmarkRender-8 22124 53812 ns/op\n```\n\nTo stop us having to re-parse the templates over and over, we'll create a type that'll hold the parsed template, and that'll have a method to do the rendering\n\n```go\ntype PostRenderer struct {\n\ttempl *template.Template\n}\n\nfunc NewPostRenderer() (*PostRenderer, error) {\n\ttempl, err := template.ParseFS(postTemplates, \"templates/*.gohtml\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &PostRenderer{templ: templ}, nil\n}\n\nfunc (r *PostRenderer) Render(w io.Writer, p Post) error {\n\n\tif err := r.templ.ExecuteTemplate(w, \"blog.gohtml\", p); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n```\n\nThis does change the interface of our code, so we'll need to update our test\n\n```go\nfunc TestRender(t *testing.T) {\n\tvar (\n\t\taPost = blogrenderer.Post{\n\t\t\tTitle:       \"hello world\",\n\t\t\tBody:        \"This is a post\",\n\t\t\tDescription: \"This is a description\",\n\t\t\tTags:        []string{\"go\", \"tdd\"},\n\t\t}\n\t)\n\n\tpostRenderer, err := blogrenderer.NewPostRenderer()\n\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tt.Run(\"it converts a single post into HTML\", func(t *testing.T) {\n\t\tbuf := bytes.Buffer{}\n\n\t\tif err := postRenderer.Render(&buf, aPost); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tapprovals.VerifyString(t, buf.String())\n\t})\n}\n```\n\nAnd our benchmark\n\n```go\nfunc BenchmarkRender(b *testing.B) {\n\tvar (\n\t\taPost = blogrenderer.Post{\n\t\t\tTitle:       \"hello world\",\n\t\t\tBody:        \"This is a post\",\n\t\t\tDescription: \"This is a description\",\n\t\t\tTags:        []string{\"go\", \"tdd\"},\n\t\t}\n\t)\n\n\tpostRenderer, err := blogrenderer.NewPostRenderer()\n\n\tif err != nil {\n\t\tb.Fatal(err)\n\t}\n\n\tfor b.Loop() {\n\t\tpostRenderer.Render(io.Discard, aPost)\n\t}\n}\n```\n\nThe test should continue to pass. How about our benchmark?\n\n`BenchmarkRender-8 362124 3131 ns/op`. The old NS per op were `53812 ns/op`, so this is a decent improvement! As we add other methods to render, say an Index page, it should simplify the code as we don't need to duplicate the template parsing.\n\n## Back to the real work\n\nIn terms of rendering posts, the important part left is actually rendering the `Body`. If you recall, that should be markdown that the author has written, so it'll need converting to HTML. \n\nWe'll leave this as an exercise for you, the reader. You should be able to find a Go library to do this for you. Use the approval test to validate what you're doing. \n\n### On testing 3rd-party libraries\n\n**Note**. Be careful not to worry too much about explicitly testing how a 3rd party library behaves in unit tests. \n\nWriting tests against code you don't control is wasteful and adds maintenance overhead. Sometimes you may wish to use [dependency injection](./dependency-injection.md) to control a dependency and mock its behaviour for a test.\n\nIn this case though, I view converting the markdown into HTML as implementation detail of rendering, and our approval tests should give us enough confidence.\n\n### Render index\n\nThe next bit of functionality we're going to do is rendering an Index, listing the posts as a HTML ordered list. \n\nWe're expanding upon our API, so we'll put our TDD hat back on. \n\n## Write the test first\n\nOn the face of it an index page seems simple, but writing the test still prompts us to make some design choices\n\n```go\nt.Run(\"it renders an index of posts\", func(t *testing.T) {\n\tbuf := bytes.Buffer{}\n\tposts := []blogrenderer.Post{{Title: \"Hello World\"}, {Title: \"Hello World 2\"}}\n\n\tif err := postRenderer.RenderIndex(&buf, posts); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tgot := buf.String()\n\twant := `<ol><li><a href=\"/post/hello-world\">Hello World</a></li><li><a href=\"/post/hello-world-2\">Hello World 2</a></li></ol>`\n\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n})\n```\n\n1. We're using the `Post`'s title field as a part of the path of the URL, but we don't really want spaces in the URL so we're replacing them with hyphens.\n2. We've added a `RenderIndex` method to our `PostRenderer` that again takes an `io.Writer` and a slice of `Post`.\n\nIf we had stuck with a test-after, approval tests approach here we would not be answering these questions in a controlled environment. **Tests give us space to think**. \n\n## Try to run the test\n\n```\n./renderer_test.go:41:13: undefined: blogrenderer.RenderIndex\n```\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\n```go\nfunc (r *PostRenderer) RenderIndex(w io.Writer, posts []Post) error {\n\treturn nil\n}\n```\n\nThe above should get the following test failure\n\n```\n=== RUN   TestRender\n=== RUN   TestRender/it_renders_an_index_of_posts\n    renderer_test.go:49: got \"\" want \"<ol><li><a href=\\\"/post/hello-world\\\">Hello World</a></li><li><a href=\\\"/post/hello-world-2\\\">Hello World 2</a></li></ol>\"\n--- FAIL: TestRender (0.00s)\n```\n\n## Write enough code to make it pass\n\nEven though this _feels_ like it should be easy, it is a bit awkward. I did it in multiple steps\n\n```go\nfunc (r *PostRenderer) RenderIndex(w io.Writer, posts []Post) error {\n\tindexTemplate := `<ol>{{range .}}<li><a href=\"/post/{{.Title}}\">{{.Title}}</a></li>{{end}}</ol>`\n\n\ttempl, err := template.New(\"index\").Parse(indexTemplate)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif err := templ.Execute(w, posts); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n```\n\nI didn't want to bother with separate template files at first, I just wanted to get it working. I view the upfront template parsing and separation as refactoring I can do later. \n\nThis doesn't pass, but it's close.\n\n```\n=== RUN   TestRender\n=== RUN   TestRender/it_renders_an_index_of_posts\n    renderer_test.go:49: got \"<ol><li><a href=\\\"/post/Hello%20World\\\">Hello World</a></li><li><a href=\\\"/post/Hello%20World%202\\\">Hello World 2</a></li></ol>\" want \"<ol><li><a href=\\\"/post/hello-world\\\">Hello World</a></li><li><a href=\\\"/post/hello-world-2\\\">Hello World 2</a></li></ol>\"\n--- FAIL: TestRender (0.00s)\n    --- FAIL: TestRender/it_renders_an_index_of_posts (0.00s)\n```\n\nYou can see that the templating code is escaping the spaces in the `href` attributes. We need a way to do a string replace of spaces with hyphens. We can't just loop through the `[]Post` and replace them in-memory because we still want the spaces displayed to the user in the anchors. \n\nWe have a few options. The first one we'll explore is passing a function in to our template. \n\n### Passing functions into templates \n\n```go\nfunc (r *PostRenderer) RenderIndex(w io.Writer, posts []Post) error {\n\tindexTemplate := `<ol>{{range .}}<li><a href=\"/post/{{sanitiseTitle .Title}}\">{{.Title}}</a></li>{{end}}</ol>`\n\n\ttempl, err := template.New(\"index\").Funcs(template.FuncMap{\n\t\t\"sanitiseTitle\": func(title string) string {\n\t\t\treturn strings.ToLower(strings.Replace(title, \" \", \"-\", -1))\n\t\t},\n\t}).Parse(indexTemplate)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif err := templ.Execute(w, posts); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n```\n\n_Before you parse a template_ you can add a `template.FuncMap` into your template, which allows you to define functions that can be called within your template. In this case we've made a `sanitiseTitle` function which we then call inside our template with `{{sanitiseTitle .Title}}`.\n\nThis is a powerful feature, being able to send functions in to your template will allow you to do some very cool things, but, should you? Going back to the principles of Mustache and logic-less templates, why did they advocate for logic-less? **What is wrong with logic in templates?** \n\nAs we've shown, in order to test our templates, *we've had to introduce a whole different kind of testing*. \n\nImagine you introduce a function into a template which has a few different permutations of behaviour and edge cases, **how will you test it**? With this current design, your only means of testing this logic is by _rendering HTML and comparing strings_. This is not an easy or sane way of testing logic, and definitely not what you'd want for _important_ business logic. \n\nEven though the approval tests technique has reduced the cost of maintaining these tests, they're still more expensive to maintain than most unit tests you'll write. They're still sensitive to any minor markup changes you might make, it's just we've made it easier to manage. We should still strive to architect our code so we don't have to write many tests around our templates, and try and separate concerns so any logic that doesn't need to live inside our rendering code is properly separated.\n\nWhat Mustache-influenced templating engines give you is a useful constraint, don't try to circumvent it too often; **don't go against the grain**. Instead, embrace the idea of [view models](https://stackoverflow.com/a/11074506/3193), where you construct specific types that contain the data you need to render, in a way that's convenient for the templating language. \n\nThis way, whatever important business logic you use to generate that bag of data can be unit tested separately, away from the messy world of HTML and templating. \n\n### Separating concerns\n\nSo what could we do instead?\n\n#### Add a method to `Post` and then call that in the template\n\nWe can call methods in our templating code on the types we send, so we could add a `SanitisedTitle` method to `Post`. This would simplify the template and we could easily unit test this logic separately if we wish. This is probably the easiest solution, although not necessarily the simplest.  \n\nA downside to this approach is that this is still _view_ logic. It's not interesting to the rest of the system but it now becomes a part of the API for a core domain object. This kind of approach over time can lead to you creating [God Objects](https://en.wikipedia.org/wiki/God_object).\n\n#### Create a dedicated view model type, such as `PostViewModel` with exactly the data we need\n\nRather than our rendering code being coupled to the domain object, `Post`, it instead takes a view model.\n\n```go\ntype PostViewModel struct {\n\tTitle, SanitisedTitle, Description, Body string\n\tTags                                     []string\n}\n```\n\nCallers of our code would have to map from `[]Post` to `[]PostView`, generating the `SanitizedTitle`. A way to keep this clean would be to have a `func NewPostView(p Post) PostView` which would encapsulate the mapping.\n\nThis would keep our rendering code logic-less and is probably the strictest separation of concerns we could do, but the trade-off is a slightly more convoluted process to get our posts rendered.\n\nBoth options are fine, in this case I am tempted to go with the first. As you evolve the system you should be wary of adding more and more ad-hoc methods just to grease the wheels of rendering; dedicated view models become more useful when the transformation between the domain object and view becomes more involved.\n\nSo we can add our method to `Post`\n\n```go\nfunc (p Post) SanitisedTitle() string {\n\treturn strings.ToLower(strings.Replace(p.Title, \" \", \"-\", -1))\n}\n```\n\nAnd then we can go back to a simpler world in our rendering code\n\n```go\nfunc (r *PostRenderer) RenderIndex(w io.Writer, posts []Post) error {\n\tindexTemplate := `<ol>{{range .}}<li><a href=\"/post/{{.SanitisedTitle}}\">{{.Title}}</a></li>{{end}}</ol>`\n\n\ttempl, err := template.New(\"index\").Parse(indexTemplate)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif err := templ.Execute(w, posts); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n```\n\n## Refactor\n\nFinally the test should be passing. We can now move our template into a file (`templates/index.gohtml`) and load it once, when we construct our renderer.\n\n```go\npackage blogrenderer\n\nimport (\n\t\"embed\"\n\t\"html/template\"\n\t\"io\"\n)\n\nvar (\n\t//go:embed \"templates/*\"\n\tpostTemplates embed.FS\n)\n\ntype PostRenderer struct {\n\ttempl *template.Template\n}\n\nfunc NewPostRenderer() (*PostRenderer, error) {\n\ttempl, err := template.ParseFS(postTemplates, \"templates/*.gohtml\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &PostRenderer{templ: templ}, nil\n}\n\nfunc (r *PostRenderer) Render(w io.Writer, p Post) error {\n\treturn r.templ.ExecuteTemplate(w, \"blog.gohtml\", p)\n}\n\nfunc (r *PostRenderer) RenderIndex(w io.Writer, posts []Post) error {\n\treturn r.templ.ExecuteTemplate(w, \"index.gohtml\", posts)\n}\n```\n\nBy parsing more than one template into `templ` we now have to call `ExecuteTemplate` and specify _which_ template we wish to render as appropriate, but hopefully you'll agree the code we've arrived at looks great.\n\nThere is a _slight_ risk if someone renames one of the template files, it would introduce a bug, but our fast to run unit tests would catch this quickly. \n\nNow we're happy with our package's API design and got some basic behaviour driven out with TDD, let's change our test to use approvals.\n\n```go\n\tt.Run(\"it renders an index of posts\", func(t *testing.T) {\n\t\tbuf := bytes.Buffer{}\n\t\tposts := []blogrenderer.Post{{Title: \"Hello World\"}, {Title: \"Hello World 2\"}}\n\n\t\tif err := postRenderer.RenderIndex(&buf, posts); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tapprovals.VerifyString(t, buf.String())\n\t})\n```\n\nRemember to run the test to see it fail, and then approve the change. \n\nFinally we can add our page furniture to our index page:\n\n```handlebars\n{{template \"top\" .}}\n<ol>{{range .}}<li><a href=\"/post/{{.SanitisedTitle}}\">{{.Title}}</a></li>{{end}}</ol>\n{{template \"bottom\" .}}\n```\n\nRe-run the test, approve the change and we're done with the index!\n\n## Rendering the markdown body\n\nI encouraged you to try it yourself, here's the approach I ended up taking.\n\n```go\npackage blogrenderer\n\nimport (\n\t\"embed\"\n\t\"github.com/gomarkdown/markdown\"\n\t\"github.com/gomarkdown/markdown/parser\"\n\t\"html/template\"\n\t\"io\"\n)\n\nvar (\n\t//go:embed \"templates/*\"\n\tpostTemplates embed.FS\n)\n\ntype PostRenderer struct {\n\ttempl    *template.Template\n\tmdParser *parser.Parser\n}\n\nfunc NewPostRenderer() (*PostRenderer, error) {\n\ttempl, err := template.ParseFS(postTemplates, \"templates/*.gohtml\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\textensions := parser.CommonExtensions | parser.AutoHeadingIDs\n\tparser := parser.NewWithExtensions(extensions)\n\n\treturn &PostRenderer{templ: templ, mdParser: parser}, nil\n}\n\nfunc (r *PostRenderer) Render(w io.Writer, p Post) error {\n\treturn r.templ.ExecuteTemplate(w, \"blog.gohtml\", newPostVM(p, r))\n}\n\nfunc (r *PostRenderer) RenderIndex(w io.Writer, posts []Post) error {\n\treturn r.templ.ExecuteTemplate(w, \"index.gohtml\", posts)\n}\n\ntype postViewModel struct {\n\tPost\n\tHTMLBody template.HTML\n}\n\nfunc newPostVM(p Post, r *PostRenderer) postViewModel {\n\tvm := postViewModel{Post: p}\n\tvm.HTMLBody = template.HTML(markdown.ToHTML([]byte(p.Body), r.mdParser, nil))\n\treturn vm\n}\n```\n\nI used the excellent [gomarkdown](https://github.com/gomarkdown/markdown) library which worked exactly how I'd hope. \n\nIf you tried to do this yourself you may have found that your body render had the HTML escaped. This is a security feature of Go's html/template package to stop malicious 3rd-party HTML being outputted. \n\nTo circumvent this, in the type you send to the render, you'll need to wrap your trusted HTML in [template.HTML](https://pkg.go.dev/html/template#HTML)\n\n> HTML encapsulates a known safe HTML document fragment. It should not be used for HTML from a third-party, or HTML with unclosed tags or comments. The outputs of a sound HTML sanitiser and a template escaped by this package are fine for use with HTML.\n>\n> Use of this type presents a security risk: the encapsulated content should come from a trusted source, as it will be included verbatim in the template output.\n\nSo I created an **unexported** view model (`postViewModel`), because I still viewed this as internal implementation detail to rendering. I have no need to test this separately and I don't want it polluting my API. \n\nI construct one when rendering so I can parse the `Body` into `HTMLBody` and then I use that field in the template to render the HTML.\n\n## Wrapping up\n\nIf you combine your learnings of the [reading files](reading-files.md) chapter and this one, you can comfortably make a well-tested, simple, static site generator and spin up a blog of your own. Find some CSS tutorials and you can make it look nice too. \n\nThis approach extends beyond blogs. Taking data from any source, be it a database, an API or a file-system and converting it into HTML and returning it from a server is a simple technique spanning many decades. People like to bemoan the complexity of modern web development but are you sure you're not just inflicting the complexity on yourself?\n\nGo is wonderful for web development, especially when you think clearly about what your real requirements are for the website you're making. Generating HTML on the server is often a better, simpler and more performant approach than creating a \"web application\" with technologies like React.\n\n### What we've learned\n\n- How to create and render HTML templates.\n- How to compose templates together and [DRY](https://en.wikipedia.org/wiki/Don't_repeat_yourself) up related markup and help us keep a consistent look and feel.\n- How to pass functions into templates, and why you should think twice about it.\n- How to write \"Approval Tests\", which help us test the big ugly output of things like template renderers. \n\n### On logic-less templates\n\nAs always, this is all about **separation of concerns**. It's important we consider what the responsibilities are of the various parts of our system. Too often people leak important business logic into templates, mixing up concerns and making systems difficult to understand, maintain and test.\n\n### Not just for HTML\n\nRemember that go has `text/template` to generate other kinds of data from a template. If you find yourself needing to transform data into some kind of structured output, the techniques laid out in this chapter can be useful. \n\n### References and further material \n\n- [John Calhoun's 'Learn Web Development with Go'](https://www.calhoun.io/intro-to-templates-p1-contextual-encoding/) has a number of excellent articles on templating.\n- [Hotwire](https://hotwired.dev) - You can use these techniques to create Hotwire web applications. It has been built by Basecamp who are primarily a Ruby on Rails shop, but because it is server-side, we can use it with Go. \n"
  },
  {
    "path": "http-handlers-revisited.md",
    "content": "# Revisiting HTTP Handlers\n\n[**You can find all the code here**](https://github.com/quii/learn-go-with-tests/tree/main/q-and-a/http-handlers-revisited)\n\nThis book already has a chapter on [testing a HTTP handler](http-server.md) but this will feature a broader discussion on designing them, so they are simple to test.\n\nWe'll take a look at a real example and how we can improve how it's designed by applying principles such as single responsibility principle and separation of concerns. These principles can be realised by using [interfaces](structs-methods-and-interfaces.md) and [dependency injection](dependency-injection.md). By doing this we'll show how testing handlers is actually quite trivial.\n\n![Common question in Go community illustrated](.gitbook/assets/amazing-art.png)\n\nTesting HTTP handlers seems to be a recurring question in the Go community, and I think it points to a wider problem of people misunderstanding how to design them.\n\nSo often people's difficulties with testing stems from the design of their code rather than the actual writing of tests. As I stress so often in this book:\n\n> If your tests are causing you pain, listen to that signal and think about the design of your code.\n\n## An example\n\n[Santosh Kumar tweeted me](https://twitter.com/sntshk/status/1255559003339284481)\n\n> How do I test a http handler which has mongodb dependency?\n\nHere is the code\n\n```go\nfunc Registration(w http.ResponseWriter, r *http.Request) {\n\tvar res model.ResponseResult\n\tvar user model.User\n\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\n\tjsonDecoder := json.NewDecoder(r.Body)\n\tjsonDecoder.DisallowUnknownFields()\n\tdefer r.Body.Close()\n\n\t// check if there is proper json body or error\n\tif err := jsonDecoder.Decode(&user); err != nil {\n\t\tres.Error = err.Error()\n\t\t// return 400 status codes\n\t\tw.WriteHeader(http.StatusBadRequest)\n\t\tjson.NewEncoder(w).Encode(res)\n\t\treturn\n\t}\n\n\t// Connect to mongodb\n\tclient, _ := mongo.NewClient(options.Client().ApplyURI(\"mongodb://127.0.0.1:27017\"))\n\tctx, _ := context.WithTimeout(context.Background(), 10*time.Second)\n\terr := client.Connect(ctx)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer client.Disconnect(ctx)\n\t// Check if username already exists in users datastore, if so, 400\n\t// else insert user right away\n\tcollection := client.Database(\"test\").Collection(\"users\")\n\tfilter := bson.D{{\"username\", user.Username}}\n\tvar foundUser model.User\n\terr = collection.FindOne(context.TODO(), filter).Decode(&foundUser)\n\tif foundUser.Username == user.Username {\n\t\tres.Error = UserExists\n\t\t// return 400 status codes\n\t\tw.WriteHeader(http.StatusBadRequest)\n\t\tjson.NewEncoder(w).Encode(res)\n\t\treturn\n\t}\n\n\tpass, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost)\n\tif err != nil {\n\t\tres.Error = err.Error()\n\t\t// return 400 status codes\n\t\tw.WriteHeader(http.StatusBadRequest)\n\t\tjson.NewEncoder(w).Encode(res)\n\t\treturn\n\t}\n\tuser.Password = string(pass)\n\n\tinsertResult, err := collection.InsertOne(context.TODO(), user)\n\tif err != nil {\n\t\tres.Error = err.Error()\n\t\t// return 400 status codes\n\t\tw.WriteHeader(http.StatusBadRequest)\n\t\tjson.NewEncoder(w).Encode(res)\n\t\treturn\n\t}\n\n\t// return 200\n\tw.WriteHeader(http.StatusOK)\n\tres.Result = fmt.Sprintf(\"%s: %s\", UserCreated, insertResult.InsertedID)\n\tjson.NewEncoder(w).Encode(res)\n\treturn\n}\n```\n\nLet's just list all the things this one function has to do:\n\n1. Write HTTP responses, send headers, status codes, etc.\n2. Decode the request's body into a `User`\n3. Connect to a database (and all the details around that)\n4. Query the database and applying some business logic depending on the result\n5. Generate a password\n6. Insert a record\n\nThis is too much.\n\n## What is a HTTP Handler and what should it do ?\n\nForgetting specific Go details for a moment, no matter what language I've worked in what has always served me well is thinking about the [separation of concerns](https://en.wikipedia.org/wiki/Separation_of_concerns) and the [single responsibility principle](https://en.wikipedia.org/wiki/Single-responsibility_principle).\n\nThis can be quite tricky to apply depending on the problem you're solving. What exactly _is_ a responsibility?\n\nThe lines can blur depending on how abstractly you're thinking and sometimes your first guess might not be right.\n\nThankfully with HTTP handlers I feel like I have a pretty good idea what they should do, no matter what project I've worked on:\n\n1. Accept a HTTP request, parse and validate it.\n2. Call some `ServiceThing` to do `ImportantBusinessLogic` with the data I got from step 1.\n3. Send an appropriate `HTTP` response depending on what `ServiceThing` returns.\n\nI'm not saying every HTTP handler _ever_ should have roughly this shape, but 99 times out of 100 that seems to be the case for me.\n\nWhen you separate these concerns:\n\n* Testing handlers becomes a breeze and is focused a small number of concerns.\n* Importantly testing `ImportantBusinessLogic` no longer has to concern itself with `HTTP`, you can test the business logic cleanly.\n* You can use `ImportantBusinessLogic` in other contexts without having to modify it.\n* If `ImportantBusinessLogic` changes what it does, so long as the interface remains the same you don't have to change your handlers.\n\n## Go's Handlers\n\n[`http.HandlerFunc`](https://golang.org/pkg/net/http/#HandlerFunc)\n\n> The HandlerFunc type is an adapter to allow the use of ordinary functions as HTTP handlers.\n\n`type HandlerFunc func(ResponseWriter, *Request)`\n\nReader, take a breath and look at the code above. What do you notice?\n\n**It is a function that takes some arguments**\n\nThere's no framework magic, no annotations, no magic beans, nothing.\n\nIt's just a function, _and we know how to test functions_.\n\nIt fits in nicely with the commentary above:\n\n* It takes a [`http.Request`](https://golang.org/pkg/net/http/#Request) which is just a bundle of data for us to inspect, parse and validate.\n* > [A `http.ResponseWriter` interface is used by an HTTP handler to construct an HTTP response.](https://golang.org/pkg/net/http/#ResponseWriter)\n\n### Super basic example test\n\n```go\nfunc Teapot(res http.ResponseWriter, req *http.Request) {\n\tres.WriteHeader(http.StatusTeapot)\n}\n\nfunc TestTeapotHandler(t *testing.T) {\n\treq := httptest.NewRequest(http.MethodGet, \"/\", nil)\n\tres := httptest.NewRecorder()\n\n\tTeapot(res, req)\n\n\tif res.Code != http.StatusTeapot {\n\t\tt.Errorf(\"got status %d but wanted %d\", res.Code, http.StatusTeapot)\n\t}\n}\n```\n\nTo test our function, we _call_ it.\n\nFor our test we pass a `httptest.ResponseRecorder` as our `http.ResponseWriter` argument, and our function will use it to write the `HTTP` response. The recorder will record (or _spy_ on) what was sent, and then we can make our assertions.\n\n## Calling a `ServiceThing` in our handler\n\nA common complaint about TDD tutorials is that they're always \"too simple\" and not \"real world enough\". My answer to that is:\n\n> Wouldn't it be nice if all your code was simple to read and test like the examples you mention?\n\nThis is one of the biggest challenges we face but need to keep striving for. It _is possible_ (although not necessarily easy) to design code, so it can be simple to read and test if we practice and apply good software engineering principles.\n\nRecapping what the handler from earlier does:\n\n1. Write HTTP responses, send headers, status codes, etc.\n2. Decode the request's body into a `User`\n3. Connect to a database (and all the details around that)\n4. Query the database and applying some business logic depending on the result\n5. Generate a password\n6. Insert a record\n\nTaking the idea of a more ideal separation of concerns I'd want it to be more like:\n\n1. Decode the request's body into a `User`\n2. Call a `UserService.Register(user)` (this is our `ServiceThing`)\n3. If there's an error act on it (the example always sends a `400 BadRequest` which I don't think is right), I'll just have a catch-all handler of a `500 Internal Server Error` _for now_. I must stress that returning `500` for all errors makes for a terrible API! Later on we can make the error handling more sophisticated, perhaps with [error types](error-types.md).\n4. If there's no error, `201 Created` with the ID as the response body (again for terseness/laziness)\n\nFor the sake of brevity I won't go over the usual TDD process, check all the other chapters for examples.\n\n### New design\n\n```go\ntype UserService interface {\n\tRegister(user User) (insertedID string, err error)\n}\n\ntype UserServer struct {\n\tservice UserService\n}\n\nfunc NewUserServer(service UserService) *UserServer {\n\treturn &UserServer{service: service}\n}\n\nfunc (u *UserServer) RegisterUser(w http.ResponseWriter, r *http.Request) {\n\tdefer r.Body.Close()\n\n\t// request parsing and validation\n\tvar newUser User\n\terr := json.NewDecoder(r.Body).Decode(&newUser)\n\n\tif err != nil {\n\t\thttp.Error(w, fmt.Sprintf(\"could not decode user payload: %v\", err), http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// call a service thing to take care of the hard work\n\tinsertedID, err := u.service.Register(newUser)\n\n\t// depending on what we get back, respond accordingly\n\tif err != nil {\n\t\t//todo: handle different kinds of errors differently\n\t\thttp.Error(w, fmt.Sprintf(\"problem registering new user: %v\", err), http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tw.WriteHeader(http.StatusCreated)\n\tfmt.Fprint(w, insertedID)\n}\n```\n\nOur `RegisterUser` method matches the shape of `http.HandlerFunc` so we're good to go. We've attached it as a method on a new type `UserServer` which contains a dependency on a `UserService` which is captured as an interface.\n\nInterfaces are a fantastic way to ensure our `HTTP` concerns are decoupled from any specific implementation; we can just call the method on the dependency, and we don't have to care _how_ a user gets registered.\n\nIf you wish to explore this approach in more detail following TDD read the [Dependency Injection](dependency-injection.md) chapter and the [HTTP Server chapter of the \"Build an application\" section](http-server.md).\n\nNow that we've decoupled ourselves from any specific implementation detail around registration writing the code for our handler is straightforward and follows the responsibilities described earlier.\n\n### The tests!\n\nThis simplicity is reflected in our tests.\n\n```go\ntype MockUserService struct {\n\tRegisterFunc    func(user User) (string, error)\n\tUsersRegistered []User\n}\n\nfunc (m *MockUserService) Register(user User) (insertedID string, err error) {\n\tm.UsersRegistered = append(m.UsersRegistered, user)\n\treturn m.RegisterFunc(user)\n}\n\nfunc TestRegisterUser(t *testing.T) {\n\tt.Run(\"can register valid users\", func(t *testing.T) {\n\t\tuser := User{Name: \"CJ\"}\n\t\texpectedInsertedID := \"whatever\"\n\n\t\tservice := &MockUserService{\n\t\t\tRegisterFunc: func(user User) (string, error) {\n\t\t\t\treturn expectedInsertedID, nil\n\t\t\t},\n\t\t}\n\t\tserver := NewUserServer(service)\n\n\t\treq := httptest.NewRequest(http.MethodGet, \"/\", userToJSON(user))\n\t\tres := httptest.NewRecorder()\n\n\t\tserver.RegisterUser(res, req)\n\n\t\tassertStatus(t, res.Code, http.StatusCreated)\n\n\t\tif res.Body.String() != expectedInsertedID {\n\t\t\tt.Errorf(\"expected body of %q but got %q\", res.Body.String(), expectedInsertedID)\n\t\t}\n\n\t\tif len(service.UsersRegistered) != 1 {\n\t\t\tt.Fatalf(\"expected 1 user added but got %d\", len(service.UsersRegistered))\n\t\t}\n\n\t\tif !reflect.DeepEqual(service.UsersRegistered[0], user) {\n\t\t\tt.Errorf(\"the user registered %+v was not what was expected %+v\", service.UsersRegistered[0], user)\n\t\t}\n\t})\n\n\tt.Run(\"returns 400 bad request if body is not valid user JSON\", func(t *testing.T) {\n\t\tserver := NewUserServer(nil)\n\n\t\treq := httptest.NewRequest(http.MethodGet, \"/\", strings.NewReader(\"trouble will find me\"))\n\t\tres := httptest.NewRecorder()\n\n\t\tserver.RegisterUser(res, req)\n\n\t\tassertStatus(t, res.Code, http.StatusBadRequest)\n\t})\n\n\tt.Run(\"returns a 500 internal server error if the service fails\", func(t *testing.T) {\n\t\tuser := User{Name: \"CJ\"}\n\n\t\tservice := &MockUserService{\n\t\t\tRegisterFunc: func(user User) (string, error) {\n\t\t\t\treturn \"\", errors.New(\"couldn't add new user\")\n\t\t\t},\n\t\t}\n\t\tserver := NewUserServer(service)\n\n\t\treq := httptest.NewRequest(http.MethodGet, \"/\", userToJSON(user))\n\t\tres := httptest.NewRecorder()\n\n\t\tserver.RegisterUser(res, req)\n\n\t\tassertStatus(t, res.Code, http.StatusInternalServerError)\n\t})\n}\n```\n\nNow our handler isn't coupled to a specific implementation of storage it is trivial for us to write a `MockUserService` to help us write simple, fast unit tests to exercise the specific responsibilities it has.\n\n### What about the database code? You're cheating!\n\nThis is all very deliberate. We don't want HTTP handlers concerned with our business logic, databases, connections, etc.\n\nBy doing this we have liberated the handler from messy details, we've _also_ made it easier to test our persistence layer and business logic as it is also no longer coupled to irrelevant HTTP details.\n\nAll we need to do is now implement our `UserService` using whatever database we want to use\n\n```go\ntype MongoUserService struct {\n}\n\nfunc NewMongoUserService() *MongoUserService {\n\t//todo: pass in DB URL as argument to this function\n\t//todo: connect to db, create a connection pool\n\treturn &MongoUserService{}\n}\n\nfunc (m MongoUserService) Register(user User) (insertedID string, err error) {\n\t// use m.mongoConnection to perform queries\n\tpanic(\"implement me\")\n}\n```\n\nWe can test this separately and once we're happy in `main` we can snap these two units together for our working application.\n\n```go\nfunc main() {\n\tmongoService := NewMongoUserService()\n\tserver := NewUserServer(mongoService)\n\thttp.ListenAndServe(\":8000\", http.HandlerFunc(server.RegisterUser))\n}\n```\n\n### A more robust and extensible design with little effort\n\nThese principles not only make our lives easier in the short-term they make the system easier to extend in the future.\n\nIt wouldn't be surprising that further iterations of this system we'd want to email the user a confirmation of registration.\n\nWith the old design we'd have to change the handler _and_ the surrounding tests. This is often how parts of code become unmaintainable, more and more functionality creeps in because it's already _designed_ that way; for the \"HTTP handler\" to handle... everything!\n\nBy separating concerns using an interface we don't have to edit the handler _at all_ because it's not concerned with the business logic around registration.\n\n## Wrapping up\n\nTesting Go's HTTP handlers is not challenging, but designing good software can be!\n\nPeople make the mistake of thinking HTTP handlers are special and throw out good software engineering practices when writing them which then makes testing them challenging.\n\nReiterating again; **Go's http handlers are just functions**. If you write them like you would other functions, with clear responsibilities, and a good separation of concerns you will have no trouble testing them, and your codebase will be healthier for it.\n"
  },
  {
    "path": "http-server/v1/main.go",
    "content": "package main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc main() {\n\thandler := http.HandlerFunc(PlayerServer)\n\tlog.Fatal(http.ListenAndServe(\":5000\", handler))\n}\n"
  },
  {
    "path": "http-server/v1/server.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n)\n\n// PlayerServer currently returns \"20\" given _any_ request.\nfunc PlayerServer(w http.ResponseWriter, r *http.Request) {\n\tfmt.Fprint(w, \"20\")\n}\n"
  },
  {
    "path": "http-server/v1/server_test.go",
    "content": "package main\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestGETPlayers(t *testing.T) {\n\trequest, _ := http.NewRequest(http.MethodGet, \"/\", nil)\n\tresponse := httptest.NewRecorder()\n\n\tPlayerServer(response, request)\n\n\tt.Run(\"returns Pepper's score\", func(t *testing.T) {\n\t\tgot := response.Body.String()\n\t\twant := \"20\"\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %q, want %q\", got, want)\n\t\t}\n\t})\n\n}\n"
  },
  {
    "path": "http-server/v2/main.go",
    "content": "package main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n)\n\n// InMemoryPlayerStore collects data about players in memory.\ntype InMemoryPlayerStore struct{}\n\n// GetPlayerScore retrieves scores for a given player.\nfunc (i *InMemoryPlayerStore) GetPlayerScore(name string) int {\n\treturn 123\n}\n\nfunc main() {\n\tserver := &PlayerServer{&InMemoryPlayerStore{}}\n\tlog.Fatal(http.ListenAndServe(\":5000\", server))\n}\n"
  },
  {
    "path": "http-server/v2/server.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n)\n\n// PlayerStore stores score information about players.\ntype PlayerStore interface {\n\tGetPlayerScore(name string) int\n}\n\n// PlayerServer is a HTTP interface for player information.\ntype PlayerServer struct {\n\tstore PlayerStore\n}\n\nfunc (p *PlayerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\n\tscore := p.store.GetPlayerScore(player)\n\n\tif score == 0 {\n\t\tw.WriteHeader(http.StatusNotFound)\n\t}\n\n\tfmt.Fprint(w, score)\n}\n"
  },
  {
    "path": "http-server/v2/server_test.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\ntype StubPlayerStore struct {\n\tscores map[string]int\n}\n\nfunc (s *StubPlayerStore) GetPlayerScore(name string) int {\n\tscore := s.scores[name]\n\treturn score\n}\n\nfunc TestGETPlayers(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{\n\t\t\t\"Pepper\": 20,\n\t\t\t\"Floyd\":  10,\n\t\t},\n\t}\n\tserver := &PlayerServer{&store}\n\n\ttests := []struct {\n\t\tname               string\n\t\tplayer             string\n\t\texpectedHTTPStatus int\n\t\texpectedScore      string\n\t}{\n\t\t{\n\t\t\tname:               \"Returns Pepper's score\",\n\t\t\tplayer:             \"Pepper\",\n\t\t\texpectedHTTPStatus: http.StatusOK,\n\t\t\texpectedScore:      \"20\",\n\t\t},\n\t\t{\n\t\t\tname:               \"Returns Floyd's score\",\n\t\t\tplayer:             \"Floyd\",\n\t\t\texpectedHTTPStatus: http.StatusOK,\n\t\t\texpectedScore:      \"10\",\n\t\t},\n\t\t{\n\t\t\tname:               \"Returns 404 on missing players\",\n\t\t\tplayer:             \"Apollo\",\n\t\t\texpectedHTTPStatus: http.StatusNotFound,\n\t\t\texpectedScore:      \"0\",\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\trequest := newGetScoreRequest(tt.player)\n\t\t\tresponse := httptest.NewRecorder()\n\n\t\t\tserver.ServeHTTP(response, request)\n\n\t\t\tassertStatus(t, response.Code, tt.expectedHTTPStatus)\n\t\t\tassertResponseBody(t, response.Body.String(), tt.expectedScore)\n\t\t})\n\t}\n}\n\nfunc assertStatus(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"did not get correct status, got %d, want %d\", got, want)\n\t}\n}\n\nfunc newGetScoreRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc assertResponseBody(t testing.TB, got, want string) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"response body is wrong, got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "http-server/v3/main.go",
    "content": "package main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n)\n\n// InMemoryPlayerStore collects data about players in memory.\ntype InMemoryPlayerStore struct{}\n\n// GetPlayerScore retrieves scores for a given player.\nfunc (i *InMemoryPlayerStore) GetPlayerScore(name string) int {\n\treturn 123\n}\n\nfunc main() {\n\tserver := &PlayerServer{&InMemoryPlayerStore{}}\n\tlog.Fatal(http.ListenAndServe(\":5000\", server))\n}\n"
  },
  {
    "path": "http-server/v3/server.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n)\n\n// PlayerStore stores score information about players.\ntype PlayerStore interface {\n\tGetPlayerScore(name string) int\n}\n\n// PlayerServer is a HTTP interface for player information.\ntype PlayerServer struct {\n\tstore PlayerStore\n}\n\nfunc (p *PlayerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\n\tswitch r.Method {\n\tcase http.MethodPost:\n\t\tp.processWin(w)\n\tcase http.MethodGet:\n\t\tp.showScore(w, r)\n\t}\n\n}\n\nfunc (p *PlayerServer) showScore(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\n\tscore := p.store.GetPlayerScore(player)\n\n\tif score == 0 {\n\t\tw.WriteHeader(http.StatusNotFound)\n\t}\n\n\tfmt.Fprint(w, score)\n}\n\nfunc (p *PlayerServer) processWin(w http.ResponseWriter) {\n\tw.WriteHeader(http.StatusAccepted)\n}\n"
  },
  {
    "path": "http-server/v3/server_test.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\ntype StubPlayerStore struct {\n\tscores map[string]int\n}\n\nfunc (s *StubPlayerStore) GetPlayerScore(name string) int {\n\tscore := s.scores[name]\n\treturn score\n}\n\nfunc TestGETPlayers(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{\n\t\t\t\"Pepper\": 20,\n\t\t\t\"Floyd\":  10,\n\t\t},\n\t}\n\tserver := &PlayerServer{&store}\n\n\ttests := []struct {\n\t\tname               string\n\t\tplayer             string\n\t\texpectedHTTPStatus int\n\t\texpectedScore      string\n\t}{\n\t\t{\n\t\t\tname:               \"Returns Pepper's score\",\n\t\t\tplayer:             \"Pepper\",\n\t\t\texpectedHTTPStatus: http.StatusOK,\n\t\t\texpectedScore:      \"20\",\n\t\t},\n\t\t{\n\t\t\tname:               \"Returns Floyd's score\",\n\t\t\tplayer:             \"Floyd\",\n\t\t\texpectedHTTPStatus: http.StatusOK,\n\t\t\texpectedScore:      \"10\",\n\t\t},\n\t\t{\n\t\t\tname:               \"Returns 404 on missing players\",\n\t\t\tplayer:             \"Apollo\",\n\t\t\texpectedHTTPStatus: http.StatusNotFound,\n\t\t\texpectedScore:      \"0\",\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\trequest := newGetScoreRequest(tt.player)\n\t\t\tresponse := httptest.NewRecorder()\n\n\t\t\tserver.ServeHTTP(response, request)\n\n\t\t\tassertStatus(t, response.Code, tt.expectedHTTPStatus)\n\t\t\tassertResponseBody(t, response.Body.String(), tt.expectedScore)\n\t\t})\n\t}\n}\n\nfunc TestStoreWins(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{},\n\t}\n\tserver := &PlayerServer{&store}\n\n\tt.Run(\"it returns accepted on POST\", func(t *testing.T) {\n\t\trequest, _ := http.NewRequest(http.MethodPost, \"/players/Pepper\", nil)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusAccepted)\n\t})\n}\n\nfunc assertStatus(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"did not get correct status, got %d, want %d\", got, want)\n\t}\n}\n\nfunc newGetScoreRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc assertResponseBody(t testing.TB, got, want string) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"response body is wrong, got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "http-server/v4/main.go",
    "content": "package main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n)\n\n// InMemoryPlayerStore collects data about players in memory.\ntype InMemoryPlayerStore struct{}\n\n// RecordWin will record a player's win.\nfunc (i *InMemoryPlayerStore) RecordWin(name string) {\n}\n\n// GetPlayerScore retrieves scores for a given player.\nfunc (i *InMemoryPlayerStore) GetPlayerScore(name string) int {\n\treturn 123\n}\n\nfunc main() {\n\tserver := &PlayerServer{&InMemoryPlayerStore{}}\n\tlog.Fatal(http.ListenAndServe(\":5000\", server))\n}\n"
  },
  {
    "path": "http-server/v4/server.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n)\n\n// PlayerStore stores score information about players.\ntype PlayerStore interface {\n\tGetPlayerScore(name string) int\n\tRecordWin(name string)\n}\n\n// PlayerServer is a HTTP interface for player information.\ntype PlayerServer struct {\n\tstore PlayerStore\n}\n\nfunc (p *PlayerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\n\tswitch r.Method {\n\tcase http.MethodPost:\n\t\tp.processWin(w, player)\n\tcase http.MethodGet:\n\t\tp.showScore(w, player)\n\t}\n}\n\nfunc (p *PlayerServer) showScore(w http.ResponseWriter, player string) {\n\tscore := p.store.GetPlayerScore(player)\n\n\tif score == 0 {\n\t\tw.WriteHeader(http.StatusNotFound)\n\t}\n\n\tfmt.Fprint(w, score)\n}\n\nfunc (p *PlayerServer) processWin(w http.ResponseWriter, player string) {\n\tp.store.RecordWin(player)\n\tw.WriteHeader(http.StatusAccepted)\n}\n"
  },
  {
    "path": "http-server/v4/server_test.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\ntype StubPlayerStore struct {\n\tscores   map[string]int\n\twinCalls []string\n}\n\nfunc (s *StubPlayerStore) GetPlayerScore(name string) int {\n\tscore := s.scores[name]\n\treturn score\n}\n\nfunc (s *StubPlayerStore) RecordWin(name string) {\n\ts.winCalls = append(s.winCalls, name)\n}\n\nfunc TestGETPlayers(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{\n\t\t\t\"Pepper\": 20,\n\t\t\t\"Floyd\":  10,\n\t\t},\n\t\tnil,\n\t}\n\tserver := &PlayerServer{&store}\n\n\ttests := []struct {\n\t\tname               string\n\t\tplayer             string\n\t\texpectedHTTPStatus int\n\t\texpectedScore      string\n\t}{\n\t\t{\n\t\t\tname:               \"Returns Pepper's score\",\n\t\t\tplayer:             \"Pepper\",\n\t\t\texpectedHTTPStatus: http.StatusOK,\n\t\t\texpectedScore:      \"20\",\n\t\t},\n\t\t{\n\t\t\tname:               \"Returns Floyd's score\",\n\t\t\tplayer:             \"Floyd\",\n\t\t\texpectedHTTPStatus: http.StatusOK,\n\t\t\texpectedScore:      \"10\",\n\t\t},\n\t\t{\n\t\t\tname:               \"Returns 404 on missing players\",\n\t\t\tplayer:             \"Apollo\",\n\t\t\texpectedHTTPStatus: http.StatusNotFound,\n\t\t\texpectedScore:      \"0\",\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\trequest := newGetScoreRequest(tt.player)\n\t\t\tresponse := httptest.NewRecorder()\n\n\t\t\tserver.ServeHTTP(response, request)\n\n\t\t\tassertStatus(t, response.Code, tt.expectedHTTPStatus)\n\t\t\tassertResponseBody(t, response.Body.String(), tt.expectedScore)\n\t\t})\n\t}\n}\n\nfunc TestStoreWins(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{},\n\t\tnil,\n\t}\n\tserver := &PlayerServer{&store}\n\n\tt.Run(\"it records wins on POST\", func(t *testing.T) {\n\t\tplayer := \"Pepper\"\n\n\t\trequest, _ := http.NewRequest(http.MethodPost, fmt.Sprintf(\"/players/%s\", player), nil)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusAccepted)\n\n\t\tif len(store.winCalls) != 1 {\n\t\t\tt.Fatalf(\"got %d calls to RecordWin want %d\", len(store.winCalls), 1)\n\t\t}\n\n\t\tif store.winCalls[0] != player {\n\t\t\tt.Errorf(\"did not store correct winner got %q want %q\", store.winCalls[0], player)\n\t\t}\n\t})\n}\n\nfunc assertStatus(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"did not get correct status, got %d, want %d\", got, want)\n\t}\n}\n\nfunc newGetScoreRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc assertResponseBody(t testing.TB, got, want string) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"response body is wrong, got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "http-server/v5/in_memory_player_store.go",
    "content": "package main\n\nimport \"sync\"\n\n// NewInMemoryPlayerStore initialises an empty player store.\nfunc NewInMemoryPlayerStore() *InMemoryPlayerStore {\n\treturn &InMemoryPlayerStore{\n\t\tmap[string]int{},\n\t\tsync.RWMutex{},\n\t}\n}\n\n// InMemoryPlayerStore collects data about players in memory.\ntype InMemoryPlayerStore struct {\n\tstore map[string]int\n\t// A mutex is used to synchronize read/write access to the map\n\tlock sync.RWMutex\n}\n\n// RecordWin will record a player's win.\nfunc (i *InMemoryPlayerStore) RecordWin(name string) {\n\ti.lock.Lock()\n\tdefer i.lock.Unlock()\n\ti.store[name]++\n}\n\n// GetPlayerScore retrieves scores for a given player.\nfunc (i *InMemoryPlayerStore) GetPlayerScore(name string) int {\n\ti.lock.RLock()\n\tdefer i.lock.RUnlock()\n\treturn i.store[name]\n}\n"
  },
  {
    "path": "http-server/v5/main.go",
    "content": "package main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc main() {\n\tserver := &PlayerServer{NewInMemoryPlayerStore()}\n\tlog.Fatal(http.ListenAndServe(\":5000\", server))\n}\n"
  },
  {
    "path": "http-server/v5/server.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n)\n\n// PlayerStore stores score information about players.\ntype PlayerStore interface {\n\tGetPlayerScore(name string) int\n\tRecordWin(name string)\n}\n\n// PlayerServer is a HTTP interface for player information.\ntype PlayerServer struct {\n\tstore PlayerStore\n}\n\nfunc (p *PlayerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\tswitch r.Method {\n\tcase http.MethodPost:\n\t\tp.processWin(w, player)\n\tcase http.MethodGet:\n\t\tp.showScore(w, player)\n\t}\n}\n\nfunc (p *PlayerServer) showScore(w http.ResponseWriter, player string) {\n\tscore := p.store.GetPlayerScore(player)\n\n\tif score == 0 {\n\t\tw.WriteHeader(http.StatusNotFound)\n\t}\n\n\tfmt.Fprint(w, score)\n}\n\nfunc (p *PlayerServer) processWin(w http.ResponseWriter, player string) {\n\tp.store.RecordWin(player)\n\tw.WriteHeader(http.StatusAccepted)\n}\n"
  },
  {
    "path": "http-server/v5/server_integration_test.go",
    "content": "package main\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestRecordingWinsAndRetrievingThem(t *testing.T) {\n\tstore := NewInMemoryPlayerStore()\n\tserver := PlayerServer{store}\n\tplayer := \"Pepper\"\n\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\n\tresponse := httptest.NewRecorder()\n\tserver.ServeHTTP(response, newGetScoreRequest(player))\n\tassertStatus(t, response.Code, http.StatusOK)\n\n\tassertResponseBody(t, response.Body.String(), \"3\")\n}\n"
  },
  {
    "path": "http-server/v5/server_test.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\ntype StubPlayerStore struct {\n\tscores   map[string]int\n\twinCalls []string\n}\n\nfunc (s *StubPlayerStore) GetPlayerScore(name string) int {\n\tscore := s.scores[name]\n\treturn score\n}\n\nfunc (s *StubPlayerStore) RecordWin(name string) {\n\ts.winCalls = append(s.winCalls, name)\n}\n\nfunc TestGETPlayers(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{\n\t\t\t\"Pepper\": 20,\n\t\t\t\"Floyd\":  10,\n\t\t},\n\t\tnil,\n\t}\n\tserver := &PlayerServer{&store}\n\n\ttests := []struct {\n\t\tname               string\n\t\tplayer             string\n\t\texpectedHTTPStatus int\n\t\texpectedScore      string\n\t}{\n\t\t{\n\t\t\tname:               \"Returns Pepper's score\",\n\t\t\tplayer:             \"Pepper\",\n\t\t\texpectedHTTPStatus: http.StatusOK,\n\t\t\texpectedScore:      \"20\",\n\t\t},\n\t\t{\n\t\t\tname:               \"Returns Floyd's score\",\n\t\t\tplayer:             \"Floyd\",\n\t\t\texpectedHTTPStatus: http.StatusOK,\n\t\t\texpectedScore:      \"10\",\n\t\t},\n\t\t{\n\t\t\tname:               \"Returns 404 on missing players\",\n\t\t\tplayer:             \"Apollo\",\n\t\t\texpectedHTTPStatus: http.StatusNotFound,\n\t\t\texpectedScore:      \"0\",\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\trequest := newGetScoreRequest(tt.player)\n\t\t\tresponse := httptest.NewRecorder()\n\n\t\t\tserver.ServeHTTP(response, request)\n\n\t\t\tassertStatus(t, response.Code, tt.expectedHTTPStatus)\n\t\t\tassertResponseBody(t, response.Body.String(), tt.expectedScore)\n\t\t})\n\t}\n}\n\nfunc TestStoreWins(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{},\n\t\tnil,\n\t}\n\tserver := &PlayerServer{&store}\n\n\tt.Run(\"it records wins on POST\", func(t *testing.T) {\n\t\tplayer := \"Pepper\"\n\n\t\trequest := newPostWinRequest(player)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusAccepted)\n\n\t\tif len(store.winCalls) != 1 {\n\t\t\tt.Fatalf(\"got %d calls to RecordWin want %d\", len(store.winCalls), 1)\n\t\t}\n\n\t\tif store.winCalls[0] != player {\n\t\t\tt.Errorf(\"did not store correct winner got %q want %q\", store.winCalls[0], player)\n\t\t}\n\t})\n}\n\nfunc assertStatus(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"did not get correct status, got %d, want %d\", got, want)\n\t}\n}\n\nfunc newGetScoreRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc newPostWinRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodPost, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc assertResponseBody(t testing.TB, got, want string) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"response body is wrong, got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "http-server.md",
    "content": "# HTTP Server\n\n**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/main/http-server)**\n\nYou have been asked to create a web server where users can track how many games players have won.\n\n-   `GET /players/{name}` should return a number indicating the total number of wins\n-   `POST /players/{name}` should record a win for that name, incrementing for every subsequent `POST`\n\nWe will follow the TDD approach, getting working software as quickly as we can and then making small iterative improvements until we have the solution. By taking this approach we\n\n-   Keep the problem space small at any given time\n-   Don't go down rabbit holes\n-   If we ever get stuck/lost, doing a revert wouldn't lose loads of work.\n\n## Red, green, refactor\n\nThroughout this book, we have emphasised the TDD process of write a test & watch it fail (red), write the _minimal_ amount of code to make it work (green) and then refactor.\n\nThis discipline of writing the minimal amount of code is important in terms of the safety TDD gives you. You should be striving to get out of \"red\" as soon as you can.\n\nKent Beck describes it as:\n\n> Make the test work quickly, committing whatever sins necessary in process.\n\nYou can commit these sins because you will refactor afterwards backed by the safety of the tests.\n\n### What if you don't do this?\n\nThe more changes you make while in red, the more likely you are to add more problems, not covered by tests.\n\nThe idea is to be iteratively writing useful code with small steps, driven by tests so that you don't fall into a rabbit hole for hours.\n\n### Chicken and egg\n\nHow can we incrementally build this? We can't `GET` a player without having stored something and it seems hard to know if `POST` has worked without the `GET` endpoint already existing.\n\nThis is where _mocking_ shines.\n\n-   `GET` will need a `PlayerStore` _thing_ to get scores for a player. This should be an interface so when we test we can create a simple stub to test our code without needing to have implemented any actual storage code.\n-   For `POST` we can _spy_ on its calls to `PlayerStore` to make sure it stores players correctly. Our implementation of saving won't be coupled to retrieval.\n-   For having some working software quickly we can make a very simple in-memory implementation and then later we can create an implementation backed by whatever storage mechanism we prefer.\n\n## Write the test first\n\nWe can write a test and make it pass by returning a hard-coded value to get us started. Kent Beck refers this as \"Faking it\". Once we have a working test we can then write more tests to help us remove that constant.\n\nBy doing this very small step, we can make the important start of getting an overall project structure working correctly without having to worry too much about our application logic.\n\nTo create a web server in Go you will typically call [ListenAndServe](https://golang.org/pkg/net/http/#ListenAndServe).\n\n```go\nfunc ListenAndServe(addr string, handler Handler) error\n```\n\nThis will start a web server listening on a port, creating a goroutine for every request and running it against a [`Handler`](https://golang.org/pkg/net/http/#Handler).\n\n```go\ntype Handler interface {\n\tServeHTTP(ResponseWriter, *Request)\n}\n```\n\nA type implements the Handler interface by implementing the `ServeHTTP` method which expects two arguments, the first is where we _write our response_ and the second is the HTTP request that was sent to the server.\n\nLet's create a file named `server_test.go` and write a test for a function `PlayerServer` that takes in those two arguments. The request sent in will be to get a player's score, which we expect to be `\"20\"`.\n```go\nfunc TestGETPlayers(t *testing.T) {\n\tt.Run(\"returns Pepper's score\", func(t *testing.T) {\n\t\trequest, _ := http.NewRequest(http.MethodGet, \"/players/Pepper\", nil)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tPlayerServer(response, request)\n\n\t\tgot := response.Body.String()\n\t\twant := \"20\"\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %q, want %q\", got, want)\n\t\t}\n\t})\n}\n```\n\nIn order to test our server, we will need a `Request` to send in and we'll want to _spy_ on what our handler writes to the `ResponseWriter`.\n\n-   We use `http.NewRequest` to create a request. The first argument is the request's method and the second is the request's path. The `nil` argument refers to the request's body, which we don't need to set in this case.\n-   `net/http/httptest` has a spy already made for us called `ResponseRecorder` so we can use that. It has many helpful methods to inspect what has been written as a response.\n\n## Try to run the test\n\n`./server_test.go:13:2: undefined: PlayerServer`\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\nThe compiler is here to help, just listen to it.\n\nCreate a file named `server.go` and define `PlayerServer`\n\n```go\nfunc PlayerServer() {}\n```\n\nTry again\n\n```\n./server_test.go:13:14: too many arguments in call to PlayerServer\n    have (*httptest.ResponseRecorder, *http.Request)\n    want ()\n```\n\nAdd the arguments to our function\n\n```go\nimport \"net/http\"\n\nfunc PlayerServer(w http.ResponseWriter, r *http.Request) {\n\n}\n```\n\nThe code now compiles and the test fails\n\n```\n=== RUN   TestGETPlayers/returns_Pepper's_score\n    --- FAIL: TestGETPlayers/returns_Pepper's_score (0.00s)\n        server_test.go:20: got '', want '20'\n```\n\n## Write enough code to make it pass\n\nFrom the DI chapter, we touched on HTTP servers with a `Greet` function. We learned that net/http's `ResponseWriter` also implements io `Writer` so we can use `fmt.Fprint` to send strings as HTTP responses.\n\n```go\nfunc PlayerServer(w http.ResponseWriter, r *http.Request) {\n\tfmt.Fprint(w, \"20\")\n}\n```\n\nThe test should now pass.\n\n## Complete the scaffolding\n\nWe want to wire this up into an application. This is important because\n\n-   We'll have _actual working software_, we don't want to write tests for the sake of it, it's good to see the code in action.\n-   As we refactor our code, it's likely we will change the structure of the program. We want to make sure this is reflected in our application too as part of the incremental approach.\n\nCreate a new `main.go` file for our application and put this code in\n\n```go\npackage main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc main() {\n\thandler := http.HandlerFunc(PlayerServer)\n\tlog.Fatal(http.ListenAndServe(\":5000\", handler))\n}\n```\n\nSo far all of our application code has been in one file, however, this isn't best practice for larger projects where you'll want to separate things into different files.\n\nTo run this, do `go build` which will take all the `.go` files in the directory and build you a program. You can then execute it with `./myprogram`.\n\n### `http.HandlerFunc`\n\nEarlier we explored that the `Handler` interface is what we need to implement in order to make a server. _Typically_ we do that by creating a `struct` and make it implement the interface by implementing its own ServeHTTP method. However the use-case for structs is for holding data but _currently_ we have no state, so it doesn't feel right to be creating one.\n\n[HandlerFunc](https://golang.org/pkg/net/http/#HandlerFunc) lets us avoid this.\n\n> The HandlerFunc type is an adapter to allow the use of ordinary functions as HTTP handlers. If f is a function with the appropriate signature, HandlerFunc(f) is a Handler that calls f.\n\n```go\ntype HandlerFunc func(ResponseWriter, *Request)\n```\n\nFrom the documentation, we see that type `HandlerFunc` has already implemented the `ServeHTTP` method.\nBy type casting our `PlayerServer` function with it, we have now implemented the required `Handler`.\n\n### `http.ListenAndServe(\":5000\"...)`\n\n`ListenAndServe` takes a port to listen on a `Handler`. If there is a problem the web server will return an error, an example of that might be the port already being listened to. For that reason we wrap the call in `log.Fatal` to log the error to the user.\n\nWhat we're going to do now is write _another_ test to force us into making a positive change to try and move away from the hard-coded value.\n\n## Write the test first\n\nWe'll add another subtest to our suite which tries to get the score of a different player, which will break our hard-coded approach.\n\n```go\nt.Run(\"returns Floyd's score\", func(t *testing.T) {\n\trequest, _ := http.NewRequest(http.MethodGet, \"/players/Floyd\", nil)\n\tresponse := httptest.NewRecorder()\n\n\tPlayerServer(response, request)\n\n\tgot := response.Body.String()\n\twant := \"10\"\n\n\tif got != want {\n\t\tt.Errorf(\"got %q, want %q\", got, want)\n\t}\n})\n```\n\nYou may have been thinking\n\n> Surely we need some kind of concept of storage to control which player gets what score. It's weird that the values seem so arbitrary in our tests.\n\nRemember we are just trying to take as small as steps as reasonably possible, so we're just trying to break the constant for now.\n\n## Try to run the test\n\n```\n=== RUN   TestGETPlayers/returns_Pepper's_score\n    --- PASS: TestGETPlayers/returns_Pepper's_score (0.00s)\n=== RUN   TestGETPlayers/returns_Floyd's_score\n    --- FAIL: TestGETPlayers/returns_Floyd's_score (0.00s)\n        server_test.go:34: got '20', want '10'\n```\n\n## Write enough code to make it pass\n\n```go\n//server.go\nfunc PlayerServer(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\n\tif player == \"Pepper\" {\n\t\tfmt.Fprint(w, \"20\")\n\t\treturn\n\t}\n\n\tif player == \"Floyd\" {\n\t\tfmt.Fprint(w, \"10\")\n\t\treturn\n\t}\n}\n```\n\nThis test has forced us to actually look at the request's URL and make a decision. So whilst in our heads, we may have been worrying about player stores and interfaces the next logical step actually seems to be about _routing_.\n\nIf we had started with the store code the amount of changes we'd have to do would be very large compared to this. **This is a smaller step towards our final goal and was driven by tests**.\n\nWe're resisting the temptation to use any routing libraries right now, just the smallest step to get our test passing.\n\n`r.URL.Path` returns the path of the request which we can then use [`strings.TrimPrefix`](https://golang.org/pkg/strings/#TrimPrefix) to trim away `/players/` to get the requested player. It's not very robust but will do the trick for now.\n\n## Refactor\n\nWe can simplify the `PlayerServer` by separating out the score retrieval into a function\n\n```go\n//server.go\nfunc PlayerServer(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\n\tfmt.Fprint(w, GetPlayerScore(player))\n}\n\nfunc GetPlayerScore(name string) string {\n\tif name == \"Pepper\" {\n\t\treturn \"20\"\n\t}\n\n\tif name == \"Floyd\" {\n\t\treturn \"10\"\n\t}\n\n\treturn \"\"\n}\n```\n\nAnd we can DRY up some of the code in the tests by making some helpers\n\n```go\n//server_test.go\nfunc TestGETPlayers(t *testing.T) {\n\tt.Run(\"returns Pepper's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Pepper\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tPlayerServer(response, request)\n\n\t\tassertResponseBody(t, response.Body.String(), \"20\")\n\t})\n\n\tt.Run(\"returns Floyd's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Floyd\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tPlayerServer(response, request)\n\n\t\tassertResponseBody(t, response.Body.String(), \"10\")\n\t})\n}\n\nfunc newGetScoreRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc assertResponseBody(t testing.TB, got, want string) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"response body is wrong, got %q want %q\", got, want)\n\t}\n}\n```\n\nHowever, we still shouldn't be happy. It doesn't feel right that our server knows the scores.\n\nOur refactoring has made it pretty clear what to do.\n\nWe moved the score calculation out of the main body of our handler into a function `GetPlayerScore`. This feels like the right place to separate the concerns using interfaces.\n\nLet's move our function we re-factored to be an interface instead\n\n```go\ntype PlayerStore interface {\n\tGetPlayerScore(name string) int\n}\n```\n\nFor our `PlayerServer` to be able to use a `PlayerStore`, it will need a reference to one. Now feels like the right time to change our architecture so that our `PlayerServer` is now a `struct`.\n\n```go\ntype PlayerServer struct {\n\tstore PlayerStore\n}\n```\n\nFinally, we will now implement the `Handler` interface by adding a method to our new struct and putting in our existing handler code.\n\n```go\nfunc (p *PlayerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\tfmt.Fprint(w, p.store.GetPlayerScore(player))\n}\n```\n\nThe only other change is we now call our `store.GetPlayerScore` to get the score, rather than the local function we defined (which we can now delete).\n\nHere is the full code listing of our server\n\n```go\n//server.go\ntype PlayerStore interface {\n\tGetPlayerScore(name string) int\n}\n\ntype PlayerServer struct {\n\tstore PlayerStore\n}\n\nfunc (p *PlayerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\tfmt.Fprint(w, p.store.GetPlayerScore(player))\n}\n```\n\n### Fix the issues\n\nThis was quite a few changes and we know our tests and application will no longer compile, but just relax and let the compiler work through it.\n\n`./main.go:9:58: type PlayerServer is not an expression`\n\nWe need to change our tests to instead create a new instance of our `PlayerServer` and then call its method `ServeHTTP`.\n\n```go\n//server_test.go\nfunc TestGETPlayers(t *testing.T) {\n\tserver := &PlayerServer{}\n\n\tt.Run(\"returns Pepper's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Pepper\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertResponseBody(t, response.Body.String(), \"20\")\n\t})\n\n\tt.Run(\"returns Floyd's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Floyd\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertResponseBody(t, response.Body.String(), \"10\")\n\t})\n}\n```\n\nNotice we're still not worrying about making stores _just yet_, we just want the compiler passing as soon as we can.\n\nYou should be in the habit of prioritising having code that compiles and then code that passes the tests.\n\nBy adding more functionality (like stub stores) whilst the code isn't compiling, we are opening ourselves up to potentially _more_ compilation problems.\n\nNow `main.go` won't compile for the same reason.\n\n```go\nfunc main() {\n\tserver := &PlayerServer{}\n\tlog.Fatal(http.ListenAndServe(\":5000\", server))\n}\n```\n\nFinally, everything is compiling but the tests are failing\n\n```\n=== RUN   TestGETPlayers/returns_the_Pepper's_score\npanic: runtime error: invalid memory address or nil pointer dereference [recovered]\n    panic: runtime error: invalid memory address or nil pointer dereference\n```\n\nThis is because we have not passed in a `PlayerStore` in our tests. We'll need to make a stub one up.\n\n```go\n//server_test.go\ntype StubPlayerStore struct {\n\tscores map[string]int\n}\n\nfunc (s *StubPlayerStore) GetPlayerScore(name string) int {\n\tscore := s.scores[name]\n\treturn score\n}\n```\n\nA `map` is a quick and easy way of making a stub key/value store for our tests. Now let's create one of these stores for our tests and send it into our `PlayerServer`.\n\n```go\n//server_test.go\nfunc TestGETPlayers(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{\n\t\t\t\"Pepper\": 20,\n\t\t\t\"Floyd\":  10,\n\t\t},\n\t}\n\tserver := &PlayerServer{&store}\n\n\tt.Run(\"returns Pepper's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Pepper\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertResponseBody(t, response.Body.String(), \"20\")\n\t})\n\n\tt.Run(\"returns Floyd's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Floyd\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertResponseBody(t, response.Body.String(), \"10\")\n\t})\n}\n```\n\nOur tests now pass and are looking better. The _intent_ behind our code is clearer now due to the introduction of the store. We're telling the reader that because we have _this data in a `PlayerStore`_ that when you use it with a `PlayerServer` you should get the following responses.\n\n### Run the application\n\nNow our tests are passing the last thing we need to do to complete this refactor is to check if our application is working. The program should start up but you'll get a horrible response if you try and hit the server at `http://localhost:5000/players/Pepper`.\n\nThe reason for this is that we have not passed in a `PlayerStore`.\n\nWe'll need to make an implementation of one, but that's difficult right now as we're not storing any meaningful data so it'll have to be hard-coded for the time being.\n\n```go\n//main.go\ntype InMemoryPlayerStore struct{}\n\nfunc (i *InMemoryPlayerStore) GetPlayerScore(name string) int {\n\treturn 123\n}\n\nfunc main() {\n\tserver := &PlayerServer{&InMemoryPlayerStore{}}\n\tlog.Fatal(http.ListenAndServe(\":5000\", server))\n}\n```\n\nIf you run `go build` again and hit the same URL you should get `\"123\"`. Not great, but until we store data that's the best we can do.\nIt also didn't feel great that our main application was starting up but not actually working. We had to manually test to see the problem.\n\nWe have a few options as to what to do next\n\n-   Handle the scenario where the player doesn't exist\n-   Handle the `POST /players/{name}` scenario\n\nWhilst the `POST` scenario gets us closer to the \"happy path\", I feel it'll be easier to tackle the missing player scenario first as we're in that context already. We'll get to the rest later.\n\n## Write the test first\n\nAdd a missing player scenario to our existing suite\n\n```go\n//server_test.go\nt.Run(\"returns 404 on missing players\", func(t *testing.T) {\n\trequest := newGetScoreRequest(\"Apollo\")\n\tresponse := httptest.NewRecorder()\n\n\tserver.ServeHTTP(response, request)\n\n\tgot := response.Code\n\twant := http.StatusNotFound\n\n\tif got != want {\n\t\tt.Errorf(\"got status %d want %d\", got, want)\n\t}\n})\n```\n\n## Try to run the test\n\n```\n=== RUN   TestGETPlayers/returns_404_on_missing_players\n    --- FAIL: TestGETPlayers/returns_404_on_missing_players (0.00s)\n        server_test.go:56: got status 200 want 404\n```\n\n## Write enough code to make it pass\n\n```go\n//server.go\nfunc (p *PlayerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\n\tw.WriteHeader(http.StatusNotFound)\n\n\tfmt.Fprint(w, p.store.GetPlayerScore(player))\n}\n```\n\nSometimes I heavily roll my eyes when TDD advocates say \"make sure you just write the minimal amount of code to make it pass\" as it can feel very pedantic.\n\nBut this scenario illustrates the example well. I have done the bare minimum (knowing it is not correct), which is write a `StatusNotFound` on **all responses** but all our tests are passing!\n\n**By doing the bare minimum to make the tests pass it can highlight gaps in your tests**. In our case, we are not asserting that we should be getting a `StatusOK` when players _do_ exist in the store.\n\nUpdate the other two tests to assert on the status and fix the code.\n\nHere are the new tests\n\n```go\n//server_test.go\nfunc TestGETPlayers(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{\n\t\t\t\"Pepper\": 20,\n\t\t\t\"Floyd\":  10,\n\t\t},\n\t}\n\tserver := &PlayerServer{&store}\n\n\tt.Run(\"returns Pepper's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Pepper\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"20\")\n\t})\n\n\tt.Run(\"returns Floyd's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Floyd\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"10\")\n\t})\n\n\tt.Run(\"returns 404 on missing players\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Apollo\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusNotFound)\n\t})\n}\n\nfunc assertStatus(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"did not get correct status, got %d, want %d\", got, want)\n\t}\n}\n\nfunc newGetScoreRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc assertResponseBody(t testing.TB, got, want string) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"response body is wrong, got %q want %q\", got, want)\n\t}\n}\n```\n\nWe're checking the status in all our tests now so I made a helper `assertStatus` to facilitate that.\n\nNow our first two tests fail because of the 404 instead of 200, so we can fix `PlayerServer` to only return not found if the score is 0.\n\n```go\n//server.go\nfunc (p *PlayerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\n\tscore := p.store.GetPlayerScore(player)\n\n\tif score == 0 {\n\t\tw.WriteHeader(http.StatusNotFound)\n\t}\n\n\tfmt.Fprint(w, score)\n}\n```\n\n### Storing scores\n\nNow that we can retrieve scores from a store it now makes sense to be able to store new scores.\n\n## Write the test first\n\n```go\n//server_test.go\nfunc TestStoreWins(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{},\n\t}\n\tserver := &PlayerServer{&store}\n\n\tt.Run(\"it returns accepted on POST\", func(t *testing.T) {\n\t\trequest, _ := http.NewRequest(http.MethodPost, \"/players/Pepper\", nil)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusAccepted)\n\t})\n}\n```\n\nFor a start let's just check we get the correct status code if we hit the particular route with POST. This lets us drive out the functionality of accepting a different kind of request and handling it differently to `GET /players/{name}`. Once this works we can then start asserting on our handler's interaction with the store.\n\n## Try to run the test\n\n```\n=== RUN   TestStoreWins/it_returns_accepted_on_POST\n    --- FAIL: TestStoreWins/it_returns_accepted_on_POST (0.00s)\n        server_test.go:70: did not get correct status, got 404, want 202\n```\n\n## Write enough code to make it pass\n\nRemember we are deliberately committing sins, so an `if` statement based on the request's method will do the trick.\n\n```go\n//server.go\nfunc (p *PlayerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\n\tif r.Method == http.MethodPost {\n\t\tw.WriteHeader(http.StatusAccepted)\n\t\treturn\n\t}\n\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\n\tscore := p.store.GetPlayerScore(player)\n\n\tif score == 0 {\n\t\tw.WriteHeader(http.StatusNotFound)\n\t}\n\n\tfmt.Fprint(w, score)\n}\n```\n\n## Refactor\n\nThe handler is looking a bit muddled now. Let's break the code up to make it easier to follow and isolate the different functionality into new functions.\n\n```go\n//server.go\nfunc (p *PlayerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\n\tswitch r.Method {\n\tcase http.MethodPost:\n\t\tp.processWin(w)\n\tcase http.MethodGet:\n\t\tp.showScore(w, r)\n\t}\n\n}\n\nfunc (p *PlayerServer) showScore(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\n\tscore := p.store.GetPlayerScore(player)\n\n\tif score == 0 {\n\t\tw.WriteHeader(http.StatusNotFound)\n\t}\n\n\tfmt.Fprint(w, score)\n}\n\nfunc (p *PlayerServer) processWin(w http.ResponseWriter) {\n\tw.WriteHeader(http.StatusAccepted)\n}\n```\n\nThis makes the routing aspect of `ServeHTTP` a bit clearer and means our next iterations on storing can just be inside `processWin`.\n\nNext, we want to check that when we do our `POST /players/{name}` that our `PlayerStore` is told to record the win.\n\n## Write the test first\n\nWe can accomplish this by extending our `StubPlayerStore` with a new `RecordWin` method and then spy on its invocations.\n\n```go\n//server_test.go\ntype StubPlayerStore struct {\n\tscores   map[string]int\n\twinCalls []string\n}\n\nfunc (s *StubPlayerStore) GetPlayerScore(name string) int {\n\tscore := s.scores[name]\n\treturn score\n}\n\nfunc (s *StubPlayerStore) RecordWin(name string) {\n\ts.winCalls = append(s.winCalls, name)\n}\n```\n\nNow extend our test to check the number of invocations for a start\n\n```go\n//server_test.go\nfunc TestStoreWins(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{},\n\t}\n\tserver := &PlayerServer{&store}\n\n\tt.Run(\"it records wins when POST\", func(t *testing.T) {\n\t\trequest := newPostWinRequest(\"Pepper\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusAccepted)\n\n\t\tif len(store.winCalls) != 1 {\n\t\t\tt.Errorf(\"got %d calls to RecordWin want %d\", len(store.winCalls), 1)\n\t\t}\n\t})\n}\n\nfunc newPostWinRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodPost, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n```\n\n## Try to run the test\n\n```\n./server_test.go:26:20: too few values in struct initializer\n./server_test.go:65:20: too few values in struct initializer\n```\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\nWe need to update our code where we create a `StubPlayerStore` as we've added a new field\n\n```go\n//server_test.go\nstore := StubPlayerStore{\n\tmap[string]int{},\n\tnil,\n}\n```\n\n```\n--- FAIL: TestStoreWins (0.00s)\n    --- FAIL: TestStoreWins/it_records_wins_when_POST (0.00s)\n        server_test.go:80: got 0 calls to RecordWin want 1\n```\n\n## Write enough code to make it pass\n\nAs we're only asserting the number of calls rather than the specific values it makes our initial iteration a little smaller.\n\nWe need to update `PlayerServer`'s idea of what a `PlayerStore` is by changing the interface if we're going to be able to call `RecordWin`.\n\n```go\n//server.go\ntype PlayerStore interface {\n\tGetPlayerScore(name string) int\n\tRecordWin(name string)\n}\n```\n\nBy doing this `main` no longer compiles\n\n```\n./main.go:17:46: cannot use InMemoryPlayerStore literal (type *InMemoryPlayerStore) as type PlayerStore in field value:\n    *InMemoryPlayerStore does not implement PlayerStore (missing RecordWin method)\n```\n\nThe compiler tells us what's wrong. Let's update `InMemoryPlayerStore` to have that method.\n\n```go\n//main.go\ntype InMemoryPlayerStore struct{}\n\nfunc (i *InMemoryPlayerStore) RecordWin(name string) {}\n```\n\nTry and run the tests and we should be back to compiling code - but the test is still failing.\n\nNow that `PlayerStore` has `RecordWin` we can call it within our `PlayerServer`\n\n```go\n//server.go\nfunc (p *PlayerServer) processWin(w http.ResponseWriter) {\n\tp.store.RecordWin(\"Bob\")\n\tw.WriteHeader(http.StatusAccepted)\n}\n```\n\nRun the tests and it should be passing! Obviously `\"Bob\"` isn't exactly what we want to send to `RecordWin`, so let's further refine the test.\n\n## Write the test first\n\n```go\n//server_test.go\nfunc TestStoreWins(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{},\n\t\tnil,\n\t}\n\tserver := &PlayerServer{&store}\n\n\tt.Run(\"it records wins on POST\", func(t *testing.T) {\n\t\tplayer := \"Pepper\"\n\n\t\trequest := newPostWinRequest(player)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusAccepted)\n\n\t\tif len(store.winCalls) != 1 {\n\t\t\tt.Fatalf(\"got %d calls to RecordWin want %d\", len(store.winCalls), 1)\n\t\t}\n\n\t\tif store.winCalls[0] != player {\n\t\t\tt.Errorf(\"did not store correct winner got %q want %q\", store.winCalls[0], player)\n\t\t}\n\t})\n}\n```\n\nNow that we know there is one element in our `winCalls` slice we can safely reference the first one and check it is equal to `player`.\n\n## Try to run the test\n\n```\n=== RUN   TestStoreWins/it_records_wins_on_POST\n    --- FAIL: TestStoreWins/it_records_wins_on_POST (0.00s)\n        server_test.go:86: did not store correct winner got 'Bob' want 'Pepper'\n```\n\n## Write enough code to make it pass\n\n```go\n//server.go\nfunc (p *PlayerServer) processWin(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\tp.store.RecordWin(player)\n\tw.WriteHeader(http.StatusAccepted)\n}\n```\n\nWe changed `processWin` to take `http.Request` so we can look at the URL to extract the player's name. Once we have that we can call our `store` with the correct value to make the test pass.\n\n## Refactor\n\nWe can DRY up this code a bit as we're extracting the player name the same way in two places\n\n```go\n//server.go\nfunc (p *PlayerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\n\tswitch r.Method {\n\tcase http.MethodPost:\n\t\tp.processWin(w, player)\n\tcase http.MethodGet:\n\t\tp.showScore(w, player)\n\t}\n}\n\nfunc (p *PlayerServer) showScore(w http.ResponseWriter, player string) {\n\tscore := p.store.GetPlayerScore(player)\n\n\tif score == 0 {\n\t\tw.WriteHeader(http.StatusNotFound)\n\t}\n\n\tfmt.Fprint(w, score)\n}\n\nfunc (p *PlayerServer) processWin(w http.ResponseWriter, player string) {\n\tp.store.RecordWin(player)\n\tw.WriteHeader(http.StatusAccepted)\n}\n```\n\nEven though our tests are passing we don't really have working software. If you try and run `main` and use the software as intended it doesn't work because we haven't got round to implementing `PlayerStore` correctly. This is fine though; by focusing on our handler we have identified the interface that we need, rather than trying to design it up-front.\n\nWe _could_ start writing some tests around our `InMemoryPlayerStore` but it's only here temporarily until we implement a more robust way of persisting player scores (i.e. a database).\n\nWhat we'll do for now is write an _integration test_ between our `PlayerServer` and `InMemoryPlayerStore` to finish off the functionality. This will let us get to our goal of being confident our application is working, without having to directly test `InMemoryPlayerStore`. Not only that, but when we get around to implementing `PlayerStore` with a database, we can test that implementation with the same integration test.\n\n### Integration tests\n\nIntegration tests can be useful for testing that larger areas of your system work but you must bear in mind:\n\n-   They are harder to write\n-   When they fail, it can be difficult to know why (usually it's a bug within a component of the integration test) and so can be harder to fix\n-   They are sometimes slower to run (as they often are used with \"real\" components, like a database)\n\nFor that reason, it is recommended that you research _The Test Pyramid_.\n\n## Write the test first\n\nIn the interest of brevity, I am going to show you the final refactored integration test.\n\n```go\n// server_integration_test.go\npackage main\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestRecordingWinsAndRetrievingThem(t *testing.T) {\n\tstore := InMemoryPlayerStore{}\n\tserver := PlayerServer{&store}\n\tplayer := \"Pepper\"\n\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\n\tresponse := httptest.NewRecorder()\n\tserver.ServeHTTP(response, newGetScoreRequest(player))\n\tassertStatus(t, response.Code, http.StatusOK)\n\n\tassertResponseBody(t, response.Body.String(), \"3\")\n}\n```\n\n-   We are creating our two components we are trying to integrate with: `InMemoryPlayerStore` and `PlayerServer`.\n-   We then fire off 3 requests to record 3 wins for `player`. We're not too concerned about the status codes in this test as it's not relevant to whether they are integrating well.\n-   The next response we do care about (so we store a variable `response`) because we are going to try and get the `player`'s score.\n\n## Try to run the test\n\n```\n--- FAIL: TestRecordingWinsAndRetrievingThem (0.00s)\n    server_integration_test.go:24: response body is wrong, got '123' want '3'\n```\n\n## Write enough code to make it pass\n\nI am going to take some liberties here and write more code than you may be comfortable with without writing a test.\n\n_This is allowed!_ We still have a test checking things should be working correctly but it is not around the specific unit we're working with (`InMemoryPlayerStore`).\n\nIf I were to get stuck in this scenario, I would revert my changes back to the failing test and then write more specific unit tests around `InMemoryPlayerStore` to help me drive out a solution.\n\n```go\n//in_memory_player_store.go\nfunc NewInMemoryPlayerStore() *InMemoryPlayerStore {\n\treturn &InMemoryPlayerStore{map[string]int{}}\n}\n\ntype InMemoryPlayerStore struct {\n\tstore map[string]int\n}\n\nfunc (i *InMemoryPlayerStore) RecordWin(name string) {\n\ti.store[name]++\n}\n\nfunc (i *InMemoryPlayerStore) GetPlayerScore(name string) int {\n\treturn i.store[name]\n}\n```\n\n-   We need to store the data so I've added a `map[string]int` to the `InMemoryPlayerStore` struct\n-   For convenience I've made `NewInMemoryPlayerStore` to initialise the store, and updated the integration test to use it:\n    ```go\n    //server_integration_test.go\n    store := NewInMemoryPlayerStore()\n    server := PlayerServer{store}\n    ```\n-   The rest of the code is just wrapping around the `map`\n\nThe integration test passes, now we just need to change `main` to use `NewInMemoryPlayerStore()`\n\n```go\n// main.go\npackage main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc main() {\n\tserver := &PlayerServer{NewInMemoryPlayerStore()}\n\tlog.Fatal(http.ListenAndServe(\":5000\", server))\n}\n```\n\nBuild it, run it and then use `curl` to test it out.\n\n-   Run this a few times, change the player names if you like `curl -X POST http://localhost:5000/players/Pepper`\n-   Check scores with `curl http://localhost:5000/players/Pepper`\n\nGreat! You've made a REST-ish service. To take this forward you'd want to pick a data store to persist the scores longer than the length of time the program runs.\n\n-   Pick a store (Bolt? Mongo? Postgres? File system?)\n-   Make `PostgresPlayerStore` implement `PlayerStore`\n-   TDD the functionality so you're sure it works\n-   Plug it into the integration test, check it's still ok\n-   Finally plug it into `main`\n\n## Refactor\n\nWe are almost there! Lets take some effort to prevent concurrency errors like these\n\n```\nfatal error: concurrent map read and map write\n```\n\nBy adding mutexes, we enforce concurrency safety especially for the counter in our `RecordWin` function. Read more about mutexes in the sync chapter.\n\n## Wrapping up\n\n### `http.Handler`\n\n-   Implement this interface to create web servers\n-   Use `http.HandlerFunc` to turn ordinary functions into `http.Handler`s\n-   Use `httptest.NewRecorder` to pass in as a `ResponseWriter` to let you spy on the responses your handler sends\n-   Use `http.NewRequest` to construct the requests you expect to come in to your system\n\n### Interfaces, Mocking and DI\n\n-   Lets you iteratively build the system up in smaller chunks\n-   Allows you to develop a handler that needs a storage without needing actual storage\n-   TDD to drive out the interfaces you need\n\n### Commit sins, then refactor (and then commit to source control)\n\n-   You need to treat having failing compilation or failing tests as a red situation that you need to get out of as soon as you can.\n-   Write just the necessary code to get there. _Then_ refactor and make the code nice.\n-   By trying to do too many changes whilst the code isn't compiling or the tests are failing puts you at risk of compounding the problems.\n-   Sticking to this approach forces you to write small tests, which means small changes, which helps keep working on complex systems manageable.\n"
  },
  {
    "path": "install-go.md",
    "content": "# Install Go, set up environment for productivity\n\nThe official installation instructions for Go are available [here](https://golang.org/doc/install).\n\n## Go Environment\n\n### Go Modules\n\nGo 1.11 introduced [Modules](https://go.dev/wiki/Modules). This approach is the default build mode since Go 1.16, therefore the use of `GOPATH` is not recommended.\n\nModules aim to solve problems related to dependency management, version selection and reproducible builds; they also enable users to run Go code outside of `GOPATH`.\n\nUsing Modules is pretty straightforward. Select any directory outside `GOPATH` as the root of your project, and create a new module with the `go mod init` command.\n\nA `go.mod` file will be generated, containing the module path, a Go version, and its dependency requirements, which are the other modules needed for a successful build.\n\nIf no `<modulepath>` is specified, `go mod init` will try to guess the module path from the directory structure. It can also be overridden by supplying an argument.\n\n```sh\nmkdir my-project\ncd my-project\ngo mod init <modulepath>\n```\n\nA `go.mod` file could look like this:\n\n```\nmodule cmd\n\ngo 1.16\n\n```\n\nThe built-in documentation provides an overview of all available `go mod` commands.\n\n```sh\ngo help mod\ngo help mod init\n```\n\n## Go Linting\n\nAn improvement over the default linter can be configured using [GolangCI-Lint](https://golangci-lint.run).\n\nThis can be installed as follows:\n\n```sh\nbrew install golangci-lint\n```\n\n## Refactoring and your tooling\n\nA big emphasis of this book is the importance of refactoring.\n\nYour tools can help you do bigger refactoring with confidence.\n\nYou should be familiar enough with your editor to perform the following with a simple key combination:\n\n- **Extract/Inline variable**. Taking magic values and giving them a name lets you simplify your code quickly.\n- **Extract method/function**. It is vital to be able to take a section of code and extract functions/methods\n- **Rename**. You should be able to rename symbols across files confidently.\n- **go fmt**. Go has an opinioned formatter called `go fmt`. Your editor should run this on every file saved.\n- **Run tests**. You should be able to do any of the above and then quickly re-run your tests to ensure your refactoring hasn't broken anything.\n\nIn addition, to help you work with your code, you should be able to:\n\n- **View function signature**. You should never be unsure how to call a function in Go. Your IDE should describe a function in terms of its documentation, its parameters and what it returns.\n- **View function definition**. If it's still unclear what a function does, you should be able to jump to the source code and try and figure it out yourself.\n- **Find usages of a symbol**. Understanding a function's context can help you make decisions when refactoring.\n\nMastering your tools will help you concentrate on the code and reduce context switching.\n\n## Wrapping up\n\nAt this point, you should have Go installed, an editor available, and some basic tooling in place. Go has a very large ecosystem of third-party products. We have identified a few useful components here. For a more complete list, see [https://awesome-go.com](https://awesome-go.com).\n"
  },
  {
    "path": "integers/v1/adder.go",
    "content": "package integers\n\n// Add takes two integers and returns the sum of them.\nfunc Add(x, y int) int {\n\treturn x + y\n}\n"
  },
  {
    "path": "integers/v1/adder_test.go",
    "content": "package integers\n\nimport \"testing\"\n\nfunc TestAdder(t *testing.T) {\n\tsum := Add(2, 2)\n\texpected := 4\n\n\tif sum != expected {\n\t\tt.Errorf(\"expected '%d' but got '%d'\", expected, sum)\n\t}\n}\n"
  },
  {
    "path": "integers/v2/adder.go",
    "content": "package integers\n\n// Add takes two integers and returns the sum of them.\nfunc Add(x, y int) int {\n\treturn x + y\n}\n"
  },
  {
    "path": "integers/v2/adder_test.go",
    "content": "package integers\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n)\n\nfunc TestAdder(t *testing.T) {\n\tsum := Add(2, 2)\n\texpected := 4\n\n\tif sum != expected {\n\t\tt.Errorf(\"expected '%d' but got '%d'\", expected, sum)\n\t}\n}\n\nfunc ExampleAdd() {\n\tsum := Add(1, 5)\n\tfmt.Println(sum)\n\t// Output: 6\n}\n"
  },
  {
    "path": "integers.md",
    "content": "# Integers\n\n**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/main/integers)**\n\nIntegers work as you would expect. Let's write an `Add` function to try things out. Create a test file called `adder_test.go` and write this code.\n\n**Note:** Go source files can only have one `package` per directory. Make sure that your files are organised into their own packages. [Here is a good explanation on this.](https://dave.cheney.net/2014/12/01/five-suggestions-for-setting-up-a-go-project)\n\nYour project directory might look something like this:\n\n```\nlearnGoWithTests\n    |\n    |-> helloworld\n    |    |- hello.go\n    |    |- hello_test.go\n    |\n    |-> integers\n    |    |- adder_test.go\n    |\n    |- go.mod\n    |- README.md\n```\n\n## Write the test first\n\n```go\npackage integers\n\nimport \"testing\"\n\nfunc TestAdder(t *testing.T) {\n\tsum := Add(2, 2)\n\texpected := 4\n\n\tif sum != expected {\n\t\tt.Errorf(\"expected '%d' but got '%d'\", expected, sum)\n\t}\n}\n```\n\nYou will notice that we're using `%d` as our format strings rather than `%q`. That's because we want it to print an integer rather than a string.\n\nAlso note that we are no longer using the main package, instead we've defined a package named `integers`, as the name suggests this will group functions for working with integers such as `Add`.\n\n## Try and run the test\n\nRun the test `go test`\n\nInspect the compilation error\n\n`./adder_test.go:6:9: undefined: Add`\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\nWrite enough code to satisfy the compiler _and that's all_ - remember we want to check that our tests fail for the correct reason.\n\n```go\npackage integers\n\nfunc Add(x, y int) int {\n\treturn 0\n}\n```\n\nRemember, when you have more than one argument of the same type \\(in our case two integers\\) rather than having `(x int, y int)` you can shorten it to `(x, y int)`.\n\nNow run the tests, and we should be happy that the test is correctly reporting what is wrong.\n\n`adder_test.go:10: expected '4' but got '0'`\n\nIf you have noticed we learnt about _named return value_ in the [last](hello-world.md#one...last...refactor?) section but aren't using the same here. It should generally be used when the meaning of the result isn't clear from context, in our case it's pretty much clear that `Add` function will add the parameters. You can refer [this](https://go.dev/wiki/CodeReviewComments#named-result-parameters) wiki for more details.\n\n## Write enough code to make it pass\n\nIn the strictest sense of TDD we should now write the _minimal amount of code to make the test pass_. A pedantic programmer may do this\n\n```go\nfunc Add(x, y int) int {\n\treturn 4\n}\n```\n\nAh hah! Foiled again, TDD is a sham right?\n\nWe could write another test, with some different numbers to force that test to fail but that feels like [a game of cat and mouse](https://en.m.wikipedia.org/wiki/Cat_and_mouse).\n\nOnce we're more familiar with Go's syntax I will introduce a technique called _\"Property Based Testing\"_, which would stop annoying developers and help you find bugs.\n\nFor now, let's fix it properly\n\n```go\nfunc Add(x, y int) int {\n\treturn x + y\n}\n```\n\nIf you re-run the tests they should pass.\n\n## Refactor\n\nThere's not a lot in the _actual_ code we can really improve on here.\n\nWe explored earlier how by naming the return argument it appears in the documentation but also in most developer's text editors.\n\nThis is great because it aids the usability of code you are writing. It is preferable that a user can understand the usage of your code by just looking at the type signature and documentation.\n\nYou can add documentation to functions with comments, and these will appear in Go Doc just like when you look at the standard library's documentation.\n\n```go\n// Add takes two integers and returns the sum of them.\nfunc Add(x, y int) int {\n\treturn x + y\n}\n```\n\n### Testable Examples\n\nIf you really want to go the extra mile you can make [Testable Examples](https://blog.golang.org/examples). You will find many examples in the standard library documentation.\n\nOften code examples that can be found outside the codebase, such as a readme file, become out of date and incorrect compared to the actual code because they don't get checked.\n\nExample functions are compiled whenever tests are executed. Because such examples are validated by the Go compiler, you can be confident your documentation's examples always reflect current code behavior.\n\nExample functions begin with `Example` (much like test functions begin with `Test`), and reside in a package's `_test.go` files. Add the following `ExampleAdd` function to the `adder_test.go` file.\n\n```go\nfunc ExampleAdd() {\n\tsum := Add(1, 5)\n\tfmt.Println(sum)\n\t// Output: 6\n}\n```\n\n(If your editor doesn't automatically import packages for you, the compilation step will fail because you will be missing `import \"fmt\"` in `adder_test.go`. It is strongly recommended you research how to have these kind of errors fixed for you automatically in whatever editor you are using.)\n\nAdding this code will cause the example to appear in your documentation, making your code even more accessible. If ever your code changes so that the example is no longer valid, your build will fail.\n\nRunning the package's test suite, we can see the example `ExampleAdd` function is executed with no further arrangement from us:\n\n```bash\n$ go test -v\n=== RUN   TestAdder\n--- PASS: TestAdder (0.00s)\n=== RUN   ExampleAdd\n--- PASS: ExampleAdd (0.00s)\n```\n\nNotice the special format of the comment, `// Output: 6`. While the example will always be compiled, adding this comment means the example will also be executed. Go ahead and temporarily remove the comment `// Output: 6`, then run `go test`, and you will see `ExampleAdd` is no longer executed.\n\nExamples without output comments are useful for demonstrating code that cannot run as unit tests, such as that which accesses the network, while guaranteeing the example at least compiles.\n\nTo view example documentation, let's take a quick look at `pkgsite`. Before navigating to your project's directory, make sure you have installed `pkgsite` by running the following command: `go install golang.org/x/pkgsite/cmd/pkgsite@latest`, then run `pkgsite -open .`, which should open a web browser for you, pointing to `http://localhost:8080`. Inside here you'll see a list of all of Go's Standard Library packages, plus Third Party packages you have installed, under which you should see your example documentation for `github.com/quii/learn-go-with-tests`. Follow that link, and then look under `Integers`, then under `func Add`, then expand `Example` and you should see the example you added for `sum := Add(1, 5)`.\n\nIf you publish your code with examples to a public URL, you can share the documentation of your code at [pkg.go.dev](https://pkg.go.dev/). For example, [here](https://pkg.go.dev/github.com/quii/learn-go-with-tests/integers/v2) is the finalised API for this chapter. This web interface allows you to search for documentation of standard library packages and third-party packages.\n\n## Wrapping up\n\nWhat we have covered:\n\n*   More practice of the TDD workflow\n*   Integers, addition\n*   Writing better documentation so users of our code can understand its usage quickly\n*   Examples of how to use our code, which are checked as part of our tests\n"
  },
  {
    "path": "intro-to-acceptance-tests.md",
    "content": "# Introduction to acceptance testing\n\nAt `$WORK`, we've been running into the need to have \"graceful shutdown\" for our services. Graceful shutdown makes sure your system finishes its work properly before it is terminated. A real-world analogy would be someone trying to wrap up a phone call properly before moving on to the next meeting, rather than just hanging up mid-sentence.\n\nThis chapter will give an intro to graceful shutdown in the context of an HTTP server, and how to write \"acceptance tests\" to give yourself confidence in the behaviour of your code.\n\nAfter reading this you'll know how to share packages with excellent tests, reduce maintenance efforts, and increase confidence in the quality of your work.\n\n## Just enough info about Kubernetes\n\nWe run our software on [Kubernetes](https://kubernetes.io/) (K8s). K8s will terminate \"pods\" (in practice, our software) for various reasons, and a common one is when we push new code that we want to deploy.\n\nWe are setting ourselves high standards regarding [DORA metrics](https://cloud.google.com/blog/products/devops-sre/using-the-four-keys-to-measure-your-devops-performance), so we work in a way where we deploy small, incremental improvements and features to production multiple times per day.\n\nWhen k8s wishes to terminate a pod, it initiates a [\"termination lifecycle\"](https://cloud.google.com/blog/products/containers-kubernetes/kubernetes-best-practices-terminating-with-grace), and a part of that is sending a SIGTERM signal to our software. This is k8s telling our code:\n\n> You need to shut yourself down, finish whatever work you're doing because after a certain \"grace period\", I will send `SIGKILL`, and it's lights out for you.\n\nOn `SIGKILL` any work your program might've been doing will be immediately stopped.\n\n## If you do not have grace\n\nDepending on the nature of your software, if you ignore `SIGTERM`, you can run into problems.\n\nOur specific problem was with in-flight HTTP requests. When an automated test was exercising our API, if k8s decided to stop the pod, the server would die, the test would not get a response from the server, and the test will fail.\n\nThis would trigger an alert in our incidents channel which requires a dev to stop what they're doing and address the problem. These intermittent failures are an annoying distraction for our team.\n\nThese problems are not unique to our tests. If a user sends a request to your system and the process gets terminated mid-flight, they'll likely be greeted with a 5xx HTTP error, not the kind of user experience you want to deliver.\n\n## When you have grace\n\nWhat we want to do is listen for `SIGTERM`, and rather than instantly killing the server, we want to:\n\n- Stop listening to any more requests\n- Allow any in-flight requests to finish\n- *Then* terminate the process\n\n## How to have grace\n\nThankfully, Go already has a mechanism for gracefully shutting down a server with [net/http/Server.Shutdown](https://pkg.go.dev/net/http#Server.Shutdown).\n\n> Shutdown gracefully shuts down the server without interrupting any active connections. Shutdown works by first closing all open listeners, then closing all idle connections, and then waiting indefinitely for connections to return to idle and then shut down. If the provided context expires before the shutdown is complete, Shutdown returns the context's error, otherwise it returns any error returned from closing the Server's underlying Listener(s).\n\nTo handle `SIGTERM` we can use [os/signal.Notify](https://pkg.go.dev/os/signal#Notify), which will send any incoming signals to a channel we provide.\n\nBy using these two features from the standard library, you can listen for `SIGTERM` and shutdown gracefully.\n\n## Graceful shutdown package\n\nTo that end, I wrote [https://pkg.go.dev/github.com/quii/go-graceful-shutdown](https://pkg.go.dev/github.com/quii/go-graceful-shutdown). It provides a decorator function for a `*http.Server` to call its `Shutdown` method when a `SIGTERM` signal is detected\n\n```go\nfunc main() {\n\tvar (\n\t\tctx        = context.Background()\n\t\thttpServer = &http.Server{Addr: \":8080\", Handler: http.HandlerFunc(acceptancetests.SlowHandler)}\n\t\tserver     = gracefulshutdown.NewServer(httpServer)\n\t)\n\n\tif err := server.ListenAndServe(ctx); err != nil {\n\t\t// this will typically happen if our responses aren't written before the ctx deadline, not much can be done\n\t\tlog.Fatalf(\"uh oh, didn't shutdown gracefully, some responses may have been lost %v\", err)\n\t}\n\n\t// hopefully, you'll always see this instead\n\tlog.Println(\"shutdown gracefully! all responses were sent\")\n}\n```\n\nThe specifics around the code are not too important for this read, but it is worth having a quick look over the code before carrying on.\n\n## Tests and feedback loops\n\nWhen we wrote the `gracefulshutdown` package, we had unit tests to prove it behaves correctly which gave us the confidence to aggressively refactor. However, we still didn't feel \"confident\" that it **really** worked.\n\nWe added a `cmd` package and made a real program to use the package we were writing. We'd manually fire it up, fire off an HTTP request to it, and then send a `SIGTERM` to see what would happen.\n\n**The engineer in you should be feeling uncomfortable with manual testing**.\nIt's boring, it doesn't scale, it's inaccurate, and it's wasteful. If you're writing a package you intend to share, but also want to keep it simple and cheap to change, manual testing is not going to cut it.\n\n## Acceptance tests\n\nIf you’ve read the rest of this book, you will have mostly written \"unit tests\". Unit tests are a fantastic tool for enabling fearless refactoring, driving good modular design, preventing regressions, and facilitating fast feedback.\n\nBy their nature, they only test small parts of your system. Usually, unit\ntests alone are *not enough* for an effective testing strategy. Remember, we want our systems to **always be shippable**. We can't rely on manual testing, so we need another kind of testing: **acceptance tests**.\n\n### What are they?\n\nAcceptance tests are a kind of \"black-box test\". They are sometimes referred\nto as \"functional tests\". They should exercise the system as a user of the system would.\n\nThe term \"black-box\" refers to the idea that the test code has no access to the internals of the system, it can only use its public interface and make assertions on the behaviours it observes. This means they can only test the system as a whole.\n\nThis is an advantageous trait because it means the tests exercise the system the same as a user would, it can't use any special workarounds that could make a test pass, but not actually prove what you need to prove. This is similar to the principle of preferring your unit test files to live inside a separate test package, for example, `package mypkg_test` rather than `package mypkg`.\n\n### Benefits of acceptance tests\n\n- When they pass, you know your entire system behaves how you want it to.\n- They are more accurate, quicker, and require less effort than manual testing.\n- When written well, they act as accurate, verified documentation of your\n  system. It doesn't fall into the trap of documentation that diverges from the real behaviour of the system.\n- No mocking! It's all real.\n\n### Potential drawbacks vs unit tests\n\n- They are expensive to write.\n- They take longer to run.\n- They are dependent on the design of the system.\n- When they fail, they typically don't give you a root cause, and can be\n  difficult to debug.\n- They don't give you feedback on the internal quality of your system. You\n  could write total garbage and still make an acceptance test pass.\n- Not all scenarios are practical to exercise due to the black-box nature.\n\nFor this reason, it is foolish to only rely on acceptance tests. They do not have many of the qualities unit tests have, and a system with a large number of acceptance tests will tend to suffer in terms of maintenance costs and poor lead time.\n\n#### Lead time?\n\nLead time refers to how long it takes from a commit being merged into your\nmain branch to it being deployed in production. This number can vary from weeks and even months for some teams to a matter of minutes. Again, at `$WORK`, we value DORA's findings and want to keep our lead time to under 10 minutes.\n\nA balanced testing approach is required for a reliable system with excellent\nlead time, and this is usually described in terms of the [Test Pyramid](https://martinfowler.com/articles/practical-test-pyramid.html).\n\n## How to write basic acceptance tests\n\nHow does this relate to the original problem? We've just written a package here, and it is entirely unit-testable.\n\nAs I mentioned, the unit tests weren't quite giving us the confidence we needed. We want to be *really* sure the package works when integrated with a real, running program. We should be able to automate the manual checks we were making.\n\nLet's take a look at the test program:\n\n```go\nfunc main() {\n\tvar (\n\t\tctx        = context.Background()\n\t\thttpServer = &http.Server{Addr: \":8080\", Handler: http.HandlerFunc(acceptancetests.SlowHandler)}\n\t\tserver     = gracefulshutdown.NewServer(httpServer)\n\t)\n\n\tif err := server.ListenAndServe(ctx); err != nil {\n\t\t// this will typically happen if our responses aren't written before the ctx deadline, not much can be done\n\t\tlog.Fatalf(\"uh oh, didn't shutdown gracefully, some responses may have been lost %v\", err)\n\t}\n\n\t// hopefully, you'll always see this instead\n\tlog.Println(\"shutdown gracefully! all responses were sent\")\n}\n```\n\nYou may have guessed that `SlowHandler` has a `time.Sleep` to delay responding, so I had time to `SIGTERM` and see what happens. The rest is fairly boilerplate:\n\n- Make a `net/http/Server`;\n- Wrap it in the library (see: [Decorator pattern](https://en.wikipedia.org/wiki/Decorator_pattern));\n- Use the wrapped version to `ListenAndServe`.\n\n### High-level steps for the acceptance test\n\n- Build the program\n- Run it (and wait for it listen on `8080`)\n- Send an HTTP request to the server\n- Before the server has a chance to send an HTTP response, send `SIGTERM`\n- See if we still get a response\n\n### Building and running the program\n\n```go\npackage acceptancetests\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n\t\"net\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"syscall\"\n\t\"time\"\n)\n\nconst (\n\tbaseBinName = \"temp-testbinary\"\n)\n\nfunc LaunchTestProgram(port string) (cleanup func(), sendInterrupt func() error, err error) {\n\tbinName, err := buildBinary()\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tsendInterrupt, kill, err := runServer(binName, port)\n\n\tcleanup = func() {\n\t\tif kill != nil {\n\t\t\tkill()\n\t\t}\n\t\tos.Remove(binName)\n\t}\n\n\tif err != nil {\n\t\tcleanup() // even though it's not listening correctly, the program could still be running\n\t\treturn nil, nil, err\n\t}\n\n\treturn cleanup, sendInterrupt, nil\n}\n\nfunc buildBinary() (string, error) {\n\tbinName := randomString(10) + \"-\" + baseBinName\n\n\tbuild := exec.Command(\"go\", \"build\", \"-o\", binName)\n\n\tif err := build.Run(); err != nil {\n\t\treturn \"\", fmt.Errorf(\"cannot build tool %s: %s\", binName, err)\n\t}\n\treturn binName, nil\n}\n\nfunc runServer(binName string, port string) (sendInterrupt func() error, kill func(), err error) {\n\tdir, err := os.Getwd()\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tcmdPath := filepath.Join(dir, binName)\n\n\tcmd := exec.Command(cmdPath)\n\n\tif err := cmd.Start(); err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"cannot run temp converter: %s\", err)\n\t}\n\n\tkill = func() {\n\t\t_ = cmd.Process.Kill()\n\t}\n\n\tsendInterrupt = func() error {\n\t\treturn cmd.Process.Signal(syscall.SIGTERM)\n\t}\n\n\terr = waitForServerListening(port)\n\n\treturn\n}\n\nfunc waitForServerListening(port string) error {\n\tfor i := 0; i < 30; i++ {\n\t\tconn, _ := net.Dial(\"tcp\", net.JoinHostPort(\"localhost\", port))\n\t\tif conn != nil {\n\t\t\tconn.Close()\n\t\t\treturn nil\n\t\t}\n\t\ttime.Sleep(100 * time.Millisecond)\n\t}\n\treturn fmt.Errorf(\"nothing seems to be listening on localhost:%s\", port)\n}\n\nfunc randomString(n int) string {\n\tvar letters = []rune(\"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\")\n\n\ts := make([]rune, n)\n\tfor i := range s {\n\t\ts[i] = letters[rand.Intn(len(letters))]\n\t}\n\treturn string(s)\n}\n```\n\n`LaunchTestProgram` is responsible for:\n- building the program\n- launching the program\n- waiting for it to listen on port `8080`\n- providing a `cleanup` function to kill the program and delete it to ensure that when our tests finish, we're left in a clean state\n- providing an `interrupt` function to send the program a `SIGTERM` to let us test the behaviour\n\nAdmittedly, this is not the nicest code in the world, but just focus on the exported function `LaunchTestProgram`, the un-exported functions it calls are uninteresting boilerplate.\n\nAs discussed, acceptance testing tends to be trickier to set up. This code does make the *testing* code substantially simpler to read, and often with acceptance tests once you've written the ceremonious code, it's done, and you can forget about it.\n\n### The acceptance test(s)\n\nWe wanted to have two acceptance tests for two programs, one with graceful shutdown and one without, so we, and the readers can see the difference in behaviour. With `LaunchTestProgram` to build and run the programs, it's quite simple to write acceptance tests for both, and we benefit from re-use with some helper functions.\n\nHere is the test for the server *with* a graceful shutdown, [you can find the test without on GitHub](https://github.com/quii/go-graceful-shutdown/blob/main/acceptancetests/withoutgracefulshutdown/main_test.go)\n\n```go\npackage main\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/quii/go-graceful-shutdown/acceptancetests\"\n\t\"github.com/quii/go-graceful-shutdown/assert\"\n)\n\nconst (\n\tport = \"8080\"\n\turl  = \"<http://localhost:\" + port\n)\n\nfunc TestGracefulShutdown(t *testing.T) {\n\tcleanup, sendInterrupt, err := acceptancetests.LaunchTestProgram(port)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tt.Cleanup(cleanup)\n\n\t// just check the server works before we shut things down\n\tassert.CanGet(t, url)\n\n\t// fire off a request, and before it has a chance to respond send SIGTERM.\n\ttime.AfterFunc(50*time.Millisecond, func() {\n\t\tassert.NoError(t, sendInterrupt())\n\t})\n\t// Without graceful shutdown, this would fail\n\tassert.CanGet(t, url)\n\n\t// after interrupt, the server should be shutdown, and no more requests will work\n\tassert.CantGet(t, url)\n}\n```\n\nWith the setup encapsulated away, the tests are comprehensive, describe the behaviour, and are relatively easy to follow.\n\n`assert.CanGet/CantGet` are helper functions I made to DRY up this common assertion for this suite.\n\n```go\nfunc CanGet(t testing.TB, url string) {\n\terrChan := make(chan error)\n\n\tgo func() {\n\t\tres, err := http.Get(url)\n\t\tif err != nil {\n\t\t\terrChan <- err\n\t\t\treturn\n\t\t}\n\t\tres.Body.Close()\n\t\terrChan <- nil\n\t}()\n\n\tselect {\n\tcase err := <-errChan:\n\t\tNoError(t, err)\n\tcase <-time.After(3 * time.Second):\n\t\tt.Errorf(\"timed out waiting for request to %q\", url)\n\t}\n}\n```\n\nThis will fire off a `GET` to `URL` on a goroutine, and if it responds without error before 3 seconds, then it will not fail. `CantGet` is omitted for brevity, [but you can view it on GitHub here](https://github.com/quii/go-graceful-shutdown/blob/main/assert/assert.go#L61).\n\nIt's important to note again, Go has all the tools you need to write acceptance tests out of the box. You don't *need* a special framework to build acceptance tests.\n\n### Small investment with a big pay-off\n\nWith these tests, readers can look at the example programs and be confident that the example *actually* works, so they can be confident in the package's claims.\n\nImportantly, as the author, we get **fast feedback** and **massive confidence** that the package works in a real-world setting.\n\n```shell\ngo test -count=1 ./...\nok  \tgithub.com/quii/go-graceful-shutdown\t0.196s\n?   \tgithub.com/quii/go-graceful-shutdown/acceptancetests\t[no test files]\nok  \tgithub.com/quii/go-graceful-shutdown/acceptancetests/withgracefulshutdown\t4.785s\nok  \tgithub.com/quii/go-graceful-shutdown/acceptancetests/withoutgracefulshutdown\t2.914s\n?   \tgithub.com/quii/go-graceful-shutdown/assert\t[no test files]\n```\n\n## Wrapping up\n\nIn this blog post, we introduced acceptance tests into your testing tool belt. They are invaluable when you start to build real systems and are an important complement to your unit tests.\n\nThe nature of *how* to write acceptance tests depends on the system you're building, but the principles stay the same. Treat your system like a \"black box\". If you're making a website, your tests should act like a user, so you'll want to use a headless web browser like [Selenium](https://www.selenium.dev/), to click on links, fill in forms, etc. For a RESTful API, you'll send HTTP requests using a client.\n\n### Taking it further for more complicated systems\n\nNon-trivial systems don't tend to be single-process applications like the one we've discussed. Typically, you'll depend on other systems such as a database. For these scenarios, you'll need to automate a local environment to test with. Tools like [docker-compose](https://docs.docker.com/compose/) are useful for spinning up containers of the environment you need to run your system locally.\n\n### The next chapter\n\nIn this post the acceptance test was written retrospectively. However, in [Growing Object-Oriented Software](http://www.growing-object-oriented-software.com) the authors show that we can use acceptance tests in a test-driven approach to act as a \"north-star\" to guide our efforts.\n\nAs systems get more complex, the costs of writing and maintaining acceptance tests can quickly spiral out of control. There are countless stories of development teams being hamstrung by expensive acceptance test suites.\n\nThe next chapter will introduce using acceptance test to guide our design\nalong with principles and techniques for managing the costs of acceptance tests.\n\n### Improving the quality of open-source\n\nIf you're writing packages you intend to share, I'd encourage you to create\nsimple example programs demonstrating what your package does and invest time in having simple-to-follow acceptance tests to give yourself, and potential users of your work, confidence.\n\nLike [Testable Examples](https://go.dev/blog/examples), seeing this little extra effort in developer experience goes a long way toward building trust in your work, and will reduce your own maintenance costs.\n\n## Recruitment plug for `$WORK`\n\nIf you fancy working in an environment with other engineers solving interesting problems, live near or around London or Porto, and enjoy the contents of this chapter and book -  please [reach out to me on Twitter](https://twitter.com/quii), and maybe we can work together soon!\n"
  },
  {
    "path": "io/v1/file_system_store.go",
    "content": "package main\n\nimport (\n\t\"io\"\n)\n\n// FileSystemPlayerStore stores players in the filesystem.\ntype FileSystemPlayerStore struct {\n\tdatabase io.ReadSeeker\n}\n\n// GetLeague returns the scores of all the players.\nfunc (f *FileSystemPlayerStore) GetLeague() []Player {\n\tf.database.Seek(0, io.SeekStart)\n\tleague, _ := NewLeague(f.database)\n\treturn league\n}\n"
  },
  {
    "path": "io/v1/file_system_store_test.go",
    "content": "package main\n\nimport (\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestFileSystemStore(t *testing.T) {\n\n\tt.Run(\"league from a reader\", func(t *testing.T) {\n\t\tdatabase := strings.NewReader(`[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\n\t\tstore := FileSystemPlayerStore{database}\n\n\t\tgot := store.GetLeague()\n\n\t\twant := []Player{\n\t\t\t{\"Cleo\", 10},\n\t\t\t{\"Chris\", 33},\n\t\t}\n\n\t\tassertLeague(t, got, want)\n\n\t\t// read again\n\t\tgot = store.GetLeague()\n\t\tassertLeague(t, got, want)\n\t})\n}\n"
  },
  {
    "path": "io/v1/in_memory_player_store.go",
    "content": "package main\n\n// NewInMemoryPlayerStore initialises an empty player store.\nfunc NewInMemoryPlayerStore() *InMemoryPlayerStore {\n\treturn &InMemoryPlayerStore{map[string]int{}}\n}\n\n// InMemoryPlayerStore collects data about players in memory.\ntype InMemoryPlayerStore struct {\n\tstore map[string]int\n}\n\n// GetLeague returns a collection of Players.\nfunc (i *InMemoryPlayerStore) GetLeague() []Player {\n\tvar league []Player\n\tfor name, wins := range i.store {\n\t\tleague = append(league, Player{name, wins})\n\t}\n\treturn league\n}\n\n// RecordWin will record a player's win.\nfunc (i *InMemoryPlayerStore) RecordWin(name string) {\n\ti.store[name]++\n}\n\n// GetPlayerScore retrieves scores for a given player.\nfunc (i *InMemoryPlayerStore) GetPlayerScore(name string) int {\n\treturn i.store[name]\n}\n"
  },
  {
    "path": "io/v1/league.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"io\"\n)\n\n// NewLeague creates a league from JSON.\nfunc NewLeague(rdr io.Reader) ([]Player, error) {\n\tvar league []Player\n\terr := json.NewDecoder(rdr).Decode(&league)\n\treturn league, err\n}\n"
  },
  {
    "path": "io/v1/main.go",
    "content": "package main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc main() {\n\tserver := NewPlayerServer(NewInMemoryPlayerStore())\n\tlog.Fatal(http.ListenAndServe(\":5000\", server))\n}\n"
  },
  {
    "path": "io/v1/server.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n)\n\n// PlayerStore stores score information about players.\ntype PlayerStore interface {\n\tGetPlayerScore(name string) int\n\tRecordWin(name string)\n\tGetLeague() []Player\n}\n\n// Player stores a name with a number of wins.\ntype Player struct {\n\tName string\n\tWins int\n}\n\n// PlayerServer is a HTTP interface for player information.\ntype PlayerServer struct {\n\tstore PlayerStore\n\thttp.Handler\n}\n\nconst jsonContentType = \"application/json\"\n\n// NewPlayerServer creates a PlayerServer with routing configured.\nfunc NewPlayerServer(store PlayerStore) *PlayerServer {\n\tp := new(PlayerServer)\n\n\tp.store = store\n\n\trouter := http.NewServeMux()\n\trouter.Handle(\"/league\", http.HandlerFunc(p.leagueHandler))\n\trouter.Handle(\"/players/\", http.HandlerFunc(p.playersHandler))\n\n\tp.Handler = router\n\n\treturn p\n}\n\nfunc (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) {\n\tw.Header().Set(\"content-type\", jsonContentType)\n\tjson.NewEncoder(w).Encode(p.store.GetLeague())\n}\n\nfunc (p *PlayerServer) playersHandler(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\n\tswitch r.Method {\n\tcase http.MethodPost:\n\t\tp.processWin(w, player)\n\tcase http.MethodGet:\n\t\tp.showScore(w, player)\n\t}\n}\n\nfunc (p *PlayerServer) showScore(w http.ResponseWriter, player string) {\n\tscore := p.store.GetPlayerScore(player)\n\n\tif score == 0 {\n\t\tw.WriteHeader(http.StatusNotFound)\n\t}\n\n\tfmt.Fprint(w, score)\n}\n\nfunc (p *PlayerServer) processWin(w http.ResponseWriter, player string) {\n\tp.store.RecordWin(player)\n\tw.WriteHeader(http.StatusAccepted)\n}\n"
  },
  {
    "path": "io/v1/server_integration_test.go",
    "content": "package main\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestRecordingWinsAndRetrievingThem(t *testing.T) {\n\tstore := NewInMemoryPlayerStore()\n\tserver := NewPlayerServer(store)\n\tplayer := \"Pepper\"\n\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\n\tt.Run(\"get score\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newGetScoreRequest(player))\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\n\t\tassertResponseBody(t, response.Body.String(), \"3\")\n\t})\n\n\tt.Run(\"get league\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newLeagueRequest())\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\t\twant := []Player{\n\t\t\t{\"Pepper\", 3},\n\t\t}\n\t\tassertLeague(t, got, want)\n\t})\n}\n"
  },
  {
    "path": "io/v1/server_test.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"reflect\"\n\t\"testing\"\n)\n\ntype StubPlayerStore struct {\n\tscores   map[string]int\n\twinCalls []string\n\tleague   []Player\n}\n\nfunc (s *StubPlayerStore) GetPlayerScore(name string) int {\n\tscore := s.scores[name]\n\treturn score\n}\n\nfunc (s *StubPlayerStore) RecordWin(name string) {\n\ts.winCalls = append(s.winCalls, name)\n}\n\nfunc (s *StubPlayerStore) GetLeague() []Player {\n\treturn s.league\n}\n\nfunc TestGETPlayers(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{\n\t\t\t\"Pepper\": 20,\n\t\t\t\"Floyd\":  10,\n\t\t},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"returns Pepper's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Pepper\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"20\")\n\t})\n\n\tt.Run(\"returns Floyd's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Floyd\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"10\")\n\t})\n\n\tt.Run(\"returns 404 on missing players\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Apollo\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusNotFound)\n\t})\n}\n\nfunc TestStoreWins(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"it records wins on POST\", func(t *testing.T) {\n\t\tplayer := \"Pepper\"\n\n\t\trequest := newPostWinRequest(player)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusAccepted)\n\n\t\tif len(store.winCalls) != 1 {\n\t\t\tt.Fatalf(\"got %d calls to RecordWin want %d\", len(store.winCalls), 1)\n\t\t}\n\n\t\tif store.winCalls[0] != player {\n\t\t\tt.Errorf(\"did not store correct winner got %q want %q\", store.winCalls[0], player)\n\t\t}\n\t})\n}\n\nfunc TestLeague(t *testing.T) {\n\n\tt.Run(\"it returns the league table as JSON\", func(t *testing.T) {\n\t\twantedLeague := []Player{\n\t\t\t{\"Cleo\", 32},\n\t\t\t{\"Chris\", 20},\n\t\t\t{\"Tiest\", 14},\n\t\t}\n\n\t\tstore := StubPlayerStore{nil, nil, wantedLeague}\n\t\tserver := NewPlayerServer(&store)\n\n\t\trequest := newLeagueRequest()\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertLeague(t, got, wantedLeague)\n\t\tassertContentType(t, response, jsonContentType)\n\n\t})\n}\n\nfunc assertContentType(t testing.TB, response *httptest.ResponseRecorder, want string) {\n\tt.Helper()\n\tif response.Header().Get(\"content-type\") != want {\n\t\tt.Errorf(\"response did not have content-type of %s, got %v\", want, response.Result().Header)\n\t}\n}\n\nfunc getLeagueFromResponse(t testing.TB, body io.Reader) []Player {\n\tt.Helper()\n\tleague, err := NewLeague(body)\n\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to parse response from server %q into slice of Player, '%v'\", body, err)\n\t}\n\n\treturn league\n}\n\nfunc assertLeague(t testing.TB, got, want []Player) {\n\tt.Helper()\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %v want %v\", got, want)\n\t}\n}\n\nfunc assertStatus(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"did not get correct status, got %d, want %d\", got, want)\n\t}\n}\n\nfunc newLeagueRequest() *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, \"/league\", nil)\n\treturn req\n}\n\nfunc newGetScoreRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc newPostWinRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodPost, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc assertResponseBody(t testing.TB, got, want string) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"response body is wrong, got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "io/v2/file_system_store.go",
    "content": "package main\n\nimport (\n\t\"io\"\n)\n\n// FileSystemPlayerStore stores players in the filesystem.\ntype FileSystemPlayerStore struct {\n\tdatabase io.ReadSeeker\n}\n\n// GetLeague returns the scores of all the players.\nfunc (f *FileSystemPlayerStore) GetLeague() []Player {\n\tf.database.Seek(0, io.SeekStart)\n\tleague, _ := NewLeague(f.database)\n\treturn league\n}\n\n// GetPlayerScore retrieves a player's score.\nfunc (f *FileSystemPlayerStore) GetPlayerScore(name string) int {\n\n\tvar wins int\n\n\tfor _, player := range f.GetLeague() {\n\t\tif player.Name == name {\n\t\t\twins = player.Wins\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn wins\n}\n"
  },
  {
    "path": "io/v2/file_system_store_test.go",
    "content": "package main\n\nimport (\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestFileSystemStore(t *testing.T) {\n\n\tt.Run(\"league from a reader\", func(t *testing.T) {\n\t\tdatabase := strings.NewReader(`[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\n\t\tstore := FileSystemPlayerStore{database}\n\n\t\tgot := store.GetLeague()\n\n\t\twant := []Player{\n\t\t\t{\"Cleo\", 10},\n\t\t\t{\"Chris\", 33},\n\t\t}\n\n\t\tassertLeague(t, got, want)\n\n\t\t// read again\n\t\tgot = store.GetLeague()\n\t\tassertLeague(t, got, want)\n\t})\n\n\tt.Run(\"/get player score\", func(t *testing.T) {\n\t\tdatabase := strings.NewReader(`[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\n\t\tstore := FileSystemPlayerStore{database}\n\n\t\tgot := store.GetPlayerScore(\"Chris\")\n\t\twant := 33\n\t\tassertScoreEquals(t, got, want)\n\t})\n}\n\nfunc assertScoreEquals(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"got %d want %d\", got, want)\n\t}\n}\n"
  },
  {
    "path": "io/v2/in_memory_player_store.go",
    "content": "package main\n\n// NewInMemoryPlayerStore initialises an empty player store.\nfunc NewInMemoryPlayerStore() *InMemoryPlayerStore {\n\treturn &InMemoryPlayerStore{map[string]int{}}\n}\n\n// InMemoryPlayerStore collects data about players in memory.\ntype InMemoryPlayerStore struct {\n\tstore map[string]int\n}\n\n// GetLeague returns a collection of Players.\nfunc (i *InMemoryPlayerStore) GetLeague() []Player {\n\tvar league []Player\n\tfor name, wins := range i.store {\n\t\tleague = append(league, Player{name, wins})\n\t}\n\treturn league\n}\n\n// RecordWin will record a player's win.\nfunc (i *InMemoryPlayerStore) RecordWin(name string) {\n\ti.store[name]++\n}\n\n// GetPlayerScore retrieves scores for a given player.\nfunc (i *InMemoryPlayerStore) GetPlayerScore(name string) int {\n\treturn i.store[name]\n}\n"
  },
  {
    "path": "io/v2/league.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n)\n\n// NewLeague creates a league from JSON.\nfunc NewLeague(rdr io.Reader) ([]Player, error) {\n\tvar league []Player\n\terr := json.NewDecoder(rdr).Decode(&league)\n\n\tif err != nil {\n\t\terr = fmt.Errorf(\"problem parsing league, %v\", err)\n\t}\n\n\treturn league, err\n}\n"
  },
  {
    "path": "io/v2/main.go",
    "content": "package main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc main() {\n\tserver := NewPlayerServer(NewInMemoryPlayerStore())\n\tlog.Fatal(http.ListenAndServe(\":5000\", server))\n}\n"
  },
  {
    "path": "io/v2/server.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n)\n\n// PlayerStore stores score information about players.\ntype PlayerStore interface {\n\tGetPlayerScore(name string) int\n\tRecordWin(name string)\n\tGetLeague() []Player\n}\n\n// Player stores a name with a number of wins.\ntype Player struct {\n\tName string\n\tWins int\n}\n\n// PlayerServer is a HTTP interface for player information.\ntype PlayerServer struct {\n\tstore PlayerStore\n\thttp.Handler\n}\n\nconst jsonContentType = \"application/json\"\n\n// NewPlayerServer creates a PlayerServer with routing configured.\nfunc NewPlayerServer(store PlayerStore) *PlayerServer {\n\tp := new(PlayerServer)\n\n\tp.store = store\n\n\trouter := http.NewServeMux()\n\trouter.Handle(\"/league\", http.HandlerFunc(p.leagueHandler))\n\trouter.Handle(\"/players/\", http.HandlerFunc(p.playersHandler))\n\n\tp.Handler = router\n\n\treturn p\n}\n\nfunc (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) {\n\tw.Header().Set(\"content-type\", jsonContentType)\n\tjson.NewEncoder(w).Encode(p.store.GetLeague())\n}\n\nfunc (p *PlayerServer) playersHandler(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\n\tswitch r.Method {\n\tcase http.MethodPost:\n\t\tp.processWin(w, player)\n\tcase http.MethodGet:\n\t\tp.showScore(w, player)\n\t}\n}\n\nfunc (p *PlayerServer) showScore(w http.ResponseWriter, player string) {\n\tscore := p.store.GetPlayerScore(player)\n\n\tif score == 0 {\n\t\tw.WriteHeader(http.StatusNotFound)\n\t}\n\n\tfmt.Fprint(w, score)\n}\n\nfunc (p *PlayerServer) processWin(w http.ResponseWriter, player string) {\n\tp.store.RecordWin(player)\n\tw.WriteHeader(http.StatusAccepted)\n}\n"
  },
  {
    "path": "io/v2/server_integration_test.go",
    "content": "package main\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestRecordingWinsAndRetrievingThem(t *testing.T) {\n\tstore := NewInMemoryPlayerStore()\n\tserver := NewPlayerServer(store)\n\tplayer := \"Pepper\"\n\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\n\tt.Run(\"get score\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newGetScoreRequest(player))\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\n\t\tassertResponseBody(t, response.Body.String(), \"3\")\n\t})\n\n\tt.Run(\"get league\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newLeagueRequest())\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\t\twant := []Player{\n\t\t\t{\"Pepper\", 3},\n\t\t}\n\t\tassertLeague(t, got, want)\n\t})\n}\n"
  },
  {
    "path": "io/v2/server_test.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"reflect\"\n\t\"testing\"\n)\n\ntype StubPlayerStore struct {\n\tscores   map[string]int\n\twinCalls []string\n\tleague   []Player\n}\n\nfunc (s *StubPlayerStore) GetPlayerScore(name string) int {\n\tscore := s.scores[name]\n\treturn score\n}\n\nfunc (s *StubPlayerStore) RecordWin(name string) {\n\ts.winCalls = append(s.winCalls, name)\n}\n\nfunc (s *StubPlayerStore) GetLeague() []Player {\n\treturn s.league\n}\n\nfunc TestGETPlayers(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{\n\t\t\t\"Pepper\": 20,\n\t\t\t\"Floyd\":  10,\n\t\t},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"returns Pepper's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Pepper\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"20\")\n\t})\n\n\tt.Run(\"returns Floyd's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Floyd\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"10\")\n\t})\n\n\tt.Run(\"returns 404 on missing players\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Apollo\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusNotFound)\n\t})\n}\n\nfunc TestStoreWins(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"it records wins on POST\", func(t *testing.T) {\n\t\tplayer := \"Pepper\"\n\n\t\trequest := newPostWinRequest(player)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusAccepted)\n\n\t\tif len(store.winCalls) != 1 {\n\t\t\tt.Fatalf(\"got %d calls to RecordWin want %d\", len(store.winCalls), 1)\n\t\t}\n\n\t\tif store.winCalls[0] != player {\n\t\t\tt.Errorf(\"did not store correct winner got %q want %q\", store.winCalls[0], player)\n\t\t}\n\t})\n}\n\nfunc TestLeague(t *testing.T) {\n\n\tt.Run(\"it returns the league table as JSON\", func(t *testing.T) {\n\t\twantedLeague := []Player{\n\t\t\t{\"Cleo\", 32},\n\t\t\t{\"Chris\", 20},\n\t\t\t{\"Tiest\", 14},\n\t\t}\n\n\t\tstore := StubPlayerStore{nil, nil, wantedLeague}\n\t\tserver := NewPlayerServer(&store)\n\n\t\trequest := newLeagueRequest()\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertLeague(t, got, wantedLeague)\n\t\tassertContentType(t, response, jsonContentType)\n\n\t})\n}\n\nfunc assertContentType(t testing.TB, response *httptest.ResponseRecorder, want string) {\n\tt.Helper()\n\tif response.Header().Get(\"content-type\") != want {\n\t\tt.Errorf(\"response did not have content-type of %s, got %v\", want, response.Result().Header)\n\t}\n}\n\nfunc getLeagueFromResponse(t testing.TB, body io.Reader) []Player {\n\tt.Helper()\n\tleague, err := NewLeague(body)\n\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to parse response from server %q into slice of Player, '%v'\", body, err)\n\t}\n\n\treturn league\n}\n\nfunc assertLeague(t testing.TB, got, want []Player) {\n\tt.Helper()\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %v want %v\", got, want)\n\t}\n}\n\nfunc assertStatus(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"did not get correct status, got %d, want %d\", got, want)\n\t}\n}\n\nfunc newLeagueRequest() *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, \"/league\", nil)\n\treturn req\n}\n\nfunc newGetScoreRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc newPostWinRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodPost, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc assertResponseBody(t testing.TB, got, want string) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"response body is wrong, got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "io/v3/file_system_store.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"io\"\n)\n\n// FileSystemPlayerStore stores players in the filesystem.\ntype FileSystemPlayerStore struct {\n\tdatabase io.ReadWriteSeeker\n}\n\n// GetLeague returns the scores of all the players.\nfunc (f *FileSystemPlayerStore) GetLeague() League {\n\tf.database.Seek(0, io.SeekStart)\n\tleague, _ := NewLeague(f.database)\n\treturn league\n}\n\n// GetPlayerScore retrieves a player's score.\nfunc (f *FileSystemPlayerStore) GetPlayerScore(name string) int {\n\n\tplayer := f.GetLeague().Find(name)\n\n\tif player != nil {\n\t\treturn player.Wins\n\t}\n\n\treturn 0\n}\n\n// RecordWin will store a win for a player, incrementing wins if already known.\nfunc (f *FileSystemPlayerStore) RecordWin(name string) {\n\tleague := f.GetLeague()\n\tplayer := league.Find(name)\n\n\tif player != nil {\n\t\tplayer.Wins++\n\t}\n\n\tf.database.Seek(0, io.SeekStart)\n\tjson.NewEncoder(f.database).Encode(league)\n}\n"
  },
  {
    "path": "io/v3/file_system_store_test.go",
    "content": "package main\n\nimport (\n\t\"io\"\n\t\"os\"\n\t\"testing\"\n)\n\nfunc createTempFile(t testing.TB, initialData string) (io.ReadWriteSeeker, func()) {\n\tt.Helper()\n\n\ttmpfile, err := os.CreateTemp(\"\", \"db\")\n\n\tif err != nil {\n\t\tt.Fatalf(\"could not create temp file %v\", err)\n\t}\n\n\ttmpfile.Write([]byte(initialData))\n\n\tremoveFile := func() {\n\t\ttmpfile.Close()\n\t\tos.Remove(tmpfile.Name())\n\t}\n\n\treturn tmpfile, removeFile\n}\n\nfunc TestFileSystemStore(t *testing.T) {\n\n\tt.Run(\"league from a reader\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore := FileSystemPlayerStore{database}\n\n\t\tgot := store.GetLeague()\n\n\t\twant := []Player{\n\t\t\t{\"Cleo\", 10},\n\t\t\t{\"Chris\", 33},\n\t\t}\n\n\t\tassertLeague(t, got, want)\n\n\t\t// read again\n\t\tgot = store.GetLeague()\n\t\tassertLeague(t, got, want)\n\t})\n\n\tt.Run(\"get player score\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore := FileSystemPlayerStore{database}\n\n\t\tgot := store.GetPlayerScore(\"Chris\")\n\t\twant := 33\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"store wins for existing players\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore := FileSystemPlayerStore{database}\n\n\t\tstore.RecordWin(\"Chris\")\n\n\t\tgot := store.GetPlayerScore(\"Chris\")\n\t\twant := 34\n\t\tassertScoreEquals(t, got, want)\n\t})\n}\n\nfunc assertScoreEquals(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"got %d want %d\", got, want)\n\t}\n}\n"
  },
  {
    "path": "io/v3/in_memory_player_store.go",
    "content": "package main\n\n// NewInMemoryPlayerStore initialises an empty player store.\nfunc NewInMemoryPlayerStore() *InMemoryPlayerStore {\n\treturn &InMemoryPlayerStore{map[string]int{}}\n}\n\n// InMemoryPlayerStore collects data about players in memory.\ntype InMemoryPlayerStore struct {\n\tstore map[string]int\n}\n\n// GetLeague returns a collection of Players.\nfunc (i *InMemoryPlayerStore) GetLeague() League {\n\tvar league []Player\n\tfor name, wins := range i.store {\n\t\tleague = append(league, Player{name, wins})\n\t}\n\treturn league\n}\n\n// RecordWin will record a player's win.\nfunc (i *InMemoryPlayerStore) RecordWin(name string) {\n\ti.store[name]++\n}\n\n// GetPlayerScore retrieves scores for a given player.\nfunc (i *InMemoryPlayerStore) GetPlayerScore(name string) int {\n\treturn i.store[name]\n}\n"
  },
  {
    "path": "io/v3/league.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n)\n\n// League stores a collection of players.\ntype League []Player\n\n// Find tries to return a player from a league.\nfunc (l League) Find(name string) *Player {\n\tfor i, p := range l {\n\t\tif p.Name == name {\n\t\t\treturn &l[i]\n\t\t}\n\t}\n\treturn nil\n}\n\n// NewLeague creates a league from JSON.\nfunc NewLeague(rdr io.Reader) (League, error) {\n\tvar league []Player\n\terr := json.NewDecoder(rdr).Decode(&league)\n\n\tif err != nil {\n\t\terr = fmt.Errorf(\"problem parsing league, %v\", err)\n\t}\n\n\treturn league, err\n}\n"
  },
  {
    "path": "io/v3/main.go",
    "content": "package main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc main() {\n\tserver := NewPlayerServer(NewInMemoryPlayerStore())\n\tlog.Fatal(http.ListenAndServe(\":5000\", server))\n}\n"
  },
  {
    "path": "io/v3/server.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n)\n\n// PlayerStore stores score information about players.\ntype PlayerStore interface {\n\tGetPlayerScore(name string) int\n\tRecordWin(name string)\n\tGetLeague() League\n}\n\n// Player stores a name with a number of wins.\ntype Player struct {\n\tName string\n\tWins int\n}\n\n// PlayerServer is a HTTP interface for player information.\ntype PlayerServer struct {\n\tstore PlayerStore\n\thttp.Handler\n}\n\nconst jsonContentType = \"application/json\"\n\n// NewPlayerServer creates a PlayerServer with routing configured.\nfunc NewPlayerServer(store PlayerStore) *PlayerServer {\n\tp := new(PlayerServer)\n\n\tp.store = store\n\n\trouter := http.NewServeMux()\n\trouter.Handle(\"/league\", http.HandlerFunc(p.leagueHandler))\n\trouter.Handle(\"/players/\", http.HandlerFunc(p.playersHandler))\n\n\tp.Handler = router\n\n\treturn p\n}\n\nfunc (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) {\n\tw.Header().Set(\"content-type\", jsonContentType)\n\tjson.NewEncoder(w).Encode(p.store.GetLeague())\n}\n\nfunc (p *PlayerServer) playersHandler(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\n\tswitch r.Method {\n\tcase http.MethodPost:\n\t\tp.processWin(w, player)\n\tcase http.MethodGet:\n\t\tp.showScore(w, player)\n\t}\n}\n\nfunc (p *PlayerServer) showScore(w http.ResponseWriter, player string) {\n\tscore := p.store.GetPlayerScore(player)\n\n\tif score == 0 {\n\t\tw.WriteHeader(http.StatusNotFound)\n\t}\n\n\tfmt.Fprint(w, score)\n}\n\nfunc (p *PlayerServer) processWin(w http.ResponseWriter, player string) {\n\tp.store.RecordWin(player)\n\tw.WriteHeader(http.StatusAccepted)\n}\n"
  },
  {
    "path": "io/v3/server_integration_test.go",
    "content": "package main\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestRecordingWinsAndRetrievingThem(t *testing.T) {\n\tstore := NewInMemoryPlayerStore()\n\tserver := NewPlayerServer(store)\n\tplayer := \"Pepper\"\n\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\n\tt.Run(\"get score\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newGetScoreRequest(player))\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\n\t\tassertResponseBody(t, response.Body.String(), \"3\")\n\t})\n\n\tt.Run(\"get league\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newLeagueRequest())\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\t\twant := []Player{\n\t\t\t{\"Pepper\", 3},\n\t\t}\n\t\tassertLeague(t, got, want)\n\t})\n}\n"
  },
  {
    "path": "io/v3/server_test.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"reflect\"\n\t\"testing\"\n)\n\ntype StubPlayerStore struct {\n\tscores   map[string]int\n\twinCalls []string\n\tleague   []Player\n}\n\nfunc (s *StubPlayerStore) GetPlayerScore(name string) int {\n\tscore := s.scores[name]\n\treturn score\n}\n\nfunc (s *StubPlayerStore) RecordWin(name string) {\n\ts.winCalls = append(s.winCalls, name)\n}\n\nfunc (s *StubPlayerStore) GetLeague() League {\n\treturn s.league\n}\n\nfunc TestGETPlayers(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{\n\t\t\t\"Pepper\": 20,\n\t\t\t\"Floyd\":  10,\n\t\t},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"returns Pepper's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Pepper\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"20\")\n\t})\n\n\tt.Run(\"returns Floyd's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Floyd\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"10\")\n\t})\n\n\tt.Run(\"returns 404 on missing players\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Apollo\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusNotFound)\n\t})\n}\n\nfunc TestStoreWins(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"it records wins on POST\", func(t *testing.T) {\n\t\tplayer := \"Pepper\"\n\n\t\trequest := newPostWinRequest(player)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusAccepted)\n\n\t\tif len(store.winCalls) != 1 {\n\t\t\tt.Fatalf(\"got %d calls to RecordWin want %d\", len(store.winCalls), 1)\n\t\t}\n\n\t\tif store.winCalls[0] != player {\n\t\t\tt.Errorf(\"did not store correct winner got %q want %q\", store.winCalls[0], player)\n\t\t}\n\t})\n}\n\nfunc TestLeague(t *testing.T) {\n\n\tt.Run(\"it returns the league table as JSON\", func(t *testing.T) {\n\t\twantedLeague := []Player{\n\t\t\t{\"Cleo\", 32},\n\t\t\t{\"Chris\", 20},\n\t\t\t{\"Tiest\", 14},\n\t\t}\n\n\t\tstore := StubPlayerStore{nil, nil, wantedLeague}\n\t\tserver := NewPlayerServer(&store)\n\n\t\trequest := newLeagueRequest()\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertLeague(t, got, wantedLeague)\n\t\tassertContentType(t, response, jsonContentType)\n\n\t})\n}\n\nfunc assertContentType(t testing.TB, response *httptest.ResponseRecorder, want string) {\n\tt.Helper()\n\tif response.Header().Get(\"content-type\") != want {\n\t\tt.Errorf(\"response did not have content-type of %s, got %v\", want, response.Result().Header)\n\t}\n}\n\nfunc getLeagueFromResponse(t testing.TB, body io.Reader) []Player {\n\tt.Helper()\n\tleague, err := NewLeague(body)\n\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to parse response from server %q into slice of Player, '%v'\", body, err)\n\t}\n\n\treturn league\n}\n\nfunc assertLeague(t testing.TB, got, want []Player) {\n\tt.Helper()\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %v want %v\", got, want)\n\t}\n}\n\nfunc assertStatus(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"did not get correct status, got %d, want %d\", got, want)\n\t}\n}\n\nfunc newLeagueRequest() *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, \"/league\", nil)\n\treturn req\n}\n\nfunc newGetScoreRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc newPostWinRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodPost, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc assertResponseBody(t testing.TB, got, want string) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"response body is wrong, got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "io/v4/file_system_store.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"io\"\n)\n\n// FileSystemPlayerStore stores players in the filesystem.\ntype FileSystemPlayerStore struct {\n\tdatabase io.ReadWriteSeeker\n}\n\n// GetLeague returns the scores of all the players.\nfunc (f *FileSystemPlayerStore) GetLeague() League {\n\tf.database.Seek(0, io.SeekStart)\n\tleague, _ := NewLeague(f.database)\n\treturn league\n}\n\n// GetPlayerScore retrieves a player's score.\nfunc (f *FileSystemPlayerStore) GetPlayerScore(name string) int {\n\n\tplayer := f.GetLeague().Find(name)\n\n\tif player != nil {\n\t\treturn player.Wins\n\t}\n\n\treturn 0\n}\n\n// RecordWin will store a win for a player, incrementing wins if already known.\nfunc (f *FileSystemPlayerStore) RecordWin(name string) {\n\tleague := f.GetLeague()\n\tplayer := league.Find(name)\n\n\tif player != nil {\n\t\tplayer.Wins++\n\t} else {\n\t\tleague = append(league, Player{name, 1})\n\t}\n\n\tf.database.Seek(0, io.SeekStart)\n\tjson.NewEncoder(f.database).Encode(league)\n}\n"
  },
  {
    "path": "io/v4/file_system_store_test.go",
    "content": "package main\n\nimport (\n\t\"io\"\n\t\"os\"\n\t\"testing\"\n)\n\nfunc createTempFile(t testing.TB, initialData string) (io.ReadWriteSeeker, func()) {\n\tt.Helper()\n\n\ttmpfile, err := os.CreateTemp(\"\", \"db\")\n\n\tif err != nil {\n\t\tt.Fatalf(\"could not create temp file %v\", err)\n\t}\n\n\ttmpfile.Write([]byte(initialData))\n\n\tremoveFile := func() {\n\t\ttmpfile.Close()\n\t\tos.Remove(tmpfile.Name())\n\t}\n\n\treturn tmpfile, removeFile\n}\n\nfunc TestFileSystemStore(t *testing.T) {\n\n\tt.Run(\"league from a reader\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore := FileSystemPlayerStore{database}\n\n\t\tgot := store.GetLeague()\n\n\t\twant := []Player{\n\t\t\t{\"Cleo\", 10},\n\t\t\t{\"Chris\", 33},\n\t\t}\n\n\t\tassertLeague(t, got, want)\n\n\t\t// read again\n\t\tgot = store.GetLeague()\n\t\tassertLeague(t, got, want)\n\t})\n\n\tt.Run(\"get player score\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore := FileSystemPlayerStore{database}\n\n\t\tgot := store.GetPlayerScore(\"Chris\")\n\t\twant := 33\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"store wins for existing players\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore := FileSystemPlayerStore{database}\n\n\t\tstore.RecordWin(\"Chris\")\n\n\t\tgot := store.GetPlayerScore(\"Chris\")\n\t\twant := 34\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"store wins for new players\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore := FileSystemPlayerStore{database}\n\n\t\tstore.RecordWin(\"Pepper\")\n\n\t\tgot := store.GetPlayerScore(\"Pepper\")\n\t\twant := 1\n\t\tassertScoreEquals(t, got, want)\n\t})\n}\n\nfunc assertScoreEquals(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"got %d want %d\", got, want)\n\t}\n}\n"
  },
  {
    "path": "io/v4/in_memory_player_store.go",
    "content": "package main\n\n// NewInMemoryPlayerStore initialises an empty player store.\nfunc NewInMemoryPlayerStore() *InMemoryPlayerStore {\n\treturn &InMemoryPlayerStore{map[string]int{}}\n}\n\n// InMemoryPlayerStore collects data about players in memory.\ntype InMemoryPlayerStore struct {\n\tstore map[string]int\n}\n\n// GetLeague returns a collection of Players.\nfunc (i *InMemoryPlayerStore) GetLeague() League {\n\tvar league []Player\n\tfor name, wins := range i.store {\n\t\tleague = append(league, Player{name, wins})\n\t}\n\treturn league\n}\n\n// RecordWin will record a player's win.\nfunc (i *InMemoryPlayerStore) RecordWin(name string) {\n\ti.store[name]++\n}\n\n// GetPlayerScore retrieves scores for a given player.\nfunc (i *InMemoryPlayerStore) GetPlayerScore(name string) int {\n\treturn i.store[name]\n}\n"
  },
  {
    "path": "io/v4/league.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n)\n\n// League stores a collection of players.\ntype League []Player\n\n// Find tries to return a player from a league.\nfunc (l League) Find(name string) *Player {\n\tfor i, p := range l {\n\t\tif p.Name == name {\n\t\t\treturn &l[i]\n\t\t}\n\t}\n\treturn nil\n}\n\n// NewLeague creates a league from JSON.\nfunc NewLeague(rdr io.Reader) (League, error) {\n\tvar league []Player\n\terr := json.NewDecoder(rdr).Decode(&league)\n\n\tif err != nil {\n\t\terr = fmt.Errorf(\"problem parsing league, %v\", err)\n\t}\n\n\treturn league, err\n}\n"
  },
  {
    "path": "io/v4/main.go",
    "content": "package main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc main() {\n\tserver := NewPlayerServer(NewInMemoryPlayerStore())\n\tlog.Fatal(http.ListenAndServe(\":5000\", server))\n}\n"
  },
  {
    "path": "io/v4/server.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n)\n\n// PlayerStore stores score information about players.\ntype PlayerStore interface {\n\tGetPlayerScore(name string) int\n\tRecordWin(name string)\n\tGetLeague() League\n}\n\n// Player stores a name with a number of wins.\ntype Player struct {\n\tName string\n\tWins int\n}\n\n// PlayerServer is a HTTP interface for player information.\ntype PlayerServer struct {\n\tstore PlayerStore\n\thttp.Handler\n}\n\nconst jsonContentType = \"application/json\"\n\n// NewPlayerServer creates a PlayerServer with routing configured.\nfunc NewPlayerServer(store PlayerStore) *PlayerServer {\n\tp := new(PlayerServer)\n\n\tp.store = store\n\n\trouter := http.NewServeMux()\n\trouter.Handle(\"/league\", http.HandlerFunc(p.leagueHandler))\n\trouter.Handle(\"/players/\", http.HandlerFunc(p.playersHandler))\n\n\tp.Handler = router\n\n\treturn p\n}\n\nfunc (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) {\n\tw.Header().Set(\"content-type\", jsonContentType)\n\tjson.NewEncoder(w).Encode(p.store.GetLeague())\n}\n\nfunc (p *PlayerServer) playersHandler(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\n\tswitch r.Method {\n\tcase http.MethodPost:\n\t\tp.processWin(w, player)\n\tcase http.MethodGet:\n\t\tp.showScore(w, player)\n\t}\n}\n\nfunc (p *PlayerServer) showScore(w http.ResponseWriter, player string) {\n\tscore := p.store.GetPlayerScore(player)\n\n\tif score == 0 {\n\t\tw.WriteHeader(http.StatusNotFound)\n\t}\n\n\tfmt.Fprint(w, score)\n}\n\nfunc (p *PlayerServer) processWin(w http.ResponseWriter, player string) {\n\tp.store.RecordWin(player)\n\tw.WriteHeader(http.StatusAccepted)\n}\n"
  },
  {
    "path": "io/v4/server_integration_test.go",
    "content": "package main\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestRecordingWinsAndRetrievingThem(t *testing.T) {\n\tstore := NewInMemoryPlayerStore()\n\tserver := NewPlayerServer(store)\n\tplayer := \"Pepper\"\n\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\n\tt.Run(\"get score\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newGetScoreRequest(player))\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\n\t\tassertResponseBody(t, response.Body.String(), \"3\")\n\t})\n\n\tt.Run(\"get league\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newLeagueRequest())\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\t\twant := []Player{\n\t\t\t{\"Pepper\", 3},\n\t\t}\n\t\tassertLeague(t, got, want)\n\t})\n}\n"
  },
  {
    "path": "io/v4/server_test.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"reflect\"\n\t\"testing\"\n)\n\ntype StubPlayerStore struct {\n\tscores   map[string]int\n\twinCalls []string\n\tleague   []Player\n}\n\nfunc (s *StubPlayerStore) GetPlayerScore(name string) int {\n\tscore := s.scores[name]\n\treturn score\n}\n\nfunc (s *StubPlayerStore) RecordWin(name string) {\n\ts.winCalls = append(s.winCalls, name)\n}\n\nfunc (s *StubPlayerStore) GetLeague() League {\n\treturn s.league\n}\n\nfunc TestGETPlayers(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{\n\t\t\t\"Pepper\": 20,\n\t\t\t\"Floyd\":  10,\n\t\t},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"returns Pepper's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Pepper\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"20\")\n\t})\n\n\tt.Run(\"returns Floyd's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Floyd\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"10\")\n\t})\n\n\tt.Run(\"returns 404 on missing players\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Apollo\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusNotFound)\n\t})\n}\n\nfunc TestStoreWins(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"it records wins on POST\", func(t *testing.T) {\n\t\tplayer := \"Pepper\"\n\n\t\trequest := newPostWinRequest(player)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusAccepted)\n\n\t\tif len(store.winCalls) != 1 {\n\t\t\tt.Fatalf(\"got %d calls to RecordWin want %d\", len(store.winCalls), 1)\n\t\t}\n\n\t\tif store.winCalls[0] != player {\n\t\t\tt.Errorf(\"did not store correct winner got %q want %q\", store.winCalls[0], player)\n\t\t}\n\t})\n}\n\nfunc TestLeague(t *testing.T) {\n\n\tt.Run(\"it returns the league table as JSON\", func(t *testing.T) {\n\t\twantedLeague := []Player{\n\t\t\t{\"Cleo\", 32},\n\t\t\t{\"Chris\", 20},\n\t\t\t{\"Tiest\", 14},\n\t\t}\n\n\t\tstore := StubPlayerStore{nil, nil, wantedLeague}\n\t\tserver := NewPlayerServer(&store)\n\n\t\trequest := newLeagueRequest()\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertLeague(t, got, wantedLeague)\n\t\tassertContentType(t, response, jsonContentType)\n\n\t})\n}\n\nfunc assertContentType(t testing.TB, response *httptest.ResponseRecorder, want string) {\n\tt.Helper()\n\tif response.Header().Get(\"content-type\") != want {\n\t\tt.Errorf(\"response did not have content-type of %s, got %v\", want, response.Result().Header)\n\t}\n}\n\nfunc getLeagueFromResponse(t testing.TB, body io.Reader) []Player {\n\tt.Helper()\n\tleague, err := NewLeague(body)\n\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to parse response from server %q into slice of Player, '%v'\", body, err)\n\t}\n\n\treturn league\n}\n\nfunc assertLeague(t testing.TB, got, want []Player) {\n\tt.Helper()\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %v want %v\", got, want)\n\t}\n}\n\nfunc assertStatus(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"did not get correct status, got %d, want %d\", got, want)\n\t}\n}\n\nfunc newLeagueRequest() *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, \"/league\", nil)\n\treturn req\n}\n\nfunc newGetScoreRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc newPostWinRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodPost, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc assertResponseBody(t testing.TB, got, want string) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"response body is wrong, got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "io/v5/file_system_store.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"io\"\n)\n\n// FileSystemPlayerStore stores players in the filesystem.\ntype FileSystemPlayerStore struct {\n\tdatabase io.ReadWriteSeeker\n}\n\n// GetLeague returns the scores of all the players.\nfunc (f *FileSystemPlayerStore) GetLeague() League {\n\tf.database.Seek(0, io.SeekStart)\n\tleague, _ := NewLeague(f.database)\n\treturn league\n}\n\n// GetPlayerScore retrieves a player's score.\nfunc (f *FileSystemPlayerStore) GetPlayerScore(name string) int {\n\n\tplayer := f.GetLeague().Find(name)\n\n\tif player != nil {\n\t\treturn player.Wins\n\t}\n\n\treturn 0\n}\n\n// RecordWin will store a win for a player, incrementing wins if already known.\nfunc (f *FileSystemPlayerStore) RecordWin(name string) {\n\tleague := f.GetLeague()\n\tplayer := league.Find(name)\n\n\tif player != nil {\n\t\tplayer.Wins++\n\t} else {\n\t\tleague = append(league, Player{name, 1})\n\t}\n\n\tf.database.Seek(0, io.SeekStart)\n\tjson.NewEncoder(f.database).Encode(league)\n}\n"
  },
  {
    "path": "io/v5/file_system_store_test.go",
    "content": "package main\n\nimport (\n\t\"io\"\n\t\"os\"\n\t\"testing\"\n)\n\nfunc createTempFile(t testing.TB, initialData string) (io.ReadWriteSeeker, func()) {\n\tt.Helper()\n\n\ttmpfile, err := os.CreateTemp(\"\", \"db\")\n\n\tif err != nil {\n\t\tt.Fatalf(\"could not create temp file %v\", err)\n\t}\n\n\ttmpfile.Write([]byte(initialData))\n\n\tremoveFile := func() {\n\t\ttmpfile.Close()\n\t\tos.Remove(tmpfile.Name())\n\t}\n\n\treturn tmpfile, removeFile\n}\n\nfunc TestFileSystemStore(t *testing.T) {\n\n\tt.Run(\"league from a reader\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore := FileSystemPlayerStore{database}\n\n\t\tgot := store.GetLeague()\n\n\t\twant := []Player{\n\t\t\t{\"Cleo\", 10},\n\t\t\t{\"Chris\", 33},\n\t\t}\n\n\t\tassertLeague(t, got, want)\n\n\t\t// read again\n\t\tgot = store.GetLeague()\n\t\tassertLeague(t, got, want)\n\t})\n\n\tt.Run(\"get player score\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore := FileSystemPlayerStore{database}\n\n\t\tgot := store.GetPlayerScore(\"Chris\")\n\t\twant := 33\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"store wins for existing players\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore := FileSystemPlayerStore{database}\n\n\t\tstore.RecordWin(\"Chris\")\n\n\t\tgot := store.GetPlayerScore(\"Chris\")\n\t\twant := 34\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"store wins for new players\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore := FileSystemPlayerStore{database}\n\n\t\tstore.RecordWin(\"Pepper\")\n\n\t\tgot := store.GetPlayerScore(\"Pepper\")\n\t\twant := 1\n\t\tassertScoreEquals(t, got, want)\n\t})\n}\n\nfunc assertScoreEquals(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"got %d want %d\", got, want)\n\t}\n}\n"
  },
  {
    "path": "io/v5/league.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n)\n\n// League stores a collection of players.\ntype League []Player\n\n// Find tries to return a player from a league.\nfunc (l League) Find(name string) *Player {\n\tfor i, p := range l {\n\t\tif p.Name == name {\n\t\t\treturn &l[i]\n\t\t}\n\t}\n\treturn nil\n}\n\n// NewLeague creates a league from JSON.\nfunc NewLeague(rdr io.Reader) (League, error) {\n\tvar league []Player\n\terr := json.NewDecoder(rdr).Decode(&league)\n\n\tif err != nil {\n\t\terr = fmt.Errorf(\"problem parsing league, %v\", err)\n\t}\n\n\treturn league, err\n}\n"
  },
  {
    "path": "io/v5/main.go",
    "content": "package main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n)\n\nconst dbFileName = \"game.db.json\"\n\nfunc main() {\n\tdb, err := os.OpenFile(dbFileName, os.O_RDWR|os.O_CREATE, 0666)\n\n\tif err != nil {\n\t\tlog.Fatalf(\"problem opening %s %v\", dbFileName, err)\n\t}\n\n\tstore := &FileSystemPlayerStore{db}\n\tserver := NewPlayerServer(store)\n\n\tlog.Fatal(http.ListenAndServe(\":5000\", server))\n}\n"
  },
  {
    "path": "io/v5/server.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n)\n\n// PlayerStore stores score information about players.\ntype PlayerStore interface {\n\tGetPlayerScore(name string) int\n\tRecordWin(name string)\n\tGetLeague() League\n}\n\n// Player stores a name with a number of wins.\ntype Player struct {\n\tName string\n\tWins int\n}\n\n// PlayerServer is a HTTP interface for player information.\ntype PlayerServer struct {\n\tstore PlayerStore\n\thttp.Handler\n}\n\nconst jsonContentType = \"application/json\"\n\n// NewPlayerServer creates a PlayerServer with routing configured.\nfunc NewPlayerServer(store PlayerStore) *PlayerServer {\n\tp := new(PlayerServer)\n\n\tp.store = store\n\n\trouter := http.NewServeMux()\n\trouter.Handle(\"/league\", http.HandlerFunc(p.leagueHandler))\n\trouter.Handle(\"/players/\", http.HandlerFunc(p.playersHandler))\n\n\tp.Handler = router\n\n\treturn p\n}\n\nfunc (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) {\n\tw.Header().Set(\"content-type\", jsonContentType)\n\tjson.NewEncoder(w).Encode(p.store.GetLeague())\n}\n\nfunc (p *PlayerServer) playersHandler(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\n\tswitch r.Method {\n\tcase http.MethodPost:\n\t\tp.processWin(w, player)\n\tcase http.MethodGet:\n\t\tp.showScore(w, player)\n\t}\n}\n\nfunc (p *PlayerServer) showScore(w http.ResponseWriter, player string) {\n\tscore := p.store.GetPlayerScore(player)\n\n\tif score == 0 {\n\t\tw.WriteHeader(http.StatusNotFound)\n\t}\n\n\tfmt.Fprint(w, score)\n}\n\nfunc (p *PlayerServer) processWin(w http.ResponseWriter, player string) {\n\tp.store.RecordWin(player)\n\tw.WriteHeader(http.StatusAccepted)\n}\n"
  },
  {
    "path": "io/v5/server_integration_test.go",
    "content": "package main\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestRecordingWinsAndRetrievingThem(t *testing.T) {\n\tdatabase, cleanDatabase := createTempFile(t, \"\")\n\tdefer cleanDatabase()\n\tstore := &FileSystemPlayerStore{database}\n\tserver := NewPlayerServer(store)\n\tplayer := \"Pepper\"\n\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\n\tt.Run(\"get score\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newGetScoreRequest(player))\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\n\t\tassertResponseBody(t, response.Body.String(), \"3\")\n\t})\n\n\tt.Run(\"get league\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newLeagueRequest())\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\t\twant := []Player{\n\t\t\t{\"Pepper\", 3},\n\t\t}\n\t\tassertLeague(t, got, want)\n\t})\n}\n"
  },
  {
    "path": "io/v5/server_test.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"reflect\"\n\t\"testing\"\n)\n\ntype StubPlayerStore struct {\n\tscores   map[string]int\n\twinCalls []string\n\tleague   []Player\n}\n\nfunc (s *StubPlayerStore) GetPlayerScore(name string) int {\n\tscore := s.scores[name]\n\treturn score\n}\n\nfunc (s *StubPlayerStore) RecordWin(name string) {\n\ts.winCalls = append(s.winCalls, name)\n}\n\nfunc (s *StubPlayerStore) GetLeague() League {\n\treturn s.league\n}\n\nfunc TestGETPlayers(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{\n\t\t\t\"Pepper\": 20,\n\t\t\t\"Floyd\":  10,\n\t\t},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"returns Pepper's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Pepper\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"20\")\n\t})\n\n\tt.Run(\"returns Floyd's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Floyd\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"10\")\n\t})\n\n\tt.Run(\"returns 404 on missing players\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Apollo\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusNotFound)\n\t})\n}\n\nfunc TestStoreWins(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"it records wins on POST\", func(t *testing.T) {\n\t\tplayer := \"Pepper\"\n\n\t\trequest := newPostWinRequest(player)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusAccepted)\n\n\t\tif len(store.winCalls) != 1 {\n\t\t\tt.Fatalf(\"got %d calls to RecordWin want %d\", len(store.winCalls), 1)\n\t\t}\n\n\t\tif store.winCalls[0] != player {\n\t\t\tt.Errorf(\"did not store correct winner got %q want %q\", store.winCalls[0], player)\n\t\t}\n\t})\n}\n\nfunc TestLeague(t *testing.T) {\n\n\tt.Run(\"it returns the league table as JSON\", func(t *testing.T) {\n\t\twantedLeague := []Player{\n\t\t\t{\"Cleo\", 32},\n\t\t\t{\"Chris\", 20},\n\t\t\t{\"Tiest\", 14},\n\t\t}\n\n\t\tstore := StubPlayerStore{nil, nil, wantedLeague}\n\t\tserver := NewPlayerServer(&store)\n\n\t\trequest := newLeagueRequest()\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertLeague(t, got, wantedLeague)\n\t\tassertContentType(t, response, jsonContentType)\n\n\t})\n}\n\nfunc assertContentType(t testing.TB, response *httptest.ResponseRecorder, want string) {\n\tt.Helper()\n\tif response.Header().Get(\"content-type\") != want {\n\t\tt.Errorf(\"response did not have content-type of %s, got %v\", want, response.Result().Header)\n\t}\n}\n\nfunc getLeagueFromResponse(t testing.TB, body io.Reader) []Player {\n\tt.Helper()\n\tleague, err := NewLeague(body)\n\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to parse response from server %q into slice of Player, '%v'\", body, err)\n\t}\n\n\treturn league\n}\n\nfunc assertLeague(t testing.TB, got, want []Player) {\n\tt.Helper()\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %v want %v\", got, want)\n\t}\n}\n\nfunc assertStatus(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"did not get correct status, got %d, want %d\", got, want)\n\t}\n}\n\nfunc newLeagueRequest() *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, \"/league\", nil)\n\treturn req\n}\n\nfunc newGetScoreRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc newPostWinRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodPost, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc assertResponseBody(t testing.TB, got, want string) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"response body is wrong, got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "io/v6/file_system_store.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"io\"\n)\n\n// FileSystemPlayerStore stores players in the filesystem.\ntype FileSystemPlayerStore struct {\n\tdatabase io.ReadWriteSeeker\n\tleague   League\n}\n\n// NewFileSystemPlayerStore creates a FileSystemPlayerStore.\nfunc NewFileSystemPlayerStore(database io.ReadWriteSeeker) *FileSystemPlayerStore {\n\tdatabase.Seek(0, io.SeekStart)\n\tleague, _ := NewLeague(database)\n\n\treturn &FileSystemPlayerStore{\n\t\tdatabase: database,\n\t\tleague:   league,\n\t}\n}\n\n// GetLeague returns the scores of all the players.\nfunc (f *FileSystemPlayerStore) GetLeague() League {\n\treturn f.league\n}\n\n// GetPlayerScore retrieves a player's score.\nfunc (f *FileSystemPlayerStore) GetPlayerScore(name string) int {\n\n\tplayer := f.league.Find(name)\n\n\tif player != nil {\n\t\treturn player.Wins\n\t}\n\n\treturn 0\n}\n\n// RecordWin will store a win for a player, incrementing wins if already known.\nfunc (f *FileSystemPlayerStore) RecordWin(name string) {\n\tplayer := f.league.Find(name)\n\n\tif player != nil {\n\t\tplayer.Wins++\n\t} else {\n\t\tf.league = append(f.league, Player{name, 1})\n\t}\n\n\tf.database.Seek(0, io.SeekStart)\n\tjson.NewEncoder(f.database).Encode(f.league)\n}\n"
  },
  {
    "path": "io/v6/file_system_store_test.go",
    "content": "package main\n\nimport (\n\t\"io\"\n\t\"os\"\n\t\"testing\"\n)\n\nfunc createTempFile(t testing.TB, initialData string) (io.ReadWriteSeeker, func()) {\n\tt.Helper()\n\n\ttmpfile, err := os.CreateTemp(\"\", \"db\")\n\n\tif err != nil {\n\t\tt.Fatalf(\"could not create temp file %v\", err)\n\t}\n\n\ttmpfile.Write([]byte(initialData))\n\n\tremoveFile := func() {\n\t\ttmpfile.Close()\n\t\tos.Remove(tmpfile.Name())\n\t}\n\n\treturn tmpfile, removeFile\n}\n\nfunc TestFileSystemStore(t *testing.T) {\n\n\tt.Run(\"league from a reader\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore := NewFileSystemPlayerStore(database)\n\n\t\tgot := store.GetLeague()\n\n\t\twant := []Player{\n\t\t\t{\"Cleo\", 10},\n\t\t\t{\"Chris\", 33},\n\t\t}\n\n\t\tassertLeague(t, got, want)\n\n\t\t// read again\n\t\tgot = store.GetLeague()\n\t\tassertLeague(t, got, want)\n\t})\n\n\tt.Run(\"get player score\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore := NewFileSystemPlayerStore(database)\n\n\t\tgot := store.GetPlayerScore(\"Chris\")\n\t\twant := 33\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"store wins for existing players\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore := NewFileSystemPlayerStore(database)\n\n\t\tstore.RecordWin(\"Chris\")\n\n\t\tgot := store.GetPlayerScore(\"Chris\")\n\t\twant := 34\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"store wins for new players\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore := NewFileSystemPlayerStore(database)\n\n\t\tstore.RecordWin(\"Pepper\")\n\n\t\tgot := store.GetPlayerScore(\"Pepper\")\n\t\twant := 1\n\t\tassertScoreEquals(t, got, want)\n\t})\n}\n\nfunc assertScoreEquals(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"got %d want %d\", got, want)\n\t}\n}\n"
  },
  {
    "path": "io/v6/league.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n)\n\n// League stores a collection of players.\ntype League []Player\n\n// Find tries to return a player from a league.\nfunc (l League) Find(name string) *Player {\n\tfor i, p := range l {\n\t\tif p.Name == name {\n\t\t\treturn &l[i]\n\t\t}\n\t}\n\treturn nil\n}\n\n// NewLeague creates a league from JSON.\nfunc NewLeague(rdr io.Reader) (League, error) {\n\tvar league []Player\n\terr := json.NewDecoder(rdr).Decode(&league)\n\n\tif err != nil {\n\t\terr = fmt.Errorf(\"problem parsing league, %v\", err)\n\t}\n\n\treturn league, err\n}\n"
  },
  {
    "path": "io/v6/main.go",
    "content": "package main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n)\n\nconst dbFileName = \"game.db.json\"\n\nfunc main() {\n\tdb, err := os.OpenFile(dbFileName, os.O_RDWR|os.O_CREATE, 0666)\n\n\tif err != nil {\n\t\tlog.Fatalf(\"problem opening %s %v\", dbFileName, err)\n\t}\n\n\tstore := NewFileSystemPlayerStore(db)\n\n\tserver := NewPlayerServer(store)\n\n\tlog.Fatal(http.ListenAndServe(\":5000\", server))\n}\n"
  },
  {
    "path": "io/v6/server.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n)\n\n// PlayerStore stores score information about players.\ntype PlayerStore interface {\n\tGetPlayerScore(name string) int\n\tRecordWin(name string)\n\tGetLeague() League\n}\n\n// Player stores a name with a number of wins.\ntype Player struct {\n\tName string\n\tWins int\n}\n\n// PlayerServer is a HTTP interface for player information.\ntype PlayerServer struct {\n\tstore PlayerStore\n\thttp.Handler\n}\n\nconst jsonContentType = \"application/json\"\n\n// NewPlayerServer creates a PlayerServer with routing configured.\nfunc NewPlayerServer(store PlayerStore) *PlayerServer {\n\tp := new(PlayerServer)\n\n\tp.store = store\n\n\trouter := http.NewServeMux()\n\trouter.Handle(\"/league\", http.HandlerFunc(p.leagueHandler))\n\trouter.Handle(\"/players/\", http.HandlerFunc(p.playersHandler))\n\n\tp.Handler = router\n\n\treturn p\n}\n\nfunc (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) {\n\tw.Header().Set(\"content-type\", jsonContentType)\n\tjson.NewEncoder(w).Encode(p.store.GetLeague())\n}\n\nfunc (p *PlayerServer) playersHandler(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\n\tswitch r.Method {\n\tcase http.MethodPost:\n\t\tp.processWin(w, player)\n\tcase http.MethodGet:\n\t\tp.showScore(w, player)\n\t}\n}\n\nfunc (p *PlayerServer) showScore(w http.ResponseWriter, player string) {\n\tscore := p.store.GetPlayerScore(player)\n\n\tif score == 0 {\n\t\tw.WriteHeader(http.StatusNotFound)\n\t}\n\n\tfmt.Fprint(w, score)\n}\n\nfunc (p *PlayerServer) processWin(w http.ResponseWriter, player string) {\n\tp.store.RecordWin(player)\n\tw.WriteHeader(http.StatusAccepted)\n}\n"
  },
  {
    "path": "io/v6/server_integration_test.go",
    "content": "package main\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestRecordingWinsAndRetrievingThem(t *testing.T) {\n\tdatabase, cleanDatabase := createTempFile(t, \"\")\n\tdefer cleanDatabase()\n\tstore := NewFileSystemPlayerStore(database)\n\tserver := NewPlayerServer(store)\n\tplayer := \"Pepper\"\n\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\n\tt.Run(\"get score\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newGetScoreRequest(player))\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\n\t\tassertResponseBody(t, response.Body.String(), \"3\")\n\t})\n\n\tt.Run(\"get league\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newLeagueRequest())\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\t\twant := []Player{\n\t\t\t{\"Pepper\", 3},\n\t\t}\n\t\tassertLeague(t, got, want)\n\t})\n}\n"
  },
  {
    "path": "io/v6/server_test.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"reflect\"\n\t\"testing\"\n)\n\ntype StubPlayerStore struct {\n\tscores   map[string]int\n\twinCalls []string\n\tleague   []Player\n}\n\nfunc (s *StubPlayerStore) GetPlayerScore(name string) int {\n\tscore := s.scores[name]\n\treturn score\n}\n\nfunc (s *StubPlayerStore) RecordWin(name string) {\n\ts.winCalls = append(s.winCalls, name)\n}\n\nfunc (s *StubPlayerStore) GetLeague() League {\n\treturn s.league\n}\n\nfunc TestGETPlayers(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{\n\t\t\t\"Pepper\": 20,\n\t\t\t\"Floyd\":  10,\n\t\t},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"returns Pepper's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Pepper\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"20\")\n\t})\n\n\tt.Run(\"returns Floyd's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Floyd\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"10\")\n\t})\n\n\tt.Run(\"returns 404 on missing players\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Apollo\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusNotFound)\n\t})\n}\n\nfunc TestStoreWins(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"it records wins on POST\", func(t *testing.T) {\n\t\tplayer := \"Pepper\"\n\n\t\trequest := newPostWinRequest(player)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusAccepted)\n\n\t\tif len(store.winCalls) != 1 {\n\t\t\tt.Fatalf(\"got %d calls to RecordWin want %d\", len(store.winCalls), 1)\n\t\t}\n\n\t\tif store.winCalls[0] != player {\n\t\t\tt.Errorf(\"did not store correct winner got %q want %q\", store.winCalls[0], player)\n\t\t}\n\t})\n}\n\nfunc TestLeague(t *testing.T) {\n\n\tt.Run(\"it returns the league table as JSON\", func(t *testing.T) {\n\t\twantedLeague := []Player{\n\t\t\t{\"Cleo\", 32},\n\t\t\t{\"Chris\", 20},\n\t\t\t{\"Tiest\", 14},\n\t\t}\n\n\t\tstore := StubPlayerStore{nil, nil, wantedLeague}\n\t\tserver := NewPlayerServer(&store)\n\n\t\trequest := newLeagueRequest()\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertLeague(t, got, wantedLeague)\n\t\tassertContentType(t, response, jsonContentType)\n\n\t})\n}\n\nfunc assertContentType(t testing.TB, response *httptest.ResponseRecorder, want string) {\n\tt.Helper()\n\tif response.Header().Get(\"content-type\") != want {\n\t\tt.Errorf(\"response did not have content-type of %s, got %v\", want, response.Result().Header)\n\t}\n}\n\nfunc getLeagueFromResponse(t testing.TB, body io.Reader) []Player {\n\tt.Helper()\n\tleague, err := NewLeague(body)\n\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to parse response from server %q into slice of Player, '%v'\", body, err)\n\t}\n\n\treturn league\n}\n\nfunc assertLeague(t testing.TB, got, want []Player) {\n\tt.Helper()\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %v want %v\", got, want)\n\t}\n}\n\nfunc assertStatus(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"did not get correct status, got %d, want %d\", got, want)\n\t}\n}\n\nfunc newLeagueRequest() *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, \"/league\", nil)\n\treturn req\n}\n\nfunc newGetScoreRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc newPostWinRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodPost, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc assertResponseBody(t testing.TB, got, want string) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"response body is wrong, got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "io/v7/file_system_store.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"io\"\n\t\"os\"\n)\n\n// FileSystemPlayerStore stores players in the filesystem.\ntype FileSystemPlayerStore struct {\n\tdatabase *json.Encoder\n\tleague   League\n}\n\n// NewFileSystemPlayerStore creates a FileSystemPlayerStore.\nfunc NewFileSystemPlayerStore(file *os.File) *FileSystemPlayerStore {\n\tfile.Seek(0, io.SeekStart)\n\tleague, _ := NewLeague(file)\n\n\treturn &FileSystemPlayerStore{\n\t\tdatabase: json.NewEncoder(&tape{file}),\n\t\tleague:   league,\n\t}\n}\n\n// GetLeague returns the scores of all the players.\nfunc (f *FileSystemPlayerStore) GetLeague() League {\n\treturn f.league\n}\n\n// GetPlayerScore retrieves a player's score.\nfunc (f *FileSystemPlayerStore) GetPlayerScore(name string) int {\n\n\tplayer := f.league.Find(name)\n\n\tif player != nil {\n\t\treturn player.Wins\n\t}\n\n\treturn 0\n}\n\n// RecordWin will store a win for a player, incrementing wins if already known.\nfunc (f *FileSystemPlayerStore) RecordWin(name string) {\n\tplayer := f.league.Find(name)\n\n\tif player != nil {\n\t\tplayer.Wins++\n\t} else {\n\t\tf.league = append(f.league, Player{name, 1})\n\t}\n\n\tf.database.Encode(f.league)\n}\n"
  },
  {
    "path": "io/v7/file_system_store_test.go",
    "content": "package main\n\nimport (\n\t\"os\"\n\t\"testing\"\n)\n\nfunc createTempFile(t testing.TB, initialData string) (*os.File, func()) {\n\tt.Helper()\n\n\ttmpfile, err := os.CreateTemp(\"\", \"db\")\n\n\tif err != nil {\n\t\tt.Fatalf(\"could not create temp file %v\", err)\n\t}\n\n\ttmpfile.Write([]byte(initialData))\n\n\tremoveFile := func() {\n\t\ttmpfile.Close()\n\t\tos.Remove(tmpfile.Name())\n\t}\n\n\treturn tmpfile, removeFile\n}\n\nfunc TestFileSystemStore(t *testing.T) {\n\n\tt.Run(\"league from a reader\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore := NewFileSystemPlayerStore(database)\n\n\t\tgot := store.GetLeague()\n\n\t\twant := []Player{\n\t\t\t{\"Cleo\", 10},\n\t\t\t{\"Chris\", 33},\n\t\t}\n\n\t\tassertLeague(t, got, want)\n\n\t\t// read again\n\t\tgot = store.GetLeague()\n\t\tassertLeague(t, got, want)\n\t})\n\n\tt.Run(\"get player score\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore := NewFileSystemPlayerStore(database)\n\n\t\tgot := store.GetPlayerScore(\"Chris\")\n\t\twant := 33\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"store wins for existing players\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore := NewFileSystemPlayerStore(database)\n\n\t\tstore.RecordWin(\"Chris\")\n\n\t\tgot := store.GetPlayerScore(\"Chris\")\n\t\twant := 34\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"store wins for new players\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore := NewFileSystemPlayerStore(database)\n\n\t\tstore.RecordWin(\"Pepper\")\n\n\t\tgot := store.GetPlayerScore(\"Pepper\")\n\t\twant := 1\n\t\tassertScoreEquals(t, got, want)\n\t})\n}\n\nfunc assertScoreEquals(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"got %d want %d\", got, want)\n\t}\n}\n"
  },
  {
    "path": "io/v7/league.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n)\n\n// League stores a collection of players.\ntype League []Player\n\n// Find tries to return a player from a league.\nfunc (l League) Find(name string) *Player {\n\tfor i, p := range l {\n\t\tif p.Name == name {\n\t\t\treturn &l[i]\n\t\t}\n\t}\n\treturn nil\n}\n\n// NewLeague creates a league from JSON.\nfunc NewLeague(rdr io.Reader) (League, error) {\n\tvar league []Player\n\terr := json.NewDecoder(rdr).Decode(&league)\n\n\tif err != nil {\n\t\terr = fmt.Errorf(\"problem parsing league, %v\", err)\n\t}\n\n\treturn league, err\n}\n"
  },
  {
    "path": "io/v7/main.go",
    "content": "package main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n)\n\nconst dbFileName = \"game.db.json\"\n\nfunc main() {\n\tdb, err := os.OpenFile(dbFileName, os.O_RDWR|os.O_CREATE, 0666)\n\n\tif err != nil {\n\t\tlog.Fatalf(\"problem opening %s %v\", dbFileName, err)\n\t}\n\n\tstore := NewFileSystemPlayerStore(db)\n\n\tserver := NewPlayerServer(store)\n\n\tlog.Fatal(http.ListenAndServe(\":5000\", server))\n}\n"
  },
  {
    "path": "io/v7/server.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n)\n\n// PlayerStore stores score information about players.\ntype PlayerStore interface {\n\tGetPlayerScore(name string) int\n\tRecordWin(name string)\n\tGetLeague() League\n}\n\n// Player stores a name with a number of wins.\ntype Player struct {\n\tName string\n\tWins int\n}\n\n// PlayerServer is a HTTP interface for player information.\ntype PlayerServer struct {\n\tstore PlayerStore\n\thttp.Handler\n}\n\nconst jsonContentType = \"application/json\"\n\n// NewPlayerServer creates a PlayerServer with routing configured.\nfunc NewPlayerServer(store PlayerStore) *PlayerServer {\n\tp := new(PlayerServer)\n\n\tp.store = store\n\n\trouter := http.NewServeMux()\n\trouter.Handle(\"/league\", http.HandlerFunc(p.leagueHandler))\n\trouter.Handle(\"/players/\", http.HandlerFunc(p.playersHandler))\n\n\tp.Handler = router\n\n\treturn p\n}\n\nfunc (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) {\n\tw.Header().Set(\"content-type\", jsonContentType)\n\tjson.NewEncoder(w).Encode(p.store.GetLeague())\n}\n\nfunc (p *PlayerServer) playersHandler(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\n\tswitch r.Method {\n\tcase http.MethodPost:\n\t\tp.processWin(w, player)\n\tcase http.MethodGet:\n\t\tp.showScore(w, player)\n\t}\n}\n\nfunc (p *PlayerServer) showScore(w http.ResponseWriter, player string) {\n\tscore := p.store.GetPlayerScore(player)\n\n\tif score == 0 {\n\t\tw.WriteHeader(http.StatusNotFound)\n\t}\n\n\tfmt.Fprint(w, score)\n}\n\nfunc (p *PlayerServer) processWin(w http.ResponseWriter, player string) {\n\tp.store.RecordWin(player)\n\tw.WriteHeader(http.StatusAccepted)\n}\n"
  },
  {
    "path": "io/v7/server_integration_test.go",
    "content": "package main\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestRecordingWinsAndRetrievingThem(t *testing.T) {\n\tdatabase, cleanDatabase := createTempFile(t, \"\")\n\tdefer cleanDatabase()\n\tstore := NewFileSystemPlayerStore(database)\n\tserver := NewPlayerServer(store)\n\tplayer := \"Pepper\"\n\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\n\tt.Run(\"get score\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newGetScoreRequest(player))\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\n\t\tassertResponseBody(t, response.Body.String(), \"3\")\n\t})\n\n\tt.Run(\"get league\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newLeagueRequest())\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\t\twant := []Player{\n\t\t\t{\"Pepper\", 3},\n\t\t}\n\t\tassertLeague(t, got, want)\n\t})\n}\n"
  },
  {
    "path": "io/v7/server_test.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"reflect\"\n\t\"testing\"\n)\n\ntype StubPlayerStore struct {\n\tscores   map[string]int\n\twinCalls []string\n\tleague   []Player\n}\n\nfunc (s *StubPlayerStore) GetPlayerScore(name string) int {\n\tscore := s.scores[name]\n\treturn score\n}\n\nfunc (s *StubPlayerStore) RecordWin(name string) {\n\ts.winCalls = append(s.winCalls, name)\n}\n\nfunc (s *StubPlayerStore) GetLeague() League {\n\treturn s.league\n}\n\nfunc TestGETPlayers(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{\n\t\t\t\"Pepper\": 20,\n\t\t\t\"Floyd\":  10,\n\t\t},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"returns Pepper's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Pepper\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"20\")\n\t})\n\n\tt.Run(\"returns Floyd's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Floyd\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"10\")\n\t})\n\n\tt.Run(\"returns 404 on missing players\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Apollo\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusNotFound)\n\t})\n}\n\nfunc TestStoreWins(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"it records wins on POST\", func(t *testing.T) {\n\t\tplayer := \"Pepper\"\n\n\t\trequest := newPostWinRequest(player)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusAccepted)\n\n\t\tif len(store.winCalls) != 1 {\n\t\t\tt.Fatalf(\"got %d calls to RecordWin want %d\", len(store.winCalls), 1)\n\t\t}\n\n\t\tif store.winCalls[0] != player {\n\t\t\tt.Errorf(\"did not store correct winner got %q want %q\", store.winCalls[0], player)\n\t\t}\n\t})\n}\n\nfunc TestLeague(t *testing.T) {\n\n\tt.Run(\"it returns the league table as JSON\", func(t *testing.T) {\n\t\twantedLeague := []Player{\n\t\t\t{\"Cleo\", 32},\n\t\t\t{\"Chris\", 20},\n\t\t\t{\"Tiest\", 14},\n\t\t}\n\n\t\tstore := StubPlayerStore{nil, nil, wantedLeague}\n\t\tserver := NewPlayerServer(&store)\n\n\t\trequest := newLeagueRequest()\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertLeague(t, got, wantedLeague)\n\t\tassertContentType(t, response, jsonContentType)\n\n\t})\n}\n\nfunc assertContentType(t testing.TB, response *httptest.ResponseRecorder, want string) {\n\tt.Helper()\n\tif response.Header().Get(\"content-type\") != want {\n\t\tt.Errorf(\"response did not have content-type of %s, got %v\", want, response.Result().Header)\n\t}\n}\n\nfunc getLeagueFromResponse(t testing.TB, body io.Reader) []Player {\n\tt.Helper()\n\tleague, err := NewLeague(body)\n\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to parse response from server %q into slice of Player, '%v'\", body, err)\n\t}\n\n\treturn league\n}\n\nfunc assertLeague(t testing.TB, got, want []Player) {\n\tt.Helper()\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %v want %v\", got, want)\n\t}\n}\n\nfunc assertStatus(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"did not get correct status, got %d, want %d\", got, want)\n\t}\n}\n\nfunc newLeagueRequest() *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, \"/league\", nil)\n\treturn req\n}\n\nfunc newGetScoreRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc newPostWinRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodPost, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc assertResponseBody(t testing.TB, got, want string) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"response body is wrong, got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "io/v7/tape.go",
    "content": "package main\n\nimport (\n\t\"io\"\n\t\"os\"\n)\n\ntype tape struct {\n\tfile *os.File\n}\n\nfunc (t *tape) Write(p []byte) (n int, err error) {\n\tt.file.Truncate(0)\n\tt.file.Seek(0, io.SeekStart)\n\treturn t.file.Write(p)\n}\n"
  },
  {
    "path": "io/v7/tape_test.go",
    "content": "package main\n\nimport (\n\t\"io\"\n\t\"testing\"\n)\n\nfunc TestTape_Write(t *testing.T) {\n\tfile, clean := createTempFile(t, \"12345\")\n\tdefer clean()\n\n\ttape := &tape{file}\n\n\ttape.Write([]byte(\"abc\"))\n\n\tfile.Seek(0, io.SeekStart)\n\tnewFileContents, _ := io.ReadAll(file)\n\n\tgot := string(newFileContents)\n\twant := \"abc\"\n\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "io/v8/file_system_store.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n)\n\n// FileSystemPlayerStore stores players in the filesystem.\ntype FileSystemPlayerStore struct {\n\tdatabase *json.Encoder\n\tleague   League\n}\n\n// NewFileSystemPlayerStore creates a FileSystemPlayerStore initialising the store if needed.\nfunc NewFileSystemPlayerStore(file *os.File) (*FileSystemPlayerStore, error) {\n\n\terr := initialisePlayerDBFile(file)\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"problem initialising player db file, %v\", err)\n\t}\n\n\tleague, err := NewLeague(file)\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"problem loading player store from file %s, %v\", file.Name(), err)\n\t}\n\n\treturn &FileSystemPlayerStore{\n\t\tdatabase: json.NewEncoder(&tape{file}),\n\t\tleague:   league,\n\t}, nil\n}\n\nfunc initialisePlayerDBFile(file *os.File) error {\n\tfile.Seek(0, io.SeekStart)\n\n\tinfo, err := file.Stat()\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"problem getting file info from file %s, %v\", file.Name(), err)\n\t}\n\n\tif info.Size() == 0 {\n\t\tfile.Write([]byte(\"[]\"))\n\t\tfile.Seek(0, io.SeekStart)\n\t}\n\n\treturn nil\n}\n\n// GetLeague returns the scores of all the players.\nfunc (f *FileSystemPlayerStore) GetLeague() League {\n\treturn f.league\n}\n\n// GetPlayerScore retrieves a player's score.\nfunc (f *FileSystemPlayerStore) GetPlayerScore(name string) int {\n\n\tplayer := f.league.Find(name)\n\n\tif player != nil {\n\t\treturn player.Wins\n\t}\n\n\treturn 0\n}\n\n// RecordWin will store a win for a player, incrementing wins if already known.\nfunc (f *FileSystemPlayerStore) RecordWin(name string) {\n\tplayer := f.league.Find(name)\n\n\tif player != nil {\n\t\tplayer.Wins++\n\t} else {\n\t\tf.league = append(f.league, Player{name, 1})\n\t}\n\n\tf.database.Encode(f.league)\n}\n"
  },
  {
    "path": "io/v8/file_system_store_test.go",
    "content": "package main\n\nimport (\n\t\"os\"\n\t\"testing\"\n)\n\nfunc createTempFile(t testing.TB, initialData string) (*os.File, func()) {\n\tt.Helper()\n\n\ttmpfile, err := os.CreateTemp(\"\", \"db\")\n\n\tif err != nil {\n\t\tt.Fatalf(\"could not create temp file %v\", err)\n\t}\n\n\ttmpfile.Write([]byte(initialData))\n\n\tremoveFile := func() {\n\t\ttmpfile.Close()\n\t\tos.Remove(tmpfile.Name())\n\t}\n\n\treturn tmpfile, removeFile\n}\n\nfunc TestFileSystemStore(t *testing.T) {\n\n\tt.Run(\"league from a reader\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tgot := store.GetLeague()\n\n\t\twant := []Player{\n\t\t\t{\"Cleo\", 10},\n\t\t\t{\"Chris\", 33},\n\t\t}\n\n\t\tassertLeague(t, got, want)\n\n\t\t// read again\n\t\tgot = store.GetLeague()\n\t\tassertLeague(t, got, want)\n\t})\n\n\tt.Run(\"get player score\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tgot := store.GetPlayerScore(\"Chris\")\n\t\twant := 33\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"store wins for existing players\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tstore.RecordWin(\"Chris\")\n\n\t\tgot := store.GetPlayerScore(\"Chris\")\n\t\twant := 34\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"store wins for new players\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tstore.RecordWin(\"Pepper\")\n\n\t\tgot := store.GetPlayerScore(\"Pepper\")\n\t\twant := 1\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"works with an empty file\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, \"\")\n\t\tdefer cleanDatabase()\n\n\t\t_, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\t})\n}\n\nfunc assertScoreEquals(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"got %d want %d\", got, want)\n\t}\n}\n\nfunc assertNoError(t testing.TB, err error) {\n\tt.Helper()\n\tif err != nil {\n\t\tt.Fatalf(\"didn't expect an error but got one, %v\", err)\n\t}\n}\n"
  },
  {
    "path": "io/v8/league.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n)\n\n// League stores a collection of players.\ntype League []Player\n\n// Find tries to return a player from a league.\nfunc (l League) Find(name string) *Player {\n\tfor i, p := range l {\n\t\tif p.Name == name {\n\t\t\treturn &l[i]\n\t\t}\n\t}\n\treturn nil\n}\n\n// NewLeague creates a league from JSON.\nfunc NewLeague(rdr io.Reader) (League, error) {\n\tvar league []Player\n\terr := json.NewDecoder(rdr).Decode(&league)\n\n\tif err != nil {\n\t\terr = fmt.Errorf(\"problem parsing league, %v\", err)\n\t}\n\n\treturn league, err\n}\n"
  },
  {
    "path": "io/v8/main.go",
    "content": "package main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n)\n\nconst dbFileName = \"game.db.json\"\n\nfunc main() {\n\tdb, err := os.OpenFile(dbFileName, os.O_RDWR|os.O_CREATE, 0666)\n\n\tif err != nil {\n\t\tlog.Fatalf(\"problem opening %s %v\", dbFileName, err)\n\t}\n\n\tstore, err := NewFileSystemPlayerStore(db)\n\n\tif err != nil {\n\t\tlog.Fatalf(\"problem creating file system player store, %v \", err)\n\t}\n\n\tserver := NewPlayerServer(store)\n\n\tlog.Fatal(http.ListenAndServe(\":5000\", server))\n}\n"
  },
  {
    "path": "io/v8/server.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n)\n\n// PlayerStore stores score information about players.\ntype PlayerStore interface {\n\tGetPlayerScore(name string) int\n\tRecordWin(name string)\n\tGetLeague() League\n}\n\n// Player stores a name with a number of wins.\ntype Player struct {\n\tName string\n\tWins int\n}\n\n// PlayerServer is a HTTP interface for player information.\ntype PlayerServer struct {\n\tstore PlayerStore\n\thttp.Handler\n}\n\nconst jsonContentType = \"application/json\"\n\n// NewPlayerServer creates a PlayerServer with routing configured.\nfunc NewPlayerServer(store PlayerStore) *PlayerServer {\n\tp := new(PlayerServer)\n\n\tp.store = store\n\n\trouter := http.NewServeMux()\n\trouter.Handle(\"/league\", http.HandlerFunc(p.leagueHandler))\n\trouter.Handle(\"/players/\", http.HandlerFunc(p.playersHandler))\n\n\tp.Handler = router\n\n\treturn p\n}\n\nfunc (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) {\n\tw.Header().Set(\"content-type\", jsonContentType)\n\tjson.NewEncoder(w).Encode(p.store.GetLeague())\n}\n\nfunc (p *PlayerServer) playersHandler(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\n\tswitch r.Method {\n\tcase http.MethodPost:\n\t\tp.processWin(w, player)\n\tcase http.MethodGet:\n\t\tp.showScore(w, player)\n\t}\n}\n\nfunc (p *PlayerServer) showScore(w http.ResponseWriter, player string) {\n\tscore := p.store.GetPlayerScore(player)\n\n\tif score == 0 {\n\t\tw.WriteHeader(http.StatusNotFound)\n\t}\n\n\tfmt.Fprint(w, score)\n}\n\nfunc (p *PlayerServer) processWin(w http.ResponseWriter, player string) {\n\tp.store.RecordWin(player)\n\tw.WriteHeader(http.StatusAccepted)\n}\n"
  },
  {
    "path": "io/v8/server_integration_test.go",
    "content": "package main\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestRecordingWinsAndRetrievingThem(t *testing.T) {\n\tdatabase, cleanDatabase := createTempFile(t, `[]`)\n\tdefer cleanDatabase()\n\tstore, err := NewFileSystemPlayerStore(database)\n\n\tassertNoError(t, err)\n\n\tserver := NewPlayerServer(store)\n\tplayer := \"Pepper\"\n\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\n\tt.Run(\"get score\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newGetScoreRequest(player))\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\n\t\tassertResponseBody(t, response.Body.String(), \"3\")\n\t})\n\n\tt.Run(\"get league\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newLeagueRequest())\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\t\twant := []Player{\n\t\t\t{\"Pepper\", 3},\n\t\t}\n\t\tassertLeague(t, got, want)\n\t})\n}\n"
  },
  {
    "path": "io/v8/server_test.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"reflect\"\n\t\"testing\"\n)\n\ntype StubPlayerStore struct {\n\tscores   map[string]int\n\twinCalls []string\n\tleague   []Player\n}\n\nfunc (s *StubPlayerStore) GetPlayerScore(name string) int {\n\tscore := s.scores[name]\n\treturn score\n}\n\nfunc (s *StubPlayerStore) RecordWin(name string) {\n\ts.winCalls = append(s.winCalls, name)\n}\n\nfunc (s *StubPlayerStore) GetLeague() League {\n\treturn s.league\n}\n\nfunc TestGETPlayers(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{\n\t\t\t\"Pepper\": 20,\n\t\t\t\"Floyd\":  10,\n\t\t},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"returns Pepper's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Pepper\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"20\")\n\t})\n\n\tt.Run(\"returns Floyd's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Floyd\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"10\")\n\t})\n\n\tt.Run(\"returns 404 on missing players\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Apollo\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusNotFound)\n\t})\n}\n\nfunc TestStoreWins(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"it records wins on POST\", func(t *testing.T) {\n\t\tplayer := \"Pepper\"\n\n\t\trequest := newPostWinRequest(player)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusAccepted)\n\n\t\tif len(store.winCalls) != 1 {\n\t\t\tt.Fatalf(\"got %d calls to RecordWin want %d\", len(store.winCalls), 1)\n\t\t}\n\n\t\tif store.winCalls[0] != player {\n\t\t\tt.Errorf(\"did not store correct winner got %q want %q\", store.winCalls[0], player)\n\t\t}\n\t})\n}\n\nfunc TestLeague(t *testing.T) {\n\n\tt.Run(\"it returns the league table as JSON\", func(t *testing.T) {\n\t\twantedLeague := []Player{\n\t\t\t{\"Cleo\", 32},\n\t\t\t{\"Chris\", 20},\n\t\t\t{\"Tiest\", 14},\n\t\t}\n\n\t\tstore := StubPlayerStore{nil, nil, wantedLeague}\n\t\tserver := NewPlayerServer(&store)\n\n\t\trequest := newLeagueRequest()\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertLeague(t, got, wantedLeague)\n\t\tassertContentType(t, response, jsonContentType)\n\n\t})\n}\n\nfunc assertContentType(t testing.TB, response *httptest.ResponseRecorder, want string) {\n\tt.Helper()\n\tif response.Header().Get(\"content-type\") != want {\n\t\tt.Errorf(\"response did not have content-type of %s, got %v\", want, response.Result().Header)\n\t}\n}\n\nfunc getLeagueFromResponse(t testing.TB, body io.Reader) []Player {\n\tt.Helper()\n\tleague, err := NewLeague(body)\n\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to parse response from server %q into slice of Player, '%v'\", body, err)\n\t}\n\n\treturn league\n}\n\nfunc assertLeague(t testing.TB, got, want []Player) {\n\tt.Helper()\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %v want %v\", got, want)\n\t}\n}\n\nfunc assertStatus(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"did not get correct status, got %d, want %d\", got, want)\n\t}\n}\n\nfunc newLeagueRequest() *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, \"/league\", nil)\n\treturn req\n}\n\nfunc newGetScoreRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc newPostWinRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodPost, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc assertResponseBody(t testing.TB, got, want string) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"response body is wrong, got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "io/v8/tape.go",
    "content": "package main\n\nimport (\n\t\"io\"\n\t\"os\"\n)\n\ntype tape struct {\n\tfile *os.File\n}\n\nfunc (t *tape) Write(p []byte) (n int, err error) {\n\tt.file.Truncate(0)\n\tt.file.Seek(0, io.SeekStart)\n\treturn t.file.Write(p)\n}\n"
  },
  {
    "path": "io/v8/tape_test.go",
    "content": "package main\n\nimport (\n\t\"io\"\n\t\"testing\"\n)\n\nfunc TestTape_Write(t *testing.T) {\n\tfile, clean := createTempFile(t, \"12345\")\n\tdefer clean()\n\n\ttape := &tape{file}\n\n\ttape.Write([]byte(\"abc\"))\n\n\tfile.Seek(0, io.SeekStart)\n\tnewFileContents, _ := io.ReadAll(file)\n\n\tgot := string(newFileContents)\n\twant := \"abc\"\n\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "io/v9/file_system_store.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"sort\"\n)\n\n// FileSystemPlayerStore stores players in the filesystem.\ntype FileSystemPlayerStore struct {\n\tdatabase *json.Encoder\n\tleague   League\n}\n\n// NewFileSystemPlayerStore creates a FileSystemPlayerStore initialising the store if needed.\nfunc NewFileSystemPlayerStore(file *os.File) (*FileSystemPlayerStore, error) {\n\n\terr := initialisePlayerDBFile(file)\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"problem initialising player db file, %v\", err)\n\t}\n\n\tleague, err := NewLeague(file)\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"problem loading player store from file %s, %v\", file.Name(), err)\n\t}\n\n\treturn &FileSystemPlayerStore{\n\t\tdatabase: json.NewEncoder(&tape{file}),\n\t\tleague:   league,\n\t}, nil\n}\n\nfunc initialisePlayerDBFile(file *os.File) error {\n\tfile.Seek(0, io.SeekStart)\n\n\tinfo, err := file.Stat()\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"problem getting file info from file %s, %v\", file.Name(), err)\n\t}\n\n\tif info.Size() == 0 {\n\t\tfile.Write([]byte(\"[]\"))\n\t\tfile.Seek(0, io.SeekStart)\n\t}\n\n\treturn nil\n}\n\n// GetLeague returns the scores of all the players.\nfunc (f *FileSystemPlayerStore) GetLeague() League {\n\tsort.Slice(f.league, func(i, j int) bool {\n\t\treturn f.league[i].Wins > f.league[j].Wins\n\t})\n\treturn f.league\n}\n\n// GetPlayerScore retrieves a player's score.\nfunc (f *FileSystemPlayerStore) GetPlayerScore(name string) int {\n\n\tplayer := f.league.Find(name)\n\n\tif player != nil {\n\t\treturn player.Wins\n\t}\n\n\treturn 0\n}\n\n// RecordWin will store a win for a player, incrementing wins if already known.\nfunc (f *FileSystemPlayerStore) RecordWin(name string) {\n\tplayer := f.league.Find(name)\n\n\tif player != nil {\n\t\tplayer.Wins++\n\t} else {\n\t\tf.league = append(f.league, Player{name, 1})\n\t}\n\n\tf.database.Encode(f.league)\n}\n"
  },
  {
    "path": "io/v9/file_system_store_test.go",
    "content": "package main\n\nimport (\n\t\"os\"\n\t\"testing\"\n)\n\nfunc createTempFile(t testing.TB, initialData string) (*os.File, func()) {\n\tt.Helper()\n\n\ttmpfile, err := os.CreateTemp(\"\", \"db\")\n\n\tif err != nil {\n\t\tt.Fatalf(\"could not create temp file %v\", err)\n\t}\n\n\ttmpfile.Write([]byte(initialData))\n\n\tremoveFile := func() {\n\t\ttmpfile.Close()\n\t\tos.Remove(tmpfile.Name())\n\t}\n\n\treturn tmpfile, removeFile\n}\n\nfunc TestFileSystemStore(t *testing.T) {\n\n\tt.Run(\"league sorted\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tgot := store.GetLeague()\n\n\t\twant := []Player{\n\t\t\t{\"Chris\", 33},\n\t\t\t{\"Cleo\", 10},\n\t\t}\n\n\t\tassertLeague(t, got, want)\n\n\t\t// read again\n\t\tgot = store.GetLeague()\n\t\tassertLeague(t, got, want)\n\t})\n\n\tt.Run(\"get player score\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tgot := store.GetPlayerScore(\"Chris\")\n\t\twant := 33\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"store wins for existing players\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tstore.RecordWin(\"Chris\")\n\n\t\tgot := store.GetPlayerScore(\"Chris\")\n\t\twant := 34\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"store wins for new players\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tstore.RecordWin(\"Pepper\")\n\n\t\tgot := store.GetPlayerScore(\"Pepper\")\n\t\twant := 1\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"works with an empty file\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, \"\")\n\t\tdefer cleanDatabase()\n\n\t\t_, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\t})\n}\n\nfunc assertScoreEquals(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"got %d want %d\", got, want)\n\t}\n}\n\nfunc assertNoError(t testing.TB, err error) {\n\tt.Helper()\n\tif err != nil {\n\t\tt.Fatalf(\"didn't expect an error but got one, %v\", err)\n\t}\n}\n"
  },
  {
    "path": "io/v9/league.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n)\n\n// League stores a collection of players.\ntype League []Player\n\n// Find tries to return a player from a league.\nfunc (l League) Find(name string) *Player {\n\tfor i, p := range l {\n\t\tif p.Name == name {\n\t\t\treturn &l[i]\n\t\t}\n\t}\n\treturn nil\n}\n\n// NewLeague creates a league from JSON.\nfunc NewLeague(rdr io.Reader) (League, error) {\n\tvar league []Player\n\terr := json.NewDecoder(rdr).Decode(&league)\n\n\tif err != nil {\n\t\terr = fmt.Errorf(\"problem parsing league, %v\", err)\n\t}\n\n\treturn league, err\n}\n"
  },
  {
    "path": "io/v9/main.go",
    "content": "package main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n)\n\nconst dbFileName = \"game.db.json\"\n\nfunc main() {\n\tdb, err := os.OpenFile(dbFileName, os.O_RDWR|os.O_CREATE, 0666)\n\n\tif err != nil {\n\t\tlog.Fatalf(\"problem opening %s %v\", dbFileName, err)\n\t}\n\n\tstore, err := NewFileSystemPlayerStore(db)\n\n\tif err != nil {\n\t\tlog.Fatalf(\"problem creating file system player store, %v \", err)\n\t}\n\n\tserver := NewPlayerServer(store)\n\n\tlog.Fatal(http.ListenAndServe(\":5000\", server))\n}\n"
  },
  {
    "path": "io/v9/server.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n)\n\n// PlayerStore stores score information about players.\ntype PlayerStore interface {\n\tGetPlayerScore(name string) int\n\tRecordWin(name string)\n\tGetLeague() League\n}\n\n// Player stores a name with a number of wins.\ntype Player struct {\n\tName string\n\tWins int\n}\n\n// PlayerServer is a HTTP interface for player information.\ntype PlayerServer struct {\n\tstore PlayerStore\n\thttp.Handler\n}\n\nconst jsonContentType = \"application/json\"\n\n// NewPlayerServer creates a PlayerServer with routing configured.\nfunc NewPlayerServer(store PlayerStore) *PlayerServer {\n\tp := new(PlayerServer)\n\n\tp.store = store\n\n\trouter := http.NewServeMux()\n\trouter.Handle(\"/league\", http.HandlerFunc(p.leagueHandler))\n\trouter.Handle(\"/players/\", http.HandlerFunc(p.playersHandler))\n\n\tp.Handler = router\n\n\treturn p\n}\n\nfunc (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) {\n\tw.Header().Set(\"content-type\", jsonContentType)\n\tjson.NewEncoder(w).Encode(p.store.GetLeague())\n}\n\nfunc (p *PlayerServer) playersHandler(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\n\tswitch r.Method {\n\tcase http.MethodPost:\n\t\tp.processWin(w, player)\n\tcase http.MethodGet:\n\t\tp.showScore(w, player)\n\t}\n}\n\nfunc (p *PlayerServer) showScore(w http.ResponseWriter, player string) {\n\tscore := p.store.GetPlayerScore(player)\n\n\tif score == 0 {\n\t\tw.WriteHeader(http.StatusNotFound)\n\t}\n\n\tfmt.Fprint(w, score)\n}\n\nfunc (p *PlayerServer) processWin(w http.ResponseWriter, player string) {\n\tp.store.RecordWin(player)\n\tw.WriteHeader(http.StatusAccepted)\n}\n"
  },
  {
    "path": "io/v9/server_integration_test.go",
    "content": "package main\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestRecordingWinsAndRetrievingThem(t *testing.T) {\n\tdatabase, cleanDatabase := createTempFile(t, `[]`)\n\tdefer cleanDatabase()\n\tstore, err := NewFileSystemPlayerStore(database)\n\n\tassertNoError(t, err)\n\n\tserver := NewPlayerServer(store)\n\tplayer := \"Pepper\"\n\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\n\tt.Run(\"get score\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newGetScoreRequest(player))\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\n\t\tassertResponseBody(t, response.Body.String(), \"3\")\n\t})\n\n\tt.Run(\"get league\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newLeagueRequest())\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\t\twant := []Player{\n\t\t\t{\"Pepper\", 3},\n\t\t}\n\t\tassertLeague(t, got, want)\n\t})\n}\n"
  },
  {
    "path": "io/v9/server_test.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"reflect\"\n\t\"testing\"\n)\n\ntype StubPlayerStore struct {\n\tscores   map[string]int\n\twinCalls []string\n\tleague   []Player\n}\n\nfunc (s *StubPlayerStore) GetPlayerScore(name string) int {\n\tscore := s.scores[name]\n\treturn score\n}\n\nfunc (s *StubPlayerStore) RecordWin(name string) {\n\ts.winCalls = append(s.winCalls, name)\n}\n\nfunc (s *StubPlayerStore) GetLeague() League {\n\treturn s.league\n}\n\nfunc TestGETPlayers(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{\n\t\t\t\"Pepper\": 20,\n\t\t\t\"Floyd\":  10,\n\t\t},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"returns Pepper's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Pepper\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"20\")\n\t})\n\n\tt.Run(\"returns Floyd's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Floyd\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"10\")\n\t})\n\n\tt.Run(\"returns 404 on missing players\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Apollo\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusNotFound)\n\t})\n}\n\nfunc TestStoreWins(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"it records wins on POST\", func(t *testing.T) {\n\t\tplayer := \"Pepper\"\n\n\t\trequest := newPostWinRequest(player)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusAccepted)\n\n\t\tif len(store.winCalls) != 1 {\n\t\t\tt.Fatalf(\"got %d calls to RecordWin want %d\", len(store.winCalls), 1)\n\t\t}\n\n\t\tif store.winCalls[0] != player {\n\t\t\tt.Errorf(\"did not store correct winner got %q want %q\", store.winCalls[0], player)\n\t\t}\n\t})\n}\n\nfunc TestLeague(t *testing.T) {\n\n\tt.Run(\"it returns the league table as JSON\", func(t *testing.T) {\n\t\twantedLeague := []Player{\n\t\t\t{\"Cleo\", 32},\n\t\t\t{\"Chris\", 20},\n\t\t\t{\"Tiest\", 14},\n\t\t}\n\n\t\tstore := StubPlayerStore{nil, nil, wantedLeague}\n\t\tserver := NewPlayerServer(&store)\n\n\t\trequest := newLeagueRequest()\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertLeague(t, got, wantedLeague)\n\t\tassertContentType(t, response, jsonContentType)\n\n\t})\n}\n\nfunc assertContentType(t testing.TB, response *httptest.ResponseRecorder, want string) {\n\tt.Helper()\n\tif response.Header().Get(\"content-type\") != want {\n\t\tt.Errorf(\"response did not have content-type of %s, got %v\", want, response.Result().Header)\n\t}\n}\n\nfunc getLeagueFromResponse(t testing.TB, body io.Reader) []Player {\n\tt.Helper()\n\tleague, err := NewLeague(body)\n\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to parse response from server %q into slice of Player, '%v'\", body, err)\n\t}\n\n\treturn league\n}\n\nfunc assertLeague(t testing.TB, got, want []Player) {\n\tt.Helper()\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %v want %v\", got, want)\n\t}\n}\n\nfunc assertStatus(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"did not get correct status, got %d, want %d\", got, want)\n\t}\n}\n\nfunc newLeagueRequest() *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, \"/league\", nil)\n\treturn req\n}\n\nfunc newGetScoreRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc newPostWinRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodPost, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc assertResponseBody(t testing.TB, got, want string) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"response body is wrong, got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "io/v9/tape.go",
    "content": "package main\n\nimport (\n\t\"io\"\n\t\"os\"\n)\n\ntype tape struct {\n\tfile *os.File\n}\n\nfunc (t *tape) Write(p []byte) (n int, err error) {\n\tt.file.Truncate(0)\n\tt.file.Seek(0, io.SeekStart)\n\treturn t.file.Write(p)\n}\n"
  },
  {
    "path": "io/v9/tape_test.go",
    "content": "package main\n\nimport (\n\t\"io\"\n\t\"testing\"\n)\n\nfunc TestTape_Write(t *testing.T) {\n\tfile, clean := createTempFile(t, \"12345\")\n\tdefer clean()\n\n\ttape := &tape{file}\n\n\ttape.Write([]byte(\"abc\"))\n\n\tfile.Seek(0, io.SeekStart)\n\tnewFileContents, _ := io.ReadAll(file)\n\n\tgot := string(newFileContents)\n\twant := \"abc\"\n\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "io.md",
    "content": "# IO and sorting\n\n**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/main/io)**\n\n[In the previous chapter](json.md) we continued iterating on our application by adding a new endpoint `/league`. Along the way we learned about how to deal with JSON, embedding types and routing.\n\nOur product owner is somewhat perturbed by the software losing the scores when the server was restarted. This is because our implementation of our store is in-memory. She is also not pleased that we didn't interpret the `/league` endpoint should return the players ordered by the number of wins!\n\n## The code so far\n\n```go\n// server.go\npackage main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n)\n\n// PlayerStore stores score information about players\ntype PlayerStore interface {\n\tGetPlayerScore(name string) int\n\tRecordWin(name string)\n\tGetLeague() []Player\n}\n\n// Player stores a name with a number of wins\ntype Player struct {\n\tName string\n\tWins int\n}\n\n// PlayerServer is a HTTP interface for player information\ntype PlayerServer struct {\n\tstore PlayerStore\n\thttp.Handler\n}\n\nconst jsonContentType = \"application/json\"\n\n// NewPlayerServer creates a PlayerServer with routing configured\nfunc NewPlayerServer(store PlayerStore) *PlayerServer {\n\tp := new(PlayerServer)\n\n\tp.store = store\n\n\trouter := http.NewServeMux()\n\trouter.Handle(\"/league\", http.HandlerFunc(p.leagueHandler))\n\trouter.Handle(\"/players/\", http.HandlerFunc(p.playersHandler))\n\n\tp.Handler = router\n\n\treturn p\n}\n\nfunc (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) {\n\tw.Header().Set(\"content-type\", jsonContentType)\n\tjson.NewEncoder(w).Encode(p.store.GetLeague())\n}\n\nfunc (p *PlayerServer) playersHandler(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\n\tswitch r.Method {\n\tcase http.MethodPost:\n\t\tp.processWin(w, player)\n\tcase http.MethodGet:\n\t\tp.showScore(w, player)\n\t}\n}\n\nfunc (p *PlayerServer) showScore(w http.ResponseWriter, player string) {\n\tscore := p.store.GetPlayerScore(player)\n\n\tif score == 0 {\n\t\tw.WriteHeader(http.StatusNotFound)\n\t}\n\n\tfmt.Fprint(w, score)\n}\n\nfunc (p *PlayerServer) processWin(w http.ResponseWriter, player string) {\n\tp.store.RecordWin(player)\n\tw.WriteHeader(http.StatusAccepted)\n}\n```\n\n```go\n// in_memory_player_store.go\npackage main\n\nfunc NewInMemoryPlayerStore() *InMemoryPlayerStore {\n\treturn &InMemoryPlayerStore{map[string]int{}}\n}\n\ntype InMemoryPlayerStore struct {\n\tstore map[string]int\n}\n\nfunc (i *InMemoryPlayerStore) GetLeague() []Player {\n\tvar league []Player\n\tfor name, wins := range i.store {\n\t\tleague = append(league, Player{name, wins})\n\t}\n\treturn league\n}\n\nfunc (i *InMemoryPlayerStore) RecordWin(name string) {\n\ti.store[name]++\n}\n\nfunc (i *InMemoryPlayerStore) GetPlayerScore(name string) int {\n\treturn i.store[name]\n}\n```\n\n```go\n// main.go\npackage main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc main() {\n\tserver := NewPlayerServer(NewInMemoryPlayerStore())\n\tlog.Fatal(http.ListenAndServe(\":5000\", server))\n}\n```\n\nYou can find the corresponding tests in the link at the top of the chapter.\n\n## Store the data\n\nThere are dozens of databases we could use for this but we're going to go for a very simple approach. We're going to store the data for this application in a file as JSON.\n\nThis keeps the data very portable and is relatively simple to implement.\n\nIt won't scale especially well but given this is a prototype it'll be fine for now. If our circumstances change and it's no longer appropriate it'll be simple to swap it out for something different because of the `PlayerStore` abstraction we have used.\n\nWe will keep the `InMemoryPlayerStore` for now so that the integration tests keep passing as we develop our new store. Once we are confident our new implementation is sufficient to make the integration test pass we will swap it in and then delete `InMemoryPlayerStore`.\n\n## Write the test first\n\nBy now you should be familiar with the interfaces around the standard library for reading data (`io.Reader`), writing data (`io.Writer`) and how we can use the standard library to test these functions without having to use real files.\n\nFor this work to be complete we'll need to implement `PlayerStore` so we'll write tests for our store calling the methods we need to implement. We'll start with `GetLeague`.\n\n```go\n//file_system_store_test.go\nfunc TestFileSystemStore(t *testing.T) {\n\n\tt.Run(\"league from a reader\", func(t *testing.T) {\n\t\tdatabase := strings.NewReader(`[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\n\t\tstore := FileSystemPlayerStore{database}\n\n\t\tgot := store.GetLeague()\n\n\t\twant := []Player{\n\t\t\t{\"Cleo\", 10},\n\t\t\t{\"Chris\", 33},\n\t\t}\n\n\t\tassertLeague(t, got, want)\n\t})\n}\n```\n\nWe're using `strings.NewReader` which will return us a `Reader`, which is what our `FileSystemPlayerStore` will use to read data. In `main` we will open a file, which is also a `Reader`.\n\n## Try to run the test\n\n```\n# github.com/quii/learn-go-with-tests/io/v1\n./file_system_store_test.go:15:12: undefined: FileSystemPlayerStore\n```\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\nLet's define `FileSystemPlayerStore` in a new file\n\n```go\n//file_system_store.go\ntype FileSystemPlayerStore struct{}\n```\n\nTry again\n\n```\n# github.com/quii/learn-go-with-tests/io/v1\n./file_system_store_test.go:15:28: too many values in struct initializer\n./file_system_store_test.go:17:15: store.GetLeague undefined (type FileSystemPlayerStore has no field or method GetLeague)\n```\n\nIt's complaining because we're passing in a `Reader` but not expecting one and it doesn't have `GetLeague` defined yet.\n\n```go\n//file_system_store.go\ntype FileSystemPlayerStore struct {\n\tdatabase io.Reader\n}\n\nfunc (f *FileSystemPlayerStore) GetLeague() []Player {\n\treturn nil\n}\n```\n\nOne more try...\n\n```\n=== RUN   TestFileSystemStore//league_from_a_reader\n    --- FAIL: TestFileSystemStore//league_from_a_reader (0.00s)\n        file_system_store_test.go:24: got [] want [{Cleo 10} {Chris 33}]\n```\n\n## Write enough code to make it pass\n\nWe've read JSON from a reader before\n\n```go\n//file_system_store.go\nfunc (f *FileSystemPlayerStore) GetLeague() []Player {\n\tvar league []Player\n\tjson.NewDecoder(f.database).Decode(&league)\n\treturn league\n}\n```\n\nThe test should pass.\n\n## Refactor\n\nWe _have_ done this before! Our test code for the server had to decode the JSON from the response.\n\nLet's try DRYing this up into a function.\n\nCreate a new file called `league.go` and put this inside.\n\n```go\n//league.go\nfunc NewLeague(rdr io.Reader) ([]Player, error) {\n\tvar league []Player\n\terr := json.NewDecoder(rdr).Decode(&league)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"problem parsing league, %v\", err)\n\t}\n\n\treturn league, err\n}\n```\n\nCall this in our implementation and in our test helper `getLeagueFromResponse` in `server_test.go`\n\n```go\n//file_system_store.go\nfunc (f *FileSystemPlayerStore) GetLeague() []Player {\n\tleague, _ := NewLeague(f.database)\n\treturn league\n}\n```\n\nWe haven't got a strategy yet for dealing with parsing errors but let's press on.\n\n### Seeking problems\n\nThere is a flaw in our implementation. First of all, let's remind ourselves how `io.Reader` is defined.\n\n```go\ntype Reader interface {\n\tRead(p []byte) (n int, err error)\n}\n```\n\nWith our file, you can imagine it reading through byte by byte until the end. What happens if you try to `Read` a second time?\n\nAdd the following to the end of our current test.\n\n```go\n//file_system_store_test.go\n\n// read again\ngot = store.GetLeague()\nassertLeague(t, got, want)\n```\n\nWe want this to pass, but if you run the test it doesn't.\n\nThe problem is our `Reader` has reached the end so there is nothing more to read. We need a way to tell it to go back to the start.\n\n[ReadSeeker](https://golang.org/pkg/io/#ReadSeeker) is another interface in the standard library that can help.\n\n```go\ntype ReadSeeker interface {\n\tReader\n\tSeeker\n}\n```\n\nRemember embedding? This is an interface comprised of `Reader` and [`Seeker`](https://golang.org/pkg/io/#Seeker)\n\n```go\ntype Seeker interface {\n\tSeek(offset int64, whence int) (int64, error)\n}\n```\n\nThis sounds good, can we change `FileSystemPlayerStore` to take this interface instead?\n\n```go\n//file_system_store.go\ntype FileSystemPlayerStore struct {\n\tdatabase io.ReadSeeker\n}\n\nfunc (f *FileSystemPlayerStore) GetLeague() []Player {\n\tf.database.Seek(0, io.SeekStart)\n\tleague, _ := NewLeague(f.database)\n\treturn league\n}\n```\n\nTry running the test, it now passes! Happily for us `strings.NewReader` that we used in our test also implements `ReadSeeker` so we didn't have to make any other changes.\n\nNext we'll implement `GetPlayerScore`.\n\n## Write the test first\n\n```go\n//file_system_store_test.go\nt.Run(\"get player score\", func(t *testing.T) {\n\tdatabase := strings.NewReader(`[\n\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\n\tstore := FileSystemPlayerStore{database}\n\n\tgot := store.GetPlayerScore(\"Chris\")\n\n\twant := 33\n\n\tif got != want {\n\t\tt.Errorf(\"got %d want %d\", got, want)\n\t}\n})\n```\n\n## Try to run the test\n\n```\n./file_system_store_test.go:38:15: store.GetPlayerScore undefined (type FileSystemPlayerStore has no field or method GetPlayerScore)\n```\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\nWe need to add the method to our new type to get the test to compile.\n\n```go\n//file_system_store.go\nfunc (f *FileSystemPlayerStore) GetPlayerScore(name string) int {\n\treturn 0\n}\n```\n\nNow it compiles and the test fails\n\n```\n=== RUN   TestFileSystemStore/get_player_score\n    --- FAIL: TestFileSystemStore//get_player_score (0.00s)\n        file_system_store_test.go:43: got 0 want 33\n```\n\n## Write enough code to make it pass\n\nWe can iterate over the league to find the player and return their score\n\n```go\n//file_system_store.go\nfunc (f *FileSystemPlayerStore) GetPlayerScore(name string) int {\n\n\tvar wins int\n\n\tfor _, player := range f.GetLeague() {\n\t\tif player.Name == name {\n\t\t\twins = player.Wins\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn wins\n}\n```\n\n## Refactor\n\nYou will have seen dozens of test helper refactorings so I'll leave this to you to make it work\n\n```go\n//file_system_store_test.go\nt.Run(\"get player score\", func(t *testing.T) {\n\tdatabase := strings.NewReader(`[\n\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\n\tstore := FileSystemPlayerStore{database}\n\n\tgot := store.GetPlayerScore(\"Chris\")\n\twant := 33\n\tassertScoreEquals(t, got, want)\n})\n```\n\nFinally, we need to start recording scores with `RecordWin`.\n\n## Write the test first\n\nOur approach is fairly short-sighted for writes. We can't (easily) just update one \"row\" of JSON in a file. We'll need to store the _whole_ new representation of our database on every write.\n\nHow do we write? We'd normally use a `Writer` but we already have our `ReadSeeker`. Potentially we could have two dependencies but the standard library already has an interface for us `ReadWriteSeeker` which lets us do all the things we'll need to do with a file.\n\nLet's update our type\n\n```go\n//file_system_store.go\ntype FileSystemPlayerStore struct {\n\tdatabase io.ReadWriteSeeker\n}\n```\n\nSee if it compiles\n\n```\n./file_system_store_test.go:15:34: cannot use database (type *strings.Reader) as type io.ReadWriteSeeker in field value:\n    *strings.Reader does not implement io.ReadWriteSeeker (missing Write method)\n./file_system_store_test.go:36:34: cannot use database (type *strings.Reader) as type io.ReadWriteSeeker in field value:\n    *strings.Reader does not implement io.ReadWriteSeeker (missing Write method)\n```\n\nIt's not too surprising that `strings.Reader` does not implement `ReadWriteSeeker` so what do we do?\n\nWe have two choices\n\n- Create a temporary file for each test. `*os.File` implements `ReadWriteSeeker`. The pro of this is it becomes more of an integration test, we're really reading and writing from the file system so it will give us a very high level of confidence. The cons are we prefer unit tests because they are faster and generally simpler. We will also need to do more work around creating temporary files and then making sure they're removed after the test.\n- We could use a third party library. [Mattetti](https://github.com/mattetti) has written a library [filebuffer](https://github.com/mattetti/filebuffer) which implements the interface we need and doesn't touch the file system.\n\nI don't think there's an especially wrong answer here, but by choosing to use a third party library I would have to explain dependency management! So we will use files instead.\n\nBefore adding our test we need to make our other tests compile by replacing the `strings.Reader` with an `os.File`.\n\nLet's create some helper functions which will create a temporary file with some data inside it, and abstract our score tests\n\n```go\n//file_system_store_test.go\nfunc createTempFile(t testing.TB, initialData string) (io.ReadWriteSeeker, func()) {\n\tt.Helper()\n\n\ttmpfile, err := os.CreateTemp(\"\", \"db\")\n\n\tif err != nil {\n\t\tt.Fatalf(\"could not create temp file %v\", err)\n\t}\n\n\ttmpfile.Write([]byte(initialData))\n\n\tremoveFile := func() {\n\t\ttmpfile.Close()\n\t\tos.Remove(tmpfile.Name())\n\t}\n\n\treturn tmpfile, removeFile\n}\n\nfunc assertScoreEquals(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"got %d want %d\", got, want)\n\t}\n}\n```\n\n[CreateTemp](https://pkg.go.dev/os#CreateTemp) creates a temporary file for us to use. The `\"db\"` value we've passed in is a prefix put on a random file name it will create. This is to ensure it won't clash with other files by accident.\n\nYou'll notice we're not only returning our `ReadWriteSeeker` (the file) but also a function. We need to make sure that the file is removed once the test is finished. We don't want to leak details of the files into the test as it's prone to error and uninteresting for the reader. By returning a `removeFile` function, we can take care of the details in our helper and all the caller has to do is run `defer cleanDatabase()`.\n\n```go\n//file_system_store_test.go\nfunc TestFileSystemStore(t *testing.T) {\n\n\tt.Run(\"league from a reader\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore := FileSystemPlayerStore{database}\n\n\t\tgot := store.GetLeague()\n\n\t\twant := []Player{\n\t\t\t{\"Cleo\", 10},\n\t\t\t{\"Chris\", 33},\n\t\t}\n\n\t\tassertLeague(t, got, want)\n\n\t\t// read again\n\t\tgot = store.GetLeague()\n\t\tassertLeague(t, got, want)\n\t})\n\n\tt.Run(\"get player score\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore := FileSystemPlayerStore{database}\n\n\t\tgot := store.GetPlayerScore(\"Chris\")\n\t\twant := 33\n\t\tassertScoreEquals(t, got, want)\n\t})\n}\n```\n\nRun the tests and they should be passing! There were a fair amount of changes but now it feels like we have our interface definition complete and it should be very easy to add new tests from now.\n\nLet's get the first iteration of recording a win for an existing player\n\n```go\n//file_system_store_test.go\nt.Run(\"store wins for existing players\", func(t *testing.T) {\n\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\tdefer cleanDatabase()\n\n\tstore := FileSystemPlayerStore{database}\n\n\tstore.RecordWin(\"Chris\")\n\n\tgot := store.GetPlayerScore(\"Chris\")\n\twant := 34\n\tassertScoreEquals(t, got, want)\n})\n```\n\n## Try to run the test\n\n`./file_system_store_test.go:67:8: store.RecordWin undefined (type FileSystemPlayerStore has no field or method RecordWin)`\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\nAdd the new method\n\n```go\n//file_system_store.go\nfunc (f *FileSystemPlayerStore) RecordWin(name string) {\n\n}\n```\n\n```\n=== RUN   TestFileSystemStore/store_wins_for_existing_players\n    --- FAIL: TestFileSystemStore/store_wins_for_existing_players (0.00s)\n        file_system_store_test.go:71: got 33 want 34\n```\n\nOur implementation is empty so the old score is getting returned.\n\n## Write enough code to make it pass\n\n```go\n//file_system_store.go\nfunc (f *FileSystemPlayerStore) RecordWin(name string) {\n\tleague := f.GetLeague()\n\n\tfor i, player := range league {\n\t\tif player.Name == name {\n\t\t\tleague[i].Wins++\n\t\t}\n\t}\n\n\tf.database.Seek(0, io.SeekStart)\n\tjson.NewEncoder(f.database).Encode(league)\n}\n```\n\nYou may be asking yourself why I am doing `league[i].Wins++` rather than `player.Wins++`.\n\nWhen you `range` over a slice you are returned the current index of the loop (in our case `i`) and a _copy_ of the element at that index. Changing the `Wins` value of a copy won't have any effect on the `league` slice that we iterate on. For that reason, we need to get the reference to the actual value by doing `league[i]` and then changing that value instead.\n\nIf you run the tests, they should now be passing.\n\n## Refactor\n\nIn `GetPlayerScore` and `RecordWin`, we are iterating over `[]Player` to find a player by name.\n\nWe could refactor this common code in the internals of `FileSystemStore` but to me, it feels like this is maybe useful code we can lift into a new type. Working with a \"League\" so far has always been with `[]Player` but we can create a new type called `League`. This will be easier for other developers to understand and then we can attach useful methods onto that type for us to use.\n\nInside `league.go` add the following\n\n```go\n//league.go\ntype League []Player\n\nfunc (l League) Find(name string) *Player {\n\tfor i, p := range l {\n\t\tif p.Name == name {\n\t\t\treturn &l[i]\n\t\t}\n\t}\n\treturn nil\n}\n```\n\nNow if anyone has a `League` they can easily find a given player.\n\nChange our `PlayerStore` interface to return `League` rather than `[]Player`. Try to re-run the tests, you'll get a compilation problem because we've changed the interface but it's very easy to fix; just change the return type from `[]Player` to `League`.\n\nThis lets us simplify our methods in `file_system_store`.\n\n```go\n//file_system_store.go\nfunc (f *FileSystemPlayerStore) GetPlayerScore(name string) int {\n\n\tplayer := f.GetLeague().Find(name)\n\n\tif player != nil {\n\t\treturn player.Wins\n\t}\n\n\treturn 0\n}\n\nfunc (f *FileSystemPlayerStore) RecordWin(name string) {\n\tleague := f.GetLeague()\n\tplayer := league.Find(name)\n\n\tif player != nil {\n\t\tplayer.Wins++\n\t}\n\n\tf.database.Seek(0, io.SeekStart)\n\tjson.NewEncoder(f.database).Encode(league)\n}\n```\n\nThis is looking much better and we can see how we might be able to find other useful functionality around `League` that can be refactored.\n\nWe now need to handle the scenario of recording wins of new players.\n\n## Write the test first\n\n```go\n//file_system_store_test.go\nt.Run(\"store wins for new players\", func(t *testing.T) {\n\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\tdefer cleanDatabase()\n\n\tstore := FileSystemPlayerStore{database}\n\n\tstore.RecordWin(\"Pepper\")\n\n\tgot := store.GetPlayerScore(\"Pepper\")\n\twant := 1\n\tassertScoreEquals(t, got, want)\n})\n```\n\n## Try to run the test\n\n```\n=== RUN   TestFileSystemStore/store_wins_for_new_players#01\n    --- FAIL: TestFileSystemStore/store_wins_for_new_players#01 (0.00s)\n        file_system_store_test.go:86: got 0 want 1\n```\n\n## Write enough code to make it pass\n\nWe just need to handle the scenario where `Find` returns `nil` because it couldn't find the player.\n\n```go\n//file_system_store.go\nfunc (f *FileSystemPlayerStore) RecordWin(name string) {\n\tleague := f.GetLeague()\n\tplayer := league.Find(name)\n\n\tif player != nil {\n\t\tplayer.Wins++\n\t} else {\n\t\tleague = append(league, Player{name, 1})\n\t}\n\n\tf.database.Seek(0, io.SeekStart)\n\tjson.NewEncoder(f.database).Encode(league)\n}\n```\n\nThe happy path is looking ok so we can now try using our new `Store` in the integration test. This will give us more confidence that the software works and then we can delete the redundant `InMemoryPlayerStore`.\n\nIn `TestRecordingWinsAndRetrievingThem` replace the old store.\n\n```go\n//server_integration_test.go\ndatabase, cleanDatabase := createTempFile(t, \"\")\ndefer cleanDatabase()\nstore := &FileSystemPlayerStore{database}\n```\n\nIf you run the test it should pass and now we can delete `InMemoryPlayerStore`. `main.go` will now have compilation problems which will motivate us to now use our new store in the \"real\" code.\n\n```go\n// main.go\npackage main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n)\n\nconst dbFileName = \"game.db.json\"\n\nfunc main() {\n\tdb, err := os.OpenFile(dbFileName, os.O_RDWR|os.O_CREATE, 0666)\n\n\tif err != nil {\n\t\tlog.Fatalf(\"problem opening %s %v\", dbFileName, err)\n\t}\n\n\tstore := &FileSystemPlayerStore{db}\n\tserver := NewPlayerServer(store)\n\n\tif err := http.ListenAndServe(\":5000\", server); err != nil {\n\t\tlog.Fatalf(\"could not listen on port 5000 %v\", err)\n\t}\n}\n```\n\n- We create a file for our database.\n- The 2nd argument to `os.OpenFile` lets you define the permissions for opening the file, in our case `O_RDWR` means we want to read and write _and_ `os.O_CREATE` means create the file if it doesn't exist.\n- The 3rd argument means sets permissions for the file, in our case, all users can read and write the file. [(See superuser.com for a more detailed explanation)](https://superuser.com/questions/295591/what-is-the-meaning-of-chmod-666).\n\nRunning the program now persists the data in a file in between restarts, hooray!\n\n## More refactoring and performance concerns\n\nEvery time someone calls `GetLeague()` or `GetPlayerScore()` we are reading the entire file and parsing it into JSON. We should not have to do that because `FileSystemStore` is entirely responsible for the state of the league; it should only need to read the file when the program starts up and only need to update the file when data changes.\n\nWe can create a constructor which can do some of this initialisation for us and store the league as a value in our `FileSystemStore` to be used on the reads instead.\n\n```go\n//file_system_store.go\ntype FileSystemPlayerStore struct {\n\tdatabase io.ReadWriteSeeker\n\tleague   League\n}\n\nfunc NewFileSystemPlayerStore(database io.ReadWriteSeeker) *FileSystemPlayerStore {\n\tdatabase.Seek(0, io.SeekStart)\n\tleague, _ := NewLeague(database)\n\treturn &FileSystemPlayerStore{\n\t\tdatabase: database,\n\t\tleague:   league,\n\t}\n}\n```\n\nThis way we only have to read from disk once. We can now replace all of our previous calls to getting the league from disk and just use `f.league` instead.\n\n```go\n//file_system_store.go\nfunc (f *FileSystemPlayerStore) GetLeague() League {\n\treturn f.league\n}\n\nfunc (f *FileSystemPlayerStore) GetPlayerScore(name string) int {\n\n\tplayer := f.league.Find(name)\n\n\tif player != nil {\n\t\treturn player.Wins\n\t}\n\n\treturn 0\n}\n\nfunc (f *FileSystemPlayerStore) RecordWin(name string) {\n\tplayer := f.league.Find(name)\n\n\tif player != nil {\n\t\tplayer.Wins++\n\t} else {\n\t\tf.league = append(f.league, Player{name, 1})\n\t}\n\n\tf.database.Seek(0, io.SeekStart)\n\tjson.NewEncoder(f.database).Encode(f.league)\n}\n```\n\nIf you try to run the tests it will now complain about initialising `FileSystemPlayerStore` so just fix them by calling our new constructor.\n\n### Another problem\n\nThere is some more naivety in the way we are dealing with files which _could_ create a very nasty bug down the line.\n\nWhen we `RecordWin`, we `Seek` back to the start of the file and then write the new data—but what if the new data was smaller than what was there before?\n\nIn our current case, this is impossible. We never edit or delete scores so the data can only get bigger. However, it would be irresponsible for us to leave the code like this; it's not unthinkable that a delete scenario could come up.\n\nHow will we test for this though? What we need to do is first refactor our code so we separate out the concern of the _kind of data we write, from the writing_. We can then test that separately to check it works how we hope.\n\nWe'll create a new type to encapsulate our \"when we write we go from the beginning\" functionality. I'm going to call it `Tape`. Create a new file with the following:\n\n```go\n// tape.go\npackage main\n\nimport \"io\"\n\ntype tape struct {\n\tfile io.ReadWriteSeeker\n}\n\nfunc (t *tape) Write(p []byte) (n int, err error) {\n\tt.file.Seek(0, io.SeekStart)\n\treturn t.file.Write(p)\n}\n```\n\nNotice that we're only implementing `Write` now, as it encapsulates the `Seek` part. This means our `FileSystemStore` can just have a reference to a `Writer` instead.\n\n```go\n//file_system_store.go\ntype FileSystemPlayerStore struct {\n\tdatabase io.Writer\n\tleague   League\n}\n```\n\nUpdate the constructor to use `Tape`\n\n```go\n//file_system_store.go\nfunc NewFileSystemPlayerStore(database io.ReadWriteSeeker) *FileSystemPlayerStore {\n\tdatabase.Seek(0, io.SeekStart)\n\tleague, _ := NewLeague(database)\n\n\treturn &FileSystemPlayerStore{\n\t\tdatabase: &tape{database},\n\t\tleague:   league,\n\t}\n}\n```\n\nFinally, we can get the amazing payoff we wanted by removing the `Seek` call from `RecordWin`. Yes, it doesn't feel much, but at least it means if we do any other kind of writes we can rely on our `Write` to behave how we need it to. Plus it will now let us test the potentially problematic code separately and fix it.\n\nLet's write the test where we want to update the entire contents of a file with something that is smaller than the original contents.\n\n## Write the test first\n\nOur test will create a file with some content, try to write to it using the `tape`, and read it all again to see what's in the file. In `tape_test.go`:\n\n```go\n//tape_test.go\nfunc TestTape_Write(t *testing.T) {\n\tfile, clean := createTempFile(t, \"12345\")\n\tdefer clean()\n\n\ttape := &tape{file}\n\n\ttape.Write([]byte(\"abc\"))\n\n\tfile.Seek(0, io.SeekStart)\n\tnewFileContents, _ := io.ReadAll(file)\n\n\tgot := string(newFileContents)\n\twant := \"abc\"\n\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n```\n\n## Try to run the test\n\n```\n=== RUN   TestTape_Write\n--- FAIL: TestTape_Write (0.00s)\n    tape_test.go:23: got 'abc45' want 'abc'\n```\n\nAs we thought! It writes the data we want, but leaves the rest of the original data remaining.\n\n## Write enough code to make it pass\n\n`os.File` has a truncate function that will let us effectively empty the file. We should be able to just call this to get what we want.\n\nChange `tape` to the following:\n\n```go\n//tape.go\ntype tape struct {\n\tfile *os.File\n}\n\nfunc (t *tape) Write(p []byte) (n int, err error) {\n\tt.file.Truncate(0)\n\tt.file.Seek(0, io.SeekStart)\n\treturn t.file.Write(p)\n}\n```\n\nThe compiler will fail in a number of places where we are expecting an `io.ReadWriteSeeker` but we are sending in `*os.File`. You should be able to fix these problems yourself by now but if you get stuck just check the source code.\n\nOnce you get it refactoring our `TestTape_Write` test should be passing!\n\n### One other small refactor\n\nIn `RecordWin` we have the line `json.NewEncoder(f.database).Encode(f.league)`.\n\nWe don't need to create a new encoder every time we write, we can initialise one in our constructor and use that instead.\n\nStore a reference to an `Encoder` in our type and initialise it in the constructor:\n\n```go\n//file_system_store.go\ntype FileSystemPlayerStore struct {\n\tdatabase *json.Encoder\n\tleague   League\n}\n\nfunc NewFileSystemPlayerStore(file *os.File) *FileSystemPlayerStore {\n\tfile.Seek(0, io.SeekStart)\n\tleague, _ := NewLeague(file)\n\n\treturn &FileSystemPlayerStore{\n\t\tdatabase: json.NewEncoder(&tape{file}),\n\t\tleague:   league,\n\t}\n}\n```\n\nUse it in `RecordWin`.\n\n```go\nfunc (f *FileSystemPlayerStore) RecordWin(name string) {\n\tplayer := f.league.Find(name)\n\n\tif player != nil {\n\t\tplayer.Wins++\n\t} else {\n\t\tf.league = append(f.league, Player{name, 1})\n\t}\n\n\tf.database.Encode(f.league)\n}\n```\n\n## Didn't we just break some rules there? Testing private things? No interfaces?\n\n### On testing private types\n\nIt's true that _in general_ you should favour not testing private things as that can sometimes lead to your tests being too tightly coupled to the implementation, which can hinder refactoring in future.\n\nHowever, we must not forget that tests should give us _confidence_.\n\nWe were not confident that our implementation would work if we added any kind of edit or delete functionality. We did not want to leave the code like that, especially if this was being worked on by more than one person who may not be aware of the shortcomings of our initial approach.\n\nFinally, it's just one test! If we decide to change the way it works it won't be a disaster to just delete the test but we have at the very least captured the requirement for future maintainers.\n\n### Interfaces\n\nWe started off the code by using `io.Reader` as that was the easiest path for us to unit test our new `PlayerStore`. As we developed the code we moved on to `io.ReadWriter` and then `io.ReadWriteSeeker`. We then found out there was nothing in the standard library that actually implemented that apart from `*os.File`. We could've taken the decision to write our own or use an open source one but it felt pragmatic just to make temporary files for the tests.\n\nFinally, we needed `Truncate` which is also on `*os.File`. It would've been an option to create our own interface capturing these requirements.\n\n```go\ntype ReadWriteSeekTruncate interface {\n\tio.ReadWriteSeeker\n\tTruncate(size int64) error\n}\n```\n\nBut what is this really giving us? Bear in mind we are _not mocking_ and it is unrealistic for a **file system** store to take any type other than an `*os.File` so we don't need the polymorphism that interfaces give us.\n\nDon't be afraid to chop and change types and experiment like we have here. The great thing about using a statically typed language is the compiler will help you with every change.\n\n## Error handling\n\nBefore we start working on sorting we should make sure we're happy with our current code and remove any technical debt we may have. It's an important principle to get to working software as quickly as possible (stay out of the red state) but that doesn't mean we should ignore error cases!\n\nIf we go back to `FileSystemStore.go` we have `league, _ := NewLeague(f.database)` in our constructor.\n\n`NewLeague` can return an error if it is unable to parse the league from the `io.Reader` that we provide.\n\nIt was pragmatic to ignore that at the time as we already had failing tests. If we had tried to tackle it at the same time, we would have been juggling two things at once.\n\nLet's make it so our constructor is capable of returning an error.\n\n```go\n//file_system_store.go\nfunc NewFileSystemPlayerStore(file *os.File) (*FileSystemPlayerStore, error) {\n\tfile.Seek(0, io.SeekStart)\n\tleague, err := NewLeague(file)\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"problem loading player store from file %s, %v\", file.Name(), err)\n\t}\n\n\treturn &FileSystemPlayerStore{\n\t\tdatabase: json.NewEncoder(&tape{file}),\n\t\tleague:   league,\n\t}, nil\n}\n```\n\nRemember it is very important to give helpful error messages (just like your tests). People on the internet jokingly say that most Go code is:\n\n```go\nif err != nil {\n\treturn err\n}\n```\n\n**That is 100% not idiomatic.** Adding contextual information (i.e what you were doing to cause the error) to your error messages makes operating your software far easier.\n\nIf you try to compile you'll get some errors.\n\n```\n./main.go:18:35: multiple-value NewFileSystemPlayerStore() in single-value context\n./file_system_store_test.go:35:36: multiple-value NewFileSystemPlayerStore() in single-value context\n./file_system_store_test.go:57:36: multiple-value NewFileSystemPlayerStore() in single-value context\n./file_system_store_test.go:70:36: multiple-value NewFileSystemPlayerStore() in single-value context\n./file_system_store_test.go:85:36: multiple-value NewFileSystemPlayerStore() in single-value context\n./server_integration_test.go:12:35: multiple-value NewFileSystemPlayerStore() in single-value context\n```\n\nIn main we'll want to exit the program, printing the error.\n\n```go\n//main.go\nstore, err := NewFileSystemPlayerStore(db)\n\nif err != nil {\n\tlog.Fatalf(\"problem creating file system player store, %v \", err)\n}\n```\n\nIn the tests we should assert there is no error. We can make a helper to help with this.\n\n```go\n//file_system_store_test.go\nfunc assertNoError(t testing.TB, err error) {\n\tt.Helper()\n\tif err != nil {\n\t\tt.Fatalf(\"didn't expect an error but got one, %v\", err)\n\t}\n}\n```\n\nWork through the other compilation problems using this helper. Finally, you should have a failing test:\n\n```\n=== RUN   TestRecordingWinsAndRetrievingThem\n--- FAIL: TestRecordingWinsAndRetrievingThem (0.00s)\n    server_integration_test.go:14: didn't expect an error but got one, problem loading player store from file /var/folders/nj/r_ccbj5d7flds0sf63yy4vb80000gn/T/db841037437, problem parsing league, EOF\n```\n\nWe cannot parse the league because the file is empty. We weren't getting errors before because we always just ignored them.\n\nLet's fix our big integration test by putting some valid JSON in it:\n\n```go\n//server_integration_test.go\nfunc TestRecordingWinsAndRetrievingThem(t *testing.T) {\n\tdatabase, cleanDatabase := createTempFile(t, `[]`)\n\t//etc...\n}\n```\n\nNow that all the tests are passing, we need to handle the scenario where the file is empty.\n\n## Write the test first\n\n```go\n//file_system_store_test.go\nt.Run(\"works with an empty file\", func(t *testing.T) {\n\tdatabase, cleanDatabase := createTempFile(t, \"\")\n\tdefer cleanDatabase()\n\n\t_, err := NewFileSystemPlayerStore(database)\n\n\tassertNoError(t, err)\n})\n```\n\n## Try to run the test\n\n```\n=== RUN   TestFileSystemStore/works_with_an_empty_file\n    --- FAIL: TestFileSystemStore/works_with_an_empty_file (0.00s)\n        file_system_store_test.go:108: didn't expect an error but got one, problem loading player store from file /var/folders/nj/r_ccbj5d7flds0sf63yy4vb80000gn/T/db019548018, problem parsing league, EOF\n```\n\n## Write enough code to make it pass\n\nChange our constructor to the following\n\n```go\n//file_system_store.go\nfunc NewFileSystemPlayerStore(file *os.File) (*FileSystemPlayerStore, error) {\n\n\tfile.Seek(0, io.SeekStart)\n\n\tinfo, err := file.Stat()\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"problem getting file info from file %s, %v\", file.Name(), err)\n\t}\n\n\tif info.Size() == 0 {\n\t\tfile.Write([]byte(\"[]\"))\n\t\tfile.Seek(0, io.SeekStart)\n\t}\n\n\tleague, err := NewLeague(file)\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"problem loading player store from file %s, %v\", file.Name(), err)\n\t}\n\n\treturn &FileSystemPlayerStore{\n\t\tdatabase: json.NewEncoder(&tape{file}),\n\t\tleague:   league,\n\t}, nil\n}\n```\n\n`file.Stat` returns stats on our file, which lets us check the size of the file. If it's empty, we `Write` an empty JSON array and `Seek` back to the start, ready for the rest of the code.\n\n## Refactor\n\nOur constructor is a bit messy now, so let's extract the initialise code into a function:\n\n```go\n//file_system_store.go\nfunc initialisePlayerDBFile(file *os.File) error {\n\tfile.Seek(0, io.SeekStart)\n\n\tinfo, err := file.Stat()\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"problem getting file info from file %s, %v\", file.Name(), err)\n\t}\n\n\tif info.Size() == 0 {\n\t\tfile.Write([]byte(\"[]\"))\n\t\tfile.Seek(0, io.SeekStart)\n\t}\n\n\treturn nil\n}\n```\n\n```go\n//file_system_store.go\nfunc NewFileSystemPlayerStore(file *os.File) (*FileSystemPlayerStore, error) {\n\n\terr := initialisePlayerDBFile(file)\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"problem initialising player db file, %v\", err)\n\t}\n\n\tleague, err := NewLeague(file)\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"problem loading player store from file %s, %v\", file.Name(), err)\n\t}\n\n\treturn &FileSystemPlayerStore{\n\t\tdatabase: json.NewEncoder(&tape{file}),\n\t\tleague:   league,\n\t}, nil\n}\n```\n\n## Sorting\n\nOur product owner wants `/league` to return the players sorted by their scores, from highest to lowest.\n\nThe main decision to make here is where in the software should this happen. If we were using a \"real\" database we would use things like `ORDER BY` so the sorting is super fast. For that reason, it feels like implementations of `PlayerStore` should be responsible.\n\n## Write the test first\n\nWe can update the assertion on our first test in `TestFileSystemStore`:\n\n```go\n//file_system_store_test.go\nt.Run(\"league sorted\", func(t *testing.T) {\n\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\tdefer cleanDatabase()\n\n\tstore, err := NewFileSystemPlayerStore(database)\n\n\tassertNoError(t, err)\n\n\tgot := store.GetLeague()\n\n\twant := League{\n\t\t{\"Chris\", 33},\n\t\t{\"Cleo\", 10},\n\t}\n\n\tassertLeague(t, got, want)\n\n\t// read again\n\tgot = store.GetLeague()\n\tassertLeague(t, got, want)\n})\n```\n\nThe order of the JSON coming in is in the wrong order and our `want` will check that it is returned to the caller in the correct order.\n\n## Try to run the test\n\n```\n=== RUN   TestFileSystemStore/league_from_a_reader,_sorted\n    --- FAIL: TestFileSystemStore/league_from_a_reader,_sorted (0.00s)\n        file_system_store_test.go:46: got [{Cleo 10} {Chris 33}] want [{Chris 33} {Cleo 10}]\n        file_system_store_test.go:51: got [{Cleo 10} {Chris 33}] want [{Chris 33} {Cleo 10}]\n```\n\n## Write enough code to make it pass\n\n```go\nfunc (f *FileSystemPlayerStore) GetLeague() League {\n\tsort.Slice(f.league, func(i, j int) bool {\n\t\treturn f.league[i].Wins > f.league[j].Wins\n\t})\n\treturn f.league\n}\n```\n\n[`sort.Slice`](https://golang.org/pkg/sort/#Slice)\n\n> Slice sorts the provided slice given the provided less function.\n\nEasy!\n\n## Wrapping up\n\n### What we've covered\n\n- The `Seeker` interface and its relation to `Reader` and `Writer`.\n- Working with files.\n- Creating an easy to use helper for testing with files that hides all the messy stuff.\n- `sort.Slice` for sorting slices.\n- Using the compiler to help us safely make structural changes to the application.\n\n### Breaking rules\n\n- Most rules in software engineering aren't really rules, just best practices that work 80% of the time.\n- We discovered a scenario where one of our previous \"rules\" of not testing internal functions was not helpful for us so we broke the rule.\n- It's important when breaking rules to understand the trade-off you are making. In our case, we were ok with it because it was just one test and would've been very difficult to exercise the scenario otherwise.\n- In order to be able to break the rules **you must understand them first**. An analogy is with learning guitar. It doesn't matter how creative you think you are, you must understand and practice the fundamentals.\n\n### Where our software is at\n\n- We have an HTTP API where you can create players and increment their score.\n- We can return a league of everyone's scores as JSON.\n- The data is persisted as a JSON file.\n"
  },
  {
    "path": "iteration.md",
    "content": "# Iteration\n\n**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/main/for)**\n\nTo do stuff repeatedly in Go, you'll need `for`. In Go there are no `while`, `do`, `until` keywords, you can only use `for`. Which is a good thing!\n\nLet's write a test for a function that repeats a character 5 times.\n\nThere's nothing new so far, so try and write it yourself for practice.\n\n## Write the test first\n\n```go\npackage iteration\n\nimport \"testing\"\n\nfunc TestRepeat(t *testing.T) {\n\trepeated := Repeat(\"a\")\n\texpected := \"aaaaa\"\n\n\tif repeated != expected {\n\t\tt.Errorf(\"expected %q but got %q\", expected, repeated)\n\t}\n}\n```\n\n## Try and run the test\n\n`./repeat_test.go:6:14: undefined: Repeat`\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\n_Keep the discipline!_ You don't need to know anything new right now to make the test fail properly.\n\nAll you need to do right now is enough to make it compile so you can check your test is written well.\n\n```go\npackage iteration\n\nfunc Repeat(character string) string {\n\treturn \"\"\n}\n```\n\nIsn't it nice to know you already know enough Go to write tests for some basic problems? This means you can now play with the production code as much as you like and know it's behaving as you'd hope.\n\n`repeat_test.go:10: expected 'aaaaa' but got ''`\n\n## Write enough code to make it pass\n\nThe `for` syntax is very unremarkable and follows most C-like languages.\n\n```go\nfunc Repeat(character string) string {\n\tvar repeated string\n\tfor i := 0; i < 5; i++ {\n\t\trepeated = repeated + character\n\t}\n\treturn repeated\n}\n```\n\nUnlike other languages like C, Java, or JavaScript there are no parentheses surrounding the three components of the for statement and the braces `{ }` are always required. You might wonder what is happening in the row\n\n```go\n\tvar repeated string\n```\n\nas we've been using `:=` so far to declare and initializing variables. However, `:=` is simply [short hand for both steps](https://gobyexample.com/variables). Here we are declaring a `string` variable only. Hence, the explicit version. We can also use `var` to declare functions, as we'll see later on.\n\nRun the test and it should pass.\n\nAdditional variants of the for loop are described [here](https://gobyexample.com/for).\n\n## Refactor\n\nNow it's time to refactor and introduce another construct `+=` assignment operator.\n\n```go\nconst repeatCount = 5\n\nfunc Repeat(character string) string {\n\tvar repeated string\n\tfor i := 0; i < repeatCount; i++ {\n\t\trepeated += character\n\t}\n\treturn repeated\n}\n```\n\n`+=` called _\"the Add AND assignment operator\"_, adds the right operand to the left operand and assigns the result to left operand. It works with other types like integers.\n\n### Benchmarking\n\nWriting [benchmarks](https://golang.org/pkg/testing/#hdr-Benchmarks) in Go is another first-class feature of the language and it is very similar to writing tests.\n\n```go\nfunc BenchmarkRepeat(b *testing.B) {\n\tfor b.Loop() {\n\t\tRepeat(\"a\")\n\t}\n}\n```\n\nYou'll see the code is very similar to a test.\n\nThe `testing.B` gives you access to the loop function. `Loop()` returns true as long as the benchmark should continue running. \n\nWhen the benchmark code is executed, it measures how long it takes. After `Loop()` returns false, `b.N` contains the total number of iterations that ran.\n\nThe number of times the code is run shouldn't matter to you, the framework will determine what is a \"good\" value for that to let you have some decent results.\n\nTo run the benchmarks do `go test -bench=.` (or if you're in Windows Powershell `go test -bench=\".\"`)\n\n```text\ngoos: darwin\ngoarch: amd64\npkg: github.com/quii/learn-go-with-tests/for/v4\n10000000           136 ns/op\nPASS\n```\n\nWhat `136 ns/op` means is our function takes on average 136 nanoseconds to run \\(on my computer\\). Which is pretty ok! To test this it ran it 10000000 times.\n\n**Note:** By default benchmarks are run sequentially.\n\nOnly the body of the loop is timed; it automatically excludes setup and cleanup code from benchmark timing. A typical benchmark is structured like:\n\n```go\nfunc Benchmark(b *testing.B) {\n\t//... setup ...\n\tfor b.Loop() {\n\t\t//... code to measure ...\n\t}\n\t//... cleanup ...\n}\n```\n\nStrings in Go are immutable, meaning every concatenation, such as in our `Repeat` function, involves copying memory to accommodate the new string. This impacts performance, particularly during heavy string concatenation.\n\nThe standard library provides the `strings.Builder`[stringsBuilder] type which minimizes memory copying.\nIt implements a `WriteString` method which we can use to concatenate strings:\n\n```go\nconst repeatCount = 5\n\nfunc Repeat(character string) string {\n\tvar repeated strings.Builder\n\tfor i := 0; i < repeatCount; i++ {\n\t\trepeated.WriteString(character)\n\t}\n\treturn repeated.String()\n}\n```\n\n**Note**: We have to call the `String` method to retrieve the final result.\n\nWe can use `BenchmarkRepeat` to confirm that `strings.Builder` significantly improves performance.\nRun `go test -bench=. -benchmem`:\n\n```text\ngoos: darwin\ngoarch: amd64\npkg: github.com/quii/learn-go-with-tests/for/v4\n10000000           25.70 ns/op           8 B/op           1 allocs/op\nPASS\n```\n\nThe `-benchmem` flag reports information about memory allocations:\n\n* `B/op`: the number of bytes allocated per iteration\n* `allocs/op`: the number of memory allocations per iteration\n\n## Practice exercises\n\n* Change the test so a caller can specify how many times the character is repeated and then fix the code\n* Write `ExampleRepeat` to document your function\n* Have a look through the [strings](https://golang.org/pkg/strings) package. Find functions you think could be useful and experiment with them by writing tests like we have here. Investing time learning the standard library will really pay off over time.\n\n## Wrapping up\n\n* More TDD practice\n* Learned `for`\n* Learned how to write benchmarks\n\n[stringsBuilder]: https://pkg.go.dev/strings#Builder\n"
  },
  {
    "path": "iterators/iterators_test.go",
    "content": "package iterators\n\nimport (\n\t\"iter\"\n\t\"slices\"\n\t\"testing\"\n)\n\nfunc Concatenate(seq iter.Seq[string]) string {\n\tvar result string\n\tfor s := range seq {\n\t\tresult += s\n\t}\n\treturn result\n}\n\n// annoyingly, there is no builtin way to go from seq2, to seq (e.g just get the values)\nfunc Values[K, V any](seq iter.Seq2[K, V]) iter.Seq[V] {\n\treturn func(yield func(V) bool) {\n\t\tfor _, v := range seq {\n\t\t\tif !yield(v) {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n}\n\n// WIP!\nfunc TestConcatenate(t *testing.T) {\n\tt.Run(\"values of a slice\", func(t *testing.T) {\n\t\tgot := Concatenate(slices.Values([]string{\"a\", \"b\", \"c\"}))\n\t\twant := \"abc\"\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %q want %q\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"values of a slice backwards\", func(t *testing.T) {\n\t\tbackward := slices.Backward([]string{\"a\", \"b\", \"c\"})\n\n\t\tgot := Concatenate(Values(backward))\n\t\twant := \"cba\"\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %q want %q\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"values of a slice sorted\", func(t *testing.T) {\n\t\tgot := Concatenate(slices.Values(slices.Sorted(slices.Values([]string{\"c\", \"a\", \"b\"}))))\n\t\twant := \"abc\"\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %q want %q\", got, want)\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "json/v1/in_memory_player_store.go",
    "content": "package main\n\n// NewInMemoryPlayerStore initialises an empty player store.\nfunc NewInMemoryPlayerStore() *InMemoryPlayerStore {\n\treturn &InMemoryPlayerStore{map[string]int{}}\n}\n\n// InMemoryPlayerStore collects data about players in memory.\ntype InMemoryPlayerStore struct {\n\tstore map[string]int\n}\n\n// RecordWin will record a player's win.\nfunc (i *InMemoryPlayerStore) RecordWin(name string) {\n\ti.store[name]++\n}\n\n// GetPlayerScore retrieves scores for a given player.\nfunc (i *InMemoryPlayerStore) GetPlayerScore(name string) int {\n\treturn i.store[name]\n}\n"
  },
  {
    "path": "json/v1/main.go",
    "content": "package main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc main() {\n\tserver := &PlayerServer{NewInMemoryPlayerStore()}\n\tlog.Fatal(http.ListenAndServe(\":5000\", server))\n}\n"
  },
  {
    "path": "json/v1/server.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n)\n\n// PlayerStore stores score information about players.\ntype PlayerStore interface {\n\tGetPlayerScore(name string) int\n\tRecordWin(name string)\n}\n\n// PlayerServer is a HTTP interface for player information.\ntype PlayerServer struct {\n\tstore PlayerStore\n}\n\nfunc (p *PlayerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\n\tswitch r.Method {\n\tcase http.MethodPost:\n\t\tp.processWin(w, player)\n\tcase http.MethodGet:\n\t\tp.showScore(w, player)\n\t}\n}\n\nfunc (p *PlayerServer) showScore(w http.ResponseWriter, player string) {\n\tscore := p.store.GetPlayerScore(player)\n\n\tif score == 0 {\n\t\tw.WriteHeader(http.StatusNotFound)\n\t}\n\n\tfmt.Fprint(w, score)\n}\n\nfunc (p *PlayerServer) processWin(w http.ResponseWriter, player string) {\n\tp.store.RecordWin(player)\n\tw.WriteHeader(http.StatusAccepted)\n}\n"
  },
  {
    "path": "json/v1/server_integration_test.go",
    "content": "package main\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestRecordingWinsAndRetrievingThem(t *testing.T) {\n\tstore := NewInMemoryPlayerStore()\n\tserver := PlayerServer{store}\n\tplayer := \"Pepper\"\n\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\n\tresponse := httptest.NewRecorder()\n\tserver.ServeHTTP(response, newGetScoreRequest(player))\n\tassertStatus(t, response.Code, http.StatusOK)\n\n\tassertResponseBody(t, response.Body.String(), \"3\")\n}\n"
  },
  {
    "path": "json/v1/server_test.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\ntype StubPlayerStore struct {\n\tscores   map[string]int\n\twinCalls []string\n}\n\nfunc (s *StubPlayerStore) GetPlayerScore(name string) int {\n\tscore := s.scores[name]\n\treturn score\n}\n\nfunc (s *StubPlayerStore) RecordWin(name string) {\n\ts.winCalls = append(s.winCalls, name)\n}\n\nfunc TestGETPlayers(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{\n\t\t\t\"Pepper\": 20,\n\t\t\t\"Floyd\":  10,\n\t\t},\n\t\tnil,\n\t}\n\tserver := &PlayerServer{&store}\n\n\tt.Run(\"returns Pepper's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Pepper\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"20\")\n\t})\n\n\tt.Run(\"returns Floyd's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Floyd\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"10\")\n\t})\n\n\tt.Run(\"returns 404 on missing players\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Apollo\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusNotFound)\n\t})\n}\n\nfunc TestStoreWins(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{},\n\t\tnil,\n\t}\n\tserver := &PlayerServer{&store}\n\n\tt.Run(\"it records wins on POST\", func(t *testing.T) {\n\t\tplayer := \"Pepper\"\n\n\t\trequest := newPostWinRequest(player)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusAccepted)\n\n\t\tif len(store.winCalls) != 1 {\n\t\t\tt.Fatalf(\"got %d calls to RecordWin want %d\", len(store.winCalls), 1)\n\t\t}\n\n\t\tif store.winCalls[0] != player {\n\t\t\tt.Errorf(\"did not store correct winner got %q want %q\", store.winCalls[0], player)\n\t\t}\n\t})\n}\n\nfunc assertStatus(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"did not get correct status, got %d, want %d\", got, want)\n\t}\n}\n\nfunc newGetScoreRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc newPostWinRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodPost, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc assertResponseBody(t testing.TB, got, want string) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"response body is wrong, got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "json/v2/in_memory_player_store.go",
    "content": "package main\n\n// NewInMemoryPlayerStore initialises an empty player store.\nfunc NewInMemoryPlayerStore() *InMemoryPlayerStore {\n\treturn &InMemoryPlayerStore{map[string]int{}}\n}\n\n// InMemoryPlayerStore collects data about players in memory.\ntype InMemoryPlayerStore struct {\n\tstore map[string]int\n}\n\n// RecordWin will record a player's win.\nfunc (i *InMemoryPlayerStore) RecordWin(name string) {\n\ti.store[name]++\n}\n\n// GetPlayerScore retrieves scores for a given player.\nfunc (i *InMemoryPlayerStore) GetPlayerScore(name string) int {\n\treturn i.store[name]\n}\n"
  },
  {
    "path": "json/v2/main.go",
    "content": "package main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc main() {\n\tserver := NewPlayerServer(NewInMemoryPlayerStore())\n\tlog.Fatal(http.ListenAndServe(\":5000\", server))\n}\n"
  },
  {
    "path": "json/v2/server.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n)\n\n// PlayerStore stores score information about players.\ntype PlayerStore interface {\n\tGetPlayerScore(name string) int\n\tRecordWin(name string)\n}\n\n// PlayerServer is a HTTP interface for player information.\ntype PlayerServer struct {\n\tstore PlayerStore\n\thttp.Handler\n}\n\n// NewPlayerServer creates a PlayerServer with routing configured.\nfunc NewPlayerServer(store PlayerStore) *PlayerServer {\n\tp := new(PlayerServer)\n\n\tp.store = store\n\n\trouter := http.NewServeMux()\n\trouter.Handle(\"/league\", http.HandlerFunc(p.leagueHandler))\n\trouter.Handle(\"/players/\", http.HandlerFunc(p.playersHandler))\n\n\tp.Handler = router\n\n\treturn p\n}\n\nfunc (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) {\n\tw.WriteHeader(http.StatusOK)\n}\n\nfunc (p *PlayerServer) playersHandler(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\n\tswitch r.Method {\n\tcase http.MethodPost:\n\t\tp.processWin(w, player)\n\tcase http.MethodGet:\n\t\tp.showScore(w, player)\n\t}\n}\n\nfunc (p *PlayerServer) showScore(w http.ResponseWriter, player string) {\n\tscore := p.store.GetPlayerScore(player)\n\n\tif score == 0 {\n\t\tw.WriteHeader(http.StatusNotFound)\n\t}\n\n\tfmt.Fprint(w, score)\n}\n\nfunc (p *PlayerServer) processWin(w http.ResponseWriter, player string) {\n\tp.store.RecordWin(player)\n\tw.WriteHeader(http.StatusAccepted)\n}\n"
  },
  {
    "path": "json/v2/server_integration_test.go",
    "content": "package main\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestRecordingWinsAndRetrievingThem(t *testing.T) {\n\tstore := NewInMemoryPlayerStore()\n\tserver := NewPlayerServer(store)\n\tplayer := \"Pepper\"\n\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\n\tresponse := httptest.NewRecorder()\n\tserver.ServeHTTP(response, newGetScoreRequest(player))\n\tassertStatus(t, response.Code, http.StatusOK)\n\n\tassertResponseBody(t, response.Body.String(), \"3\")\n}\n"
  },
  {
    "path": "json/v2/server_test.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\ntype StubPlayerStore struct {\n\tscores   map[string]int\n\twinCalls []string\n}\n\nfunc (s *StubPlayerStore) GetPlayerScore(name string) int {\n\tscore := s.scores[name]\n\treturn score\n}\n\nfunc (s *StubPlayerStore) RecordWin(name string) {\n\ts.winCalls = append(s.winCalls, name)\n}\n\nfunc TestGETPlayers(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{\n\t\t\t\"Pepper\": 20,\n\t\t\t\"Floyd\":  10,\n\t\t},\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"returns Pepper's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Pepper\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"20\")\n\t})\n\n\tt.Run(\"returns Floyd's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Floyd\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"10\")\n\t})\n\n\tt.Run(\"returns 404 on missing players\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Apollo\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusNotFound)\n\t})\n}\n\nfunc TestStoreWins(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{},\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"it records wins on POST\", func(t *testing.T) {\n\t\tplayer := \"Pepper\"\n\n\t\trequest := newPostWinRequest(player)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusAccepted)\n\n\t\tif len(store.winCalls) != 1 {\n\t\t\tt.Fatalf(\"got %d calls to RecordWin want %d\", len(store.winCalls), 1)\n\t\t}\n\n\t\tif store.winCalls[0] != player {\n\t\t\tt.Errorf(\"did not store correct winner got %q want %q\", store.winCalls[0], player)\n\t\t}\n\t})\n}\n\nfunc TestLeague(t *testing.T) {\n\tstore := StubPlayerStore{}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"it returns 200 on /league\", func(t *testing.T) {\n\t\trequest, _ := http.NewRequest(http.MethodGet, \"/league\", nil)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t})\n}\n\nfunc assertStatus(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"did not get correct status, got %d, want %d\", got, want)\n\t}\n}\n\nfunc newGetScoreRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc newPostWinRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodPost, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc assertResponseBody(t testing.TB, got, want string) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"response body is wrong, got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "json/v3/in_memory_player_store.go",
    "content": "package main\n\n// NewInMemoryPlayerStore initialises an empty player store.\nfunc NewInMemoryPlayerStore() *InMemoryPlayerStore {\n\treturn &InMemoryPlayerStore{map[string]int{}}\n}\n\n// InMemoryPlayerStore collects data about players in memory.\ntype InMemoryPlayerStore struct {\n\tstore map[string]int\n}\n\n// RecordWin will record a player's win.\nfunc (i *InMemoryPlayerStore) RecordWin(name string) {\n\ti.store[name]++\n}\n\n// GetPlayerScore retrieves scores for a given player.\nfunc (i *InMemoryPlayerStore) GetPlayerScore(name string) int {\n\treturn i.store[name]\n}\n"
  },
  {
    "path": "json/v3/main.go",
    "content": "package main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc main() {\n\tserver := NewPlayerServer(NewInMemoryPlayerStore())\n\tlog.Fatal(http.ListenAndServe(\":5000\", server))\n}\n"
  },
  {
    "path": "json/v3/server.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n)\n\n// PlayerStore stores score information about players.\ntype PlayerStore interface {\n\tGetPlayerScore(name string) int\n\tRecordWin(name string)\n}\n\n// Player stores a name with a number of wins.\ntype Player struct {\n\tName string\n\tWins int\n}\n\n// PlayerServer is a HTTP interface for player information.\ntype PlayerServer struct {\n\tstore PlayerStore\n\thttp.Handler\n}\n\n// NewPlayerServer creates a PlayerServer with routing configured.\nfunc NewPlayerServer(store PlayerStore) *PlayerServer {\n\tp := new(PlayerServer)\n\n\tp.store = store\n\n\trouter := http.NewServeMux()\n\trouter.Handle(\"/league\", http.HandlerFunc(p.leagueHandler))\n\trouter.Handle(\"/players/\", http.HandlerFunc(p.playersHandler))\n\n\tp.Handler = router\n\n\treturn p\n}\n\nfunc (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) {\n\tjson.NewEncoder(w).Encode(p.getLeagueTable())\n\tw.WriteHeader(http.StatusOK)\n}\n\nfunc (p *PlayerServer) getLeagueTable() []Player {\n\treturn []Player{\n\t\t{\"Chris\", 20},\n\t}\n}\n\nfunc (p *PlayerServer) playersHandler(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\n\tswitch r.Method {\n\tcase http.MethodPost:\n\t\tp.processWin(w, player)\n\tcase http.MethodGet:\n\t\tp.showScore(w, player)\n\t}\n}\n\nfunc (p *PlayerServer) showScore(w http.ResponseWriter, player string) {\n\tscore := p.store.GetPlayerScore(player)\n\n\tif score == 0 {\n\t\tw.WriteHeader(http.StatusNotFound)\n\t}\n\n\tfmt.Fprint(w, score)\n}\n\nfunc (p *PlayerServer) processWin(w http.ResponseWriter, player string) {\n\tp.store.RecordWin(player)\n\tw.WriteHeader(http.StatusAccepted)\n}\n"
  },
  {
    "path": "json/v3/server_integration_test.go",
    "content": "package main\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestRecordingWinsAndRetrievingThem(t *testing.T) {\n\tstore := NewInMemoryPlayerStore()\n\tserver := NewPlayerServer(store)\n\tplayer := \"Pepper\"\n\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\n\tresponse := httptest.NewRecorder()\n\tserver.ServeHTTP(response, newGetScoreRequest(player))\n\tassertStatus(t, response.Code, http.StatusOK)\n\n\tassertResponseBody(t, response.Body.String(), \"3\")\n}\n"
  },
  {
    "path": "json/v3/server_test.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\ntype StubPlayerStore struct {\n\tscores   map[string]int\n\twinCalls []string\n}\n\nfunc (s *StubPlayerStore) GetPlayerScore(name string) int {\n\tscore := s.scores[name]\n\treturn score\n}\n\nfunc (s *StubPlayerStore) RecordWin(name string) {\n\ts.winCalls = append(s.winCalls, name)\n}\n\nfunc TestGETPlayers(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{\n\t\t\t\"Pepper\": 20,\n\t\t\t\"Floyd\":  10,\n\t\t},\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"returns Pepper's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Pepper\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"20\")\n\t})\n\n\tt.Run(\"returns Floyd's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Floyd\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"10\")\n\t})\n\n\tt.Run(\"returns 404 on missing players\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Apollo\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusNotFound)\n\t})\n}\n\nfunc TestStoreWins(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{},\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"it records wins on POST\", func(t *testing.T) {\n\t\tplayer := \"Pepper\"\n\n\t\trequest := newPostWinRequest(player)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusAccepted)\n\n\t\tif len(store.winCalls) != 1 {\n\t\t\tt.Fatalf(\"got %d calls to RecordWin want %d\", len(store.winCalls), 1)\n\t\t}\n\n\t\tif store.winCalls[0] != player {\n\t\t\tt.Errorf(\"did not store correct winner got %q want %q\", store.winCalls[0], player)\n\t\t}\n\t})\n}\n\nfunc TestLeague(t *testing.T) {\n\tstore := StubPlayerStore{}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"it returns 200 on /league\", func(t *testing.T) {\n\t\trequest, _ := http.NewRequest(http.MethodGet, \"/league\", nil)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tvar got []Player\n\n\t\terr := json.NewDecoder(response.Body).Decode(&got)\n\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Unable to parse response from server %q into slice of Player, '%v'\", response.Body, err)\n\t\t}\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t})\n}\n\nfunc assertStatus(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"did not get correct status, got %d, want %d\", got, want)\n\t}\n}\n\nfunc newGetScoreRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc newPostWinRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodPost, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc assertResponseBody(t testing.TB, got, want string) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"response body is wrong, got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "json/v4/in_memory_player_store.go",
    "content": "package main\n\n// NewInMemoryPlayerStore initialises an empty player store.\nfunc NewInMemoryPlayerStore() *InMemoryPlayerStore {\n\treturn &InMemoryPlayerStore{map[string]int{}}\n}\n\n// InMemoryPlayerStore collects data about players in memory.\ntype InMemoryPlayerStore struct {\n\tstore map[string]int\n}\n\n// GetLeague currently doesn't work, but it should return the player league.\nfunc (i *InMemoryPlayerStore) GetLeague() []Player {\n\treturn nil\n}\n\n// RecordWin will record a player's win.\nfunc (i *InMemoryPlayerStore) RecordWin(name string) {\n\ti.store[name]++\n}\n\n// GetPlayerScore retrieves scores for a given player.\nfunc (i *InMemoryPlayerStore) GetPlayerScore(name string) int {\n\treturn i.store[name]\n}\n"
  },
  {
    "path": "json/v4/main.go",
    "content": "package main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc main() {\n\tserver := NewPlayerServer(NewInMemoryPlayerStore())\n\tlog.Fatal(http.ListenAndServe(\":5000\", server))\n}\n"
  },
  {
    "path": "json/v4/server.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n)\n\n// PlayerStore stores score information about players.\ntype PlayerStore interface {\n\tGetPlayerScore(name string) int\n\tRecordWin(name string)\n\tGetLeague() []Player\n}\n\n// Player stores a name with a number of wins.\ntype Player struct {\n\tName string\n\tWins int\n}\n\n// PlayerServer is a HTTP interface for player information.\ntype PlayerServer struct {\n\tstore PlayerStore\n\thttp.Handler\n}\n\n// NewPlayerServer creates a PlayerServer with routing configured.\nfunc NewPlayerServer(store PlayerStore) *PlayerServer {\n\tp := new(PlayerServer)\n\n\tp.store = store\n\n\trouter := http.NewServeMux()\n\trouter.Handle(\"/league\", http.HandlerFunc(p.leagueHandler))\n\trouter.Handle(\"/players/\", http.HandlerFunc(p.playersHandler))\n\n\tp.Handler = router\n\n\treturn p\n}\n\nfunc (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) {\n\tjson.NewEncoder(w).Encode(p.store.GetLeague())\n\tw.WriteHeader(http.StatusOK)\n}\n\nfunc (p *PlayerServer) playersHandler(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\n\tswitch r.Method {\n\tcase http.MethodPost:\n\t\tp.processWin(w, player)\n\tcase http.MethodGet:\n\t\tp.showScore(w, player)\n\t}\n}\n\nfunc (p *PlayerServer) showScore(w http.ResponseWriter, player string) {\n\tscore := p.store.GetPlayerScore(player)\n\n\tif score == 0 {\n\t\tw.WriteHeader(http.StatusNotFound)\n\t}\n\n\tfmt.Fprint(w, score)\n}\n\nfunc (p *PlayerServer) processWin(w http.ResponseWriter, player string) {\n\tp.store.RecordWin(player)\n\tw.WriteHeader(http.StatusAccepted)\n}\n"
  },
  {
    "path": "json/v4/server_integration_test.go",
    "content": "package main\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestRecordingWinsAndRetrievingThem(t *testing.T) {\n\tstore := NewInMemoryPlayerStore()\n\tserver := NewPlayerServer(store)\n\tplayer := \"Pepper\"\n\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\n\tresponse := httptest.NewRecorder()\n\tserver.ServeHTTP(response, newGetScoreRequest(player))\n\tassertStatus(t, response.Code, http.StatusOK)\n\n\tassertResponseBody(t, response.Body.String(), \"3\")\n}\n"
  },
  {
    "path": "json/v4/server_test.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"reflect\"\n\t\"testing\"\n)\n\ntype StubPlayerStore struct {\n\tscores   map[string]int\n\twinCalls []string\n\tleague   []Player\n}\n\nfunc (s *StubPlayerStore) GetPlayerScore(name string) int {\n\tscore := s.scores[name]\n\treturn score\n}\n\nfunc (s *StubPlayerStore) RecordWin(name string) {\n\ts.winCalls = append(s.winCalls, name)\n}\n\nfunc (s *StubPlayerStore) GetLeague() []Player {\n\treturn s.league\n}\n\nfunc TestGETPlayers(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{\n\t\t\t\"Pepper\": 20,\n\t\t\t\"Floyd\":  10,\n\t\t},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"returns Pepper's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Pepper\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"20\")\n\t})\n\n\tt.Run(\"returns Floyd's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Floyd\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"10\")\n\t})\n\n\tt.Run(\"returns 404 on missing players\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Apollo\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusNotFound)\n\t})\n}\n\nfunc TestStoreWins(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"it records wins on POST\", func(t *testing.T) {\n\t\tplayer := \"Pepper\"\n\n\t\trequest := newPostWinRequest(player)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusAccepted)\n\n\t\tif len(store.winCalls) != 1 {\n\t\t\tt.Fatalf(\"got %d calls to RecordWin want %d\", len(store.winCalls), 1)\n\t\t}\n\n\t\tif store.winCalls[0] != player {\n\t\t\tt.Errorf(\"did not store correct winner got %q want %q\", store.winCalls[0], player)\n\t\t}\n\t})\n}\n\nfunc TestLeague(t *testing.T) {\n\n\tt.Run(\"it returns the league table as JSON\", func(t *testing.T) {\n\t\twantedLeague := []Player{\n\t\t\t{\"Cleo\", 32},\n\t\t\t{\"Chris\", 20},\n\t\t\t{\"Tiest\", 14},\n\t\t}\n\n\t\tstore := StubPlayerStore{nil, nil, wantedLeague}\n\t\tserver := NewPlayerServer(&store)\n\n\t\trequest := newLeagueRequest()\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertLeague(t, got, wantedLeague)\n\t})\n}\n\nfunc getLeagueFromResponse(t testing.TB, body io.Reader) (league []Player) {\n\tt.Helper()\n\terr := json.NewDecoder(body).Decode(&league)\n\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to parse response from server %q into slice of Player, '%v'\", body, err)\n\t}\n\n\treturn\n}\n\nfunc assertLeague(t testing.TB, got, want []Player) {\n\tt.Helper()\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %v want %v\", got, want)\n\t}\n}\n\nfunc assertStatus(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"did not get correct status, got %d, want %d\", got, want)\n\t}\n}\n\nfunc newLeagueRequest() *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, \"/league\", nil)\n\treturn req\n}\n\nfunc newGetScoreRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc newPostWinRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodPost, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc assertResponseBody(t testing.TB, got, want string) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"response body is wrong, got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "json/v5/in_memory_player_store.go",
    "content": "package main\n\n// NewInMemoryPlayerStore initialises an empty player store.\nfunc NewInMemoryPlayerStore() *InMemoryPlayerStore {\n\treturn &InMemoryPlayerStore{map[string]int{}}\n}\n\n// InMemoryPlayerStore collects data about players in memory.\ntype InMemoryPlayerStore struct {\n\tstore map[string]int\n}\n\n// GetLeague currently doesn't work, but it should return the player league.\nfunc (i *InMemoryPlayerStore) GetLeague() []Player {\n\treturn nil\n}\n\n// RecordWin will record a player's win.\nfunc (i *InMemoryPlayerStore) RecordWin(name string) {\n\ti.store[name]++\n}\n\n// GetPlayerScore retrieves scores for a given player.\nfunc (i *InMemoryPlayerStore) GetPlayerScore(name string) int {\n\treturn i.store[name]\n}\n"
  },
  {
    "path": "json/v5/main.go",
    "content": "package main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc main() {\n\tserver := NewPlayerServer(NewInMemoryPlayerStore())\n\tlog.Fatal(http.ListenAndServe(\":5000\", server))\n}\n"
  },
  {
    "path": "json/v5/server.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n)\n\n// PlayerStore stores score information about players.\ntype PlayerStore interface {\n\tGetPlayerScore(name string) int\n\tRecordWin(name string)\n\tGetLeague() []Player\n}\n\n// Player stores a name with a number of wins.\ntype Player struct {\n\tName string\n\tWins int\n}\n\n// PlayerServer is a HTTP interface for player information.\ntype PlayerServer struct {\n\tstore PlayerStore\n\thttp.Handler\n}\n\nconst jsonContentType = \"application/json\"\n\n// NewPlayerServer creates a PlayerServer with routing configured.\nfunc NewPlayerServer(store PlayerStore) *PlayerServer {\n\tp := new(PlayerServer)\n\n\tp.store = store\n\n\trouter := http.NewServeMux()\n\trouter.Handle(\"/league\", http.HandlerFunc(p.leagueHandler))\n\trouter.Handle(\"/players/\", http.HandlerFunc(p.playersHandler))\n\n\tp.Handler = router\n\n\treturn p\n}\n\nfunc (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) {\n\tw.Header().Set(\"content-type\", jsonContentType)\n\tjson.NewEncoder(w).Encode(p.store.GetLeague())\n}\n\nfunc (p *PlayerServer) playersHandler(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\n\tswitch r.Method {\n\tcase http.MethodPost:\n\t\tp.processWin(w, player)\n\tcase http.MethodGet:\n\t\tp.showScore(w, player)\n\t}\n}\n\nfunc (p *PlayerServer) showScore(w http.ResponseWriter, player string) {\n\tscore := p.store.GetPlayerScore(player)\n\n\tif score == 0 {\n\t\tw.WriteHeader(http.StatusNotFound)\n\t}\n\n\tfmt.Fprint(w, score)\n}\n\nfunc (p *PlayerServer) processWin(w http.ResponseWriter, player string) {\n\tp.store.RecordWin(player)\n\tw.WriteHeader(http.StatusAccepted)\n}\n"
  },
  {
    "path": "json/v5/server_integration_test.go",
    "content": "package main\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestRecordingWinsAndRetrievingThem(t *testing.T) {\n\tstore := NewInMemoryPlayerStore()\n\tserver := NewPlayerServer(store)\n\tplayer := \"Pepper\"\n\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\n\tresponse := httptest.NewRecorder()\n\tserver.ServeHTTP(response, newGetScoreRequest(player))\n\tassertStatus(t, response.Code, http.StatusOK)\n\n\tassertResponseBody(t, response.Body.String(), \"3\")\n}\n"
  },
  {
    "path": "json/v5/server_test.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"reflect\"\n\t\"testing\"\n)\n\ntype StubPlayerStore struct {\n\tscores   map[string]int\n\twinCalls []string\n\tleague   []Player\n}\n\nfunc (s *StubPlayerStore) GetPlayerScore(name string) int {\n\tscore := s.scores[name]\n\treturn score\n}\n\nfunc (s *StubPlayerStore) RecordWin(name string) {\n\ts.winCalls = append(s.winCalls, name)\n}\n\nfunc (s *StubPlayerStore) GetLeague() []Player {\n\treturn s.league\n}\n\nfunc TestGETPlayers(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{\n\t\t\t\"Pepper\": 20,\n\t\t\t\"Floyd\":  10,\n\t\t},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"returns Pepper's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Pepper\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"20\")\n\t})\n\n\tt.Run(\"returns Floyd's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Floyd\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"10\")\n\t})\n\n\tt.Run(\"returns 404 on missing players\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Apollo\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusNotFound)\n\t})\n}\n\nfunc TestStoreWins(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"it records wins on POST\", func(t *testing.T) {\n\t\tplayer := \"Pepper\"\n\n\t\trequest := newPostWinRequest(player)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusAccepted)\n\n\t\tif len(store.winCalls) != 1 {\n\t\t\tt.Fatalf(\"got %d calls to RecordWin want %d\", len(store.winCalls), 1)\n\t\t}\n\n\t\tif store.winCalls[0] != player {\n\t\t\tt.Errorf(\"did not store correct winner got %q want %q\", store.winCalls[0], player)\n\t\t}\n\t})\n}\n\nfunc TestLeague(t *testing.T) {\n\n\tt.Run(\"it returns the league table as JSON\", func(t *testing.T) {\n\t\twantedLeague := []Player{\n\t\t\t{\"Cleo\", 32},\n\t\t\t{\"Chris\", 20},\n\t\t\t{\"Tiest\", 14},\n\t\t}\n\n\t\tstore := StubPlayerStore{nil, nil, wantedLeague}\n\t\tserver := NewPlayerServer(&store)\n\n\t\trequest := newLeagueRequest()\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertLeague(t, got, wantedLeague)\n\t\tassertContentType(t, response, jsonContentType)\n\n\t})\n}\n\nfunc assertContentType(t testing.TB, response *httptest.ResponseRecorder, want string) {\n\tt.Helper()\n\tif response.Result().Header.Get(\"content-type\") != want {\n\t\tt.Errorf(\"response did not have content-type of %s, got %v\", want, response.Result().Header)\n\t}\n}\n\nfunc getLeagueFromResponse(t testing.TB, body io.Reader) (league []Player) {\n\tt.Helper()\n\terr := json.NewDecoder(body).Decode(&league)\n\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to parse response from server %q into slice of Player, '%v'\", body, err)\n\t}\n\n\treturn\n}\n\nfunc assertLeague(t testing.TB, got, want []Player) {\n\tt.Helper()\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %v want %v\", got, want)\n\t}\n}\n\nfunc assertStatus(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"did not get correct status, got %d, want %d\", got, want)\n\t}\n}\n\nfunc newLeagueRequest() *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, \"/league\", nil)\n\treturn req\n}\n\nfunc newGetScoreRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc newPostWinRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodPost, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc assertResponseBody(t testing.TB, got, want string) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"response body is wrong, got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "json/v6/in_memory_player_store.go",
    "content": "package main\n\n// NewInMemoryPlayerStore initialises an empty player store.\nfunc NewInMemoryPlayerStore() *InMemoryPlayerStore {\n\treturn &InMemoryPlayerStore{map[string]int{}}\n}\n\n// InMemoryPlayerStore collects data about players in memory.\ntype InMemoryPlayerStore struct {\n\tstore map[string]int\n}\n\n// GetLeague returns a collection of Players.\nfunc (i *InMemoryPlayerStore) GetLeague() []Player {\n\tvar league []Player\n\tfor name, wins := range i.store {\n\t\tleague = append(league, Player{name, wins})\n\t}\n\treturn league\n}\n\n// RecordWin will record a player's win.\nfunc (i *InMemoryPlayerStore) RecordWin(name string) {\n\ti.store[name]++\n}\n\n// GetPlayerScore retrieves scores for a given player.\nfunc (i *InMemoryPlayerStore) GetPlayerScore(name string) int {\n\treturn i.store[name]\n}\n"
  },
  {
    "path": "json/v6/main.go",
    "content": "package main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc main() {\n\tserver := NewPlayerServer(NewInMemoryPlayerStore())\n\tlog.Fatal(http.ListenAndServe(\":5000\", server))\n}\n"
  },
  {
    "path": "json/v6/server.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n)\n\n// PlayerStore stores score information about players.\ntype PlayerStore interface {\n\tGetPlayerScore(name string) int\n\tRecordWin(name string)\n\tGetLeague() []Player\n}\n\n// Player stores a name with a number of wins.\ntype Player struct {\n\tName string\n\tWins int\n}\n\n// PlayerServer is a HTTP interface for player information.\ntype PlayerServer struct {\n\tstore PlayerStore\n\thttp.Handler\n}\n\nconst jsonContentType = \"application/json\"\n\n// NewPlayerServer creates a PlayerServer with routing configured.\nfunc NewPlayerServer(store PlayerStore) *PlayerServer {\n\tp := new(PlayerServer)\n\n\tp.store = store\n\n\trouter := http.NewServeMux()\n\trouter.Handle(\"/league\", http.HandlerFunc(p.leagueHandler))\n\trouter.Handle(\"/players/\", http.HandlerFunc(p.playersHandler))\n\n\tp.Handler = router\n\n\treturn p\n}\n\nfunc (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) {\n\tw.Header().Set(\"content-type\", jsonContentType)\n\tjson.NewEncoder(w).Encode(p.store.GetLeague())\n}\n\nfunc (p *PlayerServer) playersHandler(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\n\tswitch r.Method {\n\tcase http.MethodPost:\n\t\tp.processWin(w, player)\n\tcase http.MethodGet:\n\t\tp.showScore(w, player)\n\t}\n}\n\nfunc (p *PlayerServer) showScore(w http.ResponseWriter, player string) {\n\tscore := p.store.GetPlayerScore(player)\n\n\tif score == 0 {\n\t\tw.WriteHeader(http.StatusNotFound)\n\t}\n\n\tfmt.Fprint(w, score)\n}\n\nfunc (p *PlayerServer) processWin(w http.ResponseWriter, player string) {\n\tp.store.RecordWin(player)\n\tw.WriteHeader(http.StatusAccepted)\n}\n"
  },
  {
    "path": "json/v6/server_integration_test.go",
    "content": "package main\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestRecordingWinsAndRetrievingThem(t *testing.T) {\n\tstore := NewInMemoryPlayerStore()\n\tserver := NewPlayerServer(store)\n\tplayer := \"Pepper\"\n\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\n\tt.Run(\"get score\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newGetScoreRequest(player))\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\n\t\tassertResponseBody(t, response.Body.String(), \"3\")\n\t})\n\n\tt.Run(\"get league\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newLeagueRequest())\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\t\twant := []Player{\n\t\t\t{player, 3},\n\t\t}\n\t\tassertLeague(t, got, want)\n\t})\n}\n"
  },
  {
    "path": "json/v6/server_test.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"reflect\"\n\t\"testing\"\n)\n\ntype StubPlayerStore struct {\n\tscores   map[string]int\n\twinCalls []string\n\tleague   []Player\n}\n\nfunc (s *StubPlayerStore) GetPlayerScore(name string) int {\n\tscore := s.scores[name]\n\treturn score\n}\n\nfunc (s *StubPlayerStore) RecordWin(name string) {\n\ts.winCalls = append(s.winCalls, name)\n}\n\nfunc (s *StubPlayerStore) GetLeague() []Player {\n\treturn s.league\n}\n\nfunc TestGETPlayers(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{\n\t\t\t\"Pepper\": 20,\n\t\t\t\"Floyd\":  10,\n\t\t},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"returns Pepper's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Pepper\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"20\")\n\t})\n\n\tt.Run(\"returns Floyd's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Floyd\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"10\")\n\t})\n\n\tt.Run(\"returns 404 on missing players\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Apollo\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusNotFound)\n\t})\n}\n\nfunc TestStoreWins(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"it records wins on POST\", func(t *testing.T) {\n\t\tplayer := \"Pepper\"\n\n\t\trequest := newPostWinRequest(player)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusAccepted)\n\n\t\tif len(store.winCalls) != 1 {\n\t\t\tt.Fatalf(\"got %d calls to RecordWin want %d\", len(store.winCalls), 1)\n\t\t}\n\n\t\tif store.winCalls[0] != player {\n\t\t\tt.Errorf(\"did not store correct winner got %q want %q\", store.winCalls[0], player)\n\t\t}\n\t})\n}\n\nfunc TestLeague(t *testing.T) {\n\n\tt.Run(\"it returns the league table as JSON\", func(t *testing.T) {\n\t\twantedLeague := []Player{\n\t\t\t{\"Cleo\", 32},\n\t\t\t{\"Chris\", 20},\n\t\t\t{\"Tiest\", 14},\n\t\t}\n\n\t\tstore := StubPlayerStore{nil, nil, wantedLeague}\n\t\tserver := NewPlayerServer(&store)\n\n\t\trequest := newLeagueRequest()\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertLeague(t, got, wantedLeague)\n\t\tassertContentType(t, response, jsonContentType)\n\n\t})\n}\n\nfunc assertContentType(t testing.TB, response *httptest.ResponseRecorder, want string) {\n\tt.Helper()\n\tif response.Result().Header.Get(\"content-type\") != want {\n\t\tt.Errorf(\"response did not have content-type of %s, got %v\", want, response.Result().Header)\n\t}\n}\n\nfunc getLeagueFromResponse(t testing.TB, body io.Reader) (league []Player) {\n\tt.Helper()\n\terr := json.NewDecoder(body).Decode(&league)\n\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to parse response from server %q into slice of Player, '%v'\", body, err)\n\t}\n\n\treturn\n}\n\nfunc assertLeague(t testing.TB, got, want []Player) {\n\tt.Helper()\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %v want %v\", got, want)\n\t}\n}\n\nfunc assertStatus(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"did not get correct status, got %d, want %d\", got, want)\n\t}\n}\n\nfunc newLeagueRequest() *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, \"/league\", nil)\n\treturn req\n}\n\nfunc newGetScoreRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc newPostWinRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodPost, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc assertResponseBody(t testing.TB, got, want string) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"response body is wrong, got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "json.md",
    "content": "# JSON, routing & embedding\n\n**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/main/json)**\n\n[In the previous chapter](http-server.md) we created a web server to store how many games players have won.\n\nOur product owner has a new requirement; to have a new endpoint called `/league` which returns a list of all players stored. She would like this to be returned as JSON.\n\n## Here is the code we have so far\n\n```go\n// server.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n)\n\ntype PlayerStore interface {\n\tGetPlayerScore(name string) int\n\tRecordWin(name string)\n}\n\ntype PlayerServer struct {\n\tstore PlayerStore\n}\n\nfunc (p *PlayerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\n\tswitch r.Method {\n\tcase http.MethodPost:\n\t\tp.processWin(w, player)\n\tcase http.MethodGet:\n\t\tp.showScore(w, player)\n\t}\n}\n\nfunc (p *PlayerServer) showScore(w http.ResponseWriter, player string) {\n\tscore := p.store.GetPlayerScore(player)\n\n\tif score == 0 {\n\t\tw.WriteHeader(http.StatusNotFound)\n\t}\n\n\tfmt.Fprint(w, score)\n}\n\nfunc (p *PlayerServer) processWin(w http.ResponseWriter, player string) {\n\tp.store.RecordWin(player)\n\tw.WriteHeader(http.StatusAccepted)\n}\n```\n\n```go\n// in_memory_player_store.go\npackage main\n\nfunc NewInMemoryPlayerStore() *InMemoryPlayerStore {\n\treturn &InMemoryPlayerStore{map[string]int{}}\n}\n\ntype InMemoryPlayerStore struct {\n\tstore map[string]int\n}\n\nfunc (i *InMemoryPlayerStore) RecordWin(name string) {\n\ti.store[name]++\n}\n\nfunc (i *InMemoryPlayerStore) GetPlayerScore(name string) int {\n\treturn i.store[name]\n}\n\n```\n\n```go\n// main.go\npackage main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc main() {\n\tserver := &PlayerServer{NewInMemoryPlayerStore()}\n\tlog.Fatal(http.ListenAndServe(\":5000\", server))\n}\n```\n\nYou can find the corresponding tests in the link at the top of the chapter.\n\nWe'll start by making the league table endpoint.\n\n## Write the test first\n\nWe'll extend the existing suite as we have some useful test functions and a fake `PlayerStore` to use.\n\n```go\n//server_test.go\nfunc TestLeague(t *testing.T) {\n\tstore := StubPlayerStore{}\n\tserver := &PlayerServer{&store}\n\n\tt.Run(\"it returns 200 on /league\", func(t *testing.T) {\n\t\trequest, _ := http.NewRequest(http.MethodGet, \"/league\", nil)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t})\n}\n```\n\nBefore worrying about actual scores and JSON we will try and keep the changes small with the plan to iterate toward our goal. The simplest start is to check we can hit `/league` and get an `OK` back.\n\n## Try to run the test\n\n```\n    --- FAIL: TestLeague/it_returns_200_on_/league (0.00s)\n        server_test.go:101: status code is wrong: got 404, want 200\nFAIL\nFAIL\tplayerstore\t0.221s\nFAIL\n```\n\nOur `PlayerServer` returns a `404 Not Found`, as if we were trying to get the wins for an unknown player. Looking at how `server.go` implements `ServeHTTP`, we realize that it always assumes to be called with a URL pointing to a specific player:\n\n```go\nplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n```\n\nIn the previous chapter, we mentioned this was a fairly naive way of doing our routing. Our test informs us correctly that we need a concept how to deal with different request paths.\n\n## Write enough code to make it pass\n\nGo has a built-in routing mechanism called [`ServeMux`](https://golang.org/pkg/net/http/#ServeMux) (request multiplexer) which lets you attach `http.Handler`s to particular request paths.\n\nLet's commit some sins and get the tests passing in the quickest way we can, knowing we can refactor it with safety once we know the tests are passing.\n\n```go\n//server.go\nfunc (p *PlayerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\n\trouter := http.NewServeMux()\n\n\trouter.Handle(\"/league\", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tw.WriteHeader(http.StatusOK)\n\t}))\n\n\trouter.Handle(\"/players/\", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\n\t\tswitch r.Method {\n\t\tcase http.MethodPost:\n\t\t\tp.processWin(w, player)\n\t\tcase http.MethodGet:\n\t\t\tp.showScore(w, player)\n\t\t}\n\t}))\n\n\trouter.ServeHTTP(w, r)\n}\n```\n\n- When the request starts we create a router and then we tell it for `x` path use `y` handler.\n- So for our new endpoint, we use `http.HandlerFunc` and an _anonymous function_ to `w.WriteHeader(http.StatusOK)` when `/league` is requested to make our new test pass.\n- For the `/players/` route we just cut and paste our code into another `http.HandlerFunc`.\n- Finally, we handle the request that came in by calling our new router's `ServeHTTP` (notice how `ServeMux` is _also_ an `http.Handler`?)\n\nThe tests should now pass.\n\n## Refactor\n\n`ServeHTTP` is looking quite big, we can separate things out a bit by refactoring our handlers into separate methods.\n\n```go\n//server.go\nfunc (p *PlayerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\n\trouter := http.NewServeMux()\n\trouter.Handle(\"/league\", http.HandlerFunc(p.leagueHandler))\n\trouter.Handle(\"/players/\", http.HandlerFunc(p.playersHandler))\n\n\trouter.ServeHTTP(w, r)\n}\n\nfunc (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) {\n\tw.WriteHeader(http.StatusOK)\n}\n\nfunc (p *PlayerServer) playersHandler(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\n\tswitch r.Method {\n\tcase http.MethodPost:\n\t\tp.processWin(w, player)\n\tcase http.MethodGet:\n\t\tp.showScore(w, player)\n\t}\n}\n```\n\nIt's quite odd (and inefficient) to be setting up a router as a request comes in and then calling it. What we ideally want to do is have some kind of `NewPlayerServer` function which will take our dependencies and do the one-time setup of creating the router. Each request can then just use that one instance of the router.\n\n```go\n//server.go\ntype PlayerServer struct {\n\tstore  PlayerStore\n\trouter *http.ServeMux\n}\n\nfunc NewPlayerServer(store PlayerStore) *PlayerServer {\n\tp := &PlayerServer{\n\t\tstore,\n\t\thttp.NewServeMux(),\n\t}\n\n\tp.router.Handle(\"/league\", http.HandlerFunc(p.leagueHandler))\n\tp.router.Handle(\"/players/\", http.HandlerFunc(p.playersHandler))\n\n\treturn p\n}\n\nfunc (p *PlayerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\tp.router.ServeHTTP(w, r)\n}\n```\n\n- `PlayerServer` now needs to store a router.\n- We have moved the routing creation out of `ServeHTTP` and into our `NewPlayerServer` so this only has to be done once, not per request.\n- You will need to update all the test and production code where we used to do `PlayerServer{&store}` with `NewPlayerServer(&store)`.\n\n### One final refactor\n\nTry changing the code to the following.\n\n```go\ntype PlayerServer struct {\n\tstore PlayerStore\n\thttp.Handler\n}\n\nfunc NewPlayerServer(store PlayerStore) *PlayerServer {\n\tp := new(PlayerServer)\n\n\tp.store = store\n\n\trouter := http.NewServeMux()\n\trouter.Handle(\"/league\", http.HandlerFunc(p.leagueHandler))\n\trouter.Handle(\"/players/\", http.HandlerFunc(p.playersHandler))\n\n\tp.Handler = router\n\n\treturn p\n}\n```\n\nThen replace `server := &PlayerServer{&store}` with `server := NewPlayerServer(&store)` in `server_test.go`, `server_integration_test.go`, and `main.go`.\n\nFinally make sure you **delete** `func (p *PlayerServer) ServeHTTP(w http.ResponseWriter, r *http.Request)` as it is no longer needed!\n\n## Embedding\n\nWe changed the second property of `PlayerServer`, removing the named property `router http.ServeMux` and replaced it with `http.Handler`; this is called _embedding_.\n\n> Go does not provide the typical, type-driven notion of subclassing, but it does have the ability to “borrow” pieces of an implementation by embedding types within a struct or interface.\n\n[Effective Go - Embedding](https://golang.org/doc/effective_go.html#embedding)\n\nWhat this means is that our `PlayerServer` now has all the methods that `http.Handler` has, which is just `ServeHTTP`.\n\nTo \"fill in\" the `http.Handler` we assign it to the `router` we create in `NewPlayerServer`. We can do this because `http.ServeMux` has the method `ServeHTTP`.\n\nThis lets us remove our own `ServeHTTP` method, as we are already exposing one via the embedded type.\n\nEmbedding is a very interesting language feature. You can use it with interfaces to compose new interfaces.\n\n```go\ntype Animal interface {\n\tEater\n\tSleeper\n}\n```\n\nAnd you can use it with concrete types too, not just interfaces. As you'd expect if you embed a concrete type you'll have access to all its public methods and fields.\n\n### Any downsides?\n\nYou must be careful with embedding types because you will expose all public methods and fields of the type you embed. In our case, it is ok because we embedded just the _interface_ that we wanted to expose (`http.Handler`).\n\nIf we had been lazy and embedded `http.ServeMux` instead (the concrete type) it would still work _but_ users of `PlayerServer` would be able to add new routes to our server because `Handle(path, handler)` would be public.\n\n**When embedding types, really think about what impact that has on your public API.**\n\nIt is a _very_ common mistake to misuse embedding and end up polluting your APIs and exposing the internals of your type.\n\nNow we've restructured our application we can easily add new routes and have the start of the `/league` endpoint. We now need to make it return some useful information.\n\nWe should return some JSON that looks something like this.\n\n```json\n[\n   {\n      \"Name\":\"Bill\",\n      \"Wins\":10\n   },\n   {\n      \"Name\":\"Alice\",\n      \"Wins\":15\n   }\n]\n```\n\n## Write the test first\n\nWe'll start by trying to parse the response into something meaningful.\n\n```go\n//server_test.go\nfunc TestLeague(t *testing.T) {\n\tstore := StubPlayerStore{}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"it returns 200 on /league\", func(t *testing.T) {\n\t\trequest, _ := http.NewRequest(http.MethodGet, \"/league\", nil)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tvar got []Player\n\n\t\terr := json.NewDecoder(response.Body).Decode(&got)\n\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Unable to parse response from server %q into slice of Player, '%v'\", response.Body, err)\n\t\t}\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t})\n}\n```\n\n### Why not test the JSON string?\n\nYou could argue a simpler initial step would be just to assert that the response body has a particular JSON string.\n\nIn my experience tests that assert against JSON strings have the following problems.\n\n- *Brittleness*. If you change the data-model your tests will fail.\n- *Hard to debug*. It can be tricky to understand what the actual problem is when comparing two JSON strings.\n- *Poor intention*. Whilst the output should be JSON, what's really important is exactly what the data is, rather than how it's encoded.\n- *Re-testing the standard library*. There is no need to test how the standard library outputs JSON, it is already tested. Don't test other people's code.\n\nInstead, we should look to parse the JSON into data structures that are relevant for us to test with.\n\n### Data modelling\n\nGiven the JSON data model, it looks like we need an array of `Player` with some fields so we have created a new type to capture this.\n\n```go\n//server.go\ntype Player struct {\n\tName string\n\tWins int\n}\n```\n\n### JSON decoding\n\n```go\n//server_test.go\nvar got []Player\nerr := json.NewDecoder(response.Body).Decode(&got)\n```\n\nTo parse JSON into our data model we create a `Decoder` from `encoding/json` package and then call its `Decode` method. To create a `Decoder` it needs an `io.Reader` to read from which in our case is our response spy's `Body`.\n\n`Decode` takes the address of the thing we are trying to decode into, which is why we declare an empty slice of `Player` the line before.\n\nParsing JSON can fail so `Decode` can return an `error`. There's no point continuing the test if that fails so we check for the error and stop the test with `t.Fatalf` if it happens. Notice that we print the response body along with the error as it's important for someone running the test to see what string cannot be parsed.\n\n## Try to run the test\n\n```\n=== RUN   TestLeague/it_returns_200_on_/league\n    --- FAIL: TestLeague/it_returns_200_on_/league (0.00s)\n        server_test.go:107: Unable to parse response from server '' into slice of Player, 'unexpected end of JSON input'\n```\n\nOur endpoint currently does not return a body so it cannot be parsed into JSON.\n\n## Write enough code to make it pass\n\n```go\n//server.go\nfunc (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) {\n\tleagueTable := []Player{\n\t\t{\"Chris\", 20},\n\t}\n\n\tjson.NewEncoder(w).Encode(leagueTable)\n\n\tw.WriteHeader(http.StatusOK)\n}\n```\n\nThe test now passes.\n\n### Encoding and Decoding\n\nNotice the lovely symmetry in the standard library.\n\n- To create an `Encoder` you need an `io.Writer` which is what `http.ResponseWriter` implements.\n- To create a `Decoder` you need an `io.Reader` which the `Body` field of our response spy implements.\n\nThroughout this book, we have used `io.Writer` and this is another demonstration of its prevalence in the standard library and how a lot of libraries easily work with it.\n\n## Refactor\n\nIt would be nice to introduce a separation of concern between our handler and getting the `leagueTable` as we know we're going to not hard-code that very soon.\n\n```go\n//server.go\nfunc (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) {\n\tjson.NewEncoder(w).Encode(p.getLeagueTable())\n\tw.WriteHeader(http.StatusOK)\n}\n\nfunc (p *PlayerServer) getLeagueTable() []Player {\n\treturn []Player{\n\t\t{\"Chris\", 20},\n\t}\n}\n```\n\nNext, we'll want to extend our test so that we can control exactly what data we want back.\n\n## Write the test first\n\nWe can update the test to assert that the league table contains some players that we will stub in our store.\n\nUpdate `StubPlayerStore` to let it store a league, which is just a slice of `Player`. We'll store our expected data in there.\n\n```go\n//server_test.go\ntype StubPlayerStore struct {\n\tscores   map[string]int\n\twinCalls []string\n\tleague   []Player\n}\n```\n\nNext, update our current test by putting some players in the league property of our stub and assert they get returned from our server.\n\n```go\n//server_test.go\nfunc TestLeague(t *testing.T) {\n\n\tt.Run(\"it returns the league table as JSON\", func(t *testing.T) {\n\t\twantedLeague := []Player{\n\t\t\t{\"Cleo\", 32},\n\t\t\t{\"Chris\", 20},\n\t\t\t{\"Tiest\", 14},\n\t\t}\n\n\t\tstore := StubPlayerStore{nil, nil, wantedLeague}\n\t\tserver := NewPlayerServer(&store)\n\n\t\trequest, _ := http.NewRequest(http.MethodGet, \"/league\", nil)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tvar got []Player\n\n\t\terr := json.NewDecoder(response.Body).Decode(&got)\n\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Unable to parse response from server %q into slice of Player, '%v'\", response.Body, err)\n\t\t}\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\n\t\tif !reflect.DeepEqual(got, wantedLeague) {\n\t\t\tt.Errorf(\"got %v want %v\", got, wantedLeague)\n\t\t}\n\t})\n}\n```\n\n## Try to run the test\n\n```\n./server_test.go:33:3: too few values in struct initializer\n./server_test.go:70:3: too few values in struct initializer\n```\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\nYou'll need to update the other tests as we have a new field in `StubPlayerStore`; set it to nil for the other tests.\n\nTry running the tests again and you should get\n\n```\n=== RUN   TestLeague/it_returns_the_league_table_as_JSON\n    --- FAIL: TestLeague/it_returns_the_league_table_as_JSON (0.00s)\n        server_test.go:124: got [{Chris 20}] want [{Cleo 32} {Chris 20} {Tiest 14}]\n```\n\n## Write enough code to make it pass\n\nWe know the data is in our `StubPlayerStore` and we've abstracted that away into an interface `PlayerStore`. We need to update this so anyone passing us in a `PlayerStore` can provide us with the data for leagues.\n\n```go\n//server.go\ntype PlayerStore interface {\n\tGetPlayerScore(name string) int\n\tRecordWin(name string)\n\tGetLeague() []Player\n}\n```\n\nNow we can update our handler code to call that rather than returning a hard-coded list. Delete our method `getLeagueTable()` and then update `leagueHandler` to call `GetLeague()`.\n\n```go\n//server.go\nfunc (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) {\n\tjson.NewEncoder(w).Encode(p.store.GetLeague())\n\tw.WriteHeader(http.StatusOK)\n}\n```\n\nTry and run the tests.\n\n```\n# github.com/quii/learn-go-with-tests/json-and-io/v4\n./main.go:9:50: cannot use NewInMemoryPlayerStore() (type *InMemoryPlayerStore) as type PlayerStore in argument to NewPlayerServer:\n    *InMemoryPlayerStore does not implement PlayerStore (missing GetLeague method)\n./server_integration_test.go:11:27: cannot use store (type *InMemoryPlayerStore) as type PlayerStore in argument to NewPlayerServer:\n    *InMemoryPlayerStore does not implement PlayerStore (missing GetLeague method)\n./server_test.go:36:28: cannot use &store (type *StubPlayerStore) as type PlayerStore in argument to NewPlayerServer:\n    *StubPlayerStore does not implement PlayerStore (missing GetLeague method)\n./server_test.go:74:28: cannot use &store (type *StubPlayerStore) as type PlayerStore in argument to NewPlayerServer:\n    *StubPlayerStore does not implement PlayerStore (missing GetLeague method)\n./server_test.go:106:29: cannot use &store (type *StubPlayerStore) as type PlayerStore in argument to NewPlayerServer:\n    *StubPlayerStore does not implement PlayerStore (missing GetLeague method)\n```\n\nThe compiler is complaining because `InMemoryPlayerStore` and `StubPlayerStore` do not have the new method we added to our interface.\n\nFor `StubPlayerStore` it's pretty easy, just return the `league` field we added earlier.\n\n```go\n//server_test.go\nfunc (s *StubPlayerStore) GetLeague() []Player {\n\treturn s.league\n}\n```\n\nHere's a reminder of how `InMemoryStore` is implemented.\n\n```go\n//in_memory_player_store.go\ntype InMemoryPlayerStore struct {\n\tstore map[string]int\n}\n```\n\nWhilst it would be pretty straightforward to implement `GetLeague` \"properly\" by iterating over the map remember we are just trying to _write the minimal amount of code to make the tests pass_.\n\nSo let's just get the compiler happy for now and live with the uncomfortable feeling of an incomplete implementation in our `InMemoryStore`.\n\n```go\n//in_memory_player_store.go\nfunc (i *InMemoryPlayerStore) GetLeague() []Player {\n\treturn nil\n}\n```\n\nWhat this is really telling us is that _later_ we're going to want to test this but let's park that for now.\n\nTry and run the tests, the compiler should pass and the tests should be passing!\n\n## Refactor\n\nThe test code does not convey our intent very well and has a lot of boilerplate we can refactor away.\n\n```go\n//server_test.go\nt.Run(\"it returns the league table as JSON\", func(t *testing.T) {\n\twantedLeague := []Player{\n\t\t{\"Cleo\", 32},\n\t\t{\"Chris\", 20},\n\t\t{\"Tiest\", 14},\n\t}\n\n\tstore := StubPlayerStore{nil, nil, wantedLeague}\n\tserver := NewPlayerServer(&store)\n\n\trequest := newLeagueRequest()\n\tresponse := httptest.NewRecorder()\n\n\tserver.ServeHTTP(response, request)\n\n\tgot := getLeagueFromResponse(t, response.Body)\n\tassertStatus(t, response.Code, http.StatusOK)\n\tassertLeague(t, got, wantedLeague)\n})\n```\n\nHere are the new helpers\n\n```go\n//server_test.go\nfunc getLeagueFromResponse(t testing.TB, body io.Reader) (league []Player) {\n\tt.Helper()\n\terr := json.NewDecoder(body).Decode(&league)\n\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to parse response from server %q into slice of Player, '%v'\", body, err)\n\t}\n\n\treturn\n}\n\nfunc assertLeague(t testing.TB, got, want []Player) {\n\tt.Helper()\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %v want %v\", got, want)\n\t}\n}\n\nfunc newLeagueRequest() *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, \"/league\", nil)\n\treturn req\n}\n```\n\nOne final thing we need to do for our server to work is make sure we return a `content-type` header in the response so machines can recognise we are returning `JSON`.\n\n## Write the test first\n\nAdd this assertion to the existing test\n\n```go\n//server_test.go\nif response.Result().Header.Get(\"content-type\") != \"application/json\" {\n\tt.Errorf(\"response did not have content-type of application/json, got %v\", response.Result().Header)\n}\n```\n\n## Try to run the test\n\n```\n=== RUN   TestLeague/it_returns_the_league_table_as_JSON\n    --- FAIL: TestLeague/it_returns_the_league_table_as_JSON (0.00s)\n        server_test.go:124: response did not have content-type of application/json, got map[Content-Type:[text/plain; charset=utf-8]]\n```\n\n## Write enough code to make it pass\n\nUpdate `leagueHandler`\n\n```go\n//server.go\nfunc (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) {\n\tw.Header().Set(\"content-type\", \"application/json\")\n\tjson.NewEncoder(w).Encode(p.store.GetLeague())\n}\n```\n\nThe test should pass.\n\n## Refactor\n\nCreate a constant for \"application/json\" and use it in `leagueHandler`\n\n```go\n//server.go\nconst jsonContentType = \"application/json\"\n\nfunc (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) {\n\tw.Header().Set(\"content-type\", jsonContentType)\n\tjson.NewEncoder(w).Encode(p.store.GetLeague())\n}\n```\n\nThen add a helper for `assertContentType`.\n\n```go\n//server_test.go\nfunc assertContentType(t testing.TB, response *httptest.ResponseRecorder, want string) {\n\tt.Helper()\n\tif response.Result().Header.Get(\"content-type\") != want {\n\t\tt.Errorf(\"response did not have content-type of %s, got %v\", want, response.Result().Header)\n\t}\n}\n```\n\nUse it in the test.\n\n```go\n//server_test.go\nassertContentType(t, response, jsonContentType)\n```\n\nNow that we have sorted out `PlayerServer` for now we can turn our attention to `InMemoryPlayerStore` because right now if we tried to demo this to the product owner `/league` will not work.\n\nThe quickest way for us to get some confidence is to add to our integration test, we can hit the new endpoint and check we get back the correct response from `/league`.\n\n## Write the test first\n\nWe can use `t.Run` to break up this test a bit and we can reuse the helpers from our server tests - again showing the importance of refactoring tests.\n\n```go\n//server_integration_test.go\nfunc TestRecordingWinsAndRetrievingThem(t *testing.T) {\n\tstore := NewInMemoryPlayerStore()\n\tserver := NewPlayerServer(store)\n\tplayer := \"Pepper\"\n\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\n\tt.Run(\"get score\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newGetScoreRequest(player))\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\n\t\tassertResponseBody(t, response.Body.String(), \"3\")\n\t})\n\n\tt.Run(\"get league\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newLeagueRequest())\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\t\twant := []Player{\n\t\t\t{\"Pepper\", 3},\n\t\t}\n\t\tassertLeague(t, got, want)\n\t})\n}\n```\n\n## Try to run the test\n\n```\n=== RUN   TestRecordingWinsAndRetrievingThem/get_league\n    --- FAIL: TestRecordingWinsAndRetrievingThem/get_league (0.00s)\n        server_integration_test.go:35: got [] want [{Pepper 3}]\n```\n\n## Write enough code to make it pass\n\n`InMemoryPlayerStore` is returning `nil` when you call `GetLeague()` so we'll need to fix that.\n\n```go\n//in_memory_player_store.go\nfunc (i *InMemoryPlayerStore) GetLeague() []Player {\n\tvar league []Player\n\tfor name, wins := range i.store {\n\t\tleague = append(league, Player{name, wins})\n\t}\n\treturn league\n}\n```\n\nAll we need to do is iterate over the map and convert each key/value to a `Player`.\n\nThe test should now pass.\n\n## Wrapping up\n\nWe've continued to safely iterate on our program using TDD, making it support new endpoints in a maintainable way with a router and it can now return JSON for our consumers. In the next chapter, we will cover persisting the data and sorting our league.\n\nWhat we've covered:\n\n- **Routing**. The standard library offers you an easy to use type to do routing. It fully embraces the `http.Handler` interface in that you assign routes to `Handler`s and the router itself is also a `Handler`. It does not have some features you might expect though such as path variables (e.g `/users/{id}`). You can easily parse this information yourself but you might want to consider looking at other routing libraries if it becomes a burden. Most of the popular ones stick to the standard library's philosophy of also implementing `http.Handler`.\n- **Type embedding**. We touched a little on this technique but you can [learn more about it from Effective Go](https://golang.org/doc/effective_go.html#embedding). If there is one thing you should take away from this is that it can be extremely useful but _always thinking about your public API, only expose what's appropriate_.\n- **JSON deserializing and serializing**. The standard library makes it very trivial to serialise and deserialise your data. It is also open to configuration and you can customise how these data transformations work if necessary.\n"
  },
  {
    "path": "maps/v1/dictionary.go",
    "content": "package main\n\n// Dictionary store definitions to words.\ntype Dictionary map[string]string\n\n// Search find a word in the dictionary.\nfunc (d Dictionary) Search(word string) string {\n\treturn d[word]\n}\n"
  },
  {
    "path": "maps/v1/dictionary_test.go",
    "content": "package main\n\nimport \"testing\"\n\nfunc TestSearch(t *testing.T) {\n\tdictionary := Dictionary{\"test\": \"this is just a test\"}\n\n\tgot := dictionary.Search(\"test\")\n\twant := \"this is just a test\"\n\n\tassertStrings(t, got, want)\n}\n\nfunc assertStrings(t testing.TB, got, want string) {\n\tt.Helper()\n\n\tif got != want {\n\t\tt.Errorf(\"got error %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "maps/v2/dictionary.go",
    "content": "package main\n\nimport \"errors\"\n\n// Dictionary store definitions to words.\ntype Dictionary map[string]string\n\n// ErrNotFound means the definition could not be found for the given word.\nvar ErrNotFound = errors.New(\"could not find the word you were looking for\")\n\n// Search find a word in the dictionary.\nfunc (d Dictionary) Search(word string) (string, error) {\n\tdefinition, ok := d[word]\n\tif !ok {\n\t\treturn \"\", ErrNotFound\n\t}\n\n\treturn definition, nil\n}\n"
  },
  {
    "path": "maps/v2/dictionary_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n)\n\nfunc TestSearch(t *testing.T) {\n\tdictionary := Dictionary{\"test\": \"this is just a test\"}\n\n\tt.Run(\"known word\", func(t *testing.T) {\n\t\tgot, _ := dictionary.Search(\"test\")\n\t\twant := \"this is just a test\"\n\n\t\tassertStrings(t, got, want)\n\t})\n\n\tt.Run(\"unknown word\", func(t *testing.T) {\n\t\t_, got := dictionary.Search(\"unknown\")\n\n\t\tassertError(t, got, ErrNotFound)\n\t})\n}\n\nfunc assertStrings(t testing.TB, got, want string) {\n\tt.Helper()\n\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n\nfunc assertError(t testing.TB, got, want error) {\n\tt.Helper()\n\n\tif got != want {\n\t\tt.Errorf(\"got error %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "maps/v3/dictionary.go",
    "content": "package main\n\nimport \"errors\"\n\n// Dictionary store definitions to words.\ntype Dictionary map[string]string\n\n// ErrNotFound means the definition could not be found for the given word.\nvar ErrNotFound = errors.New(\"could not find the word you were looking for\")\n\n// Search find a word in the dictionary.\nfunc (d Dictionary) Search(word string) (string, error) {\n\tdefinition, ok := d[word]\n\tif !ok {\n\t\treturn \"\", ErrNotFound\n\t}\n\n\treturn definition, nil\n}\n\n// Add inserts a word and definition into the dictionary.\nfunc (d Dictionary) Add(word, definition string) {\n\td[word] = definition\n}\n"
  },
  {
    "path": "maps/v3/dictionary_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n)\n\nfunc TestSearch(t *testing.T) {\n\tdictionary := Dictionary{\"test\": \"this is just a test\"}\n\n\tt.Run(\"known word\", func(t *testing.T) {\n\t\tgot, _ := dictionary.Search(\"test\")\n\t\twant := \"this is just a test\"\n\n\t\tassertStrings(t, got, want)\n\t})\n\n\tt.Run(\"unknown word\", func(t *testing.T) {\n\t\t_, got := dictionary.Search(\"unknown\")\n\n\t\tassertError(t, got, ErrNotFound)\n\t})\n}\n\nfunc TestAdd(t *testing.T) {\n\tdictionary := Dictionary{}\n\tword := \"test\"\n\tdefinition := \"this is just a test\"\n\n\tdictionary.Add(word, definition)\n\n\tassertDefinition(t, dictionary, word, definition)\n}\n\nfunc assertStrings(t testing.TB, got, want string) {\n\tt.Helper()\n\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n\nfunc assertError(t testing.TB, got, want error) {\n\tt.Helper()\n\n\tif got != want {\n\t\tt.Errorf(\"got error %q want %q\", got, want)\n\t}\n}\n\nfunc assertDefinition(t testing.TB, dictionary Dictionary, word, definition string) {\n\tt.Helper()\n\n\tgot, err := dictionary.Search(word)\n\tif err != nil {\n\t\tt.Fatal(\"should find added word:\", err)\n\t}\n\n\tif definition != got {\n\t\tt.Errorf(\"got %q want %q\", got, definition)\n\t}\n}\n"
  },
  {
    "path": "maps/v4/dictionary.go",
    "content": "package main\n\nconst (\n\t// ErrNotFound means the definition could not be found for the given word\n\tErrNotFound = DictionaryErr(\"could not find the word you were looking for\")\n\n\t// ErrWordExists means you are trying to add a word that is already known\n\tErrWordExists = DictionaryErr(\"cannot add word because it already exists\")\n)\n\n// DictionaryErr are errors that can happen when interacting with the dictionary.\ntype DictionaryErr string\n\nfunc (e DictionaryErr) Error() string {\n\treturn string(e)\n}\n\n// Dictionary store definitions to words.\ntype Dictionary map[string]string\n\n// Search find a word in the dictionary.\nfunc (d Dictionary) Search(word string) (string, error) {\n\tdefinition, ok := d[word]\n\tif !ok {\n\t\treturn \"\", ErrNotFound\n\t}\n\n\treturn definition, nil\n}\n\n// Add inserts a word and definition into the dictionary.\nfunc (d Dictionary) Add(word, definition string) error {\n\t_, err := d.Search(word)\n\tswitch err {\n\tcase ErrNotFound:\n\t\td[word] = definition\n\tcase nil:\n\t\treturn ErrWordExists\n\tdefault:\n\t\treturn err\n\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "maps/v4/dictionary_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n)\n\nfunc TestSearch(t *testing.T) {\n\tdictionary := Dictionary{\"test\": \"this is just a test\"}\n\n\tt.Run(\"known word\", func(t *testing.T) {\n\t\tgot, _ := dictionary.Search(\"test\")\n\t\twant := \"this is just a test\"\n\n\t\tassertStrings(t, got, want)\n\t})\n\n\tt.Run(\"unknown word\", func(t *testing.T) {\n\t\t_, got := dictionary.Search(\"unknown\")\n\n\t\tassertError(t, got, ErrNotFound)\n\t})\n}\n\nfunc TestAdd(t *testing.T) {\n\tt.Run(\"new word\", func(t *testing.T) {\n\t\tdictionary := Dictionary{}\n\t\tword := \"test\"\n\t\tdefinition := \"this is just a test\"\n\n\t\terr := dictionary.Add(word, definition)\n\n\t\tassertError(t, err, nil)\n\t\tassertDefinition(t, dictionary, word, definition)\n\t})\n\n\tt.Run(\"existing word\", func(t *testing.T) {\n\t\tword := \"test\"\n\t\tdefinition := \"this is just a test\"\n\t\tdictionary := Dictionary{word: definition}\n\t\terr := dictionary.Add(word, \"new test\")\n\n\t\tassertError(t, err, ErrWordExists)\n\t\tassertDefinition(t, dictionary, word, definition)\n\t})\n}\n\nfunc assertStrings(t testing.TB, got, want string) {\n\tt.Helper()\n\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n\nfunc assertError(t testing.TB, got, want error) {\n\tt.Helper()\n\n\tif got != want {\n\t\tt.Errorf(\"got error %q want %q\", got, want)\n\t}\n}\n\nfunc assertDefinition(t testing.TB, dictionary Dictionary, word, definition string) {\n\tt.Helper()\n\n\tgot, err := dictionary.Search(word)\n\tif err != nil {\n\t\tt.Fatal(\"should find added word:\", err)\n\t}\n\n\tif definition != got {\n\t\tt.Errorf(\"got %q want %q\", got, definition)\n\t}\n}\n"
  },
  {
    "path": "maps/v5/dictionary.go",
    "content": "package main\n\nconst (\n\t// ErrNotFound means the definition could not be found for the given word\n\tErrNotFound = DictionaryErr(\"could not find the word you were looking for\")\n\n\t// ErrWordExists means you are trying to add a word that is already known\n\tErrWordExists = DictionaryErr(\"cannot add word because it already exists\")\n)\n\n// DictionaryErr are errors that can happen when interacting with the dictionary.\ntype DictionaryErr string\n\nfunc (e DictionaryErr) Error() string {\n\treturn string(e)\n}\n\n// Dictionary store definitions to words.\ntype Dictionary map[string]string\n\n// Search find a word in the dictionary.\nfunc (d Dictionary) Search(word string) (string, error) {\n\tdefinition, ok := d[word]\n\tif !ok {\n\t\treturn \"\", ErrNotFound\n\t}\n\n\treturn definition, nil\n}\n\n// Add inserts a word and definition into the dictionary.\nfunc (d Dictionary) Add(word, definition string) error {\n\t_, err := d.Search(word)\n\tswitch err {\n\tcase ErrNotFound:\n\t\td[word] = definition\n\tcase nil:\n\t\treturn ErrWordExists\n\tdefault:\n\t\treturn err\n\n\t}\n\n\treturn nil\n}\n\n// Update changes the definition of a given word.\nfunc (d Dictionary) Update(word, definition string) {\n\td[word] = definition\n}\n"
  },
  {
    "path": "maps/v5/dictionary_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n)\n\nfunc TestSearch(t *testing.T) {\n\tdictionary := Dictionary{\"test\": \"this is just a test\"}\n\n\tt.Run(\"known word\", func(t *testing.T) {\n\t\tgot, _ := dictionary.Search(\"test\")\n\t\twant := \"this is just a test\"\n\n\t\tassertStrings(t, got, want)\n\t})\n\n\tt.Run(\"unknown word\", func(t *testing.T) {\n\t\t_, got := dictionary.Search(\"unknown\")\n\n\t\tassertError(t, got, ErrNotFound)\n\t})\n}\n\nfunc TestAdd(t *testing.T) {\n\tt.Run(\"new word\", func(t *testing.T) {\n\t\tdictionary := Dictionary{}\n\t\tword := \"test\"\n\t\tdefinition := \"this is just a test\"\n\n\t\terr := dictionary.Add(word, definition)\n\n\t\tassertError(t, err, nil)\n\t\tassertDefinition(t, dictionary, word, definition)\n\t})\n\n\tt.Run(\"existing word\", func(t *testing.T) {\n\t\tword := \"test\"\n\t\tdefinition := \"this is just a test\"\n\t\tdictionary := Dictionary{word: definition}\n\t\terr := dictionary.Add(word, \"new test\")\n\n\t\tassertError(t, err, ErrWordExists)\n\t\tassertDefinition(t, dictionary, word, definition)\n\t})\n}\n\nfunc TestUpdate(t *testing.T) {\n\tword := \"test\"\n\tdefinition := \"this is just a test\"\n\tdictionary := Dictionary{word: definition}\n\tnewDefinition := \"new definition\"\n\n\tdictionary.Update(word, newDefinition)\n\n\tassertDefinition(t, dictionary, word, newDefinition)\n}\n\nfunc assertStrings(t testing.TB, got, want string) {\n\tt.Helper()\n\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n\nfunc assertError(t testing.TB, got, want error) {\n\tt.Helper()\n\n\tif got != want {\n\t\tt.Errorf(\"got error %q want %q\", got, want)\n\t}\n}\n\nfunc assertDefinition(t testing.TB, dictionary Dictionary, word, definition string) {\n\tt.Helper()\n\n\tgot, err := dictionary.Search(word)\n\tif err != nil {\n\t\tt.Fatal(\"should find added word:\", err)\n\t}\n\n\tif definition != got {\n\t\tt.Errorf(\"got %q want %q\", got, definition)\n\t}\n}\n"
  },
  {
    "path": "maps/v6/dictionary.go",
    "content": "package main\n\nconst (\n\t// ErrNotFound means the definition could not be found for the given word\n\tErrNotFound = DictionaryErr(\"could not find the word you were looking for\")\n\n\t// ErrWordExists means you are trying to add a word that is already known\n\tErrWordExists = DictionaryErr(\"cannot add word because it already exists\")\n\n\t// ErrWordDoesNotExist occurs when trying to perform an operation on a word not in the dictionary\n\tErrWordDoesNotExist = DictionaryErr(\"cannot perform operation on word because it does not exist\")\n)\n\n// DictionaryErr are errors that can happen when interacting with the dictionary.\ntype DictionaryErr string\n\nfunc (e DictionaryErr) Error() string {\n\treturn string(e)\n}\n\n// Dictionary store definitions to words.\ntype Dictionary map[string]string\n\n// Search find a word in the dictionary.\nfunc (d Dictionary) Search(word string) (string, error) {\n\tdefinition, ok := d[word]\n\tif !ok {\n\t\treturn \"\", ErrNotFound\n\t}\n\n\treturn definition, nil\n}\n\n// Add inserts a word and definition into the dictionary.\nfunc (d Dictionary) Add(word, definition string) error {\n\t_, err := d.Search(word)\n\tswitch err {\n\tcase ErrNotFound:\n\t\td[word] = definition\n\tcase nil:\n\t\treturn ErrWordExists\n\tdefault:\n\t\treturn err\n\n\t}\n\n\treturn nil\n}\n\n// Update changes the definition of a given word.\nfunc (d Dictionary) Update(word, definition string) error {\n\t_, err := d.Search(word)\n\tswitch err {\n\tcase ErrNotFound:\n\t\treturn ErrWordDoesNotExist\n\tcase nil:\n\t\td[word] = definition\n\tdefault:\n\t\treturn err\n\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "maps/v6/dictionary_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n)\n\nfunc TestSearch(t *testing.T) {\n\tdictionary := Dictionary{\"test\": \"this is just a test\"}\n\n\tt.Run(\"known word\", func(t *testing.T) {\n\t\tgot, _ := dictionary.Search(\"test\")\n\t\twant := \"this is just a test\"\n\n\t\tassertStrings(t, got, want)\n\t})\n\n\tt.Run(\"unknown word\", func(t *testing.T) {\n\t\t_, got := dictionary.Search(\"unknown\")\n\n\t\tassertError(t, got, ErrNotFound)\n\t})\n}\n\nfunc TestAdd(t *testing.T) {\n\tt.Run(\"new word\", func(t *testing.T) {\n\t\tdictionary := Dictionary{}\n\t\tword := \"test\"\n\t\tdefinition := \"this is just a test\"\n\n\t\terr := dictionary.Add(word, definition)\n\n\t\tassertError(t, err, nil)\n\t\tassertDefinition(t, dictionary, word, definition)\n\t})\n\n\tt.Run(\"existing word\", func(t *testing.T) {\n\t\tword := \"test\"\n\t\tdefinition := \"this is just a test\"\n\t\tdictionary := Dictionary{word: definition}\n\t\terr := dictionary.Add(word, \"new test\")\n\n\t\tassertError(t, err, ErrWordExists)\n\t\tassertDefinition(t, dictionary, word, definition)\n\t})\n}\n\nfunc TestUpdate(t *testing.T) {\n\tt.Run(\"existing word\", func(t *testing.T) {\n\t\tword := \"test\"\n\t\tdefinition := \"this is just a test\"\n\t\tnewDefinition := \"new definition\"\n\t\tdictionary := Dictionary{word: definition}\n\t\terr := dictionary.Update(word, newDefinition)\n\n\t\tassertError(t, err, nil)\n\t\tassertDefinition(t, dictionary, word, newDefinition)\n\t})\n\n\tt.Run(\"new word\", func(t *testing.T) {\n\t\tword := \"test\"\n\t\tdefinition := \"this is just a test\"\n\t\tdictionary := Dictionary{}\n\n\t\terr := dictionary.Update(word, definition)\n\n\t\tassertError(t, err, ErrWordDoesNotExist)\n\t})\n}\n\nfunc assertStrings(t testing.TB, got, want string) {\n\tt.Helper()\n\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n\nfunc assertError(t testing.TB, got, want error) {\n\tt.Helper()\n\n\tif got != want {\n\t\tt.Errorf(\"got error %q want %q\", got, want)\n\t}\n}\n\nfunc assertDefinition(t testing.TB, dictionary Dictionary, word, definition string) {\n\tt.Helper()\n\n\tgot, err := dictionary.Search(word)\n\tif err != nil {\n\t\tt.Fatal(\"should find added word:\", err)\n\t}\n\n\tif definition != got {\n\t\tt.Errorf(\"got %q want %q\", got, definition)\n\t}\n}\n"
  },
  {
    "path": "maps/v7/dictionary.go",
    "content": "package main\n\nconst (\n\t// ErrNotFound means the definition could not be found for the given word\n\tErrNotFound = DictionaryErr(\"could not find the word you were looking for\")\n\n\t// ErrWordExists means you are trying to add a word that is already known\n\tErrWordExists = DictionaryErr(\"cannot add word because it already exists\")\n\n\t// ErrWordDoesNotExist occurs when trying to perform an operation on a word not in the dictionary\n\tErrWordDoesNotExist = DictionaryErr(\"cannot perform operation on word because it does not exist\")\n)\n\n// DictionaryErr are errors that can happen when interacting with the dictionary.\ntype DictionaryErr string\n\nfunc (e DictionaryErr) Error() string {\n\treturn string(e)\n}\n\n// Dictionary store definitions to words.\ntype Dictionary map[string]string\n\n// Search find a word in the dictionary.\nfunc (d Dictionary) Search(word string) (string, error) {\n\tdefinition, ok := d[word]\n\tif !ok {\n\t\treturn \"\", ErrNotFound\n\t}\n\n\treturn definition, nil\n}\n\n// Add inserts a word and definition into the dictionary.\nfunc (d Dictionary) Add(word, definition string) error {\n\t_, err := d.Search(word)\n\tswitch err {\n\tcase ErrNotFound:\n\t\td[word] = definition\n\tcase nil:\n\t\treturn ErrWordExists\n\tdefault:\n\t\treturn err\n\n\t}\n\n\treturn nil\n}\n\n// Update changes the definition of a given word.\nfunc (d Dictionary) Update(word, definition string) error {\n\t_, err := d.Search(word)\n\tswitch err {\n\tcase ErrNotFound:\n\t\treturn ErrWordDoesNotExist\n\tcase nil:\n\t\td[word] = definition\n\tdefault:\n\t\treturn err\n\n\t}\n\n\treturn nil\n}\n\n// Delete removes a word from the dictionary.\nfunc (d Dictionary) Delete(word string) error {\n\t_, err := d.Search(word)\n\tswitch err {\n\tcase ErrNotFound:\n\t\treturn ErrWordDoesNotExist\n\tcase nil:\n\t\tdelete(d, word)\n\tdefault:\n\t\treturn err\n\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "maps/v7/dictionary_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n)\n\nfunc TestSearch(t *testing.T) {\n\tdictionary := Dictionary{\"test\": \"this is just a test\"}\n\n\tt.Run(\"known word\", func(t *testing.T) {\n\t\tgot, _ := dictionary.Search(\"test\")\n\t\twant := \"this is just a test\"\n\n\t\tassertStrings(t, got, want)\n\t})\n\n\tt.Run(\"unknown word\", func(t *testing.T) {\n\t\t_, got := dictionary.Search(\"unknown\")\n\n\t\tassertError(t, got, ErrNotFound)\n\t})\n}\n\nfunc TestAdd(t *testing.T) {\n\tt.Run(\"new word\", func(t *testing.T) {\n\t\tdictionary := Dictionary{}\n\t\tword := \"test\"\n\t\tdefinition := \"this is just a test\"\n\n\t\terr := dictionary.Add(word, definition)\n\n\t\tassertError(t, err, nil)\n\t\tassertDefinition(t, dictionary, word, definition)\n\t})\n\n\tt.Run(\"existing word\", func(t *testing.T) {\n\t\tword := \"test\"\n\t\tdefinition := \"this is just a test\"\n\t\tdictionary := Dictionary{word: definition}\n\t\terr := dictionary.Add(word, \"new test\")\n\n\t\tassertError(t, err, ErrWordExists)\n\t\tassertDefinition(t, dictionary, word, definition)\n\t})\n}\n\nfunc TestUpdate(t *testing.T) {\n\tt.Run(\"existing word\", func(t *testing.T) {\n\t\tword := \"test\"\n\t\tdefinition := \"this is just a test\"\n\t\tnewDefinition := \"new definition\"\n\t\tdictionary := Dictionary{word: definition}\n\t\terr := dictionary.Update(word, newDefinition)\n\n\t\tassertError(t, err, nil)\n\t\tassertDefinition(t, dictionary, word, newDefinition)\n\t})\n\n\tt.Run(\"new word\", func(t *testing.T) {\n\t\tword := \"test\"\n\t\tdefinition := \"this is just a test\"\n\t\tdictionary := Dictionary{}\n\n\t\terr := dictionary.Update(word, definition)\n\n\t\tassertError(t, err, ErrWordDoesNotExist)\n\t})\n}\n\nfunc TestDelete(t *testing.T) {\n\tt.Run(\"existing word\", func(t *testing.T) {\n\t\tword := \"test\"\n\t\tdictionary := Dictionary{word: \"test definition\"}\n\n\t\terr := dictionary.Delete(word)\n\n\t\tassertError(t, err, nil)\n\n\t\t_, err = dictionary.Search(word)\n\n\t\tassertError(t, err, ErrNotFound)\n\t})\n\n\tt.Run(\"non-existing word\", func(t *testing.T) {\n\t\tword := \"test\"\n\t\tdictionary := Dictionary{}\n\n\t\terr := dictionary.Delete(word)\n\n\t\tassertError(t, err, ErrWordDoesNotExist)\n\t})\n}\n\nfunc assertStrings(t testing.TB, got, want string) {\n\tt.Helper()\n\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n\nfunc assertError(t testing.TB, got, want error) {\n\tt.Helper()\n\n\tif got != want {\n\t\tt.Errorf(\"got error %q want %q\", got, want)\n\t}\n}\n\nfunc assertDefinition(t testing.TB, dictionary Dictionary, word, definition string) {\n\tt.Helper()\n\n\tgot, err := dictionary.Search(word)\n\tif err != nil {\n\t\tt.Fatal(\"should find added word:\", err)\n\t}\n\n\tif definition != got {\n\t\tt.Errorf(\"got %q want %q\", got, definition)\n\t}\n}\n"
  },
  {
    "path": "maps.md",
    "content": "# Maps\n\n**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/main/maps)**\n\nIn [arrays & slices](arrays-and-slices.md), you saw how to store values in order. Now, we will look at a way to store items by a `key` and look them up quickly.\n\nMaps allow you to store items in a manner similar to a dictionary. You can think of the `key` as the word and the `value` as the definition. And what better way is there to learn about Maps than to build our own dictionary?\n\nFirst, assuming we already have some words with their definitions in the dictionary, if we search for a word, it should return the definition of it.\n\n## Write the test first\n\nIn `dictionary_test.go`\n\n```go\npackage main\n\nimport \"testing\"\n\nfunc TestSearch(t *testing.T) {\n\tdictionary := map[string]string{\"test\": \"this is just a test\"}\n\n\tgot := Search(dictionary, \"test\")\n\twant := \"this is just a test\"\n\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q given, %q\", got, want, \"test\")\n\t}\n}\n```\n\nDeclaring a Map is somewhat similar to an array. Except, it starts with the `map` keyword and requires two types. The first is the key type, which is written inside the `[]`. The second is the value type, which goes right after the `[]`.\n\nThe key type is special. It can only be a comparable type because without the ability to tell if 2 keys are equal, we have no way to ensure that we are getting the correct value. Comparable types are explained in depth in the [language spec](https://golang.org/ref/spec#Comparison_operators).\n\nThe value type, on the other hand, can be any type you want. It can even be another map.\n\nEverything else in this test should be familiar.\n\n## Try to run the test\n\nBy running `go test` the compiler will fail with `./dictionary_test.go:8:9: undefined: Search`.\n\n## Write the minimal amount of code for the test to run and check the output\n\nIn `dictionary.go`\n\n```go\npackage main\n\nfunc Search(dictionary map[string]string, word string) string {\n\treturn \"\"\n}\n```\n\nYour test should now fail with a *clear error message*\n\n`dictionary_test.go:12: got '' want 'this is just a test' given, 'test'`.\n\n## Write enough code to make it pass\n\n```go\nfunc Search(dictionary map[string]string, word string) string {\n\treturn dictionary[word]\n}\n```\n\nGetting a value out of a Map is the same as getting a value out of Array `map[key]`.\n\n## Refactor\n\n```go\nfunc TestSearch(t *testing.T) {\n\tdictionary := map[string]string{\"test\": \"this is just a test\"}\n\n\tgot := Search(dictionary, \"test\")\n\twant := \"this is just a test\"\n\n\tassertStrings(t, got, want)\n}\n\nfunc assertStrings(t testing.TB, got, want string) {\n\tt.Helper()\n\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n```\n\nI decided to create an `assertStrings` helper to make the implementation more general.\n\n### Using a custom type\n\nWe can improve our dictionary's usage by creating a new type around map and making `Search` a method.\n\nIn `dictionary_test.go`:\n\n```go\nfunc TestSearch(t *testing.T) {\n\tdictionary := Dictionary{\"test\": \"this is just a test\"}\n\n\tgot := dictionary.Search(\"test\")\n\twant := \"this is just a test\"\n\n\tassertStrings(t, got, want)\n}\n```\n\nWe started using the `Dictionary` type, which we have not defined yet. Then called `Search` on the `Dictionary` instance.\n\nWe did not need to change `assertStrings`.\n\nIn `dictionary.go`:\n\n```go\ntype Dictionary map[string]string\n\nfunc (d Dictionary) Search(word string) string {\n\treturn d[word]\n}\n```\n\nHere we created a `Dictionary` type which acts as a thin wrapper around `map`. With the custom type defined, we can create the `Search` method.\n\n## Write the test first\n\nThe basic search was very easy to implement, but what will happen if we supply a word that's not in our dictionary?\n\nWe actually get nothing back. This is good because the program can continue to run, but there is a better approach. The function can report that the word is not in the dictionary. This way, the user isn't left wondering if the word doesn't exist or if there is just no definition (this might not seem very useful for a dictionary. However, it's a scenario that could be key in other usecases).\n\n```go\nfunc TestSearch(t *testing.T) {\n\tdictionary := Dictionary{\"test\": \"this is just a test\"}\n\n\tt.Run(\"known word\", func(t *testing.T) {\n\t\tgot, _ := dictionary.Search(\"test\")\n\t\twant := \"this is just a test\"\n\n\t\tassertStrings(t, got, want)\n\t})\n\n\tt.Run(\"unknown word\", func(t *testing.T) {\n\t\t_, err := dictionary.Search(\"unknown\")\n\t\twant := \"could not find the word you were looking for\"\n\n\t\tif err == nil {\n\t\t\tt.Fatal(\"expected to get an error.\")\n\t\t}\n\n\t\tassertStrings(t, err.Error(), want)\n\t})\n}\n```\n\nThe way to handle this scenario in Go is to return a second argument which is an `Error` type.\n\nNotice that as we've seen in the [pointers and error section](./pointers-and-errors.md) here in order to assert the error message\nwe first check that the error is not `nil` and then use `.Error()` method to get the string which we can then pass to the assertion.\n\n## Try and run the test\n\nThis does not compile\n\n```\n./dictionary_test.go:18:10: assignment mismatch: 2 variables but 1 values\n```\n\n## Write the minimal amount of code for the test to run and check the output\n\n```go\nfunc (d Dictionary) Search(word string) (string, error) {\n\treturn d[word], nil\n}\n```\n\nYour test should now fail with a much clearer error message.\n\n`dictionary_test.go:22: expected to get an error.`\n\n## Write enough code to make it pass\n\n```go\nfunc (d Dictionary) Search(word string) (string, error) {\n\tdefinition, ok := d[word]\n\tif !ok {\n\t\treturn \"\", errors.New(\"could not find the word you were looking for\")\n\t}\n\n\treturn definition, nil\n}\n```\n\nIn order to make this pass, we are using an interesting property of the map lookup. It can return 2 values. The second value is a boolean which indicates if the key was found successfully.\n\nThis property allows us to differentiate between a word that doesn't exist and a word that just doesn't have a definition.\n\n## Refactor\n\n```go\nvar ErrNotFound = errors.New(\"could not find the word you were looking for\")\n\nfunc (d Dictionary) Search(word string) (string, error) {\n\tdefinition, ok := d[word]\n\tif !ok {\n\t\treturn \"\", ErrNotFound\n\t}\n\n\treturn definition, nil\n}\n```\n\nWe can get rid of the magic error in our `Search` function by extracting it into a variable. This will also allow us to have a better test.\n\n```go\nt.Run(\"unknown word\", func(t *testing.T) {\n\t_, got := dictionary.Search(\"unknown\")\n\tif got == nil {\n\t\tt.Fatal(\"expected to get an error.\")\n\t}\n\tassertError(t, got, ErrNotFound)\n})\n```\n```go\nfunc assertError(t testing.TB, got, want error) {\n\tt.Helper()\n\n\tif got != want {\n\t\tt.Errorf(\"got error %q want %q\", got, want)\n\t}\n}\n```\n\nBy creating a new helper we were able to simplify our test, and start using our `ErrNotFound` variable so our test doesn't fail if we change the error text in the future.\n\n## Write the test first\n\nWe have a great way to search the dictionary. However, we have no way to add new words to our dictionary.\n\n```go\nfunc TestAdd(t *testing.T) {\n\tdictionary := Dictionary{}\n\tdictionary.Add(\"test\", \"this is just a test\")\n\n\twant := \"this is just a test\"\n\tgot, err := dictionary.Search(\"test\")\n\tif err != nil {\n\t\tt.Fatal(\"should find added word:\", err)\n\t}\n\n\tassertStrings(t, got, want)\n}\n```\n\nIn this test, we are utilizing our `Search` function to make the validation of the dictionary a little easier.\n\n## Write the minimal amount of code for the test to run and check output\n\nIn `dictionary.go`\n\n```go\nfunc (d Dictionary) Add(word, definition string) {\n}\n```\n\nYour test should now fail\n\n```\ndictionary_test.go:31: should find added word: could not find the word you were looking for\n```\n\n## Write enough code to make it pass\n\n```go\nfunc (d Dictionary) Add(word, definition string) {\n\td[word] = definition\n}\n```\n\nAdding to a map is also similar to an array. You just need to specify a key and set it equal to a value.\n\n### Pointers, copies, et al\n\nAn interesting property of maps is that you can modify them without passing as an address to it (e.g `&myMap`)\n\nThis may make them _feel_ like a \"reference type\", [but as Dave Cheney describes](https://dave.cheney.net/2017/04/30/if-a-map-isnt-a-reference-variable-what-is-it) they are not.\n\n> A map value is a pointer to a runtime.hmap structure.\n\nSo when you pass a map to a function/method, you are indeed copying it, but just the pointer part, not the underlying data structure that contains the data.\n\nA gotcha with maps is that they can be a `nil` value. A `nil` map behaves like an empty map when reading, but attempts to write to a `nil` map will cause a runtime panic. You can read more about maps [here](https://blog.golang.org/go-maps-in-action).\n\nTherefore, you should never initialize a nil map variable:\n\n```go\nvar m map[string]string\n```\n\nInstead, you can initialize an empty map or use the `make` keyword to create a map for you:\n\n```go\nvar dictionary = map[string]string{}\n\n// OR\n\nvar dictionary = make(map[string]string)\n```\n\nBoth approaches create an empty `hash map` and point `dictionary` at it. Which ensures that you will never get a runtime panic.\n\n## Refactor\n\nThere isn't much to refactor in our implementation but the test could use a little simplification.\n\n```go\nfunc TestAdd(t *testing.T) {\n\tdictionary := Dictionary{}\n\tword := \"test\"\n\tdefinition := \"this is just a test\"\n\n\tdictionary.Add(word, definition)\n\n\tassertDefinition(t, dictionary, word, definition)\n}\n\nfunc assertDefinition(t testing.TB, dictionary Dictionary, word, definition string) {\n\tt.Helper()\n\n\tgot, err := dictionary.Search(word)\n\tif err != nil {\n\t\tt.Fatal(\"should find added word:\", err)\n\t}\n\tassertStrings(t, got, definition)\n}\n```\n\nWe made variables for word and definition, and moved the definition assertion into its own helper function.\n\nOur `Add` is looking good. Except, we didn't consider what happens when the value we are trying to add already exists!\n\nMap will not throw an error if the value already exists. Instead, they will go ahead and overwrite the value with the newly provided value. This can be convenient in practice, but makes our function name less than accurate. `Add` should not modify existing values. It should only add new words to our dictionary.\n\n## Write the test first\n\n```go\nfunc TestAdd(t *testing.T) {\n\tt.Run(\"new word\", func(t *testing.T) {\n\t\tdictionary := Dictionary{}\n\t\tword := \"test\"\n\t\tdefinition := \"this is just a test\"\n\n\t\terr := dictionary.Add(word, definition)\n\n\t\tassertError(t, err, nil)\n\t\tassertDefinition(t, dictionary, word, definition)\n\t})\n\n\tt.Run(\"existing word\", func(t *testing.T) {\n\t\tword := \"test\"\n\t\tdefinition := \"this is just a test\"\n\t\tdictionary := Dictionary{word: definition}\n\t\terr := dictionary.Add(word, \"new test\")\n\n\t\tassertError(t, err, ErrWordExists)\n\t\tassertDefinition(t, dictionary, word, definition)\n\t})\n}\n```\n\nFor this test, we modified `Add` to return an error, which we are validating against a new error variable, `ErrWordExists`. We also modified the previous test to check for a `nil` error.\n\n## Try to run test\n\nThe compiler will fail because we are not returning a value for `Add`.\n\n```\n./dictionary_test.go:30:13: dictionary.Add(word, definition) used as value\n./dictionary_test.go:41:13: dictionary.Add(word, \"new test\") used as value\n```\n\n## Write the minimal amount of code for the test to run and check the output\n\nIn `dictionary.go`\n\n```go\nvar (\n\tErrNotFound   = errors.New(\"could not find the word you were looking for\")\n\tErrWordExists = errors.New(\"cannot add word because it already exists\")\n)\n\nfunc (d Dictionary) Add(word, definition string) error {\n\td[word] = definition\n\treturn nil\n}\n```\n\nNow we get two more errors. We are still modifying the value, and returning a `nil` error.\n\n```\ndictionary_test.go:43: got error '%!q(<nil>)' want 'cannot add word because it already exists'\ndictionary_test.go:44: got 'new test' want 'this is just a test'\n```\n\n## Write enough code to make it pass\n\n```go\nfunc (d Dictionary) Add(word, definition string) error {\n\t_, err := d.Search(word)\n\n\tswitch err {\n\tcase ErrNotFound:\n\t\td[word] = definition\n\tcase nil:\n\t\treturn ErrWordExists\n\tdefault:\n\t\treturn err\n\t}\n\n\treturn nil\n}\n```\n\nHere we are using a `switch` statement to match on the error. Having a `switch` like this provides an extra safety net, in case `Search` returns an error other than `ErrNotFound`.\n\n## Refactor\n\nWe don't have too much to refactor, but as our error usage grows we can make a few modifications.\n\n```go\nconst (\n\tErrNotFound   = DictionaryErr(\"could not find the word you were looking for\")\n\tErrWordExists = DictionaryErr(\"cannot add word because it already exists\")\n)\n\ntype DictionaryErr string\n\nfunc (e DictionaryErr) Error() string {\n\treturn string(e)\n}\n```\n\nWe made the errors constant; this required us to create our own `DictionaryErr` type which implements the `error` interface. You can read more about the details in [this excellent article by Dave Cheney](https://dave.cheney.net/2016/04/07/constant-errors). Simply put, it makes the errors more reusable and immutable.\n\nNext, let's create a function to `Update` the definition of a word.\n\n## Write the test first\n\n```go\nfunc TestUpdate(t *testing.T) {\n\tword := \"test\"\n\tdefinition := \"this is just a test\"\n\tdictionary := Dictionary{word: definition}\n\tnewDefinition := \"new definition\"\n\n\tdictionary.Update(word, newDefinition)\n\n\tassertDefinition(t, dictionary, word, newDefinition)\n}\n```\n\n`Update` is very closely related to `Add` and will be our next implementation.\n\n## Try and run the test\n\n```\n./dictionary_test.go:53:2: dictionary.Update undefined (type Dictionary has no field or method Update)\n```\n\n## Write minimal amount of code for the test to run and check the failing test output\n\nWe already know how to deal with an error like this. We need to define our function.\n\n```go\nfunc (d Dictionary) Update(word, definition string) {}\n```\n\nWith that in place, we are able to see that we need to change the definition of the word.\n\n```\ndictionary_test.go:55: got 'this is just a test' want 'new definition'\n```\n\n## Write enough code to make it pass\n\nWe already saw how to do this when we fixed the issue with `Add`. So let's implement something really similar to `Add`.\n\n```go\nfunc (d Dictionary) Update(word, definition string) {\n\td[word] = definition\n}\n```\n\nThere is no refactoring we need to do on this since it was a simple change. However, we now have the same issue as with `Add`. If we pass in a new word, `Update` will add it to the dictionary.\n\n## Write the test first\n\n```go\nt.Run(\"existing word\", func(t *testing.T) {\n\tword := \"test\"\n\tdefinition := \"this is just a test\"\n\tdictionary := Dictionary{word: definition}\n\tnewDefinition := \"new definition\"\n\n\terr := dictionary.Update(word, newDefinition)\n\n\tassertError(t, err, nil)\n\tassertDefinition(t, dictionary, word, newDefinition)\n})\n\nt.Run(\"new word\", func(t *testing.T) {\n\tword := \"test\"\n\tdefinition := \"this is just a test\"\n\tdictionary := Dictionary{}\n\n\terr := dictionary.Update(word, definition)\n\n\tassertError(t, err, ErrWordDoesNotExist)\n})\n```\n\nWe added yet another error type for when the word does not exist. We also modified `Update` to return an `error` value.\n\n## Try and run the test\n\n```\n./dictionary_test.go:53:16: dictionary.Update(word, newDefinition) used as value\n./dictionary_test.go:64:16: dictionary.Update(word, definition) used as value\n./dictionary_test.go:66:23: undefined: ErrWordDoesNotExist\n```\n\nWe get 3 errors this time, but we know how to deal with these.\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\n```go\nconst (\n\tErrNotFound         = DictionaryErr(\"could not find the word you were looking for\")\n\tErrWordExists       = DictionaryErr(\"cannot add word because it already exists\")\n\tErrWordDoesNotExist = DictionaryErr(\"cannot perform operation on word because it does not exist\")\n)\n\nfunc (d Dictionary) Update(word, definition string) error {\n\td[word] = definition\n\treturn nil\n}\n```\n\nWe added our own error type and are returning a `nil` error.\n\nWith these changes, we now get a very clear error:\n\n```\ndictionary_test.go:66: got error '%!q(<nil>)' want 'cannot update word because it does not exist'\n```\n\n## Write enough code to make it pass\n\n```go\nfunc (d Dictionary) Update(word, definition string) error {\n\t_, err := d.Search(word)\n\n\tswitch err {\n\tcase ErrNotFound:\n\t\treturn ErrWordDoesNotExist\n\tcase nil:\n\t\td[word] = definition\n\tdefault:\n\t\treturn err\n\t}\n\n\treturn nil\n}\n```\n\nThis function looks almost identical to `Add` except we switched when we update the `dictionary` and when we return an error.\n\n### Note on declaring a new error for Update\n\nWe could reuse `ErrNotFound` and not add a new error. However, it is often better to have a precise error for when an update fails.\n\nHaving specific errors gives you more information about what went wrong. Here is an example in a web app:\n\n> You can redirect the user when `ErrNotFound` is encountered, but display an error message when `ErrWordDoesNotExist` is encountered.\n\nNext, let's create a function to `Delete` a word in the dictionary.\n\n## Write the test first\n\n```go\nfunc TestDelete(t *testing.T) {\n\tword := \"test\"\n\tdictionary := Dictionary{word: \"test definition\"}\n\n\tdictionary.Delete(word)\n\n\t_, err := dictionary.Search(word)\n\tassertError(t, err, ErrNotFound)\n}\n```\n\nOur test creates a `Dictionary` with a word and then checks if the word has been removed.\n\n## Try to run the test\n\nBy running `go test` we get:\n\n```\n./dictionary_test.go:74:6: dictionary.Delete undefined (type Dictionary has no field or method Delete)\n```\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\n```go\nfunc (d Dictionary) Delete(word string) {\n\n}\n```\n\nAfter we add this, the test tells us we are not deleting the word.\n\n```\ndictionary_test.go:78: got error '%!q(<nil>)' want 'could not find the word you were looking for'\n```\n\n## Write enough code to make it pass\n\n```go\nfunc (d Dictionary) Delete(word string) {\n\tdelete(d, word)\n}\n```\n\nGo has a built-in function `delete` that works on maps. It takes two arguments and returns nothing. The first argument is the map and the second is the key to be removed.\n\n## Refactor\nThere isn't much to refactor, but we can implement the same logic from `Update` to handle cases where word doesn't exist.\n\n```go\nfunc TestDelete(t *testing.T) {\n\tt.Run(\"existing word\", func(t *testing.T) {\n\t\tword := \"test\"\n\t\tdictionary := Dictionary{word: \"test definition\"}\n\n\t\terr := dictionary.Delete(word)\n\n\t\tassertError(t, err, nil)\n\n\t\t_, err = dictionary.Search(word)\n\n\t\tassertError(t, err, ErrNotFound)\n\t})\n\n\tt.Run(\"non-existing word\", func(t *testing.T) {\n\t\tword := \"test\"\n\t\tdictionary := Dictionary{}\n\n\t\terr := dictionary.Delete(word)\n\n\t\tassertError(t, err, ErrWordDoesNotExist)\n\t})\n}\n```\n\n## Try to run test\n\nThe compiler will fail because we are not returning a value for `Delete`.\n\n```\n./dictionary_test.go:77:10: dictionary.Delete(word) (no value) used as value\n./dictionary_test.go:90:10: dictionary.Delete(word) (no value) used as value\n```\n\n## Write enough code to make it pass\n\n```go\nfunc (d Dictionary) Delete(word string) error {\n\t_, err := d.Search(word)\n\n\tswitch err {\n\tcase ErrNotFound:\n\t\treturn ErrWordDoesNotExist\n\tcase nil:\n\t\tdelete(d, word)\n\tdefault:\n\t\treturn err\n\t}\n\n\treturn nil\n}\n```\n\nWe are again using a switch statement to match on the error when we attempt to delete a word that doesn't exist. \n\n## Wrapping up\n\nIn this section, we covered a lot. We made a full CRUD (Create, Read, Update and Delete) API for our dictionary. Throughout the process we learned how to:\n\n* Create maps\n* Search for items in maps\n* Add new items to maps\n* Update items in maps\n* Delete items from a map\n* Learned more about errors\n  * How to create errors that are constants\n  * Writing error wrappers\n"
  },
  {
    "path": "math/v1/clockface/clockface.go",
    "content": "package clockface\n\nimport \"time\"\n\n// A Point represents a two dimensional Cartesian coordinate.\ntype Point struct {\n\tX float64\n\tY float64\n}\n\n// SecondHand is the unit vector of the second hand of an analogue clock at time `t`.\n// represented as a Point.\nfunc SecondHand(t time.Time) Point {\n\treturn Point{150, 60}\n}\n"
  },
  {
    "path": "math/v1/clockface/clockface_test.go",
    "content": "package clockface_test\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/quii/learn-go-with-tests/math/v1/clockface\"\n)\n\nfunc TestSecondHandAtMidnight(t *testing.T) {\n\ttm := time.Date(1337, time.January, 1, 0, 0, 0, 0, time.UTC)\n\n\twant := clockface.Point{X: 150, Y: 150 - 90}\n\tgot := clockface.SecondHand(tm)\n\n\tif got != want {\n\t\tt.Errorf(\"Got %v, wanted %v\", got, want)\n\t}\n}\n"
  },
  {
    "path": "math/v10/clockface/clockface/main.go",
    "content": "package main\n\nimport (\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/quii/learn-go-with-tests/math/v10/clockface\"\n)\n\nfunc main() {\n\tt := time.Now()\n\tclockface.SVGWriter(os.Stdout, t)\n}\n"
  },
  {
    "path": "math/v10/clockface/clockface.go",
    "content": "package clockface\n\nimport (\n\t\"math\"\n\t\"time\"\n)\n\n// A Point represents a two dimensional Cartesian coordinate.\ntype Point struct {\n\tX float64\n\tY float64\n}\n\nfunc secondsInRadians(t time.Time) float64 {\n\treturn (math.Pi / (30 / float64(t.Second())))\n}\n\nfunc secondHandPoint(t time.Time) Point {\n\treturn angleToPoint(secondsInRadians(t))\n}\n\nfunc minutesInRadians(t time.Time) float64 {\n\treturn (secondsInRadians(t) / 60) +\n\t\t(math.Pi / (30 / float64(t.Minute())))\n}\n\nfunc minuteHandPoint(t time.Time) Point {\n\treturn angleToPoint(minutesInRadians(t))\n}\n\nfunc hoursInRadians(t time.Time) float64 {\n\treturn (minutesInRadians(t) / 12) +\n\t\t(math.Pi / (6 / float64(t.Hour()%12)))\n}\n\nfunc angleToPoint(angle float64) Point {\n\tx := math.Sin(angle)\n\ty := math.Cos(angle)\n\n\treturn Point{x, y}\n}\n"
  },
  {
    "path": "math/v10/clockface/clockface_acceptance_test.go",
    "content": "package clockface_test\n\nimport (\n\t\"bytes\"\n\t\"encoding/xml\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/quii/learn-go-with-tests/math/v10/clockface\"\n)\n\ntype SVG struct {\n\tXMLName xml.Name `xml:\"svg\"`\n\tText    string   `xml:\",chardata\"`\n\tXmlns   string   `xml:\"xmlns,attr\"`\n\tWidth   string   `xml:\"width,attr\"`\n\tHeight  string   `xml:\"height,attr\"`\n\tViewBox string   `xml:\"viewBox,attr\"`\n\tVersion string   `xml:\"version,attr\"`\n\tCircle  Circle   `xml:\"circle\"`\n\tLine    []Line   `xml:\"line\"`\n}\n\ntype Line struct {\n\tX1 float64 `xml:\"x1,attr\"`\n\tY1 float64 `xml:\"y1,attr\"`\n\tX2 float64 `xml:\"x2,attr\"`\n\tY2 float64 `xml:\"y2,attr\"`\n}\n\ntype Circle struct {\n\tCx float64 `xml:\"cx,attr\"`\n\tCy float64 `xml:\"cy,attr\"`\n\tR  float64 `xml:\"r,attr\"`\n}\n\nfunc TestSVGWriterSecondHand(t *testing.T) {\n\tcases := []struct {\n\t\ttime time.Time\n\t\tline Line\n\t}{\n\t\t{\n\t\t\tsimpleTime(0, 0, 0),\n\t\t\tLine{150, 150, 150, 60},\n\t\t},\n\t\t{\n\t\t\tsimpleTime(0, 0, 30),\n\t\t\tLine{150, 150, 150, 240},\n\t\t},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tb := bytes.Buffer{}\n\t\t\tclockface.SVGWriter(&b, c.time)\n\n\t\t\tsvg := SVG{}\n\t\t\txml.Unmarshal(b.Bytes(), &svg)\n\n\t\t\tif !containsLine(c.line, svg.Line) {\n\t\t\t\tt.Errorf(\"Expected to find the second hand line %+v, in the SVG lines %+v\", c.line, svg.Line)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSVGWriterMinutedHand(t *testing.T) {\n\tcases := []struct {\n\t\ttime time.Time\n\t\tline Line\n\t}{\n\t\t{\n\t\t\tsimpleTime(0, 0, 0),\n\t\t\tLine{150, 150, 150, 70},\n\t\t},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tb := bytes.Buffer{}\n\t\t\tclockface.SVGWriter(&b, c.time)\n\n\t\t\tsvg := SVG{}\n\t\t\txml.Unmarshal(b.Bytes(), &svg)\n\n\t\t\tif !containsLine(c.line, svg.Line) {\n\t\t\t\tt.Errorf(\"Expected to find the minute hand line %+v, in the SVG lines %+v\", c.line, svg.Line)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSVGWriterHourHand(t *testing.T) {\n\tcases := []struct {\n\t\ttime time.Time\n\t\tline Line\n\t}{\n\t\t// {\n\t\t// \tsimpleTime(6, 0, 0),\n\t\t// \tLine{150, 150, 150, 200},\n\t\t// },\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tb := bytes.Buffer{}\n\t\t\tclockface.SVGWriter(&b, c.time)\n\n\t\t\tsvg := SVG{}\n\t\t\txml.Unmarshal(b.Bytes(), &svg)\n\n\t\t\tif !containsLine(c.line, svg.Line) {\n\t\t\t\tt.Errorf(\"Expected to find the hour hand line %+v, in the SVG lines %+v\", c.line, svg.Line)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc containsLine(l Line, ls []Line) bool {\n\tfor _, line := range ls {\n\t\tif line == l {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc simpleTime(hours, minutes, seconds int) time.Time {\n\treturn time.Date(312, time.October, 28, hours, minutes, seconds, 0, time.UTC)\n}\n\nfunc testName(t time.Time) string {\n\treturn t.Format(\"15:04:05\")\n}\n"
  },
  {
    "path": "math/v10/clockface/clockface_test.go",
    "content": "package clockface\n\nimport (\n\t\"math\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestSecondsInRadians(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tangle float64\n\t}{\n\t\t{simpleTime(0, 0, 30), math.Pi},\n\t\t{simpleTime(0, 0, 0), 0},\n\t\t{simpleTime(0, 0, 45), (math.Pi / 2) * 3},\n\t\t{simpleTime(0, 0, 7), (math.Pi / 30) * 7},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := secondsInRadians(c.time)\n\t\t\tif !roughlyEqualFloat64(got, c.angle) {\n\t\t\t\tt.Fatalf(\"Wanted %v radians, but got %v\", c.angle, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSecondHandPoint(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tpoint Point\n\t}{\n\t\t{simpleTime(0, 0, 30), Point{0, -1}},\n\t\t{simpleTime(0, 0, 45), Point{-1, 0}},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := secondHandPoint(c.time)\n\t\t\tif !roughlyEqualPoint(got, c.point) {\n\t\t\t\tt.Fatalf(\"Wanted %v Point, but got %v\", c.point, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestMinutesInRadians(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tangle float64\n\t}{\n\t\t{simpleTime(0, 30, 0), math.Pi},\n\t\t{simpleTime(0, 0, 7), 7 * (math.Pi / (30 * 60))},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := minutesInRadians(c.time)\n\t\t\tif !roughlyEqualFloat64(got, c.angle) {\n\t\t\t\tt.Fatalf(\"Wanted %v radians, but got %v\", c.angle, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestMinuteHandPoint(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tpoint Point\n\t}{\n\t\t{simpleTime(0, 30, 0), Point{0, -1}},\n\t\t{simpleTime(0, 45, 0), Point{-1, 0}},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := minuteHandPoint(c.time)\n\t\t\tif !roughlyEqualPoint(got, c.point) {\n\t\t\t\tt.Fatalf(\"Wanted %v Point, but got %v\", c.point, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestHoursInRadians(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tangle float64\n\t}{\n\t\t{simpleTime(6, 0, 0), math.Pi},\n\t\t{simpleTime(0, 0, 0), 0},\n\t\t{simpleTime(21, 0, 0), math.Pi * 1.5},\n\t\t{simpleTime(0, 1, 30), math.Pi / ((6 * 60 * 60) / 90)},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := hoursInRadians(c.time)\n\t\t\tif !roughlyEqualFloat64(got, c.angle) {\n\t\t\t\tt.Fatalf(\"Wanted %v radians, but got %v\", c.angle, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc roughlyEqualFloat64(a, b float64) bool {\n\tconst equalityThreshold = 1e-7\n\treturn math.Abs(a-b) < equalityThreshold\n}\n\nfunc roughlyEqualPoint(a, b Point) bool {\n\treturn roughlyEqualFloat64(a.X, b.X) &&\n\t\troughlyEqualFloat64(a.Y, b.Y)\n}\n\nfunc simpleTime(hours, minutes, seconds int) time.Time {\n\treturn time.Date(312, time.October, 28, hours, minutes, seconds, 0, time.UTC)\n}\n\nfunc testName(t time.Time) string {\n\treturn t.Format(\"15:04:05\")\n}\n"
  },
  {
    "path": "math/v10/clockface/svgWriter.go",
    "content": "package clockface\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"time\"\n)\n\nconst (\n\tsecondHandLength = 90\n\tminuteHandLength = 80\n\tclockCentreX     = 150\n\tclockCentreY     = 150\n)\n\n// SVGWriter writes an SVG representation of an analogue clock, showing the time t, to the writer w.\nfunc SVGWriter(w io.Writer, t time.Time) {\n\tio.WriteString(w, svgStart)\n\tio.WriteString(w, bezel)\n\tsecondHand(w, t)\n\tminuteHand(w, t)\n\tio.WriteString(w, svgEnd)\n}\n\nfunc secondHand(w io.Writer, t time.Time) {\n\tp := makeHand(secondHandPoint(t), secondHandLength)\n\tfmt.Fprintf(w, `<line x1=\"150\" y1=\"150\" x2=\"%.3f\" y2=\"%.3f\" style=\"fill:none;stroke:#f00;stroke-width:3px;\"/>`, p.X, p.Y)\n}\n\nfunc minuteHand(w io.Writer, t time.Time) {\n\tp := makeHand(minuteHandPoint(t), minuteHandLength)\n\tfmt.Fprintf(w, `<line x1=\"150\" y1=\"150\" x2=\"%.3f\" y2=\"%.3f\" style=\"fill:none;stroke:#000;stroke-width:3px;\"/>`, p.X, p.Y)\n}\n\nfunc makeHand(p Point, length float64) Point {\n\tp = Point{p.X * length, p.Y * length}\n\tp = Point{p.X, -p.Y}\n\treturn Point{p.X + clockCentreX, p.Y + clockCentreY}\n}\n\nconst svgStart = `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n<svg xmlns=\"http://www.w3.org/2000/svg\"\n     width=\"100%\"\n     height=\"100%\"\n     viewBox=\"0 0 300 300\"\n     version=\"2.0\">`\n\nconst bezel = `<circle cx=\"150\" cy=\"150\" r=\"100\" style=\"fill:#fff;stroke:#000;stroke-width:5px;\"/>`\n\nconst svgEnd = `</svg>`\n"
  },
  {
    "path": "math/v11/clockface/clockface/main.go",
    "content": "package main\n\nimport (\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/quii/learn-go-with-tests/math/v9/clockface\"\n)\n\nfunc main() {\n\tt := time.Now()\n\tclockface.SVGWriter(os.Stdout, t)\n}\n"
  },
  {
    "path": "math/v11/clockface/clockface.go",
    "content": "package clockface\n\nimport (\n\t\"math\"\n\t\"time\"\n)\n\n// A Point represents a two dimensional Cartesian coordinate.\ntype Point struct {\n\tX float64\n\tY float64\n}\n\nfunc secondsInRadians(t time.Time) float64 {\n\treturn (math.Pi / (30 / float64(t.Second())))\n}\n\nfunc secondHandPoint(t time.Time) Point {\n\treturn angleToPoint(secondsInRadians(t))\n}\n\nfunc minutesInRadians(t time.Time) float64 {\n\treturn (secondsInRadians(t) / 60) +\n\t\t(math.Pi / (30 / float64(t.Minute())))\n}\n\nfunc minuteHandPoint(t time.Time) Point {\n\treturn angleToPoint(minutesInRadians(t))\n}\n\nfunc hoursInRadians(t time.Time) float64 {\n\treturn (minutesInRadians(t) / 12) +\n\t\t(math.Pi / (6 / float64(t.Hour()%12)))\n}\n\nfunc hourHandPoint(t time.Time) Point {\n\treturn angleToPoint(hoursInRadians(t))\n}\n\nfunc angleToPoint(angle float64) Point {\n\tx := math.Sin(angle)\n\ty := math.Cos(angle)\n\n\treturn Point{x, y}\n}\n"
  },
  {
    "path": "math/v11/clockface/clockface_acceptance_test.go",
    "content": "package clockface_test\n\nimport (\n\t\"bytes\"\n\t\"encoding/xml\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/quii/learn-go-with-tests/math/v11/clockface\"\n)\n\ntype SVG struct {\n\tXMLName xml.Name `xml:\"svg\"`\n\tText    string   `xml:\",chardata\"`\n\tXmlns   string   `xml:\"xmlns,attr\"`\n\tWidth   string   `xml:\"width,attr\"`\n\tHeight  string   `xml:\"height,attr\"`\n\tViewBox string   `xml:\"viewBox,attr\"`\n\tVersion string   `xml:\"version,attr\"`\n\tCircle  Circle   `xml:\"circle\"`\n\tLine    []Line   `xml:\"line\"`\n}\n\ntype Line struct {\n\tX1 float64 `xml:\"x1,attr\"`\n\tY1 float64 `xml:\"y1,attr\"`\n\tX2 float64 `xml:\"x2,attr\"`\n\tY2 float64 `xml:\"y2,attr\"`\n}\n\ntype Circle struct {\n\tCx float64 `xml:\"cx,attr\"`\n\tCy float64 `xml:\"cy,attr\"`\n\tR  float64 `xml:\"r,attr\"`\n}\n\nfunc TestSVGWriterSecondHand(t *testing.T) {\n\tcases := []struct {\n\t\ttime time.Time\n\t\tline Line\n\t}{\n\t\t{\n\t\t\tsimpleTime(0, 0, 0),\n\t\t\tLine{150, 150, 150, 60},\n\t\t},\n\t\t{\n\t\t\tsimpleTime(0, 0, 30),\n\t\t\tLine{150, 150, 150, 240},\n\t\t},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tb := bytes.Buffer{}\n\t\t\tclockface.SVGWriter(&b, c.time)\n\n\t\t\tsvg := SVG{}\n\t\t\txml.Unmarshal(b.Bytes(), &svg)\n\n\t\t\tif !containsLine(c.line, svg.Line) {\n\t\t\t\tt.Errorf(\"Expected to find the second hand line %+v, in the SVG lines %+v\", c.line, svg.Line)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSVGWriterMinutedHand(t *testing.T) {\n\tcases := []struct {\n\t\ttime time.Time\n\t\tline Line\n\t}{\n\t\t{\n\t\t\tsimpleTime(0, 0, 0),\n\t\t\tLine{150, 150, 150, 70},\n\t\t},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tb := bytes.Buffer{}\n\t\t\tclockface.SVGWriter(&b, c.time)\n\n\t\t\tsvg := SVG{}\n\t\t\txml.Unmarshal(b.Bytes(), &svg)\n\n\t\t\tif !containsLine(c.line, svg.Line) {\n\t\t\t\tt.Errorf(\"Expected to find the minute hand line %+v, in the SVG lines %+v\", c.line, svg.Line)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSVGWriterHourHand(t *testing.T) {\n\tcases := []struct {\n\t\ttime time.Time\n\t\tline Line\n\t}{\n\t\t// {\n\t\t// \tsimpleTime(6, 0, 0),\n\t\t// \tLine{150, 150, 150, 200},\n\t\t// },\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tb := bytes.Buffer{}\n\t\t\tclockface.SVGWriter(&b, c.time)\n\n\t\t\tsvg := SVG{}\n\t\t\txml.Unmarshal(b.Bytes(), &svg)\n\n\t\t\tif !containsLine(c.line, svg.Line) {\n\t\t\t\tt.Errorf(\"Expected to find the hour hand line %+v, in the SVG lines %+v\", c.line, svg.Line)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc containsLine(l Line, ls []Line) bool {\n\tfor _, line := range ls {\n\t\tif line == l {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc simpleTime(hours, minutes, seconds int) time.Time {\n\treturn time.Date(312, time.October, 28, hours, minutes, seconds, 0, time.UTC)\n}\n\nfunc testName(t time.Time) string {\n\treturn t.Format(\"15:04:05\")\n}\n"
  },
  {
    "path": "math/v11/clockface/clockface_test.go",
    "content": "package clockface\n\nimport (\n\t\"math\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestSecondsInRadians(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tangle float64\n\t}{\n\t\t{simpleTime(0, 0, 30), math.Pi},\n\t\t{simpleTime(0, 0, 0), 0},\n\t\t{simpleTime(0, 0, 45), (math.Pi / 2) * 3},\n\t\t{simpleTime(0, 0, 7), (math.Pi / 30) * 7},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := secondsInRadians(c.time)\n\t\t\tif !roughlyEqualFloat64(got, c.angle) {\n\t\t\t\tt.Fatalf(\"Wanted %v radians, but got %v\", c.angle, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSecondHandPoint(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tpoint Point\n\t}{\n\t\t{simpleTime(0, 0, 30), Point{0, -1}},\n\t\t{simpleTime(0, 0, 45), Point{-1, 0}},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := secondHandPoint(c.time)\n\t\t\tif !roughlyEqualPoint(got, c.point) {\n\t\t\t\tt.Fatalf(\"Wanted %v Point, but got %v\", c.point, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestMinutesInRadians(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tangle float64\n\t}{\n\t\t{simpleTime(0, 30, 0), math.Pi},\n\t\t{simpleTime(0, 0, 7), 7 * (math.Pi / (30 * 60))},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := minutesInRadians(c.time)\n\t\t\tif !roughlyEqualFloat64(got, c.angle) {\n\t\t\t\tt.Fatalf(\"Wanted %v radians, but got %v\", c.angle, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestMinuteHandPoint(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tpoint Point\n\t}{\n\t\t{simpleTime(0, 30, 0), Point{0, -1}},\n\t\t{simpleTime(0, 45, 0), Point{-1, 0}},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := minuteHandPoint(c.time)\n\t\t\tif !roughlyEqualPoint(got, c.point) {\n\t\t\t\tt.Fatalf(\"Wanted %v Point, but got %v\", c.point, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestHoursInRadians(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tangle float64\n\t}{\n\t\t{simpleTime(6, 0, 0), math.Pi},\n\t\t{simpleTime(0, 0, 0), 0},\n\t\t{simpleTime(21, 0, 0), math.Pi * 1.5},\n\t\t{simpleTime(0, 1, 30), math.Pi / ((6 * 60 * 60) / 90)},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := hoursInRadians(c.time)\n\t\t\tif !roughlyEqualFloat64(got, c.angle) {\n\t\t\t\tt.Fatalf(\"Wanted %v radians, but got %v\", c.angle, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestHourHandPoint(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tpoint Point\n\t}{\n\t\t{simpleTime(6, 0, 0), Point{0, -1}},\n\t\t{simpleTime(21, 0, 0), Point{-1, 0}},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := hourHandPoint(c.time)\n\t\t\tif !roughlyEqualPoint(got, c.point) {\n\t\t\t\tt.Fatalf(\"Wanted %v Point, but got %v\", c.point, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc roughlyEqualFloat64(a, b float64) bool {\n\tconst equalityThreshold = 1e-7\n\treturn math.Abs(a-b) < equalityThreshold\n}\n\nfunc roughlyEqualPoint(a, b Point) bool {\n\treturn roughlyEqualFloat64(a.X, b.X) &&\n\t\troughlyEqualFloat64(a.Y, b.Y)\n}\n\nfunc simpleTime(hours, minutes, seconds int) time.Time {\n\treturn time.Date(312, time.October, 28, hours, minutes, seconds, 0, time.UTC)\n}\n\nfunc testName(t time.Time) string {\n\treturn t.Format(\"15:04:05\")\n}\n"
  },
  {
    "path": "math/v11/clockface/svgWriter.go",
    "content": "package clockface\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"time\"\n)\n\nconst (\n\tsecondHandLength = 90\n\tminuteHandLength = 80\n\tclockCentreX     = 150\n\tclockCentreY     = 150\n)\n\n// SVGWriter writes an SVG representation of an analogue clock, showing the time t, to the writer w.\nfunc SVGWriter(w io.Writer, t time.Time) {\n\tio.WriteString(w, svgStart)\n\tio.WriteString(w, bezel)\n\tsecondHand(w, t)\n\tminuteHand(w, t)\n\tio.WriteString(w, svgEnd)\n}\n\nfunc secondHand(w io.Writer, t time.Time) {\n\tp := makeHand(secondHandPoint(t), secondHandLength)\n\tfmt.Fprintf(w, `<line x1=\"150\" y1=\"150\" x2=\"%.3f\" y2=\"%.3f\" style=\"fill:none;stroke:#f00;stroke-width:3px;\"/>`, p.X, p.Y)\n}\n\nfunc minuteHand(w io.Writer, t time.Time) {\n\tp := makeHand(minuteHandPoint(t), minuteHandLength)\n\tfmt.Fprintf(w, `<line x1=\"150\" y1=\"150\" x2=\"%.3f\" y2=\"%.3f\" style=\"fill:none;stroke:#000;stroke-width:3px;\"/>`, p.X, p.Y)\n}\n\nfunc makeHand(p Point, length float64) Point {\n\tp = Point{p.X * length, p.Y * length}\n\tp = Point{p.X, -p.Y}\n\treturn Point{p.X + clockCentreX, p.Y + clockCentreY}\n}\n\nconst svgStart = `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n<svg xmlns=\"http://www.w3.org/2000/svg\"\n     width=\"100%\"\n     height=\"100%\"\n     viewBox=\"0 0 300 300\"\n     version=\"2.0\">`\n\nconst bezel = `<circle cx=\"150\" cy=\"150\" r=\"100\" style=\"fill:#fff;stroke:#000;stroke-width:5px;\"/>`\n\nconst svgEnd = `</svg>`\n"
  },
  {
    "path": "math/v12/clockface/clockface/main.go",
    "content": "package main\n\nimport (\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/quii/learn-go-with-tests/math/v12/clockface\"\n)\n\nfunc main() {\n\tt := time.Now()\n\tclockface.SVGWriter(os.Stdout, t)\n}\n"
  },
  {
    "path": "math/v12/clockface/clockface.go",
    "content": "package clockface\n\nimport (\n\t\"math\"\n\t\"time\"\n)\n\nconst (\n\tsecondsInHalfClock = 30\n\tsecondsInClock     = 2 * secondsInHalfClock\n\tminutesInHalfClock = 30\n\tminutesInClock     = 2 * minutesInHalfClock\n\thoursInHalfClock   = 6\n\thoursInClock       = 2 * hoursInHalfClock\n)\n\n// A Point represents a two dimensional Cartesian coordinate.\ntype Point struct {\n\tX float64\n\tY float64\n}\n\nfunc secondsInRadians(t time.Time) float64 {\n\treturn (math.Pi / (secondsInHalfClock / float64(t.Second())))\n}\n\nfunc secondHandPoint(t time.Time) Point {\n\treturn angleToPoint(secondsInRadians(t))\n}\n\nfunc minutesInRadians(t time.Time) float64 {\n\treturn (secondsInRadians(t) / minutesInClock) +\n\t\t(math.Pi / (minutesInHalfClock / float64(t.Minute())))\n}\n\nfunc minuteHandPoint(t time.Time) Point {\n\treturn angleToPoint(minutesInRadians(t))\n}\n\nfunc hoursInRadians(t time.Time) float64 {\n\treturn (minutesInRadians(t) / hoursInClock) +\n\t\t(math.Pi / (hoursInHalfClock / float64(t.Hour()%hoursInClock)))\n}\n\nfunc hourHandPoint(t time.Time) Point {\n\treturn angleToPoint(hoursInRadians(t))\n}\n\nfunc angleToPoint(angle float64) Point {\n\tx := math.Sin(angle)\n\ty := math.Cos(angle)\n\n\treturn Point{x, y}\n}\n"
  },
  {
    "path": "math/v12/clockface/clockface_acceptance_test.go",
    "content": "package clockface_test\n\nimport (\n\t\"bytes\"\n\t\"encoding/xml\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/quii/learn-go-with-tests/math/v12/clockface\"\n)\n\ntype SVG struct {\n\tXMLName xml.Name `xml:\"svg\"`\n\tText    string   `xml:\",chardata\"`\n\tXmlns   string   `xml:\"xmlns,attr\"`\n\tWidth   string   `xml:\"width,attr\"`\n\tHeight  string   `xml:\"height,attr\"`\n\tViewBox string   `xml:\"viewBox,attr\"`\n\tVersion string   `xml:\"version,attr\"`\n\tCircle  Circle   `xml:\"circle\"`\n\tLine    []Line   `xml:\"line\"`\n}\n\ntype Line struct {\n\tX1 float64 `xml:\"x1,attr\"`\n\tY1 float64 `xml:\"y1,attr\"`\n\tX2 float64 `xml:\"x2,attr\"`\n\tY2 float64 `xml:\"y2,attr\"`\n}\n\ntype Circle struct {\n\tCx float64 `xml:\"cx,attr\"`\n\tCy float64 `xml:\"cy,attr\"`\n\tR  float64 `xml:\"r,attr\"`\n}\n\nfunc TestSVGWriterSecondHand(t *testing.T) {\n\tcases := []struct {\n\t\ttime time.Time\n\t\tline Line\n\t}{\n\t\t{\n\t\t\tsimpleTime(0, 0, 0),\n\t\t\tLine{150, 150, 150, 60},\n\t\t},\n\t\t{\n\t\t\tsimpleTime(0, 0, 30),\n\t\t\tLine{150, 150, 150, 240},\n\t\t},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tb := bytes.Buffer{}\n\t\t\tclockface.SVGWriter(&b, c.time)\n\n\t\t\tsvg := SVG{}\n\t\t\txml.Unmarshal(b.Bytes(), &svg)\n\n\t\t\tif !containsLine(c.line, svg.Line) {\n\t\t\t\tt.Errorf(\"Expected to find the second hand line %+v, in the SVG lines %+v\", c.line, svg.Line)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSVGWriterMinutedHand(t *testing.T) {\n\tcases := []struct {\n\t\ttime time.Time\n\t\tline Line\n\t}{\n\t\t{\n\t\t\tsimpleTime(0, 0, 0),\n\t\t\tLine{150, 150, 150, 70},\n\t\t},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tb := bytes.Buffer{}\n\t\t\tclockface.SVGWriter(&b, c.time)\n\n\t\t\tsvg := SVG{}\n\t\t\txml.Unmarshal(b.Bytes(), &svg)\n\n\t\t\tif !containsLine(c.line, svg.Line) {\n\t\t\t\tt.Errorf(\"Expected to find the minute hand line %+v, in the SVG lines %+v\", c.line, svg.Line)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSVGWriterHourHand(t *testing.T) {\n\tcases := []struct {\n\t\ttime time.Time\n\t\tline Line\n\t}{\n\t\t{\n\t\t\tsimpleTime(6, 0, 0),\n\t\t\tLine{150, 150, 150, 200},\n\t\t},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tb := bytes.Buffer{}\n\t\t\tclockface.SVGWriter(&b, c.time)\n\n\t\t\tsvg := SVG{}\n\t\t\txml.Unmarshal(b.Bytes(), &svg)\n\n\t\t\tif !containsLine(c.line, svg.Line) {\n\t\t\t\tt.Errorf(\"Expected to find the hour hand line %+v, in the SVG lines %+v\", c.line, svg.Line)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc containsLine(l Line, ls []Line) bool {\n\tfor _, line := range ls {\n\t\tif line == l {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc simpleTime(hours, minutes, seconds int) time.Time {\n\treturn time.Date(312, time.October, 28, hours, minutes, seconds, 0, time.UTC)\n}\n\nfunc testName(t time.Time) string {\n\treturn t.Format(\"15:04:05\")\n}\n"
  },
  {
    "path": "math/v12/clockface/clockface_test.go",
    "content": "package clockface\n\nimport (\n\t\"math\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestSecondsInRadians(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tangle float64\n\t}{\n\t\t{simpleTime(0, 0, 30), math.Pi},\n\t\t{simpleTime(0, 0, 0), 0},\n\t\t{simpleTime(0, 0, 45), (math.Pi / 2) * 3},\n\t\t{simpleTime(0, 0, 7), (math.Pi / 30) * 7},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := secondsInRadians(c.time)\n\t\t\tif !roughlyEqualFloat64(got, c.angle) {\n\t\t\t\tt.Fatalf(\"Wanted %v radians, but got %v\", c.angle, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSecondHandPoint(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tpoint Point\n\t}{\n\t\t{simpleTime(0, 0, 30), Point{0, -1}},\n\t\t{simpleTime(0, 0, 45), Point{-1, 0}},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := secondHandPoint(c.time)\n\t\t\tif !roughlyEqualPoint(got, c.point) {\n\t\t\t\tt.Fatalf(\"Wanted %v Point, but got %v\", c.point, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestMinutesInRadians(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tangle float64\n\t}{\n\t\t{simpleTime(0, 30, 0), math.Pi},\n\t\t{simpleTime(0, 0, 7), 7 * (math.Pi / (30 * 60))},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := minutesInRadians(c.time)\n\t\t\tif !roughlyEqualFloat64(got, c.angle) {\n\t\t\t\tt.Fatalf(\"Wanted %v radians, but got %v\", c.angle, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestMinuteHandPoint(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tpoint Point\n\t}{\n\t\t{simpleTime(0, 30, 0), Point{0, -1}},\n\t\t{simpleTime(0, 45, 0), Point{-1, 0}},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := minuteHandPoint(c.time)\n\t\t\tif !roughlyEqualPoint(got, c.point) {\n\t\t\t\tt.Fatalf(\"Wanted %v Point, but got %v\", c.point, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestHoursInRadians(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tangle float64\n\t}{\n\t\t{simpleTime(6, 0, 0), math.Pi},\n\t\t{simpleTime(0, 0, 0), 0},\n\t\t{simpleTime(21, 0, 0), math.Pi * 1.5},\n\t\t{simpleTime(0, 1, 30), math.Pi / ((6 * 60 * 60) / 90)},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := hoursInRadians(c.time)\n\t\t\tif !roughlyEqualFloat64(got, c.angle) {\n\t\t\t\tt.Fatalf(\"Wanted %v radians, but got %v\", c.angle, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestHourHandPoint(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tpoint Point\n\t}{\n\t\t{simpleTime(6, 0, 0), Point{0, -1}},\n\t\t{simpleTime(21, 0, 0), Point{-1, 0}},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := hourHandPoint(c.time)\n\t\t\tif !roughlyEqualPoint(got, c.point) {\n\t\t\t\tt.Fatalf(\"Wanted %v Point, but got %v\", c.point, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc roughlyEqualFloat64(a, b float64) bool {\n\tconst equalityThreshold = 1e-7\n\treturn math.Abs(a-b) < equalityThreshold\n}\n\nfunc roughlyEqualPoint(a, b Point) bool {\n\treturn roughlyEqualFloat64(a.X, b.X) &&\n\t\troughlyEqualFloat64(a.Y, b.Y)\n}\n\nfunc simpleTime(hours, minutes, seconds int) time.Time {\n\treturn time.Date(312, time.October, 28, hours, minutes, seconds, 0, time.UTC)\n}\n\nfunc testName(t time.Time) string {\n\treturn t.Format(\"15:04:05\")\n}\n"
  },
  {
    "path": "math/v12/clockface/svgWriter.go",
    "content": "package clockface\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"time\"\n)\n\nconst (\n\tsecondHandLength = 90\n\tminuteHandLength = 80\n\thourHandLength   = 50\n\tclockCentreX     = 150\n\tclockCentreY     = 150\n)\n\n// SVGWriter writes an SVG representation of an analogue clock, showing the time t, to the writer w.\nfunc SVGWriter(w io.Writer, t time.Time) {\n\tio.WriteString(w, svgStart)\n\tio.WriteString(w, bezel)\n\tsecondHand(w, t)\n\tminuteHand(w, t)\n\thourHand(w, t)\n\tio.WriteString(w, svgEnd)\n}\n\nfunc secondHand(w io.Writer, t time.Time) {\n\tp := makeHand(secondHandPoint(t), secondHandLength)\n\tfmt.Fprintf(w, `<line x1=\"150\" y1=\"150\" x2=\"%.3f\" y2=\"%.3f\" style=\"fill:none;stroke:#f00;stroke-width:3px;\"/>`, p.X, p.Y)\n}\n\nfunc minuteHand(w io.Writer, t time.Time) {\n\tp := makeHand(minuteHandPoint(t), minuteHandLength)\n\tfmt.Fprintf(w, `<line x1=\"150\" y1=\"150\" x2=\"%.3f\" y2=\"%.3f\" style=\"fill:none;stroke:#000;stroke-width:3px;\"/>`, p.X, p.Y)\n}\n\nfunc hourHand(w io.Writer, t time.Time) {\n\tp := makeHand(hourHandPoint(t), hourHandLength)\n\tfmt.Fprintf(w, `<line x1=\"150\" y1=\"150\" x2=\"%.3f\" y2=\"%.3f\" style=\"fill:none;stroke:#000;stroke-width:3px;\"/>`, p.X, p.Y)\n}\n\nfunc makeHand(p Point, length float64) Point {\n\tp = Point{p.X * length, p.Y * length}\n\tp = Point{p.X, -p.Y}\n\treturn Point{p.X + clockCentreX, p.Y + clockCentreY}\n}\n\nconst svgStart = `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n<svg xmlns=\"http://www.w3.org/2000/svg\"\n     width=\"100%\"\n     height=\"100%\"\n     viewBox=\"0 0 300 300\"\n     version=\"2.0\">`\n\nconst bezel = `<circle cx=\"150\" cy=\"150\" r=\"100\" style=\"fill:#fff;stroke:#000;stroke-width:5px;\"/>`\n\nconst svgEnd = `</svg>`\n"
  },
  {
    "path": "math/v2/clockface/clockface.go",
    "content": "package clockface\n\nimport (\n\t\"math\"\n\t\"time\"\n)\n\n// A Point represents a two dimensional Cartesian coordinate.\ntype Point struct {\n\tX float64\n\tY float64\n}\n\n// SecondHand is the unit vector of the second hand of an analogue clock at time `t`.\n// represented as a Point.\nfunc SecondHand(t time.Time) Point {\n\treturn Point{150, 60}\n}\n\nfunc secondsInRadians(t time.Time) float64 {\n\treturn math.Pi\n}\n"
  },
  {
    "path": "math/v2/clockface/clockface_acceptance_test.go",
    "content": "package clockface_test\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/quii/learn-go-with-tests/math/v1/clockface\"\n)\n\nfunc TestSecondHandAtMidnight(t *testing.T) {\n\ttm := time.Date(1337, time.January, 1, 0, 0, 0, 0, time.UTC)\n\n\twant := clockface.Point{X: 150, Y: 150 - 90}\n\tgot := clockface.SecondHand(tm)\n\n\tif got != want {\n\t\tt.Errorf(\"Got %v, wanted %v\", got, want)\n\t}\n}\n\n// func TestSecondHandAt30Seconds(t *testing.T) {\n// \ttm := time.Date(1337, time.January, 1, 0, 0, 30, 0, time.UTC)\n\n// \twant := clockface.Point{X: 150, Y: 150 + 90}\n// \tgot := clockface.SecondHand(tm)\n\n// \tif got != want {\n// \t\tt.Errorf(\"Got %v, wanted %v\", got, want)\n// \t}\n// }\n"
  },
  {
    "path": "math/v2/clockface/clockface_test.go",
    "content": "package clockface\n\nimport (\n\t\"math\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestSecondsInRadians(t *testing.T) {\n\tthirtySeconds := time.Date(312, time.October, 28, 0, 0, 30, 0, time.UTC)\n\twant := math.Pi\n\tgot := secondsInRadians(thirtySeconds)\n\n\tif want != got {\n\t\tt.Fatalf(\"Wanted %v radians, but got %v\", want, got)\n\t}\n}\n"
  },
  {
    "path": "math/v3/clockface/clockface.go",
    "content": "package clockface\n\nimport (\n\t\"math\"\n\t\"time\"\n)\n\n// A Point represents a two dimensional Cartesian coordinate.\ntype Point struct {\n\tX float64\n\tY float64\n}\n\n// SecondHand is the unit vector of the second hand of an analogue clock at time `t`.\n// represented as a Point.\nfunc SecondHand(t time.Time) Point {\n\treturn Point{150, 60}\n}\n\nfunc secondsInRadians(t time.Time) float64 {\n\treturn (math.Pi / (30 / (float64(t.Second()))))\n}\n"
  },
  {
    "path": "math/v3/clockface/clockface_acceptance_test.go",
    "content": "package clockface_test\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/quii/learn-go-with-tests/math/v1/clockface\"\n)\n\nfunc TestSecondHandAtMidnight(t *testing.T) {\n\ttm := time.Date(1337, time.January, 1, 0, 0, 0, 0, time.UTC)\n\n\twant := clockface.Point{X: 150, Y: 150 - 90}\n\tgot := clockface.SecondHand(tm)\n\n\tif got != want {\n\t\tt.Errorf(\"Got %v, wanted %v\", got, want)\n\t}\n}\n\n// func TestSecondHandAt30Seconds(t *testing.T) {\n// \ttm := time.Date(1337, time.January, 1, 0, 0, 30, 0, time.UTC)\n\n// \twant := clockface.Point{X: 150, Y: 150 + 90}\n// \tgot := clockface.SecondHand(tm)\n\n// \tif got != want {\n// \t\tt.Errorf(\"Got %v, wanted %v\", got, want)\n// \t}\n// }\n"
  },
  {
    "path": "math/v3/clockface/clockface_test.go",
    "content": "package clockface\n\nimport (\n\t\"math\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestSecondsInRadians(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tangle float64\n\t}{\n\t\t{simpleTime(0, 0, 30), math.Pi},\n\t\t{simpleTime(0, 0, 0), 0},\n\t\t{simpleTime(0, 0, 45), (math.Pi / 2) * 3},\n\t\t{simpleTime(0, 0, 7), (math.Pi / 30) * 7},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := secondsInRadians(c.time)\n\t\t\tif got != c.angle {\n\t\t\t\tt.Fatalf(\"Wanted %v radians, but got %v\", c.angle, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc simpleTime(hours, minutes, seconds int) time.Time {\n\treturn time.Date(312, time.October, 28, hours, minutes, seconds, 0, time.UTC)\n}\n\nfunc testName(t time.Time) string {\n\treturn t.Format(\"15:04:05\")\n}\n"
  },
  {
    "path": "math/v4/clockface/clockface.go",
    "content": "package clockface\n\nimport (\n\t\"math\"\n\t\"time\"\n)\n\n// A Point represents a two dimensional Cartesian coordinate.\ntype Point struct {\n\tX float64\n\tY float64\n}\n\n// SecondHand is the unit vector of the second hand of an analogue clock at time `t`.\n// represented as a Point.\nfunc SecondHand(t time.Time) Point {\n\treturn Point{150, 60}\n}\n\nfunc secondsInRadians(t time.Time) float64 {\n\treturn (math.Pi / (30 / (float64(t.Second()))))\n}\n\nfunc secondHandPoint(t time.Time) Point {\n\tangle := secondsInRadians(t)\n\tx := math.Sin(angle)\n\ty := math.Cos(angle)\n\n\treturn Point{x, y}\n}\n"
  },
  {
    "path": "math/v4/clockface/clockface_acceptance_test.go",
    "content": "package clockface_test\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/quii/learn-go-with-tests/math/v1/clockface\"\n)\n\nfunc TestSecondHandAtMidnight(t *testing.T) {\n\ttm := time.Date(1337, time.January, 1, 0, 0, 0, 0, time.UTC)\n\n\twant := clockface.Point{X: 150, Y: 150 - 90}\n\tgot := clockface.SecondHand(tm)\n\n\tif got != want {\n\t\tt.Errorf(\"Got %v, wanted %v\", got, want)\n\t}\n}\n\n// func TestSecondHandAt30Seconds(t *testing.T) {\n// \ttm := time.Date(1337, time.January, 1, 0, 0, 30, 0, time.UTC)\n\n// \twant := clockface.Point{X: 150, Y: 150 + 90}\n// \tgot := clockface.SecondHand(tm)\n\n// \tif got != want {\n// \t\tt.Errorf(\"Got %v, wanted %v\", got, want)\n// \t}\n// }\n"
  },
  {
    "path": "math/v4/clockface/clockface_test.go",
    "content": "package clockface\n\nimport (\n\t\"math\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestSecondsInRadians(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tangle float64\n\t}{\n\t\t{simpleTime(0, 0, 30), math.Pi},\n\t\t{simpleTime(0, 0, 0), 0},\n\t\t{simpleTime(0, 0, 45), (math.Pi / 2) * 3},\n\t\t{simpleTime(0, 0, 7), (math.Pi / 30) * 7},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := secondsInRadians(c.time)\n\t\t\tif got != c.angle {\n\t\t\t\tt.Fatalf(\"Wanted %v radians, but got %v\", c.angle, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSecondHandPoint(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tpoint Point\n\t}{\n\t\t{simpleTime(0, 0, 30), Point{0, -1}},\n\t\t{simpleTime(0, 0, 45), Point{-1, 0}},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := secondHandPoint(c.time)\n\t\t\tif !roughlyEqualPoint(got, c.point) {\n\t\t\t\tt.Fatalf(\"Wanted %v Point, but got %v\", c.point, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc roughlyEqualFloat64(a, b float64) bool {\n\tconst equalityThreshold = 1e-7\n\treturn math.Abs(a-b) < equalityThreshold\n}\n\nfunc roughlyEqualPoint(a, b Point) bool {\n\treturn roughlyEqualFloat64(a.X, b.X) &&\n\t\troughlyEqualFloat64(a.Y, b.Y)\n}\n\nfunc simpleTime(hours, minutes, seconds int) time.Time {\n\treturn time.Date(312, time.October, 28, hours, minutes, seconds, 0, time.UTC)\n}\n\nfunc testName(t time.Time) string {\n\treturn t.Format(\"15:04:05\")\n}\n"
  },
  {
    "path": "math/v5/clockface/clockface.go",
    "content": "package clockface\n\nimport (\n\t\"math\"\n\t\"time\"\n)\n\n// A Point represents a two dimensional Cartesian coordinate.\ntype Point struct {\n\tX float64\n\tY float64\n}\n\nconst secondHandLength = 90\nconst clockCentreX = 150\nconst clockCentreY = 150\n\n// SecondHand is the unit vector of the second hand of an analogue clock at time `t`.\n// represented as a Point.\nfunc SecondHand(t time.Time) Point {\n\tp := secondHandPoint(t)\n\tp = Point{p.X * secondHandLength, p.Y * secondHandLength}\n\tp = Point{p.X, -p.Y}\n\tp = Point{p.X + clockCentreX, p.Y + clockCentreY} //translate\n\treturn p\n}\n\nfunc secondsInRadians(t time.Time) float64 {\n\treturn (math.Pi / (30 / (float64(t.Second()))))\n}\n\nfunc secondHandPoint(t time.Time) Point {\n\tangle := secondsInRadians(t)\n\tx := math.Sin(angle)\n\ty := math.Cos(angle)\n\n\treturn Point{x, y}\n}\n"
  },
  {
    "path": "math/v5/clockface/clockface_acceptance_test.go",
    "content": "package clockface_test\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/quii/learn-go-with-tests/math/v5/clockface\"\n)\n\nfunc TestSecondHandAtMidnight(t *testing.T) {\n\ttm := time.Date(1337, time.January, 1, 0, 0, 0, 0, time.UTC)\n\n\twant := clockface.Point{X: 150, Y: 150 - 90}\n\tgot := clockface.SecondHand(tm)\n\n\tif got != want {\n\t\tt.Errorf(\"Got %v, wanted %v\", got, want)\n\t}\n}\n\nfunc TestSecondHandAt30Seconds(t *testing.T) {\n\ttm := time.Date(1337, time.January, 1, 0, 0, 30, 0, time.UTC)\n\n\twant := clockface.Point{X: 150, Y: 150 + 90}\n\tgot := clockface.SecondHand(tm)\n\n\tif got != want {\n\t\tt.Errorf(\"Got %v, wanted %v\", got, want)\n\t}\n}\n"
  },
  {
    "path": "math/v5/clockface/clockface_test.go",
    "content": "package clockface\n\nimport (\n\t\"math\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestSecondsInRadians(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tangle float64\n\t}{\n\t\t{simpleTime(0, 0, 30), math.Pi},\n\t\t{simpleTime(0, 0, 0), 0},\n\t\t{simpleTime(0, 0, 45), (math.Pi / 2) * 3},\n\t\t{simpleTime(0, 0, 7), (math.Pi / 30) * 7},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := secondsInRadians(c.time)\n\t\t\tif got != c.angle {\n\t\t\t\tt.Fatalf(\"Wanted %v radians, but got %v\", c.angle, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSecondHandPoint(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tpoint Point\n\t}{\n\t\t{simpleTime(0, 0, 30), Point{0, -1}},\n\t\t{simpleTime(0, 0, 45), Point{-1, 0}},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := secondHandPoint(c.time)\n\t\t\tif !roughlyEqualPoint(got, c.point) {\n\t\t\t\tt.Fatalf(\"Wanted %v Point, but got %v\", c.point, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc roughlyEqualFloat64(a, b float64) bool {\n\tconst equalityThreshold = 1e-7\n\treturn math.Abs(a-b) < equalityThreshold\n}\n\nfunc roughlyEqualPoint(a, b Point) bool {\n\treturn roughlyEqualFloat64(a.X, b.X) &&\n\t\troughlyEqualFloat64(a.Y, b.Y)\n}\n\nfunc simpleTime(hours, minutes, seconds int) time.Time {\n\treturn time.Date(312, time.October, 28, hours, minutes, seconds, 0, time.UTC)\n}\n\nfunc testName(t time.Time) string {\n\treturn t.Format(\"15:04:05\")\n}\n"
  },
  {
    "path": "math/v6/clockface/clockface/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/quii/learn-go-with-tests/math/v6/clockface\"\n)\n\nfunc main() {\n\tt := time.Now()\n\tsh := clockface.SecondHand(t)\n\tio.WriteString(os.Stdout, svgStart)\n\tio.WriteString(os.Stdout, bezel)\n\tio.WriteString(os.Stdout, secondHandTag(sh))\n\tio.WriteString(os.Stdout, svgEnd)\n}\n\nfunc secondHandTag(p clockface.Point) string {\n\treturn fmt.Sprintf(`<line x1=\"150\" y1=\"150\" x2=\"%f\" y2=\"%f\" style=\"fill:none;stroke:#f00;stroke-width:3px;\"/>`, p.X, p.Y)\n}\n\nconst svgStart = `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n<svg xmlns=\"http://www.w3.org/2000/svg\"\n     width=\"100%\"\n     height=\"100%\"\n     viewBox=\"0 0 300 300\"\n     version=\"2.0\">`\n\nconst bezel = `<circle cx=\"150\" cy=\"150\" r=\"100\" style=\"fill:#fff;stroke:#000;stroke-width:5px;\"/>`\n\nconst svgEnd = `</svg>`\n"
  },
  {
    "path": "math/v6/clockface/clockface.go",
    "content": "package clockface\n\nimport (\n\t\"math\"\n\t\"time\"\n)\n\n// A Point represents a two dimensional Cartesian coordinate.\ntype Point struct {\n\tX float64\n\tY float64\n}\n\nconst secondHandLength = 90\nconst clockCentreX = 150\nconst clockCentreY = 150\n\n// SecondHand is the unit vector of the second hand of an analogue clock at time `t`.\n// represented as a Point.\nfunc SecondHand(t time.Time) Point {\n\tp := secondHandPoint(t)\n\tp = Point{p.X * secondHandLength, p.Y * secondHandLength}\n\tp = Point{p.X, -p.Y}\n\tp = Point{p.X + clockCentreX, p.Y + clockCentreY} //translate\n\treturn p\n}\n\nfunc secondsInRadians(t time.Time) float64 {\n\treturn (math.Pi / (30 / (float64(t.Second()))))\n}\n\nfunc secondHandPoint(t time.Time) Point {\n\tangle := secondsInRadians(t)\n\tx := math.Sin(angle)\n\ty := math.Cos(angle)\n\n\treturn Point{x, y}\n}\n"
  },
  {
    "path": "math/v6/clockface/clockface_acceptance_test.go",
    "content": "package clockface_test\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/quii/learn-go-with-tests/math/v6/clockface\"\n)\n\nfunc TestSecondHandAtMidnight(t *testing.T) {\n\ttm := time.Date(1337, time.January, 1, 0, 0, 0, 0, time.UTC)\n\n\twant := clockface.Point{X: 150, Y: 150 - 90}\n\tgot := clockface.SecondHand(tm)\n\n\tif got != want {\n\t\tt.Errorf(\"Got %v, wanted %v\", got, want)\n\t}\n}\n\nfunc TestSecondHandAt30Seconds(t *testing.T) {\n\ttm := time.Date(1337, time.January, 1, 0, 0, 30, 0, time.UTC)\n\n\twant := clockface.Point{X: 150, Y: 150 + 90}\n\tgot := clockface.SecondHand(tm)\n\n\tif got != want {\n\t\tt.Errorf(\"Got %v, wanted %v\", got, want)\n\t}\n}\n"
  },
  {
    "path": "math/v6/clockface/clockface_test.go",
    "content": "package clockface\n\nimport (\n\t\"math\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestSecondsInRadians(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tangle float64\n\t}{\n\t\t{simpleTime(0, 0, 30), math.Pi},\n\t\t{simpleTime(0, 0, 0), 0},\n\t\t{simpleTime(0, 0, 45), (math.Pi / 2) * 3},\n\t\t{simpleTime(0, 0, 7), (math.Pi / 30) * 7},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := secondsInRadians(c.time)\n\t\t\tif got != c.angle {\n\t\t\t\tt.Fatalf(\"Wanted %v radians, but got %v\", c.angle, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSecondHandPoint(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tpoint Point\n\t}{\n\t\t{simpleTime(0, 0, 30), Point{0, -1}},\n\t\t{simpleTime(0, 0, 45), Point{-1, 0}},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := secondHandPoint(c.time)\n\t\t\tif !roughlyEqualPoint(got, c.point) {\n\t\t\t\tt.Fatalf(\"Wanted %v Point, but got %v\", c.point, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc roughlyEqualFloat64(a, b float64) bool {\n\tconst equalityThreshold = 1e-7\n\treturn math.Abs(a-b) < equalityThreshold\n}\n\nfunc roughlyEqualPoint(a, b Point) bool {\n\treturn roughlyEqualFloat64(a.X, b.X) &&\n\t\troughlyEqualFloat64(a.Y, b.Y)\n}\n\nfunc simpleTime(hours, minutes, seconds int) time.Time {\n\treturn time.Date(312, time.October, 28, hours, minutes, seconds, 0, time.UTC)\n}\n\nfunc testName(t time.Time) string {\n\treturn t.Format(\"15:04:05\")\n}\n"
  },
  {
    "path": "math/v7/clockface/clockface/main.go",
    "content": "package main\n\nimport (\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/quii/learn-go-with-tests/math/v7/clockface\"\n)\n\nfunc main() {\n\tt := time.Now()\n\tclockface.SVGWriter(os.Stdout, t)\n}\n"
  },
  {
    "path": "math/v7/clockface/clockface.go",
    "content": "package clockface\n\nimport (\n\t\"math\"\n\t\"time\"\n)\n\n// A Point represents a two dimensional Cartesian coordinate.\ntype Point struct {\n\tX float64\n\tY float64\n}\n\nfunc secondsInRadians(t time.Time) float64 {\n\treturn (math.Pi / (30 / (float64(t.Second()))))\n}\n\nfunc secondHandPoint(t time.Time) Point {\n\tangle := secondsInRadians(t)\n\tx := math.Sin(angle)\n\ty := math.Cos(angle)\n\n\treturn Point{x, y}\n}\n"
  },
  {
    "path": "math/v7/clockface/clockface_acceptance_test.go",
    "content": "package clockface_test\n\nimport (\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/quii/learn-go-with-tests/math/v7/clockface\"\n)\n\nfunc TestSVGWriterAtMidnight(t *testing.T) {\n\ttm := time.Date(1337, time.January, 1, 0, 0, 0, 0, time.UTC)\n\n\tvar b strings.Builder\n\tclockface.SVGWriter(&b, tm)\n\tgot := b.String()\n\n\twant := `<line x1=\"150\" y1=\"150\" x2=\"150.000\" y2=\"60.000\"`\n\n\tif !strings.Contains(got, want) {\n\t\tt.Errorf(\"Expected to find the second hand %v, in the SVG output %v\", want, got)\n\t}\n}\n\nfunc TestSVGWriterAt30Seconds(t *testing.T) {\n\ttm := time.Date(1337, time.January, 1, 0, 0, 30, 0, time.UTC)\n\n\tvar b strings.Builder\n\tclockface.SVGWriter(&b, tm)\n\tgot := b.String()\n\n\twant := `<line x1=\"150\" y1=\"150\" x2=\"150.000\" y2=\"240.000\"`\n\n\tif !strings.Contains(got, want) {\n\t\tt.Errorf(\"Expected to find the second hand %v, in the SVG output %v\", want, got)\n\t}\n}\n"
  },
  {
    "path": "math/v7/clockface/clockface_test.go",
    "content": "package clockface\n\nimport (\n\t\"math\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestSecondsInRadians(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tangle float64\n\t}{\n\t\t{simpleTime(0, 0, 30), math.Pi},\n\t\t{simpleTime(0, 0, 0), 0},\n\t\t{simpleTime(0, 0, 45), (math.Pi / 2) * 3},\n\t\t{simpleTime(0, 0, 7), (math.Pi / 30) * 7},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := secondsInRadians(c.time)\n\t\t\tif got != c.angle {\n\t\t\t\tt.Fatalf(\"Wanted %v radians, but got %v\", c.angle, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSecondHandPoint(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tpoint Point\n\t}{\n\t\t{simpleTime(0, 0, 30), Point{0, -1}},\n\t\t{simpleTime(0, 0, 45), Point{-1, 0}},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := secondHandPoint(c.time)\n\t\t\tif !roughlyEqualPoint(got, c.point) {\n\t\t\t\tt.Fatalf(\"Wanted %v Point, but got %v\", c.point, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc roughlyEqualFloat64(a, b float64) bool {\n\tconst equalityThreshold = 1e-7\n\treturn math.Abs(a-b) < equalityThreshold\n}\n\nfunc roughlyEqualPoint(a, b Point) bool {\n\treturn roughlyEqualFloat64(a.X, b.X) &&\n\t\troughlyEqualFloat64(a.Y, b.Y)\n}\n\nfunc simpleTime(hours, minutes, seconds int) time.Time {\n\treturn time.Date(312, time.October, 28, hours, minutes, seconds, 0, time.UTC)\n}\n\nfunc testName(t time.Time) string {\n\treturn t.Format(\"15:04:05\")\n}\n"
  },
  {
    "path": "math/v7/clockface/svgWriter.go",
    "content": "package clockface\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"time\"\n)\n\nconst (\n\tsecondHandLength = 90\n\tclockCentreX     = 150\n\tclockCentreY     = 150\n)\n\n// SVGWriter writes an SVG representation of an analogue clock, showing the time t, to the writer w.\nfunc SVGWriter(w io.Writer, t time.Time) {\n\tio.WriteString(w, svgStart)\n\tio.WriteString(w, bezel)\n\tsecondHand(w, t)\n\tio.WriteString(w, svgEnd)\n}\n\nfunc secondHand(w io.Writer, t time.Time) {\n\tp := secondHandPoint(t)\n\tp = Point{p.X * secondHandLength, p.Y * secondHandLength}\n\tp = Point{p.X, -p.Y}\n\tp = Point{p.X + clockCentreX, p.Y + clockCentreY} //translate\n\tfmt.Fprintf(w, `<line x1=\"150\" y1=\"150\" x2=\"%.3f\" y2=\"%.3f\" style=\"fill:none;stroke:#f00;stroke-width:3px;\"/>`, p.X, p.Y)\n}\n\nconst svgStart = `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n<svg xmlns=\"http://www.w3.org/2000/svg\"\n     width=\"100%\"\n     height=\"100%\"\n     viewBox=\"0 0 300 300\"\n     version=\"2.0\">`\n\nconst bezel = `<circle cx=\"150\" cy=\"150\" r=\"100\" style=\"fill:#fff;stroke:#000;stroke-width:5px;\"/>`\n\nconst svgEnd = `</svg>`\n"
  },
  {
    "path": "math/v7b/clockface/clockface/main.go",
    "content": "package main\n\nimport (\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/quii/learn-go-with-tests/math/v7/clockface\"\n)\n\nfunc main() {\n\tt := time.Now()\n\tclockface.SVGWriter(os.Stdout, t)\n}\n"
  },
  {
    "path": "math/v7b/clockface/clockface.go",
    "content": "package clockface\n\nimport (\n\t\"math\"\n\t\"time\"\n)\n\n// A Point represents a two dimensional Cartesian coordinate.\ntype Point struct {\n\tX float64\n\tY float64\n}\n\nfunc secondsInRadians(t time.Time) float64 {\n\treturn (math.Pi / (30 / (float64(t.Second()))))\n}\n\nfunc secondHandPoint(t time.Time) Point {\n\tangle := secondsInRadians(t)\n\tx := math.Sin(angle)\n\ty := math.Cos(angle)\n\n\treturn Point{x, y}\n}\n"
  },
  {
    "path": "math/v7b/clockface/clockface_acceptance_test.go",
    "content": "package clockface_test\n\nimport (\n\t\"bytes\"\n\t\"encoding/xml\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/quii/learn-go-with-tests/math/v7b/clockface\"\n)\n\ntype Svg struct {\n\tXMLName xml.Name `xml:\"svg\"`\n\tText    string   `xml:\",chardata\"`\n\tXmlns   string   `xml:\"xmlns,attr\"`\n\tWidth   string   `xml:\"width,attr\"`\n\tHeight  string   `xml:\"height,attr\"`\n\tViewBox string   `xml:\"viewBox,attr\"`\n\tVersion string   `xml:\"version,attr\"`\n\tCircle  struct {\n\t\tText  string `xml:\",chardata\"`\n\t\tCx    string `xml:\"cx,attr\"`\n\t\tCy    string `xml:\"cy,attr\"`\n\t\tR     string `xml:\"r,attr\"`\n\t\tStyle string `xml:\"style,attr\"`\n\t} `xml:\"circle\"`\n\tLine []struct {\n\t\tText  string `xml:\",chardata\"`\n\t\tX1    string `xml:\"x1,attr\"`\n\t\tY1    string `xml:\"y1,attr\"`\n\t\tX2    string `xml:\"x2,attr\"`\n\t\tY2    string `xml:\"y2,attr\"`\n\t\tStyle string `xml:\"style,attr\"`\n\t} `xml:\"line\"`\n}\n\nfunc TestSVGWriterAtMidnight(t *testing.T) {\n\ttm := time.Date(1337, time.January, 1, 0, 0, 0, 0, time.UTC)\n\tb := bytes.Buffer{}\n\n\tclockface.SVGWriter(&b, tm)\n\n\tsvg := Svg{}\n\n\txml.Unmarshal(b.Bytes(), &svg)\n\n\tx2 := \"150.000\"\n\ty2 := \"60.000\"\n\n\tfor _, line := range svg.Line {\n\t\tif line.X2 == x2 && line.Y2 == y2 {\n\t\t\treturn\n\t\t}\n\t}\n\n\tt.Errorf(\"Expected to find the second hand with x2 of %v and y2 of %v, in the SVG output %v\", x2, y2, b.String())\n}\n"
  },
  {
    "path": "math/v7b/clockface/clockface_test.go",
    "content": "package clockface\n\nimport (\n\t\"math\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestSecondsInRadians(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tangle float64\n\t}{\n\t\t{simpleTime(0, 0, 30), math.Pi},\n\t\t{simpleTime(0, 0, 0), 0},\n\t\t{simpleTime(0, 0, 45), (math.Pi / 2) * 3},\n\t\t{simpleTime(0, 0, 7), (math.Pi / 30) * 7},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := secondsInRadians(c.time)\n\t\t\tif got != c.angle {\n\t\t\t\tt.Fatalf(\"Wanted %v radians, but got %v\", c.angle, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSecondHandPoint(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tpoint Point\n\t}{\n\t\t{simpleTime(0, 0, 30), Point{0, -1}},\n\t\t{simpleTime(0, 0, 45), Point{-1, 0}},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := secondHandPoint(c.time)\n\t\t\tif !roughlyEqualPoint(got, c.point) {\n\t\t\t\tt.Fatalf(\"Wanted %v Point, but got %v\", c.point, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc roughlyEqualFloat64(a, b float64) bool {\n\tconst equalityThreshold = 1e-7\n\treturn math.Abs(a-b) < equalityThreshold\n}\n\nfunc roughlyEqualPoint(a, b Point) bool {\n\treturn roughlyEqualFloat64(a.X, b.X) &&\n\t\troughlyEqualFloat64(a.Y, b.Y)\n}\n\nfunc simpleTime(hours, minutes, seconds int) time.Time {\n\treturn time.Date(312, time.October, 28, hours, minutes, seconds, 0, time.UTC)\n}\n\nfunc testName(t time.Time) string {\n\treturn t.Format(\"15:04:05\")\n}\n"
  },
  {
    "path": "math/v7b/clockface/svgWriter.go",
    "content": "package clockface\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"time\"\n)\n\nconst (\n\tsecondHandLength = 90\n\tclockCentreX     = 150\n\tclockCentreY     = 150\n)\n\n// SVGWriter writes an SVG representation of an analogue clock, showing the time t, to the writer w.\nfunc SVGWriter(w io.Writer, t time.Time) {\n\tio.WriteString(w, svgStart)\n\tio.WriteString(w, bezel)\n\tsecondHand(w, t)\n\tio.WriteString(w, svgEnd)\n}\n\nfunc secondHand(w io.Writer, t time.Time) {\n\tp := secondHandPoint(t)\n\tp = Point{p.X * secondHandLength, p.Y * secondHandLength}\n\tp = Point{p.X, -p.Y}\n\tp = Point{p.X + clockCentreX, p.Y + clockCentreY} //translate\n\tfmt.Fprintf(w, `<line x1=\"150\" y1=\"150\" x2=\"%.3f\" y2=\"%.3f\" style=\"fill:none;stroke:#f00;stroke-width:3px;\"/>`, p.X, p.Y)\n}\n\nconst svgStart = `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n<svg xmlns=\"http://www.w3.org/2000/svg\"\n     width=\"100%\"\n     height=\"100%\"\n     viewBox=\"0 0 300 300\"\n     version=\"2.0\">`\n\nconst bezel = `<circle cx=\"150\" cy=\"150\" r=\"100\" style=\"fill:#fff;stroke:#000;stroke-width:5px;\"/>`\n\nconst svgEnd = `</svg>`\n"
  },
  {
    "path": "math/v7c/clockface/clockface/main.go",
    "content": "package main\n\nimport (\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/quii/learn-go-with-tests/math/v7/clockface\"\n)\n\nfunc main() {\n\tt := time.Now()\n\tclockface.SVGWriter(os.Stdout, t)\n}\n"
  },
  {
    "path": "math/v7c/clockface/clockface.go",
    "content": "package clockface\n\nimport (\n\t\"math\"\n\t\"time\"\n)\n\n// A Point represents a two dimensional Cartesian coordinate.\ntype Point struct {\n\tX float64\n\tY float64\n}\n\nfunc secondsInRadians(t time.Time) float64 {\n\treturn (math.Pi / (30 / (float64(t.Second()))))\n}\n\nfunc secondHandPoint(t time.Time) Point {\n\tangle := secondsInRadians(t)\n\tx := math.Sin(angle)\n\ty := math.Cos(angle)\n\n\treturn Point{x, y}\n}\n"
  },
  {
    "path": "math/v7c/clockface/clockface_acceptance_test.go",
    "content": "package clockface_test\n\nimport (\n\t\"bytes\"\n\t\"encoding/xml\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/quii/learn-go-with-tests/math/v7c/clockface\"\n)\n\ntype SVG struct {\n\tXMLName xml.Name `xml:\"svg\"`\n\tText    string   `xml:\",chardata\"`\n\tXmlns   string   `xml:\"xmlns,attr\"`\n\tWidth   string   `xml:\"width,attr\"`\n\tHeight  string   `xml:\"height,attr\"`\n\tViewBox string   `xml:\"viewBox,attr\"`\n\tVersion string   `xml:\"version,attr\"`\n\tCircle  Circle   `xml:\"circle\"`\n\tLine    []Line   `xml:\"line\"`\n}\n\ntype Line struct {\n\tX1 float64 `xml:\"x1,attr\"`\n\tY1 float64 `xml:\"y1,attr\"`\n\tX2 float64 `xml:\"x2,attr\"`\n\tY2 float64 `xml:\"y2,attr\"`\n}\n\ntype Circle struct {\n\tCx float64 `xml:\"cx,attr\"`\n\tCy float64 `xml:\"cy,attr\"`\n\tR  float64 `xml:\"r,attr\"`\n}\n\nfunc TestSVGWriterSecondHand(t *testing.T) {\n\tcases := []struct {\n\t\ttime time.Time\n\t\tline Line\n\t}{\n\t\t{\n\t\t\tsimpleTime(0, 0, 0),\n\t\t\tLine{150, 150, 150, 60},\n\t\t},\n\t\t{\n\t\t\tsimpleTime(0, 0, 30),\n\t\t\tLine{150, 150, 150, 240},\n\t\t},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tb := bytes.Buffer{}\n\t\t\tclockface.SVGWriter(&b, c.time)\n\n\t\t\tsvg := SVG{}\n\t\t\txml.Unmarshal(b.Bytes(), &svg)\n\n\t\t\tif !containsLine(c.line, svg.Line) {\n\t\t\t\tt.Errorf(\"Expected to find the second hand line %+v, in the SVG lines %+v\", c.line, svg.Line)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc containsLine(l Line, ls []Line) bool {\n\tfor _, line := range ls {\n\t\tif line == l {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc simpleTime(hours, minutes, seconds int) time.Time {\n\treturn time.Date(312, time.October, 28, hours, minutes, seconds, 0, time.UTC)\n}\n\nfunc testName(t time.Time) string {\n\treturn t.Format(\"15:04:05\")\n}\n"
  },
  {
    "path": "math/v7c/clockface/clockface_test.go",
    "content": "package clockface\n\nimport (\n\t\"math\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestSecondsInRadians(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tangle float64\n\t}{\n\t\t{simpleTime(0, 0, 30), math.Pi},\n\t\t{simpleTime(0, 0, 0), 0},\n\t\t{simpleTime(0, 0, 45), (math.Pi / 2) * 3},\n\t\t{simpleTime(0, 0, 7), (math.Pi / 30) * 7},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := secondsInRadians(c.time)\n\t\t\tif got != c.angle {\n\t\t\t\tt.Fatalf(\"Wanted %v radians, but got %v\", c.angle, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSecondHandPoint(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tpoint Point\n\t}{\n\t\t{simpleTime(0, 0, 30), Point{0, -1}},\n\t\t{simpleTime(0, 0, 45), Point{-1, 0}},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := secondHandPoint(c.time)\n\t\t\tif !roughlyEqualPoint(got, c.point) {\n\t\t\t\tt.Fatalf(\"Wanted %v Point, but got %v\", c.point, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc roughlyEqualFloat64(a, b float64) bool {\n\tconst equalityThreshold = 1e-7\n\treturn math.Abs(a-b) < equalityThreshold\n}\n\nfunc roughlyEqualPoint(a, b Point) bool {\n\treturn roughlyEqualFloat64(a.X, b.X) &&\n\t\troughlyEqualFloat64(a.Y, b.Y)\n}\n\nfunc simpleTime(hours, minutes, seconds int) time.Time {\n\treturn time.Date(312, time.October, 28, hours, minutes, seconds, 0, time.UTC)\n}\n\nfunc testName(t time.Time) string {\n\treturn t.Format(\"15:04:05\")\n}\n"
  },
  {
    "path": "math/v7c/clockface/svgWriter.go",
    "content": "package clockface\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"time\"\n)\n\nconst (\n\tsecondHandLength = 90\n\tclockCentreX     = 150\n\tclockCentreY     = 150\n)\n\n// SVGWriter writes an SVG representation of an analogue clock, showing the time t, to the writer w.\nfunc SVGWriter(w io.Writer, t time.Time) {\n\tio.WriteString(w, svgStart)\n\tio.WriteString(w, bezel)\n\tsecondHand(w, t)\n\tio.WriteString(w, svgEnd)\n}\n\nfunc secondHand(w io.Writer, t time.Time) {\n\tp := secondHandPoint(t)\n\tp = Point{p.X * secondHandLength, p.Y * secondHandLength}\n\tp = Point{p.X, -p.Y}\n\tp = Point{p.X + clockCentreX, p.Y + clockCentreY} //translate\n\tfmt.Fprintf(w, `<line x1=\"150\" y1=\"150\" x2=\"%.3f\" y2=\"%.3f\" style=\"fill:none;stroke:#f00;stroke-width:3px;\"/>`, p.X, p.Y)\n}\n\nconst svgStart = `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n<svg xmlns=\"http://www.w3.org/2000/svg\"\n     width=\"100%\"\n     height=\"100%\"\n     viewBox=\"0 0 300 300\"\n     version=\"2.0\">`\n\nconst bezel = `<circle cx=\"150\" cy=\"150\" r=\"100\" style=\"fill:#fff;stroke:#000;stroke-width:5px;\"/>`\n\nconst svgEnd = `</svg>`\n"
  },
  {
    "path": "math/v8/clockface/clockface/main.go",
    "content": "package main\n\nimport (\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/quii/learn-go-with-tests/math/v7/clockface\"\n)\n\nfunc main() {\n\tt := time.Now()\n\tclockface.SVGWriter(os.Stdout, t)\n}\n"
  },
  {
    "path": "math/v8/clockface/clockface.go",
    "content": "package clockface\n\nimport (\n\t\"math\"\n\t\"time\"\n)\n\n// A Point represents a two dimensional Cartesian coordinate.\ntype Point struct {\n\tX float64\n\tY float64\n}\n\nfunc secondsInRadians(t time.Time) float64 {\n\treturn (math.Pi / (30 / float64(t.Second())))\n}\n\nfunc secondHandPoint(t time.Time) Point {\n\tangle := secondsInRadians(t)\n\tx := math.Sin(angle)\n\ty := math.Cos(angle)\n\n\treturn Point{x, y}\n}\n\nfunc minutesInRadians(t time.Time) float64 {\n\treturn (secondsInRadians(t) / 60) +\n\t\t(math.Pi / (30 / float64(t.Minute())))\n}\n"
  },
  {
    "path": "math/v8/clockface/clockface_acceptance_test.go",
    "content": "package clockface_test\n\nimport (\n\t\"bytes\"\n\t\"encoding/xml\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/quii/learn-go-with-tests/math/v8/clockface\"\n)\n\ntype SVG struct {\n\tXMLName xml.Name `xml:\"svg\"`\n\tText    string   `xml:\",chardata\"`\n\tXmlns   string   `xml:\"xmlns,attr\"`\n\tWidth   string   `xml:\"width,attr\"`\n\tHeight  string   `xml:\"height,attr\"`\n\tViewBox string   `xml:\"viewBox,attr\"`\n\tVersion string   `xml:\"version,attr\"`\n\tCircle  Circle   `xml:\"circle\"`\n\tLine    []Line   `xml:\"line\"`\n}\n\ntype Line struct {\n\tX1 float64 `xml:\"x1,attr\"`\n\tY1 float64 `xml:\"y1,attr\"`\n\tX2 float64 `xml:\"x2,attr\"`\n\tY2 float64 `xml:\"y2,attr\"`\n}\n\ntype Circle struct {\n\tCx float64 `xml:\"cx,attr\"`\n\tCy float64 `xml:\"cy,attr\"`\n\tR  float64 `xml:\"r,attr\"`\n}\n\nfunc TestSVGWriterSecondHand(t *testing.T) {\n\tcases := []struct {\n\t\ttime time.Time\n\t\tline Line\n\t}{\n\t\t{\n\t\t\tsimpleTime(0, 0, 0),\n\t\t\tLine{150, 150, 150, 60},\n\t\t},\n\t\t{\n\t\t\tsimpleTime(0, 0, 30),\n\t\t\tLine{150, 150, 150, 240},\n\t\t},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tb := bytes.Buffer{}\n\t\t\tclockface.SVGWriter(&b, c.time)\n\n\t\t\tsvg := SVG{}\n\t\t\txml.Unmarshal(b.Bytes(), &svg)\n\n\t\t\tif !containsLine(c.line, svg.Line) {\n\t\t\t\tt.Errorf(\"Expected to find the second hand line %+v, in the SVG lines %+v\", c.line, svg.Line)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSVGWriterMinuteHand(t *testing.T) {\n\tcases := []struct {\n\t\ttime time.Time\n\t\tline Line\n\t}{\n\t\t// {\n\t\t// \tsimpleTime(0, 0, 0),\n\t\t// \tLine{150, 150, 150, 70},\n\t\t// },\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tb := bytes.Buffer{}\n\t\t\tclockface.SVGWriter(&b, c.time)\n\n\t\t\tsvg := SVG{}\n\t\t\txml.Unmarshal(b.Bytes(), &svg)\n\n\t\t\tif !containsLine(c.line, svg.Line) {\n\t\t\t\tt.Errorf(\"Expected to find the minute hand line %+v, in the SVG lines %+v\", c.line, svg.Line)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc containsLine(l Line, ls []Line) bool {\n\tfor _, line := range ls {\n\t\tif line == l {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc simpleTime(hours, minutes, seconds int) time.Time {\n\treturn time.Date(312, time.October, 28, hours, minutes, seconds, 0, time.UTC)\n}\n\nfunc testName(t time.Time) string {\n\treturn t.Format(\"15:04:05\")\n}\n"
  },
  {
    "path": "math/v8/clockface/clockface_test.go",
    "content": "package clockface\n\nimport (\n\t\"math\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestSecondsInRadians(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tangle float64\n\t}{\n\t\t{simpleTime(0, 0, 30), math.Pi},\n\t\t{simpleTime(0, 0, 0), 0},\n\t\t{simpleTime(0, 0, 45), (math.Pi / 2) * 3},\n\t\t{simpleTime(0, 0, 7), (math.Pi / 30) * 7},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := secondsInRadians(c.time)\n\t\t\tif got != c.angle {\n\t\t\t\tt.Fatalf(\"Wanted %v radians, but got %v\", c.angle, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSecondHandPoint(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tpoint Point\n\t}{\n\t\t{simpleTime(0, 0, 30), Point{0, -1}},\n\t\t{simpleTime(0, 0, 45), Point{-1, 0}},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := secondHandPoint(c.time)\n\t\t\tif !roughlyEqualPoint(got, c.point) {\n\t\t\t\tt.Fatalf(\"Wanted %v Point, but got %v\", c.point, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestMinutesInRadians(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tangle float64\n\t}{\n\t\t{simpleTime(0, 30, 0), math.Pi},\n\t\t{simpleTime(0, 0, 7), 7 * (math.Pi / (30 * 60))},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := minutesInRadians(c.time)\n\t\t\tif got != c.angle {\n\t\t\t\tt.Fatalf(\"Wanted %v radians, but got %v\", c.angle, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc roughlyEqualFloat64(a, b float64) bool {\n\tconst equalityThreshold = 1e-7\n\treturn math.Abs(a-b) < equalityThreshold\n}\n\nfunc roughlyEqualPoint(a, b Point) bool {\n\treturn roughlyEqualFloat64(a.X, b.X) &&\n\t\troughlyEqualFloat64(a.Y, b.Y)\n}\n\nfunc simpleTime(hours, minutes, seconds int) time.Time {\n\treturn time.Date(312, time.October, 28, hours, minutes, seconds, 0, time.UTC)\n}\n\nfunc testName(t time.Time) string {\n\treturn t.Format(\"15:04:05\")\n}\n"
  },
  {
    "path": "math/v8/clockface/svgWriter.go",
    "content": "package clockface\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"time\"\n)\n\nconst (\n\tsecondHandLength = 90\n\tclockCentreX     = 150\n\tclockCentreY     = 150\n)\n\n// SVGWriter writes an SVG representation of an analogue clock, showing the time t, to the writer w.\nfunc SVGWriter(w io.Writer, t time.Time) {\n\tio.WriteString(w, svgStart)\n\tio.WriteString(w, bezel)\n\tsecondHand(w, t)\n\tio.WriteString(w, svgEnd)\n}\n\nfunc secondHand(w io.Writer, t time.Time) {\n\tp := secondHandPoint(t)\n\tp = Point{p.X * secondHandLength, p.Y * secondHandLength}\n\tp = Point{p.X, -p.Y}\n\tp = Point{p.X + clockCentreX, p.Y + clockCentreY} //translate\n\tfmt.Fprintf(w, `<line x1=\"150\" y1=\"150\" x2=\"%.3f\" y2=\"%.3f\" style=\"fill:none;stroke:#f00;stroke-width:3px;\"/>`, p.X, p.Y)\n}\n\nconst svgStart = `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n<svg xmlns=\"http://www.w3.org/2000/svg\"\n     width=\"100%\"\n     height=\"100%\"\n     viewBox=\"0 0 300 300\"\n     version=\"2.0\">`\n\nconst bezel = `<circle cx=\"150\" cy=\"150\" r=\"100\" style=\"fill:#fff;stroke:#000;stroke-width:5px;\"/>`\n\nconst svgEnd = `</svg>`\n"
  },
  {
    "path": "math/v9/clockface/clockface/main.go",
    "content": "package main\n\nimport (\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/quii/learn-go-with-tests/math/v9/clockface\"\n)\n\nfunc main() {\n\tt := time.Now()\n\tclockface.SVGWriter(os.Stdout, t)\n}\n"
  },
  {
    "path": "math/v9/clockface/clockface.go",
    "content": "package clockface\n\nimport (\n\t\"math\"\n\t\"time\"\n)\n\n// A Point represents a two dimensional Cartesian coordinate.\ntype Point struct {\n\tX float64\n\tY float64\n}\n\nfunc secondsInRadians(t time.Time) float64 {\n\treturn (math.Pi / (30 / float64(t.Second())))\n}\n\nfunc secondHandPoint(t time.Time) Point {\n\treturn angleToPoint(secondsInRadians(t))\n}\n\nfunc minutesInRadians(t time.Time) float64 {\n\treturn (secondsInRadians(t) / 60) +\n\t\t(math.Pi / (30 / float64(t.Minute())))\n}\n\nfunc minuteHandPoint(t time.Time) Point {\n\treturn angleToPoint(minutesInRadians(t))\n}\n\nfunc angleToPoint(angle float64) Point {\n\tx := math.Sin(angle)\n\ty := math.Cos(angle)\n\n\treturn Point{x, y}\n}\n"
  },
  {
    "path": "math/v9/clockface/clockface_acceptance_test.go",
    "content": "package clockface_test\n\nimport (\n\t\"bytes\"\n\t\"encoding/xml\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/quii/learn-go-with-tests/math/v9/clockface\"\n)\n\ntype SVG struct {\n\tXMLName xml.Name `xml:\"svg\"`\n\tText    string   `xml:\",chardata\"`\n\tXmlns   string   `xml:\"xmlns,attr\"`\n\tWidth   string   `xml:\"width,attr\"`\n\tHeight  string   `xml:\"height,attr\"`\n\tViewBox string   `xml:\"viewBox,attr\"`\n\tVersion string   `xml:\"version,attr\"`\n\tCircle  Circle   `xml:\"circle\"`\n\tLine    []Line   `xml:\"line\"`\n}\n\ntype Line struct {\n\tX1 float64 `xml:\"x1,attr\"`\n\tY1 float64 `xml:\"y1,attr\"`\n\tX2 float64 `xml:\"x2,attr\"`\n\tY2 float64 `xml:\"y2,attr\"`\n}\n\ntype Circle struct {\n\tCx float64 `xml:\"cx,attr\"`\n\tCy float64 `xml:\"cy,attr\"`\n\tR  float64 `xml:\"r,attr\"`\n}\n\nfunc TestSVGWriterSecondHand(t *testing.T) {\n\tcases := []struct {\n\t\ttime time.Time\n\t\tline Line\n\t}{\n\t\t{\n\t\t\tsimpleTime(0, 0, 0),\n\t\t\tLine{150, 150, 150, 60},\n\t\t},\n\t\t{\n\t\t\tsimpleTime(0, 0, 30),\n\t\t\tLine{150, 150, 150, 240},\n\t\t},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tb := bytes.Buffer{}\n\t\t\tclockface.SVGWriter(&b, c.time)\n\n\t\t\tsvg := SVG{}\n\t\t\txml.Unmarshal(b.Bytes(), &svg)\n\n\t\t\tif !containsLine(c.line, svg.Line) {\n\t\t\t\tt.Errorf(\"Expected to find the second hand line %+v, in the SVG lines %+v\", c.line, svg.Line)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSVGWriterMinutedHand(t *testing.T) {\n\tcases := []struct {\n\t\ttime time.Time\n\t\tline Line\n\t}{\n\t\t{\n\t\t\tsimpleTime(0, 0, 0),\n\t\t\tLine{150, 150, 150, 70},\n\t\t},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tb := bytes.Buffer{}\n\t\t\tclockface.SVGWriter(&b, c.time)\n\n\t\t\tsvg := SVG{}\n\t\t\txml.Unmarshal(b.Bytes(), &svg)\n\n\t\t\tif !containsLine(c.line, svg.Line) {\n\t\t\t\tt.Errorf(\"Expected to find the minute hand line %+v, in the SVG lines %+v\", c.line, svg.Line)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc containsLine(l Line, ls []Line) bool {\n\tfor _, line := range ls {\n\t\tif line == l {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc simpleTime(hours, minutes, seconds int) time.Time {\n\treturn time.Date(312, time.October, 28, hours, minutes, seconds, 0, time.UTC)\n}\n\nfunc testName(t time.Time) string {\n\treturn t.Format(\"15:04:05\")\n}\n"
  },
  {
    "path": "math/v9/clockface/clockface_test.go",
    "content": "package clockface\n\nimport (\n\t\"math\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestSecondsInRadians(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tangle float64\n\t}{\n\t\t{simpleTime(0, 0, 30), math.Pi},\n\t\t{simpleTime(0, 0, 0), 0},\n\t\t{simpleTime(0, 0, 45), (math.Pi / 2) * 3},\n\t\t{simpleTime(0, 0, 7), (math.Pi / 30) * 7},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := secondsInRadians(c.time)\n\t\t\tif got != c.angle {\n\t\t\t\tt.Fatalf(\"Wanted %v radians, but got %v\", c.angle, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSecondHandPoint(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tpoint Point\n\t}{\n\t\t{simpleTime(0, 0, 30), Point{0, -1}},\n\t\t{simpleTime(0, 0, 45), Point{-1, 0}},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := secondHandPoint(c.time)\n\t\t\tif !roughlyEqualPoint(got, c.point) {\n\t\t\t\tt.Fatalf(\"Wanted %v Point, but got %v\", c.point, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestMinutesInRadians(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tangle float64\n\t}{\n\t\t{simpleTime(0, 30, 0), math.Pi},\n\t\t{simpleTime(0, 0, 7), 7 * (math.Pi / (30 * 60))},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := minutesInRadians(c.time)\n\t\t\tif got != c.angle {\n\t\t\t\tt.Fatalf(\"Wanted %v radians, but got %v\", c.angle, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestMinuteHandPoint(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tpoint Point\n\t}{\n\t\t{simpleTime(0, 30, 0), Point{0, -1}},\n\t\t{simpleTime(0, 45, 0), Point{-1, 0}},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := minuteHandPoint(c.time)\n\t\t\tif !roughlyEqualPoint(got, c.point) {\n\t\t\t\tt.Fatalf(\"Wanted %v Point, but got %v\", c.point, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc roughlyEqualFloat64(a, b float64) bool {\n\tconst equalityThreshold = 1e-7\n\treturn math.Abs(a-b) < equalityThreshold\n}\n\nfunc roughlyEqualPoint(a, b Point) bool {\n\treturn roughlyEqualFloat64(a.X, b.X) &&\n\t\troughlyEqualFloat64(a.Y, b.Y)\n}\n\nfunc simpleTime(hours, minutes, seconds int) time.Time {\n\treturn time.Date(312, time.October, 28, hours, minutes, seconds, 0, time.UTC)\n}\n\nfunc testName(t time.Time) string {\n\treturn t.Format(\"15:04:05\")\n}\n"
  },
  {
    "path": "math/v9/clockface/svgWriter.go",
    "content": "package clockface\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"time\"\n)\n\nconst (\n\tsecondHandLength = 90\n\tminuteHandLength = 80\n\tclockCentreX     = 150\n\tclockCentreY     = 150\n)\n\n// SVGWriter writes an SVG representation of an analogue clock, showing the time t, to the writer w.\nfunc SVGWriter(w io.Writer, t time.Time) {\n\tio.WriteString(w, svgStart)\n\tio.WriteString(w, bezel)\n\tsecondHand(w, t)\n\tminuteHand(w, t)\n\tio.WriteString(w, svgEnd)\n}\n\nfunc secondHand(w io.Writer, t time.Time) {\n\tp := makeHand(secondHandPoint(t), secondHandLength)\n\tfmt.Fprintf(w, `<line x1=\"150\" y1=\"150\" x2=\"%.3f\" y2=\"%.3f\" style=\"fill:none;stroke:#f00;stroke-width:3px;\"/>`, p.X, p.Y)\n}\n\nfunc minuteHand(w io.Writer, t time.Time) {\n\tp := makeHand(minuteHandPoint(t), minuteHandLength)\n\tfmt.Fprintf(w, `<line x1=\"150\" y1=\"150\" x2=\"%.3f\" y2=\"%.3f\" style=\"fill:none;stroke:#000;stroke-width:3px;\"/>`, p.X, p.Y)\n}\n\nfunc makeHand(p Point, length float64) Point {\n\tp = Point{p.X * length, p.Y * length}\n\tp = Point{p.X, -p.Y}\n\treturn Point{p.X + clockCentreX, p.Y + clockCentreY}\n}\n\nconst svgStart = `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n<svg xmlns=\"http://www.w3.org/2000/svg\"\n     width=\"100%\"\n     height=\"100%\"\n     viewBox=\"0 0 300 300\"\n     version=\"2.0\">`\n\nconst bezel = `<circle cx=\"150\" cy=\"150\" r=\"100\" style=\"fill:#fff;stroke:#000;stroke-width:5px;\"/>`\n\nconst svgEnd = `</svg>`\n"
  },
  {
    "path": "math/vFinal/clockface/clockface/main.go",
    "content": "// Writes an SVG clockface of the current time to Stdout.\npackage main\n\nimport (\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/quii/learn-go-with-tests/math/vFinal/clockface/svg\"\n)\n\nfunc main() {\n\tt := time.Now()\n\tsvg.Write(os.Stdout, t)\n}\n"
  },
  {
    "path": "math/vFinal/clockface/clockface.go",
    "content": "// Package clockface provides functions that calculate the positions of the hands.\n// of an analogue clock,.\npackage clockface\n\nimport (\n\t\"math\"\n\t\"time\"\n)\n\nconst (\n\tsecondsInHalfClock = 30\n\tsecondsInClock     = 2 * secondsInHalfClock\n\tminutesInHalfClock = 30\n\tminutesInClock     = 2 * minutesInHalfClock\n\thoursInHalfClock   = 6\n\thoursInClock       = 2 * hoursInHalfClock\n)\n\n// A Point is a Cartesian coordinate. They are used in the package.\n// to represent the unit vector from the origin of a clock hand.\ntype Point struct {\n\tX float64\n\tY float64\n}\n\n// SecondsInRadians returns the angle of the second hand from 12 o'clock in radians.\nfunc SecondsInRadians(t time.Time) float64 {\n\treturn (math.Pi / (secondsInHalfClock / float64(t.Second())))\n}\n\n// SecondHandPoint is the unit vector of the second hand at time `t`,.\n// represented a Point.\nfunc SecondHandPoint(t time.Time) Point {\n\treturn angleToPoint(SecondsInRadians(t))\n}\n\n// MinutesInRadians returns the angle of the minute hand from 12 o'clock in radians.\nfunc MinutesInRadians(t time.Time) float64 {\n\treturn (SecondsInRadians(t) / minutesInClock) +\n\t\t(math.Pi / (minutesInHalfClock / float64(t.Minute())))\n}\n\n// MinuteHandPoint is the unit vector of the minute hand at time `t`,.\n// represented a Point.\nfunc MinuteHandPoint(t time.Time) Point {\n\treturn angleToPoint(MinutesInRadians(t))\n}\n\n// HoursInRadians returns the angle of the hour hand from 12 o'clock in radians.\nfunc HoursInRadians(t time.Time) float64 {\n\treturn (MinutesInRadians(t) / hoursInClock) +\n\t\t(math.Pi / (hoursInHalfClock / float64(t.Hour()%hoursInClock)))\n}\n\n// HourHandPoint is the unit vector of the hour hand at time `t`,.\n// represented a Point.\nfunc HourHandPoint(t time.Time) Point {\n\treturn angleToPoint(HoursInRadians(t))\n}\n\nfunc angleToPoint(angle float64) Point {\n\tx := math.Sin(angle)\n\ty := math.Cos(angle)\n\n\treturn Point{x, y}\n}\n"
  },
  {
    "path": "math/vFinal/clockface/clockface_test.go",
    "content": "package clockface_test\n\nimport (\n\t\"math\"\n\t\"testing\"\n\t\"time\"\n\n\t. \"github.com/quii/learn-go-with-tests/math/vFinal/clockface\"\n)\n\nfunc TestSecondsInRadians(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tangle float64\n\t}{\n\t\t{simpleTime(0, 0, 30), math.Pi},\n\t\t{simpleTime(0, 0, 0), 0},\n\t\t{simpleTime(0, 0, 45), (math.Pi / 2) * 3},\n\t\t{simpleTime(0, 0, 7), (math.Pi / 30) * 7},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := SecondsInRadians(c.time)\n\t\t\tif !roughlyEqualFloat64(got, c.angle) {\n\t\t\t\tt.Fatalf(\"Wanted %v radians, but got %v\", c.angle, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSecondHandPoint(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tpoint Point\n\t}{\n\t\t{simpleTime(0, 0, 30), Point{X: 0, Y: -1}},\n\t\t{simpleTime(0, 0, 45), Point{X: -1, Y: 0}},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := SecondHandPoint(c.time)\n\t\t\tif !roughlyEqualPoint(got, c.point) {\n\t\t\t\tt.Fatalf(\"Wanted %v Point, but got %v\", c.point, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestMinutesInRadians(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tangle float64\n\t}{\n\t\t{simpleTime(0, 30, 0), math.Pi},\n\t\t{simpleTime(0, 0, 7), 7 * (math.Pi / (30 * 60))},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := MinutesInRadians(c.time)\n\t\t\tif !roughlyEqualFloat64(got, c.angle) {\n\t\t\t\tt.Fatalf(\"Wanted %v radians, but got %v\", c.angle, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestMinuteHandPoint(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tpoint Point\n\t}{\n\t\t{simpleTime(0, 30, 0), Point{X: 0, Y: -1}},\n\t\t{simpleTime(0, 45, 0), Point{X: -1, Y: 0}},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := MinuteHandPoint(c.time)\n\t\t\tif !roughlyEqualPoint(got, c.point) {\n\t\t\t\tt.Fatalf(\"Wanted %v Point, but got %v\", c.point, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestHoursInRadians(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tangle float64\n\t}{\n\t\t{simpleTime(6, 0, 0), math.Pi},\n\t\t{simpleTime(0, 0, 0), 0},\n\t\t{simpleTime(21, 0, 0), math.Pi * 1.5},\n\t\t{simpleTime(0, 1, 30), math.Pi / ((6 * 60 * 60) / 90)},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := HoursInRadians(c.time)\n\t\t\tif !roughlyEqualFloat64(got, c.angle) {\n\t\t\t\tt.Fatalf(\"Wanted %v radians, but got %v\", c.angle, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestHourHandPoint(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tpoint Point\n\t}{\n\t\t{simpleTime(6, 0, 0), Point{X: 0, Y: -1}},\n\t\t{simpleTime(21, 0, 0), Point{X: -1, Y: 0}},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := HourHandPoint(c.time)\n\t\t\tif !roughlyEqualPoint(got, c.point) {\n\t\t\t\tt.Fatalf(\"Wanted %v Point, but got %v\", c.point, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc roughlyEqualFloat64(a, b float64) bool {\n\tconst equalityThreshold = 1e-7\n\treturn math.Abs(a-b) < equalityThreshold\n}\n\nfunc roughlyEqualPoint(a, b Point) bool {\n\treturn roughlyEqualFloat64(a.X, b.X) &&\n\t\troughlyEqualFloat64(a.Y, b.Y)\n}\n\nfunc simpleTime(hours, minutes, seconds int) time.Time {\n\treturn time.Date(312, time.October, 28, hours, minutes, seconds, 0, time.UTC)\n}\n\nfunc testName(t time.Time) string {\n\treturn t.Format(\"15:04:05\")\n}\n"
  },
  {
    "path": "math/vFinal/clockface/svg/svg.go",
    "content": "// Package svg produces an SVG clockface representation of a time.\npackage svg\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"time\"\n\n\tcf \"github.com/quii/learn-go-with-tests/math/vFinal/clockface\"\n)\n\nconst (\n\tsecondHandLength = 90\n\tminuteHandLength = 80\n\thourHandLength   = 50\n\tclockCentreX     = 150\n\tclockCentreY     = 150\n)\n\n// Write writes an SVG representation of an analogue clock, showing the time t, to the writer w.\nfunc Write(w io.Writer, t time.Time) {\n\tio.WriteString(w, svgStart)\n\tio.WriteString(w, bezel)\n\tsecondHand(w, t)\n\tminuteHand(w, t)\n\thourHand(w, t)\n\tio.WriteString(w, svgEnd)\n}\n\nfunc secondHand(w io.Writer, t time.Time) {\n\tp := makeHand(cf.SecondHandPoint(t), secondHandLength)\n\tfmt.Fprintf(w, `<line x1=\"150\" y1=\"150\" x2=\"%.3f\" y2=\"%.3f\" style=\"fill:none;stroke:#f00;stroke-width:3px;\"/>`, p.X, p.Y)\n}\n\nfunc minuteHand(w io.Writer, t time.Time) {\n\tp := makeHand(cf.MinuteHandPoint(t), minuteHandLength)\n\tfmt.Fprintf(w, `<line x1=\"150\" y1=\"150\" x2=\"%.3f\" y2=\"%.3f\" style=\"fill:none;stroke:#000;stroke-width:3px;\"/>`, p.X, p.Y)\n}\n\nfunc hourHand(w io.Writer, t time.Time) {\n\tp := makeHand(cf.HourHandPoint(t), hourHandLength)\n\tfmt.Fprintf(w, `<line x1=\"150\" y1=\"150\" x2=\"%.3f\" y2=\"%.3f\" style=\"fill:none;stroke:#000;stroke-width:3px;\"/>`, p.X, p.Y)\n}\n\nfunc makeHand(p cf.Point, length float64) cf.Point {\n\tp = cf.Point{X: p.X * length, Y: p.Y * length}\n\tp = cf.Point{X: p.X, Y: -p.Y}\n\treturn cf.Point{X: p.X + clockCentreX, Y: p.Y + clockCentreY}\n}\n\nconst svgStart = `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n<svg xmlns=\"http://www.w3.org/2000/svg\"\n     width=\"100%\"\n     height=\"100%\"\n     viewBox=\"0 0 300 300\"\n     version=\"2.0\">`\n\nconst bezel = `<circle cx=\"150\" cy=\"150\" r=\"100\" style=\"fill:#fff;stroke:#000;stroke-width:5px;\"/>`\n\nconst svgEnd = `</svg>`\n"
  },
  {
    "path": "math/vFinal/clockface/svg/svg_test.go",
    "content": "package svg_test\n\nimport (\n\t\"bytes\"\n\t\"encoding/xml\"\n\t\"testing\"\n\t\"time\"\n\n\t. \"github.com/quii/learn-go-with-tests/math/vFinal/clockface/svg\"\n)\n\ntype SVG struct {\n\tXMLName xml.Name `xml:\"svg\"`\n\tText    string   `xml:\",chardata\"`\n\tXmlns   string   `xml:\"xmlns,attr\"`\n\tWidth   string   `xml:\"width,attr\"`\n\tHeight  string   `xml:\"height,attr\"`\n\tViewBox string   `xml:\"viewBox,attr\"`\n\tVersion string   `xml:\"version,attr\"`\n\tCircle  Circle   `xml:\"circle\"`\n\tLine    []Line   `xml:\"line\"`\n}\n\ntype Line struct {\n\tX1 float64 `xml:\"x1,attr\"`\n\tY1 float64 `xml:\"y1,attr\"`\n\tX2 float64 `xml:\"x2,attr\"`\n\tY2 float64 `xml:\"y2,attr\"`\n}\n\ntype Circle struct {\n\tCx float64 `xml:\"cx,attr\"`\n\tCy float64 `xml:\"cy,attr\"`\n\tR  float64 `xml:\"r,attr\"`\n}\n\nfunc TestSVGWriterSecondHand(t *testing.T) {\n\tcases := []struct {\n\t\ttime time.Time\n\t\tline Line\n\t}{\n\t\t{\n\t\t\tsimpleTime(0, 0, 0),\n\t\t\tLine{150, 150, 150, 60},\n\t\t},\n\t\t{\n\t\t\tsimpleTime(0, 0, 30),\n\t\t\tLine{150, 150, 150, 240},\n\t\t},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tb := bytes.Buffer{}\n\t\t\tWrite(&b, c.time)\n\n\t\t\tsvg := SVG{}\n\t\t\txml.Unmarshal(b.Bytes(), &svg)\n\n\t\t\tif !containsLine(c.line, svg.Line) {\n\t\t\t\tt.Errorf(\"Expected to find the second hand line %+v, in the SVG lines %+v\", c.line, svg.Line)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSVGWriterMinutedHand(t *testing.T) {\n\tcases := []struct {\n\t\ttime time.Time\n\t\tline Line\n\t}{\n\t\t{\n\t\t\tsimpleTime(0, 0, 0),\n\t\t\tLine{150, 150, 150, 70},\n\t\t},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tb := bytes.Buffer{}\n\t\t\tWrite(&b, c.time)\n\n\t\t\tsvg := SVG{}\n\t\t\txml.Unmarshal(b.Bytes(), &svg)\n\n\t\t\tif !containsLine(c.line, svg.Line) {\n\t\t\t\tt.Errorf(\"Expected to find the minute hand line %+v, in the SVG lines %+v\", c.line, svg.Line)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSVGWriterHourHand(t *testing.T) {\n\tcases := []struct {\n\t\ttime time.Time\n\t\tline Line\n\t}{\n\t\t{\n\t\t\tsimpleTime(6, 0, 0),\n\t\t\tLine{150, 150, 150, 200},\n\t\t},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tb := bytes.Buffer{}\n\t\t\tWrite(&b, c.time)\n\n\t\t\tsvg := SVG{}\n\t\t\txml.Unmarshal(b.Bytes(), &svg)\n\n\t\t\tif !containsLine(c.line, svg.Line) {\n\t\t\t\tt.Errorf(\"Expected to find the hour hand line %+v, in the SVG lines %+v\", c.line, svg.Line)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc containsLine(l Line, ls []Line) bool {\n\tfor _, line := range ls {\n\t\tif line == l {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc simpleTime(hours, minutes, seconds int) time.Time {\n\treturn time.Date(312, time.October, 28, hours, minutes, seconds, 0, time.UTC)\n}\n\nfunc testName(t time.Time) string {\n\treturn t.Format(\"15:04:05\")\n}\n"
  },
  {
    "path": "math.md",
    "content": "# Maths\n\n[**You can find all the code for this chapter here**](https://github.com/quii/learn-go-with-tests/tree/main/math)\n\nFor all the power of modern computers to perform huge sums at lightning speed, the average developer rarely uses any mathematics to do their job. But not today! Today we'll use mathematics to solve a _real_ problem. And not boring mathematics - we're going to use trigonometry and vectors and all sorts of stuff that you always said you'd never have to use after highschool.\n\n## The Problem\n\nYou want to make an SVG of a clock. Not a digital clock - no, that would be easy - an _analogue_ clock, with hands. You're not looking for anything fancy, just a nice function that takes a `Time` from the `time` package and spits out an SVG of a clock with all the hands - hour, minute and second - pointing in the right direction. How hard can that be?\n\nFirst we're going to need an SVG of a clock for us to play with. SVGs are a fantastic image format to manipulate programmatically because they're written as a series of shapes, described in XML. So this clock:\n\n![an svg of a clock](.gitbook/assets/example_clock.svg)\n\nis described like this:\n\n```xml\n<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n<svg xmlns=\"http://www.w3.org/2000/svg\"\n     width=\"100%\"\n     height=\"100%\"\n     viewBox=\"0 0 300 300\"\n     version=\"2.0\">\n\n  <!-- bezel -->\n  <circle cx=\"150\" cy=\"150\" r=\"100\" style=\"fill:#fff;stroke:#000;stroke-width:5px;\"/>\n\n  <!-- hour hand -->\n  <line x1=\"150\" y1=\"150\" x2=\"114.150000\" y2=\"132.260000\"\n        style=\"fill:none;stroke:#000;stroke-width:7px;\"/>\n\n  <!-- minute hand -->\n  <line x1=\"150\" y1=\"150\" x2=\"101.290000\" y2=\"99.730000\"\n        style=\"fill:none;stroke:#000;stroke-width:7px;\"/>\n\n  <!-- second hand -->\n  <line x1=\"150\" y1=\"150\" x2=\"77.190000\" y2=\"202.900000\"\n        style=\"fill:none;stroke:#f00;stroke-width:3px;\"/>\n</svg>\n```\n\nIt's a circle with three lines, each of the lines starting in the middle of the circle (x=150, y=150), and ending some distance away.\n\nSo what we're going to do is reconstruct the above somehow, but change the lines so they point in the appropriate directions for a given time.\n\n## An Acceptance Test\n\nBefore we get too stuck in, lets think about an acceptance test.\n\nWait, you don't know what an acceptance test is yet. Look, let me try to explain.\n\nLet me ask you: what does winning look like? How do we know we've finished work? TDD provides a good way of knowing when you've finished: when the test passes. Sometimes it's nice - actually, almost all of the time it's nice - to write a test that tells you when you've finished writing the whole usable feature. Not just a test that tells you that a particular function is working in the way you expect, but a test that tells you that the whole thing you're trying to achieve - the 'feature' - is complete.\n\nThese tests are sometimes called 'acceptance tests', sometimes called 'feature tests'. The idea is that you write a really high level test to describe what you're trying to achieve - a user clicks a button on a website, and they see a complete list of the Pokémon they've caught, for instance. When we've written that test, we can then write more tests - unit tests - that build towards a working system that will pass the acceptance test. So for our example these tests might be about rendering a webpage with a button, testing route handlers on a web server, performing database look ups, etc. All of these things will be TDD'd, and all of them will go towards making the original acceptance test pass.\n\nSomething like this _classic_ picture by Nat Pryce and Steve Freeman\n\n![Outside-in feedback loops in TDD](.gitbook/assets/TDD-outside-in.jpg)\n\nAnyway, let's try and write that acceptance test - the one that will let us know when we're done.\n\nWe've got an example clock, so let's think about what the important parameters are going to be.\n\n```\n<line x1=\"150\" y1=\"150\" x2=\"114.150000\" y2=\"132.260000\"\n        style=\"fill:none;stroke:#000;stroke-width:7px;\"/>\n```\n\nThe centre of the clock (the attributes `x1` and `y1` for this line) is the same for each hand of the clock. The numbers that need to change for each hand of the clock - the parameters to whatever builds the SVG - are the `x2` and `y2` attributes. We'll need an X and a Y for each of the hands of the clock.\n\nI _could_ think about more parameters - the radius of the clockface circle, the size of the SVG, the colours of the hands, their shape, etc... but it's better to start off by solving a simple, concrete problem with a simple, concrete solution, and then to start adding parameters to make it generalised.\n\nSo we'll say that\n\n* every clock has a centre of (150, 150)\n* the hour hand is 50 long\n* the minute hand is 80 long\n* the second hand is 90 long.\n\nA thing to note about SVGs: the origin - point (0,0) - is at the _top left_ hand corner, not the _bottom left_ as we might expect. It'll be important to remember this when we're working out where what numbers to plug in to our lines.\n\nFinally, I'm not deciding _how_ to construct the SVG - we could use a template from the [`text/template`](https://golang.org/pkg/text/template/) package, or we could just send bytes into a `bytes.Buffer` or a writer. But we know we'll need those numbers, so let's focus on testing something that creates them.\n\n### Write the test first\n\nSo my first test looks like this:\n\n```go\npackage clockface_test\n\nimport (\n\t\"projectpath/clockface\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestSecondHandAtMidnight(t *testing.T) {\n\ttm := time.Date(1337, time.January, 1, 0, 0, 0, 0, time.UTC)\n\n\twant := clockface.Point{X: 150, Y: 150 - 90}\n\tgot := clockface.SecondHand(tm)\n\n\tif got != want {\n\t\tt.Errorf(\"Got %v, wanted %v\", got, want)\n\t}\n}\n```\n\nRemember how SVGs plot their coordinates from the top left hand corner? To place the second hand at midnight we expect that it hasn't moved from the centre of the clockface on the X axis - still 150 - and the Y axis is the length of the hand 'up' from the centre; 150 minus 90.\n\n### Try to run the test\n\nThis drives out the expected failures around the missing functions and types:\n\n```\n--- FAIL: TestSecondHandAtMidnight (0.00s)\n./clockface_test.go:13:10: undefined: clockface.Point\n./clockface_test.go:14:9: undefined: clockface.SecondHand\n```\n\nSo a `Point` where the tip of the second hand should go, and a function to get it.\n\n### Write the minimal amount of code for the test to run and check the failing test output\n\nLet's implement those types to get the code to compile\n\n```go\npackage clockface\n\nimport \"time\"\n\n// A Point represents a two-dimensional Cartesian coordinate\ntype Point struct {\n\tX float64\n\tY float64\n}\n\n// SecondHand is the unit vector of the second hand of an analogue clock at time `t`\n// represented as a Point.\nfunc SecondHand(t time.Time) Point {\n\treturn Point{}\n}\n```\n\nand now we get:\n\n```\n--- FAIL: TestSecondHandAtMidnight (0.00s)\n    clockface_test.go:17: Got {0 0}, wanted {150 60}\nFAIL\nexit status 1\nFAIL\tlearn-go-with-tests/math/clockface\t0.006s\n```\n\n### Write enough code to make it pass\n\nWhen we get the expected failure, we can fill in the return value of `SecondHand`:\n\n```go\n// SecondHand is the unit vector of the second hand of an analogue clock at time `t`\n// represented as a Point.\nfunc SecondHand(t time.Time) Point {\n\treturn Point{150, 60}\n}\n```\n\nBehold, a passing test.\n\n```\nPASS\nok  \t    clockface\t0.006s\n```\n\n### Refactor\n\nNo need to refactor yet - there's barely enough code!\n\n### Repeat for new requirements\n\nWe probably need to do some work here that doesn't just involve returning a clock that shows midnight for every time...\n\n### Write the test first\n\n```go\nfunc TestSecondHandAt30Seconds(t *testing.T) {\n\ttm := time.Date(1337, time.January, 1, 0, 0, 30, 0, time.UTC)\n\n\twant := clockface.Point{X: 150, Y: 150 + 90}\n\tgot := clockface.SecondHand(tm)\n\n\tif got != want {\n\t\tt.Errorf(\"Got %v, wanted %v\", got, want)\n\t}\n}\n```\n\nSame idea, but now the second hand is pointing _downwards_ so we _add_ the length to the Y axis.\n\nThis will compile... but how do we make it pass?\n\n## Thinking time\n\nHow are we going to solve this problem?\n\nEvery minute the second hand goes through the same 60 states, pointing in 60 different directions. When it's 0 seconds it points to the top of the clockface, when it's 30 seconds it points to the bottom of the clockface. Easy enough.\n\nSo if I wanted to think about in what direction the second hand was pointing at, say, 37 seconds, I'd want the angle between 12 o'clock and 37/60ths around the circle. In degrees this is `(360 / 60 ) * 37 = 222`, but it's easier just to remember that it's `37/60` of a complete rotation.\n\nBut the angle is only half the story; we need to know the X and Y coordinate that the tip of the second hand is pointing at. How can we work that out?\n\n## Math\n\nImagine a circle with a radius of 1 drawn around the origin - the coordinate `0, 0`.\n\n![picture of the unit circle](.gitbook/assets/unit_circle.png)\n\nThis is called the 'unit circle' because... well, the radius is 1 unit!\n\nThe circumference of the circle is made of points on the grid - more coordinates. The x and y components of each of these coordinates form a triangle, the hypotenuse of which is always 1 (i.e. the radius of the circle).\n\n![picture of the unit circle with a point defined on the circumference](.gitbook/assets/unit_circle_coords.png)\n\nNow, trigonometry will let us work out the lengths of X and Y for each triangle if we know the angle they make with the origin. The X coordinate will be cos(a), and the Y coordinate will be sin(a), where a is the angle made between the line and the (positive) x axis.\n\n![picture of the unit circle with the x and y elements of a ray defined as cos(a) and sin(a) respectively, where a is the angle made by the ray with the x axis](<.gitbook/assets/unit_circle_params (1).png>)\n\n(If you don't believe this, [go and look at Wikipedia...](https://en.wikipedia.org/wiki/Sine#Unit_circle_definition))\n\nOne final twist - because we want to measure the angle from 12 o'clock rather than from the X axis (3 o'clock), we need to swap the axis around; now x = sin(a) and y = cos(a).\n\n![unit circle ray defined from by angle from y axis](.gitbook/assets/unit_circle_12_oclock.png)\n\nSo now we know how to get the angle of the second hand (1/60th of a circle for each second) and the X and Y coordinates. We'll need functions for both `sin` and `cos`.\n\n## `math`\n\nHappily the Go `math` package has both, with one small snag we'll need to get our heads around; if we look at the description of [`math.Cos`](https://golang.org/pkg/math/#Cos):\n\n> Cos returns the cosine of the radian argument x.\n\nIt wants the angle to be in radians. So what's a radian? Instead of defining the full turn of a circle to be made up of 360 degrees, we define a full turn as being 2π radians. There are good reasons to do this that we won't go in to.\n\nNow that we've done some reading, some learning and some thinking, we can write our next test.\n\n### Write the test first\n\nAll this maths is hard and confusing. I'm not confident I understand what's going on - so let's write a test! We don't need to solve the whole problem in one go - let's start off with working out the correct angle, in radians, for the second hand at a particular time.\n\nI'm going to _comment out_ the acceptance test that I was working on while I'm working on these tests - I don't want to get distracted by that test while I'm getting this one to pass.\n\n### A recap on packages\n\nAt the moment, our acceptance tests are in the `clockface_test` package. Our tests can be outside of the `clockface` package - as long as their name ends with `_test.go` they can be run.\n\nI'm going to write these radians tests _within_ the `clockface` package; they may never get exported, and they may get deleted (or moved) once I have a better grip on what's going on. I'll rename my acceptance test file to `clockface_acceptance_test.go`, so that I can create a _new_ file called `clockface_test` to test seconds in radians.\n\n```go\npackage clockface\n\nimport (\n\t\"math\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestSecondsInRadians(t *testing.T) {\n\tthirtySeconds := time.Date(312, time.October, 28, 0, 0, 30, 0, time.UTC)\n\twant := math.Pi\n\tgot := secondsInRadians(thirtySeconds)\n\n\tif want != got {\n\t\tt.Fatalf(\"Wanted %v radians, but got %v\", want, got)\n\t}\n}\n```\n\nHere we're testing that 30 seconds past the minute should put the second hand at halfway around the clock. And it's our first use of the `math` package! If a full turn of a circle is 2π radians, we know that halfway round should just be π radians. `math.Pi` provides us with a value for π.\n\n### Try to run the test\n\n```\n./clockface_test.go:12:9: undefined: secondsInRadians\n```\n\n### Write the minimal amount of code for the test to run and check the failing test output\n\n```go\nfunc secondsInRadians(t time.Time) float64 {\n\treturn 0\n}\n```\n\n```\nclockface_test.go:15: Wanted 3.141592653589793 radians, but got 0\n```\n\n### Write enough code to make it pass\n\n```go\nfunc secondsInRadians(t time.Time) float64 {\n\treturn math.Pi\n}\n```\n\n```\nPASS\nok  \tclockface\t0.011s\n```\n\n### Refactor\n\nNothing needs refactoring yet\n\n### Repeat for new requirements\n\nNow we can extend the test to cover a few more scenarios. I'm going to skip forward a bit and show some already refactored test code - it should be clear enough how I got where I want to.\n\n```go\nfunc TestSecondsInRadians(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tangle float64\n\t}{\n\t\t{simpleTime(0, 0, 30), math.Pi},\n\t\t{simpleTime(0, 0, 0), 0},\n\t\t{simpleTime(0, 0, 45), (math.Pi / 2) * 3},\n\t\t{simpleTime(0, 0, 7), (math.Pi / 30) * 7},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := secondsInRadians(c.time)\n\t\t\tif got != c.angle {\n\t\t\t\tt.Fatalf(\"Wanted %v radians, but got %v\", c.angle, got)\n\t\t\t}\n\t\t})\n\t}\n}\n```\n\nI added a couple of helper functions to make writing this table based test a little less tedious. `testName` converts a time into a digital watch format (HH:MM:SS), and `simpleTime` constructs a `time.Time` using only the parts we actually care about (again, hours, minutes and seconds). Here they are:\n\n```go\nfunc simpleTime(hours, minutes, seconds int) time.Time {\n\treturn time.Date(312, time.October, 28, hours, minutes, seconds, 0, time.UTC)\n}\n\nfunc testName(t time.Time) string {\n\treturn t.Format(\"15:04:05\")\n}\n```\n\nThese two functions should help make these tests (and future tests) a little easier to write and maintain.\n\nThis gives us some nice test output:\n\n```\nclockface_test.go:24: Wanted 0 radians, but got 3.141592653589793\n\nclockface_test.go:24: Wanted 4.71238898038469 radians, but got 3.141592653589793\n```\n\nTime to implement all of that maths stuff we were talking about above:\n\n```go\nfunc secondsInRadians(t time.Time) float64 {\n\treturn float64(t.Second()) * (math.Pi / 30)\n}\n```\n\nOne second is (2π / 60) radians... cancel out the 2 and we get π/30 radians. Multiply that by the number of seconds (as a `float64`) and we should now have all the tests passing...\n\n```\nclockface_test.go:24: Wanted 3.141592653589793 radians, but got 3.1415926535897936\n```\n\nWait, what?\n\n### Floats are horrible\n\nFloating point arithmetic is [notoriously inaccurate](https://0.30000000000000004.com/). Computers can only really handle integers, and rational numbers to some extent. Decimal numbers start to become inaccurate, especially when we factor them up and down as we are in the `secondsInRadians` function. By dividing `math.Pi` by 30 and then by multiplying it by 30 we've ended up with _a number that's no longer the same as `math.Pi`_.\n\nThere are two ways around this:\n\n1. Live with it\n2. Refactor our function by refactoring our equation\n\nNow (1) may not seem all that appealing, but it's often the only way to make floating point equality work. Being inaccurate by some infinitesimal fraction is frankly not going to matter for the purposes of drawing a clockface, so we could write a function that defines a 'close enough' equality for our angles. But there's a simple way we can get the accuracy back: we rearrange the equation so that we're no longer dividing down and then multiplying up. We can do it all by just dividing.\n\nSo instead of\n\n```\nnumberOfSeconds * π / 30\n```\n\nwe can write\n\n```\nπ / (30 / numberOfSeconds)\n```\n\nwhich is equivalent.\n\nIn Go:\n\n```go\nfunc secondsInRadians(t time.Time) float64 {\n\treturn (math.Pi / (30 / (float64(t.Second()))))\n}\n```\n\nAnd we get a pass.\n\n```\nPASS\nok      clockface     0.005s\n```\n\nIt should all look [something like this](https://github.com/quii/learn-go-with-tests/tree/main/math/v3/clockface).\n\n### A note on dividing by zero\n\nComputers often don't like dividing by zero because infinity is a bit strange.\n\nIn Go if you try to explicitly divide by zero you will get a compilation error.\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nfunc main() {\n\tfmt.Println(10.0 / 0.0) // fails to compile\n}\n```\n\nObviously the compiler can't always predict that you'll divide by zero, such as our `t.Second()`\n\nTry this\n\n```go\nfunc main() {\n\tfmt.Println(10.0 / zero())\n}\n\nfunc zero() float64 {\n\treturn 0.0\n}\n```\n\nIt will print `+Inf` (infinity). Dividing by +Inf seems to result in zero and we can see this with the following:\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"math\"\n)\n\nfunc main() {\n\tfmt.Println(secondsinradians())\n}\n\nfunc zero() float64 {\n\treturn 0.0\n}\n\nfunc secondsinradians() float64 {\n\treturn (math.Pi / (30 / (float64(zero()))))\n}\n```\n\n### Repeat for new requirements\n\nSo we've got the first part covered here - we know what angle the second hand will be pointing at in radians. Now we need to work out the coordinates.\n\nAgain, let's keep this as simple as possible and only work with the _unit circle_; the circle with a radius of 1. This means that our hands will all have a length of one but, on the bright side, it means that the maths will be easy for us to swallow.\n\n### Write the test first\n\n```go\nfunc TestSecondHandPoint(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tpoint Point\n\t}{\n\t\t{simpleTime(0, 0, 30), Point{0, -1}},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := secondHandPoint(c.time)\n\t\t\tif got != c.point {\n\t\t\t\tt.Fatalf(\"Wanted %v Point, but got %v\", c.point, got)\n\t\t\t}\n\t\t})\n\t}\n}\n```\n\n### Try to run the test\n\n```\n./clockface_test.go:40:11: undefined: secondHandPoint\n```\n\n### Write the minimal amount of code for the test to run and check the failing test output\n\n```go\nfunc secondHandPoint(t time.Time) Point {\n\treturn Point{}\n}\n```\n\n```\nclockface_test.go:42: Wanted {0 -1} Point, but got {0 0}\n```\n\n### Write enough code to make it pass\n\n```go\nfunc secondHandPoint(t time.Time) Point {\n\treturn Point{0, -1}\n}\n```\n\n```\nPASS\nok  \tclockface\t0.007s\n```\n\n### Repeat for new requirements\n\n```go\nfunc TestSecondHandPoint(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tpoint Point\n\t}{\n\t\t{simpleTime(0, 0, 30), Point{0, -1}},\n\t\t{simpleTime(0, 0, 45), Point{-1, 0}},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := secondHandPoint(c.time)\n\t\t\tif got != c.point {\n\t\t\t\tt.Fatalf(\"Wanted %v Point, but got %v\", c.point, got)\n\t\t\t}\n\t\t})\n\t}\n}\n```\n\n### Try to run the test\n\n```\nclockface_test.go:43: Wanted {-1 0} Point, but got {0 -1}\n```\n\n### Write enough code to make it pass\n\nRemember our unit circle picture?\n\n![picture of the unit circle with the x and y elements of a ray defined as cos(a) and sin(a) respectively, where a is the angle made by the ray with the x axis](<.gitbook/assets/unit_circle_params (1).png>)\n\nAlso recall that we want to measure the angle from 12 o'clock which is the Y axis instead of from the X axis which we would like measuring the angle between the second hand and 3 o'clock.\n\n![unit circle ray defined from by angle from y axis](.gitbook/assets/unit_circle_12_oclock.png)\n\nWe now want the equation that produces X and Y. Let's write it into seconds:\n\n```go\nfunc secondHandPoint(t time.Time) Point {\n\tangle := secondsInRadians(t)\n\tx := math.Sin(angle)\n\ty := math.Cos(angle)\n\n\treturn Point{x, y}\n}\n```\n\nNow we get\n\n```\nclockface_test.go:43: Wanted {0 -1} Point, but got {1.2246467991473515e-16 -1}\n\nclockface_test.go:43: Wanted {-1 0} Point, but got {-1 -1.8369701987210272e-16}\n```\n\nWait, what (again)? Looks like we've been cursed by the floats once more - both of those unexpected numbers are _infinitesimal_ - way down at the 16th decimal place. So again we can either choose to try to increase precision, or to just say that they're roughly equal and get on with our lives.\n\nOne option to increase the accuracy of these angles would be to use the rational type `Rat` from the `math/big` package. But given the objective is to draw an SVG and not land on the moon, I think we can live with a bit of fuzziness.\n\n```go\nfunc TestSecondHandPoint(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tpoint Point\n\t}{\n\t\t{simpleTime(0, 0, 30), Point{0, -1}},\n\t\t{simpleTime(0, 0, 45), Point{-1, 0}},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := secondHandPoint(c.time)\n\t\t\tif !roughlyEqualPoint(got, c.point) {\n\t\t\t\tt.Fatalf(\"Wanted %v Point, but got %v\", c.point, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc roughlyEqualFloat64(a, b float64) bool {\n\tconst equalityThreshold = 1e-7\n\treturn math.Abs(a-b) < equalityThreshold\n}\n\nfunc roughlyEqualPoint(a, b Point) bool {\n\treturn roughlyEqualFloat64(a.X, b.X) &&\n\t\troughlyEqualFloat64(a.Y, b.Y)\n}\n```\n\nWe've defined two functions to define approximate equality between two `Points` - they'll work if the X and Y elements are within 0.0000001 of each other. That's still pretty accurate.\n\nAnd now we get:\n\n```\nPASS\nok  \tclockface\t0.007s\n```\n\n### Refactor\n\nI'm still pretty happy with this.\n\nHere's [what it looks like now](https://github.com/quii/learn-go-with-tests/tree/main/math/v4/clockface)\n\n### Repeat for new requirements\n\nWell, saying _new_ isn't entirely accurate - really what we can do now is get that acceptance test passing! Let's remind ourselves of what it looks like:\n\n```go\nfunc TestSecondHandAt30Seconds(t *testing.T) {\n\ttm := time.Date(1337, time.January, 1, 0, 0, 30, 0, time.UTC)\n\n\twant := clockface.Point{X: 150, Y: 150 + 90}\n\tgot := clockface.SecondHand(tm)\n\n\tif got != want {\n\t\tt.Errorf(\"Got %v, wanted %v\", got, want)\n\t}\n}\n```\n\n### Try to run the test\n\n```\nclockface_acceptance_test.go:28: Got {150 60}, wanted {150 240}\n```\n\n### Write enough code to make it pass\n\nWe need to do three things to convert our unit vector into a point on the SVG:\n\n1. Scale it to the length of the hand\n2. Flip it over the X axis to account for the SVG having an origin in the top left hand corner\n3. Translate it to the right position (so that it's coming from an origin of (150,150))\n\nFun times!\n\n```go\n// SecondHand is the unit vector of the second hand of an analogue clock at time `t`\n// represented as a Point.\nfunc SecondHand(t time.Time) Point {\n\tp := secondHandPoint(t)\n\tp = Point{p.X * 90, p.Y * 90}   // scale\n\tp = Point{p.X, -p.Y}            // flip\n\tp = Point{p.X + 150, p.Y + 150} // translate\n\treturn p\n}\n```\n\nScale, flip, and translate in exactly that order. Hooray maths!\n\n```\nPASS\nok  \tclockface\t0.007s\n```\n\n### Refactor\n\nThere's a few magic numbers here that should get pulled out as constants, so let's do that\n\n```go\nconst secondHandLength = 90\nconst clockCentreX = 150\nconst clockCentreY = 150\n\n// SecondHand is the unit vector of the second hand of an analogue clock at time `t`\n// represented as a Point.\nfunc SecondHand(t time.Time) Point {\n\tp := secondHandPoint(t)\n\tp = Point{p.X * secondHandLength, p.Y * secondHandLength}\n\tp = Point{p.X, -p.Y}\n\tp = Point{p.X + clockCentreX, p.Y + clockCentreY} //translate\n\treturn p\n}\n```\n\n## Draw the clock\n\nWell... the second hand anyway...\n\nLet's do this thing - because there's nothing worse than not delivering some value when it's just sitting there waiting to get out into the world to dazzle people. Let's draw a second hand!\n\nWe're going to stick a new directory under our main `clockface` package directory, called (confusingly), `clockface`. In there we'll put the `main` package that will create the binary that will build an SVG:\n\n```\n|-- clockface\n|       |-- main.go\n|-- clockface.go\n|-- clockface_acceptance_test.go\n|-- clockface_test.go\n```\n\nInside `main.go`, you'll start with this code but change the import for the clockface package to point at your own version:\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"time\"\n\n\t\"learn-go-with-tests/math/clockface\" // REPLACE THIS!\n)\n\nfunc main() {\n\tt := time.Now()\n\tsh := clockface.SecondHand(t)\n\tio.WriteString(os.Stdout, svgStart)\n\tio.WriteString(os.Stdout, bezel)\n\tio.WriteString(os.Stdout, secondHandTag(sh))\n\tio.WriteString(os.Stdout, svgEnd)\n}\n\nfunc secondHandTag(p clockface.Point) string {\n\treturn fmt.Sprintf(`<line x1=\"150\" y1=\"150\" x2=\"%f\" y2=\"%f\" style=\"fill:none;stroke:#f00;stroke-width:3px;\"/>`, p.X, p.Y)\n}\n\nconst svgStart = `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n<svg xmlns=\"http://www.w3.org/2000/svg\"\n     width=\"100%\"\n     height=\"100%\"\n     viewBox=\"0 0 300 300\"\n     version=\"2.0\">`\n\nconst bezel = `<circle cx=\"150\" cy=\"150\" r=\"100\" style=\"fill:#fff;stroke:#000;stroke-width:5px;\"/>`\n\nconst svgEnd = `</svg>`\n```\n\nOh boy am I not trying to win any prizes for beautiful code with _this_ mess - but it does the job. It's writing an SVG out to `os.Stdout` - one string at a time.\n\nIf we build this\n\n```\ngo build\n```\n\nand run it, sending the output into a file\n\n```\n./clockface > clock.svg\n```\n\nWe should see something like\n\n![a clock with only a second hand](.gitbook/assets/clock.svg)\n\nAnd this is [how the code looks](https://github.com/quii/learn-go-with-tests/tree/main/math/v6/clockface).\n\n### Refactor\n\nThis stinks. Well, it doesn't quite _stink_ stink, but I'm not happy about it.\n\n1. That whole `SecondHand` function is _super_ tied to being an SVG... without mentioning SVGs or actually producing an SVG...\n2. ... while at the same time I'm not testing any of my SVG code.\n\nYeah, I guess I screwed up. This feels wrong. Let's try to recover with a more SVG-centric test.\n\nWhat are our options? Well, we could try testing that the characters spewing out of the `SVGWriter` contain things that look like the sort of SVG tag we're expecting for a particular time. For instance:\n\n```go\nfunc TestSVGWriterAtMidnight(t *testing.T) {\n\ttm := time.Date(1337, time.January, 1, 0, 0, 0, 0, time.UTC)\n\n\tvar b strings.Builder\n\tclockface.SVGWriter(&b, tm)\n\tgot := b.String()\n\n\twant := `<line x1=\"150\" y1=\"150\" x2=\"150\" y2=\"60\"`\n\n\tif !strings.Contains(got, want) {\n\t\tt.Errorf(\"Expected to find the second hand %v, in the SVG output %v\", want, got)\n\t}\n}\n```\n\nBut is this really an improvement?\n\nNot only will it still pass if I don't produce a valid SVG (as it's only testing that a string appears in the output), but it will also fail if I make the smallest, unimportant change to that string - if I add an extra space between the attributes, for instance.\n\nThe _biggest_ smell is that I'm testing a data structure - XML - by looking at its representation as a series of characters - as a string. This is _never_, _ever_ a good idea as it produces problems just like the ones I outline above: a test that's both too fragile and not sensitive enough. A test that's testing the wrong thing!\n\nSo the only solution is to test the output _as XML_. And to do that we'll need to parse it.\n\n## Parsing XML\n\n[`encoding/xml`](https://pkg.go.dev/encoding/xml) is the Go package that can handle all things to do with simple XML parsing.\n\nThe function [`xml.Unmarshal`](https://pkg.go.dev/encoding/xml#Unmarshal) takes a `[]byte` of XML data, and a pointer to a struct for it to get unmarshalled in to.\n\nSo we'll need a struct to unmarshall our XML into. We could spend some time working out what the correct names for all of the nodes and attributes, and how to write the correct structure but, happily, someone has written [`zek`](https://github.com/miku/zek) a program that will automate all of that hard work for us. Even better, there's an online version at [https://xml-to-go.github.io/](https://xml-to-go.github.io/). Just paste the SVG from the top of the file into one box and - bam - out pops:\n\n```go\ntype Svg struct {\n\tXMLName xml.Name `xml:\"svg\"`\n\tText    string   `xml:\",chardata\"`\n\tXmlns   string   `xml:\"xmlns,attr\"`\n\tWidth   string   `xml:\"width,attr\"`\n\tHeight  string   `xml:\"height,attr\"`\n\tViewBox string   `xml:\"viewBox,attr\"`\n\tVersion string   `xml:\"version,attr\"`\n\tCircle  struct {\n\t\tText  string `xml:\",chardata\"`\n\t\tCx    string `xml:\"cx,attr\"`\n\t\tCy    string `xml:\"cy,attr\"`\n\t\tR     string `xml:\"r,attr\"`\n\t\tStyle string `xml:\"style,attr\"`\n\t} `xml:\"circle\"`\n\tLine []struct {\n\t\tText  string `xml:\",chardata\"`\n\t\tX1    string `xml:\"x1,attr\"`\n\t\tY1    string `xml:\"y1,attr\"`\n\t\tX2    string `xml:\"x2,attr\"`\n\t\tY2    string `xml:\"y2,attr\"`\n\t\tStyle string `xml:\"style,attr\"`\n\t} `xml:\"line\"`\n}\n```\n\nWe could make adjustments to this if we needed to (like changing the name of the struct to `SVG`) but it's definitely good enough to start us off. Paste the struct into the `clockface_acceptance_test` file and let's write a test with it:\n\n```go\nfunc TestSVGWriterAtMidnight(t *testing.T) {\n\ttm := time.Date(1337, time.January, 1, 0, 0, 0, 0, time.UTC)\n\n\tb := bytes.Buffer{}\n\tclockface.SVGWriter(&b, tm)\n\n\tsvg := Svg{}\n\txml.Unmarshal(b.Bytes(), &svg)\n\n\tx2 := \"150\"\n\ty2 := \"60\"\n\n\tfor _, line := range svg.Line {\n\t\tif line.X2 == x2 && line.Y2 == y2 {\n\t\t\treturn\n\t\t}\n\t}\n\n\tt.Errorf(\"Expected to find the second hand with x2 of %+v and y2 of %+v, in the SVG output %v\", x2, y2, b.String())\n}\n```\n\nWe write the output of `clockface.SVGWriter` to a `bytes.Buffer` and then `Unmarshal` it into an `Svg`. We then look at each `Line` in the `Svg` to see if any of them have the expected `X2` and `Y2` values. If we get a match we return early (passing the test); if not we fail with a (hopefully) informative message.\n\n```sh\n./clockface_acceptance_test.go:41:2: undefined: clockface.SVGWriter\n```\n\nLooks like we'd better create `SVGWriter.go`...\n\n```go\npackage clockface\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"time\"\n)\n\nconst (\n\tsecondHandLength = 90\n\tclockCentreX     = 150\n\tclockCentreY     = 150\n)\n\n// SVGWriter writes an SVG representation of an analogue clock, showing the time t, to the writer w\nfunc SVGWriter(w io.Writer, t time.Time) {\n\tio.WriteString(w, svgStart)\n\tio.WriteString(w, bezel)\n\tsecondHand(w, t)\n\tio.WriteString(w, svgEnd)\n}\n\nfunc secondHand(w io.Writer, t time.Time) {\n\tp := secondHandPoint(t)\n\tp = Point{p.X * secondHandLength, p.Y * secondHandLength} // scale\n\tp = Point{p.X, -p.Y}                                      // flip\n\tp = Point{p.X + clockCentreX, p.Y + clockCentreY}         // translate\n\tfmt.Fprintf(w, `<line x1=\"150\" y1=\"150\" x2=\"%f\" y2=\"%f\" style=\"fill:none;stroke:#f00;stroke-width:3px;\"/>`, p.X, p.Y)\n}\n\nconst svgStart = `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n<svg xmlns=\"http://www.w3.org/2000/svg\"\n     width=\"100%\"\n     height=\"100%\"\n     viewBox=\"0 0 300 300\"\n     version=\"2.0\">`\n\nconst bezel = `<circle cx=\"150\" cy=\"150\" r=\"100\" style=\"fill:#fff;stroke:#000;stroke-width:5px;\"/>`\n\nconst svgEnd = `</svg>`\n```\n\nThe most beautiful SVG writer? No. But hopefully it'll do the job...\n\n```\nclockface_acceptance_test.go:56: Expected to find the second hand with x2 of 150 and y2 of 60, in the SVG output <?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n    <!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n    <svg xmlns=\"http://www.w3.org/2000/svg\"\n         width=\"100%\"\n         height=\"100%\"\n         viewBox=\"0 0 300 300\"\n         version=\"2.0\"><circle cx=\"150\" cy=\"150\" r=\"100\" style=\"fill:#fff;stroke:#000;stroke-width:5px;\"/><line x1=\"150\" y1=\"150\" x2=\"150.000000\" y2=\"60.000000\" style=\"fill:none;stroke:#f00;stroke-width:3px;\"/></svg>\n```\n\nOooops! The `%f` format directive is printing our coordinates to the default level of precision - six decimal places. We should be explicit as to what level of precision we're expecting for the coordinates. Let's say three decimal places.\n\n```go\n\tfmt.Fprintf(w, `<line x1=\"150\" y1=\"150\" x2=\"%.3f\" y2=\"%.3f\" style=\"fill:none;stroke:#f00;stroke-width:3px;\"/>`, p.X, p.Y)\n```\n\nAnd after we update our expectations in the test\n\n```go\n\tx2 := \"150.000\"\n\ty2 := \"60.000\"\n```\n\nWe get:\n\n```\nPASS\nok  \tclockface\t0.006s\n```\n\nWe can now shorten our `main` function:\n\n```go\npackage main\n\nimport (\n\t\"os\"\n\t\"time\"\n\n\t\"learn-go-with-tests/math/clockface\"\n)\n\nfunc main() {\n\tt := time.Now()\n\tclockface.SVGWriter(os.Stdout, t)\n}\n```\n\nThis is what [things should look like now](https://github.com/quii/learn-go-with-tests/tree/main/math/v7b/clockface).\n\nAnd we can write a test for another time following the same pattern, but not before...\n\n### Refactor\n\nThree things stick out:\n\n1. We're not really testing for all of the information we need to ensure is present - what about the `x1` values, for instance?\n2. Also, those attributes for `x1` etc. aren't really `strings` are they? They're numbers!\n3. Do I really care about the `style` of the hand? Or, for that matter, the empty `Text` node that's been generated by `zak`?\n\nWe can do better. Let's make a few adjustments to the `Svg` struct, and the tests, to sharpen everything up.\n\n```go\ntype SVG struct {\n\tXMLName xml.Name `xml:\"svg\"`\n\tXmlns   string   `xml:\"xmlns,attr\"`\n\tWidth   string   `xml:\"width,attr\"`\n\tHeight  string   `xml:\"height,attr\"`\n\tViewBox string   `xml:\"viewBox,attr\"`\n\tVersion string   `xml:\"version,attr\"`\n\tCircle  Circle   `xml:\"circle\"`\n\tLine    []Line   `xml:\"line\"`\n}\n\ntype Circle struct {\n\tCx float64 `xml:\"cx,attr\"`\n\tCy float64 `xml:\"cy,attr\"`\n\tR  float64 `xml:\"r,attr\"`\n}\n\ntype Line struct {\n\tX1 float64 `xml:\"x1,attr\"`\n\tY1 float64 `xml:\"y1,attr\"`\n\tX2 float64 `xml:\"x2,attr\"`\n\tY2 float64 `xml:\"y2,attr\"`\n}\n```\n\nHere I've\n\n* Made the important parts of the struct named types -- the `Line` and the `Circle`\n* Turned the numeric attributes into `float64`s instead of `string`s.\n* Deleted unused attributes like `Style` and `Text`\n* Renamed `Svg` to `SVG` because _it's the right thing to do_.\n\nThis will let us assert more precisely on the line we're looking for:\n\n```go\nfunc TestSVGWriterAtMidnight(t *testing.T) {\n\ttm := time.Date(1337, time.January, 1, 0, 0, 0, 0, time.UTC)\n\tb := bytes.Buffer{}\n\n\tclockface.SVGWriter(&b, tm)\n\n\tsvg := SVG{}\n\n\txml.Unmarshal(b.Bytes(), &svg)\n\n\twant := Line{150, 150, 150, 60}\n\n\tfor _, line := range svg.Line {\n\t\tif line == want {\n\t\t\treturn\n\t\t}\n\t}\n\n\tt.Errorf(\"Expected to find the second hand line %+v, in the SVG lines %+v\", want, svg.Line)\n}\n```\n\nFinally we can take a leaf out of the unit tests' tables, and we can write a helper function `containsLine(line Line, lines []Line) bool` to really make these tests shine:\n\n```go\nfunc TestSVGWriterSecondHand(t *testing.T) {\n\tcases := []struct {\n\t\ttime time.Time\n\t\tline Line\n\t}{\n\t\t{\n\t\t\tsimpleTime(0, 0, 0),\n\t\t\tLine{150, 150, 150, 60},\n\t\t},\n\t\t{\n\t\t\tsimpleTime(0, 0, 30),\n\t\t\tLine{150, 150, 150, 240},\n\t\t},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tb := bytes.Buffer{}\n\t\t\tclockface.SVGWriter(&b, c.time)\n\n\t\t\tsvg := SVG{}\n\t\t\txml.Unmarshal(b.Bytes(), &svg)\n\n\t\t\tif !containsLine(c.line, svg.Line) {\n\t\t\t\tt.Errorf(\"Expected to find the second hand line %+v, in the SVG lines %+v\", c.line, svg.Line)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc containsLine(l Line, ls []Line) bool {\n\tfor _, line := range ls {\n\t\tif line == l {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n```\n\nHere's what [it looks like](https://github.com/quii/learn-go-with-tests/tree/main/math/v7c/clockface)\n\nNow _that's_ what I call an acceptance test!\n\n### Write the test first\n\nSo that's the second hand done. Now let's get started on the minute hand.\n\n```go\nfunc TestSVGWriterMinuteHand(t *testing.T) {\n\tcases := []struct {\n\t\ttime time.Time\n\t\tline Line\n\t}{\n\t\t{\n\t\t\tsimpleTime(0, 0, 0),\n\t\t\tLine{150, 150, 150, 70},\n\t\t},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tb := bytes.Buffer{}\n\t\t\tclockface.SVGWriter(&b, c.time)\n\n\t\t\tsvg := SVG{}\n\t\t\txml.Unmarshal(b.Bytes(), &svg)\n\n\t\t\tif !containsLine(c.line, svg.Line) {\n\t\t\t\tt.Errorf(\"Expected to find the minute hand line %+v, in the SVG lines %+v\", c.line, svg.Line)\n\t\t\t}\n\t\t})\n\t}\n}\n```\n\n### Try to run the test\n\n```\nclockface_acceptance_test.go:87: Expected to find the minute hand line {X1:150 Y1:150 X2:150 Y2:70}, in the SVG lines [{X1:150 Y1:150 X2:150 Y2:60}]\n```\n\nWe'd better start building some other clock hands, Much in the same way as we produced the tests for the second hand, we can iterate to produce the following set of tests. Again we'll comment out our acceptance test while we get this working:\n\n```go\nfunc TestMinutesInRadians(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tangle float64\n\t}{\n\t\t{simpleTime(0, 30, 0), math.Pi},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := minutesInRadians(c.time)\n\t\t\tif got != c.angle {\n\t\t\t\tt.Fatalf(\"Wanted %v radians, but got %v\", c.angle, got)\n\t\t\t}\n\t\t})\n\t}\n}\n```\n\n### Try to run the test\n\n```\n./clockface_test.go:59:11: undefined: minutesInRadians\n```\n\n### Write the minimal amount of code for the test to run and check the failing test output\n\n```go\nfunc minutesInRadians(t time.Time) float64 {\n\treturn math.Pi\n}\n```\n\n### Repeat for new requirements\n\nWell, OK - now let's make ourselves do some _real_ work. We could model the minute hand as only moving every full minute - so that it 'jumps' from 30 to 31 minutes past without moving in between. But that would look a bit rubbish. What we want it to do is move a _tiny little bit_ every second.\n\n```go\nfunc TestMinutesInRadians(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tangle float64\n\t}{\n\t\t{simpleTime(0, 30, 0), math.Pi},\n\t\t{simpleTime(0, 0, 7), 7 * (math.Pi / (30 * 60))},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := minutesInRadians(c.time)\n\t\t\tif got != c.angle {\n\t\t\t\tt.Fatalf(\"Wanted %v radians, but got %v\", c.angle, got)\n\t\t\t}\n\t\t})\n\t}\n}\n```\n\nHow much is that tiny little bit? Well...\n\n* Sixty seconds in a minute\n* thirty minutes in a half turn of the circle (`math.Pi` radians)\n* so `30 * 60` seconds in a half turn.\n* So if the time is 7 seconds past the hour ...\n* ... we're expecting to see the minute hand at `7 * (math.Pi / (30 * 60))` radians past the 12.\n\n### Try to run the test\n\n```\nclockface_test.go:62: Wanted 0.012217304763960306 radians, but got 3.141592653589793\n```\n\n### Write enough code to make it pass\n\nIn the immortal words of Jennifer Aniston: [Here comes the science bit](https://www.youtube.com/watch?v=29Im23SPNok)\n\n```go\nfunc minutesInRadians(t time.Time) float64 {\n\treturn (secondsInRadians(t) / 60) +\n\t\t(math.Pi / (30 / float64(t.Minute())))\n}\n```\n\nRather than working out how far to push the minute hand around the clockface for every second from scratch, here we can just leverage the `secondsInRadians` function. For every second the minute hand will move 1/60th of the angle the second hand moves.\n\n```go\nsecondsInRadians(t) / 60\n```\n\nThen we just add on the movement for the minutes - similar to the movement of the second hand.\n\n```go\nmath.Pi / (30 / float64(t.Minute()))\n```\n\nAnd...\n\n```\nPASS\nok  \tclockface\t0.007s\n```\n\nNice and easy. This is what things [look like now](https://github.com/quii/learn-go-with-tests/tree/main/math/v8/clockface/clockface_acceptance_test.go)\n\n### Repeat for new requirements\n\nShould I add more cases to the `minutesInRadians` test? At the moment there are only two. How many cases do I need before I move on to the testing the `minuteHandPoint` function?\n\nOne of my favourite TDD quotes, often attributed to Kent Beck, is\n\n> Write tests until fear is transformed into boredom.\n\nAnd, frankly, I'm bored of testing that function. I'm confident I know how it works. So it's on to the next one.\n\n### Write the test first\n\n```go\nfunc TestMinuteHandPoint(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tpoint Point\n\t}{\n\t\t{simpleTime(0, 30, 0), Point{0, -1}},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := minuteHandPoint(c.time)\n\t\t\tif !roughlyEqualPoint(got, c.point) {\n\t\t\t\tt.Fatalf(\"Wanted %v Point, but got %v\", c.point, got)\n\t\t\t}\n\t\t})\n\t}\n}\n```\n\n### Try to run the test\n\n```\n./clockface_test.go:79:11: undefined: minuteHandPoint\n```\n\n### Write the minimal amount of code for the test to run and check the failing test output\n\n```go\nfunc minuteHandPoint(t time.Time) Point {\n\treturn Point{}\n}\n```\n\n```\nclockface_test.go:80: Wanted {0 -1} Point, but got {0 0}\n```\n\n### Write enough code to make it pass\n\n```go\nfunc minuteHandPoint(t time.Time) Point {\n\treturn Point{0, -1}\n}\n```\n\n```\nPASS\nok  \tclockface\t0.007s\n```\n\n### Repeat for new requirements\n\nAnd now for some actual work\n\n```go\nfunc TestMinuteHandPoint(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tpoint Point\n\t}{\n\t\t{simpleTime(0, 30, 0), Point{0, -1}},\n\t\t{simpleTime(0, 45, 0), Point{-1, 0}},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := minuteHandPoint(c.time)\n\t\t\tif !roughlyEqualPoint(got, c.point) {\n\t\t\t\tt.Fatalf(\"Wanted %v Point, but got %v\", c.point, got)\n\t\t\t}\n\t\t})\n\t}\n}\n```\n\n```\nclockface_test.go:81: Wanted {-1 0} Point, but got {0 -1}\n```\n\n### Write enough code to make it pass\n\nA quick copy and paste of the `secondHandPoint` function with some minor changes ought to do it...\n\n```go\nfunc minuteHandPoint(t time.Time) Point {\n\tangle := minutesInRadians(t)\n\tx := math.Sin(angle)\n\ty := math.Cos(angle)\n\n\treturn Point{x, y}\n}\n```\n\n```\nPASS\nok  \tclockface\t0.009s\n```\n\n### Refactor\n\nWe've definitely got a bit of repetition in the `minuteHandPoint` and `secondHandPoint` - I know because we just copied and pasted one to make the other. Let's DRY it out with a function.\n\n```go\nfunc angleToPoint(angle float64) Point {\n\tx := math.Sin(angle)\n\ty := math.Cos(angle)\n\n\treturn Point{x, y}\n}\n```\n\nand we can rewrite `minuteHandPoint` and `secondHandPoint` as one liners:\n\n```go\nfunc minuteHandPoint(t time.Time) Point {\n\treturn angleToPoint(minutesInRadians(t))\n}\n```\n\n```go\nfunc secondHandPoint(t time.Time) Point {\n\treturn angleToPoint(secondsInRadians(t))\n}\n```\n\n```\nPASS\nok  \tclockface\t0.007s\n```\n\nNow we can uncomment the acceptance test and get to work drawing the minute hand.\n\n### Write enough code to make it pass\n\nThe `minuteHand` function is a copy-and-paste of `secondHand` with some minor adjustments, such as declaring a `minuteHandLength`:\n\n```go\nconst minuteHandLength = 80\n\n//...\n\nfunc minuteHand(w io.Writer, t time.Time) {\n\tp := minuteHandPoint(t)\n\tp = Point{p.X * minuteHandLength, p.Y * minuteHandLength}\n\tp = Point{p.X, -p.Y}\n\tp = Point{p.X + clockCentreX, p.Y + clockCentreY}\n\tfmt.Fprintf(w, `<line x1=\"150\" y1=\"150\" x2=\"%.3f\" y2=\"%.3f\" style=\"fill:none;stroke:#000;stroke-width:3px;\"/>`, p.X, p.Y)\n}\n```\n\nAnd a call to it in our `SVGWriter` function:\n\n```go\nfunc SVGWriter(w io.Writer, t time.Time) {\n\tio.WriteString(w, svgStart)\n\tio.WriteString(w, bezel)\n\tsecondHand(w, t)\n\tminuteHand(w, t)\n\tio.WriteString(w, svgEnd)\n}\n```\n\nNow we should see that `TestSVGWriterMinuteHand` passes:\n\n```\nPASS\nok  \tclockface\t0.006s\n```\n\nBut the proof of the pudding is in the eating - if we now compile and run our `clockface` program, we should see something like\n\n![a clock with second and minute hands](<.gitbook/assets/clock (1).svg>)\n\n### Refactor\n\nLet's remove the duplication from the `secondHand` and `minuteHand` functions, putting all of that scale, flip and translate logic all in one place.\n\n```go\nfunc secondHand(w io.Writer, t time.Time) {\n\tp := makeHand(secondHandPoint(t), secondHandLength)\n\tfmt.Fprintf(w, `<line x1=\"150\" y1=\"150\" x2=\"%.3f\" y2=\"%.3f\" style=\"fill:none;stroke:#f00;stroke-width:3px;\"/>`, p.X, p.Y)\n}\n\nfunc minuteHand(w io.Writer, t time.Time) {\n\tp := makeHand(minuteHandPoint(t), minuteHandLength)\n\tfmt.Fprintf(w, `<line x1=\"150\" y1=\"150\" x2=\"%.3f\" y2=\"%.3f\" style=\"fill:none;stroke:#000;stroke-width:3px;\"/>`, p.X, p.Y)\n}\n\nfunc makeHand(p Point, length float64) Point {\n\tp = Point{p.X * length, p.Y * length}\n\tp = Point{p.X, -p.Y}\n\treturn Point{p.X + clockCentreX, p.Y + clockCentreY}\n}\n```\n\n```\nPASS\nok  \tclockface\t0.007s\n```\n\nThis is [where we're up to now](https://github.com/quii/learn-go-with-tests/tree/main/math/v9/clockface).\n\nThere... now it's just the hour hand to do!\n\n### Write the test first\n\n```go\nfunc TestSVGWriterHourHand(t *testing.T) {\n\tcases := []struct {\n\t\ttime time.Time\n\t\tline Line\n\t}{\n\t\t{\n\t\t\tsimpleTime(6, 0, 0),\n\t\t\tLine{150, 150, 150, 200},\n\t\t},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tb := bytes.Buffer{}\n\t\t\tclockface.SVGWriter(&b, c.time)\n\n\t\t\tsvg := SVG{}\n\t\t\txml.Unmarshal(b.Bytes(), &svg)\n\n\t\t\tif !containsLine(c.line, svg.Line) {\n\t\t\t\tt.Errorf(\"Expected to find the hour hand line %+v, in the SVG lines %+v\", c.line, svg.Line)\n\t\t\t}\n\t\t})\n\t}\n}\n```\n\n### Try to run the test\n\n```\nclockface_acceptance_test.go:113: Expected to find the hour hand line {X1:150 Y1:150 X2:150 Y2:200}, in the SVG lines [{X1:150 Y1:150 X2:150 Y2:60} {X1:150 Y1:150 X2:150 Y2:70}]\n```\n\nAgain, let's comment this one out until we've got the some coverage with the lower level tests:\n\n### Write the test first\n\n```go\nfunc TestHoursInRadians(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tangle float64\n\t}{\n\t\t{simpleTime(6, 0, 0), math.Pi},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := hoursInRadians(c.time)\n\t\t\tif got != c.angle {\n\t\t\t\tt.Fatalf(\"Wanted %v radians, but got %v\", c.angle, got)\n\t\t\t}\n\t\t})\n\t}\n}\n```\n\n### Try to run the test\n\n```\n./clockface_test.go:97:11: undefined: hoursInRadians\n```\n\n### Write the minimal amount of code for the test to run and check the failing test output\n\n```go\nfunc hoursInRadians(t time.Time) float64 {\n\treturn math.Pi\n}\n```\n\n```\nPASS\nok  \tclockface\t0.007s\n```\n\n### Repeat for new requirements\n\n```go\nfunc TestHoursInRadians(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tangle float64\n\t}{\n\t\t{simpleTime(6, 0, 0), math.Pi},\n\t\t{simpleTime(0, 0, 0), 0},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := hoursInRadians(c.time)\n\t\t\tif got != c.angle {\n\t\t\t\tt.Fatalf(\"Wanted %v radians, but got %v\", c.angle, got)\n\t\t\t}\n\t\t})\n\t}\n}\n```\n\n### Try to run the test\n\n```\nclockface_test.go:100: Wanted 0 radians, but got 3.141592653589793\n```\n\n### Write enough code to make it pass\n\n```go\nfunc hoursInRadians(t time.Time) float64 {\n\treturn (math.Pi / (6 / float64(t.Hour())))\n}\n```\n\n### Repeat for new requirements\n\n```go\nfunc TestHoursInRadians(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tangle float64\n\t}{\n\t\t{simpleTime(6, 0, 0), math.Pi},\n\t\t{simpleTime(0, 0, 0), 0},\n\t\t{simpleTime(21, 0, 0), math.Pi * 1.5},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := hoursInRadians(c.time)\n\t\t\tif got != c.angle {\n\t\t\t\tt.Fatalf(\"Wanted %v radians, but got %v\", c.angle, got)\n\t\t\t}\n\t\t})\n\t}\n}\n```\n\n### Try to run the test\n\n```\nclockface_test.go:101: Wanted 4.71238898038469 radians, but got 10.995574287564276\n```\n\n### Write enough code to make it pass\n\n```go\nfunc hoursInRadians(t time.Time) float64 {\n\treturn (math.Pi / (6 / (float64(t.Hour() % 12))))\n}\n```\n\nRemember, this is not a 24-hour clock; we have to use the remainder operator to get the remainder of the current hour divided by 12.\n\n```\nPASS\nok  \tlearn-go-with-tests/math/clockface\t0.008s\n```\n\n### Write the test first\n\nNow let's try to move the hour hand around the clockface based on the minutes and the seconds that have passed.\n\n```go\nfunc TestHoursInRadians(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tangle float64\n\t}{\n\t\t{simpleTime(6, 0, 0), math.Pi},\n\t\t{simpleTime(0, 0, 0), 0},\n\t\t{simpleTime(21, 0, 0), math.Pi * 1.5},\n\t\t{simpleTime(0, 1, 30), math.Pi / ((6 * 60 * 60) / 90)},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := hoursInRadians(c.time)\n\t\t\tif got != c.angle {\n\t\t\t\tt.Fatalf(\"Wanted %v radians, but got %v\", c.angle, got)\n\t\t\t}\n\t\t})\n\t}\n}\n```\n\n### Try to run the test\n\n```\nclockface_test.go:102: Wanted 0.013089969389957472 radians, but got 0\n```\n\n### Write enough code to make it pass\n\nAgain, a bit of thinking is now required. We need to move the hour hand along a little bit for both the minutes and the seconds. Luckily we have an angle already to hand for the minutes and the seconds - the one returned by `minutesInRadians`. We can reuse it!\n\nSo the only question is by what factor to reduce the size of that angle. One full turn is one hour for the minute hand, but for the hour hand it's twelve hours. So we just divide the angle returned by `minutesInRadians` by twelve:\n\n```go\nfunc hoursInRadians(t time.Time) float64 {\n\treturn (minutesInRadians(t) / 12) +\n\t\t(math.Pi / (6 / float64(t.Hour()%12)))\n}\n```\n\nand behold:\n\n```\nclockface_test.go:104: Wanted 0.013089969389957472 radians, but got 0.01308996938995747\n```\n\nFloating point arithmetic strikes again.\n\nLet's update our test to use `roughlyEqualFloat64` for the comparison of the angles.\n\n```go\nfunc TestHoursInRadians(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tangle float64\n\t}{\n\t\t{simpleTime(6, 0, 0), math.Pi},\n\t\t{simpleTime(0, 0, 0), 0},\n\t\t{simpleTime(21, 0, 0), math.Pi * 1.5},\n\t\t{simpleTime(0, 1, 30), math.Pi / ((6 * 60 * 60) / 90)},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := hoursInRadians(c.time)\n\t\t\tif !roughlyEqualFloat64(got, c.angle) {\n\t\t\t\tt.Fatalf(\"Wanted %v radians, but got %v\", c.angle, got)\n\t\t\t}\n\t\t})\n\t}\n}\n```\n\n```\nPASS\nok  \tclockface\t0.007s\n```\n\n### Refactor\n\nIf we're going to use `roughlyEqualFloat64` in _one_ of our radians tests, we should probably use it for _all_ of them. That's a nice and simple refactor, which will leave things [looking like this](https://github.com/quii/learn-go-with-tests/tree/main/math/v10/clockface).\n\n## Hour Hand Point\n\nRight, it's time to calculate where the hour hand point is going to go by working out the unit vector.\n\n### Write the test first\n\n```go\nfunc TestHourHandPoint(t *testing.T) {\n\tcases := []struct {\n\t\ttime  time.Time\n\t\tpoint Point\n\t}{\n\t\t{simpleTime(6, 0, 0), Point{0, -1}},\n\t\t{simpleTime(21, 0, 0), Point{-1, 0}},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tgot := hourHandPoint(c.time)\n\t\t\tif !roughlyEqualPoint(got, c.point) {\n\t\t\t\tt.Fatalf(\"Wanted %v Point, but got %v\", c.point, got)\n\t\t\t}\n\t\t})\n\t}\n}\n```\n\nWait, am I going to write _two_ test cases _at once_? Isn't this _bad TDD_?\n\n### On TDD Zealotry\n\nTest driven development is not a religion. Some people might act like it is - usually people who don't do TDD but are happy to moan on Twitter or Dev.to that it's only done by zealots and that they're 'being pragmatic' when they don't write tests. But it's not a religion. It's a tool.\n\nI _know_ what the two tests are going to be - I've tested two other clock hands in exactly the same way - and I already know what my implementation is going to be - I wrote a function for the general case of changing an angle into a point in the minute hand iteration.\n\nI'm not going to plough through TDD ceremony for the sake of it. TDD is a technique that helps me understand the code I'm writing - and the code that I'm going to write - better. TDD gives me feedback, knowledge and insight. But if I've already got that knowledge, then I'm not going to plough through the ceremony for no reason. Neither tests nor TDD are an end in themselves.\n\nMy confidence has increased, so I feel I can make larger strides forward. I'm going to 'skip' a few steps, because I know where I am, I know where I'm going and I've been down this road before.\n\nBut also note: I'm not skipping writing the tests entirely - I'm still writing them first. They're just appearing in less granular chunks.\n\n### Try to run the test\n\n```\n./clockface_test.go:119:11: undefined: hourHandPoint\n```\n\n### Write enough code to make it pass\n\n```go\nfunc hourHandPoint(t time.Time) Point {\n\treturn angleToPoint(hoursInRadians(t))\n}\n```\n\nAs I said, I know where I am, and I know where I'm going. Why pretend otherwise? The tests will soon tell me if I'm wrong.\n\n```\nPASS\nok  \tlearn-go-with-tests/math/clockface\t0.009s\n```\n\n## Draw the hour hand\n\nAnd finally we get to draw in the hour hand. We can bring in that acceptance test by uncommenting it:\n\n```go\nfunc TestSVGWriterHourHand(t *testing.T) {\n\tcases := []struct {\n\t\ttime time.Time\n\t\tline Line\n\t}{\n\t\t{\n\t\t\tsimpleTime(6, 0, 0),\n\t\t\tLine{150, 150, 150, 200},\n\t\t},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(testName(c.time), func(t *testing.T) {\n\t\t\tb := bytes.Buffer{}\n\t\t\tclockface.SVGWriter(&b, c.time)\n\n\t\t\tsvg := SVG{}\n\t\t\txml.Unmarshal(b.Bytes(), &svg)\n\n\t\t\tif !containsLine(c.line, svg.Line) {\n\t\t\t\tt.Errorf(\"Expected to find the hour hand line %+v, in the SVG lines %+v\", c.line, svg.Line)\n\t\t\t}\n\t\t})\n\t}\n}\n```\n\n### Try to run the test\n\n```\nclockface_acceptance_test.go:113: Expected to find the hour hand line {X1:150 Y1:150 X2:150 Y2:200},\n    in the SVG lines [{X1:150 Y1:150 X2:150 Y2:60} {X1:150 Y1:150 X2:150 Y2:70}]\n```\n\n### Write enough code to make it pass\n\nAnd we can now make our final adjustments to the SVG writing constants and functions:\n\n```go\nconst (\n\tsecondHandLength = 90\n\tminuteHandLength = 80\n\thourHandLength   = 50\n\tclockCentreX     = 150\n\tclockCentreY     = 150\n)\n\n// SVGWriter writes an SVG representation of an analogue clock, showing the time t, to the writer w\nfunc SVGWriter(w io.Writer, t time.Time) {\n\tio.WriteString(w, svgStart)\n\tio.WriteString(w, bezel)\n\tsecondHand(w, t)\n\tminuteHand(w, t)\n\thourHand(w, t)\n\tio.WriteString(w, svgEnd)\n}\n\n// ...\n\nfunc hourHand(w io.Writer, t time.Time) {\n\tp := makeHand(hourHandPoint(t), hourHandLength)\n\tfmt.Fprintf(w, `<line x1=\"150\" y1=\"150\" x2=\"%.3f\" y2=\"%.3f\" style=\"fill:none;stroke:#000;stroke-width:3px;\"/>`, p.X, p.Y)\n}\n\n```\n\nAnd so...\n\n```\nok  \tclockface\t0.007s\n```\n\nLet's just check by compiling and running our `clockface` program.\n\n![a clock](<.gitbook/assets/clock (2).svg>)\n\n### Refactor\n\nLooking at `clockface.go`, there are a few 'magic numbers' floating about. They are all based around how many hours/minutes/seconds there are in a half-turn around a clockface. Let's refactor so that we make explicit their meaning.\n\n```go\nconst (\n\tsecondsInHalfClock = 30\n\tsecondsInClock     = 2 * secondsInHalfClock\n\tminutesInHalfClock = 30\n\tminutesInClock     = 2 * minutesInHalfClock\n\thoursInHalfClock   = 6\n\thoursInClock       = 2 * hoursInHalfClock\n)\n```\n\nWhy do this? Well, it makes explicit what each number _means_ in the equation. If - _when_ - we come back to this code, these names will help us to understand what's going on.\n\nMoreover, should we ever want to make some really, really WEIRD clocks - ones with 4 hours for the hour hand, and 20 seconds for the second hand say - these constants could easily become parameters. We're helping to leave that door open (even if we never go through it).\n\n## Wrapping up\n\nDo we need to do anything else?\n\nFirst, let's pat ourselves on the back - we've written a program that makes an SVG clockface. It works and it's great. It will only ever make one sort of clockface - but that's fine! Maybe you only _want_ one sort of clockface. There's nothing wrong with a program that solves a specific problem and nothing else.\n\n### A Program... and a Library\n\nBut the code we've written _does_ solve a more general set of problems to do with drawing a clockface. Because we used tests to think about each small part of the problem in isolation, and because we codified that isolation with functions, we've built a very reasonable little API for clockface calculations.\n\nWe can work on this project and turn it into something more general - a library for calculating clockface angles and/or vectors.\n\nIn fact, providing the library along with the program is _a really good idea_. It costs us nothing, while increasing the utility of our program and helping to document how it works.\n\n> APIs should come with programs, and vice versa. An API that you must write C code to use, which cannot be invoked easily from the command line, is harder to learn and use. And contrariwise, it's a royal pain to have interfaces whose only open, documented form is a program, so you cannot invoke them easily from a C program. -- Henry Spencer, in _The Art of Unix Programming_\n\nIn [my final take on this program](https://github.com/quii/learn-go-with-tests/tree/main/math/vFinal/clockface), I've made the unexported functions within `clockface` into a public API for the library, with functions to calculate the angle and unit vector for each of the clock hands. I've also split the SVG generation part into its own package, `svg`, which is then used by the `clockface` program directly. Naturally I've documented each of the functions and packages.\n\nTalking about SVGs...\n\n### The Most Valuable Test\n\nI'm sure you've noticed that the most sophisticated piece of code for handling SVGs isn't in our application code at all; it's in the test code. Should this make us feel uncomfortable? Shouldn't we do something like\n\n* use a template from `text/template`?\n* use an XML library (much as we're doing in our test)?\n* use an SVG library?\n\nWe could refactor our code to do any of these things, and we can do so because it doesn't matter _how_ we produce our SVG, what is important is _what_ we produce - _an SVG_. As such, the part of our system that needs to know the most about SVGs - that needs to be the strictest about what constitutes an SVG - is the test for the SVG output: it needs to have enough context and knowledge about what an SVG is for us to be confident that we're outputting an SVG. The _what_ of an SVG lives in our tests; the _how_ in the code.\n\nWe may have felt odd that we were pouring a lot of time and effort into those SVG tests - importing an XML library, parsing XML, refactoring the structs - but that test code is a valuable part of our codebase - possibly more valuable than the current production code. It will help guarantee that the output is always a valid SVG, no matter what we choose to use to produce it.\n\nTests are not second class citizens - they are not 'throwaway' code. Good tests will last a lot longer than the version of the code they are testing. You should never feel like you're spending 'too much time' writing your tests. It is an investment.\n\n1. In short it makes it easier to do calculus with circles as π just keeps coming up as an angle if you use normal degrees, so if you count your angles in πs it makes all the equations simpler.\n"
  },
  {
    "path": "meta.tmpl.tex",
    "content": "\\usepackage{fancyhdr}\n\n\\usepackage{xeCJK}\n\\usepackage{fontspec}% set Chinese fonts, as follows\n\\setmainfont{DejaVu Sans}\n\\setmonofont{DejaVu Sans}\n\\setsansfont{DejaVu Sans}\n\\setCJKmainfont[BoldFont=Noto Sans CJK TC,ItalicFont=Noto Sans CJK TC]{Noto Sans CJK TC}\n\\setCJKsansfont[BoldFont=Noto Sans CJK TC]{Noto Sans CJK TC}\n\\setCJKmonofont{Noto Sans Mono CJK TC}\n\n\\pagestyle{fancy}\n\\fancyhf{}\n\\cfoot{%%FOOTER_VERSION%%}\n"
  },
  {
    "path": "mocking/v1/countdown_test.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n)\n\nfunc TestCountdown(t *testing.T) {\n\tbuffer := &bytes.Buffer{}\n\n\tCountdown(buffer)\n\n\tgot := buffer.String()\n\twant := \"3\"\n\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "mocking/v1/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n)\n\n// Countdown prints a countdown from 3 to out.\nfunc Countdown(out io.Writer) {\n\tfmt.Fprint(out, \"3\")\n}\n\nfunc main() {\n\tCountdown(os.Stdout)\n}\n"
  },
  {
    "path": "mocking/v2/countdown_test.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n)\n\nfunc TestCountdown(t *testing.T) {\n\tbuffer := &bytes.Buffer{}\n\n\tCountdown(buffer)\n\n\tgot := buffer.String()\n\twant := `3\n2\n1\nGo!`\n\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "mocking/v2/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n)\n\nconst finalWord = \"Go!\"\nconst countdownStart = 3\n\n// Countdown prints a countdown from 3 to out.\nfunc Countdown(out io.Writer) {\n\tfor i := countdownStart; i > 0; i-- {\n\t\tfmt.Fprintln(out, i)\n\t}\n\tfmt.Fprint(out, finalWord)\n}\n\nfunc main() {\n\tCountdown(os.Stdout)\n}\n"
  },
  {
    "path": "mocking/v3/countdown_test.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n)\n\nfunc TestCountdown(t *testing.T) {\n\tbuffer := &bytes.Buffer{}\n\tspySleeper := &SpySleeper{}\n\n\tCountdown(buffer, spySleeper)\n\n\tgot := buffer.String()\n\twant := `3\n2\n1\nGo!`\n\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n\n\tif spySleeper.Calls != 3 {\n\t\tt.Errorf(\"not enough calls to sleeper, want 3 got %d\", spySleeper.Calls)\n\t}\n}\n\ntype SpySleeper struct {\n\tCalls int\n}\n\nfunc (s *SpySleeper) Sleep() {\n\ts.Calls++\n}\n"
  },
  {
    "path": "mocking/v3/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"time\"\n)\n\n// Sleeper allows you to put delays.\ntype Sleeper interface {\n\tSleep()\n}\n\n// DefaultSleeper is an implementation of Sleeper with a predefined delay.\ntype DefaultSleeper struct{}\n\n// Sleep will pause execution for the defined Duration.\nfunc (d *DefaultSleeper) Sleep() {\n\ttime.Sleep(1 * time.Second)\n}\n\nconst finalWord = \"Go!\"\nconst countdownStart = 3\n\n// Countdown prints a countdown from 3 to out with a delay between count provided by Sleeper.\nfunc Countdown(out io.Writer, sleeper Sleeper) {\n\tfor i := countdownStart; i > 0; i-- {\n\t\tfmt.Fprintln(out, i)\n\t\tsleeper.Sleep()\n\t}\n\n\tfmt.Fprint(out, finalWord)\n}\n\nfunc main() {\n\tsleeper := &DefaultSleeper{}\n\tCountdown(os.Stdout, sleeper)\n}\n"
  },
  {
    "path": "mocking/v4/countdown_test.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestCountdown(t *testing.T) {\n\n\tt.Run(\"prints 3 to Go!\", func(t *testing.T) {\n\t\tbuffer := &bytes.Buffer{}\n\t\tCountdown(buffer, &SpyCountdownOperations{})\n\n\t\tgot := buffer.String()\n\t\twant := `3\n2\n1\nGo!`\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %q want %q\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"sleep in-between the prints\", func(t *testing.T) {\n\t\tspySleepPrinter := &SpyCountdownOperations{}\n\t\tCountdown(spySleepPrinter, spySleepPrinter)\n\n\t\twant := []string{\n\t\t\twrite,\n\t\t\tsleep,\n\t\t\twrite,\n\t\t\tsleep,\n\t\t\twrite,\n\t\t\tsleep,\n\t\t\twrite,\n\t\t}\n\n\t\tif !reflect.DeepEqual(want, spySleepPrinter.Calls) {\n\t\t\tt.Errorf(\"wanted calls %v got %v\", want, spySleepPrinter.Calls)\n\t\t}\n\t})\n}\n\ntype SpyCountdownOperations struct {\n\tCalls []string\n}\n\nfunc (s *SpyCountdownOperations) Sleep() {\n\ts.Calls = append(s.Calls, sleep)\n}\n\nfunc (s *SpyCountdownOperations) Write(p []byte) (n int, err error) {\n\ts.Calls = append(s.Calls, write)\n\treturn\n}\n\nconst write = \"write\"\nconst sleep = \"sleep\"\n"
  },
  {
    "path": "mocking/v4/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"time\"\n)\n\n// Sleeper allows you to put delays.\ntype Sleeper interface {\n\tSleep()\n}\n\n// DefaultSleeper is an implementation of Sleeper with a predefined delay.\ntype DefaultSleeper struct{}\n\n// Sleep will pause execution for the defined Duration.\nfunc (d *DefaultSleeper) Sleep() {\n\ttime.Sleep(1 * time.Second)\n}\n\nconst finalWord = \"Go!\"\nconst countdownStart = 3\n\n// Countdown prints a countdown from 3 to out with a delay between count provided by Sleeper.\nfunc Countdown(out io.Writer, sleeper Sleeper) {\n\n\tfor i := countdownStart; i > 0; i-- {\n\t\tfmt.Fprintln(out, i)\n\t\tsleeper.Sleep()\n\t}\n\n\tfmt.Fprint(out, finalWord)\n}\n\nfunc main() {\n\tsleeper := &DefaultSleeper{}\n\tCountdown(os.Stdout, sleeper)\n}\n"
  },
  {
    "path": "mocking/v5/countdown_test.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestCountdown(t *testing.T) {\n\n\tt.Run(\"prints 3 to Go!\", func(t *testing.T) {\n\t\tbuffer := &bytes.Buffer{}\n\t\tCountdown(buffer, &SpyCountdownOperations{})\n\n\t\tgot := buffer.String()\n\t\twant := `3\n2\n1\nGo!`\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %q want %q\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"sleep before every print\", func(t *testing.T) {\n\t\tspySleepPrinter := &SpyCountdownOperations{}\n\t\tCountdown(spySleepPrinter, spySleepPrinter)\n\n\t\twant := []string{\n\t\t\twrite,\n\t\t\tsleep,\n\t\t\twrite,\n\t\t\tsleep,\n\t\t\twrite,\n\t\t\tsleep,\n\t\t\twrite,\n\t\t}\n\n\t\tif !reflect.DeepEqual(want, spySleepPrinter.Calls) {\n\t\t\tt.Errorf(\"wanted calls %v got %v\", want, spySleepPrinter.Calls)\n\t\t}\n\t})\n}\n\nfunc TestConfigurableSleeper(t *testing.T) {\n\tsleepTime := 5 * time.Second\n\n\tspyTime := &SpyTime{}\n\tsleeper := ConfigurableSleeper{sleepTime, spyTime.SetDurationSlept}\n\tsleeper.Sleep()\n\n\tif spyTime.durationSlept != sleepTime {\n\t\tt.Errorf(\"should have slept for %v but slept for %v\", sleepTime, spyTime.durationSlept)\n\t}\n}\n\ntype SpyCountdownOperations struct {\n\tCalls []string\n}\n\nfunc (s *SpyCountdownOperations) Sleep() {\n\ts.Calls = append(s.Calls, sleep)\n}\n\nfunc (s *SpyCountdownOperations) Write(p []byte) (n int, err error) {\n\ts.Calls = append(s.Calls, write)\n\treturn\n}\n\nconst write = \"write\"\nconst sleep = \"sleep\"\n\ntype SpyTime struct {\n\tdurationSlept time.Duration\n}\n\nfunc (s *SpyTime) SetDurationSlept(duration time.Duration) {\n\ts.durationSlept = duration\n}\n"
  },
  {
    "path": "mocking/v5/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"time\"\n)\n\n// Sleeper allows you to put delays.\ntype Sleeper interface {\n\tSleep()\n}\n\n// ConfigurableSleeper is an implementation of Sleeper with a defined delay.\ntype ConfigurableSleeper struct {\n\tduration time.Duration\n\tsleep    func(time.Duration)\n}\n\n// Sleep will pause execution for the defined Duration.\nfunc (c *ConfigurableSleeper) Sleep() {\n\tc.sleep(c.duration)\n}\n\nconst finalWord = \"Go!\"\nconst countdownStart = 3\n\n// Countdown prints a countdown from 3 to out with a delay between count provided by Sleeper.\nfunc Countdown(out io.Writer, sleeper Sleeper) {\n\n\tfor i := countdownStart; i > 0; i-- {\n\t\tfmt.Fprintln(out, i)\n\t\tsleeper.Sleep()\n\t}\n\n\tfmt.Fprint(out, finalWord)\n}\n\nfunc main() {\n\tsleeper := &ConfigurableSleeper{1 * time.Second, time.Sleep}\n\tCountdown(os.Stdout, sleeper)\n}\n"
  },
  {
    "path": "mocking/v6/countdown_test.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestCountdown(t *testing.T) {\n\n\tt.Run(\"prints 3 to Go!\", func(t *testing.T) {\n\t\tbuffer := &bytes.Buffer{}\n\t\tCountdown(buffer, &SpyCountdownOperations{})\n\n\t\tgot := buffer.String()\n\t\twant := `3\n2\n1\nGo!`\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %q want %q\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"sleep before every print\", func(t *testing.T) {\n\t\tspySleepPrinter := &SpyCountdownOperations{}\n\t\tCountdown(spySleepPrinter, spySleepPrinter)\n\n\t\twant := []string{\n\t\t\twrite,\n\t\t\tsleep,\n\t\t\twrite,\n\t\t\tsleep,\n\t\t\twrite,\n\t\t\tsleep,\n\t\t\twrite,\n\t\t}\n\n\t\tif !reflect.DeepEqual(want, spySleepPrinter.Calls) {\n\t\t\tt.Errorf(\"wanted calls %v got %v\", want, spySleepPrinter.Calls)\n\t\t}\n\t})\n}\n\nfunc TestConfigurableSleeper(t *testing.T) {\n\tsleepTime := 5 * time.Second\n\n\tspyTime := &SpyTime{}\n\tsleeper := ConfigurableSleeper{sleepTime, spyTime.SetDurationSlept}\n\tsleeper.Sleep()\n\n\tif spyTime.durationSlept != sleepTime {\n\t\tt.Errorf(\"should have slept for %v but slept for %v\", sleepTime, spyTime.durationSlept)\n\t}\n}\n\ntype SpyCountdownOperations struct {\n\tCalls []string\n}\n\nfunc (s *SpyCountdownOperations) Sleep() {\n\ts.Calls = append(s.Calls, sleep)\n}\n\nfunc (s *SpyCountdownOperations) Write(p []byte) (n int, err error) {\n\ts.Calls = append(s.Calls, write)\n\treturn\n}\n\nconst write = \"write\"\nconst sleep = \"sleep\"\n\ntype SpyTime struct {\n\tdurationSlept time.Duration\n}\n\nfunc (s *SpyTime) SetDurationSlept(duration time.Duration) {\n\ts.durationSlept = duration\n}\n"
  },
  {
    "path": "mocking/v6/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"iter\"\n\t\"os\"\n\t\"time\"\n)\n\n// Sleeper allows you to put delays.\ntype Sleeper interface {\n\tSleep()\n}\n\n// ConfigurableSleeper is an implementation of Sleeper with a defined delay.\ntype ConfigurableSleeper struct {\n\tduration time.Duration\n\tsleep    func(time.Duration)\n}\n\n// Sleep will pause execution for the defined Duration.\nfunc (c *ConfigurableSleeper) Sleep() {\n\tc.sleep(c.duration)\n}\n\nconst finalWord = \"Go!\"\n\n// Countdown prints a countdown from 3 to out with a delay between count provided by Sleeper.\nfunc Countdown(out io.Writer, sleeper Sleeper) {\n\tfor i := range countDownFrom(3) {\n\t\tfmt.Fprintln(out, i)\n\t\tsleeper.Sleep()\n\t}\n\n\tfmt.Fprint(out, finalWord)\n}\n\nfunc countDownFrom(from int) iter.Seq[int] {\n\treturn func(yield func(int) bool) {\n\t\tfor i := from; i > 0; i-- {\n\t\t\tif !yield(i) {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc main() {\n\tsleeper := &ConfigurableSleeper{1 * time.Second, time.Sleep}\n\tCountdown(os.Stdout, sleeper)\n}\n"
  },
  {
    "path": "mocking.md",
    "content": "# Mocking\n\n**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/main/mocking)**\n\nYou have been asked to write a program which counts down from 3, printing each number on a new line (with a 1-second pause) and when it reaches zero it will print \"Go!\" and exit.\n\n```\n3\n2\n1\nGo!\n```\n\nWe'll tackle this by writing a function called `Countdown` which we will then put inside a `main` program so it looks something like this:\n\n```go\npackage main\n\nfunc main() {\n\tCountdown()\n}\n```\n\nWhile this is a pretty trivial program, to test it fully we will need as always to take an _iterative_, _test-driven_ approach.\n\nWhat do I mean by iterative? We make sure we take the smallest steps we can to have _useful software_.\n\nWe don't want to spend a long time with code that will theoretically work after some hacking because that's often how developers fall down rabbit holes. **It's an important skill to be able to slice up requirements as small as you can so you can have _working software_.**\n\nHere's how we can divide our work up and iterate on it:\n\n- Print 3\n- Print 3, 2, 1 and Go!\n- Wait a second between each line\n\n## Write the test first\n\nOur software needs to print to stdout and we saw how we could use Dependency Injection (DI) to facilitate testing this in the DI section.\n\n```go\nfunc TestCountdown(t *testing.T) {\n\tbuffer := &bytes.Buffer{}\n\n\tCountdown(buffer)\n\n\tgot := buffer.String()\n\twant := \"3\"\n\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n```\n\nIf anything like `buffer` is unfamiliar to you, re-read [the previous section](dependency-injection.md).\n\nWe know we want our `Countdown` function to write data somewhere and `io.Writer` is the de-facto way of capturing that as an interface in Go.\n\n- In `main` we will send to `os.Stdout` so our users see the countdown printed to the terminal.\n- In test we will send to `bytes.Buffer` so our tests can capture what data is being generated.\n\n## Try and run the test\n\n`./countdown_test.go:11:2: undefined: Countdown`\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\nDefine `Countdown`\n\n```go\nfunc Countdown() {}\n```\n\nTry again\n\n```\n./countdown_test.go:11:11: too many arguments in call to Countdown\n    have (*bytes.Buffer)\n    want ()\n```\n\nThe compiler is telling you what your function signature could be, so update it.\n\n```go\nfunc Countdown(out *bytes.Buffer) {}\n```\n\n`countdown_test.go:17: got '' want '3'`\n\nPerfect!\n\n## Write enough code to make it pass\n\n```go\nfunc Countdown(out *bytes.Buffer) {\n\tfmt.Fprint(out, \"3\")\n}\n```\n\nWe're using `fmt.Fprint` which takes an `io.Writer` (like `*bytes.Buffer`) and sends a `string` to it. The test should pass.\n\n## Refactor\n\nWe know that while `*bytes.Buffer` works, it would be better to use a general purpose interface instead.\n\n```go\nfunc Countdown(out io.Writer) {\n\tfmt.Fprint(out, \"3\")\n}\n```\n\nRe-run the tests and they should be passing.\n\nTo complete matters, let's now wire up our function into a `main` so we have some working software to reassure ourselves we're making progress.\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n)\n\nfunc Countdown(out io.Writer) {\n\tfmt.Fprint(out, \"3\")\n}\n\nfunc main() {\n\tCountdown(os.Stdout)\n}\n```\n\nTry and run the program and be amazed at your handywork.\n\nYes this seems trivial but this approach is what I would recommend for any project. **Take a thin slice of functionality and make it work end-to-end, backed by tests.**\n\nNext we can make it print 2,1 and then \"Go!\".\n\n## Write the test first\n\nBy investing in getting the overall plumbing working right, we can iterate on our solution safely and easily. We will no longer need to stop and re-run the program to be confident of it working as all the logic is tested.\n\n```go\nfunc TestCountdown(t *testing.T) {\n\tbuffer := &bytes.Buffer{}\n\n\tCountdown(buffer)\n\n\tgot := buffer.String()\n\twant := `3\n2\n1\nGo!`\n\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n```\n\nThe backtick syntax is another way of creating a `string` but lets you include things like newlines, which is perfect for our test.\n\n## Try and run the test\n\n```\ncountdown_test.go:21: got '3' want '3\n        2\n        1\n        Go!'\n```\n## Write enough code to make it pass\n\n```go\nfunc Countdown(out io.Writer) {\n\tfor i := 3; i > 0; i-- {\n\t\tfmt.Fprintln(out, i)\n\t}\n\tfmt.Fprint(out, \"Go!\")\n}\n```\n\nUse a `for` loop counting backwards with `i--` and use `fmt.Fprintln` to print to `out` with our number followed by a newline character. Finally use `fmt.Fprint` to send \"Go!\" aftward.\n\n## Refactor\n\nThere's not much to refactor other than refactoring some magic values into named constants.\n\n```go\nconst finalWord = \"Go!\"\nconst countdownStart = 3\n\nfunc Countdown(out io.Writer) {\n\tfor i := countdownStart; i > 0; i-- {\n\t\tfmt.Fprintln(out, i)\n\t}\n\tfmt.Fprint(out, finalWord)\n}\n```\n\nIf you run the program now, you should get the desired output but we don't have it as a dramatic countdown with the 1-second pauses.\n\nGo lets you achieve this with `time.Sleep`. Try adding it in to our code.\n\n```go\nfunc Countdown(out io.Writer) {\n\tfor i := countdownStart; i > 0; i-- {\n\t\tfmt.Fprintln(out, i)\n\t\ttime.Sleep(1 * time.Second)\n\t}\n\n\tfmt.Fprint(out, finalWord)\n}\n```\n\nIf you run the program it works as we want it to.\n\n## Mocking\n\nThe tests still pass and the software works as intended but we have some problems:\n- Our tests take 3 seconds to run.\n    - Every forward-thinking post about software development emphasises the importance of quick feedback loops.\n    - **Slow tests ruin developer productivity**.\n    - Imagine if the requirements get more sophisticated warranting more tests. Are we happy with 3s added to the test run for every new test of `Countdown`?\n- We have not tested an important property of our function.\n\nWe have a dependency on `Sleep`ing which we need to extract so we can then control it in our tests.\n\nIf we can _mock_ `time.Sleep` we can use _dependency injection_ to use it instead of a \"real\" `time.Sleep` and then we can **spy on the calls** to make assertions on them.\n\n## Write the test first\n\nLet's define our dependency as an interface. This lets us then use a _real_ Sleeper in `main` and a _spy sleeper_ in our tests. By using an interface our `Countdown` function is oblivious to this and adds some flexibility for the caller.\n\n```go\ntype Sleeper interface {\n\tSleep()\n}\n```\n\nI made a design decision that our `Countdown` function would not be responsible for how long the sleep is. This simplifies our code a little for now at least and means a user of our function can configure that sleepiness however they like.\n\nNow we need to make a _mock_ of it for our tests to use.\n\n```go\ntype SpySleeper struct {\n\tCalls int\n}\n\nfunc (s *SpySleeper) Sleep() {\n\ts.Calls++\n}\n```\n\n_Spies_ are a kind of _mock_ which can record how a dependency is used. They can record the arguments sent in, how many times it has been called, etc. In our case, we're keeping track of how many times `Sleep()` is called so we can check it in our test.\n\nUpdate the tests to inject a dependency on our Spy and assert that the sleep has been called 3 times.\n\n```go\nfunc TestCountdown(t *testing.T) {\n\tbuffer := &bytes.Buffer{}\n\tspySleeper := &SpySleeper{}\n\n\tCountdown(buffer, spySleeper)\n\n\tgot := buffer.String()\n\twant := `3\n2\n1\nGo!`\n\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n\n\tif spySleeper.Calls != 3 {\n\t\tt.Errorf(\"not enough calls to sleeper, want 3 got %d\", spySleeper.Calls)\n\t}\n}\n```\n\n## Try and run the test\n\n```\ntoo many arguments in call to Countdown\n    have (*bytes.Buffer, *SpySleeper)\n    want (io.Writer)\n```\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\nWe need to update `Countdown` to accept our `Sleeper`\n\n```go\nfunc Countdown(out io.Writer, sleeper Sleeper) {\n\tfor i := countdownStart; i > 0; i-- {\n\t\tfmt.Fprintln(out, i)\n\t\ttime.Sleep(1 * time.Second)\n\t}\n\n\tfmt.Fprint(out, finalWord)\n}\n```\n\nIf you try again, your `main` will no longer compile for the same reason\n\n```\n./main.go:26:11: not enough arguments in call to Countdown\n    have (*os.File)\n    want (io.Writer, Sleeper)\n```\n\nLet's create a _real_ sleeper which implements the interface we need\n\n```go\ntype DefaultSleeper struct{}\n\nfunc (d *DefaultSleeper) Sleep() {\n\ttime.Sleep(1 * time.Second)\n}\n```\n\nWe can then use it in our real application like so\n\n```go\nfunc main() {\n\tsleeper := &DefaultSleeper{}\n\tCountdown(os.Stdout, sleeper)\n}\n```\n\n## Write enough code to make it pass\n\nThe test is now compiling but not passing because we're still calling the `time.Sleep` rather than the injected in dependency. Let's fix that.\n\n```go\nfunc Countdown(out io.Writer, sleeper Sleeper) {\n\tfor i := countdownStart; i > 0; i-- {\n\t\tfmt.Fprintln(out, i)\n\t\tsleeper.Sleep()\n\t}\n\n\tfmt.Fprint(out, finalWord)\n}\n```\n\nThe test should pass and no longer take 3 seconds.\n\n### Still some problems\n\nThere's still another important property we haven't tested.\n\n`Countdown` should sleep before each next print, e.g:\n\n- `Print N`\n- `Sleep`\n- `Print N-1`\n- `Sleep`\n- `Print Go!`\n- etc\n\nOur latest change only asserts that it has slept 3 times, but those sleeps could occur out of sequence.\n\nWhen writing tests if you're not confident that your tests are giving you sufficient confidence, just break it! (make sure you have committed your changes to source control first though). Change the code to the following\n\n```go\nfunc Countdown(out io.Writer, sleeper Sleeper) {\n\tfor i := countdownStart; i > 0; i-- {\n\t\tsleeper.Sleep()\n\t}\n\n\tfor i := countdownStart; i > 0; i-- {\n\t\tfmt.Fprintln(out, i)\n\t}\n\n\tfmt.Fprint(out, finalWord)\n}\n```\n\nIf you run your tests they should still be passing even though the implementation is wrong.\n\nLet's use spying again with a new test to check the order of operations is correct.\n\nWe have two different dependencies and we want to record all of their operations into one list. So we'll create _one spy for them both_.\n\n```go\ntype SpyCountdownOperations struct {\n\tCalls []string\n}\n\nfunc (s *SpyCountdownOperations) Sleep() {\n\ts.Calls = append(s.Calls, sleep)\n}\n\nfunc (s *SpyCountdownOperations) Write(p []byte) (n int, err error) {\n\ts.Calls = append(s.Calls, write)\n\treturn\n}\n\nconst write = \"write\"\nconst sleep = \"sleep\"\n```\n\nOur `SpyCountdownOperations` implements both `io.Writer` and `Sleeper`, recording every call into one slice. In this test we're only concerned about the order of operations, so just recording them as list of named operations is sufficient.\n\nWe can now add a sub-test into our test suite which verifies our sleeps and prints operate in the order we hope\n\n```go\nt.Run(\"sleep before every print\", func(t *testing.T) {\n\tspySleepPrinter := &SpyCountdownOperations{}\n\tCountdown(spySleepPrinter, spySleepPrinter)\n\n\twant := []string{\n\t\twrite,\n\t\tsleep,\n\t\twrite,\n\t\tsleep,\n\t\twrite,\n\t\tsleep,\n\t\twrite,\n\t}\n\n\tif !reflect.DeepEqual(want, spySleepPrinter.Calls) {\n\t\tt.Errorf(\"wanted calls %v got %v\", want, spySleepPrinter.Calls)\n\t}\n})\n```\n\nThis test should now fail. Revert `Countdown` back to how it was to fix the test.\n\nWe now have two tests spying on the `Sleeper` so we can now refactor our test so one is testing what is being printed and the other one is ensuring we're sleeping between the prints. Finally, we can delete our first spy as it's not used anymore.\n\n```go\nfunc TestCountdown(t *testing.T) {\n\n\tt.Run(\"prints 3 to Go!\", func(t *testing.T) {\n\t\tbuffer := &bytes.Buffer{}\n\t\tCountdown(buffer, &SpyCountdownOperations{})\n\n\t\tgot := buffer.String()\n\t\twant := `3\n2\n1\nGo!`\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %q want %q\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"sleep before every print\", func(t *testing.T) {\n\t\tspySleepPrinter := &SpyCountdownOperations{}\n\t\tCountdown(spySleepPrinter, spySleepPrinter)\n\n\t\twant := []string{\n\t\t\twrite,\n\t\t\tsleep,\n\t\t\twrite,\n\t\t\tsleep,\n\t\t\twrite,\n\t\t\tsleep,\n\t\t\twrite,\n\t\t}\n\n\t\tif !reflect.DeepEqual(want, spySleepPrinter.Calls) {\n\t\t\tt.Errorf(\"wanted calls %v got %v\", want, spySleepPrinter.Calls)\n\t\t}\n\t})\n}\n```\n\nWe now have our function and its 2 important properties properly tested.\n\n## Extending Sleeper to be configurable\n\nA nice feature would be for the `Sleeper` to be configurable. This means that we can adjust the sleep time in our main program.\n\n### Write the test first\n\nLet's first create a new type for `ConfigurableSleeper` that accepts what we need for configuration and testing.\n\n```go\ntype ConfigurableSleeper struct {\n\tduration time.Duration\n\tsleep    func(time.Duration)\n}\n```\n\nWe are using `duration` to configure the time slept and `sleep` as a way to pass in a sleep function. The signature of `sleep` is the same as for `time.Sleep` allowing us to use `time.Sleep` in our real implementation and the following spy in our tests:\n\n```go\ntype SpyTime struct {\n\tdurationSlept time.Duration\n}\n\nfunc (s *SpyTime) SetDurationSlept(duration time.Duration) {\n\ts.durationSlept = duration\n}\n```\n\nWith our spy in place, we can create a new test for the configurable sleeper.\n\n```go\nfunc TestConfigurableSleeper(t *testing.T) {\n\tsleepTime := 5 * time.Second\n\n\tspyTime := &SpyTime{}\n\tsleeper := ConfigurableSleeper{sleepTime, spyTime.SetDurationSlept}\n\tsleeper.Sleep()\n\n\tif spyTime.durationSlept != sleepTime {\n\t\tt.Errorf(\"should have slept for %v but slept for %v\", sleepTime, spyTime.durationSlept)\n\t}\n}\n```\n\nThere should be nothing new in this test and it is set up very similar to the previous mock tests.\n\n### Try and run the test\n```\nsleeper.Sleep undefined (type ConfigurableSleeper has no field or method Sleep, but does have sleep)\n\n```\n\nYou should see a very clear error message indicating that we do not have a `Sleep` method created on our `ConfigurableSleeper`.\n\n### Write the minimal amount of code for the test to run and check failing test output\n```go\nfunc (c *ConfigurableSleeper) Sleep() {\n}\n```\n\nWith our new `Sleep` function implemented we have a failing test.\n\n```\ncountdown_test.go:56: should have slept for 5s but slept for 0s\n```\n\n### Write enough code to make it pass\n\nAll we need to do now is implement the `Sleep` function for `ConfigurableSleeper`.\n\n```go\nfunc (c *ConfigurableSleeper) Sleep() {\n\tc.sleep(c.duration)\n}\n```\n\nWith this change all of the tests should be passing again and you might wonder why all the hassle as the main program didn't change at all. Hopefully it becomes clear after the following section.\n\n### Cleanup and refactor\n\nThe last thing we need to do is to actually use our `ConfigurableSleeper` in the main function.\n\n```go\nfunc main() {\n\tsleeper := &ConfigurableSleeper{1 * time.Second, time.Sleep}\n\tCountdown(os.Stdout, sleeper)\n}\n```\n\nIf we run the tests and the program manually, we can see that all the behavior remains the same.\n\nSince we are using the `ConfigurableSleeper`, it is now safe to delete the `DefaultSleeper` implementation. Wrapping up our program and having a more [generic](https://stackoverflow.com/questions/19291776/whats-the-difference-between-abstraction-and-generalization) Sleeper with arbitrary long countdowns.\n\n## But isn't mocking evil?\n\nYou may have heard mocking is evil. Just like anything in software development it can be used for evil, just like [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself).\n\nPeople normally get in to a bad state when they don't _listen to their tests_ and are _not respecting the refactoring stage_.\n\nIf your mocking code is becoming complicated or you are having to mock out lots of things to test something, you should _listen_ to that bad feeling and think about your code. Usually it is a sign of\n\n- The thing you are testing is having to do too many things (because it has too many dependencies to mock)\n  - Break the module apart so it does less\n- Its dependencies are too fine-grained\n  - Think about how you can consolidate some of these dependencies into one meaningful module\n- Your test is too concerned with implementation details\n  - Favour testing expected behaviour rather than the implementation\n\nNormally a lot of mocking points to _bad abstraction_ in your code.\n\n**What people see here is a weakness in TDD but it is actually a strength**, more often than not poor test code is a result of bad design or put more nicely, well-designed code is easy to test.\n\n### But mocks and tests are still making my life hard!\n\nEver run into this situation?\n\n- You want to do some refactoring\n- To do this you end up changing lots of tests\n- You question TDD and make a post on Medium titled \"Mocking considered harmful\"\n\nThis is usually a sign of you testing too much _implementation detail_. Try to make it so your tests are testing _useful behaviour_ unless the implementation is really important to how the system runs.\n\nIt is sometimes hard to know _what level_ to test exactly but here are some thought processes and rules I try to follow:\n\n- **The definition of refactoring is that the code changes but the behaviour stays the same**. If you have decided to do some refactoring in theory you should be able to make the commit without any test changes. So when writing a test ask yourself\n  - Am I testing the behaviour I want, or the implementation details?\n  - If I were to refactor this code, would I have to make lots of changes to the tests?\n- Although Go lets you test private functions, I would avoid it as private functions are implementation detail to support public behaviour. Test the public behaviour. Sandi Metz describes private functions as being \"less stable\" and you don't want to couple your tests to them.\n- I feel like if a test is working with **more than 3 mocks then it is a red flag** - time for a rethink on the design\n- Use spies with caution. Spies let you see the insides of the algorithm you are writing which can be very useful but that means a tighter coupling between your test code and the implementation. **Be sure you actually care about these details if you're going to spy on them**\n\n#### Can't I just use a mocking framework?\n\nMocking requires no magic and is relatively simple; using a framework can make mocking seem more complicated than it is. We don't use automocking in this chapter so that we get:\n\n- a better understanding of how to mock\n- practice implementing interfaces\n\nIn collaborative projects there is value in auto-generating mocks. In a team, a mock generation tool codifies consistency around the test doubles. This will avoid inconsistently written test doubles which can translate to inconsistently written tests.\n\nYou should only use a mock generator that generates test doubles against an interface. Any tool that overly dictates how tests are written, or that use lots of 'magic', can get in the sea.\n\n## Wrapping up\n\n### More on TDD approach\n\n- When faced with less trivial examples, break the problem down into \"thin vertical slices\". Try to get to a point where you have _working software backed by tests_ as soon as you can, to avoid getting in rabbit holes and taking a \"big bang\" approach.\n- Once you have some working software it should be easier to _iterate with small steps_ until you arrive at the software you need.\n\n> \"When to use iterative development? You should use iterative development only on projects that you want to succeed.\"\n\nMartin Fowler.\n\n### Mocking\n\n- **Without mocking important areas of your code will be untested**. In our case we would not be able to test that our code paused between each print but there are countless other examples. Calling a service that _can_ fail? Wanting to test your system in a particular state? It is very hard to test these scenarios without mocking.\n- Without mocks you may have to set up databases and other third parties things just to test simple business rules. You're likely to have slow tests, resulting in **slow feedback loops**.\n- By having to spin up a database or a webservice to test something you're likely to have **fragile tests** due to the unreliability of such services.\n\nOnce a developer learns about mocking it becomes very easy to over-test every single facet of a system in terms of the _way it works_ rather than _what it does_. Always be mindful about **the value of your tests** and what impact they would have in future refactoring.\n\nIn this post about mocking we have only covered **Spies**, which are a kind of mock. Mocks are a type of \"test double.\"\n\n> [Test Double is a generic term for any case where you replace a production object for testing purposes.](https://martinfowler.com/bliki/TestDouble.html)\n\nUnder test doubles, there are various types like stubs, spies and indeed mocks! Check out [Martin Fowler's post](https://martinfowler.com/bliki/TestDouble.html) for more detail.\n\n## Bonus - Example of iterators from go 1.23\n\nIn Go 1.23 [iterators were introduced](https://tip.golang.org/doc/go1.23). We can use iterators in various ways, in this instance we can make a `countdownFrom` iterator, which will return the numbers to countdown in reverse order.\n\nBefore we get into how we write custom iterators, let's see how we use them. Rather than writing a fairly imperative looking loop to count down from a number, we can make this code look more expressive by `range`-ing over our custom `countdownFrom` iterator.\n\n```go\nfunc Countdown(out io.Writer, sleeper Sleeper) {\n\tfor i := range countDownFrom(3) {\n\t\tfmt.Fprintln(out, i)\n\t\tsleeper.Sleep()\n\t}\n\n\tfmt.Fprint(out, finalWord)\n}\n```\n\nTo write an iterator like `countDownFrom`, you need to write a function in a particular way. From the docs:\n\n    The “range” clause in a “for-range” loop now accepts iterator functions of the following types\n        func(func() bool)\n        func(func(K) bool)\n        func(func(K, V) bool)\n\n(The `K` and `V` stand for key and value types, respectively.)\n\nIn our case, we don't have keys, just values. Go also provides a convenience type `iter.Seq[T]` which is a type alias for `func(func(T) bool)`.\n\n```go\nfunc countDownFrom(from int) iter.Seq[int] {\n\treturn func(yield func(int) bool) {\n\t\tfor i := from; i > 0; i-- {\n\t\t\tif !yield(i) {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n}\n```\n\nThis is a simple iterator, which will yield numbers in reverse order, starting from, `from` - perfect for our usecase. "
  },
  {
    "path": "os-exec.md",
    "content": "# OS Exec\n\n**[You can find all the code here](https://github.com/quii/learn-go-with-tests/tree/main/q-and-a/os-exec)**\n\n[keith6014](https://www.reddit.com/user/keith6014) asks on [reddit](https://www.reddit.com/r/golang/comments/aaz8ji/testdata_and_function_setup_help/)\n\n> I am executing a command using os/exec.Command() which generated XML data. The command will be executed in a function called GetData().\n\n> In order to test GetData(), I have some testdata which I created.\n\n> In my _test.go I have a TestGetData which calls GetData() but that will use os.exec, instead I would like for it to use my testdata.\n\n> What is a good way to achieve this? When calling GetData should I have a \"test\" flag mode so it will read a file ie GetData(mode string)?\n\nA few things\n\n- When something is difficult to test, it's often due to the separation of concerns not being quite right\n- Don't add \"test modes\" into your code, instead use [Dependency Injection](./dependency-injection.md) so that you can model your dependencies and separate concerns.\n\nI have taken the liberty of guessing what the code might look like\n\n```go\ntype Payload struct {\n\tMessage string `xml:\"message\"`\n}\n\nfunc GetData() string {\n\tcmd := exec.Command(\"cat\", \"msg.xml\")\n\n\tout, _ := cmd.StdoutPipe()\n\tvar payload Payload\n\tdecoder := xml.NewDecoder(out)\n\n\t// these 3 can return errors but I'm ignoring for brevity\n\tcmd.Start()\n\tdecoder.Decode(&payload)\n\tcmd.Wait()\n\n\treturn strings.ToUpper(payload.Message)\n}\n```\n\n- It uses `exec.Command` which allows you to execute an external command to the process\n- We capture the output in `cmd.StdoutPipe` which returns us a `io.ReadCloser` (this will become important)\n- The rest of the code is more or less copy and pasted from the [excellent documentation](https://golang.org/pkg/os/exec/#example_Cmd_StdoutPipe).\n    - We capture any output from stdout into an `io.ReadCloser` and then we `Start` the command and then wait for all the data to be read by calling `Wait`. In between those two calls we decode the data into our `Payload` struct.\n\nHere is what is contained inside `msg.xml`\n\n```xml\n<payload>\n    <message>Happy New Year!</message>\n</payload>\n```\n\nI wrote a simple test to show it in action\n\n```go\nfunc TestGetData(t *testing.T) {\n\tgot := GetData()\n\twant := \"HAPPY NEW YEAR!\"\n\n\tif got != want {\n\t\tt.Errorf(\"got %q, want %q\", got, want)\n\t}\n}\n```\n\n## Testable code\n\nTestable code is decoupled and single purpose. To me it feels like there are two main concerns for this code\n\n1. Retrieving the raw XML data\n2. Decoding the XML data and applying our business logic (in this case `strings.ToUpper` on the `<message>`)\n\nThe first part is just copying the example from the standard lib.\n\nThe second part is where we have our business logic and by looking at the code we can see where the \"seam\" in our logic starts; it's where we get our `io.ReadCloser`. We can use this existing abstraction to separate concerns and make our code testable.\n\n**The problem with GetData is the business logic is coupled with the means of getting the XML. To make our design better we need to decouple them**\n\nOur `TestGetData` can act as our integration test between our two concerns so we'll keep hold of that to make sure it keeps working.\n\nHere is what the newly separated code looks like\n\n```go\ntype Payload struct {\n\tMessage string `xml:\"message\"`\n}\n\nfunc GetData(data io.Reader) string {\n\tvar payload Payload\n\txml.NewDecoder(data).Decode(&payload)\n\treturn strings.ToUpper(payload.Message)\n}\n\nfunc getXMLFromCommand() io.Reader {\n\tcmd := exec.Command(\"cat\", \"msg.xml\")\n\tout, _ := cmd.StdoutPipe()\n\n\tcmd.Start()\n\tdata, _ := io.ReadAll(out)\n\tcmd.Wait()\n\n\treturn bytes.NewReader(data)\n}\n\nfunc TestGetDataIntegration(t *testing.T) {\n\tgot := GetData(getXMLFromCommand())\n\twant := \"HAPPY NEW YEAR!\"\n\n\tif got != want {\n\t\tt.Errorf(\"got %q, want %q\", got, want)\n\t}\n}\n```\n\nNow that `GetData` takes its input from just an `io.Reader` we have made it testable and it is no longer concerned how the data is retrieved; people can re-use the function with anything that returns an `io.Reader` (which is extremely common). For example we could start fetching the XML from a URL instead of the command line.\n\n```go\nfunc TestGetData(t *testing.T) {\n\tinput := strings.NewReader(`\n<payload>\n    <message>Cats are the best animal</message>\n</payload>`)\n\n\tgot := GetData(input)\n\twant := \"CATS ARE THE BEST ANIMAL\"\n\n\tif got != want {\n\t\tt.Errorf(\"got %q, want %q\", got, want)\n\t}\n}\n\n```\n\nHere is an example of a unit test for `GetData`.\n\nBy separating the concerns and using existing abstractions within Go testing our important business logic is a breeze.\n"
  },
  {
    "path": "pdf-cover.md",
    "content": "![](epub-cover.png)\n"
  },
  {
    "path": "pdf-cover.tex",
    "content": "\\includegraphics{epub-cover.png}\n\\thispagestyle{empty}\n"
  },
  {
    "path": "pointers/v1/wallet.go",
    "content": "package main\n\nimport \"fmt\"\n\n// Bitcoin represents a number of Bitcoins.\ntype Bitcoin int\n\nfunc (b Bitcoin) String() string {\n\treturn fmt.Sprintf(\"%d BTC\", b)\n}\n\n// Wallet stores the number of Bitcoin someone owns.\ntype Wallet struct {\n\tbalance Bitcoin\n}\n\n// Deposit will add some Bitcoin to a wallet.\nfunc (w *Wallet) Deposit(amount Bitcoin) {\n\tw.balance += amount\n}\n\n// Balance returns the number of Bitcoin a wallet has.\nfunc (w *Wallet) Balance() Bitcoin {\n\treturn w.balance\n}\n"
  },
  {
    "path": "pointers/v1/wallet_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n)\n\nfunc TestWallet(t *testing.T) {\n\n\twallet := Wallet{}\n\n\twallet.Deposit(Bitcoin(10))\n\n\tgot := wallet.Balance()\n\n\twant := Bitcoin(10)\n\n\tif got != want {\n\t\tt.Errorf(\"got %s want %s\", got, want)\n\t}\n}\n"
  },
  {
    "path": "pointers/v2/wallet.go",
    "content": "package main\n\nimport \"fmt\"\n\n// Bitcoin represents a number of Bitcoins.\ntype Bitcoin int\n\nfunc (b Bitcoin) String() string {\n\treturn fmt.Sprintf(\"%d BTC\", b)\n}\n\n// Wallet stores the number of Bitcoin someone owns.\ntype Wallet struct {\n\tbalance Bitcoin\n}\n\n// Deposit will add some Bitcoin to a wallet.\nfunc (w *Wallet) Deposit(amount Bitcoin) {\n\tw.balance += amount\n}\n\n// Withdraw subtracts some Bitcoin from the wallet.\nfunc (w *Wallet) Withdraw(amount Bitcoin) {\n\tw.balance -= amount\n}\n\n// Balance returns the number of Bitcoin a wallet has.\nfunc (w *Wallet) Balance() Bitcoin {\n\treturn w.balance\n}\n"
  },
  {
    "path": "pointers/v2/wallet_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n)\n\nfunc TestWallet(t *testing.T) {\n\n\tassertBalance := func(t testing.TB, wallet Wallet, want Bitcoin) {\n\t\tt.Helper()\n\t\tgot := wallet.Balance()\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %s want %s\", got, want)\n\t\t}\n\t}\n\n\tt.Run(\"deposit\", func(t *testing.T) {\n\t\twallet := Wallet{}\n\t\twallet.Deposit(Bitcoin(10))\n\t\tassertBalance(t, wallet, Bitcoin(10))\n\t})\n\n\tt.Run(\"withdraw\", func(t *testing.T) {\n\t\twallet := Wallet{balance: Bitcoin(20)}\n\t\twallet.Withdraw(10)\n\t\tassertBalance(t, wallet, Bitcoin(10))\n\t})\n\n}\n"
  },
  {
    "path": "pointers/v3/wallet.go",
    "content": "package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n)\n\n// Bitcoin represents a number of Bitcoins.\ntype Bitcoin int\n\nfunc (b Bitcoin) String() string {\n\treturn fmt.Sprintf(\"%d BTC\", b)\n}\n\n// Wallet stores the number of Bitcoin someone owns.\ntype Wallet struct {\n\tbalance Bitcoin\n}\n\n// Deposit will add some Bitcoin to a wallet.\nfunc (w *Wallet) Deposit(amount Bitcoin) {\n\tw.balance += amount\n}\n\n// Withdraw subtracts some Bitcoin from the wallet, returning an error if it cannot be performed.\nfunc (w *Wallet) Withdraw(amount Bitcoin) error {\n\n\tif amount > w.balance {\n\t\treturn errors.New(\"oh no\")\n\t}\n\n\tw.balance -= amount\n\treturn nil\n}\n\n// Balance returns the number of Bitcoin a wallet has.\nfunc (w *Wallet) Balance() Bitcoin {\n\treturn w.balance\n}\n"
  },
  {
    "path": "pointers/v3/wallet_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n)\n\nfunc TestWallet(t *testing.T) {\n\n\tassertBalance := func(t testing.TB, wallet Wallet, want Bitcoin) {\n\t\tt.Helper()\n\t\tgot := wallet.Balance()\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %s want %s\", got, want)\n\t\t}\n\t}\n\n\tassertError := func(t testing.TB, err error) {\n\t\tt.Helper()\n\t\tif err == nil {\n\t\t\tt.Error(\"wanted an error but didn't get one\")\n\t\t}\n\t}\n\n\tt.Run(\"deposit\", func(t *testing.T) {\n\t\twallet := Wallet{}\n\t\twallet.Deposit(Bitcoin(10))\n\n\t\tassertBalance(t, wallet, Bitcoin(10))\n\t})\n\n\tt.Run(\"withdraw with funds\", func(t *testing.T) {\n\t\twallet := Wallet{Bitcoin(20)}\n\t\twallet.Withdraw(Bitcoin(10))\n\n\t\tassertBalance(t, wallet, Bitcoin(10))\n\t})\n\n\tt.Run(\"withdraw insufficient funds\", func(t *testing.T) {\n\t\tstartingBalance := Bitcoin(20)\n\t\twallet := Wallet{startingBalance}\n\t\terr := wallet.Withdraw(Bitcoin(100))\n\n\t\tassertError(t, err)\n\t\tassertBalance(t, wallet, startingBalance)\n\t})\n}\n"
  },
  {
    "path": "pointers/v4/wallet.go",
    "content": "package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n)\n\n// Bitcoin represents a number of Bitcoins.\ntype Bitcoin int\n\nfunc (b Bitcoin) String() string {\n\treturn fmt.Sprintf(\"%d BTC\", b)\n}\n\n// Wallet stores the number of Bitcoin someone owns.\ntype Wallet struct {\n\tbalance Bitcoin\n}\n\n// Deposit will add some Bitcoin to a wallet.\nfunc (w *Wallet) Deposit(amount Bitcoin) {\n\tw.balance += amount\n}\n\n// ErrInsufficientFunds means a wallet does not have enough Bitcoin to perform a withdraw.\nvar ErrInsufficientFunds = errors.New(\"cannot withdraw, insufficient funds\")\n\n// Withdraw subtracts some Bitcoin from the wallet, returning an error if it cannot be performed.\nfunc (w *Wallet) Withdraw(amount Bitcoin) error {\n\n\tif amount > w.balance {\n\t\treturn ErrInsufficientFunds\n\t}\n\n\tw.balance -= amount\n\treturn nil\n}\n\n// Balance returns the number of Bitcoin a wallet has.\nfunc (w *Wallet) Balance() Bitcoin {\n\treturn w.balance\n}\n"
  },
  {
    "path": "pointers/v4/wallet_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n)\n\nfunc TestWallet(t *testing.T) {\n\n\tt.Run(\"deposit\", func(t *testing.T) {\n\t\twallet := Wallet{}\n\t\twallet.Deposit(Bitcoin(10))\n\n\t\tassertBalance(t, wallet, Bitcoin(10))\n\t})\n\n\tt.Run(\"withdraw with funds\", func(t *testing.T) {\n\t\twallet := Wallet{Bitcoin(20)}\n\t\terr := wallet.Withdraw(Bitcoin(10))\n\n\t\tassertBalance(t, wallet, Bitcoin(10))\n\t\tassertNoError(t, err)\n\t})\n\n\tt.Run(\"withdraw insufficient funds\", func(t *testing.T) {\n\t\tstartingBalance := Bitcoin(20)\n\t\twallet := Wallet{startingBalance}\n\t\terr := wallet.Withdraw(Bitcoin(100))\n\n\t\tassertBalance(t, wallet, startingBalance)\n\t\tassertError(t, err, ErrInsufficientFunds)\n\t})\n}\n\nfunc assertBalance(t testing.TB, wallet Wallet, want Bitcoin) {\n\tt.Helper()\n\tgot := wallet.Balance()\n\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n\nfunc assertNoError(t testing.TB, got error) {\n\tt.Helper()\n\tif got != nil {\n\t\tt.Fatal(\"got an error but didn't want one\")\n\t}\n}\n\nfunc assertError(t testing.TB, got, want error) {\n\tt.Helper()\n\tif got == nil {\n\t\tt.Fatal(\"didn't get an error but wanted one\")\n\t}\n\n\tif got != want {\n\t\tt.Errorf(\"got %q, want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "pointers-and-errors.md",
    "content": "# Pointers & errors\n\n[**You can find all the code for this chapter here**](https://github.com/quii/learn-go-with-tests/tree/main/pointers)\n\nWe learned about structs in the last section which let us capture a number of values related around a concept.\n\nAt some point you may wish to use structs to manage state, exposing methods to let users change the state in a way that you can control.\n\n**Fintech loves Go** and uhhh bitcoins? So let's show what an amazing banking system we can make.\n\nLet's make a `Wallet` struct which lets us deposit `Bitcoin`.\n\n## Write the test first\n\n```go\nfunc TestWallet(t *testing.T) {\n\n\twallet := Wallet{}\n\n\twallet.Deposit(10)\n\n\tgot := wallet.Balance()\n\twant := 10\n\n\tif got != want {\n\t\tt.Errorf(\"got %d want %d\", got, want)\n\t}\n}\n```\n\nIn the [previous example](structs-methods-and-interfaces.md) we accessed fields directly with the field name, however in our _very secure wallet_ we don't want to expose our inner state to the rest of the world. We want to control access via methods.\n\n## Try to run the test\n\n`./wallet_test.go:7:12: undefined: Wallet`\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\nThe compiler doesn't know what a `Wallet` is so let's tell it.\n\n```go\ntype Wallet struct{}\n```\n\nNow we've made our wallet, try and run the test again\n\n```\n./wallet_test.go:9:8: wallet.Deposit undefined (type Wallet has no field or method Deposit)\n./wallet_test.go:11:15: wallet.Balance undefined (type Wallet has no field or method Balance)\n```\n\nWe need to define these methods.\n\nRemember to only do enough to make the tests run. We need to make sure our test fails correctly with a clear error message.\n\n```go\nfunc (w Wallet) Deposit(amount int) {\n\n}\n\nfunc (w Wallet) Balance() int {\n\treturn 0\n}\n```\n\nIf this syntax is unfamiliar go back and read the structs section.\n\nThe tests should now compile and run\n\n`wallet_test.go:15: got 0 want 10`\n\n## Write enough code to make it pass\n\nWe will need some kind of _balance_ variable in our struct to store the state\n\n```go\ntype Wallet struct {\n\tbalance int\n}\n```\n\nIn Go if a symbol (variables, types, functions et al) starts with a lowercase symbol then it is private _outside the package it's defined in_.\n\nIn our case we want our methods to be able to manipulate this value, but no one else.\n\nRemember we can access the internal `balance` field in the struct using the \"receiver\" variable.\n\n```go\nfunc (w Wallet) Deposit(amount int) {\n\tw.balance += amount\n}\n\nfunc (w Wallet) Balance() int {\n\treturn w.balance\n}\n```\n\nWith our career in fintech secured, run the test suite and bask in the passing test\n\n`wallet_test.go:15: got 0 want 10`\n\n### That's not quite right\n\nWell this is confusing, our code looks like it should work. We add the new amount onto our balance and then the balance method should return the current state of it.\n\nIn Go, **when you call a function or a method the arguments are** _**copied**_.\n\nWhen calling `func (w Wallet) Deposit(amount int)` the `w` is a copy of whatever we called the method from.\n\nWithout getting too computer-sciency, when you create a value - like a wallet, it is stored somewhere in memory. You can find out what the _address_ of that bit of memory with `&myVal`.\n\nExperiment by adding some prints to your code\n\n```go\nfunc TestWallet(t *testing.T) {\n\n\twallet := Wallet{}\n\n\twallet.Deposit(10)\n\n\tgot := wallet.Balance()\n\n\tfmt.Printf(\"address of balance in test is %p \\n\", &wallet.balance)\n\n\twant := 10\n\n\tif got != want {\n\t\tt.Errorf(\"got %d want %d\", got, want)\n\t}\n}\n```\n\n```go\nfunc (w Wallet) Deposit(amount int) {\n\tfmt.Printf(\"address of balance in Deposit is %p \\n\", &w.balance)\n\tw.balance += amount\n}\n```\n\nThe `%p` placeholder prints memory addresses in base 16 notation with leading `0x`s and the  escape character prints a new line. Note that we get the pointer (memory address) of something by placing an `&` character at the beginning of the symbol.\n\nNow re-run the test\n\n```\naddress of balance in Deposit is 0xc420012268\naddress of balance in test is 0xc420012260\n```\n\nYou can see that the addresses of the two balances are different. So when we change the value of the balance inside the code, we are working on a copy of what came from the test. Therefore the balance in the test is unchanged.\n\nWe can fix this with _pointers_. [Pointers](https://gobyexample.com/pointers) let us _point_ to some values and then let us change them. So rather than taking a copy of the whole Wallet, we instead take a pointer to that wallet so that we can change the original values within it.\n\n```go\nfunc (w *Wallet) Deposit(amount int) {\n\tw.balance += amount\n}\n\nfunc (w *Wallet) Balance() int {\n\treturn w.balance\n}\n```\n\nThe difference is the receiver type is `*Wallet` rather than `Wallet` which you can read as \"a pointer to a wallet\".\n\nTry and re-run the tests and they should pass.\n\nNow you might wonder, why did they pass? We didn't dereference the pointer in the function, like so:\n\n```go\nfunc (w *Wallet) Balance() int {\n\treturn (*w).balance\n}\n```\n\nand seemingly addressed the object directly. In fact, the code above using `(*w)` is absolutely valid. However, the makers of Go deemed this notation cumbersome, so the language permits us to write `w.balance`, without an explicit dereference. These pointers to structs even have their own name: _struct pointers_ and they are [automatically dereferenced](https://golang.org/ref/spec#Method_values).\n\nTechnically you do not need to change `Balance` to use a pointer receiver as taking a copy of the balance is fine. However, by convention you should keep your method receiver types the same for consistency.\n\n## Refactor\n\nWe said we were making a Bitcoin wallet but we have not mentioned them so far. We've been using `int` because they're a good type for counting things!\n\nIt seems a bit overkill to create a `struct` for this. `int` is fine in terms of the way it works but it's not descriptive.\n\nGo lets you create new types from existing ones.\n\nThe syntax is `type MyName OriginalType`\n\n```go\ntype Bitcoin int\n\ntype Wallet struct {\n\tbalance Bitcoin\n}\n\nfunc (w *Wallet) Deposit(amount Bitcoin) {\n\tw.balance += amount\n}\n\nfunc (w *Wallet) Balance() Bitcoin {\n\treturn w.balance\n}\n```\n\n```go\nfunc TestWallet(t *testing.T) {\n\n\twallet := Wallet{}\n\n\twallet.Deposit(Bitcoin(10))\n\n\tgot := wallet.Balance()\n\n\twant := Bitcoin(10)\n\n\tif got != want {\n\t\tt.Errorf(\"got %d want %d\", got, want)\n\t}\n}\n```\n\nTo make `Bitcoin` you just use the syntax `Bitcoin(999)`.\n\nBy doing this we're making a new type and we can declare _methods_ on them. This can be very useful when you want to add some domain specific functionality on top of existing types.\n\nLet's implement [Stringer](https://golang.org/pkg/fmt/#Stringer) on Bitcoin\n\n```go\ntype Stringer interface {\n\tString() string\n}\n```\n\nThis interface is defined in the `fmt` package and lets you define how your type is printed when used with the `%s` format string in prints.\n\n```go\nfunc (b Bitcoin) String() string {\n\treturn fmt.Sprintf(\"%d BTC\", b)\n}\n```\n\nAs you can see, the syntax for creating a method on a type declaration is the same as it is on a struct.\n\nNext we need to update our test format strings so they will use `String()` instead.\n\n```go\n\tif got != want {\n\t\tt.Errorf(\"got %s want %s\", got, want)\n\t}\n```\n\nTo see this in action, deliberately break the test so we can see it\n\n`wallet_test.go:18: got 10 BTC want 20 BTC`\n\nThis makes it clearer what's going on in our test.\n\nThe next requirement is for a `Withdraw` function.\n\n## Write the test first\n\nPretty much the opposite of `Deposit()`\n\n```go\nfunc TestWallet(t *testing.T) {\n\n\tt.Run(\"deposit\", func(t *testing.T) {\n\t\twallet := Wallet{}\n\n\t\twallet.Deposit(Bitcoin(10))\n\n\t\tgot := wallet.Balance()\n\n\t\twant := Bitcoin(10)\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %s want %s\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"withdraw\", func(t *testing.T) {\n\t\twallet := Wallet{balance: Bitcoin(20)}\n\n\t\twallet.Withdraw(Bitcoin(10))\n\n\t\tgot := wallet.Balance()\n\n\t\twant := Bitcoin(10)\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %s want %s\", got, want)\n\t\t}\n\t})\n}\n```\n\n## Try to run the test\n\n`./wallet_test.go:26:9: wallet.Withdraw undefined (type Wallet has no field or method Withdraw)`\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\n```go\nfunc (w *Wallet) Withdraw(amount Bitcoin) {\n\n}\n```\n\n`wallet_test.go:33: got 20 BTC want 10 BTC`\n\n## Write enough code to make it pass\n\n```go\nfunc (w *Wallet) Withdraw(amount Bitcoin) {\n\tw.balance -= amount\n}\n```\n\n## Refactor\n\nThere's some duplication in our tests, lets refactor that out.\n\n```go\nfunc TestWallet(t *testing.T) {\n\n\tassertBalance := func(t testing.TB, wallet Wallet, want Bitcoin) {\n\t\tt.Helper()\n\t\tgot := wallet.Balance()\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %s want %s\", got, want)\n\t\t}\n\t}\n\n\tt.Run(\"deposit\", func(t *testing.T) {\n\t\twallet := Wallet{}\n\t\twallet.Deposit(Bitcoin(10))\n\t\tassertBalance(t, wallet, Bitcoin(10))\n\t})\n\n\tt.Run(\"withdraw\", func(t *testing.T) {\n\t\twallet := Wallet{balance: Bitcoin(20)}\n\t\twallet.Withdraw(Bitcoin(10))\n\t\tassertBalance(t, wallet, Bitcoin(10))\n\t})\n\n}\n```\n\nWhat should happen if you try to `Withdraw` more than is left in the account? For now, our requirement is to assume there is not an overdraft facility.\n\nHow do we signal a problem when using `Withdraw`?\n\nIn Go, if you want to indicate an error it is idiomatic for your function to return an `err` for the caller to check and act on.\n\nLet's try this out in a test.\n\n## Write the test first\n\n```go\nt.Run(\"withdraw insufficient funds\", func(t *testing.T) {\n\tstartingBalance := Bitcoin(20)\n\twallet := Wallet{startingBalance}\n\terr := wallet.Withdraw(Bitcoin(100))\n\n\tassertBalance(t, wallet, startingBalance)\n\n\tif err == nil {\n\t\tt.Error(\"wanted an error but didn't get one\")\n\t}\n})\n```\n\nWe want `Withdraw` to return an error _if_ you try to take out more than you have and the balance should stay the same.\n\nWe then check an error has returned by failing the test if it is `nil`.\n\n`nil` is synonymous with `null` from other programming languages. Errors can be `nil` because the return type of `Withdraw` will be `error`, which is an interface. If you see a function that takes arguments or returns values that are interfaces, they can be nillable.\n\nLike `null` if you try to access a value that is `nil` it will throw a **runtime panic**. This is bad! You should make sure that you check for nils.\n\n## Try and run the test\n\n`./wallet_test.go:31:25: wallet.Withdraw(Bitcoin(100)) used as value`\n\nThe wording is perhaps a little unclear, but our previous intent with `Withdraw` was just to call it, it will never return a value. To make this compile we will need to change it so it has a return type.\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\n```go\nfunc (w *Wallet) Withdraw(amount Bitcoin) error {\n\tw.balance -= amount\n\treturn nil\n}\n```\n\nAgain, it is very important to just write enough code to satisfy the compiler. We correct our `Withdraw` method to return `error` and for now we have to return _something_ so let's just return `nil`.\n\n## Write enough code to make it pass\n\n```go\nfunc (w *Wallet) Withdraw(amount Bitcoin) error {\n\n\tif amount > w.balance {\n\t\treturn errors.New(\"oh no\")\n\t}\n\n\tw.balance -= amount\n\treturn nil\n}\n```\n\nRemember to import `errors` into your code.\n\n`errors.New` creates a new `error` with a message of your choosing.\n\n## Refactor\n\nLet's make a quick test helper for our error check to improve the test's readability\n\n```go\nassertError := func(t testing.TB, err error) {\n\tt.Helper()\n\tif err == nil {\n\t\tt.Error(\"wanted an error but didn't get one\")\n\t}\n}\n```\n\nAnd in our test\n\n```go\nt.Run(\"withdraw insufficient funds\", func(t *testing.T) {\n\tstartingBalance := Bitcoin(20)\n\twallet := Wallet{startingBalance}\n\terr := wallet.Withdraw(Bitcoin(100))\n\n\tassertError(t, err)\n\tassertBalance(t, wallet, startingBalance)\n})\n```\n\nHopefully when returning an error of \"oh no\" you were thinking that we _might_ iterate on that because it doesn't seem that useful to return.\n\nAssuming that the error ultimately gets returned to the user, let's update our test to assert on some kind of error message rather than just the existence of an error.\n\n## Write the test first\n\nUpdate our helper for a `string` to compare against.\n\n```go\nassertError := func(t testing.TB, got error, want string) {\n\tt.Helper()\n\n\tif got == nil {\n\t\tt.Fatal(\"didn't get an error but wanted one\")\n\t}\n\n\tif got.Error() != want {\n\t\tt.Errorf(\"got %q, want %q\", got, want)\n\t}\n}\n```\n\nAs you can see `Error`s can be converted to a string with the `.Error()` method, which we do in order to compare it with the string we want. We are also making sure that the error is not `nil` to ensure we don't call `.Error()` on `nil`.\n\nAnd then update the caller\n\n```go\nt.Run(\"withdraw insufficient funds\", func(t *testing.T) {\n\tstartingBalance := Bitcoin(20)\n\twallet := Wallet{startingBalance}\n\terr := wallet.Withdraw(Bitcoin(100))\n\n\tassertError(t, err, \"cannot withdraw, insufficient funds\")\n\tassertBalance(t, wallet, startingBalance)\n})\n```\n\nWe've introduced `t.Fatal` which will stop the test if it is called. This is because we don't want to make any more assertions on the error returned if there isn't one around. Without this the test would carry on to the next step and panic because of a nil pointer.\n\n## Try to run the test\n\n`wallet_test.go:61: got err 'oh no' want 'cannot withdraw, insufficient funds'`\n\n## Write enough code to make it pass\n\n```go\nfunc (w *Wallet) Withdraw(amount Bitcoin) error {\n\n\tif amount > w.balance {\n\t\treturn errors.New(\"cannot withdraw, insufficient funds\")\n\t}\n\n\tw.balance -= amount\n\treturn nil\n}\n```\n\n## Refactor\n\nWe have duplication of the error message in both the test code and the `Withdraw` code.\n\nIt would be really annoying for the test to fail if someone wanted to re-word the error and it's just too much detail for our test. We don't _really_ care what the exact wording is, just that some kind of meaningful error around withdrawing is returned given a certain condition.\n\nIn Go, errors are values, so we can refactor it out into a variable and have a single source of truth for it.\n\n```go\nvar ErrInsufficientFunds = errors.New(\"cannot withdraw, insufficient funds\")\n\nfunc (w *Wallet) Withdraw(amount Bitcoin) error {\n\n\tif amount > w.balance {\n\t\treturn ErrInsufficientFunds\n\t}\n\n\tw.balance -= amount\n\treturn nil\n}\n```\n\nThe `var` keyword allows us to define values global to the package.\n\nThis is a positive change in itself because now our `Withdraw` function looks very clear.\n\nNext we can refactor our test code to use this value instead of specific strings.\n\n```go\nfunc TestWallet(t *testing.T) {\n\n\tt.Run(\"deposit\", func(t *testing.T) {\n\t\twallet := Wallet{}\n\t\twallet.Deposit(Bitcoin(10))\n\t\tassertBalance(t, wallet, Bitcoin(10))\n\t})\n\n\tt.Run(\"withdraw with funds\", func(t *testing.T) {\n\t\twallet := Wallet{Bitcoin(20)}\n\t\twallet.Withdraw(Bitcoin(10))\n\t\tassertBalance(t, wallet, Bitcoin(10))\n\t})\n\n\tt.Run(\"withdraw insufficient funds\", func(t *testing.T) {\n\t\twallet := Wallet{Bitcoin(20)}\n\t\terr := wallet.Withdraw(Bitcoin(100))\n\n\t\tassertError(t, err, ErrInsufficientFunds)\n\t\tassertBalance(t, wallet, Bitcoin(20))\n\t})\n}\n\nfunc assertBalance(t testing.TB, wallet Wallet, want Bitcoin) {\n\tt.Helper()\n\tgot := wallet.Balance()\n\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n\nfunc assertError(t testing.TB, got, want error) {\n\tt.Helper()\n\tif got == nil {\n\t\tt.Fatal(\"didn't get an error but wanted one\")\n\t}\n\n\tif got != want {\n\t\tt.Errorf(\"got %q, want %q\", got, want)\n\t}\n}\n```\n\nAnd now the test is easier to follow too.\n\nI have moved the helpers out of the main test function just so when someone opens up a file they can start reading our assertions first, rather than some helpers.\n\nAnother useful property of tests is that they help us understand the _real_ usage of our code so we can make sympathetic code. We can see here that a developer can simply call our code and do an equals check to `ErrInsufficientFunds` and act accordingly.\n\n### Unchecked errors\n\nWhilst the Go compiler helps you a lot, sometimes there are things you can still miss and error handling can sometimes be tricky.\n\nThere is one scenario we have not tested. To find it, run the following in a terminal to install `errcheck`, one of many linters available for Go.\n\n`go install github.com/kisielk/errcheck@latest`\n\nThen, inside the directory with your code run `errcheck .`\n\nYou should get something like\n\n`wallet_test.go:17:18: wallet.Withdraw(Bitcoin(10))`\n\nWhat this is telling us is that we have not checked the error being returned on that line of code. That line of code on my computer corresponds to our normal withdraw scenario because we have not checked that if the `Withdraw` is successful that an error is _not_ returned.\n\nHere is the final test code that accounts for this.\n\n```go\nfunc TestWallet(t *testing.T) {\n\n\tt.Run(\"deposit\", func(t *testing.T) {\n\t\twallet := Wallet{}\n\t\twallet.Deposit(Bitcoin(10))\n\n\t\tassertBalance(t, wallet, Bitcoin(10))\n\t})\n\n\tt.Run(\"withdraw with funds\", func(t *testing.T) {\n\t\twallet := Wallet{Bitcoin(20)}\n\t\terr := wallet.Withdraw(Bitcoin(10))\n\n\t\tassertNoError(t, err)\n\t\tassertBalance(t, wallet, Bitcoin(10))\n\t})\n\n\tt.Run(\"withdraw insufficient funds\", func(t *testing.T) {\n\t\twallet := Wallet{Bitcoin(20)}\n\t\terr := wallet.Withdraw(Bitcoin(100))\n\n\t\tassertError(t, err, ErrInsufficientFunds)\n\t\tassertBalance(t, wallet, Bitcoin(20))\n\t})\n}\n\nfunc assertBalance(t testing.TB, wallet Wallet, want Bitcoin) {\n\tt.Helper()\n\tgot := wallet.Balance()\n\n\tif got != want {\n\t\tt.Errorf(\"got %s want %s\", got, want)\n\t}\n}\n\nfunc assertNoError(t testing.TB, got error) {\n\tt.Helper()\n\tif got != nil {\n\t\tt.Fatal(\"got an error but didn't want one\")\n\t}\n}\n\nfunc assertError(t testing.TB, got error, want error) {\n\tt.Helper()\n\tif got == nil {\n\t\tt.Fatal(\"didn't get an error but wanted one\")\n\t}\n\n\tif got != want {\n\t\tt.Errorf(\"got %s, want %s\", got, want)\n\t}\n}\n```\n\n## Wrapping up\n\n### Pointers\n\n* Go copies values when you pass them to functions/methods, so if you're writing a function that needs to mutate state you'll need it to take a pointer to the thing you want to change.\n* The fact that Go takes a copy of values is useful a lot of the time but sometimes you won't want your system to make a copy of something, in which case you need to pass a reference. Examples include referencing very large data structures or things where only one instance is necessary (like database connection pools).\n\n### nil\n\n* Pointers can be nil\n* When a function returns a pointer to something, you need to make sure you check if it's nil or you might raise a runtime exception - the compiler won't help you here.\n* Useful for when you want to describe a value that could be missing\n\n### Errors\n\n* Errors are the way to signify failure when calling a function/method.\n* By listening to our tests we concluded that checking for a string in an error would result in a flaky test. So we refactored our implementation to use a meaningful value instead and this resulted in easier to test code and concluded this would be easier for users of our API too.\n* This is not the end of the story with error handling, you can do more sophisticated things but this is just an intro. Later sections will cover more strategies.\n* [Don’t just check errors, handle them gracefully](https://dave.cheney.net/2016/04/27/dont-just-check-errors-handle-them-gracefully)\n\n### Create new types from existing ones\n\n* Useful for adding more domain specific meaning to values\n* Can let you implement interfaces\n\nPointers and errors are a big part of writing Go that you need to get comfortable with. Thankfully the compiler will _usually_ help you out if you do something wrong, just take your time and read the error.\n"
  },
  {
    "path": "q-and-a/context-aware-reader/context_aware_reader.go",
    "content": "package cancelreader\n\nimport (\n\t\"context\"\n\t\"io\"\n)\n\n// NewCancellableReader will stop reading to rdr if ctx is cancelled.\nfunc NewCancellableReader(ctx context.Context, rdr io.Reader) io.Reader {\n\treturn &readerCtx{\n\t\tctx:      ctx,\n\t\tdelegate: rdr,\n\t}\n}\n\ntype readerCtx struct {\n\tctx      context.Context\n\tdelegate io.Reader\n}\n\nfunc (r *readerCtx) Read(p []byte) (n int, err error) {\n\tif err := r.ctx.Err(); err != nil {\n\t\treturn 0, err\n\t}\n\treturn r.delegate.Read(p)\n}\n"
  },
  {
    "path": "q-and-a/context-aware-reader/context_aware_reader_test.go",
    "content": "package cancelreader\n\nimport (\n\t\"context\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestContextAwareReader(t *testing.T) {\n\tt.Run(\"behaves like a normal reader\", func(t *testing.T) {\n\t\trdr := NewCancellableReader(context.Background(), strings.NewReader(\"123456\"))\n\t\tgot := make([]byte, 3)\n\t\t_, err := rdr.Read(got)\n\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tassertBufferHas(t, got, \"123\")\n\n\t\t_, err = rdr.Read(got)\n\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tassertBufferHas(t, got, \"456\")\n\t})\n\n\tt.Run(\"stops reading when cancelled\", func(t *testing.T) {\n\t\tctx, cancel := context.WithCancel(context.Background())\n\t\trdr := NewCancellableReader(ctx, strings.NewReader(\"123456\"))\n\t\tgot := make([]byte, 3)\n\t\t_, err := rdr.Read(got)\n\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tassertBufferHas(t, got, \"123\")\n\n\t\tcancel()\n\n\t\tn, err := rdr.Read(got)\n\n\t\tif err == nil {\n\t\t\tt.Error(\"expected an error after cancellation but didn't get one\")\n\t\t}\n\n\t\tif n > 0 {\n\t\t\tt.Errorf(\"expected 0 bytes to be read after cancellation but %d were read\", n)\n\t\t}\n\t})\n}\n\nfunc assertBufferHas(t testing.TB, buf []byte, want string) {\n\tt.Helper()\n\tgot := string(buf)\n\tif got != want {\n\t\tt.Errorf(\"got %q, want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "q-and-a/error-types/error-types_test.go",
    "content": "package errortypes\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\ntype BadStatusError struct {\n\tURL    string\n\tStatus int\n}\n\nfunc (b BadStatusError) Error() string {\n\treturn fmt.Sprintf(\"did not get 200 from %s, got %d\", b.URL, b.Status)\n}\n\n// DumbGetter will get the string body of url if it gets a 200.\nfunc DumbGetter(url string) (string, error) {\n\tres, err := http.Get(url)\n\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"problem fetching from %s, %v\", url, err)\n\t}\n\n\tif res.StatusCode != http.StatusOK {\n\t\treturn \"\", BadStatusError{URL: url, Status: res.StatusCode}\n\t}\n\n\tdefer res.Body.Close()\n\tbody, _ := io.ReadAll(res.Body) // ignoring err for brevity\n\n\treturn string(body), nil\n}\n\nfunc TestDumbGetter(t *testing.T) {\n\tt.Run(\"when you don't get a 200 you get a status error\", func(t *testing.T) {\n\n\t\tsvr := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {\n\t\t\tres.WriteHeader(http.StatusTeapot)\n\t\t}))\n\t\tdefer svr.Close()\n\n\t\t_, err := DumbGetter(svr.URL)\n\n\t\tif err == nil {\n\t\t\tt.Fatal(\"expected an error\")\n\t\t}\n\n\t\tgot, isStatusErr := err.(BadStatusError)\n\n\t\tif !isStatusErr {\n\t\t\tt.Fatalf(\"was not a BadStatusError, got %T\", err)\n\t\t}\n\n\t\twant := BadStatusError{URL: svr.URL, Status: http.StatusTeapot}\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %v, want %v\", got, want)\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "q-and-a/error-types/v2/error-types_test.go",
    "content": "package errortypes\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\ntype BadStatusError struct {\n\tURL    string\n\tStatus int\n}\n\nfunc (b BadStatusError) Error() string {\n\treturn fmt.Sprintf(\"did not get 200 from %s, got %d\", b.URL, b.Status)\n}\n\n// DumbGetter will get the string body of url if it gets a 200.\nfunc DumbGetter(url string) (string, error) {\n\tres, err := http.Get(url)\n\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"problem fetching from %s, %v\", url, err)\n\t}\n\n\tif res.StatusCode != http.StatusOK {\n\t\treturn \"\", BadStatusError{URL: url, Status: res.StatusCode}\n\t}\n\n\tdefer res.Body.Close()\n\tbody, _ := io.ReadAll(res.Body) // ignoring err for brevity\n\n\treturn string(body), nil\n}\n\nfunc TestDumbGetter(t *testing.T) {\n\n\tt.Run(\"when you don't get a 200 you get a status error\", func(t *testing.T) {\n\n\t\tsvr := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {\n\t\t\tres.WriteHeader(http.StatusTeapot)\n\t\t}))\n\t\tdefer svr.Close()\n\n\t\t_, err := DumbGetter(svr.URL)\n\n\t\tif err == nil {\n\t\t\tt.Fatal(\"expected an error\")\n\t\t}\n\n\t\tvar got BadStatusError\n\t\tisBadStatusError := errors.As(err, &got)\n\t\twant := BadStatusError{URL: svr.URL, Status: http.StatusTeapot}\n\n\t\tif !isBadStatusError {\n\t\t\tt.Fatalf(\"was not a BadStatusError, got %T\", err)\n\t\t}\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %v, want %v\", got, want)\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "q-and-a/http-handlers-revisited/basic_test.go",
    "content": "package main\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc Teapot(res http.ResponseWriter, req *http.Request) {\n\tres.WriteHeader(http.StatusTeapot)\n}\n\nfunc TestTeapotHandler(t *testing.T) {\n\treq := httptest.NewRequest(http.MethodGet, \"/\", nil)\n\tres := httptest.NewRecorder()\n\n\tTeapot(res, req)\n\n\tif res.Code != http.StatusTeapot {\n\t\tt.Errorf(\"got status %d but wanted %d\", res.Code, http.StatusTeapot)\n\t}\n}\n"
  },
  {
    "path": "q-and-a/http-handlers-revisited/still_basic.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n)\n\n// User represents a person in our system.\ntype User struct {\n\tName string\n}\n\n// UserService provides ways of working with users.\ntype UserService interface {\n\tRegister(user User) (insertedID string, err error)\n}\n\n// UserServer provides an HTTP API for working with users.\ntype UserServer struct {\n\tservice UserService\n}\n\n// NewUserServer creates a UserServer.\nfunc NewUserServer(service UserService) *UserServer {\n\treturn &UserServer{service: service}\n}\n\n// RegisterUser is a http handler for storing users.\nfunc (u *UserServer) RegisterUser(w http.ResponseWriter, r *http.Request) {\n\tdefer r.Body.Close()\n\n\tvar newUser User\n\terr := json.NewDecoder(r.Body).Decode(&newUser)\n\n\tif err != nil {\n\t\thttp.Error(w, fmt.Sprintf(\"could not decode user payload: %v\", err), http.StatusBadRequest)\n\t\treturn\n\t}\n\n\tinsertedID, err := u.service.Register(newUser)\n\n\tif err != nil {\n\t\t//todo: handle different kinds of errors differently\n\t\thttp.Error(w, fmt.Sprintf(\"problem registering new user: %v\", err), http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tw.WriteHeader(http.StatusCreated)\n\tfmt.Fprint(w, insertedID)\n}\n\n// MongoUserService provides storage functionality for Users.\ntype MongoUserService struct {\n}\n\n// NewMongoUserService creates a new MongoUserService managing connection pools etc probably!.\nfunc NewMongoUserService() *MongoUserService {\n\t//todo: pass in DB URL as argument to this function\n\t//todo: connect to db, create a connection pool\n\treturn &MongoUserService{}\n}\n\n// Register will store a user in mongo.\nfunc (m MongoUserService) Register(user User) (insertedID string, err error) {\n\t// use m.mongoConnection to perform queries\n\tpanic(\"implement me\")\n}\n\nfunc main() {\n\tmongoService := NewMongoUserService()\n\tserver := NewUserServer(mongoService)\n\tlog.Fatal(http.ListenAndServe(\":8000\", http.HandlerFunc(server.RegisterUser)))\n}\n"
  },
  {
    "path": "q-and-a/http-handlers-revisited/still_basic_test.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n)\n\ntype MockUserService struct {\n\tRegisterFunc    func(user User) (string, error)\n\tUsersRegistered []User\n}\n\nfunc (m *MockUserService) Register(user User) (insertedID string, err error) {\n\tm.UsersRegistered = append(m.UsersRegistered, user)\n\treturn m.RegisterFunc(user)\n}\n\nfunc TestRegisterUser(t *testing.T) {\n\tt.Run(\"can register valid users\", func(t *testing.T) {\n\t\tuser := User{Name: \"CJ\"}\n\t\texpectedInsertedID := \"whatever\"\n\n\t\tservice := &MockUserService{\n\t\t\tRegisterFunc: func(user User) (string, error) {\n\t\t\t\treturn expectedInsertedID, nil\n\t\t\t},\n\t\t}\n\t\tserver := NewUserServer(service)\n\n\t\treq := httptest.NewRequest(http.MethodGet, \"/\", userToJSON(user))\n\t\tres := httptest.NewRecorder()\n\n\t\tserver.RegisterUser(res, req)\n\n\t\tassertStatus(t, res.Code, http.StatusCreated)\n\n\t\tif res.Body.String() != expectedInsertedID {\n\t\t\tt.Errorf(\"expected body of %q but got %q\", res.Body.String(), expectedInsertedID)\n\t\t}\n\n\t\tif len(service.UsersRegistered) != 1 {\n\t\t\tt.Fatalf(\"expected 1 user added but got %d\", len(service.UsersRegistered))\n\t\t}\n\n\t\tif !reflect.DeepEqual(service.UsersRegistered[0], user) {\n\t\t\tt.Errorf(\"the user registered %+v was not what was expected %+v\", service.UsersRegistered[0], user)\n\t\t}\n\t})\n\n\tt.Run(\"returns 400 bad request if body is not valid user JSON\", func(t *testing.T) {\n\t\tserver := NewUserServer(nil)\n\n\t\treq := httptest.NewRequest(http.MethodGet, \"/\", strings.NewReader(\"trouble will find me\"))\n\t\tres := httptest.NewRecorder()\n\n\t\tserver.RegisterUser(res, req)\n\n\t\tassertStatus(t, res.Code, http.StatusBadRequest)\n\t})\n\n\tt.Run(\"returns a 500 internal server error if the service fails\", func(t *testing.T) {\n\t\tuser := User{Name: \"CJ\"}\n\n\t\tservice := &MockUserService{\n\t\t\tRegisterFunc: func(user User) (string, error) {\n\t\t\t\treturn \"\", errors.New(\"couldn't add new user\")\n\t\t\t},\n\t\t}\n\t\tserver := NewUserServer(service)\n\n\t\treq := httptest.NewRequest(http.MethodGet, \"/\", userToJSON(user))\n\t\tres := httptest.NewRecorder()\n\n\t\tserver.RegisterUser(res, req)\n\n\t\tassertStatus(t, res.Code, http.StatusInternalServerError)\n\t})\n}\n\nfunc assertStatus(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"wanted http status %d but got %d\", got, want)\n\t}\n}\n\nfunc userToJSON(user User) io.Reader {\n\tstuff, _ := json.Marshal(user)\n\treturn bytes.NewReader(stuff)\n}\n"
  },
  {
    "path": "q-and-a/os-exec/msg.xml",
    "content": "<payload>\n    <message>Happy New Year!</message>\n</payload>\n"
  },
  {
    "path": "q-and-a/os-exec/os-exec_test.go",
    "content": "package osexec\n\nimport (\n\t\"bytes\"\n\t\"encoding/xml\"\n\t\"io\"\n\t\"os/exec\"\n\t\"strings\"\n\t\"testing\"\n)\n\ntype Payload struct {\n\tMessage string `xml:\"message\"`\n}\n\nfunc GetData(data io.Reader) string {\n\tvar payload Payload\n\txml.NewDecoder(data).Decode(&payload)\n\treturn strings.ToUpper(payload.Message)\n}\n\nfunc getXMLFromCommand() io.Reader {\n\tcmd := exec.Command(\"cat\", \"msg.xml\")\n\tout, _ := cmd.StdoutPipe()\n\n\tcmd.Start()\n\tdata, _ := io.ReadAll(out)\n\tcmd.Wait()\n\n\treturn bytes.NewReader(data)\n}\n\nfunc TestGetDataIntegration(t *testing.T) {\n\tgot := GetData(getXMLFromCommand())\n\twant := \"HAPPY NEW YEAR!\"\n\n\tif got != want {\n\t\tt.Errorf(\"got %q, want %q\", got, want)\n\t}\n}\n\nfunc TestGetData(t *testing.T) {\n\tinput := strings.NewReader(`\n<payload>\n    <message>Cats are the best animal</message>\n</payload>`)\n\n\tgot := GetData(input)\n\twant := \"CATS ARE THE BEST ANIMAL\"\n\n\tif got != want {\n\t\tt.Errorf(\"got %q, want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "reading-files/blogposts.go",
    "content": "package blogposts\n\nimport (\n\t\"io/fs\"\n)\n\n// NewPostsFromFS returns a collection of blog posts from a file system. If it does not conform to the format then it'll return an error\nfunc NewPostsFromFS(fileSystem fs.FS) ([]Post, error) {\n\tdir, err := fs.ReadDir(fileSystem, \".\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar posts []Post\n\tfor _, f := range dir {\n\t\tpost, err := getPost(fileSystem, f)\n\t\tif err != nil {\n\t\t\treturn nil, err //todo: needs clarification, should we totally fail if one file fails? or just ignore?\n\t\t}\n\t\tposts = append(posts, post)\n\t}\n\treturn posts, nil\n}\n\nfunc getPost(fileSystem fs.FS, f fs.DirEntry) (Post, error) {\n\tpostFile, err := fileSystem.Open(f.Name())\n\tif err != nil {\n\t\treturn Post{}, err\n\t}\n\tdefer postFile.Close()\n\n\treturn newPost(postFile)\n}\n"
  },
  {
    "path": "reading-files/blogposts_test.go",
    "content": "package blogposts_test\n\nimport (\n\tblogposts \"github.com/quii/learn-go-with-tests/reading-files\"\n\t\"reflect\"\n\t\"testing\"\n\t\"testing/fstest\"\n)\n\nfunc TestNewBlogPosts(t *testing.T) {\n\tconst (\n\t\tfirstBody = `Title: Post 1\nDescription: Description 1\nTags: tdd, go\n---\nHello\nWorld`\n\t\tsecondBody = `Title: Post 2\nDescription: Description 2\nTags: rust, borrow-checker\n---\nB\nL\nM`\n\t)\n\n\tfs := fstest.MapFS{\n\t\t\"hello world.md\":  {Data: []byte(firstBody)},\n\t\t\"hello-world2.md\": {Data: []byte(secondBody)},\n\t}\n\n\tposts, err := blogposts.NewPostsFromFS(fs)\n\n\tassertNoError(t, err)\n\n\tassertPostsLength(t, posts, fs)\n\n\tassertPost(t, posts[0], blogposts.Post{\n\t\tTitle:       \"Post 1\",\n\t\tDescription: \"Description 1\",\n\t\tTags:        []string{\"tdd\", \"go\"},\n\t\tBody: `Hello\nWorld`,\n\t})\n}\n\nfunc assertNoError(t *testing.T, err error) {\n\tt.Helper()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc assertPostsLength(t *testing.T, posts []blogposts.Post, fs fstest.MapFS) {\n\tt.Helper()\n\tif len(posts) != len(fs) {\n\t\tt.Errorf(\"got %d posts, wanted %d posts\", len(posts), len(fs))\n\t}\n}\n\nfunc assertPost(t *testing.T, got blogposts.Post, want blogposts.Post) {\n\tt.Helper()\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %+v, want %+v\", got, want)\n\t}\n}\n"
  },
  {
    "path": "reading-files/post.go",
    "content": "package blogposts\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n)\n\n// Post represents a post on a blog\ntype Post struct {\n\tTitle       string\n\tDescription string\n\tTags        []string\n\tBody        string\n}\n\nconst (\n\ttitleSeparator       = \"Title: \"\n\tdescriptionSeparator = \"Description: \"\n\ttagsSeparator        = \"Tags: \"\n)\n\nfunc newPost(postBody io.Reader) (Post, error) {\n\tscanner := bufio.NewScanner(postBody)\n\n\treadMetaLine := func(tagName string) string {\n\t\tscanner.Scan()\n\t\treturn strings.TrimPrefix(scanner.Text(), tagName)\n\t}\n\n\treturn Post{\n\t\tTitle:       readMetaLine(titleSeparator),\n\t\tDescription: readMetaLine(descriptionSeparator),\n\t\tTags:        strings.Split(readMetaLine(tagsSeparator), \", \"),\n\t\tBody:        readBody(scanner),\n\t}, nil\n}\n\nfunc readBody(scanner *bufio.Scanner) string {\n\tscanner.Scan() // ignore a line\n\tbuf := bytes.Buffer{}\n\tfor scanner.Scan() {\n\t\tfmt.Fprintln(&buf, scanner.Text())\n\t}\n\treturn strings.TrimSuffix(buf.String(), \"\\n\")\n}\n"
  },
  {
    "path": "reading-files.md",
    "content": "# Reading files\n\n* [**You can find all the code for this chapter here**](https://github.com/quii/learn-go-with-tests/tree/main/reading-files)\n* [Here is a video of me working through the problem and taking questions from the Twitch stream](https://www.youtube.com/watch?v=nXts4dEJnkU)\n\nIn this chapter we're going to learn how to read some files, get some data out of them, and do something useful.\n\nPretend you're working with your friend to create some blog software. The idea is an author will write their posts in markdown, with some metadata at the top of the file. On startup, the web server will read a folder to create some `Post`s, and then a separate `NewHandler` function will use those `Post`s as a datasource for the blog's webserver.\n\nWe've been asked to create the package that converts a given folder of blog post files into a collection of `Post`s.\n\n### Example data\n\nhello world.md\n\n```markdown\nTitle: Hello, TDD world!\nDescription: First post on our wonderful blog\nTags: tdd, go\n---\nHello world!\n\nThe body of posts starts after the `---`\n```\n\n### Expected data\n\n```go\ntype Post struct {\n\tTitle, Description, Body string\n\tTags                     []string\n}\n```\n\n## Iterative, test-driven development\n\nWe'll take an iterative approach where we're always taking simple, safe steps toward our goal.\n\nThis requires us to break up our work, but we should be careful not to fall into the trap of taking a [\"bottom up\"](https://en.wikipedia.org/wiki/Top-down_and_bottom-up_design) approach.\n\nWe should not trust our over-active imaginations when we start work. We could be tempted into making some kind of abstraction that is only validated once we stick everything together, such as some kind of `BlogPostFileParser`.\n\nThis is _not_ iterative and is missing out on the tight feedback loops that TDD is supposed to bring us.\n\nKent Beck says:\n\n> Optimism is an occupational hazard of programming. Feedback is the treatment.\n\nInstead, our approach should strive to be as close to delivering _real_ consumer value as quickly as possible (often called a \"happy path\"). Once we have delivered a small amount of consumer value end-to-end, further iteration of the rest of the requirements is usually straightforward.\n\n## Thinking about the kind of test we want to see\n\nLet's remind ourselves of our mindset and goals when starting:\n\n* **Write the test we want to see**. Think about how we'd like to use the code we're going to write from a consumer's point of view.\n* Focus on _what_ and _why_, but don't get distracted by _how_.\n\nOur package needs to offer a function that can be pointed at a folder, and return us some posts.\n\n```go\nvar posts []blogposts.Post\nposts = blogposts.NewPostsFromFS(\"some-folder\")\n```\n\nTo write a test around this, we'd need some kind of test folder with some example posts in it. _There's nothing terribly wrong with this_, but you are making some trade-offs:\n\n* for each test you may need to create new files to test a particular behaviour\n* some behaviour will be challenging to test, such as failing to load files\n* the tests will run a little slower because they will need to access the file system\n\nWe're also unnecessarily coupling ourselves to a specific implementation of the file system.\n\n### File system abstractions introduced in Go 1.16\n\nGo 1.16 introduced an abstraction for file systems; the [io/fs](https://golang.org/pkg/io/fs/) package.\n\n> Package fs defines basic interfaces to a file system. A file system can be provided by the host operating system but also by other packages.\n\nThis lets us loosen our coupling to a specific file system, which will then let us inject different implementations according to our needs.\n\n> [On the producer side of the interface, the new embed.FS type implements fs.FS, as does zip.Reader. The new os.DirFS function provides an implementation of fs.FS backed by a tree of operating system files.](https://golang.org/doc/go1.16#fs)\n\nIf we use this interface, users of our package have a number of options baked-in to the standard library to use. Learning to leverage interfaces defined in Go's standard library (e.g. `io.fs`, [`io.Reader`](https://golang.org/pkg/io/#Reader), [`io.Writer`](https://golang.org/pkg/io/#Writer)), is vital to writing loosely coupled packages. These packages can then be re-used in contexts different to those you imagined, with minimal fuss from your consumers.\n\nIn our case, maybe our consumer wants the posts to be embedded into the Go binary rather than files in a \"real\" filesystem? Either way, _our code doesn't need to care_.\n\nFor our tests, the package [testing/fstest](https://golang.org/pkg/testing/fstest/) offers us an implementation of [io/FS](https://golang.org/pkg/io/fs/#FS) to use, similar to the tools we're familiar with in [net/http/httptest](https://golang.org/pkg/net/http/httptest/).\n\nGiven this information, the following feels like a better approach,\n\n```go\nvar posts []blogposts.Post\nposts = blogposts.NewPostsFromFS(someFS)\n```\n\n## Write the test first\n\nWe should keep scope as small and useful as possible. If we prove that we can read all the files in a directory, that will be a good start. This will give us confidence in the software we're writing. We can check that the count of `[]Post` returned is the same as the number of files in our fake file system.\n\nCreate a new project to work through this chapter.\n\n* `mkdir blogposts`\n* `cd blogposts`\n* `go mod init github.com/{your-name}/blogposts`\n* `touch blogposts_test.go`\n\n```go\npackage blogposts_test\n\nimport (\n\t\"testing\"\n\t\"testing/fstest\"\n)\n\nfunc TestNewBlogPosts(t *testing.T) {\n\tfs := fstest.MapFS{\n\t\t\"hello world.md\":  {Data: []byte(\"hi\")},\n\t\t\"hello-world2.md\": {Data: []byte(\"hola\")},\n\t}\n\n\tposts := blogposts.NewPostsFromFS(fs)\n\n\tif len(posts) != len(fs) {\n\t\tt.Errorf(\"got %d posts, wanted %d posts\", len(posts), len(fs))\n\t}\n}\n```\n\nNotice that the package of our test is `blogposts_test`. Remember, when TDD is practiced well we take a _consumer-driven_ approach: we don't want to test internal details because _consumers_ don't care about them. By appending `_test` to our intended package name, we only access exported members from our package - just like a real user of our package.\n\nWe've imported [`testing/fstest`](https://golang.org/pkg/testing/fstest/) which gives us access to the [`fstest.MapFS`](https://golang.org/pkg/testing/fstest/#MapFS) type. Our fake file system will pass `fstest.MapFS` to our package.\n\n> A MapFS is a simple in-memory file system for use in tests, represented as a map from path names (arguments to Open) to information about the files or directories they represent.\n\nThis feels simpler than maintaining a folder of test files, and it will execute quicker.\n\nFinally, we codified the usage of our API from a consumer's point of view, then checked if it creates the correct number of posts.\n\n## Try to run the test\n\n```\n./blogpost_test.go:15:12: undefined: blogposts\n```\n\n## Write the minimal amount of code for the test to run and _check the failing test output_\n\nThe package doesn't exist. Create a new file `blogposts.go` and put `package blogposts` inside it. You'll need to then import that package into your tests. For me, the imports now look like:\n\n```go\nimport (\n\tblogposts \"github.com/quii/learn-go-with-tests/reading-files\"\n\t\"testing\"\n\t\"testing/fstest\"\n)\n```\n\nNow the tests won't compile because our new package does not have a `NewPostsFromFS` function, that returns some kind of collection.\n\n```\n./blogpost_test.go:16:12: undefined: blogposts.NewPostsFromFS\n```\n\nThis forces us to make the skeleton of our function to make the test run. Remember not to overthink the code at this point; we're only trying to get a running test, and to make sure it fails as we'd expect. If we skip this step we may skip over assumptions and, write a test which is not useful.\n\n```go\npackage blogposts\n\nimport \"testing/fstest\"\n\ntype Post struct {\n}\n\nfunc NewPostsFromFS(fileSystem fstest.MapFS) []Post {\n\treturn nil\n}\n```\n\nThe test should now correctly fail\n\n```\n=== RUN   TestNewBlogPosts\n    blogposts_test.go:48: got 0 posts, wanted 2 posts\n```\n\n## Write enough code to make it pass\n\nWe _could_ [\"slime\"](https://deniseyu.github.io/leveling-up-tdd/) this to make it pass:\n\n```go\nfunc NewPostsFromFS(fileSystem fstest.MapFS) []Post {\n\treturn []Post{{}, {}}\n}\n```\n\nBut, as Denise Yu wrote:\n\n> Sliming is useful for giving a “skeleton” to your object. Designing an interface and executing logic are two concerns, and sliming tests strategically lets you focus on one at a time.\n\nWe already have our structure. So, what do we do instead?\n\nAs we've cut scope, all we need to do is read the directory and create a post for each file we encounter. We don't have to worry about opening files and parsing them just yet.\n\n```go\nfunc NewPostsFromFS(fileSystem fstest.MapFS) []Post {\n\tdir, _ := fs.ReadDir(fileSystem, \".\")\n\tvar posts []Post\n\tfor range dir {\n\t\tposts = append(posts, Post{})\n\t}\n\treturn posts\n}\n```\n\n[`fs.ReadDir`](https://golang.org/pkg/io/fs/#ReadDir) reads a directory inside a given `fs.FS` returning [`[]DirEntry`](https://golang.org/pkg/io/fs/#DirEntry).\n\nAlready our idealised view of the world has been foiled because errors can happen, but remember now our focus is _making the test pass_, not changing design, so we'll ignore the error for now.\n\nThe rest of the code is straightforward: iterate over the entries, create a `Post` for each one and, return the slice.\n\n## Refactor\n\nEven though our tests are passing, we can't use our new package outside of this context, because it is coupled to a concrete implementation `fstest.MapFS`. But, it doesn't have to be. Change the argument to our `NewPostsFromFS` function to accept the interface from the standard library.\n\n```go\nfunc NewPostsFromFS(fileSystem fs.FS) []Post {\n\tdir, _ := fs.ReadDir(fileSystem, \".\")\n\tvar posts []Post\n\tfor range dir {\n\t\tposts = append(posts, Post{})\n\t}\n\treturn posts\n}\n```\n\nRe-run the tests: everything should be working.\n\n### Error handling\n\nWe parked error handling earlier when we focused on making the happy-path work. Before continuing to iterate on the functionality, we should acknowledge that errors can happen when working with files. Beyond reading the directory, we can run into problems when we open individual files. Let's change our API (via our tests first, naturally) so that it can return an `error`.\n\n```go\nfunc TestNewBlogPosts(t *testing.T) {\n\tfs := fstest.MapFS{\n\t\t\"hello world.md\":  {Data: []byte(\"hi\")},\n\t\t\"hello-world2.md\": {Data: []byte(\"hola\")},\n\t}\n\n\tposts, err := blogposts.NewPostsFromFS(fs)\n\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif len(posts) != len(fs) {\n\t\tt.Errorf(\"got %d posts, wanted %d posts\", len(posts), len(fs))\n\t}\n}\n```\n\nRun the test: it should complain about the wrong number of return values. Fixing the code is straightforward.\n\n```go\nfunc NewPostsFromFS(fileSystem fs.FS) ([]Post, error) {\n\tdir, err := fs.ReadDir(fileSystem, \".\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar posts []Post\n\tfor range dir {\n\t\tposts = append(posts, Post{})\n\t}\n\treturn posts, nil\n}\n```\n\nThis will make the test pass. The TDD practitioner in you might be annoyed we didn't see a failing test before writing the code to propagate the error from `fs.ReadDir`. To do this \"properly\", we'd need a new test where we inject a failing `fs.FS` test-double to make `fs.ReadDir` return an `error`.\n\n```go\ntype StubFailingFS struct {\n}\n\nfunc (s StubFailingFS) Open(name string) (fs.File, error) {\n\treturn nil, errors.New(\"oh no, i always fail\")\n}\n```\n\n```go\n// later\n_, err := blogposts.NewPostsFromFS(StubFailingFS{})\n```\n\nThis should give you confidence in our approach. The interface we're using has one method, which makes creating test-doubles to test different scenarios trivial.\n\nIn some cases, testing error handling is the pragmatic thing to do but, in our case, we're not doing anything _interesting_ with the error, we're just propagating it, so it's not worth the hassle of writing a new test.\n\nLogically, our next iterations will be around expanding our `Post` type so that it has some useful data.\n\n## Write the test first\n\nWe'll start with the first line in the proposed blog post schema, the title field.\n\nWe need to change the contents of the test files so they match what was specified, and then we can make an assertion that it is parsed correctly.\n\n```go\nfunc TestNewBlogPosts(t *testing.T) {\n\tfs := fstest.MapFS{\n\t\t\"hello world.md\":  {Data: []byte(\"Title: Post 1\")},\n\t\t\"hello-world2.md\": {Data: []byte(\"Title: Post 2\")},\n\t}\n\n\t// rest of test code cut for brevity\n\tgot := posts[0]\n\twant := blogposts.Post{Title: \"Post 1\"}\n\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %+v, want %+v\", got, want)\n\t}\n}\n```\n\n## Try to run the test\n\n```\n./blogpost_test.go:58:26: unknown field 'Title' in struct literal of type blogposts.Post\n```\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\nAdd the new field to our `Post` type so that the test will run\n\n```go\ntype Post struct {\n\tTitle string\n}\n```\n\nRe-run the test, and you should get a clear, failing test\n\n```\n=== RUN   TestNewBlogPosts\n=== RUN   TestNewBlogPosts/parses_the_post\n    blogpost_test.go:61: got {Title:}, want {Title:Post 1}\n```\n\n## Write enough code to make it pass\n\nWe'll need to open each file and then extract the title\n\n```go\nfunc NewPostsFromFS(fileSystem fs.FS) ([]Post, error) {\n\tdir, err := fs.ReadDir(fileSystem, \".\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar posts []Post\n\tfor _, f := range dir {\n\t\tpost, err := getPost(fileSystem, f)\n\t\tif err != nil {\n\t\t\treturn nil, err //todo: needs clarification, should we totally fail if one file fails? or just ignore?\n\t\t}\n\t\tposts = append(posts, post)\n\t}\n\treturn posts, nil\n}\n\nfunc getPost(fileSystem fs.FS, f fs.DirEntry) (Post, error) {\n\tpostFile, err := fileSystem.Open(f.Name())\n\tif err != nil {\n\t\treturn Post{}, err\n\t}\n\tdefer postFile.Close()\n\n\tpostData, err := io.ReadAll(postFile)\n\tif err != nil {\n\t\treturn Post{}, err\n\t}\n\n\tpost := Post{Title: string(postData)[7:]}\n\treturn post, nil\n}\n```\n\nRemember our focus at this point is not to write elegant code, it's just to get to a point where we have working software.\n\nEven though this feels like a small increment forward it still required us to write a fair amount of code and make some assumptions in respect to error handling. This would be a point where you should talk to your colleagues and decide the best approach.\n\nThe iterative approach has given us fast feedback that our understanding of the requirements is incomplete.\n\n`fs.FS` gives us a way of opening a file within it by name with its `Open` method. From there we read the data from the file and, for now, we do not need any sophisticated parsing, just cutting out the `Title:` text by slicing the string.\n\n## Refactor\n\nSeparating the 'opening file code' from the 'parsing file contents code' will make the code simpler to understand and work with.\n\n```go\nfunc getPost(fileSystem fs.FS, f fs.DirEntry) (Post, error) {\n\tpostFile, err := fileSystem.Open(f.Name())\n\tif err != nil {\n\t\treturn Post{}, err\n\t}\n\tdefer postFile.Close()\n\treturn newPost(postFile)\n}\n\nfunc newPost(postFile fs.File) (Post, error) {\n\tpostData, err := io.ReadAll(postFile)\n\tif err != nil {\n\t\treturn Post{}, err\n\t}\n\n\tpost := Post{Title: string(postData)[7:]}\n\treturn post, nil\n}\n```\n\nWhen you refactor out new functions or methods, take care and think about the arguments. You're designing here, and are free to think deeply about what is appropriate because you have passing tests. Think about coupling and cohesion. In this case you should ask yourself:\n\n> Does `newPost` have to be coupled to an `fs.File` ? Do we use all the methods and data from this type? What do we _really_ need?\n\nIn our case we only use it as an argument to `io.ReadAll` which needs an `io.Reader`. So we should loosen the coupling in our function and ask for an `io.Reader`.\n\n```go\nfunc newPost(postFile io.Reader) (Post, error) {\n\tpostData, err := io.ReadAll(postFile)\n\tif err != nil {\n\t\treturn Post{}, err\n\t}\n\n\tpost := Post{Title: string(postData)[7:]}\n\treturn post, nil\n}\n```\n\nYou can make a similar argument for our `getPost` function, which takes an `fs.DirEntry` argument but simply calls `Name()` to get the file name. We don't need all that; let's decouple from that type and pass the file name through as a string. Here's the fully refactored code:\n\n```go\nfunc NewPostsFromFS(fileSystem fs.FS) ([]Post, error) {\n\tdir, err := fs.ReadDir(fileSystem, \".\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar posts []Post\n\tfor _, f := range dir {\n\t\tpost, err := getPost(fileSystem, f.Name())\n\t\tif err != nil {\n\t\t\treturn nil, err //todo: needs clarification, should we totally fail if one file fails? or just ignore?\n\t\t}\n\t\tposts = append(posts, post)\n\t}\n\treturn posts, nil\n}\n\nfunc getPost(fileSystem fs.FS, fileName string) (Post, error) {\n\tpostFile, err := fileSystem.Open(fileName)\n\tif err != nil {\n\t\treturn Post{}, err\n\t}\n\tdefer postFile.Close()\n\treturn newPost(postFile)\n}\n\nfunc newPost(postFile io.Reader) (Post, error) {\n\tpostData, err := io.ReadAll(postFile)\n\tif err != nil {\n\t\treturn Post{}, err\n\t}\n\n\tpost := Post{Title: string(postData)[7:]}\n\treturn post, nil\n}\n```\n\nFrom now on, most of our efforts can be neatly contained within `newPost`. The concerns of opening and iterating over files are done, and now we can focus on extracting the data for our `Post` type. Whilst not technically necessary, files are a nice way to logically group related things together, so I moved the `Post` type and `newPost` into a new `post.go` file.\n\n### Test helper\n\nWe should take care of our tests too. We're going to be making assertions on `Posts` a lot, so we should write some code to help with that\n\n```go\nfunc assertPost(t *testing.T, got blogposts.Post, want blogposts.Post) {\n\tt.Helper()\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %+v, want %+v\", got, want)\n\t}\n}\n```\n\n```go\nassertPost(t, posts[0], blogposts.Post{Title: \"Post 1\"})\n```\n\n## Write the test first\n\nLet's extend our test further to extract the next line from the file, the description. Up until making it pass should now feel comfortable and familiar.\n\n```go\nfunc TestNewBlogPosts(t *testing.T) {\n\tconst (\n\t\tfirstBody = `Title: Post 1\nDescription: Description 1`\n\t\tsecondBody = `Title: Post 2\nDescription: Description 2`\n\t)\n\n\tfs := fstest.MapFS{\n\t\t\"hello world.md\":  {Data: []byte(firstBody)},\n\t\t\"hello-world2.md\": {Data: []byte(secondBody)},\n\t}\n\n\t// rest of test code cut for brevity\n\tassertPost(t, posts[0], blogposts.Post{\n\t\tTitle:       \"Post 1\",\n\t\tDescription: \"Description 1\",\n\t})\n\n}\n```\n\n## Try to run the test\n\n```\n./blogpost_test.go:47:58: unknown field 'Description' in struct literal of type blogposts.Post\n```\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\nAdd the new field to `Post`.\n\n```go\ntype Post struct {\n\tTitle       string\n\tDescription string\n}\n```\n\nThe tests should now compile, and fail.\n\n```\n=== RUN   TestNewBlogPosts\n    blogpost_test.go:47: got {Title:Post 1\n        Description: Description 1 Description:}, want {Title:Post 1 Description:Description 1}\n```\n\n## Write enough code to make it pass\n\nThe standard library has a handy library for helping you scan through data, line by line; [`bufio.Scanner`](https://golang.org/pkg/bufio/#Scanner)\n\n> Scanner provides a convenient interface for reading data such as a file of newline-delimited lines of text.\n\n```go\nfunc newPost(postFile io.Reader) (Post, error) {\n\tscanner := bufio.NewScanner(postFile)\n\n\tscanner.Scan()\n\ttitleLine := scanner.Text()\n\n\tscanner.Scan()\n\tdescriptionLine := scanner.Text()\n\n\treturn Post{Title: titleLine[7:], Description: descriptionLine[13:]}, nil\n}\n```\n\nHandily, it also takes an `io.Reader` to read through (thank you again, loose-coupling), we don't need to change our function arguments.\n\nCall `Scan` to read a line, and then extract the data using `Text`.\n\nThis function could never return an `error`. It would be tempting at this point to remove it from the return type, but we know we'll have to handle invalid file structures later so, we may as well leave it.\n\n## Refactor\n\nWe have repetition around scanning a line and then reading the text. We know we're going to do this operation at least one more time, it's a simple refactor to DRY up so let's start with that.\n\n```go\nfunc newPost(postFile io.Reader) (Post, error) {\n\tscanner := bufio.NewScanner(postFile)\n\n\treadLine := func() string {\n\t\tscanner.Scan()\n\t\treturn scanner.Text()\n\t}\n\n\ttitle := readLine()[7:]\n\tdescription := readLine()[13:]\n\n\treturn Post{Title: title, Description: description}, nil\n}\n```\n\nThis has barely saved any lines of code, but that's rarely the point of refactoring. What I'm trying to do here is just separating the _what_ from the _how_ of reading lines to make the code a little more declarative to the reader.\n\nWhilst the magic numbers of 7 and 13 get the job done, they're not awfully descriptive.\n\n```go\nconst (\n\ttitleSeparator       = \"Title: \"\n\tdescriptionSeparator = \"Description: \"\n)\n\nfunc newPost(postFile io.Reader) (Post, error) {\n\tscanner := bufio.NewScanner(postFile)\n\n\treadLine := func() string {\n\t\tscanner.Scan()\n\t\treturn scanner.Text()\n\t}\n\n\ttitle := readLine()[len(titleSeparator):]\n\tdescription := readLine()[len(descriptionSeparator):]\n\n\treturn Post{Title: title, Description: description}, nil\n}\n```\n\nNow that I'm staring at the code with my creative refactoring mind, I'd like to try making our readLine function take care of removing the tag. There's also a more readable way of trimming a prefix from a string with the function `strings.TrimPrefix`.\n\n```go\nfunc newPost(postBody io.Reader) (Post, error) {\n\tscanner := bufio.NewScanner(postBody)\n\n\treadMetaLine := func(tagName string) string {\n\t\tscanner.Scan()\n\t\treturn strings.TrimPrefix(scanner.Text(), tagName)\n\t}\n\n\treturn Post{\n\t\tTitle:       readMetaLine(titleSeparator),\n\t\tDescription: readMetaLine(descriptionSeparator),\n\t}, nil\n}\n```\n\nYou may or may not like this idea, but I do. The point is in the refactoring state we are free to play with the internal details, and you can keep running your tests to check things still behave correctly. We can always go back to previous states if we're not happy. The TDD approach gives us this license to frequently experiment with ideas, so we have more shots at writing great code.\n\nThe next requirement is extracting the post's tags. If you're following along, I'd recommend trying to implement it yourself before reading on. You should now have a good, iterative rhythm and feel confident to extract the next line and parse out the data.\n\nFor brevity, I will not go through the TDD steps, but here's the test with tags added.\n\n```go\nfunc TestNewBlogPosts(t *testing.T) {\n\tconst (\n\t\tfirstBody = `Title: Post 1\nDescription: Description 1\nTags: tdd, go`\n\t\tsecondBody = `Title: Post 2\nDescription: Description 2\nTags: rust, borrow-checker`\n\t)\n\n\t// rest of test code cut for brevity\n\tassertPost(t, posts[0], blogposts.Post{\n\t\tTitle:       \"Post 1\",\n\t\tDescription: \"Description 1\",\n\t\tTags:        []string{\"tdd\", \"go\"},\n\t})\n}\n```\n\nYou're only cheating yourself if you just copy and paste what I write. To make sure we're all on the same page, here's my code which includes extracting the tags.\n\n```go\nconst (\n\ttitleSeparator       = \"Title: \"\n\tdescriptionSeparator = \"Description: \"\n\ttagsSeparator        = \"Tags: \"\n)\n\nfunc newPost(postBody io.Reader) (Post, error) {\n\tscanner := bufio.NewScanner(postBody)\n\n\treadMetaLine := func(tagName string) string {\n\t\tscanner.Scan()\n\t\treturn strings.TrimPrefix(scanner.Text(), tagName)\n\t}\n\n\treturn Post{\n\t\tTitle:       readMetaLine(titleSeparator),\n\t\tDescription: readMetaLine(descriptionSeparator),\n\t\tTags:        strings.Split(readMetaLine(tagsSeparator), \", \"),\n\t}, nil\n}\n```\n\nHopefully no surprises here. We were able to re-use `readMetaLine` to get the next line for the tags and then split them up using `strings.Split`.\n\nThe last iteration on our happy path is to extract the body.\n\nHere's a reminder of the proposed file format.\n\n```markdown\nTitle: Hello, TDD world!\nDescription: First post on our wonderful blog\nTags: tdd, go\n---\nHello world!\n\nThe body of posts starts after the `---`\n```\n\nWe've read the first 3 lines already. We then need to read one more line, discard it and then the remainder of the file contains the post's body.\n\n## Write the test first\n\nChange the test data to have the separator, and a body with a few newlines to check we grab all the content.\n\n```go\n\tconst (\n\t\tfirstBody = `Title: Post 1\nDescription: Description 1\nTags: tdd, go\n---\nHello\nWorld`\n\t\tsecondBody = `Title: Post 2\nDescription: Description 2\nTags: rust, borrow-checker\n---\nB\nL\nM`\n\t)\n```\n\nAdd to our assertion like the others\n\n```go\n\tassertPost(t, posts[0], blogposts.Post{\n\t\tTitle:       \"Post 1\",\n\t\tDescription: \"Description 1\",\n\t\tTags:        []string{\"tdd\", \"go\"},\n\t\tBody: `Hello\nWorld`,\n\t})\n```\n\n## Try to run the test\n\n```\n./blogpost_test.go:60:3: unknown field 'Body' in struct literal of type blogposts.Post\n```\n\nAs we'd expect.\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\nAdd `Body` to `Post` and the test should fail.\n\n```\n=== RUN   TestNewBlogPosts\n    blogposts_test.go:38: got {Title:Post 1 Description:Description 1 Tags:[tdd go] Body:}, want {Title:Post 1 Description:Description 1 Tags:[tdd go] Body:Hello\n        World}\n```\n\n## Write enough code to make it pass\n\n1. Scan the next line to ignore the `---` separator.\n2. Keep scanning until there's nothing left to scan.\n\n```go\nfunc newPost(postBody io.Reader) (Post, error) {\n\tscanner := bufio.NewScanner(postBody)\n\n\treadMetaLine := func(tagName string) string {\n\t\tscanner.Scan()\n\t\treturn strings.TrimPrefix(scanner.Text(), tagName)\n\t}\n\n\ttitle := readMetaLine(titleSeparator)\n\tdescription := readMetaLine(descriptionSeparator)\n\ttags := strings.Split(readMetaLine(tagsSeparator), \", \")\n\n\tscanner.Scan() // ignore a line\n\n\tbuf := bytes.Buffer{}\n\tfor scanner.Scan() {\n\t\tfmt.Fprintln(&buf, scanner.Text())\n\t}\n\tbody := strings.TrimSuffix(buf.String(), \"\\n\")\n\n\treturn Post{\n\t\tTitle:       title,\n\t\tDescription: description,\n\t\tTags:        tags,\n\t\tBody:        body,\n\t}, nil\n}\n```\n\n* `scanner.Scan()` returns a `bool` which indicates whether there's more data to scan, so we can use that with a `for` loop to keep reading through the data until the end.\n* After every `Scan()` we write the data into the buffer using `fmt.Fprintln`. We use the version that adds a newline because the scanner removes the newlines from each line, but we need to maintain them.\n* Because of the above, we need to trim the final newline, so we don't have a trailing one.\n\n## Refactor\n\nEncapsulating the idea of getting the rest of the data into a function will help future readers quickly understand _what_ is happening in `newPost`, without having to concern themselves with implementation specifics.\n\n```go\nfunc newPost(postBody io.Reader) (Post, error) {\n\tscanner := bufio.NewScanner(postBody)\n\n\treadMetaLine := func(tagName string) string {\n\t\tscanner.Scan()\n\t\treturn strings.TrimPrefix(scanner.Text(), tagName)\n\t}\n\n\treturn Post{\n\t\tTitle:       readMetaLine(titleSeparator),\n\t\tDescription: readMetaLine(descriptionSeparator),\n\t\tTags:        strings.Split(readMetaLine(tagsSeparator), \", \"),\n\t\tBody:        readBody(scanner),\n\t}, nil\n}\n\nfunc readBody(scanner *bufio.Scanner) string {\n\tscanner.Scan() // ignore a line\n\tbuf := bytes.Buffer{}\n\tfor scanner.Scan() {\n\t\tfmt.Fprintln(&buf, scanner.Text())\n\t}\n\treturn strings.TrimSuffix(buf.String(), \"\\n\")\n}\n```\n\n## Iterating further\n\nWe've made our \"steel thread\" of functionality, taking the shortest route to get to our happy path, but clearly there's some distance to go before it is production ready.\n\nWe haven't handled:\n\n* when the file's format is not correct\n* the file is not a `.md`\n* what if the order of the metadata fields is different? Should that be allowed? Should we be able to handle it?\n\nCrucially though, we have working software, and we have defined our interface. The above are just further iterations, more tests to write and drive our behaviour. To support any of the above we shouldn't have to change our _design_, just implementation details.\n\nKeeping focused on the goal means we made the important decisions, and validated them against the desired behaviour, rather than getting bogged down on matters that won't affect the overall design.\n\n## Wrapping up\n\n`fs.FS`, and the other changes in Go 1.16 give us some elegant ways of reading data from file systems and testing them simply.\n\nIf you wish to try out the code \"for real\":\n\n* Create a `cmd` folder within the project, add a `main.go` file\n* Add the following code\n\n```go\nimport (\n\tblogposts \"github.com/quii/fstest-spike\"\n\t\"log\"\n\t\"os\"\n)\n\nfunc main() {\n\tposts, err := blogposts.NewPostsFromFS(os.DirFS(\"posts\"))\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tlog.Println(posts)\n}\n```\n\n* Add some markdown files into a `posts` folder and run the program!\n\nNotice the symmetry between the production code\n\n```go\nposts, err := blogposts.NewPostsFromFS(os.DirFS(\"posts\"))\n```\n\nAnd the tests\n\n```go\nposts, err := blogposts.NewPostsFromFS(fs)\n```\n\nThis is when consumer-driven, top-down TDD _feels correct_.\n\nA user of our package can look at our tests and quickly get up to speed with what it's supposed to do and how to use it. As maintainers, we can be _confident our tests are useful because they're from a consumer's point of view_. We're not testing implementation details or other incidental details, so we can be reasonably confident that our tests will help us, rather than hinder us when refactoring.\n\nBy relying on good software engineering practices like [**dependency injection**](dependency-injection.md) our code is simple to test and re-use.\n\nWhen you're creating packages, even if they're only internal to your project, prefer a top-down consumer driven approach. This will stop you over-imagining designs and making abstractions you may not even need and will help ensure the tests you write are useful.\n\nThe iterative approach kept every step small, and the continuous feedback helped us uncover unclear requirements possibly sooner than with other, more ad-hoc approaches.\n\n### Writing?\n\nIt's important to note that these new features only have operations for _reading_ files. If your work needs to do writing, you'll need to look elsewhere. Remember to keep thinking about what the standard library offers currently, if you're writing data you should probably look into leveraging existing interfaces such as `io.Writer` to keep your code loosely-coupled and re-usable.\n\n### Further reading\n\n* This was a light intro to `io/fs`. [Ben Congdon has done an excellent write-up](https://benjamincongdon.me/blog/2021/01/21/A-Tour-of-Go-116s-iofs-package/) which was a lot of help for writing this chapter.\n* [Discussion on the file system interfaces](https://github.com/golang/go/issues/41190)\n"
  },
  {
    "path": "refactoring-checklist.md",
    "content": "# Refactoring step, starting checklist\n\nRefactoring is a skill that, once practised enough, becomes, in most cases, second nature reasonably easy.\n\nThe activity often gets conflated with more significant design changes, but they are separate. Delineating between refactoring and other programming activities is helpful because it allows me to work with clarity and discipline.\n\n## Refactoring vs other activities\n\nRefactoring is just improving existing code and <u>not changing behaviour</u>; therefore, tests shouldn't have to change.\n\nThis is why it's the 3rd step of the TDD cycle. Once you have added a behaviour and a test to back it up, refactoring should be an activity which requires no change to your test code. **You're doing something else** if you are \"refactoring\" some code and having to change tests at the same time.\n\nMany very helpful refactorings are simple to learn and easy to do (your IDE almost entirely automates many) but, over time, become hugely impactful to the quality of our system.\n\n### Other activities, such as \"big\" design\n\n> So I'm not changing the \"real\" behaviour, but I must change my tests? What is that?\n\nLet's say you're working on a type and want to improve its code's quality. *Refactoring shouldn't require you to change the tests*, so you can't:\n\n- Change behaviour\n- Change method signatures\n\n...as your tests are coupled to those two things, but you can:\n\n- Introduce private methods, fields and even new types & interfaces\n- Change the internals of public methods\n\nWhat if you want to change the signature of a method?\n\n```go\nfunc (b BirthdayGreeter) WishHappyBirthday(age int, firstname, lastname string, email Email) {\n\t// some fascinating emailing code\n}\n```\n\nYou may feel its argument list is too long and want to bring more cohesion and meaning to the code.\n\n```go\nfunc (b BirthdayGreeter) WishHappyBirthday(person Person)\n```\n\nWell, you're **designing** now and must ensure you tread carefully. If you don't do this with discipline, you can make a mess of your code, the test behind it, *and* probably the things that depend on it - remember, it's not just your tests using `WishHappyBirthday`. Hopefully, it's used by \"real\" code too!\n\n**You should still be able to drive this change with a test first**. You can split hairs over whether this is a \"behaviour\" change, but you want your method to behave differently.\n\nAs this is a behaviour change, apply the TDD process here too. One benefit of TDD is that it gives you a simple, safe, repeatable way of driving behaviour change in your system; why abandon it in these situations just because it *feels* different?\n\nIn this case, you'll change your existing tests to use the new type. The iterative, small steps you usually do with TDD to reduce risk and bring discipline & clarity will help you in these situations, too.\n\nChances are you'll have several tests that call `WishHappyBirthday`; in these scenarios, I'd suggest commenting out all but one of the tests, driving out the change, and then working through the rest of the tests as you see fit.\n\n### Big design\n\nDesign can require more significant changes and more extensive conversations and usually has a level of subjectivity to it. Changing the design of parts of your system is usually a longer process than refactoring; nonetheless, you should still endeavour to reduce risk by thinking about how to do it in small steps.\n\n### Seeing the wood for the trees\n\n> [If someone can't **see the wood for the trees** in British English, or can't see the forest for the trees in American English, they are very involved in the details of something and so they do not notice what is important about the thing as a whole.](https://www.collinsdictionary.com/dictionary/english/cant-see-the-wood-for-the-trees)\n\nTalking about the \"big\" design issues is more accessible when the **underlying code is well-factored**. If you and your colleagues have to spend a significant amount of time mentally parsing a mess of code every time they open a file, what chance do you have to think about the design of the code?\n\nThis is why **constant refactoring is so significant in the TDD process**. If we fail to address the minor design issues, we'll find it hard to engineer the overall design of our more extensive system.\n\nSadly, badly-factored code gets exponentially worse as engineers pile on complexity on top of shaky foundations.\n\n## Starting mental-checklist\n\n**Get in the habit of running through a mental checklist every TDD cycle.** The more you force yourself to practice, the easier it gets. **It is a skill that needs practice.** Remember, each of these changes should not require any change in your tests.\n\nI have included shortcuts for IntelliJ/GoLand, which my colleagues and I use. Whenever I coach a new engineer, I encourage them to try and gain the muscle memory and habit of using these tools to refactor quickly and safely.\n\n### Inline variables\n\nIf you create a variable, only for it to be passed on to another method/function:\n\n```go\nurl := baseURL + \"/user/\" + id\nres, err := client.Get(url)\n```\n\nConsider inlining it (`command+option+n`) *unless* the variable name adds significant meaning.\n\n```go\nres, err := client.Get(baseURL + \"/user/\" + id)\n```\n\nDon't be _too_ clever about inlining; the goal is not to have zero variables and instead have ridiculous one-liners that no one can read. If you can add significant naming to a value, it might be best to leave it be.\n\n### DRY up values with extract variables\n\n\"Don't repeat yourself\" (DRY). Using the same value multiple times in a function? Consider extracting and capturing a variable in a meaningful variable name (`command+option+v`).\n\nThis helps with readability and makes changing the value easier in future, as you won't have to remember to update multiple occurrences of the same value.\n\n### DRY up stuff in general\n\n[DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) gets a bad rep these days, with some justification. DRY is one of those concepts that is *too* easy to understand at a superficial level and then gets misapplied.\n\nAn engineer can easily take DRY too far, creating baffling, entangled abstractions to save some lines of code rather than the *real* idea of DRY, which is capturing an _idea_ in one place. Reducing the number of lines of code is often a side-effect of DRY, **but it is not the actual goal**.\n\nSo yes, DRY can be misapplied, but the extreme opposite of refusing to DRY up anything is also evil. Repeated code adds noise and increases maintenance costs. A refusal to gather related concepts or values into one thing due to fear of DRY misuse causes *different* problems.\n\nSo rather than being extremist on either side of \"must DRY everything\" or \"DRY is bad\", engage your brain and think about the code you see in front of you. What is repeated? Does it need to be? Does the parameter list look sensible if you encapsulate some repeated code into a method? Does it feel self-documenting and encapsulate the \"idea\" clearly?\n\nNine times out of 10, you can look at the argument list of a function, and if it looks messy and confusing, then it is likely to be a poor application of DRY.\n\nIf making some code DRY feels hard, you're probably making things more complex; consider stopping.\n\nDRY with care, **but practising this frequently will improve your judgement**. I encourage my colleagues to \"just try it\" and use source control to get back to safety if it is wrong.\n\n<u>**Trying these things will teach you more than discussing it**</u>, and source control coupled with good automated tests gives you the perfect setup to experiment and learn.\n\n### Extract \"Magic\" values.\n\n> [Unique values with unexplained meaning or multiple occurrences which could (preferably) be replaced with named constants](https://en.wikipedia.org/wiki/Magic_number_(programming))\n\nUse extract variable (command+option+v) or constant (command+option+c) to give meaning to magic values. This can be seen as the inverse of the inlining refactor. I often find myself \"toggling\" the code with inline and extract to help me judge what I think reads better.\n\nRemember that extracting repeated values also adds a level of _coupling_. Everything that uses that value is now coupled. Consider the following code:\n\n```go\nfunc main() {\n\tapi1Client := http.Client{\n\t\tTimeout: 1 * time.Second,\n\t}\n\tapi2Client := http.Client{\n\t\tTimeout: 1 * time.Second,\n\t}\n\tapi3Client := http.Client{\n\t\tTimeout: 1 * time.Second,\n\t}\n\t//etc\n}\n```\n\nWe are setting up some HTTP clients for our application. There are some _magic values_ here, and we could DRY up the `Timeout` by extracting a variable and giving it a meaningful name.\n\n![A screenshot of me extracting variable](https://i.imgur.com/4sgUG7L.png)\n\nNow the code looks like this\n\n```go\nfunc main() {\n\ttimeout := 1 * time.Second\n\tapi1Client := http.Client{\n\t\tTimeout: timeout,\n\t}\n\tapi2Client := http.Client{\n\t\tTimeout: timeout,\n\t}\n\tapi3Client := http.Client{\n\t\tTimeout: timeout,\n\t}\n\t// etc..\n}\n```\n\nWe no longer have a magic value; we have given it a meaningful name, but we have also made it so all three clients **share the same timeout**. That _may_ be what you want; refactors are quite context-specific, but it's something to be wary of.\n\nIf you can use your IDE well, you can do the _inline_ refactor to let the clients have separate `Timeout` values again.\n\n### Make public methods/functions easy to scan\n\nDoes your code have excessively long public methods or functions?\n\nEncapsulate the steps in private methods/functions with the extract method (`command+option+m`) refactor.\n\nThe code below has some boring, distracting ceremony around creating a JSON string and turning it into an `io.Reader` so that we can `POST` it in an HTTP request.\n\n```go\nfunc (ws *WidgetService) CreateWidget(name string) error {\n\turl := ws.baseURL + \"/widgets\"\n\tpayload := []byte(`{\"name\": \"` + name + `\"}`)\n\n\treq, err := http.NewRequest(\n\t\thttp.MethodPost,\n\t\turl,\n\t\tbytes.NewBuffer(payload),\n\t)\n\t//todo: handle codes, err etc\n}\n```\n\nFirst, use the inline variable refactor (command+option+n) to put the `payload` into the buffer creation.\n\n```go\nfunc (ws *WidgetService) CreateWidget(name string) error {\n\turl := ws.baseURL + \"/widgets\"\n\treq, err := http.NewRequest(\n\t\thttp.MethodPost,\n\t\turl,\n\t\tbytes.NewBuffer([]byte(`{\"name\": \"`+name+`\"}`)),\n\t)\n\t// etc\n}\n```\n\nNow, we can extract the creation of the JSON payload into a function using the extract method refactor (`command+option+m`) to remove the noise from the method.\n\n```go\nfunc (ws *WidgetService) CreateWidget(name string) error {\n\turl := ws.baseURL + \"/widgets\"\n\treq, err := http.NewRequest(\n\t\thttp.MethodPost,\n\t\turl,\n\t\tcreateWidgetPayload(name),\n\t)\n\t// etc\n}\n```\n\nPublic methods and functions should describe *what* they do rather than *how* they do it.\n\n> **Whenever I have to think to understand what the code is doing, I ask myself if I can refactor the code to make that understanding more immediately apparent**\n\n-- Martin Fowler\n\nThis helps you understand the overall design better, and it then allows you to ask questions about responsibilities:\n\n>  Why does this method do X? Shouldn't that live in Y?\n\n> Why does this method do so many tasks? Can we consolidate this elsewhere?\n\nPrivate functions and methods are great; they let you wrap up irrelevant hows into whats.\n\n#### But now I don't know how it works!\n\nA common objection to this refactoring, favouring smaller functions and methods composed of others, is that it can make understanding how the code works difficult. My blunt reply to this is\n\n> Have you learned how to navigate codebases using your tooling effectively?\n\nQuite deliberately, as the _writer_ of `CreateWidget`, I do not want the creation of a specific string to be an essential character in the narration of the method. It is distracting, irrelevant noise for the reader 99% of the time.\n\nHowever, if someone _does_ care, you press `command+b`  (or whatever \"navigate to symbol\" is for you) on `createWidgetPayload` ... and read it. Press `command+left-arrow` to go back again.\n\n### Move value creation to construction time.\n\nMethods often have to create value and use them, like the `url` in our `CreateWidget` method from before.\n\n```go\ntype WidgetService struct {\n\tbaseURL string\n\tclient  *http.Client\n}\n\nfunc NewWidgetService(baseURL string) *WidgetService {\n\tclient := http.Client{\n\t\tTimeout: 10 * time.Second,\n\t}\n\treturn &WidgetService{baseURL: baseURL, client: &client}\n}\n\nfunc (ws *WidgetService) CreateWidget(name string) error {\n\turl := ws.baseURL + \"/widgets\"\n\treq, err := http.NewRequest(\n\t\thttp.MethodPost,\n\t\turl,\n\t\tcreateWidgetPayload(name),\n\t)\n\t// etc\n}\n```\n\nA refactoring technique you could apply here is, if a value is being created **that is not dependant on the arguments to the method**, then you can instead create a _field_ in your type and calculate it in your constructor function.\n\n```go\ntype WidgetService struct {\n\tclient          *http.Client\n\tcreateWidgetURL string\n}\n\nfunc NewWidgetService(baseURL string) *WidgetService {\n\tclient := http.Client{\n\t\tTimeout: 10 * time.Second,\n\t}\n\treturn &WidgetService{\n\t\tcreateWidgetURL: baseURL + \"/widgets\",\n\t\tclient:          &client,\n\t}\n}\n\nfunc (ws *WidgetService) CreateWidget(name string) error {\n\treq, err := http.NewRequest(\n\t\thttp.MethodPost,\n\t\tws.createWidgetURL,\n\t\tcreateWidgetPayload(name),\n\t)\n\t// etc\n}\n```\n\nBy moving them to construction time, you can simplify your methods.\n\n#### Comparing and contrasting `CreateWidget`\n\nStarting with\n\n```go\nfunc (ws *WidgetService) CreateWidget(name string) error {\n\turl := ws.baseURL + \"/widgets\"\n\tpayload := []byte(`{\"name\": \"` + name + `\"}`)\n\treq, err := http.NewRequest(\n\t\thttp.MethodPost,\n\t\turl,\n\t\tbytes.NewBuffer(payload),\n\t)\n\t// etc\n}\n\n```\n\nWith a few basic refactors, driven almost entirely using automated tooling, we resulted in\n\n```go\nfunc (ws *WidgetService) CreateWidget(name string) error {\n\treq, err := http.NewRequest(\n\t\thttp.MethodPost,\n\t\tws.createWidgetURL,\n\t\tcreateWidgetPayload(name),\n\t)\n\t// etc\n}\n```\n\nThis is a small improvement, but it undoubtedly reads better. If you are well-practised, this kind of improvement will barely take you a minute, and so long as you have applied TDD well, you'll have the safety net of tests to ensure you're not breaking anything. These continuous minor improvements are vital to the long-term health of a codebase.\n\n### Try to remove comments.\n\n> A heuristic we follow is that whenever we feel the need to comment something, we write a method instead.\n\n-- Martin Fowler\n\nAgain, the extract method refactor can be your friend here.\n\n## Exceptions to the rule\n\nThere are improvements you can make to your code that require a change in your tests, which I would still be happy to put into the \"refactoring\" bucket, even though it breaks the rule.\n\nA simple example would be renaming a public symbol (e.g., a method, type, or function) with `shift+F6`. This will, of course, change the production and test codes.\n\nHowever, as it is an **automated and safe** change, the risk of going into a spiral of breaking tests and production code that so many fall into with other kinds of *design* changes is minimal.\n\nFor that reason, any changes you can safely perform with your IDE/editor, I would still happily call refactoring.\n\n## Use your tools to help you practice refactoring.\n\n- You should run your unit tests every time you do one of these small changes. We invest time in making our code unit-testable, and the feedback loop of a few milliseconds is one of the significant benefits; use it!\n- Lean on source control. You shouldn't feel shy about trying out ideas. If you're happy, commit it; if not, revert. This should feel comfortable and easy and not a big deal.\n- The better you leverage your unit tests and source control, the easier to *practice* refactoring. Once you master this discipline, **your design skills increase quickly** because you have a reliable and effective feedback loop and safety net.\n- Too often in my career, I've heard developers complain about not having time to refactor; unfortunately, it is clear that it takes so much time for them because they don't do it with discipline - and they have not practised it enough.\n- Whilst typing is never the bottleneck, you should be able to use whatever editor/IDE you use to refactor safely and quickly. For instance, if your tool doesn't let you extract variables at a keystroke, you'll do it less because it's more labour-intensive and risky.\n\n## Don't ask permission to refactor\n\nRefactoring should be a frequent occurrence in your work, something you're doing all the time. It also, shouldn't be a time-sink, especially if it's done little and often.\n\nIf you don't refactor, your internal quality will suffer, your team's capacity will drop, and pressure will increase.\n\nMartin Fowler has one more fantastic quote for us.\n\n> Other than when you are very close to a deadline, however, you should not put off refactoring because you haven’t got time. Experience with several projects has shown that a bout of refactoring results in increased productivity. Not having enough time usually is a sign that you need to do some refactoring.\n\n## Wrap up\n\nThis is not an extensive list, just a start. Read Martin Fowler's Refactoring book (2nd ed) to become a pro.\n\nRefactoring should be extremely quick and safe when you're well-practised, so there's little excuse not to do it. Too many view refactoring as a decision for others to make rather than a skill to learn to where it's a regular part of your work.\n\nWe should always strive to leave code in an *exemplary* state.\n\nGood refactoring leads to code that is easier to understand. An understanding of the code means better designs are easier to spot. It is much harder to find designs in systems with massive functions, needlessly duplicated code, deep nesting, etc. **Frequent, small refactoring is necessary for better design**.\n\n"
  },
  {
    "path": "reflection/v1/reflection.go",
    "content": "package main\n\nimport \"reflect\"\n\nfunc walk(x interface{}, fn func(input string)) {\n\tval := reflect.ValueOf(x)\n\tfield := val.Field(0)\n\tfn(field.String())\n}\n"
  },
  {
    "path": "reflection/v1/reflection_test.go",
    "content": "package main\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestWalk(t *testing.T) {\n\n\tcases := []struct {\n\t\tName          string\n\t\tInput         interface{}\n\t\tExpectedCalls []string\n\t}{\n\t\t{\n\t\t\t\"struct with one string field\",\n\t\t\tstruct {\n\t\t\t\tName string\n\t\t\t}{\"Chris\"},\n\t\t\t[]string{\"Chris\"},\n\t\t},\n\t}\n\n\tfor _, test := range cases {\n\t\tt.Run(test.Name, func(t *testing.T) {\n\t\t\tvar got []string\n\t\t\twalk(test.Input, func(input string) {\n\t\t\t\tgot = append(got, input)\n\t\t\t})\n\n\t\t\tif !reflect.DeepEqual(got, test.ExpectedCalls) {\n\t\t\t\tt.Errorf(\"got %v, want %v\", got, test.ExpectedCalls)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "reflection/v10/reflection.go",
    "content": "package main\n\nimport (\n\t\"reflect\"\n)\n\nfunc walk(x interface{}, fn func(input string)) {\n\tval := getValue(x)\n\n\twalkValue := func(value reflect.Value) {\n\t\twalk(value.Interface(), fn)\n\t}\n\n\tswitch val.Kind() {\n\tcase reflect.String:\n\t\tfn(val.String())\n\tcase reflect.Struct:\n\t\tfor i := 0; i < val.NumField(); i++ {\n\t\t\twalkValue(val.Field(i))\n\t\t}\n\tcase reflect.Slice, reflect.Array:\n\t\tfor i := 0; i < val.Len(); i++ {\n\t\t\twalkValue(val.Index(i))\n\t\t}\n\tcase reflect.Map:\n\t\tfor _, key := range val.MapKeys() {\n\t\t\twalkValue(val.MapIndex(key))\n\t\t}\n\tcase reflect.Chan:\n\t\tfor v, ok := val.Recv(); ok; v, ok = val.Recv() {\n\t\t\twalkValue(v)\n\t\t}\n\tcase reflect.Func:\n\t\tvalFnResult := val.Call(nil)\n\t\tfor _, res := range valFnResult {\n\t\t\twalkValue(res)\n\t\t}\n\t}\n}\n\nfunc getValue(x interface{}) reflect.Value {\n\tval := reflect.ValueOf(x)\n\n\tif val.Kind() == reflect.Ptr {\n\t\tval = val.Elem()\n\t}\n\n\treturn val\n}\n"
  },
  {
    "path": "reflection/v10/reflection_test.go",
    "content": "package main\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestWalk(t *testing.T) {\n\n\tcases := []struct {\n\t\tName          string\n\t\tInput         interface{}\n\t\tExpectedCalls []string\n\t}{\n\t\t{\n\t\t\t\"struct with one string field\",\n\t\t\tstruct{ Name string }{\"Chris\"},\n\t\t\t[]string{\"Chris\"},\n\t\t},\n\t\t{\n\t\t\t\"struct with two string fields\",\n\t\t\tstruct {\n\t\t\t\tName string\n\t\t\t\tCity string\n\t\t\t}{\"Chris\", \"London\"},\n\t\t\t[]string{\"Chris\", \"London\"},\n\t\t},\n\t\t{\n\t\t\t\"struct with non string field\",\n\t\t\tstruct {\n\t\t\t\tName string\n\t\t\t\tAge  int\n\t\t\t}{\"Chris\", 33},\n\t\t\t[]string{\"Chris\"},\n\t\t},\n\t\t{\n\t\t\t\"nested fields\",\n\t\t\tPerson{\n\t\t\t\t\"Chris\",\n\t\t\t\tProfile{33, \"London\"},\n\t\t\t},\n\t\t\t[]string{\"Chris\", \"London\"},\n\t\t},\n\t\t{\n\t\t\t\"pointers to things\",\n\t\t\t&Person{\n\t\t\t\t\"Chris\",\n\t\t\t\tProfile{33, \"London\"},\n\t\t\t},\n\t\t\t[]string{\"Chris\", \"London\"},\n\t\t},\n\t\t{\n\t\t\t\"slices\",\n\t\t\t[]Profile{\n\t\t\t\t{33, \"London\"},\n\t\t\t\t{34, \"Reykjavík\"},\n\t\t\t},\n\t\t\t[]string{\"London\", \"Reykjavík\"},\n\t\t},\n\t\t{\n\t\t\t\"arrays\",\n\t\t\t[2]Profile{\n\t\t\t\t{33, \"London\"},\n\t\t\t\t{34, \"Reykjavík\"},\n\t\t\t},\n\t\t\t[]string{\"London\", \"Reykjavík\"},\n\t\t},\n\t}\n\n\tfor _, test := range cases {\n\t\tt.Run(test.Name, func(t *testing.T) {\n\t\t\tvar got []string\n\t\t\twalk(test.Input, func(input string) {\n\t\t\t\tgot = append(got, input)\n\t\t\t})\n\n\t\t\tif !reflect.DeepEqual(got, test.ExpectedCalls) {\n\t\t\t\tt.Errorf(\"got %v, want %v\", got, test.ExpectedCalls)\n\t\t\t}\n\t\t})\n\t}\n\n\tt.Run(\"with maps\", func(t *testing.T) {\n\t\taMap := map[string]string{\n\t\t\t\"Foo\": \"Bar\",\n\t\t\t\"Baz\": \"Boz\",\n\t\t}\n\n\t\tvar got []string\n\t\twalk(aMap, func(input string) {\n\t\t\tgot = append(got, input)\n\t\t})\n\n\t\tassertContains(t, got, \"Bar\")\n\t\tassertContains(t, got, \"Boz\")\n\t})\n\n\tt.Run(\"with channels\", func(t *testing.T) {\n\t\taChannel := make(chan Profile)\n\n\t\tgo func() {\n\t\t\taChannel <- Profile{33, \"Berlin\"}\n\t\t\taChannel <- Profile{34, \"Katowice\"}\n\t\t\tclose(aChannel)\n\t\t}()\n\n\t\tvar got []string\n\t\twant := []string{\"Berlin\", \"Katowice\"}\n\n\t\twalk(aChannel, func(input string) {\n\t\t\tgot = append(got, input)\n\t\t})\n\n\t\tif !reflect.DeepEqual(got, want) {\n\t\t\tt.Errorf(\"got %v, want %v\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"with function\", func(t *testing.T) {\n\t\taFunction := func() (Profile, Profile) {\n\t\t\treturn Profile{33, \"Berlin\"}, Profile{34, \"Katowice\"}\n\t\t}\n\n\t\tvar got []string\n\t\twant := []string{\"Berlin\", \"Katowice\"}\n\n\t\twalk(aFunction, func(input string) {\n\t\t\tgot = append(got, input)\n\t\t})\n\n\t\tif !reflect.DeepEqual(got, want) {\n\t\t\tt.Errorf(\"got %v, want %v\", got, want)\n\t\t}\n\t})\n}\n\ntype Person struct {\n\tName    string\n\tProfile Profile\n}\n\ntype Profile struct {\n\tAge  int\n\tCity string\n}\n\nfunc assertContains(t testing.TB, haystack []string, needle string) {\n\tt.Helper()\n\tcontains := false\n\tfor _, x := range haystack {\n\t\tif x == needle {\n\t\t\tcontains = true\n\t\t\tbreak\n\t\t}\n\t}\n\tif !contains {\n\t\tt.Errorf(\"expected %v to contain %q but it didn't\", haystack, needle)\n\t}\n}\n"
  },
  {
    "path": "reflection/v2/reflection.go",
    "content": "package main\n\nimport \"reflect\"\n\nfunc walk(x interface{}, fn func(input string)) {\n\tval := reflect.ValueOf(x)\n\n\tfor i := 0; i < val.NumField(); i++ {\n\t\tfield := val.Field(i)\n\t\tfn(field.String())\n\t}\n}\n"
  },
  {
    "path": "reflection/v2/reflection_test.go",
    "content": "package main\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestWalk(t *testing.T) {\n\n\tcases := []struct {\n\t\tName          string\n\t\tInput         interface{}\n\t\tExpectedCalls []string\n\t}{\n\t\t{\n\t\t\t\"struct with one string field\",\n\t\t\tstruct {\n\t\t\t\tName string\n\t\t\t}{\"Chris\"},\n\t\t\t[]string{\"Chris\"},\n\t\t},\n\t\t{\n\t\t\t\"struct with two string fields\",\n\t\t\tstruct {\n\t\t\t\tName string\n\t\t\t\tCity string\n\t\t\t}{\"Chris\", \"London\"},\n\t\t\t[]string{\"Chris\", \"London\"},\n\t\t},\n\t}\n\n\tfor _, test := range cases {\n\t\tt.Run(test.Name, func(t *testing.T) {\n\t\t\tvar got []string\n\t\t\twalk(test.Input, func(input string) {\n\t\t\t\tgot = append(got, input)\n\t\t\t})\n\n\t\t\tif !reflect.DeepEqual(got, test.ExpectedCalls) {\n\t\t\t\tt.Errorf(\"got %v, want %v\", got, test.ExpectedCalls)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "reflection/v3/reflection.go",
    "content": "package main\n\nimport (\n\t\"reflect\"\n)\n\nfunc walk(x interface{}, fn func(input string)) {\n\tval := reflect.ValueOf(x)\n\n\tfor i := 0; i < val.NumField(); i++ {\n\t\tfield := val.Field(i)\n\n\t\tif field.Kind() == reflect.String {\n\t\t\tfn(field.String())\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "reflection/v3/reflection_test.go",
    "content": "package main\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestWalk(t *testing.T) {\n\n\tcases := []struct {\n\t\tName          string\n\t\tInput         interface{}\n\t\tExpectedCalls []string\n\t}{\n\t\t{\n\t\t\t\"struct with one string field\",\n\t\t\tstruct{ Name string }{\"Chris\"},\n\t\t\t[]string{\"Chris\"},\n\t\t},\n\t\t{\n\t\t\t\"struct with two string fields\",\n\t\t\tstruct {\n\t\t\t\tName string\n\t\t\t\tCity string\n\t\t\t}{\"Chris\", \"London\"},\n\t\t\t[]string{\"Chris\", \"London\"},\n\t\t},\n\t\t{\n\t\t\t\"struct with non string field\",\n\t\t\tstruct {\n\t\t\t\tName string\n\t\t\t\tAge  int\n\t\t\t}{\"Chris\", 33},\n\t\t\t[]string{\"Chris\"},\n\t\t},\n\t}\n\n\tfor _, test := range cases {\n\t\tt.Run(test.Name, func(t *testing.T) {\n\t\t\tvar got []string\n\t\t\twalk(test.Input, func(input string) {\n\t\t\t\tgot = append(got, input)\n\t\t\t})\n\n\t\t\tif !reflect.DeepEqual(got, test.ExpectedCalls) {\n\t\t\t\tt.Errorf(\"got %v, want %v\", got, test.ExpectedCalls)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "reflection/v4/reflection.go",
    "content": "package main\n\nimport (\n\t\"reflect\"\n)\n\nfunc walk(x interface{}, fn func(input string)) {\n\tval := reflect.ValueOf(x)\n\n\tfor i := 0; i < val.NumField(); i++ {\n\t\tfield := val.Field(i)\n\n\t\tswitch field.Kind() {\n\t\tcase reflect.String:\n\t\t\tfn(field.String())\n\t\tcase reflect.Struct:\n\t\t\twalk(field.Interface(), fn)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "reflection/v4/reflection_test.go",
    "content": "package main\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestWalk(t *testing.T) {\n\n\tcases := []struct {\n\t\tName          string\n\t\tInput         interface{}\n\t\tExpectedCalls []string\n\t}{\n\t\t{\n\t\t\t\"struct with one string field\",\n\t\t\tstruct{ Name string }{\"Chris\"},\n\t\t\t[]string{\"Chris\"},\n\t\t},\n\t\t{\n\t\t\t\"struct with two string fields\",\n\t\t\tstruct {\n\t\t\t\tName string\n\t\t\t\tCity string\n\t\t\t}{\"Chris\", \"London\"},\n\t\t\t[]string{\"Chris\", \"London\"},\n\t\t},\n\t\t{\n\t\t\t\"struct with non string field\",\n\t\t\tstruct {\n\t\t\t\tName string\n\t\t\t\tAge  int\n\t\t\t}{\"Chris\", 33},\n\t\t\t[]string{\"Chris\"},\n\t\t},\n\t\t{\n\t\t\t\"nested fields\",\n\t\t\tPerson{\n\t\t\t\t\"Chris\",\n\t\t\t\tProfile{33, \"London\"},\n\t\t\t},\n\t\t\t[]string{\"Chris\", \"London\"},\n\t\t},\n\t}\n\n\tfor _, test := range cases {\n\t\tt.Run(test.Name, func(t *testing.T) {\n\t\t\tvar got []string\n\t\t\twalk(test.Input, func(input string) {\n\t\t\t\tgot = append(got, input)\n\t\t\t})\n\n\t\t\tif !reflect.DeepEqual(got, test.ExpectedCalls) {\n\t\t\t\tt.Errorf(\"got %v, want %v\", got, test.ExpectedCalls)\n\t\t\t}\n\t\t})\n\t}\n}\n\ntype Person struct {\n\tName    string\n\tProfile Profile\n}\n\ntype Profile struct {\n\tAge  int\n\tCity string\n}\n"
  },
  {
    "path": "reflection/v5/reflection.go",
    "content": "package main\n\nimport (\n\t\"reflect\"\n)\n\nfunc walk(x interface{}, fn func(input string)) {\n\tval := getValue(x)\n\n\tfor i := 0; i < val.NumField(); i++ {\n\t\tfield := val.Field(i)\n\n\t\tswitch field.Kind() {\n\t\tcase reflect.String:\n\t\t\tfn(field.String())\n\t\tcase reflect.Struct:\n\t\t\twalk(field.Interface(), fn)\n\t\t}\n\t}\n}\n\nfunc getValue(x interface{}) reflect.Value {\n\tval := reflect.ValueOf(x)\n\n\tif val.Kind() == reflect.Ptr {\n\t\tval = val.Elem()\n\t}\n\n\treturn val\n}\n"
  },
  {
    "path": "reflection/v5/reflection_test.go",
    "content": "package main\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestWalk(t *testing.T) {\n\n\tcases := []struct {\n\t\tName          string\n\t\tInput         interface{}\n\t\tExpectedCalls []string\n\t}{\n\t\t{\n\t\t\t\"struct with one string field\",\n\t\t\tstruct{ Name string }{\"Chris\"},\n\t\t\t[]string{\"Chris\"},\n\t\t},\n\t\t{\n\t\t\t\"struct with two string fields\",\n\t\t\tstruct {\n\t\t\t\tName string\n\t\t\t\tCity string\n\t\t\t}{\"Chris\", \"London\"},\n\t\t\t[]string{\"Chris\", \"London\"},\n\t\t},\n\t\t{\n\t\t\t\"struct with non string field\",\n\t\t\tstruct {\n\t\t\t\tName string\n\t\t\t\tAge  int\n\t\t\t}{\"Chris\", 33},\n\t\t\t[]string{\"Chris\"},\n\t\t},\n\t\t{\n\t\t\t\"nested fields\",\n\t\t\tPerson{\n\t\t\t\t\"Chris\",\n\t\t\t\tProfile{33, \"London\"},\n\t\t\t},\n\t\t\t[]string{\"Chris\", \"London\"},\n\t\t},\n\t\t{\n\t\t\t\"pointers to things\",\n\t\t\t&Person{\n\t\t\t\t\"Chris\",\n\t\t\t\tProfile{33, \"London\"},\n\t\t\t},\n\t\t\t[]string{\"Chris\", \"London\"},\n\t\t},\n\t}\n\n\tfor _, test := range cases {\n\t\tt.Run(test.Name, func(t *testing.T) {\n\t\t\tvar got []string\n\t\t\twalk(test.Input, func(input string) {\n\t\t\t\tgot = append(got, input)\n\t\t\t})\n\n\t\t\tif !reflect.DeepEqual(got, test.ExpectedCalls) {\n\t\t\t\tt.Errorf(\"got %v, want %v\", got, test.ExpectedCalls)\n\t\t\t}\n\t\t})\n\t}\n}\n\ntype Person struct {\n\tName    string\n\tProfile Profile\n}\n\ntype Profile struct {\n\tAge  int\n\tCity string\n}\n"
  },
  {
    "path": "reflection/v6/reflection.go",
    "content": "package main\n\nimport (\n\t\"reflect\"\n)\n\nfunc walk(x interface{}, fn func(input string)) {\n\tval := getValue(x)\n\n\tnumberOfValues := 0\n\tvar getField func(int) reflect.Value\n\n\tswitch val.Kind() {\n\tcase reflect.String:\n\t\tfn(val.String())\n\tcase reflect.Struct:\n\t\tnumberOfValues = val.NumField()\n\t\tgetField = val.Field\n\tcase reflect.Slice:\n\t\tnumberOfValues = val.Len()\n\t\tgetField = val.Index\n\t}\n\n\tfor i := 0; i < numberOfValues; i++ {\n\t\twalk(getField(i).Interface(), fn)\n\t}\n}\n\nfunc getValue(x interface{}) reflect.Value {\n\tval := reflect.ValueOf(x)\n\n\tif val.Kind() == reflect.Ptr {\n\t\tval = val.Elem()\n\t}\n\n\treturn val\n}\n"
  },
  {
    "path": "reflection/v6/reflection_test.go",
    "content": "package main\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestWalk(t *testing.T) {\n\n\tcases := []struct {\n\t\tName          string\n\t\tInput         interface{}\n\t\tExpectedCalls []string\n\t}{\n\t\t{\n\t\t\t\"struct with one string field\",\n\t\t\tstruct{ Name string }{\"Chris\"},\n\t\t\t[]string{\"Chris\"},\n\t\t},\n\t\t{\n\t\t\t\"struct with two string fields\",\n\t\t\tstruct {\n\t\t\t\tName string\n\t\t\t\tCity string\n\t\t\t}{\"Chris\", \"London\"},\n\t\t\t[]string{\"Chris\", \"London\"},\n\t\t},\n\t\t{\n\t\t\t\"struct with non string field\",\n\t\t\tstruct {\n\t\t\t\tName string\n\t\t\t\tAge  int\n\t\t\t}{\"Chris\", 33},\n\t\t\t[]string{\"Chris\"},\n\t\t},\n\t\t{\n\t\t\t\"nested fields\",\n\t\t\tPerson{\n\t\t\t\t\"Chris\",\n\t\t\t\tProfile{33, \"London\"},\n\t\t\t},\n\t\t\t[]string{\"Chris\", \"London\"},\n\t\t},\n\t\t{\n\t\t\t\"pointers to things\",\n\t\t\t&Person{\n\t\t\t\t\"Chris\",\n\t\t\t\tProfile{33, \"London\"},\n\t\t\t},\n\t\t\t[]string{\"Chris\", \"London\"},\n\t\t},\n\t\t{\n\t\t\t\"slices\",\n\t\t\t[]Profile{\n\t\t\t\t{33, \"London\"},\n\t\t\t\t{34, \"Reykjavík\"},\n\t\t\t},\n\t\t\t[]string{\"London\", \"Reykjavík\"},\n\t\t},\n\t}\n\n\tfor _, test := range cases {\n\t\tt.Run(test.Name, func(t *testing.T) {\n\t\t\tvar got []string\n\t\t\twalk(test.Input, func(input string) {\n\t\t\t\tgot = append(got, input)\n\t\t\t})\n\n\t\t\tif !reflect.DeepEqual(got, test.ExpectedCalls) {\n\t\t\t\tt.Errorf(\"got %v, want %v\", got, test.ExpectedCalls)\n\t\t\t}\n\t\t})\n\t}\n}\n\ntype Person struct {\n\tName    string\n\tProfile Profile\n}\n\ntype Profile struct {\n\tAge  int\n\tCity string\n}\n"
  },
  {
    "path": "reflection/v7/reflection.go",
    "content": "package main\n\nimport (\n\t\"reflect\"\n)\n\nfunc walk(x interface{}, fn func(input string)) {\n\tval := getValue(x)\n\n\tnumberOfValues := 0\n\tvar getField func(int) reflect.Value\n\n\tswitch val.Kind() {\n\tcase reflect.String:\n\t\tfn(val.String())\n\tcase reflect.Struct:\n\t\tnumberOfValues = val.NumField()\n\t\tgetField = val.Field\n\tcase reflect.Slice, reflect.Array:\n\t\tnumberOfValues = val.Len()\n\t\tgetField = val.Index\n\t}\n\n\tfor i := 0; i < numberOfValues; i++ {\n\t\twalk(getField(i).Interface(), fn)\n\t}\n}\n\nfunc getValue(x interface{}) reflect.Value {\n\tval := reflect.ValueOf(x)\n\n\tif val.Kind() == reflect.Ptr {\n\t\tval = val.Elem()\n\t}\n\n\treturn val\n}\n"
  },
  {
    "path": "reflection/v7/reflection_test.go",
    "content": "package main\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestWalk(t *testing.T) {\n\n\tcases := []struct {\n\t\tName          string\n\t\tInput         interface{}\n\t\tExpectedCalls []string\n\t}{\n\t\t{\n\t\t\t\"struct with one string field\",\n\t\t\tstruct{ Name string }{\"Chris\"},\n\t\t\t[]string{\"Chris\"},\n\t\t},\n\t\t{\n\t\t\t\"struct with two string fields\",\n\t\t\tstruct {\n\t\t\t\tName string\n\t\t\t\tCity string\n\t\t\t}{\"Chris\", \"London\"},\n\t\t\t[]string{\"Chris\", \"London\"},\n\t\t},\n\t\t{\n\t\t\t\"struct with non string field\",\n\t\t\tstruct {\n\t\t\t\tName string\n\t\t\t\tAge  int\n\t\t\t}{\"Chris\", 33},\n\t\t\t[]string{\"Chris\"},\n\t\t},\n\t\t{\n\t\t\t\"nested fields\",\n\t\t\tPerson{\n\t\t\t\t\"Chris\",\n\t\t\t\tProfile{33, \"London\"},\n\t\t\t},\n\t\t\t[]string{\"Chris\", \"London\"},\n\t\t},\n\t\t{\n\t\t\t\"pointers to things\",\n\t\t\t&Person{\n\t\t\t\t\"Chris\",\n\t\t\t\tProfile{33, \"London\"},\n\t\t\t},\n\t\t\t[]string{\"Chris\", \"London\"},\n\t\t},\n\t\t{\n\t\t\t\"slices\",\n\t\t\t[]Profile{\n\t\t\t\t{33, \"London\"},\n\t\t\t\t{34, \"Reykjavík\"},\n\t\t\t},\n\t\t\t[]string{\"London\", \"Reykjavík\"},\n\t\t},\n\t\t{\n\t\t\t\"arrays\",\n\t\t\t[2]Profile{\n\t\t\t\t{33, \"London\"},\n\t\t\t\t{34, \"Reykjavík\"},\n\t\t\t},\n\t\t\t[]string{\"London\", \"Reykjavík\"},\n\t\t},\n\t}\n\n\tfor _, test := range cases {\n\t\tt.Run(test.Name, func(t *testing.T) {\n\t\t\tvar got []string\n\t\t\twalk(test.Input, func(input string) {\n\t\t\t\tgot = append(got, input)\n\t\t\t})\n\n\t\t\tif !reflect.DeepEqual(got, test.ExpectedCalls) {\n\t\t\t\tt.Errorf(\"got %v, want %v\", got, test.ExpectedCalls)\n\t\t\t}\n\t\t})\n\t}\n}\n\ntype Person struct {\n\tName    string\n\tProfile Profile\n}\n\ntype Profile struct {\n\tAge  int\n\tCity string\n}\n"
  },
  {
    "path": "reflection/v8/reflection.go",
    "content": "package main\n\nimport (\n\t\"reflect\"\n)\n\nfunc walk(x interface{}, fn func(input string)) {\n\tval := getValue(x)\n\n\twalkValue := func(value reflect.Value) {\n\t\twalk(value.Interface(), fn)\n\t}\n\n\tswitch val.Kind() {\n\tcase reflect.String:\n\t\tfn(val.String())\n\tcase reflect.Struct:\n\t\tfor i := 0; i < val.NumField(); i++ {\n\t\t\twalkValue(val.Field(i))\n\t\t}\n\tcase reflect.Slice, reflect.Array:\n\t\tfor i := 0; i < val.Len(); i++ {\n\t\t\twalkValue(val.Index(i))\n\t\t}\n\tcase reflect.Map:\n\t\tfor _, key := range val.MapKeys() {\n\t\t\twalkValue(val.MapIndex(key))\n\t\t}\n\t}\n}\n\nfunc getValue(x interface{}) reflect.Value {\n\tval := reflect.ValueOf(x)\n\n\tif val.Kind() == reflect.Ptr {\n\t\tval = val.Elem()\n\t}\n\n\treturn val\n}\n"
  },
  {
    "path": "reflection/v8/reflection_test.go",
    "content": "package main\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestWalk(t *testing.T) {\n\n\tcases := []struct {\n\t\tName          string\n\t\tInput         interface{}\n\t\tExpectedCalls []string\n\t}{\n\t\t{\n\t\t\t\"struct with one string field\",\n\t\t\tstruct{ Name string }{\"Chris\"},\n\t\t\t[]string{\"Chris\"},\n\t\t},\n\t\t{\n\t\t\t\"struct with two string fields\",\n\t\t\tstruct {\n\t\t\t\tName string\n\t\t\t\tCity string\n\t\t\t}{\"Chris\", \"London\"},\n\t\t\t[]string{\"Chris\", \"London\"},\n\t\t},\n\t\t{\n\t\t\t\"struct with non string field\",\n\t\t\tstruct {\n\t\t\t\tName string\n\t\t\t\tAge  int\n\t\t\t}{\"Chris\", 33},\n\t\t\t[]string{\"Chris\"},\n\t\t},\n\t\t{\n\t\t\t\"nested fields\",\n\t\t\tPerson{\n\t\t\t\t\"Chris\",\n\t\t\t\tProfile{33, \"London\"},\n\t\t\t},\n\t\t\t[]string{\"Chris\", \"London\"},\n\t\t},\n\t\t{\n\t\t\t\"pointers to things\",\n\t\t\t&Person{\n\t\t\t\t\"Chris\",\n\t\t\t\tProfile{33, \"London\"},\n\t\t\t},\n\t\t\t[]string{\"Chris\", \"London\"},\n\t\t},\n\t\t{\n\t\t\t\"slices\",\n\t\t\t[]Profile{\n\t\t\t\t{33, \"London\"},\n\t\t\t\t{34, \"Reykjavík\"},\n\t\t\t},\n\t\t\t[]string{\"London\", \"Reykjavík\"},\n\t\t},\n\t\t{\n\t\t\t\"arrays\",\n\t\t\t[2]Profile{\n\t\t\t\t{33, \"London\"},\n\t\t\t\t{34, \"Reykjavík\"},\n\t\t\t},\n\t\t\t[]string{\"London\", \"Reykjavík\"},\n\t\t},\n\t}\n\n\tfor _, test := range cases {\n\t\tt.Run(test.Name, func(t *testing.T) {\n\t\t\tvar got []string\n\t\t\twalk(test.Input, func(input string) {\n\t\t\t\tgot = append(got, input)\n\t\t\t})\n\n\t\t\tif !reflect.DeepEqual(got, test.ExpectedCalls) {\n\t\t\t\tt.Errorf(\"got %v, want %v\", got, test.ExpectedCalls)\n\t\t\t}\n\t\t})\n\t}\n\n\tt.Run(\"with maps\", func(t *testing.T) {\n\t\taMap := map[string]string{\n\t\t\t\"Foo\": \"Bar\",\n\t\t\t\"Baz\": \"Boz\",\n\t\t}\n\n\t\tvar got []string\n\t\twalk(aMap, func(input string) {\n\t\t\tgot = append(got, input)\n\t\t})\n\n\t\tassertContains(t, got, \"Bar\")\n\t\tassertContains(t, got, \"Boz\")\n\t})\n}\n\ntype Person struct {\n\tName    string\n\tProfile Profile\n}\n\ntype Profile struct {\n\tAge  int\n\tCity string\n}\n\nfunc assertContains(t testing.TB, haystack []string, needle string) {\n\tt.Helper()\n\tcontains := false\n\tfor _, x := range haystack {\n\t\tif x == needle {\n\t\t\tcontains = true\n\t\t}\n\t}\n\tif !contains {\n\t\tt.Errorf(\"expected %v to contain %q but it didn't\", haystack, needle)\n\t}\n}\n"
  },
  {
    "path": "reflection/v9/reflection.go",
    "content": "package main\n\nimport (\n\t\"reflect\"\n)\n\nfunc walk(x interface{}, fn func(input string)) {\n\tval := getValue(x)\n\n\twalkValue := func(value reflect.Value) {\n\t\twalk(value.Interface(), fn)\n\t}\n\n\tswitch val.Kind() {\n\tcase reflect.String:\n\t\tfn(val.String())\n\tcase reflect.Struct:\n\t\tfor i := 0; i < val.NumField(); i++ {\n\t\t\twalkValue(val.Field(i))\n\t\t}\n\tcase reflect.Slice, reflect.Array:\n\t\tfor i := 0; i < val.Len(); i++ {\n\t\t\twalkValue(val.Index(i))\n\t\t}\n\tcase reflect.Map:\n\t\tfor _, key := range val.MapKeys() {\n\t\t\twalkValue(val.MapIndex(key))\n\t\t}\n\tcase reflect.Chan:\n\t\tfor v, ok := val.Recv(); ok; v, ok = val.Recv() {\n\t\t\twalkValue(v)\n\t\t}\n\t}\n}\n\nfunc getValue(x interface{}) reflect.Value {\n\tval := reflect.ValueOf(x)\n\n\tif val.Kind() == reflect.Ptr {\n\t\tval = val.Elem()\n\t}\n\n\treturn val\n}\n"
  },
  {
    "path": "reflection/v9/reflection_test.go",
    "content": "package main\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestWalk(t *testing.T) {\n\n\tcases := []struct {\n\t\tName          string\n\t\tInput         interface{}\n\t\tExpectedCalls []string\n\t}{\n\t\t{\n\t\t\t\"struct with one string field\",\n\t\t\tstruct{ Name string }{\"Chris\"},\n\t\t\t[]string{\"Chris\"},\n\t\t},\n\t\t{\n\t\t\t\"struct with two string fields\",\n\t\t\tstruct {\n\t\t\t\tName string\n\t\t\t\tCity string\n\t\t\t}{\"Chris\", \"London\"},\n\t\t\t[]string{\"Chris\", \"London\"},\n\t\t},\n\t\t{\n\t\t\t\"struct with non string field\",\n\t\t\tstruct {\n\t\t\t\tName string\n\t\t\t\tAge  int\n\t\t\t}{\"Chris\", 33},\n\t\t\t[]string{\"Chris\"},\n\t\t},\n\t\t{\n\t\t\t\"nested fields\",\n\t\t\tPerson{\n\t\t\t\t\"Chris\",\n\t\t\t\tProfile{33, \"London\"},\n\t\t\t},\n\t\t\t[]string{\"Chris\", \"London\"},\n\t\t},\n\t\t{\n\t\t\t\"pointers to things\",\n\t\t\t&Person{\n\t\t\t\t\"Chris\",\n\t\t\t\tProfile{33, \"London\"},\n\t\t\t},\n\t\t\t[]string{\"Chris\", \"London\"},\n\t\t},\n\t\t{\n\t\t\t\"slices\",\n\t\t\t[]Profile{\n\t\t\t\t{33, \"London\"},\n\t\t\t\t{34, \"Reykjavík\"},\n\t\t\t},\n\t\t\t[]string{\"London\", \"Reykjavík\"},\n\t\t},\n\t\t{\n\t\t\t\"arrays\",\n\t\t\t[2]Profile{\n\t\t\t\t{33, \"London\"},\n\t\t\t\t{34, \"Reykjavík\"},\n\t\t\t},\n\t\t\t[]string{\"London\", \"Reykjavík\"},\n\t\t},\n\t}\n\n\tfor _, test := range cases {\n\t\tt.Run(test.Name, func(t *testing.T) {\n\t\t\tvar got []string\n\t\t\twalk(test.Input, func(input string) {\n\t\t\t\tgot = append(got, input)\n\t\t\t})\n\n\t\t\tif !reflect.DeepEqual(got, test.ExpectedCalls) {\n\t\t\t\tt.Errorf(\"got %v, want %v\", got, test.ExpectedCalls)\n\t\t\t}\n\t\t})\n\t}\n\n\tt.Run(\"with maps\", func(t *testing.T) {\n\t\taMap := map[string]string{\n\t\t\t\"Foo\": \"Bar\",\n\t\t\t\"Baz\": \"Boz\",\n\t\t}\n\n\t\tvar got []string\n\t\twalk(aMap, func(input string) {\n\t\t\tgot = append(got, input)\n\t\t})\n\n\t\tassertContains(t, got, \"Bar\")\n\t\tassertContains(t, got, \"Boz\")\n\t})\n\n\tt.Run(\"with channels\", func(t *testing.T) {\n\t\taChannel := make(chan Profile)\n\n\t\tgo func() {\n\t\t\taChannel <- Profile{33, \"Berlin\"}\n\t\t\taChannel <- Profile{34, \"Katowice\"}\n\t\t\tclose(aChannel)\n\t\t}()\n\n\t\tvar got []string\n\t\twant := []string{\"Berlin\", \"Katowice\"}\n\n\t\twalk(aChannel, func(input string) {\n\t\t\tgot = append(got, input)\n\t\t})\n\n\t\tif !reflect.DeepEqual(got, want) {\n\t\t\tt.Errorf(\"got %v, want %v\", got, want)\n\t\t}\n\t})\n}\n\ntype Person struct {\n\tName    string\n\tProfile Profile\n}\n\ntype Profile struct {\n\tAge  int\n\tCity string\n}\n\nfunc assertContains(t testing.TB, haystack []string, needle string) {\n\tt.Helper()\n\tcontains := false\n\tfor _, x := range haystack {\n\t\tif x == needle {\n\t\t\tcontains = true\n\t\t}\n\t}\n\tif !contains {\n\t\tt.Errorf(\"expected %v to contain %q but it didn't\", haystack, needle)\n\t}\n}\n"
  },
  {
    "path": "reflection.md",
    "content": "# Reflection\n\n**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/main/reflection)**\n\n[From Twitter](https://twitter.com/peterbourgon/status/1011403901419937792?s=09)\n\n> golang challenge: write a function `walk(x interface{}, fn func(string))` which takes a struct `x` and calls `fn` for all strings fields found inside. difficulty level: recursively.\n\nTo do this we will need to use _reflection_.\n\n> Reflection in computing is the ability of a program to examine its own structure, particularly through types; it's a form of metaprogramming. It's also a great source of confusion.\n\nFrom [The Go Blog: Reflection](https://blog.golang.org/laws-of-reflection)\n\n## What is `interface{}`?\n\nWe have enjoyed the type-safety that Go has offered us in terms of functions that work with known types, such as `string`, `int` and our own types like `BankAccount`.\n\nThis means that we get some documentation for free and the compiler will complain if you try and pass the wrong type to a function.\n\nYou may come across scenarios though where you want to write a function where you don't know the type at compile time.\n\nGo lets us get around this with the type `interface{}` which you can think of as just _any_ type (in fact, in Go `any` is an [alias](https://cs.opensource.google/go/go/+/master:src/builtin/builtin.go;drc=master;l=95) for `interface{}`).\n\nSo `walk(x interface{}, fn func(string))` will accept any value for `x`.\n\n### So why not use `interface{}` for everything and have really flexible functions?\n\n- As a user of a function that takes `interface{}` you lose type safety. What if you meant to pass `Herd.species` of type `string` into a function but instead did `Herd.count` which is an `int`? The compiler won't be able to inform you of your mistake. You also have no idea _what_ you're allowed to pass to a function. Knowing that a function takes a `UserService` for instance is very useful.\n- As a writer of such a function, you have to be able to inspect _anything_ that has been passed to you and try and figure out what the type is and what you can do with it. This is done using _reflection_. This can be quite clumsy and difficult to read and is generally less performant (as you have to do checks at runtime).\n\nIn short only use reflection if you really need to.\n\nIf you want polymorphic functions, consider if you could design it around an interface (not `interface{}`, confusingly) so that users can use your function with multiple types if they implement whatever methods you need for your function to work.\n\nOur function will need to be able to work with lots of different things. As always we'll take an iterative approach, writing tests for each new thing we want to support and refactoring along the way until we're done.\n\n## Write the test first\n\nWe'll want to call our function with a struct that has a string field in it (`x`). Then we can spy on the function (`fn`) passed in to see if it is called.\n\n```go\nfunc TestWalk(t *testing.T) {\n\n\texpected := \"Chris\"\n\tvar got []string\n\n\tx := struct {\n\t\tName string\n\t}{expected}\n\n\twalk(x, func(input string) {\n\t\tgot = append(got, input)\n\t})\n\n\tif len(got) != 1 {\n\t\tt.Errorf(\"wrong number of function calls, got %d want %d\", len(got), 1)\n\t}\n}\n```\n\n- We want to store a slice of strings (`got`) which stores which strings were passed into `fn` by `walk`. Often in previous chapters, we have made dedicated types for this to spy on function/method invocations but in this case, we can just pass in an anonymous function for `fn` that closes over `got`.\n- We use an anonymous `struct` with a `Name` field of type string to go for the simplest \"happy\" path.\n- Finally, call `walk` with `x` and the spy and for now just check the length of `got`, we'll be more specific with our assertions once we've got something very basic working.\n\n## Try to run the test\n\n```\n./reflection_test.go:21:2: undefined: walk\n```\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\nWe need to define `walk`\n\n```go\nfunc walk(x interface{}, fn func(input string)) {\n\n}\n```\n\nTry and run the test again\n\n```\n=== RUN   TestWalk\n--- FAIL: TestWalk (0.00s)\n    reflection_test.go:19: wrong number of function calls, got 0 want 1\nFAIL\n```\n\n## Write enough code to make it pass\n\nWe can call the spy with any string to make this pass.\n\n```go\nfunc walk(x interface{}, fn func(input string)) {\n\tfn(\"I still can't believe South Korea beat Germany 2-0 to put them last in their group\")\n}\n```\n\nThe test should now be passing. The next thing we'll need to do is make a more specific assertion on what our `fn` is being called with.\n\n## Write the test first\n\nAdd the following to the existing test to check the string passed to `fn` is correct\n\n```go\nif got[0] != expected {\n\tt.Errorf(\"got %q, want %q\", got[0], expected)\n}\n```\n\n## Try to run the test\n\n```\n=== RUN   TestWalk\n--- FAIL: TestWalk (0.00s)\n    reflection_test.go:23: got 'I still can't believe South Korea beat Germany 2-0 to put them last in their group', want 'Chris'\nFAIL\n```\n\n## Write enough code to make it pass\n\n```go\nfunc walk(x interface{}, fn func(input string)) {\n\tval := reflect.ValueOf(x)\n\tfield := val.Field(0)\n\tfn(field.String())\n}\n```\n\nThis code is _very unsafe and very naive_, but remember: our goal when we are in \"red\" (the tests failing) is to write the smallest amount of code possible. We then write more tests to address our concerns.\n\nWe need to use reflection to have a look at `x` and try and look at its properties.\n\nThe [reflect package](https://pkg.go.dev/reflect) has a function `ValueOf` which returns us a `Value` of a given variable. This has ways for us to inspect a value, including its fields which we use on the next line.\n\nWe then make some very optimistic assumptions about the value passed in:\n\n- We look at the first and only field. However, there may be no fields at all, which would cause a panic.\n- We then call `String()`, which returns the underlying value as a string. However, this would be wrong if the field was something other than a string.\n\n## Refactor\n\nOur code is passing for the simple case but we know our code has a lot of shortcomings.\n\nWe're going to be writing a number of tests where we pass in different values and checking the array of strings that `fn` was called with.\n\nWe should refactor our test into a table based test to make this easier to continue testing new scenarios.\n\n```go\nfunc TestWalk(t *testing.T) {\n\n\tcases := []struct {\n\t\tName          string\n\t\tInput         interface{}\n\t\tExpectedCalls []string\n\t}{\n\t\t{\n\t\t\t\"struct with one string field\",\n\t\t\tstruct {\n\t\t\t\tName string\n\t\t\t}{\"Chris\"},\n\t\t\t[]string{\"Chris\"},\n\t\t},\n\t}\n\n\tfor _, test := range cases {\n\t\tt.Run(test.Name, func(t *testing.T) {\n\t\t\tvar got []string\n\t\t\twalk(test.Input, func(input string) {\n\t\t\t\tgot = append(got, input)\n\t\t\t})\n\n\t\t\tif !reflect.DeepEqual(got, test.ExpectedCalls) {\n\t\t\t\tt.Errorf(\"got %v, want %v\", got, test.ExpectedCalls)\n\t\t\t}\n\t\t})\n\t}\n}\n```\n\nNow we can easily add a scenario to see what happens if we have more than one string field.\n\n## Write the test first\n\nAdd the following scenario to the `cases`.\n\n```\n{\n    \"struct with two string fields\",\n    struct {\n        Name string\n        City string\n    }{\"Chris\", \"London\"},\n    []string{\"Chris\", \"London\"},\n},\n```\n\n## Try to run the test\n\n```\n=== RUN   TestWalk/struct_with_two_string_fields\n    --- FAIL: TestWalk/struct_with_two_string_fields (0.00s)\n        reflection_test.go:40: got [Chris], want [Chris London]\n```\n\n## Write enough code to make it pass\n\n```go\nfunc walk(x interface{}, fn func(input string)) {\n\tval := reflect.ValueOf(x)\n\n\tfor i := 0; i < val.NumField(); i++ {\n\t\tfield := val.Field(i)\n\t\tfn(field.String())\n\t}\n}\n```\n\n`val` has a method `NumField` which returns the number of fields in the value. This lets us iterate over the fields and call `fn` which passes our test.\n\n## Refactor\n\nIt doesn't look like there's any obvious refactors here that would improve the code so let's press on.\n\nThe next shortcoming in `walk` is that it assumes every field is a `string`. Let's write a test for this scenario.\n\n## Write the test first\n\nAdd the following case\n\n```\n{\n    \"struct with non string field\",\n    struct {\n        Name string\n        Age  int\n    }{\"Chris\", 33},\n    []string{\"Chris\"},\n},\n```\n\n## Try to run the test\n\n```\n=== RUN   TestWalk/struct_with_non_string_field\n    --- FAIL: TestWalk/struct_with_non_string_field (0.00s)\n        reflection_test.go:46: got [Chris <int Value>], want [Chris]\n```\n\n## Write enough code to make it pass\n\nWe need to check that the type of the field is a `string`.\n\n```go\nfunc walk(x interface{}, fn func(input string)) {\n\tval := reflect.ValueOf(x)\n\n\tfor i := 0; i < val.NumField(); i++ {\n\t\tfield := val.Field(i)\n\n\t\tif field.Kind() == reflect.String {\n\t\t\tfn(field.String())\n\t\t}\n\t}\n}\n```\n\nWe can do that by checking its [`Kind`](https://pkg.go.dev/reflect#Kind).\n\n## Refactor\n\nAgain it looks like the code is reasonable enough for now.\n\nThe next scenario is what if it isn't a \"flat\" `struct`? In other words, what happens if we have a `struct` with some nested fields?\n\n## Write the test first\n\nWe have been using the anonymous struct syntax to declare types ad-hocly for our tests so we could continue to do that like so\n\n```\n{\n    \"nested fields\",\n    struct {\n        Name string\n        Profile struct {\n            Age  int\n            City string\n        }\n    }{\"Chris\", struct {\n        Age  int\n        City string\n    }{33, \"London\"}},\n    []string{\"Chris\", \"London\"},\n},\n```\n\nBut we can see that when you get inner anonymous structs the syntax gets a little messy. [There is a proposal to make it so the syntax would be nicer](https://github.com/golang/go/issues/12854).\n\nLet's just refactor this by making a known type for this scenario and reference it in the test. There is a little indirection in that some of the code for our test is outside the test but readers should be able to infer the structure of the `struct` by looking at the initialisation.\n\nAdd the following type declarations somewhere in your test file\n\n```go\ntype Person struct {\n\tName    string\n\tProfile Profile\n}\n\ntype Profile struct {\n\tAge  int\n\tCity string\n}\n```\n\nNow we can add this to our cases which reads a lot clearer than before\n\n```\n{\n    \"nested fields\",\n    Person{\n        \"Chris\",\n        Profile{33, \"London\"},\n    },\n    []string{\"Chris\", \"London\"},\n},\n```\n\n## Try to run the test\n\n```\n=== RUN   TestWalk/Nested_fields\n    --- FAIL: TestWalk/nested_fields (0.00s)\n        reflection_test.go:54: got [Chris], want [Chris London]\n```\n\nThe problem is we're only iterating on the fields on the first level of the type's hierarchy.\n\n## Write enough code to make it pass\n\n```go\nfunc walk(x interface{}, fn func(input string)) {\n\tval := reflect.ValueOf(x)\n\n\tfor i := 0; i < val.NumField(); i++ {\n\t\tfield := val.Field(i)\n\n\t\tif field.Kind() == reflect.String {\n\t\t\tfn(field.String())\n\t\t}\n\n\t\tif field.Kind() == reflect.Struct {\n\t\t\twalk(field.Interface(), fn)\n\t\t}\n\t}\n}\n```\n\nThe solution is quite simple, we again inspect its `Kind` and if it happens to be a `struct` we just call `walk` again on that inner `struct`.\n\n## Refactor\n\n```go\nfunc walk(x interface{}, fn func(input string)) {\n\tval := reflect.ValueOf(x)\n\n\tfor i := 0; i < val.NumField(); i++ {\n\t\tfield := val.Field(i)\n\n\t\tswitch field.Kind() {\n\t\tcase reflect.String:\n\t\t\tfn(field.String())\n\t\tcase reflect.Struct:\n\t\t\twalk(field.Interface(), fn)\n\t\t}\n\t}\n}\n```\n\nWhen you're doing a comparison on the same value more than once _generally_ refactoring into a `switch` will improve readability and make your code easier to extend.\n\nWhat if the value of the struct passed in is a pointer?\n\n## Write the test first\n\nAdd this case\n\n```\n{\n    \"pointers to things\",\n    &Person{\n        \"Chris\",\n        Profile{33, \"London\"},\n    },\n    []string{\"Chris\", \"London\"},\n},\n```\n\n## Try to run the test\n\n```\n=== RUN   TestWalk/pointers_to_things\npanic: reflect: call of reflect.Value.NumField on ptr Value [recovered]\n    panic: reflect: call of reflect.Value.NumField on ptr Value\n```\n\n## Write enough code to make it pass\n\n```go\nfunc walk(x interface{}, fn func(input string)) {\n\tval := reflect.ValueOf(x)\n\n\tif val.Kind() == reflect.Pointer {\n\t\tval = val.Elem()\n\t}\n\n\tfor i := 0; i < val.NumField(); i++ {\n\t\tfield := val.Field(i)\n\n\t\tswitch field.Kind() {\n\t\tcase reflect.String:\n\t\t\tfn(field.String())\n\t\tcase reflect.Struct:\n\t\t\twalk(field.Interface(), fn)\n\t\t}\n\t}\n}\n```\n\nYou can't use `NumField` on a pointer `Value`, we need to extract the underlying value before we can do that by using `Elem()`.\n\n## Refactor\n\nLet's encapsulate the responsibility of extracting the `reflect.Value` from a given `interface{}` into a function.\n\n```go\nfunc walk(x interface{}, fn func(input string)) {\n\tval := getValue(x)\n\n\tfor i := 0; i < val.NumField(); i++ {\n\t\tfield := val.Field(i)\n\n\t\tswitch field.Kind() {\n\t\tcase reflect.String:\n\t\t\tfn(field.String())\n\t\tcase reflect.Struct:\n\t\t\twalk(field.Interface(), fn)\n\t\t}\n\t}\n}\n\nfunc getValue(x interface{}) reflect.Value {\n\tval := reflect.ValueOf(x)\n\n\tif val.Kind() == reflect.Pointer {\n\t\tval = val.Elem()\n\t}\n\n\treturn val\n}\n```\n\nThis actually adds _more_ code but I feel the abstraction level is right.\n\n- Get the `reflect.Value` of `x` so I can inspect it, I don't care how.\n- Iterate over the fields, doing whatever needs to be done depending on its type.\n\nNext, we need to cover slices.\n\n## Write the test first\n\n```\n{\n    \"slices\",\n    []Profile {\n        {33, \"London\"},\n        {34, \"Reykjavík\"},\n    },\n    []string{\"London\", \"Reykjavík\"},\n},\n```\n\n## Try to run the test\n\n```\n=== RUN   TestWalk/slices\npanic: reflect: call of reflect.Value.NumField on slice Value [recovered]\n    panic: reflect: call of reflect.Value.NumField on slice Value\n```\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\nThis is similar to the pointer scenario before, we are trying to call `NumField` on our `reflect.Value` but it doesn't have one as it's not a struct.\n\n## Write enough code to make it pass\n\n```go\nfunc walk(x interface{}, fn func(input string)) {\n\tval := getValue(x)\n\n\tif val.Kind() == reflect.Slice {\n\t\tfor i := 0; i < val.Len(); i++ {\n\t\t\twalk(val.Index(i).Interface(), fn)\n\t\t}\n\t\treturn\n\t}\n\n\tfor i := 0; i < val.NumField(); i++ {\n\t\tfield := val.Field(i)\n\n\t\tswitch field.Kind() {\n\t\tcase reflect.String:\n\t\t\tfn(field.String())\n\t\tcase reflect.Struct:\n\t\t\twalk(field.Interface(), fn)\n\t\t}\n\t}\n}\n```\n\n## Refactor\n\nThis works but it's yucky. No worries, we have working code backed by tests so we are free to tinker all we like.\n\nIf you think a little abstractly, we want to call `walk` on either\n\n- Each field in a struct\n- Each _thing_ in a slice\n\nOur code at the moment does this but doesn't reflect it very well. We just have a check at the start to see if it's a slice (with a `return` to stop the rest of the code executing) and if it's not we just assume it's a struct.\n\nLet's rework the code so instead we check the type _first_ and then do our work.\n\n```go\nfunc walk(x interface{}, fn func(input string)) {\n\tval := getValue(x)\n\n\tswitch val.Kind() {\n\tcase reflect.Struct:\n\t\tfor i := 0; i < val.NumField(); i++ {\n\t\t\twalk(val.Field(i).Interface(), fn)\n\t\t}\n\tcase reflect.Slice:\n\t\tfor i := 0; i < val.Len(); i++ {\n\t\t\twalk(val.Index(i).Interface(), fn)\n\t\t}\n\tcase reflect.String:\n\t\tfn(val.String())\n\t}\n}\n```\n\nLooking much better! If it's a struct or a slice we iterate over its values calling `walk` on each one. Otherwise, if it's a `reflect.String` we can call `fn`.\n\nStill, to me it feels like it could be better. There's repetition of the operation of iterating over fields/values and then calling `walk` but conceptually they're the same.\n\n```go\nfunc walk(x interface{}, fn func(input string)) {\n\tval := getValue(x)\n\n\tnumberOfValues := 0\n\tvar getField func(int) reflect.Value\n\n\tswitch val.Kind() {\n\tcase reflect.String:\n\t\tfn(val.String())\n\tcase reflect.Struct:\n\t\tnumberOfValues = val.NumField()\n\t\tgetField = val.Field\n\tcase reflect.Slice:\n\t\tnumberOfValues = val.Len()\n\t\tgetField = val.Index\n\t}\n\n\tfor i := 0; i < numberOfValues; i++ {\n\t\twalk(getField(i).Interface(), fn)\n\t}\n}\n```\n\nIf the `value` is a `reflect.String` then we just call `fn` like normal.\n\nOtherwise, our `switch` will extract out two things depending on the type\n\n- How many fields there are\n- How to extract the `Value` (`Field` or `Index`)\n\nOnce we've determined those things we can iterate through `numberOfValues` calling `walk` with the result of the `getField` function.\n\nNow we've done this, handling arrays should be trivial.\n\n## Write the test first\n\nAdd to the cases\n\n```\n{\n    \"arrays\",\n    [2]Profile {\n        {33, \"London\"},\n        {34, \"Reykjavík\"},\n    },\n    []string{\"London\", \"Reykjavík\"},\n},\n```\n\n## Try to run the test\n\n```\n=== RUN   TestWalk/arrays\n    --- FAIL: TestWalk/arrays (0.00s)\n        reflection_test.go:78: got [], want [London Reykjavík]\n```\n\n## Write enough code to make it pass\n\nArrays can be handled the same way as slices, so just add it to the case with a comma\n\n```go\nfunc walk(x interface{}, fn func(input string)) {\n\tval := getValue(x)\n\n\tnumberOfValues := 0\n\tvar getField func(int) reflect.Value\n\n\tswitch val.Kind() {\n\tcase reflect.String:\n\t\tfn(val.String())\n\tcase reflect.Struct:\n\t\tnumberOfValues = val.NumField()\n\t\tgetField = val.Field\n\tcase reflect.Slice, reflect.Array:\n\t\tnumberOfValues = val.Len()\n\t\tgetField = val.Index\n\t}\n\n\tfor i := 0; i < numberOfValues; i++ {\n\t\twalk(getField(i).Interface(), fn)\n\t}\n}\n```\n\nThe next type we want to handle is `map`.\n\n## Write the test first\n\n```\n{\n    \"maps\",\n    map[string]string{\n        \"Cow\": \"Moo\",\n        \"Sheep\": \"Baa\",\n    },\n    []string{\"Moo\", \"Baa\"},\n},\n```\n\n## Try to run the test\n\n```\n=== RUN   TestWalk/maps\n    --- FAIL: TestWalk/maps (0.00s)\n        reflection_test.go:86: got [], want [Moo Baa]\n```\n\n## Write enough code to make it pass\n\nAgain if you think a little abstractly you can see that `map` is very similar to `struct`, it's just the keys are unknown at compile time.\n\n```go\nfunc walk(x interface{}, fn func(input string)) {\n\tval := getValue(x)\n\n\tnumberOfValues := 0\n\tvar getField func(int) reflect.Value\n\n\tswitch val.Kind() {\n\tcase reflect.String:\n\t\tfn(val.String())\n\tcase reflect.Struct:\n\t\tnumberOfValues = val.NumField()\n\t\tgetField = val.Field\n\tcase reflect.Slice, reflect.Array:\n\t\tnumberOfValues = val.Len()\n\t\tgetField = val.Index\n\tcase reflect.Map:\n\t\tfor _, key := range val.MapKeys() {\n\t\t\twalk(val.MapIndex(key).Interface(), fn)\n\t\t}\n\t}\n\n\tfor i := 0; i < numberOfValues; i++ {\n\t\twalk(getField(i).Interface(), fn)\n\t}\n}\n```\n\nHowever, by design you cannot get values out of a map by index. It's only done by _key_, so that breaks our abstraction, darn.\n\n## Refactor\n\nHow do you feel right now? It felt like maybe a nice abstraction at the time but now the code feels a little wonky.\n\n_This is OK!_ Refactoring is a journey and sometimes we will make mistakes. A major point of TDD is it gives us the freedom to try these things out.\n\nBy taking small steps backed by tests this is in no way an irreversible situation. Let's just put it back to how it was before the refactor.\n\n```go\nfunc walk(x interface{}, fn func(input string)) {\n\tval := getValue(x)\n\n\twalkValue := func(value reflect.Value) {\n\t\twalk(value.Interface(), fn)\n\t}\n\n\tswitch val.Kind() {\n\tcase reflect.String:\n\t\tfn(val.String())\n\tcase reflect.Struct:\n\t\tfor i := 0; i < val.NumField(); i++ {\n\t\t\twalkValue(val.Field(i))\n\t\t}\n\tcase reflect.Slice, reflect.Array:\n\t\tfor i := 0; i < val.Len(); i++ {\n\t\t\twalkValue(val.Index(i))\n\t\t}\n\tcase reflect.Map:\n\t\tfor _, key := range val.MapKeys() {\n\t\t\twalkValue(val.MapIndex(key))\n\t\t}\n\t}\n}\n```\n\nWe've introduced `walkValue` which DRYs up the calls to `walk` inside our `switch` so that they only have to extract out the `reflect.Value`s from `val`.\n\n### One final problem\n\nRemember that maps in Go do not guarantee order. So your tests will sometimes fail because we assert that the calls to `fn` are done in a particular order.\n\nTo fix this, we'll need to move our assertion with the maps to a new test where we do not care about the order.\n\n```go\nt.Run(\"with maps\", func(t *testing.T) {\n\taMap := map[string]string{\n\t\t\"Cow\":   \"Moo\",\n\t\t\"Sheep\": \"Baa\",\n\t}\n\n\tvar got []string\n\twalk(aMap, func(input string) {\n\t\tgot = append(got, input)\n\t})\n\n\tassertContains(t, got, \"Moo\")\n\tassertContains(t, got, \"Baa\")\n})\n```\n\nHere is how `assertContains` is defined\n\n```go\nfunc assertContains(t testing.TB, haystack []string, needle string) {\n\tt.Helper()\n\tcontains := false\n\tfor _, x := range haystack {\n\t\tif x == needle {\n\t\t\tcontains = true\n\t\t}\n\t}\n\tif !contains {\n\t\tt.Errorf(\"expected %v to contain %q but it didn't\", haystack, needle)\n\t}\n}\n```\n\nSince we have extracted maps into a new test, we haven't seen the failure message. Intentionally break the `with maps` test here so that you can check the error message, then fix it again so all tests are passing.\n\nThe next type we want to handle is `chan`.\n\n## Write the test first\n\n```go\nt.Run(\"with channels\", func(t *testing.T) {\n\taChannel := make(chan Profile)\n\n\tgo func() {\n\t\taChannel <- Profile{33, \"Berlin\"}\n\t\taChannel <- Profile{34, \"Katowice\"}\n\t\tclose(aChannel)\n\t}()\n\n\tvar got []string\n\twant := []string{\"Berlin\", \"Katowice\"}\n\n\twalk(aChannel, func(input string) {\n\t\tgot = append(got, input)\n\t})\n\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %v, want %v\", got, want)\n\t}\n})\n```\n\n## Try to run the test\n\n```\n--- FAIL: TestWalk (0.00s)\n    --- FAIL: TestWalk/with_channels (0.00s)\n        reflection_test.go:115: got [], want [Berlin Katowice]\n```\n\n## Write enough code to make it pass\n\nWe can iterate through all values sent through channel until it was closed with Recv()\n\n```go\nfunc walk(x interface{}, fn func(input string)) {\n\tval := getValue(x)\n\n\twalkValue := func(value reflect.Value) {\n\t\twalk(value.Interface(), fn)\n\t}\n\n\tswitch val.Kind() {\n\tcase reflect.String:\n\t\tfn(val.String())\n\tcase reflect.Struct:\n\t\tfor i := 0; i < val.NumField(); i++ {\n\t\t\twalkValue(val.Field(i))\n\t\t}\n\tcase reflect.Slice, reflect.Array:\n\t\tfor i := 0; i < val.Len(); i++ {\n\t\t\twalkValue(val.Index(i))\n\t\t}\n\tcase reflect.Map:\n\t\tfor _, key := range val.MapKeys() {\n\t\t\twalkValue(val.MapIndex(key))\n\t\t}\n\tcase reflect.Chan:\n\t\tfor {\n\t\t\tif v, ok := val.Recv(); ok {\n\t\t\t\twalkValue(v)\n\t\t\t} else {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n}\n```\nThe next type we want to handle is `func`.\n\n## Write the test first\n\n```go\nt.Run(\"with function\", func(t *testing.T) {\n\taFunction := func() (Profile, Profile) {\n\t\treturn Profile{33, \"Berlin\"}, Profile{34, \"Katowice\"}\n\t}\n\n\tvar got []string\n\twant := []string{\"Berlin\", \"Katowice\"}\n\n\twalk(aFunction, func(input string) {\n\t\tgot = append(got, input)\n\t})\n\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %v, want %v\", got, want)\n\t}\n})\n```\n\n## Try to run the test\n\n```\n--- FAIL: TestWalk (0.00s)\n    --- FAIL: TestWalk/with_function (0.00s)\n        reflection_test.go:132: got [], want [Berlin Katowice]\n```\n\n## Write enough code to make it pass\n\nNon zero-argument functions do not seem to make a lot of sense in this scenario. But we should allow for arbitrary return values.\n\n```go\nfunc walk(x interface{}, fn func(input string)) {\n\tval := getValue(x)\n\n\twalkValue := func(value reflect.Value) {\n\t\twalk(value.Interface(), fn)\n\t}\n\n\tswitch val.Kind() {\n\tcase reflect.String:\n\t\tfn(val.String())\n\tcase reflect.Struct:\n\t\tfor i := 0; i < val.NumField(); i++ {\n\t\t\twalkValue(val.Field(i))\n\t\t}\n\tcase reflect.Slice, reflect.Array:\n\t\tfor i := 0; i < val.Len(); i++ {\n\t\t\twalkValue(val.Index(i))\n\t\t}\n\tcase reflect.Map:\n\t\tfor _, key := range val.MapKeys() {\n\t\t\twalkValue(val.MapIndex(key))\n\t\t}\n\tcase reflect.Chan:\n\t\tfor v, ok := val.Recv(); ok; v, ok = val.Recv() {\n\t\t\twalkValue(v)\n\t\t}\n\tcase reflect.Func:\n\t\tvalFnResult := val.Call(nil)\n\t\tfor _, res := range valFnResult {\n\t\t\twalkValue(res)\n\t\t}\n\t}\n}\n```\n\n## Wrapping up\n\n- Introduced some concepts from the `reflect` package.\n- Used recursion to traverse arbitrary data structures.\n- Did an in retrospect bad refactor but didn't get too upset about it. By working iteratively with tests it's not such a big deal.\n- This only covered a small aspect of reflection. [The Go blog has an excellent post covering more details](https://blog.golang.org/laws-of-reflection).\n- Now that you know about reflection, do your best to avoid using it.\n"
  },
  {
    "path": "revisiting-arrays-and-slices-with-generics.md",
    "content": "# Revisiting arrays and slices with generics\n\n**[The code for this chapter is a continuation from Arrays and Slices, found here](https://github.com/quii/learn-go-with-tests/tree/main/arrays)**\n\nTake a look at both `SumAll` and `SumAllTails` that we wrote in [arrays and slices](arrays-and-slices.md). If you don't have your version please copy the code from the [arrays and slices](arrays-and-slices.md) chapter along with the tests.\n\n```go\n// Sum calculates the total from a slice of numbers.\nfunc Sum(numbers []int) int {\n\tvar sum int\n\tfor _, number := range numbers {\n\t\tsum += number\n\t}\n\treturn sum\n}\n\n// SumAllTails calculates the sums of all but the first number given a collection of slices.\nfunc SumAllTails(numbersToSum ...[]int) []int {\n\tvar sums []int\n\tfor _, numbers := range numbersToSum {\n\t\tif len(numbers) == 0 {\n\t\t\tsums = append(sums, 0)\n\t\t} else {\n\t\t\ttail := numbers[1:]\n\t\t\tsums = append(sums, Sum(tail))\n\t\t}\n\t}\n\n\treturn sums\n}\n```\n\nDo you see a recurring pattern?\n\n- Create some kind of \"initial\" result value.\n- Iterate over the collection, applying some kind of operation (or function) to the result and the next item in the slice, setting a new value for the result\n- Return the result.\n\nThis idea is commonly talked about in functional programming circles, often times called 'reduce' or [fold](https://en.wikipedia.org/wiki/Fold_(higher-order_function)).\n\n> In functional programming, fold (also termed reduce, accumulate, aggregate, compress, or inject) refers to a family of higher-order functions that analyze a recursive data structure and through use of a given combining operation, recombine the results of recursively processing its constituent parts, building up a return value. Typically, a fold is presented with a combining function, a top node of a data structure, and possibly some default values to be used under certain conditions. The fold then proceeds to combine elements of the data structure's hierarchy, using the function in a systematic way.\n\nGo has always had higher-order functions, and as of version 1.18 it also has [generics](./generics.md), so it is now possible to define some of these functions discussed in our wider field. There's no point burying your head in the sand, this is a very common abstraction outside the Go ecosystem and it'll be beneficial to understand it.\n\nNow, I know some of you are probably cringing at this.\n\n> Go is supposed to be simple\n\n**Don't conflate easiness, with simplicity**. Doing loops and copy-pasting code is easy, but it's not necessarily simple. For more on simple vs easy, watch [Rich Hickey's masterpiece of a talk - Simple Made Easy](https://www.youtube.com/watch?v=SxdOUGdseq4).\n\n**Don't conflate unfamiliarity, with complexity**. Fold/reduce may initially sound scary and computer-sciencey but all it really is, is an abstraction over a very common operation. Taking a collection, and combining it into one item. When you step back, you'll realise you probably do this _a lot_.\n\n## A generic refactor\n\nA mistake people often make with shiny new language features is they start by using them without having a concrete use-case. They rely on conjecture and guesswork to guide their efforts.\n\nThankfully we've written our \"useful\" functions and have tests around them, so now we are free to experiment with ideas in the refactoring stage of TDD and know that whatever we're trying, has a verification of its value via our unit tests.\n\nUsing generics as a tool for simplifying code via the refactoring step is far more likely to guide you to useful improvements, rather than premature abstractions.\n\nWe are safe to try things out, re-run our tests, if we like the change we can commit. If not, just revert the change. This freedom to experiment is one of the truly huge values of TDD.\n\nYou should be familiar with the generics syntax [from the previous chapter](generics.md), try and write your own `Reduce` function and use it inside `Sum` and `SumAllTails`.\n\n### Hints\n\nIf you think about the arguments to your function first, it'll give you a very small set of valid solutions\n  - The array you want to reduce\n  - Some kind of combining function\n\n\"Reduce\" is an incredibly well documented pattern, there's no need to re-invent the wheel. [Read the wiki, in particular the lists section](https://en.wikipedia.org/wiki/Fold_(higher-order_function)), it should prompt you for another argument you'll need.\n\n> In practice, it is convenient and natural to have an initial value\n\n### My first-pass of `Reduce`\n\n```go\nfunc Reduce[A any](collection []A, f func(A, A) A, initialValue A) A {\n\tvar result = initialValue\n\tfor _, x := range collection {\n\t\tresult = f(result, x)\n\t}\n\treturn result\n}\n```\n\nReduce captures the _essence_ of the pattern, it's a function that takes a collection, an accumulating function, an initial value, and returns a single value. There's no messy distractions around concrete types.\n\nIf you understand generics syntax, you should have no problem understanding what this function does. By using the recognised term `Reduce`, programmers from other languages understand the intent too.\n\n### The usage\n\n```go\n// Sum calculates the total from a slice of numbers.\nfunc Sum(numbers []int) int {\n\tadd := func(acc, x int) int { return acc + x }\n\treturn Reduce(numbers, add, 0)\n}\n\n// SumAllTails calculates the sums of all but the first number given a collection of slices.\nfunc SumAllTails(numbers ...[]int) []int {\n\tsumTail := func(acc, x []int) []int {\n\t\tif len(x) == 0 {\n\t\t\treturn append(acc, 0)\n\t\t} else {\n\t\t\ttail := x[1:]\n\t\t\treturn append(acc, Sum(tail))\n\t\t}\n\t}\n\n\treturn Reduce(numbers, sumTail, []int{})\n}\n```\n\n`Sum` and `SumAllTails` now describe the behaviour of their computations as the functions declared on their first lines respectively. The act of running the computation on the collection is abstracted away in `Reduce`.\n\n## Further applications of reduce\n\nUsing tests we can play around with our reduce function to see how re-usable it is. I have copied over our generic assertion functions from the previous chapter.\n\n```go\nfunc TestReduce(t *testing.T) {\n\tt.Run(\"multiplication of all elements\", func(t *testing.T) {\n\t\tmultiply := func(x, y int) int {\n\t\t\treturn x * y\n\t\t}\n\n\t\tAssertEqual(t, Reduce([]int{1, 2, 3}, multiply, 1), 6)\n\t})\n\n\tt.Run(\"concatenate strings\", func(t *testing.T) {\n\t\tconcatenate := func(x, y string) string {\n\t\t\treturn x + y\n\t\t}\n\n\t\tAssertEqual(t, Reduce([]string{\"a\", \"b\", \"c\"}, concatenate, \"\"), \"abc\")\n\t})\n}\n```\n\n### The zero value\n\nIn the multiplication example, we show the reason for having a default value as an argument to `Reduce`. If we relied on Go's default value of 0 for `int`, we'd multiply our initial value by 0, and then the following ones, so you'd only ever get 0. By setting it to 1, the first element in the slice will stay the same, and the rest will multiply by the next elements.\n\nIf you wish to sound clever with your nerd friends, you'd call this [The Identity Element](https://en.wikipedia.org/wiki/Identity_element).\n\n> In mathematics, an identity element, or neutral element, of a binary operation operating on a set is an element of the set which leaves unchanged every element of the set when the operation is applied.\n\nIn addition, the identity element is 0.\n\n`1 + 0 = 1`\n\nWith multiplication, it is 1.\n\n`1 * 1 = 1`\n\n## What if we wish to reduce into a different type from `A`?\n\nSuppose we had a list of transactions `Transaction` and we wanted a function that would take them, plus a name to figure out their bank balance.\n\nLet's follow the TDD process.\n\n## Write the test first\n\n```go\nfunc TestBadBank(t *testing.T) {\n\ttransactions := []Transaction{\n\t\t{\n\t\t\tFrom: \"Chris\",\n\t\t\tTo:   \"Riya\",\n\t\t\tSum:  100,\n\t\t},\n\t\t{\n\t\t\tFrom: \"Adil\",\n\t\t\tTo:   \"Chris\",\n\t\t\tSum:  25,\n\t\t},\n\t}\n\n\tAssertEqual(t, BalanceFor(transactions, \"Riya\"), 100)\n\tAssertEqual(t, BalanceFor(transactions, \"Chris\"), -75)\n\tAssertEqual(t, BalanceFor(transactions, \"Adil\"), -25)\n}\n```\n\n## Try to run the test\n```\n# github.com/quii/learn-go-with-tests/arrays/v8 [github.com/quii/learn-go-with-tests/arrays/v8.test]\n./bad_bank_test.go:6:20: undefined: Transaction\n./bad_bank_test.go:18:14: undefined: BalanceFor\n```\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\nWe don't have our types or functions yet, add them to make the test run.\n\n```go\ntype Transaction struct {\n\tFrom string\n\tTo   string\n\tSum  float64\n}\n\nfunc BalanceFor(transactions []Transaction, name string) float64 {\n\treturn 0.0\n}\n```\n\nWhen you run the test you should see the following:\n\n```\n=== RUN   TestBadBank\n    bad_bank_test.go:19: got 0, want 100\n    bad_bank_test.go:20: got 0, want -75\n    bad_bank_test.go:21: got 0, want -25\n--- FAIL: TestBadBank (0.00s)\n```\n\n## Write enough code to make it pass\n\nLet's write the code as if we didn't have a `Reduce` function first.\n\n```go\nfunc BalanceFor(transactions []Transaction, name string) float64 {\n\tvar balance float64\n\tfor _, t := range transactions {\n\t\tif t.From == name {\n\t\t\tbalance -= t.Sum\n\t\t}\n\t\tif t.To == name {\n\t\t\tbalance += t.Sum\n\t\t}\n\t}\n\treturn balance\n}\n```\n\n## Refactor\n\nAt this point, have some source control discipline and commit your work. We have working software, ready to challenge Monzo, Barclays, et al.\n\nNow our work is committed, we are free to play around with it, and try some different ideas out in the refactoring phase. To be fair, the code we have isn't exactly bad, but for the sake of this exercise, I want to demonstrate the same code using `Reduce`.\n\n```go\nfunc BalanceFor(transactions []Transaction, name string) float64 {\n\tadjustBalance := func(currentBalance float64, t Transaction) float64 {\n\t\tif t.From == name {\n\t\t\treturn currentBalance - t.Sum\n\t\t}\n\t\tif t.To == name {\n\t\t\treturn currentBalance + t.Sum\n\t\t}\n\t\treturn currentBalance\n\t}\n\treturn Reduce(transactions, adjustBalance, 0.0)\n}\n```\n\nBut this won't compile.\n\n```\n./bad_bank.go:19:35: type func(acc float64, t Transaction) float64 of adjustBalance does not match inferred type func(Transaction, Transaction) Transaction for func(A, A) A\n```\n\nThe reason is we're trying to reduce to a _different_ type than the type of the collection. This sounds scary, but actually just requires us to adjust the type signature of `Reduce` to make it work. We won't have to change the function body, and we won't have to change any of our existing callers.\n\n```go\nfunc Reduce[A, B any](collection []A, f func(B, A) B, initialValue B) B {\n\tvar result = initialValue\n\tfor _, x := range collection {\n\t\tresult = f(result, x)\n\t}\n\treturn result\n}\n```\n\nWe've added a second type constraint which has allowed us to loosen the constraints on `Reduce`. This allows people to `Reduce` from a collection of `A` into a `B`. In our case from `Transaction` to `float64`.\n\nThis makes `Reduce` more general-purpose and reusable, and still type-safe. If you try and run the tests again they should compile, and pass.\n\n## Extending the bank\n\nFor fun, I wanted to improve the ergonomics of the bank code. I've omitted the TDD process for brevity.\n\n```go\nfunc TestBadBank(t *testing.T) {\n\tvar (\n\t\triya  = Account{Name: \"Riya\", Balance: 100}\n\t\tchris = Account{Name: \"Chris\", Balance: 75}\n\t\tadil  = Account{Name: \"Adil\", Balance: 200}\n\n\t\ttransactions = []Transaction{\n\t\t\tNewTransaction(chris, riya, 100),\n\t\t\tNewTransaction(adil, chris, 25),\n\t\t}\n\t)\n\n\tnewBalanceFor := func(account Account) float64 {\n\t\treturn NewBalanceFor(account, transactions).Balance\n\t}\n\n\tAssertEqual(t, newBalanceFor(riya), 200)\n\tAssertEqual(t, newBalanceFor(chris), 0)\n\tAssertEqual(t, newBalanceFor(adil), 175)\n}\n```\n\nAnd here's the updated code\n\n```go\npackage main\n\ntype Transaction struct {\n\tFrom string\n\tTo   string\n\tSum  float64\n}\n\nfunc NewTransaction(from, to Account, sum float64) Transaction {\n\treturn Transaction{From: from.Name, To: to.Name, Sum: sum}\n}\n\ntype Account struct {\n\tName    string\n\tBalance float64\n}\n\nfunc NewBalanceFor(account Account, transactions []Transaction) Account {\n\treturn Reduce(\n\t\ttransactions,\n\t\tapplyTransaction,\n\t\taccount,\n\t)\n}\n\nfunc applyTransaction(a Account, transaction Transaction) Account {\n\tif transaction.From == a.Name {\n\t\ta.Balance -= transaction.Sum\n\t}\n\tif transaction.To == a.Name {\n\t\ta.Balance += transaction.Sum\n\t}\n\treturn a\n}\n```\n\nI feel this really shows the power of using concepts like `Reduce`. The `NewBalanceFor` feels more _declarative_, describing _what_ happens, rather than _how_. Often when we're reading code, we're darting through lots of files, and we're trying to understand _what_ is happening, rather than _how_, and this style of code facilitates this well.\n\nIf I wish to dig in to the detail I can, and I can see the _business logic_ of `applyTransaction` without worrying about loops and mutating state; `Reduce` takes care of that separately.\n\n\n### Fold/reduce are pretty universal\n\nThe possibilities are endless™️ with `Reduce` (or `Fold`). It's a common pattern for a reason, it's not just for arithmetic or string concatenation. Try a few other applications.\n\n- Why not mix some `color.RGBA` into a single colour?\n- Total up the number of votes in a poll, or items in a shopping basket.\n- More or less anything involving processing a list.\n\n## Find\n\nNow that Go has generics, combining them with higher-order-functions, we can reduce a lot of boilerplate code within our projects, to help our systems be easier to understand and manage.\n\nNo longer do you need to write specific `Find` functions for each type of collection you want to search, instead re-use or write a `Find` function. If you understood the `Reduce` function above, writing a `Find` function will be trivial.\n\nHere's a test\n\n```go\nfunc TestFind(t *testing.T) {\n\tt.Run(\"find first even number\", func(t *testing.T) {\n\t\tnumbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}\n\n\t\tfirstEvenNumber, found := Find(numbers, func(x int) bool {\n\t\t\treturn x%2 == 0\n\t\t})\n\t\tAssertTrue(t, found)\n\t\tAssertEqual(t, firstEvenNumber, 2)\n\t})\n}\n```\n\nAnd here's the implementation\n\n```go\nfunc Find[A any](items []A, predicate func(A) bool) (value A, found bool) {\n\tfor _, v := range items {\n\t\tif predicate(v) {\n\t\t\treturn v, true\n\t\t}\n\t}\n\treturn\n}\n```\n\nAgain, because it takes a generic type, we can re-use it in many ways\n\n```go\ntype Person struct {\n\tName string\n}\n\nt.Run(\"Find the best programmer\", func(t *testing.T) {\n\tpeople := []Person{\n\t\tPerson{Name: \"Kent Beck\"},\n\t\tPerson{Name: \"Martin Fowler\"},\n\t\tPerson{Name: \"Chris James\"},\n\t}\n\n\tking, found := Find(people, func(p Person) bool {\n\t\treturn strings.Contains(p.Name, \"Chris\")\n\t})\n\n\tAssertTrue(t, found)\n\tAssertEqual(t, king, Person{Name: \"Chris James\"})\n})\n```\n\nAs you can see, this code is flawless.\n\n## Wrapping up\n\nWhen done tastefully, higher-order functions like these will make your code simpler to read and maintain, but remember the rule of thumb:\n\nUse the TDD process to drive out real, specific behaviour that you actually need, in the refactoring stage you then _might_ discover some useful abstractions to help tidy the code up.\n\nPractice combining TDD with good source control habits. Commit your work when your test is passing, _before_ trying to refactor. This way if you make a mess, you can easily get yourself back to your working state.\n\n### Names matter\n\nMake an effort to do some research outside of Go, so you don't re-invent patterns that already exist with an already established name.\n\nWriting a function takes a collection of `A` and converts them to `B`? Don't call it `Convert`, that's [`Map`](https://en.wikipedia.org/wiki/Map_(higher-order_function)). Using the \"proper\" name for these items will reduce the cognitive burden for others and make it more search engine friendly to learn more.\n\n### This doesn't feel idiomatic?\n\nTry to have an open-mind.\n\nWhilst the idioms of Go won't, and shouldn't _radically_ change due to generics being released, the idioms _will_ change - due to the language changing! This should not be a controversial point.\n\nSaying\n\n> This is not idiomatic\n\nWithout any more detail, is not an actionable, or useful thing to say. Especially when discussing new language features.\n\nDiscuss with your colleagues patterns and style of code based on their merits rather than dogma. So long as you have well-designed tests, you'll always be able to refactor and shift things as you understand what works well for you, and your team.\n\n### Resources\n\nFold is a real fundamental in computer science. Here's some interesting resources if you wish to dig more into it\n- [Wikipedia: Fold](https://en.wikipedia.org/wiki/Fold)\n- [A tutorial on the universality and expressiveness of fold](http://www.cs.nott.ac.uk/~pszgmh/fold.pdf)\n"
  },
  {
    "path": "roman-numerals/v1/numeral_test.go",
    "content": "package v1\n\nimport \"testing\"\n\nfunc TestRomanNumerals(t *testing.T) {\n\tgot := ConvertToRoman(1)\n\twant := \"I\"\n\n\tif got != want {\n\t\tt.Errorf(\"got %q, want %q\", got, want)\n\t}\n}\n\nfunc ConvertToRoman(arabic int) string {\n\treturn \"I\"\n}\n"
  },
  {
    "path": "roman-numerals/v10/numeral_test.go",
    "content": "package v1\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n)\n\nvar (\n\tcases = []struct {\n\t\tArabic int\n\t\tRoman  string\n\t}{\n\t\t{Arabic: 1, Roman: \"I\"},\n\t\t{Arabic: 2, Roman: \"II\"},\n\t\t{Arabic: 3, Roman: \"III\"},\n\t\t{Arabic: 4, Roman: \"IV\"},\n\t\t{Arabic: 5, Roman: \"V\"},\n\t\t{Arabic: 6, Roman: \"VI\"},\n\t\t{Arabic: 7, Roman: \"VII\"},\n\t\t{Arabic: 8, Roman: \"VIII\"},\n\t\t{Arabic: 9, Roman: \"IX\"},\n\t\t{Arabic: 10, Roman: \"X\"},\n\t\t{Arabic: 14, Roman: \"XIV\"},\n\t\t{Arabic: 18, Roman: \"XVIII\"},\n\t\t{Arabic: 20, Roman: \"XX\"},\n\t\t{Arabic: 39, Roman: \"XXXIX\"},\n\t\t{Arabic: 40, Roman: \"XL\"},\n\t\t{Arabic: 47, Roman: \"XLVII\"},\n\t\t{Arabic: 49, Roman: \"XLIX\"},\n\t\t{Arabic: 50, Roman: \"L\"},\n\t\t{Arabic: 100, Roman: \"C\"},\n\t\t{Arabic: 90, Roman: \"XC\"},\n\t\t{Arabic: 400, Roman: \"CD\"},\n\t\t{Arabic: 500, Roman: \"D\"},\n\t\t{Arabic: 900, Roman: \"CM\"},\n\t\t{Arabic: 1000, Roman: \"M\"},\n\t\t{Arabic: 1984, Roman: \"MCMLXXXIV\"},\n\t\t{Arabic: 3999, Roman: \"MMMCMXCIX\"},\n\t\t{Arabic: 2014, Roman: \"MMXIV\"},\n\t\t{Arabic: 1006, Roman: \"MVI\"},\n\t\t{Arabic: 798, Roman: \"DCCXCVIII\"},\n\t}\n)\n\nfunc TestConvertingToRomanNumerals(t *testing.T) {\n\tfor _, test := range cases {\n\t\tt.Run(fmt.Sprintf(\"%d gets converted to '%s\", test.Arabic, test.Roman), func(t *testing.T) {\n\t\t\tgot := ConvertToRoman(test.Arabic)\n\t\t\tif got != test.Roman {\n\t\t\t\tt.Errorf(\"got %q, want %q\", got, test.Roman)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestConvertingToArabic(t *testing.T) {\n\tfor _, test := range cases {\n\t\tt.Run(fmt.Sprintf(\"%q gets converted to %d\", test.Roman, test.Arabic), func(t *testing.T) {\n\t\t\tgot := ConvertToArabic(test.Roman)\n\t\t\tif got != test.Arabic {\n\t\t\t\tt.Errorf(\"got %d, want %d\", got, test.Arabic)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "roman-numerals/v10/roman_numerals.go",
    "content": "package v1\n\nimport \"strings\"\n\n// ConvertToArabic converts a Roman Numeral to an Arabic number.\nfunc ConvertToArabic(roman string) int {\n\tarabic := 0\n\n\tfor _, numeral := range allRomanNumerals {\n\t\tfor strings.HasPrefix(roman, numeral.Symbol) {\n\t\t\tarabic += numeral.Value\n\t\t\troman = strings.TrimPrefix(roman, numeral.Symbol)\n\t\t}\n\t}\n\n\treturn arabic\n}\n\n// ConvertToRoman converts an Arabic number to a Roman Numeral.\nfunc ConvertToRoman(arabic int) string {\n\tvar result strings.Builder\n\n\tfor _, numeral := range allRomanNumerals {\n\t\tfor arabic >= numeral.Value {\n\t\t\tresult.WriteString(numeral.Symbol)\n\t\t\tarabic -= numeral.Value\n\t\t}\n\t}\n\n\treturn result.String()\n}\n\ntype romanNumeral struct {\n\tValue  int\n\tSymbol string\n}\n\nvar allRomanNumerals = []romanNumeral{\n\t{1000, \"M\"},\n\t{900, \"CM\"},\n\t{500, \"D\"},\n\t{400, \"CD\"},\n\t{100, \"C\"},\n\t{90, \"XC\"},\n\t{50, \"L\"},\n\t{40, \"XL\"},\n\t{10, \"X\"},\n\t{9, \"IX\"},\n\t{5, \"V\"},\n\t{4, \"IV\"},\n\t{1, \"I\"},\n}\n"
  },
  {
    "path": "roman-numerals/v11/numeral_test.go",
    "content": "package v1\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"testing/quick\"\n)\n\nvar (\n\tcases = []struct {\n\t\tArabic uint16\n\t\tRoman  string\n\t}{\n\t\t{Arabic: 1, Roman: \"I\"},\n\t\t{Arabic: 2, Roman: \"II\"},\n\t\t{Arabic: 3, Roman: \"III\"},\n\t\t{Arabic: 4, Roman: \"IV\"},\n\t\t{Arabic: 5, Roman: \"V\"},\n\t\t{Arabic: 6, Roman: \"VI\"},\n\t\t{Arabic: 7, Roman: \"VII\"},\n\t\t{Arabic: 8, Roman: \"VIII\"},\n\t\t{Arabic: 9, Roman: \"IX\"},\n\t\t{Arabic: 10, Roman: \"X\"},\n\t\t{Arabic: 14, Roman: \"XIV\"},\n\t\t{Arabic: 18, Roman: \"XVIII\"},\n\t\t{Arabic: 20, Roman: \"XX\"},\n\t\t{Arabic: 39, Roman: \"XXXIX\"},\n\t\t{Arabic: 40, Roman: \"XL\"},\n\t\t{Arabic: 47, Roman: \"XLVII\"},\n\t\t{Arabic: 49, Roman: \"XLIX\"},\n\t\t{Arabic: 50, Roman: \"L\"},\n\t\t{Arabic: 100, Roman: \"C\"},\n\t\t{Arabic: 90, Roman: \"XC\"},\n\t\t{Arabic: 400, Roman: \"CD\"},\n\t\t{Arabic: 500, Roman: \"D\"},\n\t\t{Arabic: 900, Roman: \"CM\"},\n\t\t{Arabic: 1000, Roman: \"M\"},\n\t\t{Arabic: 1984, Roman: \"MCMLXXXIV\"},\n\t\t{Arabic: 3999, Roman: \"MMMCMXCIX\"},\n\t\t{Arabic: 2014, Roman: \"MMXIV\"},\n\t\t{Arabic: 1006, Roman: \"MVI\"},\n\t\t{Arabic: 798, Roman: \"DCCXCVIII\"},\n\t}\n)\n\nfunc TestConvertingToRomanNumerals(t *testing.T) {\n\tfor _, test := range cases {\n\t\tt.Run(fmt.Sprintf(\"%d gets converted to '%s\", test.Arabic, test.Roman), func(t *testing.T) {\n\t\t\tgot := ConvertToRoman(test.Arabic)\n\t\t\tif got != test.Roman {\n\t\t\t\tt.Errorf(\"got %q, want %q\", got, test.Roman)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestConvertingToArabic(t *testing.T) {\n\tfor _, test := range cases {\n\t\tt.Run(fmt.Sprintf(\"%q gets converted to %d\", test.Roman, test.Arabic), func(t *testing.T) {\n\t\t\tgot := ConvertToArabic(test.Roman)\n\t\t\tif got != test.Arabic {\n\t\t\t\tt.Errorf(\"got %d, want %d\", got, test.Arabic)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestPropertiesOfConversion(t *testing.T) {\n\tassertion := func(arabic uint16) bool {\n\t\tif arabic > 3999 {\n\t\t\treturn true\n\t\t}\n\t\tt.Log(\"testing\", arabic)\n\t\troman := ConvertToRoman(arabic)\n\t\tfromRoman := ConvertToArabic(roman)\n\t\treturn fromRoman == arabic\n\t}\n\n\tif err := quick.Check(assertion, &quick.Config{\n\t\tMaxCount: 1000,\n\t}); err != nil {\n\t\tt.Error(\"failed checks\", err)\n\t}\n}\n"
  },
  {
    "path": "roman-numerals/v11/roman_numerals.go",
    "content": "package v1\n\nimport \"strings\"\n\n// ConvertToArabic converts a Roman Numeral to an Arabic number.\nfunc ConvertToArabic(roman string) uint16 {\n\tvar arabic uint16 = 0\n\n\tfor _, numeral := range allRomanNumerals {\n\t\tfor strings.HasPrefix(roman, numeral.Symbol) {\n\t\t\tarabic += numeral.Value\n\t\t\troman = strings.TrimPrefix(roman, numeral.Symbol)\n\t\t}\n\t}\n\n\treturn arabic\n}\n\n// ConvertToRoman converts an Arabic number to a Roman Numeral.\nfunc ConvertToRoman(arabic uint16) string {\n\tvar result strings.Builder\n\n\tfor _, numeral := range allRomanNumerals {\n\t\tfor arabic >= numeral.Value {\n\t\t\tresult.WriteString(numeral.Symbol)\n\t\t\tarabic -= numeral.Value\n\t\t}\n\t}\n\n\treturn result.String()\n}\n\ntype romanNumeral struct {\n\tValue  uint16\n\tSymbol string\n}\n\nvar allRomanNumerals = []romanNumeral{\n\t{1000, \"M\"},\n\t{900, \"CM\"},\n\t{500, \"D\"},\n\t{400, \"CD\"},\n\t{100, \"C\"},\n\t{90, \"XC\"},\n\t{50, \"L\"},\n\t{40, \"XL\"},\n\t{10, \"X\"},\n\t{9, \"IX\"},\n\t{5, \"V\"},\n\t{4, \"IV\"},\n\t{1, \"I\"},\n}\n"
  },
  {
    "path": "roman-numerals/v2/numeral_test.go",
    "content": "package v1\n\nimport \"testing\"\n\nfunc TestRomanNumerals(t *testing.T) {\n\tcases := []struct {\n\t\tDescription string\n\t\tArabic      int\n\t\tWant        string\n\t}{\n\t\t{\"1 gets converted to I\", 1, \"I\"},\n\t\t{\"2 gets converted to II\", 2, \"II\"},\n\t}\n\n\tfor _, test := range cases {\n\t\tt.Run(test.Description, func(t *testing.T) {\n\t\t\tgot := ConvertToRoman(test.Arabic)\n\t\t\tif got != test.Want {\n\t\t\t\tt.Errorf(\"got %q, want %q\", got, test.Want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc ConvertToRoman(arabic int) string {\n\tif arabic == 2 {\n\t\treturn \"II\"\n\t}\n\treturn \"I\"\n}\n"
  },
  {
    "path": "roman-numerals/v3/numeral_test.go",
    "content": "package v1\n\nimport (\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestRomanNumerals(t *testing.T) {\n\tcases := []struct {\n\t\tDescription string\n\t\tArabic      int\n\t\tWant        string\n\t}{\n\t\t{\"1 gets converted to I\", 1, \"I\"},\n\t\t{\"2 gets converted to II\", 2, \"II\"},\n\t\t{\"3 gets converted to III\", 3, \"III\"},\n\t}\n\n\tfor _, test := range cases {\n\t\tt.Run(test.Description, func(t *testing.T) {\n\t\t\tgot := ConvertToRoman(test.Arabic)\n\t\t\tif got != test.Want {\n\t\t\t\tt.Errorf(\"got %q, want %q\", got, test.Want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc ConvertToRoman(arabic int) string {\n\n\tvar result strings.Builder\n\n\tfor i := 0; i < arabic; i++ {\n\t\tresult.WriteString(\"I\")\n\t}\n\n\treturn result.String()\n}\n"
  },
  {
    "path": "roman-numerals/v4/numeral_test.go",
    "content": "package v1\n\nimport (\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestRomanNumerals(t *testing.T) {\n\tcases := []struct {\n\t\tDescription string\n\t\tArabic      int\n\t\tWant        string\n\t}{\n\t\t{\"1 gets converted to I\", 1, \"I\"},\n\t\t{\"2 gets converted to II\", 2, \"II\"},\n\t\t{\"3 gets converted to III\", 3, \"III\"},\n\t\t{\"4 gets converted to IV (can't repeat more than 3 times)\", 4, \"IV\"},\n\t}\n\n\tfor _, test := range cases {\n\t\tt.Run(test.Description, func(t *testing.T) {\n\t\t\tgot := ConvertToRoman(test.Arabic)\n\t\t\tif got != test.Want {\n\t\t\t\tt.Errorf(\"got %q, want %q\", got, test.Want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc ConvertToRoman(arabic int) string {\n\n\tvar result strings.Builder\n\n\tfor i := arabic; i > 0; i-- {\n\t\tif i == 4 {\n\t\t\tresult.WriteString(\"IV\")\n\t\t\tbreak\n\t\t}\n\t\tresult.WriteString(\"I\")\n\t}\n\n\treturn result.String()\n}\n"
  },
  {
    "path": "roman-numerals/v5/numeral_test.go",
    "content": "package v1\n\nimport (\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestRomanNumerals(t *testing.T) {\n\tcases := []struct {\n\t\tDescription string\n\t\tArabic      int\n\t\tWant        string\n\t}{\n\t\t{\"1 gets converted to I\", 1, \"I\"},\n\t\t{\"2 gets converted to II\", 2, \"II\"},\n\t\t{\"3 gets converted to III\", 3, \"III\"},\n\t\t{\"4 gets converted to IV (can't repeat more than 3 times)\", 4, \"IV\"},\n\t\t{\"5 gets converted to V\", 5, \"V\"},\n\t\t{\"6 gets converted to VI\", 6, \"VI\"},\n\t\t{\"7 gets converted to VII\", 7, \"VII\"},\n\t}\n\n\tfor _, test := range cases {\n\t\tt.Run(test.Description, func(t *testing.T) {\n\t\t\tgot := ConvertToRoman(test.Arabic)\n\t\t\tif got != test.Want {\n\t\t\t\tt.Errorf(\"got %q, want %q\", got, test.Want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc ConvertToRoman(arabic int) string {\n\n\tvar result strings.Builder\n\n\tfor arabic > 0 {\n\t\tswitch {\n\t\tcase arabic > 4:\n\t\t\tresult.WriteString(\"V\")\n\t\t\tarabic -= 5\n\t\tcase arabic > 3:\n\t\t\tresult.WriteString(\"IV\")\n\t\t\tarabic -= 4\n\t\tdefault:\n\t\t\tresult.WriteString(\"I\")\n\t\t\tarabic--\n\t\t}\n\t}\n\n\treturn result.String()\n}\n"
  },
  {
    "path": "roman-numerals/v6/numeral_test.go",
    "content": "package v1\n\nimport (\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestRomanNumerals(t *testing.T) {\n\tcases := []struct {\n\t\tDescription string\n\t\tArabic      int\n\t\tWant        string\n\t}{\n\t\t{\"1 gets converted to I\", 1, \"I\"},\n\t\t{\"2 gets converted to II\", 2, \"II\"},\n\t\t{\"3 gets converted to III\", 3, \"III\"},\n\t\t{\"4 gets converted to IV (can't repeat more than 3 times)\", 4, \"IV\"},\n\t\t{\"5 gets converted to V\", 5, \"V\"},\n\t\t{\"6 gets converted to VI\", 6, \"VI\"},\n\t\t{\"7 gets converted to VII\", 7, \"VII\"},\n\t\t{\"8 gets converted to VIII\", 8, \"VIII\"},\n\t}\n\n\tfor _, test := range cases {\n\t\tt.Run(test.Description, func(t *testing.T) {\n\t\t\tgot := ConvertToRoman(test.Arabic)\n\t\t\tif got != test.Want {\n\t\t\t\tt.Errorf(\"got %q, want %q\", got, test.Want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc ConvertToRoman(arabic int) string {\n\n\tvar result strings.Builder\n\n\tfor arabic > 0 {\n\t\tswitch {\n\t\tcase arabic > 4:\n\t\t\tresult.WriteString(\"V\")\n\t\t\tarabic -= 5\n\t\tcase arabic > 3:\n\t\t\tresult.WriteString(\"IV\")\n\t\t\tarabic -= 4\n\t\tdefault:\n\t\t\tresult.WriteString(\"I\")\n\t\t\tarabic--\n\t\t}\n\t}\n\n\treturn result.String()\n}\n"
  },
  {
    "path": "roman-numerals/v7/numeral_test.go",
    "content": "package v1\n\nimport (\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestRomanNumerals(t *testing.T) {\n\tcases := []struct {\n\t\tDescription string\n\t\tArabic      int\n\t\tWant        string\n\t}{\n\t\t{\"1 gets converted to I\", 1, \"I\"},\n\t\t{\"2 gets converted to II\", 2, \"II\"},\n\t\t{\"3 gets converted to III\", 3, \"III\"},\n\t\t{\"4 gets converted to IV (can't repeat more than 3 times)\", 4, \"IV\"},\n\t\t{\"5 gets converted to V\", 5, \"V\"},\n\t\t{\"6 gets converted to VI\", 6, \"VI\"},\n\t\t{\"7 gets converted to VII\", 7, \"VII\"},\n\t\t{\"8 gets converted to VIII\", 8, \"VIII\"},\n\t\t{\"9 gets converted to IX\", 9, \"IX\"},\n\t\t{\"10 gets converted to X\", 10, \"X\"},\n\t\t{\"14 gets converted to XIV\", 14, \"XIV\"},\n\t\t{\"18 gets converted to XVIII\", 18, \"XVIII\"},\n\t\t{\"20 gets converted to XX\", 20, \"XX\"},\n\t\t{\"39 gets converted to XXXIX\", 39, \"XXXIX\"},\n\t}\n\n\tfor _, test := range cases {\n\t\tt.Run(test.Description, func(t *testing.T) {\n\t\t\tgot := ConvertToRoman(test.Arabic)\n\t\t\tif got != test.Want {\n\t\t\t\tt.Errorf(\"got %q, want %q\", got, test.Want)\n\t\t\t}\n\t\t})\n\t}\n}\n\ntype RomanNumeral struct {\n\tValue  int\n\tSymbol string\n}\n\nvar RomanNumerals = []RomanNumeral{\n\t{10, \"X\"},\n\t{9, \"IX\"},\n\t{5, \"V\"},\n\t{4, \"IV\"},\n\t{1, \"I\"},\n}\n\nfunc ConvertToRoman(arabic int) string {\n\n\tvar result strings.Builder\n\n\tfor _, numeral := range RomanNumerals {\n\t\tfor arabic >= numeral.Value {\n\t\t\tresult.WriteString(numeral.Symbol)\n\t\t\tarabic -= numeral.Value\n\t\t}\n\t}\n\n\treturn result.String()\n}\n"
  },
  {
    "path": "roman-numerals/v8/numeral_test.go",
    "content": "package v1\n\nimport (\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestRomanNumerals(t *testing.T) {\n\tcases := []struct {\n\t\tDescription string\n\t\tArabic      int\n\t\tWant        string\n\t}{\n\t\t{\"1 gets converted to I\", 1, \"I\"},\n\t\t{\"2 gets converted to II\", 2, \"II\"},\n\t\t{\"3 gets converted to III\", 3, \"III\"},\n\t\t{\"4 gets converted to IV (can't repeat more than 3 times)\", 4, \"IV\"},\n\t\t{\"5 gets converted to V\", 5, \"V\"},\n\t\t{\"6 gets converted to VI\", 6, \"VI\"},\n\t\t{\"7 gets converted to VII\", 7, \"VII\"},\n\t\t{\"8 gets converted to VIII\", 8, \"VIII\"},\n\t\t{\"9 gets converted to IX\", 9, \"IX\"},\n\t\t{\"10 gets converted to X\", 10, \"X\"},\n\t\t{\"14 gets converted to XIV\", 14, \"XIV\"},\n\t\t{\"18 gets converted to XVIII\", 18, \"XVIII\"},\n\t\t{\"20 gets converted to XX\", 20, \"XX\"},\n\t\t{\"39 gets converted to XXXIX\", 39, \"XXXIX\"},\n\t\t{\"40 gets converted to XL\", 40, \"XL\"},\n\t\t{\"47 gets converted to XLVII\", 47, \"XLVII\"},\n\t\t{\"49 gets converted to XLIX\", 49, \"XLIX\"},\n\t\t{\"50 gets converted to L\", 50, \"L\"},\n\t}\n\n\tfor _, test := range cases {\n\t\tt.Run(test.Description, func(t *testing.T) {\n\t\t\tgot := ConvertToRoman(test.Arabic)\n\t\t\tif got != test.Want {\n\t\t\t\tt.Errorf(\"got %q, want %q\", got, test.Want)\n\t\t\t}\n\t\t})\n\t}\n}\n\ntype RomanNumeral struct {\n\tValue  int\n\tSymbol string\n}\n\nvar RomanNumerals = []RomanNumeral{\n\t{50, \"L\"},\n\t{40, \"XL\"},\n\t{10, \"X\"},\n\t{9, \"IX\"},\n\t{5, \"V\"},\n\t{4, \"IV\"},\n\t{1, \"I\"},\n}\n\nfunc ConvertToRoman(arabic int) string {\n\n\tvar result strings.Builder\n\n\tfor _, numeral := range RomanNumerals {\n\t\tfor arabic >= numeral.Value {\n\t\t\tresult.WriteString(numeral.Symbol)\n\t\t\tarabic -= numeral.Value\n\t\t}\n\t}\n\n\treturn result.String()\n}\n"
  },
  {
    "path": "roman-numerals/v9/numeral_test.go",
    "content": "package v1\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestRomanNumerals(t *testing.T) {\n\tcases := []struct {\n\t\tArabic int\n\t\tRoman  string\n\t}{\n\t\t{Arabic: 1, Roman: \"I\"},\n\t\t{Arabic: 2, Roman: \"II\"},\n\t\t{Arabic: 3, Roman: \"III\"},\n\t\t{Arabic: 4, Roman: \"IV\"},\n\t\t{Arabic: 5, Roman: \"V\"},\n\t\t{Arabic: 6, Roman: \"VI\"},\n\t\t{Arabic: 7, Roman: \"VII\"},\n\t\t{Arabic: 8, Roman: \"VIII\"},\n\t\t{Arabic: 9, Roman: \"IX\"},\n\t\t{Arabic: 10, Roman: \"X\"},\n\t\t{Arabic: 14, Roman: \"XIV\"},\n\t\t{Arabic: 18, Roman: \"XVIII\"},\n\t\t{Arabic: 20, Roman: \"XX\"},\n\t\t{Arabic: 39, Roman: \"XXXIX\"},\n\t\t{Arabic: 40, Roman: \"XL\"},\n\t\t{Arabic: 47, Roman: \"XLVII\"},\n\t\t{Arabic: 49, Roman: \"XLIX\"},\n\t\t{Arabic: 50, Roman: \"L\"},\n\t\t{Arabic: 100, Roman: \"C\"},\n\t\t{Arabic: 90, Roman: \"XC\"},\n\t\t{Arabic: 400, Roman: \"CD\"},\n\t\t{Arabic: 500, Roman: \"D\"},\n\t\t{Arabic: 900, Roman: \"CM\"},\n\t\t{Arabic: 1000, Roman: \"M\"},\n\t\t{Arabic: 1984, Roman: \"MCMLXXXIV\"},\n\t\t{Arabic: 3999, Roman: \"MMMCMXCIX\"},\n\t\t{Arabic: 2014, Roman: \"MMXIV\"},\n\t\t{Arabic: 1006, Roman: \"MVI\"},\n\t\t{Arabic: 798, Roman: \"DCCXCVIII\"},\n\t}\n\tfor _, test := range cases {\n\t\tt.Run(fmt.Sprintf(\"%d gets converted to '%s\", test.Arabic, test.Roman), func(t *testing.T) {\n\t\t\tgot := ConvertToRoman(test.Arabic)\n\t\t\tif got != test.Roman {\n\t\t\t\tt.Errorf(\"got %q, want %q\", got, test.Roman)\n\t\t\t}\n\t\t})\n\t}\n}\n\ntype RomanNumeral struct {\n\tValue  int\n\tSymbol string\n}\n\nvar RomanNumerals = []RomanNumeral{\n\t{1000, \"M\"},\n\t{900, \"CM\"},\n\t{500, \"D\"},\n\t{400, \"CD\"},\n\t{100, \"C\"},\n\t{90, \"XC\"},\n\t{50, \"L\"},\n\t{40, \"XL\"},\n\t{10, \"X\"},\n\t{9, \"IX\"},\n\t{5, \"V\"},\n\t{4, \"IV\"},\n\t{1, \"I\"},\n}\n\nfunc ConvertToRoman(arabic int) string {\n\n\tvar result strings.Builder\n\n\tfor _, numeral := range RomanNumerals {\n\t\tfor arabic >= numeral.Value {\n\t\t\tresult.WriteString(numeral.Symbol)\n\t\t\tarabic -= numeral.Value\n\t\t}\n\t}\n\n\treturn result.String()\n}\n"
  },
  {
    "path": "roman-numerals.md",
    "content": "# Roman Numerals\n\n**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/main/roman-numerals)**\n\nSome companies will ask you to do the [Roman Numeral Kata](http://codingdojo.org/kata/RomanNumerals/) as part of the interview process. This chapter will show how you can tackle it with TDD.\n\nWe are going to write a function which converts an [Arabic number](https://en.wikipedia.org/wiki/Arabic_numerals) (numbers 0 to 9) to a Roman Numeral.\n\nIf you haven't heard of [Roman Numerals](https://en.wikipedia.org/wiki/Roman_numerals) they are how the Romans wrote down numbers.\n\nYou build them by sticking symbols together and those symbols represent numbers\n\nSo `I` is \"one\". `III` is three.\n\nSeems easy but there's a few interesting rules. `V` means five, but `IV` is 4 (not `IIII`).\n\n`MCMLXXXIV` is 1984. That looks complicated and it's hard to imagine how we can write code to figure this out right from the start.\n\nAs this book stresses, a key skill for software developers is to try and identify \"thin vertical slices\" of _useful_ functionality and then **iterating**. The TDD workflow helps facilitate iterative development.\n\nSo rather than 1984, let's start with 1.\n\n## Write the test first\n\n```go\nfunc TestRomanNumerals(t *testing.T) {\n\tgot := ConvertToRoman(1)\n\twant := \"I\"\n\n\tif got != want {\n\t\tt.Errorf(\"got %q, want %q\", got, want)\n\t}\n}\n```\n\nIf you've got this far in the book this is hopefully feeling very boring and routine to you. That's a good thing.\n\n## Try to run the test\n\n```console\n./numeral_test.go:6:9: undefined: ConvertToRoman\n```\n\nLet the compiler guide the way\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\nCreate our function but don't make the test pass yet, always make sure the tests fails how you expect\n\n```go\nfunc ConvertToRoman(arabic int) string {\n\treturn \"\"\n}\n```\n\nIt should run now\n\n```console\n=== RUN   TestRomanNumerals\n--- FAIL: TestRomanNumerals (0.00s)\n    numeral_test.go:10: got '', want 'I'\nFAIL\n```\n\n## Write enough code to make it pass\n\n```go\nfunc ConvertToRoman(arabic int) string {\n\treturn \"I\"\n}\n```\n\n## Refactor\n\nNot much to refactor yet.\n\n_I know_ it feels weird just to hard-code the result but with TDD we want to stay out of \"red\" for as long as possible. It may _feel_ like we haven't accomplished much but we've defined our API and got a test capturing one of our rules; even if the \"real\" code is pretty dumb.\n\nNow use that uneasy feeling to write a new test to force us to write slightly less dumb code.\n\n## Write the test first\n\nWe can use subtests to nicely group our tests\n\n```go\nfunc TestRomanNumerals(t *testing.T) {\n\tt.Run(\"1 gets converted to I\", func(t *testing.T) {\n\t\tgot := ConvertToRoman(1)\n\t\twant := \"I\"\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %q, want %q\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"2 gets converted to II\", func(t *testing.T) {\n\t\tgot := ConvertToRoman(2)\n\t\twant := \"II\"\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %q, want %q\", got, want)\n\t\t}\n\t})\n}\n```\n\n## Try to run the test\n\n```console\n=== RUN   TestRomanNumerals/2_gets_converted_to_II\n    --- FAIL: TestRomanNumerals/2_gets_converted_to_II (0.00s)\n        numeral_test.go:20: got 'I', want 'II'\n```\n\nNot much surprise there\n\n## Write enough code to make it pass\n\n```go\nfunc ConvertToRoman(arabic int) string {\n\tif arabic == 2 {\n\t\treturn \"II\"\n\t}\n\treturn \"I\"\n}\n```\n\nYup, it still feels like we're not actually tackling the problem. So we need to write more tests to drive us forward.\n\n## Refactor\n\nWe have some repetition in our tests. When you're testing something which feels like it's a matter of \"given input X, we expect Y\" you should probably use table based tests.\n\n```go\nfunc TestRomanNumerals(t *testing.T) {\n\tcases := []struct {\n\t\tDescription string\n\t\tArabic      int\n\t\tWant        string\n\t}{\n\t\t{\"1 gets converted to I\", 1, \"I\"},\n\t\t{\"2 gets converted to II\", 2, \"II\"},\n\t}\n\n\tfor _, test := range cases {\n\t\tt.Run(test.Description, func(t *testing.T) {\n\t\t\tgot := ConvertToRoman(test.Arabic)\n\t\t\tif got != test.Want {\n\t\t\t\tt.Errorf(\"got %q, want %q\", got, test.Want)\n\t\t\t}\n\t\t})\n\t}\n}\n```\n\nWe can now easily add more cases without having to write any more test boilerplate.\n\nLet's push on and go for 3\n\n## Write the test first\n\nAdd the following to our cases\n\n```\n{\"3 gets converted to III\", 3, \"III\"},\n```\n\n## Try to run the test\n\n```console\n=== RUN   TestRomanNumerals/3_gets_converted_to_III\n    --- FAIL: TestRomanNumerals/3_gets_converted_to_III (0.00s)\n        numeral_test.go:20: got 'I', want 'III'\n```\n\n## Write enough code to make it pass\n\n```go\nfunc ConvertToRoman(arabic int) string {\n\tif arabic == 3 {\n\t\treturn \"III\"\n\t}\n\tif arabic == 2 {\n\t\treturn \"II\"\n\t}\n\treturn \"I\"\n}\n```\n\n## Refactor\n\nOK so I'm starting to not enjoy these if statements and if you look at the code hard enough you can see that we're building a string of `I` based on the size of `arabic`.\n\nWe \"know\" that for more complicated numbers we will be doing some kind of arithmetic and string concatenation.\n\nLet's try a refactor with these thoughts in mind, it _might not_ be suitable for the end solution but that's OK. We can always throw our code away and start afresh with the tests we have to guide us.\n\n```go\nfunc ConvertToRoman(arabic int) string {\n\n\tvar result strings.Builder\n\n\tfor i := 0; i < arabic; i++ {\n\t\tresult.WriteString(\"I\")\n\t}\n\n\treturn result.String()\n}\n```\n\nYou might remember [`strings.Builder`](https://golang.org/pkg/strings/#Builder) from our discussion\nabout [benchmarking](iteration.md#benchmarking)\n\n> A Builder is used to efficiently build a string using Write methods. It minimizes memory copying.\n\nNormally I wouldn't bother with such optimisations until I have an actual performance problem but the amount of code is not much larger than a \"manual\" appending on a string so we may as well use the faster approach.\n\nThe code looks better to me and describes the domain _as we know it right now_.\n\n### The Romans were into DRY too...\n\nThings start getting more complicated now. The Romans in their wisdom thought repeating characters would become hard to read and count. So a rule with Roman Numerals is you can't have the same character repeated more than 3 times in a row.\n\nInstead you take the next highest symbol and then \"subtract\" by putting a symbol to the left of it. Not all symbols can be used as subtractors; only I (1), X (10) and C (100).\n\nFor example `5` in Roman Numerals is `V`. To create 4 you do not do `IIII`, instead you do `IV`.\n\n## Write the test first\n\n```\n{\"4 gets converted to IV (can't repeat more than 3 times)\", 4, \"IV\"},\n```\n\n## Try to run the test\n\n```console\n=== RUN   TestRomanNumerals/4_gets_converted_to_IV_(cant_repeat_more_than_3_times)\n    --- FAIL: TestRomanNumerals/4_gets_converted_to_IV_(cant_repeat_more_than_3_times) (0.00s)\n        numeral_test.go:24: got 'IIII', want 'IV'\n```\n\n## Write enough code to make it pass\n\n```go\nfunc ConvertToRoman(arabic int) string {\n\n\tif arabic == 4 {\n\t\treturn \"IV\"\n\t}\n\n\tvar result strings.Builder\n\n\tfor i := 0; i < arabic; i++ {\n\t\tresult.WriteString(\"I\")\n\t}\n\n\treturn result.String()\n}\n```\n\n## Refactor\n\nI don't \"like\" that we have broken our string building pattern and I want to carry on with it.\n\n```go\nfunc ConvertToRoman(arabic int) string {\n\n\tvar result strings.Builder\n\n\tfor i := arabic; i > 0; i-- {\n\t\tif i == 4 {\n\t\t\tresult.WriteString(\"IV\")\n\t\t\tbreak\n\t\t}\n\t\tresult.WriteString(\"I\")\n\t}\n\n\treturn result.String()\n}\n```\n\nIn order for 4 to \"fit\" with my current thinking I now count down from the Arabic number, adding symbols to our string as we progress. Not sure if this will work in the long run but let's see!\n\nLet's make 5 work\n\n## Write the test first\n\n```\n{\"5 gets converted to V\", 5, \"V\"},\n```\n\n## Try to run the test\n\n```console\n=== RUN   TestRomanNumerals/5_gets_converted_to_V\n    --- FAIL: TestRomanNumerals/5_gets_converted_to_V (0.00s)\n        numeral_test.go:25: got 'IIV', want 'V'\n```\n\n## Write enough code to make it pass\n\nJust copy the approach we did for 4\n\n```go\nfunc ConvertToRoman(arabic int) string {\n\n\tvar result strings.Builder\n\n\tfor i := arabic; i > 0; i-- {\n\t\tif i == 5 {\n\t\t\tresult.WriteString(\"V\")\n\t\t\tbreak\n\t\t}\n\t\tif i == 4 {\n\t\t\tresult.WriteString(\"IV\")\n\t\t\tbreak\n\t\t}\n\t\tresult.WriteString(\"I\")\n\t}\n\n\treturn result.String()\n}\n```\n\n## Refactor\n\nRepetition in loops like this are usually a sign of an abstraction waiting to be called out. Short-circuiting loops can be an effective tool for readability but it could also be telling you something else.\n\nWe are looping over our Arabic number and if we hit certain symbols we are calling `break` but what we are _really_ doing is subtracting over `i` in a ham-fisted manner.\n\n```go\nfunc ConvertToRoman(arabic int) string {\n\n\tvar result strings.Builder\n\n\tfor arabic > 0 {\n\t\tswitch {\n\t\tcase arabic > 4:\n\t\t\tresult.WriteString(\"V\")\n\t\t\tarabic -= 5\n\t\tcase arabic > 3:\n\t\t\tresult.WriteString(\"IV\")\n\t\t\tarabic -= 4\n\t\tdefault:\n\t\t\tresult.WriteString(\"I\")\n\t\t\tarabic--\n\t\t}\n\t}\n\n\treturn result.String()\n}\n```\n\n- Given the signals I'm reading from our code, driven from our tests of some very basic scenarios I can see that to build a Roman Numeral I need to subtract from `arabic` as I apply symbols\n- The `for` loop no longer relies on an `i` and instead we will keep building our string until we have subtracted enough symbols away from `arabic`.\n\nI'm pretty sure this approach will be valid for 6 (VI), 7 (VII) and 8 (VIII) too. Nonetheless add the cases in to our test suite and check (I won't include the code for brevity, check the github for samples if you're unsure).\n\n9 follows the same rule as 4 in that we should subtract `I` from the representation of the following number. 10 is represented in Roman Numerals with `X`; so therefore 9 should be `IX`.\n\n## Write the test first\n\n```\n{\"9 gets converted to IX\", 9, \"IX\"},\n```\n## Try to run the test\n\n```console\n=== RUN   TestRomanNumerals/9_gets_converted_to_IX\n    --- FAIL: TestRomanNumerals/9_gets_converted_to_IX (0.00s)\n        numeral_test.go:29: got 'VIV', want 'IX'\n```\n\n## Write enough code to make it pass\n\nWe should be able to adopt the same approach as before\n\n```\ncase arabic > 8:\n    result.WriteString(\"IX\")\n    arabic -= 9\n```\n\n## Refactor\n\nIt _feels_ like the code is still telling us there's a refactor somewhere but it's not totally obvious to me, so let's keep going.\n\nI'll skip the code for this too, but add to your test cases a test for `10` which should be `X` and make it pass before reading on.\n\nHere are a few tests I added as I'm confident up to 39 our code should work\n\n```\n{\"10 gets converted to X\", 10, \"X\"},\n{\"14 gets converted to XIV\", 14, \"XIV\"},\n{\"18 gets converted to XVIII\", 18, \"XVIII\"},\n{\"20 gets converted to XX\", 20, \"XX\"},\n{\"39 gets converted to XXXIX\", 39, \"XXXIX\"},\n```\n\nIf you've ever done OO programming, you'll know that you should view `switch` statements with a bit of suspicion. Usually you are capturing a concept or data inside some imperative code when in fact it could be captured in a class structure instead.\n\nGo isn't strictly OO but that doesn't mean we ignore the lessons OO offers entirely (as much as some would like to tell you).\n\nOur switch statement is describing some truths about Roman Numerals along with behaviour.\n\nWe can refactor this by decoupling the data from the behaviour.\n\n```go\ntype RomanNumeral struct {\n\tValue  int\n\tSymbol string\n}\n\nvar allRomanNumerals = []RomanNumeral{\n\t{10, \"X\"},\n\t{9, \"IX\"},\n\t{5, \"V\"},\n\t{4, \"IV\"},\n\t{1, \"I\"},\n}\n\nfunc ConvertToRoman(arabic int) string {\n\n\tvar result strings.Builder\n\n\tfor _, numeral := range allRomanNumerals {\n\t\tfor arabic >= numeral.Value {\n\t\t\tresult.WriteString(numeral.Symbol)\n\t\t\tarabic -= numeral.Value\n\t\t}\n\t}\n\n\treturn result.String()\n}\n```\n\nThis feels much better. We've declared some rules around the numerals as data rather than hidden in an algorithm and we can see how we just work through the Arabic number, trying to add symbols to our result if they fit.\n\nDoes this abstraction work for bigger numbers? Extend the test suite so it works for the Roman number for 50 which is `L`.\n\nHere are some test cases, try and make them pass.\n\n```\n{\"40 gets converted to XL\", 40, \"XL\"},\n{\"47 gets converted to XLVII\", 47, \"XLVII\"},\n{\"49 gets converted to XLIX\", 49, \"XLIX\"},\n{\"50 gets converted to L\", 50, \"L\"},\n```\n\nNeed help? You can see what symbols to add in [this gist](https://gist.github.com/pamelafox/6c7b948213ba55332d86efd0f0b037de).\n\n## And the rest!\n\nHere are the remaining symbols\n\n| Arabic | Roman |\n| ------ | :---: |\n| 100    |   C   |\n| 500    |   D   |\n| 1000   |   M   |\n\nTake the same approach for the remaining symbols, it should just be a matter of adding data to both the tests and our array of symbols.\n\nDoes your code work for `1984`: `MCMLXXXIV` ?\n\nHere is my final test suite\n\n```go\nfunc TestRomanNumerals(t *testing.T) {\n\tcases := []struct {\n\t\tArabic int\n\t\tRoman  string\n\t}{\n\t\t{Arabic: 1, Roman: \"I\"},\n\t\t{Arabic: 2, Roman: \"II\"},\n\t\t{Arabic: 3, Roman: \"III\"},\n\t\t{Arabic: 4, Roman: \"IV\"},\n\t\t{Arabic: 5, Roman: \"V\"},\n\t\t{Arabic: 6, Roman: \"VI\"},\n\t\t{Arabic: 7, Roman: \"VII\"},\n\t\t{Arabic: 8, Roman: \"VIII\"},\n\t\t{Arabic: 9, Roman: \"IX\"},\n\t\t{Arabic: 10, Roman: \"X\"},\n\t\t{Arabic: 14, Roman: \"XIV\"},\n\t\t{Arabic: 18, Roman: \"XVIII\"},\n\t\t{Arabic: 20, Roman: \"XX\"},\n\t\t{Arabic: 39, Roman: \"XXXIX\"},\n\t\t{Arabic: 40, Roman: \"XL\"},\n\t\t{Arabic: 47, Roman: \"XLVII\"},\n\t\t{Arabic: 49, Roman: \"XLIX\"},\n\t\t{Arabic: 50, Roman: \"L\"},\n\t\t{Arabic: 100, Roman: \"C\"},\n\t\t{Arabic: 90, Roman: \"XC\"},\n\t\t{Arabic: 400, Roman: \"CD\"},\n\t\t{Arabic: 500, Roman: \"D\"},\n\t\t{Arabic: 900, Roman: \"CM\"},\n\t\t{Arabic: 1000, Roman: \"M\"},\n\t\t{Arabic: 1984, Roman: \"MCMLXXXIV\"},\n\t\t{Arabic: 3999, Roman: \"MMMCMXCIX\"},\n\t\t{Arabic: 2014, Roman: \"MMXIV\"},\n\t\t{Arabic: 1006, Roman: \"MVI\"},\n\t\t{Arabic: 798, Roman: \"DCCXCVIII\"},\n\t}\n\tfor _, test := range cases {\n\t\tt.Run(fmt.Sprintf(\"%d gets converted to %q\", test.Arabic, test.Roman), func(t *testing.T) {\n\t\t\tgot := ConvertToRoman(test.Arabic)\n\t\t\tif got != test.Roman {\n\t\t\t\tt.Errorf(\"got %q, want %q\", got, test.Roman)\n\t\t\t}\n\t\t})\n\t}\n}\n```\n\n- I removed `description` as I felt the _data_ described enough of the information.\n- I added a few other edge cases I found just to give me a little more confidence. With table based tests this is very cheap to do.\n\nI didn't change the algorithm, all I had to do was update the `allRomanNumerals` array.\n\n```go\nvar allRomanNumerals = []RomanNumeral{\n\t{1000, \"M\"},\n\t{900, \"CM\"},\n\t{500, \"D\"},\n\t{400, \"CD\"},\n\t{100, \"C\"},\n\t{90, \"XC\"},\n\t{50, \"L\"},\n\t{40, \"XL\"},\n\t{10, \"X\"},\n\t{9, \"IX\"},\n\t{5, \"V\"},\n\t{4, \"IV\"},\n\t{1, \"I\"},\n}\n```\n\n## Parsing Roman Numerals\n\nWe're not done yet. Next we're going to write a function that converts _from_ a Roman Numeral to an `int`\n\n## Write the test first\n\nWe can re-use our test cases here with a little refactoring\n\nMove the `cases` variable outside of the test as a package variable in a `var` block.\n\n```go\nfunc TestConvertingToArabic(t *testing.T) {\n\tfor _, test := range cases[:1] {\n\t\tt.Run(fmt.Sprintf(\"%q gets converted to %d\", test.Roman, test.Arabic), func(t *testing.T) {\n\t\t\tgot := ConvertToArabic(test.Roman)\n\t\t\tif got != test.Arabic {\n\t\t\t\tt.Errorf(\"got %d, want %d\", got, test.Arabic)\n\t\t\t}\n\t\t})\n\t}\n}\n```\n\nNotice I am using the slice functionality to just run one of the tests for now (`cases[:1]`) as trying to make all of those tests pass all at once is too big a leap\n\n## Try to run the test\n\n```console\n./numeral_test.go:60:11: undefined: ConvertToArabic\n```\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\nAdd our new function definition\n\n```go\nfunc ConvertToArabic(roman string) int {\n\treturn 0\n}\n```\n\nThe test should now run and fail\n\n```console\n--- FAIL: TestConvertingToArabic (0.00s)\n    --- FAIL: TestConvertingToArabic/'I'_gets_converted_to_1 (0.00s)\n        numeral_test.go:62: got 0, want 1\n```\n\n## Write enough code to make it pass\n\nYou know what to do\n\n```go\nfunc ConvertToArabic(roman string) int {\n\treturn 1\n}\n```\n\nNext, change the slice index in our test to move to the next test case (e.g. `cases[:2]`). Make it pass yourself with the dumbest code you can think of, continue writing dumb code (best book ever right?) for the third case too. Here's my dumb code.\n\n```go\nfunc ConvertToArabic(roman string) int {\n\tif roman == \"III\" {\n\t\treturn 3\n\t}\n\tif roman == \"II\" {\n\t\treturn 2\n\t}\n\treturn 1\n}\n```\n\nThrough the dumbness of _real code that works_ we can start to see a pattern like before. We need to iterate through the input and build _something_, in this case a total.\n\n```go\nfunc ConvertToArabic(roman string) int {\n\ttotal := 0\n\tfor range roman {\n\t\ttotal++\n\t}\n\treturn total\n}\n```\n\n## Write the test first\n\nNext we move to `cases[:4]` (`IV`) which now fails because it gets 2 back as that's the length of the string.\n\n## Write enough code to make it pass\n\n```go\n// earlier..\nvar allRomanNumerals = RomanNumerals{\n\t{1000, \"M\"},\n\t{900, \"CM\"},\n\t{500, \"D\"},\n\t{400, \"CD\"},\n\t{100, \"C\"},\n\t{90, \"XC\"},\n\t{50, \"L\"},\n\t{40, \"XL\"},\n\t{10, \"X\"},\n\t{9, \"IX\"},\n\t{5, \"V\"},\n\t{4, \"IV\"},\n\t{1, \"I\"},\n}\n\n// later..\nfunc ConvertToArabic(roman string) int {\n\tvar arabic = 0\n\n\tfor _, numeral := range allRomanNumerals {\n\t\tfor strings.HasPrefix(roman, numeral.Symbol) {\n\t\t\tarabic += numeral.Value\n\t\t\troman = strings.TrimPrefix(roman, numeral.Symbol)\n\t\t}\n\t}\n\n\treturn arabic\n}\n```\n\nIt is basically the algorithm of `ConvertToRoman(int)` implemented backwards. Here, we loop over the given roman numeral string:\n- We look for roman numeral symbols taken from `allRomanNumerals`, highest to lowest, at the beginning of the string.\n- If we find the prefix, we add its value to  `arabic` and trim the prefix.\n\nAt the end, we return the sum as the arabic number.\n\nThe `HasPrefix(s, prefix)` checks whether string `s` starts with `prefix` and `TrimPrefix(s, prefix)` removes the `prefix` from `s`, so we can proceed with the remaining roman numeral symbols. It works with `IV` and all other test cases.\n\nYou can implement this as a recursive function, which is more elegant (in my opinion) but might be slower. I'll leave this up to you and some `Benchmark...` tests.\n\nNow that we have our functions to convert an arabic number into a roman numeral and back, we can take our tests a step further:\n\n## An intro to property based tests\n\nThere have been a few rules in the domain of Roman Numerals that we have worked with in this chapter\n\n- Can't have more than 3 consecutive symbols\n- Only I (1), X (10) and C (100) can be \"subtractors\"\n- Taking the result of `ConvertToRoman(N)` and passing it to `ConvertToArabic` should return us `N`\n\nThe tests we have written so far can be described as \"example\" based tests where we provide _examples_ for the tooling to verify.\n\nWhat if we could take these rules that we know about our domain and somehow exercise them against our code?\n\nProperty based tests help you do this by throwing random data at your code and verifying the rules you describe always hold true. A lot of people think property based tests are mainly about random data but they would be mistaken. The real challenge about property based tests is having a _good_ understanding of your domain so you can write these properties.\n\nEnough words, let's see some code\n\n> **⚠️ Linux Users:** Please **DO NOT** run the test below immediately. It will likely freeze your system (requiring a hard reboot).\n>\n> <details>\n> <summary>Click here to see why (Technical Explanation)</summary>\n>\n> The `testing/quick` package generates random integers up to `int64` max. Our current naive implementation attempts to build a string of that length in memory (quadrillions of characters).\n>\n> While macOS and Windows often handle this gracefully (UI remains responsive), Linux kernels usually encounter \"swap thrashing,\" causing the entire system to freeze before the process can be killed.\n> </details>\n\n```go\nfunc TestPropertiesOfConversion(t *testing.T) {\n\tassertion := func(arabic int) bool {\n\t\troman := ConvertToRoman(arabic)\n\t\tfromRoman := ConvertToArabic(roman)\n\t\treturn fromRoman == arabic\n\t}\n\n\tif err := quick.Check(assertion, nil); err != nil {\n\t\tt.Error(\"failed checks\", err)\n\t}\n}\n```\n\n### Rationale of property\n\nOur first test will check that if we transform a number into Roman, when we use our other function to convert it back to a number that we get what we originally had.\n\n- Given random number (e.g `4`).\n- Call `ConvertToRoman` with random number (should return `IV` if `4`).\n- Take the result of above and pass it to `ConvertToArabic`.\n- The above should give us our original input (`4`).\n\nThis feels like a good test to build us confidence because it should break if there's a bug in either. The only way it could pass is if they have the same kind of bug; which isn't impossible but feels unlikely.\n\n### Technical explanation\n\n We're using the [testing/quick](https://golang.org/pkg/testing/quick/) package from the standard library\n\n Reading from the bottom, we provide `quick.Check` a function that it will run against a number of random inputs, if the function returns `false` it will be seen as failing the check.\n\n Our `assertion` function above takes a random number and runs our functions to test the property.\n\n### Run our test\n\n Try running it; your computer may hang for a while, so kill it when you're bored :)\n\n What's going on? Try adding the following to the assertion code.\n\n ```go\nassertion := func(arabic int) bool {\n\tif arabic < 0 || arabic > 3999 {\n\t\tlog.Println(arabic)\n\t\treturn true\n\t}\n\troman := ConvertToRoman(arabic)\n\tfromRoman := ConvertToArabic(roman)\n\treturn fromRoman == arabic\n}\n```\n\nYou should see something like this:\n\n```console\n=== RUN   TestPropertiesOfConversion\n2019/07/09 14:41:27 6849766357708982977\n2019/07/09 14:41:27 -7028152357875163913\n2019/07/09 14:41:27 -6752532134903680693\n2019/07/09 14:41:27 4051793897228170080\n2019/07/09 14:41:27 -1111868396280600429\n2019/07/09 14:41:27 8851967058300421387\n2019/07/09 14:41:27 562755830018219185\n```\n\nJust running this very simple property has exposed a flaw in our implementation. We used `int` as our input but:\n- You can't do negative numbers with Roman Numerals\n- Given our rule of a max of 3 consecutive symbols we can't represent a value greater than 3999 ([well, kinda](https://www.quora.com/Which-is-the-maximum-number-in-Roman-numerals)) and `int` has a much higher maximum value than 3999.\n\nThis is great! We've been forced to think more deeply about our domain which is a real strength of property based tests.\n\nClearly `int` is not a great type. What if we tried something a little more appropriate?\n\n### [`uint16`](https://golang.org/pkg/builtin/#uint16)\n\nGo has types for _unsigned integers_, which means they cannot be negative; so that rules out one class of bug in our code immediately. By adding 16, it means it is a 16 bit integer which can store a max of `65535`, which is still too big but gets us closer to what we need.\n\nTry updating the code to use `uint16` rather than `int`. I updated `assertion` in the test to give a bit more visibility.\n\n```go\nassertion := func(arabic uint16) bool {\n\tif arabic > 3999 {\n\t\treturn true\n\t}\n\tt.Log(\"testing\", arabic)\n\troman := ConvertToRoman(arabic)\n\tfromRoman := ConvertToArabic(roman)\n\treturn fromRoman == arabic\n}\n```\nNotice that now we are logging the input using the `log` method from the testing framework. Make sure you run the `go test` command with the flag `-v` to print the additional output (`go test -v`).\n\nIf you run the test they now actually run and you can see what is being tested. You can run multiple times to see our code stands up well to the various values! This gives me a lot of confidence that our code is working how we want.\n\nThe default number of runs `quick.Check` performs is 100 but you can change that with a config.\n\n```go\nif err := quick.Check(assertion, &quick.Config{\n\tMaxCount: 1000,\n}); err != nil {\n\tt.Error(\"failed checks\", err)\n}\n```\n\n### Further work\n\n- Can you write property tests that check the other properties we described?\n- Can you think of a way of making it so it's impossible for someone to call our code with a number greater than 3999?\n    - You could return an error\n    - Or create a new type that cannot represent > 3999\n        - What do you think is best?\n\n## Wrapping up\n\n### More TDD practice with iterative development\n\nDid the thought of writing code that converts 1984 into MCMLXXXIV feel intimidating to you at first? It did to me and I've been writing software for quite a long time.\n\nThe trick, as always, is to **get started with something simple** and take **small steps**.\n\nAt no point in this process did we make any large leaps, do any huge refactorings, or get in a mess.\n\nI can hear someone cynically saying \"this is just a kata\". I can't argue with that, but I still take this same approach for every project I work on. I never ship a big distributed system in my first step, I find the simplest thing the team could ship (usually a \"Hello world\" website) and then iterate on small bits of functionality in manageable chunks, just like how we did here.\n\nThe skill is knowing _how_ to split work up, and that comes with practice and with some lovely TDD to help you on your way.\n\n### Property based tests\n\n- Built into the standard library\n- If you can think of ways to describe your domain rules in code, they are an excellent tool for giving you more confidence\n- Force you to think about your domain deeply\n- Potentially a nice complement to your test suite\n\n## Postscript\n\nThis book is reliant on valuable feedback from the community.\n[Dave](http://github.com/gypsydave5) is an enormous help in practically every\nchapter. But he had a real rant about my use of 'Arabic numerals' in this\nchapter so, in the interests of full disclosure, here's what he said.\n\n> Just going to write up why a value of type `int` isn't really an 'arabic\n> numeral'. This might be me being way too precise so I'll completely understand\n> if you tell me to f off.\n>\n> A _digit_ is a character used in the representation of numbers - from the Latin\n> for 'finger', as we usually have ten of them. In the Arabic (also called\n> Hindu-Arabic) number system there are ten of them. These Arabic digits are:\n>\n> ```console\n>   0 1 2 3 4 5 6 7 8 9\n> ```\n>\n> A _numeral_ is the representation of a number using a collection of digits.\n> An Arabic numeral is a number represented by Arabic digits in a base 10\n> positional number system. We say 'positional' because each digit has\n> a different value based upon its position in the numeral. So\n>\n> ```console\n>   1337\n> ```\n>\n> The `1` has a value of one thousand because its the first digit in a four\n> digit numeral.\n>\n> Roman are built using a reduced number of digits (`I`, `V` etc...) mainly as\n> values to produce the numeral. There's a bit of positional stuff but it's\n> mostly `I` always representing 'one'.\n>\n> So, given this, is `int` an 'Arabic number'? The idea of a number is not at\n> all tied to its representation - we can see this if we ask ourselves what the\n> correct representation of this number is:\n>\n> ```console\n> 255\n> 11111111\n> two-hundred and fifty-five\n> FF\n> 377\n> ```\n>\n> Yes, this is a trick question. They're all correct. They're the representation\n> of the same number in the decimal,  binary, English, hexadecimal and octal\n> number systems respectively.\n>\n> The representation of a number as a numeral is _independent_ of its properties\n> as a number - and we can see this when we look at integer literals in Go:\n>\n> ```go\n> \t0xFF == 255 // true\n> ```\n>\n> And how we can print integers in a format string:\n>\n> ```go\n> n := 255\n> fmt.Printf(\"%b %c %d %o %q %x %X %U\", n, n, n, n, n, n, n, n)\n> // 11111111 ÿ 255 377 'ÿ' ff FF U+00FF\n> ```\n>\n> We can write the same integer both as a hexadecimal and an Arabic (decimal)\n> numeral.\n>\n> So when the function signature looks like `ConvertToRoman(arabic int) string`\n> it's making a bit of an assumption about how it's being called. Because\n> sometimes `arabic` will be written as a decimal integer literal\n>\n> ```go\n> \tConvertToRoman(255)\n> ```\n>\n> But it could just as well be written\n>\n> ```go\n> \tConvertToRoman(0xFF)\n> ```\n>\n> Really, we're not 'converting' from an Arabic numeral at all, we're 'printing'  -\n> representing - an `int` as a Roman numeral - and `int`s are not numerals,\n> Arabic or otherwise; they're just numbers. The `ConvertToRoman` function is\n> more like `strconv.Itoa` in that it's turning an `int` into a `string`.\n>\n> But every other version of the kata doesn't care about this distinction so\n> :shrug:\n"
  },
  {
    "path": "scaling-acceptance-tests.md",
    "content": "# Learn Go with Tests - Scaling Acceptance Tests (and light intro to gRPC)\n\nThis chapter is a follow-up to [Intro to acceptance tests](https://quii.gitbook.io/learn-go-with-tests/testing-fundamentals/intro-to-acceptance-tests). You can find [the finished code for this chapter on GitHub](https://github.com/quii/go-specs-greet).\n\nAcceptance tests are essential, and they directly impact your ability to confidently evolve your system over time, with a reasonable cost of change.\n\nThey're also a fantastic tool to help you work with legacy code. When faced with a poor codebase without any tests, please resist the temptation to start refactoring. Instead, write some acceptance tests to give you a safety net to freely change the system's internals without affecting its functional external behaviour. ATs need not be concerned with internal quality, so they're a great fit in these situations.\n\nAfter reading this, you'll appreciate that acceptance tests are useful for verification and can also be used in the development process by helping us change our system more deliberately and methodically, reducing wasted effort.\n\n## Prerequisite material\n\nThe inspiration for this chapter is borne of many years of frustration with acceptance tests. Two videos I would recommend you watch are:\n\n- Dave Farley - [How to write acceptance tests](https://www.youtube.com/watch?v=JDD5EEJgpHU)\n- Nat Pryce - [E2E functional tests that can run in milliseconds](https://www.youtube.com/watch?v=Fk4rCn4YLLU)\n\n\"Growing Object Oriented Software\" (GOOS) is such an important book for many software engineers, including myself. The approach it prescribes is the one I coach engineers I work with to follow.\n\n- [GOOS](http://www.growing-object-oriented-software.com) - Nat Pryce & Steve Freeman\n\nFinally, [Riya Dattani](https://twitter.com/dattaniriya) and I spoke about this topic in the context of BDD in our talk, [Acceptance tests, BDD and Go](https://www.youtube.com/watch?v=ZMWJCk_0WrY).\n\n## Recap\n\nWe're talking about \"black-box\" tests that verify your system behaves as expected from the outside, from a \"**business perspective**\". The tests do not have access to the innards of the system it tests; they're only concerned with **what** your system does rather than **how**.\n\n## Anatomy of bad acceptance tests\n\nOver many years, I've worked for several companies and teams. Each of them recognised the need for acceptance tests; some way to test a system from a user's point of view and to verify it works how it's intended, but almost without exception, the cost of these tests became a real problem for the team.\n\n- Slow to run\n- Brittle\n- Flaky\n- Expensive to maintain, and seem to make changing the software harder than it ought to be\n- Can only run in a particular environment, causing slow and poor feedback loops\n\nLet's say you intend to write an acceptance test around a website you're building. You decide to use a headless web browser (like [Selenium](https://www.selenium.dev)) to simulate a user clicking buttons on your website to verify it does what it needs to do.\n\nOver time, your website's markup has to change as new features are discovered, and engineers bike-shed over whether something should be an `<article>` or a `<section>` for the billionth time.\n\nEven though your team are only making minor changes to the system, barely noticeable to the actual user, you find yourself wasting lots of time updating your ATs.\n\n### Tight-coupling\n\nThink about what prompts acceptance tests to change:\n\n- An external behaviour change. If you want to change what the system does, changing the acceptance test suite seems reasonable, if not desirable.\n- An implementation detail change / refactoring. Ideally, this shouldn't prompt a change, or if it does, a minor one.\n\nToo often, though, the latter is the reason acceptance tests have to change. To the point where engineers even become reluctant to change their system because of the perceived effort of updating tests!\n\n![Riya and myself talking about separating concerns in our tests](https://i.imgur.com/bbG6z57.png)\n\nThese problems stem from not applying well-established and practised engineering habits written by the authors mentioned above. **You can't write acceptance tests like unit tests**; they require more thought and different practices.\n\n## Anatomy of good acceptance tests\n\nIf we want acceptance tests that only change when we change behaviour and not implementation detail, it stands to reason that we need to separate those concerns.\n\n### On types of complexity\n\nAs software engineers, we have to deal with two kinds of complexity.\n\n- **Accidental complexity** is the complexity we have to deal with because we're working with computers, stuff like networks, disks, APIs, etc.\n\n- **Essential complexity** is sometimes referred to as \"domain logic\". It's the particular rules and truths within your domain.\n  - For example, \"if an account owner withdraws more money than is available, they are overdrawn\". This statement says nothing about computers; this statement was true before computers were even used in banks!\n\nEssential complexity should be expressible to a non-technical person, and it's valuable to have modelled it in our \"domain\" code, and in our acceptance tests.\n\n### Separation of concerns\n\nWhat Dave Farley proposed in the video earlier, and what Riya and I also discussed, is we should have the idea of **specifications**. Specifications describe the behaviour of the system we want without being coupled with accidental complexity or implementation detail.\n\nThis idea should feel reasonable to you. In production code, we frequently strive to separate concerns and decouple units of work. Would you not hesitate to introduce an `interface` to allow your `HTTP` handler to decouple it from non-HTTP concerns? Let's take this same line of thinking for our acceptance tests.\n\nDave Farley describes a specific structure.\n\n![Dave Farley on Acceptance Tests](https://i.imgur.com/nPwpihG.png)\n\nAt GopherconUK, Riya and I put this in Go terms.\n\n![Separation of concerns](https://i.imgur.com/qdY4RJe.png)\n\n### Testing on steroids\n\nDecoupling how the specification is executed allows us to reuse it in different scenarios. We can:\n\n#### Make our drivers configurable\n\nThis means you can run your ATs locally, in your staging and (ideally) production environments.\n- Too many teams engineer their systems such that acceptance tests are impossible to run locally. This introduces an intolerably slow feedback loop. Wouldn't you rather be confident your ATs will pass _before_ integrating your code? If the tests start breaking, is it acceptable that you'd be unable to reproduce the failure locally and instead, have to commit changes and cross your fingers that it'll pass 20 minutes later in a different environment?\n- Remember, just because your tests pass in staging doesn't mean your system will work. Dev/Prod parity is, at best, a white lie. [I test in prod](https://increment.com/testing/i-test-in-production/).\n- There are always differences between the environments that can affect the *behaviour* of your system. A CDN could have some cache headers incorrectly set; a downstream service you depend on may behave differently; a configuration value may be incorrect. But wouldn't it be nice if you could run your specifications in prod to catch these problems quickly?\n\n#### Plug in _different_ drivers to test other parts of your system\n\nThis flexibility allows us to test behaviours at different abstraction and architectural layers, which allows us to have more focused tests beyond black-box tests.\n- For instance, you may have a web page with an API behind it. Why not use the same specification to test both? You can use a headless web browser for the web page, and HTTP calls for the API.\n- Taking this idea further, ideally, we want the **code to model essential complexity** (as \"domain\" code) so we should also be able to use our specifications for unit tests. This will give swift feedback that the essential complexity in our system is modelled and behaves correctly.\n\n\n### Acceptance tests changing for the right reasons\n\nWith this approach, the only reason for your specifications to change is if the behaviour of the system changes, which is reasonable.\n\n- If your HTTP API has to change, you have one obvious place to update it, the driver.\n- If your markup changes, again, update the specific driver.\n\nAs your system grows, you'll find yourself reusing drivers for multiple tests, which again means if implementation detail changes, you only have to update one, usually obvious place.\n\nWhen done right, this approach gives us flexibility in our implementation detail and stability in our specifications. Importantly, it provides a simple and obvious structure for managing change, which becomes essential as a system and its team grows.\n\n### Acceptance tests as a method for software development\n\nIn our talk, Riya and I discussed acceptance tests and their relation to BDD. We talked about how starting your work by trying to _understand the problem you're trying to solve_ and expressing it as a specification helps focus your intent and is a great way to start your work.\n\nI was first introduced to this way of working in GOOS. A while ago, I summarised the ideas on my blog. Here is an extract from my post [Why TDD](https://quii.dev/The_Why_of_TDD)\n\n---\n\nTDD is focused on letting you design for the behaviour you precisely need, iteratively. When starting a new area, you must identify a key, necessary behaviour and aggressively cut scope.\n\nFollow a \"top-down\" approach, starting with an acceptance test (AT) that exercises the behaviour from the outside. This will act as a north-star for your efforts. All you should be focused on is making that test pass. This test will likely be failing for a while whilst you develop enough code to make it pass.\n\n![](https://i.imgur.com/pxTaYu4.png)\n\nOnce your AT is set up, you can break into the TDD process to drive out enough units to make the AT pass. The trick is to not worry too much about design at this point; get enough code to make the AT pass because you're still learning and exploring the problem.\n\nTaking this first step is often more extensive than you think, setting up web servers, routing, configuration, etc., which is why keeping the scope of the work small is essential. We want to make that first positive step on our blank canvas and have it backed by a passing AT so we can continue to iterate quickly and safely.\n\n![](https://i.imgur.com/t5y5opw.png)\n\nAs you develop, listen to your tests, and they should give you signals to help you push your design in a better direction but, again, anchored to the behaviour rather than our imagination.\n\nTypically, your first \"unit\" that does the hard work to make the AT pass will grow too big to be comfortable, even for this small amount of behaviour. This is when you can start thinking about how to break the problem down and introduce new collaborators.\n\n![](https://i.imgur.com/UYqd7Cq.png)\n\nThis is where test doubles (e.g. fakes, mocks) are handy because most of the complexity that lives internally within software doesn't usually reside in implementation detail but \"between\" the units and how they interact.\n\n#### The perils of bottom-up\n\nThis is a \"top-down\" approach rather than a \"bottom-up\". Bottom-up has its uses, but it carries an element of risk. By building \"services\" and code without it being integrated into your application quickly and without verifying a high-level test, **you risk wasting lots of effort on unvalidated ideas**.\n\nThis is a crucial property of the acceptance-test-driven approach, using tests to get real validation of our code.\n\nToo many times, I've encountered engineers who have made a chunk of code, in isolation, bottom-up, they think is going to solve a job, but it:\n\n- Doesn't work how we want to\n- Does stuff we don't need\n- Doesn't integrate easily\n- Requires a ton of re-writing anyway\n\nThis is waste.\n\n## Enough talk, time to code\n\nUnlike other chapters, you'll need [Docker](https://www.docker.com) installed because we'll be running our applications in containers. It's assumed at this point in the book you're comfortable writing Go code, importing from different packages, etc.\n\nCreate a new project with `go mod init github.com/quii/go-specs-greet` (you can put whatever you like here but if you change the path you will need to change all internal imports to match)\n\nMake a folder `specifications` to hold our specifications, and add a file `greet.go`\n\n```go\npackage specifications\n\nimport (\n\t\"testing\"\n\n\t\"github.com/alecthomas/assert/v2\"\n)\n\ntype Greeter interface {\n\tGreet() (string, error)\n}\n\nfunc GreetSpecification(t testing.TB, greeter Greeter) {\n\tgot, err := greeter.Greet()\n\tassert.NoError(t, err)\n\tassert.Equal(t, got, \"Hello, world\")\n}\n```\n\nMy IDE (Goland) takes care of the fuss of adding dependencies for me, but if you need to do it manually, you'd do\n\n`go get github.com/alecthomas/assert/v2`\n\nGiven Farley's acceptance test design (Specification->DSL->Driver->System), we now have a decoupled specification from implementation. It doesn't know or care about _how_ we `Greet`; it's just concerned with the essential complexity of our domain. Admittedly this complexity isn't much right now, but we'll expand upon the spec to add more functionality as we further iterate. It's always important to start small!\n\nYou could view the interface as our first step of a DSL; as the project grows, you may find the need to abstract differently, but for now, this is fine.\n\nAt this point, this level of ceremony to decouple our specification from implementation might make some people accuse us of \"overly abstracting\". **I promise you that acceptance tests that are too coupled to implementation become a real burden on engineering teams**. I am confident that most acceptance tests out in the wild are expensive to maintain due to this inappropriate coupling; rather than the reverse of being overly abstract.\n\nWe can use this specification to verify any \"system\" that can `Greet`.\n\n### First system: HTTP API\n\nWe require to provide a \"greeter service\" over HTTP. So we'll need to create:\n\n1. A **driver**. In this case, one works with an HTTP system by using an **HTTP client**. This code will know how to work with our API. Drivers translate DSLs into system-specific calls; in our case, the driver will implement the interface specifications define.\n2. An **HTTP server** with a greet API\n3. A **test**, which is responsible for managing the life-cycle of spinning up the server and then plugging the driver into the specification to run it as a test\n\n## Write the test first\n\nThe initial process for creating a black-box test that compiles and runs your program, executes the test and then cleans everything up can be quite labour intensive. That's why it's preferable to do it at the start of your project with minimal functionality. I typically start all my projects with a \"hello world\" server implementation, with all of my tests set up and ready for me to build the actual functionality quickly.\n\nThe mental model of \"specifications\", \"drivers\", and \"acceptance tests\" can take a little time to get used to, so follow carefully. It can be helpful to \"work backwards\" by trying to call the specification first.\n\nCreate some structure to house the program we intend to ship.\n\n`mkdir -p cmd/httpserver`\n\nInside the new folder, create a new file `greeter_server_test.go`, and add the following.\n\n```go\npackage main_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/quii/go-specs-greet/specifications\"\n)\n\nfunc TestGreeterServer(t *testing.T) {\n\tspecifications.GreetSpecification(t, nil)\n}\n```\n\nWe wish to run our specification in a Go test. We already have access to a `*testing.T`, so that's the first argument, but what about the second?\n\n`specifications.Greeter` is an interface, which we will implement with a `Driver` by changing the new TestGreeterServer code to the following:\n\n```go\nimport (\n\tgo_specs_greet \"github.com/quii/go-specs-greet\"\n)\n\nfunc TestGreeterServer(t *testing.T) {\n\tdriver := go_specs_greet.Driver{BaseURL: \"http://localhost:8080\"}\n\tspecifications.GreetSpecification(t, driver)\n}\n```\n\nIt would be favourable for our `Driver` to be configurable to run it against different environments, including locally, so we have added a `BaseURL` field.\n\n## Try to run the test\n\n```\n./greeter_server_test.go:46:12: undefined: go_specs_greet.Driver\n```\n\nWe're still practising TDD here! It's a big first step we have to make; we need to make a few files and write maybe more code than we're typically used to, but when you're first starting, this is often the case. It's so important we try to remember the red step's rules.\n\n> Commit as many sins as necessary to get the test passing\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\nHold your nose; remember, we can refactor when the test has passed. Here's the code for the driver in `driver.go` which we will place in the project root:\n\n```go\npackage go_specs_greet\n\nimport (\n\t\"io\"\n\t\"net/http\"\n)\n\ntype Driver struct {\n\tBaseURL string\n}\n\nfunc (d Driver) Greet() (string, error) {\n\tres, err := http.Get(d.BaseURL + \"/greet\")\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tdefer res.Body.Close()\n\tgreeting, err := io.ReadAll(res.Body)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn string(greeting), nil\n}\n```\n\n\nNotes:\n\n- You could argue that I should be writing tests to drive out the various `if err != nil`, but in my experience, so long as you're not doing anything with the `err`, tests that say \"you return the error you get\" are relatively low value.\n- **You shouldn't use the default HTTP client**. Later we'll pass in an HTTP client to configure it with timeouts etc., but for now, we're just trying to get ourselves to a passing test.\n-  In our `greeter_server_test.go` we called the Driver function from `go_specs_greet` package which we have now created, don't forget to add `github.com/quii/go-specs-greet` to its imports.\nTry and rerun the tests; they should now compile but not pass.\n\n```\nGet \"http://localhost:8080/greet\": dial tcp [::1]:8080: connect: connection refused\n```\n\nWe have a `Driver`, but we have not started our application yet, so it cannot do an HTTP request. We need our acceptance test to coordinate building, running and finally killing our system for the test to run.\n\n### Running our application\n\nIt's common for teams to build Docker images of their systems to deploy, so for our test we'll do the same\n\nTo help us use Docker in our tests, we will use [Testcontainers](https://golang.testcontainers.org). Testcontainers gives us a programmatic way to build Docker images and manage container life-cycles.\n\n`go get github.com/testcontainers/testcontainers-go`\n\nNow you can edit `cmd/httpserver/greeter_server_test.go` to read as follows:\n\n```go\npackage main_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/alecthomas/assert/v2\"\n\tgo_specs_greet \"github.com/quii/go-specs-greet\"\n\t\"github.com/quii/go-specs-greet/specifications\"\n\t\"github.com/testcontainers/testcontainers-go\"\n\t\"github.com/testcontainers/testcontainers-go/wait\"\n)\n\nfunc TestGreeterServer(t *testing.T) {\n\tctx := context.Background()\n\n\treq := testcontainers.ContainerRequest{\n\t\tFromDockerfile: testcontainers.FromDockerfile{\n\t\t\tContext:    \"../../.\",\n\t\t\tDockerfile: \"./cmd/httpserver/Dockerfile\",\n\t\t\t// set to false if you want less spam, but this is helpful if you're having troubles\n\t\t\tPrintBuildLog: true,\n\t\t},\n\t\tExposedPorts: []string{\"8080:8080\"},\n\t\tWaitingFor:   wait.ForHTTP(\"/\").WithPort(\"8080\"),\n\t}\n\tcontainer, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{\n\t\tContainerRequest: req,\n\t\tStarted:          true,\n\t})\n\tassert.NoError(t, err)\n\tt.Cleanup(func() {\n\t\tassert.NoError(t, container.Terminate(ctx))\n\t})\n\n\tdriver := go_specs_greet.Driver{BaseURL: \"http://localhost:8080\"}\n\tspecifications.GreetSpecification(t, driver)\n}\n```\n\nTry and run the test.\n\n```\n=== RUN   TestGreeterHandler\n2022/09/10 18:49:44 Starting container id: 03e8588a1be4 image: docker.io/testcontainers/ryuk:0.3.3\n2022/09/10 18:49:45 Waiting for container id 03e8588a1be4 image: docker.io/testcontainers/ryuk:0.3.3\n2022/09/10 18:49:45 Container is ready id: 03e8588a1be4 image: docker.io/testcontainers/ryuk:0.3.3\n    greeter_server_test.go:32: Did not expect an error but got:\n        Error response from daemon: Cannot locate specified Dockerfile: ./cmd/httpserver/Dockerfile: failed to create container\n--- FAIL: TestGreeterHandler (0.59s)\n```\n\nWe need to create a Dockerfile for our program. Inside our `httpserver` folder, create a `Dockerfile` and add the following.\n\n```dockerfile\n# Make sure to specify the same Go version as the one in the go.mod file.\n# For example, golang:1.22.1-alpine.\nFROM golang:1.18-alpine\n\nWORKDIR /app\n\nCOPY go.mod ./\n\nRUN go mod download\n\nCOPY . .\n\nRUN go build -o svr cmd/httpserver/*.go\n\nEXPOSE 8080\nCMD [ \"./svr\" ]\n```\n\nDon't worry too much about the details here; it can be refined and optimised, but for this example, it'll suffice. The advantage of our approach here is we can later improve our Dockerfile and have a test to prove it works as we intend it to. This is a real strength of having black-box tests!\n\nTry and rerun the test; it should complain about not being able to build the image. Of course, that's because we haven't written a program to build yet!\n\nFor the test to fully execute, we'll need to create a program that listens on `8080`, but **that's all**. Stick to the TDD discipline, don't write the production code that would make the test pass until we've verified the test fails as we'd expect.\n\nCreate a `main.go` inside our `httpserver` folder with the following\n\n```go\npackage main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc main() {\n\thandler := http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {\n\t})\n\tif err := http.ListenAndServe(\":8080\", handler); err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n```\n\nTry to run the test again, and it should fail with the following.\n\n```\n    greet.go:16: Expected values to be equal:\n        +Hello, World\n        \\ No newline at end of file\n--- FAIL: TestGreeterHandler (2.09s)\n```\n\n## Write enough code to make it pass\n\nUpdate the handler to behave how our specification wants it to\n\n```go\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc main() {\n\thandler := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {\n\t\tfmt.Fprint(w, \"Hello, world\")\n\t})\n\tif err := http.ListenAndServe(\":8080\", handler); err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n```\n\n## Refactor\n\nWhilst this technically isn't a refactor, we shouldn't rely on the default HTTP client, so let's change our Driver, so we can supply one, which our test will give.\n\n```go\nimport (\n\t\"io\"\n\t\"net/http\"\n)\n\ntype Driver struct {\n\tBaseURL string\n\tClient  *http.Client\n}\n\nfunc (d Driver) Greet() (string, error) {\n\tres, err := d.Client.Get(d.BaseURL + \"/greet\")\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tdefer res.Body.Close()\n\tgreeting, err := io.ReadAll(res.Body)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn string(greeting), nil\n}\n```\n\nIn our test in `cmd/httpserver/greeter_server_test.go`, update the creation of the driver to pass in a client.\n\n```go\nclient := http.Client{\n\tTimeout: 1 * time.Second,\n}\n\ndriver := go_specs_greet.Driver{BaseURL: \"http://localhost:8080\", Client: &client}\nspecifications.GreetSpecification(t, driver)\n```\n\nIt's good practice to keep `main.go` as simple as possible; it should only be concerned with piecing together the building blocks you make into an application.\n\nCreate a file in the project root called `handler.go` and move our code into there.\n\n```go\npackage go_specs_greet\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n)\n\nfunc Handler(w http.ResponseWriter, r *http.Request) {\n\tfmt.Fprint(w, \"Hello, world\")\n}\n```\n\nUpdate `main.go` to import and use the handler instead.\n\n```go\npackage main\n\nimport (\n\t\"net/http\"\n\n\tgo_specs_greet \"github.com/quii/go-specs-greet\"\n)\n\nfunc main() {\n\thandler := http.HandlerFunc(go_specs_greet.Handler)\n\thttp.ListenAndServe(\":8080\", handler)\n}\n```\n\n## Reflect\n\nThe first step felt like an effort. We've made several `go` files to create and test an HTTP handler that returns a hard-coded string. This \"iteration 0\" ceremony and setup will serve us well for further iterations.\n\nChanging functionality should be simple and controlled by driving it through the specification and dealing with whatever changes it forces us to make. Now the `DockerFile` and `testcontainers` are set up for our acceptance test; we shouldn't have to change these files unless the way we construct our application changes.\n\nWe'll see this with our following requirement, greet a particular person.\n\n## Write the test first\n\nEdit our specification\n\n```go\npackage specifications\n\nimport (\n\t\"testing\"\n\n\t\"github.com/alecthomas/assert/v2\"\n)\n\ntype Greeter interface {\n\tGreet(name string) (string, error)\n}\n\nfunc GreetSpecification(t testing.TB, greeter Greeter) {\n\tgot, err := greeter.Greet(\"Mike\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, got, \"Hello, Mike\")\n}\n```\n\nTo allow us to greet specific people, we need to change the interface to our system to accept a `name` parameter.\n\n## Try to run the test\n\n```\n./greeter_server_test.go:48:39: cannot use driver (variable of type go_specs_greet.Driver) as type specifications.Greeter in argument to specifications.GreetSpecification:\n\tgo_specs_greet.Driver does not implement specifications.Greeter (wrong type for Greet method)\n\t\thave Greet() (string, error)\n\t\twant Greet(name string) (string, error)\n```\n\nThe change in the specification has meant our driver needs to be updated.\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\nUpdate the driver so that it specifies a `name` query value in the request to ask for a particular `name` to be greeted.\n\n```go\nimport \"io\"\n\nfunc (d Driver) Greet(name string) (string, error) {\n\tres, err := d.Client.Get(d.BaseURL + \"/greet?name=\" + name)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tdefer res.Body.Close()\n\tgreeting, err := io.ReadAll(res.Body)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn string(greeting), nil\n}\n```\n\nThe test should now run, and fail.\n\n```\n    greet.go:16: Expected values to be equal:\n        -Hello, world\n        \\ No newline at end of file\n        +Hello, Mike\n        \\ No newline at end of file\n--- FAIL: TestGreeterHandler (1.92s)\n```\n\n## Write enough code to make it pass\n\nExtract the `name` from the request and greet.\n\n```go\nimport (\n\t\"fmt\"\n\t\"net/http\"\n)\n\nfunc Handler(w http.ResponseWriter, r *http.Request) {\n\tfmt.Fprintf(w, \"Hello, %s\", r.URL.Query().Get(\"name\"))\n}\n```\n\nThe test should now pass.\n\n## Refactor\n\nIn [HTTP Handlers Revisited,](https://github.com/quii/learn-go-with-tests/blob/main/http-handlers-revisited.md) we discussed how important it is for HTTP handlers should only be responsible for handling HTTP concerns; any \"domain logic\" should live outside of the handler. This allows us to develop domain logic in isolation from HTTP, making it simpler to test and understand.\n\nLet's pull apart these concerns.\n\nUpdate our handler in `./handler.go` as follows:\n\n```go\nfunc Handler(w http.ResponseWriter, r *http.Request) {\n\tname := r.URL.Query().Get(\"name\")\n\tfmt.Fprint(w, Greet(name))\n}\n```\n\nCreate new file `./greet.go`:\n```go\npackage go_specs_greet\n\nimport \"fmt\"\n\nfunc Greet(name string) string {\n\treturn fmt.Sprintf(\"Hello, %s\", name)\n}\n```\n\n## A slight diversion in to the \"adapter\" design pattern\n\nNow that we've separated our domain logic of greeting people into a separate function, we are now free to write unit tests for our greet function. This is undoubtedly a lot simpler than testing it through a specification that goes through a driver that hits a web server, to get a string!\n\nWouldn't it be nice if we could reuse our specification here too? After all, the specification's point is decoupled from implementation details. If the specification captures our **essential complexity** and our \"domain\" code is supposed to model it, we should be able to use them together.\n\nLet's give it a go by creating  `./greet_test.go` as follows:\n\n```go\npackage go_specs_greet_test\n\nimport (\n\t\"testing\"\n\n\tgo_specs_greet \"github.com/quii/go-specs-greet\"\n\t\"github.com/quii/go-specs-greet/specifications\"\n)\n\nfunc TestGreet(t *testing.T) {\n\tspecifications.GreetSpecification(t, go_specs_greet.Greet)\n}\n\n```\n\nThis would be nice, but it doesn't work\n\n```\n./greet_test.go:11:39: cannot use go_specs_greet.Greet (value of type func(name string) string) as type specifications.Greeter in argument to specifications.GreetSpecification:\n\tfunc(name string) string does not implement specifications.Greeter (missing Greet method)\n```\n\nOur specification wants something that has a method `Greet()` not a function.\n\nThe compilation error is frustrating; we have a thing that we \"know\" is a `Greeter`, but it's not quite in the right **shape** for the compiler to let us use it. This is what the **adapter** pattern caters for.\n\n> In [software engineering](https://en.wikipedia.org/wiki/Software_engineering), the **adapter pattern** is a [software design pattern](https://en.wikipedia.org/wiki/Software_design_pattern) (also known as [wrapper](https://en.wikipedia.org/wiki/Wrapper_function), an alternative naming shared with the [decorator pattern](https://en.wikipedia.org/wiki/Decorator_pattern)) that allows the [interface](https://en.wikipedia.org/wiki/Interface_(computer_science)) of an existing [class](https://en.wikipedia.org/wiki/Class_(computer_science)) to be used as another interface.[[1\\]](https://en.wikipedia.org/wiki/Adapter_pattern#cite_note-HeadFirst-1) It is often used to make existing classes work with others without modifying their [source code](https://en.wikipedia.org/wiki/Source_code).\n\nA lot of fancy words for something relatively simple, which is often the case with design patterns, which is why people tend to roll their eyes at them. The value of design patterns is not specific implementations but a language to describe specific solutions to common problems engineers face. If you have a team that has a shared vocabulary, it reduces the friction in communication.\n\nAdd this code in `./specifications/adapters.go`\n\n```go\ntype GreetAdapter func(name string) string\n\nfunc (g GreetAdapter) Greet(name string) (string, error) {\n\treturn g(name), nil\n}\n```\n\nWe can now use our adapter in our test to plug our `Greet` function into the specification.\n\n```go\npackage go_specs_greet_test\n\nimport (\n\t\"testing\"\n\n\tgospecsgreet \"github.com/quii/go-specs-greet\"\n\t\"github.com/quii/go-specs-greet/specifications\"\n)\n\nfunc TestGreet(t *testing.T) {\n\tspecifications.GreetSpecification(\n\t\tt,\n\t\tspecifications.GreetAdapter(gospecsgreet.Greet),\n\t)\n}\n```\n\nThe adapter pattern is handy when you have a type that exhibits the behaviour that an interface wants, but isn't in the right shape.\n\n## Reflect\n\nThe behaviour change felt simple, right? OK, maybe it was simply due to the nature of the problem, but this method of work gives you discipline and a simple, repeatable way of changing your system from top to bottom:\n\n- Analyse your problem and identify a slight improvement to your system that pushes you in the right direction\n- Capture the new essential complexity in a specification\n- Follow the compilation errors until the AT runs\n- Update your implementation to make the system behave according to the specification\n- Refactor\n\nAfter the pain of the first iteration, we didn't have to edit our acceptance test code because we have the separation of specifications, drivers and implementation. Changing our specification required us to update our driver and finally our implementation, but the boilerplate code around _how_ to spin up the system as a container was unaffected.\n\nEven with the overhead of building a docker image for our application and spinning up the container, the feedback loop for testing our **entire** application is very tight:\n\n```\nquii@Chriss-MacBook-Pro go-specs-greet % go test ./...\nok  \tgithub.com/quii/go-specs-greet\t0.181s\nok  \tgithub.com/quii/go-specs-greet/cmd/httpserver\t2.221s\n?   \tgithub.com/quii/go-specs-greet/specifications\t[no test files]\n```\n\nNow, imagine your CTO has now decided that gRPC is _the future_. She wants you to expose this same functionality over a gRPC server whilst maintaining the existing HTTP server.\n\nThis is an example of **accidental complexity**. Remember, accidental complexity is the complexity we have to deal with because we're working with computers, stuff like networks, disks, APIs, etc. **The essential complexity has not changed**, so we shouldn't have to change our specifications.\n\nMany repository structures and design patterns are mainly dealing with separating types of complexity. For instance, \"ports and adapters\" ask that you separate your domain code from anything to do with accidental complexity; that code lives in an \"adapters\" folder.\n\n### Making the change easy\n\nSometimes, it makes sense to do some refactoring _before_ making a change.\n\n> First make the change easy, then make the easy change\n\n~Kent Beck\n\nFor that reason, let's move our `http` code - `driver.go` and `handler.go` - into a package called `httpserver` within an `adapters` folder and change their package names to `httpserver`.\n\nYou'll now need to import the root package into `handler.go` to refer to the Greet method...\n\n```go\npackage httpserver\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\n\tgo_specs_greet \"github.com/quii/go-specs-greet/domain/interactions\"\n)\n\nfunc Handler(w http.ResponseWriter, r *http.Request) {\n\tname := r.URL.Query().Get(\"name\")\n\tfmt.Fprint(w, go_specs_greet.Greet(name))\n}\n\n```\n\nimport your httpserver adapter into main.go:\n\n```go\npackage main\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/quii/go-specs-greet/adapters/httpserver\"\n)\n\nfunc main() {\n\thandler := http.HandlerFunc(httpserver.Handler)\n\thttp.ListenAndServe(\":8080\", handler)\n}\n```\n\nand update the import and reference to `Driver` in greeter_server_test.go:\n\n```go\ndriver := httpserver.Driver{BaseURL: \"http://localhost:8080\", Client: &client}\n```\n\nFinally, it's helpful to gather our domain level code in to its own folder too. Don't be lazy and have a `domain` folder in your projects with hundreds of unrelated types and functions. Make an effort to think about your domain and group ideas that belong together, together. This will make your project easier to understand and will improve the quality of your imports.\n\nRather than seeing\n\n```go\ndomain.Greet\n```\n\nWhich is just a bit weird, instead favour\n\n```go\ninteractions.Greet\n```\n\nCreate a `domain` folder to house all your domain code, and within it, an `interactions` folder. Depending on your tooling, you may have to update some imports and code.\n\nOur project tree should now look like this:\n\n```\nquii@Chriss-MacBook-Pro go-specs-greet % tree\n.\n├── Makefile\n├── README.md\n├── adapters\n│   └── httpserver\n│       ├── driver.go\n│       └── handler.go\n├── cmd\n│   └── httpserver\n|       ├── Dockerfile\n│       ├── greeter_server_test.go\n│       └── main.go\n├── domain\n│   └── interactions\n│       ├── greet.go\n│       └── greet_test.go\n├── go.mod\n├── go.sum\n└── specifications\n    └── adapters.go\n    └── greet.go\n\n```\n\nOur domain code, **essential complexity**, lives at the root of our go module, and code that will allow us to use them in \"the real world\" are organised into **adapters**. The `cmd` folder is where we can compose these logical groupings into practical applications, which have black-box tests to verify it all works. Nice!\n\nFinally, we can do a _tiny_ bit of tidying up our acceptance test. If you consider the high-level steps of our acceptance test:\n\n- Build a docker image\n- Wait for it to be listening on _some_ port\n- Create a driver that understands how to translate the DSL into system specific calls\n- Plug in the driver into the specification\n\n... you'll realise we have the same requirements for an acceptance test for the gRPC server!\n\nThe `adapters` folder seems a good place as any, so inside a file called `docker.go`, encapsulate the first two steps in a function that we'll reuse next.\n\n```go\npackage adapters\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/alecthomas/assert/v2\"\n\t\"github.com/docker/go-connections/nat\"\n\t\"github.com/testcontainers/testcontainers-go\"\n\t\"github.com/testcontainers/testcontainers-go/wait\"\n)\n\nfunc StartDockerServer(\n\tt testing.TB,\n\tport string,\n\tdockerFilePath string,\n) {\n\tctx := context.Background()\n\tt.Helper()\n\treq := testcontainers.ContainerRequest{\n\t\tFromDockerfile: testcontainers.FromDockerfile{\n\t\t\tContext:       \"../../.\",\n\t\t\tDockerfile:    dockerFilePath,\n\t\t\tPrintBuildLog: true,\n\t\t},\n\t\tExposedPorts: []string{fmt.Sprintf(\"%s:%s\", port, port)},\n\t\tWaitingFor:   wait.ForListeningPort(nat.Port(port)).WithStartupTimeout(5 * time.Second),\n\t}\n\tcontainer, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{\n\t\tContainerRequest: req,\n\t\tStarted:          true,\n\t})\n\tassert.NoError(t, err)\n\tt.Cleanup(func() {\n\t\tassert.NoError(t, container.Terminate(ctx))\n\t})\n}\n```\n\nThis gives us an opportunity to clean up our acceptance test a little\n\n```go\nfunc TestGreeterServer(t *testing.T) {\n\tvar (\n\t\tport           = \"8080\"\n\t\tdockerFilePath = \"./cmd/httpserver/Dockerfile\"\n\t\tbaseURL        = fmt.Sprintf(\"http://localhost:%s\", port)\n\t\tdriver         = httpserver.Driver{BaseURL: baseURL, Client: &http.Client{\n\t\t\tTimeout: 1 * time.Second,\n\t\t}}\n\t)\n\n\tadapters.StartDockerServer(t, port, dockerFilePath)\n\tspecifications.GreetSpecification(t, driver)\n}\n```\n\nThis should make writing the _next_ test simpler.\n\n## Write the test first\n\nThis new functionality can be accomplished by creating a new `adapter` to interact with our domain code. For that reason we:\n\n- Shouldn't have to change the specification;\n- Should be able to reuse the specification;\n- Should be able to reuse the domain code.\n\nCreate a new folder `grpcserver` inside `cmd` to house our new program and the corresponding acceptance test. Inside `cmd/grpc_server/greeter_server_test.go`, add an acceptance test, which looks very similar to our HTTP server test, not by coincidence but by design.\n\n```go\npackage main_test\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/quii/go-specs-greet/adapters\"\n\t\"github.com/quii/go-specs-greet/adapters/grpcserver\"\n\t\"github.com/quii/go-specs-greet/specifications\"\n)\n\nfunc TestGreeterServer(t *testing.T) {\n\tvar (\n\t\tport           = \"50051\"\n\t\tdockerFilePath = \"./cmd/grpcserver/Dockerfile\"\n\t\tdriver         = grpcserver.Driver{Addr: fmt.Sprintf(\"localhost:%s\", port)}\n\t)\n\n\tadapters.StartDockerServer(t, port, dockerFilePath)\n\tspecifications.GreetSpecification(t, &driver)\n}\n```\n\nThe only differences are:\n\n- We use a different docker file, because we're building a different program\n- This means we'll need a new `Driver`, that'll use `gRPC` to interact with our new program\n\n## Try to run the test\n\n```\n./greeter_server_test.go:26:12: undefined: grpcserver\n```\n\nWe haven't created a `Driver` yet, so it won't compile.\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\nCreate a `grpcserver` folder inside `adapters` and inside it create `driver.go`\n\n```go\npackage grpcserver\n\ntype Driver struct {\n\tAddr string\n}\n\nfunc (d Driver) Greet(name string) (string, error) {\n\treturn \"\", nil\n}\n```\n\nIf you run again, it should now _compile_ but not pass because we haven't created a Dockerfile and corresponding program to run.\n\nCreate a new `Dockerfile` inside `cmd/grpcserver`.\n\n```dockerfile\n# Make sure to specify the same Go version as the one in the go.mod file.\nFROM golang:1.18-alpine\n\nWORKDIR /app\n\nCOPY go.mod ./\n\nRUN go mod download\n\nCOPY . .\n\nRUN go build -o svr cmd/grpcserver/*.go\n\nEXPOSE 50051\nCMD [ \"./svr\" ]\n```\n\nAnd a `main.go`\n\n```go\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(\"implement me\")\n}\n```\n\nYou should find now that the test fails because our server is not listening on the port. Now is the time to start building our client and server with gRPC.\n\n## Write enough code to make it pass\n\n### gRPC\n\nIf you're unfamiliar with gRPC, I'd start by looking at the [gRPC website](https://grpc.io). Still, for this chapter, it's just another kind of adapter into our system, a way for other systems to call (**r**emote **p**rocedure **c**all) our excellent domain code.\n\nThe twist is you define a \"service definition\" using Protocol Buffers. You then generate server and client code from the definition. This not only works for Go but for most mainstream languages too. This means you can share a definition with other teams in your company who may not even write Go and can still do service-to-service communication smoothly.\n\nIf you haven't used gRPC before, you'll need to install a **Protocol buffer compiler** and some **Go plugins**. [The gRPC website has clear instructions on how to do this](https://grpc.io/docs/languages/go/quickstart/).\n\nInside the same folder as our new driver, add a `greet.proto` file with the following\n\n```protobuf\nsyntax = \"proto3\";\n\noption go_package = \"github.com/quii/adapters/grpcserver\";\n\npackage grpcserver;\n\nservice Greeter {\n  rpc Greet (GreetRequest) returns (GreetReply) {}\n}\n\nmessage GreetRequest {\n  string name = 1;\n}\n\nmessage GreetReply {\n  string message = 1;\n}\n```\n\nTo understand this definition, you don't need to be an expert in Protocol Buffers. We define a service with a Greet method and then describe the incoming and outgoing message types.\n\nInside `adapters/grpcserver` run the following to generate the client and server code\n\n```\nprotoc --go_out=. --go_opt=paths=source_relative \\\n    --go-grpc_out=. --go-grpc_opt=paths=source_relative \\\n    greet.proto\n```\n\nIf it worked, we would have some code generated for us to use. Let's start by using the generated client code inside our `Driver`.\n\n```go\npackage grpcserver\n\nimport (\n\t\"context\"\n\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/credentials/insecure\"\n)\n\ntype Driver struct {\n\tAddr string\n}\n\nfunc (d Driver) Greet(name string) (string, error) {\n\t//todo: we shouldn't redial every time we call greet, refactor out when we're green\n\tconn, err := grpc.Dial(d.Addr, grpc.WithTransportCredentials(insecure.NewCredentials()))\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tdefer conn.Close()\n\n\tclient := NewGreeterClient(conn)\n\tgreeting, err := client.Greet(context.Background(), &GreetRequest{\n\t\tName: name,\n\t})\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn greeting.Message, nil\n}\n```\n\nNow that we have a client, we need to update our `main.go` to create a server. Remember, at this point; we're just trying to get our test to pass and not worrying about code quality.\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"log\"\n\t\"net\"\n\n\t\"github.com/quii/go-specs-greet/adapters/grpcserver\"\n\t\"google.golang.org/grpc\"\n)\n\nfunc main() {\n\tlis, err := net.Listen(\"tcp\", \":50051\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\ts := grpc.NewServer()\n\tgrpcserver.RegisterGreeterServer(s, &GreetServer{})\n\n\tif err := s.Serve(lis); err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n\ntype GreetServer struct {\n\tgrpcserver.UnimplementedGreeterServer\n}\n\nfunc (g GreetServer) Greet(ctx context.Context, request *grpcserver.GreetRequest) (*grpcserver.GreetReply, error) {\n\treturn &grpcserver.GreetReply{Message: \"fixme\"}, nil\n}\n```\n\nTo create our gRPC server, we have to implement the interface it generated for us\n\n```go\n// GreeterServer is the server API for Greeter service.\n// All implementations must embed UnimplementedGreeterServer\n// for forward compatibility\ntype GreeterServer interface {\n\tGreet(context.Context, *GreetRequest) (*GreetReply, error)\n\tmustEmbedUnimplementedGreeterServer()\n}\n```\n\nOur `main` function:\n\n- Listens on a port\n- Creates a `GreetServer` that implements the interface, and then registers it with `grpcServer.RegisterGreeterServer`, along with a `grpc.Server`.\n- Uses the server with the listener\n\nIt wouldn't be a massive extra effort to call our domain code inside `greetServer.Greet` rather than hard-coding `fix-me` in the message, but I'd like to run our acceptance test first to see if everything is working on a transport level and verify the failing test output.\n\n```\ngreet.go:16: Expected values to be equal:\n-fixme\n\\ No newline at end of file\n+Hello, Mike\n\\ No newline at end of file\n```\n\nNice! We can see our driver is able to connect to our gRPC server in the test.\n\nNow, call our domain code inside our `GreetServer`\n\n```go\ntype GreetServer struct {\n\tgrpcserver.UnimplementedGreeterServer\n}\n\nfunc (g GreetServer) Greet(ctx context.Context, request *grpcserver.GreetRequest) (*grpcserver.GreetReply, error) {\n\treturn &grpcserver.GreetReply{Message: interactions.Greet(request.Name)}, nil\n}\n```\n\nFinally, it passes! We have an acceptance test that proves our gRPC greet server behaves how we'd like.\n\n## Refactor\n\nWe committed several sins to get the test passing, but now they're passing, we have the safety net to refactor.\n\n### Simplify main\n\nAs before, we don't want `main` to have too much code inside it. We can move our new `GreetServer` into `adapters/grpcserver` as that's where it should live. In terms of cohesion, if we change the service definition, we want the \"blast-radius\" of change to be confined to that area of our code.\n\n### Don't redial in our driver every time\n\nWe only have one test, but if we expand our specification (we will), it doesn't make sense for the Driver to redial for every RPC call.\n\n```go\npackage grpcserver\n\nimport (\n\t\"context\"\n\t\"sync\"\n\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/credentials/insecure\"\n)\n\ntype Driver struct {\n\tAddr string\n\n\tconnectionOnce sync.Once\n\tconn           *grpc.ClientConn\n\tclient         GreeterClient\n}\n\nfunc (d *Driver) Greet(name string) (string, error) {\n\tclient, err := d.getClient()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tgreeting, err := client.Greet(context.Background(), &GreetRequest{\n\t\tName: name,\n\t})\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn greeting.Message, nil\n}\n\nfunc (d *Driver) getClient() (GreeterClient, error) {\n\tvar err error\n\td.connectionOnce.Do(func() {\n\t\td.conn, err = grpc.Dial(d.Addr, grpc.WithTransportCredentials(insecure.NewCredentials()))\n\t\td.client = NewGreeterClient(d.conn)\n\t})\n\treturn d.client, err\n}\n```\n\nHere we're showing how we can use [`sync.Once`](https://pkg.go.dev/sync#Once) to ensure our `Driver` only attempts to create a connection to our server once.\n\nLet's take a look at the current state of our project structure before moving on.\n\n```\nquii@Chriss-MacBook-Pro go-specs-greet % tree\n.\n├── Makefile\n├── README.md\n├── adapters\n│   ├── docker.go\n│   ├── grpcserver\n│   │   ├── driver.go\n│   │   ├── greet.pb.go\n│   │   ├── greet.proto\n│   │   ├── greet_grpc.pb.go\n│   │   └── server.go\n│   └── httpserver\n│       ├── driver.go\n│       └── handler.go\n├── cmd\n│   ├── grpcserver\n│   │   ├── Dockerfile\n│   │   ├── greeter_server_test.go\n│   │   └── main.go\n│   └── httpserver\n│       ├── Dockerfile\n│       ├── greeter_server_test.go\n│       └── main.go\n├── domain\n│   └── interactions\n│       ├── greet.go\n│       └── greet_test.go\n├── go.mod\n├── go.sum\n└── specifications\n    └── greet.go\n```\n\n- `adapters` have cohesive units of functionality grouped together\n- `cmd` holds our applications and corresponding acceptance tests\n- Our code is totally decoupled from any accidental complexity\n\n### Consolidating `Dockerfile`\n\nYou've probably noticed the two `Dockerfiles` are almost identical beyond the path to the binary we wish to build.\n\n`Dockerfiles` can accept arguments to let us reuse them in different contexts, which sounds perfect. We can delete our 2 Dockerfiles and instead have one at the root of the project with the following\n\n```dockerfile\n# Make sure to specify the same Go version as the one in the go.mod file.\nFROM golang:1.18-alpine\n\nWORKDIR /app\n\nARG bin_to_build\n\nCOPY go.mod ./\n\nRUN go mod download\n\nCOPY . .\n\nRUN go build -o svr cmd/${bin_to_build}/main.go\n\nCMD [ \"./svr\" ]\n```\n\nWe'll have to update our `StartDockerServer` function to pass in the argument when we build the images\n\n```go\nfunc StartDockerServer(\n\tt testing.TB,\n\tport string,\n\tbinToBuild string,\n) {\n\tctx := context.Background()\n\tt.Helper()\n\treq := testcontainers.ContainerRequest{\n\t\tFromDockerfile: testcontainers.FromDockerfile{\n\t\t\tContext:    \"../../.\",\n\t\t\tDockerfile: \"Dockerfile\",\n\t\t\tBuildArgs: map[string]*string{\n\t\t\t\t\"bin_to_build\": &binToBuild,\n\t\t\t},\n\t\t\tPrintBuildLog: true,\n\t\t},\n\t\tExposedPorts: []string{fmt.Sprintf(\"%s:%s\", port, port)},\n\t\tWaitingFor:   wait.ForListeningPort(nat.Port(port)).WithStartupTimeout(5 * time.Second),\n\t}\n\tcontainer, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{\n\t\tContainerRequest: req,\n\t\tStarted:          true,\n\t})\n\tassert.NoError(t, err)\n\tt.Cleanup(func() {\n\t\tassert.NoError(t, container.Terminate(ctx))\n\t})\n}\n```\n\nAnd finally, update our tests to pass in the image to build (do this for the other test and change `grpcserver` to `httpserver`).\n\n```go\nfunc TestGreeterServer(t *testing.T) {\n\tvar (\n\t\tport   = \"50051\"\n\t\tdriver = grpcserver.Driver{Addr: fmt.Sprintf(\"localhost:%s\", port)}\n\t)\n\n\tadapters.StartDockerServer(t, port, \"grpcserver\")\n\tspecifications.GreetSpecification(t, &driver)\n}\n```\n\n### Separating different kinds of tests\n\nAcceptance tests are great in that they test the whole system works from a pure user-facing, behavioural POV, but they do have their downsides compared to unit tests:\n\n- Slower\n- Quality of feedback is often not as focused as a unit test\n- Doesn't help you with internal quality, or design\n\n[The Test Pyramid](https://martinfowler.com/articles/practical-test-pyramid.html) guides us on the kind of mix we want for our test suite, you should read Fowler's post for more detail, but the very simplistic summary for this post is \"lots of unit tests and a few acceptance tests\".\n\nFor that reason, as a project grows you often may be in situations where the acceptance tests can take a few minutes to run. To offer a friendly developer experience for people checking out your project, you can enable developers to run the different kinds of tests separately.\n\nIt's preferable that running `go test ./...` should be runnable with no further set up from an engineer, beyond say a few key dependencies such as the Go compiler (obviously) and perhaps Docker.\n\nGo provides a mechanism for engineers to run only \"short\" tests with the [short flag](https://pkg.go.dev/testing#Short)\n\n`go test -short ./...`\n\nWe can add to our acceptance tests to see if the user wants to run our acceptance tests by inspecting the value of the flag\n\n```go\nif testing.Short() {\n\tt.Skip()\n}\n```\n\nI made a `Makefile` to show this usage\n\n```makefile\nbuild:\n\tgolangci-lint run\n\tgo test ./...\n\nunit-tests:\n\tgo test -short ./...\n```\n\n### When should I write acceptance tests?\n\nThe best practice is to favour having lots of fast running unit tests and a few acceptance tests, but how do you decide when you should write an acceptance test, vs unit tests?\n\nIt's difficult to give a concrete rule, but the questions I typically ask myself are:\n\n- Is this an edge case? I'd prefer to unit test those\n- Is this something that the non-computer people talk about a lot? I would prefer to have a lot of confidence the key thing \"really\" works, so I'd add an acceptance test\n- Am I describing a user journey, rather than a specific function? Acceptance test\n- Would unit tests give me enough confidence? Sometimes you're taking an existing journey that already has an acceptance test, but you're adding other functionality to deal with different scenarios due to different inputs. In this case, adding another acceptance test adds a cost but brings little value, so I'd prefer some unit tests.\n\n## Iterating on our work\n\nWith all this effort, you'd hope extending our system will now be simple. Making a system that is simple to work on, is not necessarily easy, but it's worth the time, and is substantially easier to do when you start a project.\n\nLet's extend our API to include a \"curse\" functionality.\n\n## Write the test first\n\nThis is brand-new behaviour, so we should start with an acceptance test. In our specification file, add the following\n\n```go\ntype MeanGreeter interface {\n\tCurse(name string) (string, error)\n}\n\nfunc CurseSpecification(t *testing.T, meany MeanGreeter) {\n\tgot, err := meany.Curse(\"Chris\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, got, \"Go to hell, Chris!\")\n}\n```\n\nPick one of our acceptance tests and try to use the specification\n\n```go\nfunc TestGreeterServer(t *testing.T) {\n\tif testing.Short() {\n\t\tt.Skip()\n\t}\n\tvar (\n\t\tport   = \"50051\"\n\t\tdriver = grpcserver.Driver{Addr: fmt.Sprintf(\"localhost:%s\", port)}\n\t)\n\n\tt.Cleanup(driver.Close)\n\tadapters.StartDockerServer(t, port, \"grpcserver\")\n\tspecifications.GreetSpecification(t, &driver)\n\tspecifications.CurseSpecification(t, &driver)\n}\n```\n\n## Try to run the test\n\n```\n# github.com/quii/go-specs-greet/cmd/grpcserver_test [github.com/quii/go-specs-greet/cmd/grpcserver.test]\n./greeter_server_test.go:27:39: cannot use &driver (value of type *grpcserver.Driver) as type specifications.MeanGreeter in argument to specifications.CurseSpecification:\n\t*grpcserver.Driver does not implement specifications.MeanGreeter (missing Curse method)\n```\n\nOur `Driver` doesn't support `Curse` yet.\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\nRemember we're just trying to get the test to run, so add the method to `Driver`\n\n```go\nfunc (d *Driver) Curse(name string) (string, error) {\n\treturn \"\", nil\n}\n```\n\nIf you try again, the test should compile, run, and fail\n\n```\ngreet.go:26: Expected values to be equal:\n+Go to hell, Chris!\n\\ No newline at end of file\n```\n\n## Write enough code to make it pass\n\nWe'll need to update our protocol buffer specification have a `Curse` method on it, and then regenerate our code.\n\n```protobuf\nservice Greeter {\n  rpc Greet (GreetRequest) returns (GreetReply) {}\n  rpc Curse (GreetRequest) returns (GreetReply) {}\n}\n```\n\nYou could argue that reusing the types `GreetRequest` and `GreetReply` is inappropriate coupling, but we can deal with that in the refactoring stage. As I keep stressing, we're just trying to get the test passing, so we verify the software works, _then_ we can make it nice.\n\nRe-generate our code with (inside `adapters/grpcserver`).\n\n```\nprotoc --go_out=. --go_opt=paths=source_relative \\\n    --go-grpc_out=. --go-grpc_opt=paths=source_relative \\\n    greet.proto\n```\n\n### Update driver\n\nNow the client code has been updated, we can now call `Curse` in our `Driver`\n\n```go\nfunc (d *Driver) Curse(name string) (string, error) {\n\tclient, err := d.getClient()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tgreeting, err := client.Curse(context.Background(), &GreetRequest{\n\t\tName: name,\n\t})\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn greeting.Message, nil\n}\n```\n\n### Update server\n\nFinally, we need to add the `Curse` method to our `Server`\n\n```go\npackage grpcserver\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/quii/go-specs-greet/domain/interactions\"\n)\n\ntype GreetServer struct {\n\tUnimplementedGreeterServer\n}\n\nfunc (g GreetServer) Curse(ctx context.Context, request *GreetRequest) (*GreetReply, error) {\n\treturn &GreetReply{Message: fmt.Sprintf(\"Go to hell, %s!\", request.Name)}, nil\n}\n\nfunc (g GreetServer) Greet(ctx context.Context, request *GreetRequest) (*GreetReply, error) {\n\treturn &GreetReply{Message: interactions.Greet(request.Name)}, nil\n}\n```\n\nThe tests should now pass.\n\n## Refactor\n\nTry doing this yourself.\n\n- Extract the `Curse` \"domain logic\", away from the grpc server, as we did for `Greet`. Use the specification as a unit test against your domain logic\n- Have different types in the protobuf to ensure the message types for `Greet` and `Curse` are decoupled.\n\n## Implementing `Curse` for the HTTP server\n\nAgain, an exercise for you, the reader. We have our domain-level specification and our domain-level logic neatly separated. If you've followed this chapter, this should be very straightforward.\n\n- Add the specification to the existing acceptance test for the HTTP server\n- Update your `Driver`\n- Add the new endpoint to the server, and reuse the domain code to implement the functionality. You may wish to use `http.NewServeMux` to handle the routeing to the separate endpoints.\n\nRemember to work in small steps, commit and run your tests frequently. If you get really stuck [you can find my implementation on GitHub](https://github.com/quii/go-specs-greet).\n\n## Enhance both systems by updating the domain logic with a unit test\n\nAs mentioned, not every change to a system should be driven via an acceptance test. Permutations of business rules and edge cases should be simple to drive via a unit test if you have separated concerns well.\n\nAdd a unit test to our `Greet` function to default the `name` to `World` if it is empty. You should see how simple this is, and then the business rules are reflected in both applications for \"free\".\n\n## Wrapping up\n\nBuilding systems with a reasonable cost of change requires you to have ATs engineered to help you, not become a maintenance burden. They can be used as a means of guiding, or as a GOOS says, \"growing\" your software methodically.\n\nHopefully, with this example, you can see our application's predictable, structured workflow for driving change and how you could use it for your work.\n\nYou can imagine talking to a stakeholder who wants to extend the system you work on in some way. Capture it in a domain-centric, implementation-agnostic way in a specification, and use it as a north star towards your efforts. Riya and I describe leveraging BDD techniques like \"Example Mapping\" [in our GopherconUK talk](https://www.youtube.com/watch?v=ZMWJCk_0WrY) to help you understand the essential complexity more deeply and allow you to write more detailed and meaningful specifications.\n\nSeparating essential and accidental complexity concerns will make your work less ad-hoc and more structured and deliberate; this ensures the resiliency of your acceptance tests and helps them become less of a maintenance burden.\n\nDave Farley gives an excellent tip:\n\n> Imagine the least technical person that you can think of, who understands the problem-domain, reading your Acceptance Tests. The tests should make sense to that person.\n\nSpecifications should then double up as documentation. They should specify clearly how a system should behave. This idea is the principle around tools like [Cucumber](https://cucumber.io), which offers you a DSL for capturing behaviours as code, and then you convert that DSL into system calls, just like we did here.\n\n### What has been covered\n\n- Writing abstract specifications allows you to express the essential complexity of the problem you're solving and remove accidental complexity. This will enable you to reuse the specifications in different contexts.\n- How to use [Testcontainers](https://golang.testcontainers.org) to manage the life-cycle of your system for ATs. This allows you to thoroughly test the image you intend to ship on your computer, giving you fast feedback and confidence.\n- A brief intro into containerising your application with Docker\n- gRPC\n- Rather than chasing canned folder structures, you can use your development approach to naturally drive out the structure of your application, based on your own needs\n\n### Further material\n\n- In this example, our \"DSL\" is not much of a DSL; we just used interfaces to decouple our specification from the real world and allow us to express domain logic cleanly. As your system grows, this level of abstraction might become clumsy and unclear. [Read into the \"Screenplay Pattern\"](https://cucumber.io/blog/bdd/understanding-screenplay-(part-1)/) if you want to find more ideas as to how to structure your specifications.\n- For emphasis, [Growing Object-Oriented Software, Guided by Tests,](http://www.growing-object-oriented-software.com) is a classic. It demonstrates applying this \"London style\", \"top-down\" approach to writing software. Anyone who has enjoyed Learn Go with Tests should get much value from reading GOOS.\n- [In the example code repository](https://github.com/quii/go-specs-greet), there's more code and ideas I haven't written about here, such as multi-stage docker build, you may wish to check this out.\n  - In particular, *for fun*, I made a **third program**, a website with some HTML forms to `Greet` and `Curse`. The `Driver` leverages the excellent-looking [https://github.com/go-rod/rod](https://github.com/go-rod/rod) module, which allows it to work with the website with a browser, just like a user would. Looking at the git history, you can see how I started not using any templating tools \"just to make it work\" Then, once I passed my acceptance test, I had the freedom to do so without fear of breaking things. -->\n"
  },
  {
    "path": "select/v1/racer.go",
    "content": "package racer\n\nimport (\n\t\"net/http\"\n\t\"time\"\n)\n\n// Racer compares the response times of a and b, returning the fastest one.\nfunc Racer(a, b string) (winner string) {\n\taDuration := measureResponseTime(a)\n\tbDuration := measureResponseTime(b)\n\n\tif aDuration < bDuration {\n\t\treturn a\n\t}\n\n\treturn b\n}\n\nfunc measureResponseTime(url string) time.Duration {\n\tstart := time.Now()\n\thttp.Get(url)\n\treturn time.Since(start)\n}\n"
  },
  {
    "path": "select/v1/racer_test.go",
    "content": "package racer\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestRacer(t *testing.T) {\n\n\tslowServer := makeDelayedServer(20 * time.Millisecond)\n\tfastServer := makeDelayedServer(0 * time.Millisecond)\n\n\tdefer slowServer.Close()\n\tdefer fastServer.Close()\n\n\tslowURL := slowServer.URL\n\tfastURL := fastServer.URL\n\n\twant := fastURL\n\tgot := Racer(slowURL, fastURL)\n\n\tif got != want {\n\t\tt.Errorf(\"got %q, want %q\", got, want)\n\t}\n}\n\nfunc makeDelayedServer(delay time.Duration) *httptest.Server {\n\treturn httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\ttime.Sleep(delay)\n\t\tw.WriteHeader(http.StatusOK)\n\t}))\n}\n"
  },
  {
    "path": "select/v2/racer.go",
    "content": "package racer\n\nimport (\n\t\"net/http\"\n)\n\n// Racer compares the response times of a and b, returning the fastest one.\nfunc Racer(a, b string) (winner string) {\n\tselect {\n\tcase <-ping(a):\n\t\treturn a\n\tcase <-ping(b):\n\t\treturn b\n\t}\n}\n\nfunc ping(url string) chan struct{} {\n\tch := make(chan struct{})\n\tgo func() {\n\t\thttp.Get(url)\n\t\tclose(ch)\n\t}()\n\treturn ch\n}\n"
  },
  {
    "path": "select/v2/racer_test.go",
    "content": "package racer\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestRacer(t *testing.T) {\n\n\tslowServer := makeDelayedServer(20 * time.Millisecond)\n\tfastServer := makeDelayedServer(0 * time.Millisecond)\n\n\tdefer slowServer.Close()\n\tdefer fastServer.Close()\n\n\tslowURL := slowServer.URL\n\tfastURL := fastServer.URL\n\n\twant := fastURL\n\tgot := Racer(slowURL, fastURL)\n\n\tif got != want {\n\t\tt.Errorf(\"got %q, want %q\", got, want)\n\t}\n}\n\nfunc makeDelayedServer(delay time.Duration) *httptest.Server {\n\treturn httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\ttime.Sleep(delay)\n\t\tw.WriteHeader(http.StatusOK)\n\t}))\n}\n"
  },
  {
    "path": "select/v3/racer.go",
    "content": "package racer\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"time\"\n)\n\nvar tenSecondTimeout = 10 * time.Second\n\n// Racer compares the response times of a and b, returning the fastest one, timing out after 10s.\nfunc Racer(a, b string) (winner string, error error) {\n\treturn ConfigurableRacer(a, b, tenSecondTimeout)\n}\n\n// ConfigurableRacer compares the response times of a and b, returning the fastest one.\nfunc ConfigurableRacer(a, b string, timeout time.Duration) (winner string, error error) {\n\tselect {\n\tcase <-ping(a):\n\t\treturn a, nil\n\tcase <-ping(b):\n\t\treturn b, nil\n\tcase <-time.After(timeout):\n\t\treturn \"\", fmt.Errorf(\"timed out waiting for %s and %s\", a, b)\n\t}\n}\n\nfunc ping(url string) chan struct{} {\n\tch := make(chan struct{})\n\tgo func() {\n\t\thttp.Get(url)\n\t\tclose(ch)\n\t}()\n\treturn ch\n}\n"
  },
  {
    "path": "select/v3/racer_test.go",
    "content": "package racer\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestRacer(t *testing.T) {\n\n\tt.Run(\"compares speeds of servers, returning the url of the fastest one\", func(t *testing.T) {\n\t\tslowServer := makeDelayedServer(20 * time.Millisecond)\n\t\tfastServer := makeDelayedServer(0 * time.Millisecond)\n\n\t\tdefer slowServer.Close()\n\t\tdefer fastServer.Close()\n\n\t\tslowURL := slowServer.URL\n\t\tfastURL := fastServer.URL\n\n\t\twant := fastURL\n\t\tgot, err := Racer(slowURL, fastURL)\n\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"did not expect an error but got one %v\", err)\n\t\t}\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %q, want %q\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"returns an error if a server doesn't respond within 10s\", func(t *testing.T) {\n\t\tserver := makeDelayedServer(25 * time.Millisecond)\n\n\t\tdefer server.Close()\n\n\t\t_, err := ConfigurableRacer(server.URL, server.URL, 20*time.Millisecond)\n\n\t\tif err == nil {\n\t\t\tt.Error(\"expected an error but didn't get one\")\n\t\t}\n\t})\n}\n\nfunc makeDelayedServer(delay time.Duration) *httptest.Server {\n\treturn httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\ttime.Sleep(delay)\n\t\tw.WriteHeader(http.StatusOK)\n\t}))\n}\n"
  },
  {
    "path": "select.md",
    "content": "# Select\n\n**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/main/select)**\n\nYou have been asked to make a function called `WebsiteRacer` which takes two URLs and \"races\" them by hitting them with an HTTP GET and returning the URL which returned first. If none of them return within 10 seconds then it should return an `error`.\n\nFor this, we will be using:\n\n- `net/http` to make the HTTP calls.\n- `net/http/httptest` to help us test them.\n- goroutines.\n- `select` to synchronise processes.\n\n## Write the test first\n\nLet's start with something naive to get us going.\n\n```go\nfunc TestRacer(t *testing.T) {\n\tslowURL := \"http://www.facebook.com\"\n\tfastURL := \"http://www.quii.dev\"\n\n\twant := fastURL\n\tgot := Racer(slowURL, fastURL)\n\n\tif got != want {\n\t\tt.Errorf(\"got %q, want %q\", got, want)\n\t}\n}\n```\n\nWe know this isn't perfect and has problems, but it's a start. It's important not to get too hung-up on getting things perfect first time.\n\n## Try to run the test\n\n`./racer_test.go:14:9: undefined: Racer`\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\n```go\nfunc Racer(a, b string) (winner string) {\n\treturn\n}\n```\n\n`racer_test.go:25: got '', want 'http://www.quii.dev'`\n\n## Write enough code to make it pass\n\n```go\nfunc Racer(a, b string) (winner string) {\n\tstartA := time.Now()\n\thttp.Get(a)\n\taDuration := time.Since(startA)\n\n\tstartB := time.Now()\n\thttp.Get(b)\n\tbDuration := time.Since(startB)\n\n\tif aDuration < bDuration {\n\t\treturn a\n\t}\n\n\treturn b\n}\n```\n\nFor each URL:\n\n1. We use `time.Now()` to record just before we try and get the `URL`.\n1. Then we use [`http.Get`](https://golang.org/pkg/net/http/#Client.Get) to try and perform an HTTP `GET` request against the `URL`. This function returns an [`http.Response`](https://golang.org/pkg/net/http/#Response) and an `error` but so far we are not interested in these values.\n1. `time.Since` takes the start time and returns a `time.Duration` of the difference.\n\nOnce we have done this we simply compare the durations to see which is the quickest.\n\n### Problems\n\nThis may or may not make the test pass for you. The problem is we're reaching out to real websites to test our own logic.\n\nTesting code that uses HTTP is so common that Go has tools in the standard library to help you test it.\n\nIn the mocking and dependency injection chapters, we covered how ideally we don't want to be relying on external services to test our code because they can be\n\n- Slow\n- Flaky\n- Can't test edge cases\n\nIn the standard library, there is a package called [`net/http/httptest`](https://golang.org/pkg/net/http/httptest/) which enables users to easily create a mock HTTP server.\n\nLet's change our tests to use mocks so we have reliable servers to test against that we can control.\n\n```go\nfunc TestRacer(t *testing.T) {\n\n\tslowServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\ttime.Sleep(20 * time.Millisecond)\n\t\tw.WriteHeader(http.StatusOK)\n\t}))\n\n\tfastServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tw.WriteHeader(http.StatusOK)\n\t}))\n\n\tslowURL := slowServer.URL\n\tfastURL := fastServer.URL\n\n\twant := fastURL\n\tgot := Racer(slowURL, fastURL)\n\n\tif got != want {\n\t\tt.Errorf(\"got %q, want %q\", got, want)\n\t}\n\n\tslowServer.Close()\n\tfastServer.Close()\n}\n```\n\nThe syntax may look a bit busy but just take your time.\n\n`httptest.NewServer` takes an `http.HandlerFunc` which we are sending in via an _anonymous function_.\n\n`http.HandlerFunc` is a type that looks like this: `type HandlerFunc func(ResponseWriter, *Request)`.\n\nAll it's really saying is it needs a function that takes a `ResponseWriter` and a `Request`, which is not too surprising for an HTTP server.\n\nIt turns out there's really no extra magic here, **this is also how you would write a _real_ HTTP server in Go**. The only difference is we are wrapping it in an `httptest.NewServer` which makes it easier to use with testing, as it finds an open port to listen on and then you can close it when you're done with your test.\n\nInside our two servers, we make the slow one have a short `time.Sleep` when we get a request to make it slower than the other one. Both servers then write an `OK` response with `w.WriteHeader(http.StatusOK)` back to the caller.\n\nIf you re-run the test it will definitely pass now and should be faster. Play with these sleeps to deliberately break the test.\n\n## Refactor\n\nWe have some duplication in both our production code and test code.\n\n```go\nfunc Racer(a, b string) (winner string) {\n\taDuration := measureResponseTime(a)\n\tbDuration := measureResponseTime(b)\n\n\tif aDuration < bDuration {\n\t\treturn a\n\t}\n\n\treturn b\n}\n\nfunc measureResponseTime(url string) time.Duration {\n\tstart := time.Now()\n\thttp.Get(url)\n\treturn time.Since(start)\n}\n```\n\nThis DRY-ing up makes our `Racer` code a lot easier to read.\n\n```go\nfunc TestRacer(t *testing.T) {\n\n\tslowServer := makeDelayedServer(20 * time.Millisecond)\n\tfastServer := makeDelayedServer(0 * time.Millisecond)\n\n\tdefer slowServer.Close()\n\tdefer fastServer.Close()\n\n\tslowURL := slowServer.URL\n\tfastURL := fastServer.URL\n\n\twant := fastURL\n\tgot := Racer(slowURL, fastURL)\n\n\tif got != want {\n\t\tt.Errorf(\"got %q, want %q\", got, want)\n\t}\n}\n\nfunc makeDelayedServer(delay time.Duration) *httptest.Server {\n\treturn httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\ttime.Sleep(delay)\n\t\tw.WriteHeader(http.StatusOK)\n\t}))\n}\n```\n\nWe've refactored creating our fake servers into a function called `makeDelayedServer` to move some uninteresting code out of the test and reduce repetition.\n\n### `defer`\n\nBy prefixing a function call with `defer` it will now call that function _at the end of the containing function_.\n\nSometimes you will need to clean up resources, such as closing a file or in our case closing a server so that it does not continue to listen to a port.\n\nYou want this to execute at the end of the function, but keep the instruction near where you created the server for the benefit of future readers of the code.\n\nOur refactoring is an improvement and is a reasonable solution given the Go features covered so far, but we can make the solution simpler.\n\n### Synchronising processes\n\n- Why are we testing the speeds of the websites one after another when Go is great at concurrency? We should be able to check both at the same time.\n- We don't really care about _the exact response times_ of the requests, we just want to know which one comes back first.\n\nTo do this, we're going to introduce a new construct called `select` which helps us synchronise processes really easily and clearly.\n\n```go\nfunc Racer(a, b string) (winner string) {\n\tselect {\n\tcase <-ping(a):\n\t\treturn a\n\tcase <-ping(b):\n\t\treturn b\n\t}\n}\n\nfunc ping(url string) chan struct{} {\n\tch := make(chan struct{})\n\tgo func() {\n\t\thttp.Get(url)\n\t\tclose(ch)\n\t}()\n\treturn ch\n}\n```\n\n#### `ping`\n\nWe have defined a function `ping` which creates a `chan struct{}` and returns it.\n\nIn our case, we don't _care_ what type is sent to the channel, _we just want to signal we are done_ and closing the channel works perfectly!\n\nWhy `struct{}` and not another type like a `bool`? Well, a `chan struct{}` is the smallest data type available from a memory perspective so we\nget no allocation versus a `bool`. Since we are closing and not sending anything on the chan, why allocate anything?\n\nInside the same function, we start a goroutine which will send a signal into that channel once we have completed `http.Get(url)`.\n\n##### Always `make` channels\n\nNotice how we have to use `make` when creating a channel; rather than say `var ch chan struct{}`. When you use `var` the variable will be initialised with the \"zero\" value of the type. So for `string` it is `\"\"`, `int` it is 0, etc.\n\nFor channels the zero value is `nil` and if you try and send to it with `<-` it will block forever because you cannot send to `nil` channels\n\n[You can see this in action in The Go Playground](https://play.golang.org/p/IIbeAox5jKA)\n#### `select`\n\nYou'll recall from the concurrency chapter that you can wait for values to be sent to a channel with `myVar := <-ch`. This is a _blocking_ call, as you're waiting for a value.\n\n`select` allows you to wait on _multiple_ channels. The first one to send a value \"wins\" and the code underneath the `case` is executed.\n\nWe use `ping` in our `select` to set up two channels, one for each of our `URL`s. Whichever one writes to its channel first will have its code executed in the `select`, which results in its `URL` being returned (and being the winner).\n\nAfter these changes, the intent behind our code is very clear and the implementation is actually simpler.\n\n### Timeouts\n\nOur final requirement was to return an error if `Racer` takes longer than 10 seconds.\n\n## Write the test first\n\n```go\nfunc TestRacer(t *testing.T) {\n\tt.Run(\"compares speeds of servers, returning the url of the fastest one\", func(t *testing.T) {\n\t\tslowServer := makeDelayedServer(20 * time.Millisecond)\n\t\tfastServer := makeDelayedServer(0 * time.Millisecond)\n\n\t\tdefer slowServer.Close()\n\t\tdefer fastServer.Close()\n\n\t\tslowURL := slowServer.URL\n\t\tfastURL := fastServer.URL\n\n\t\twant := fastURL\n\t\tgot, _ := Racer(slowURL, fastURL)\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %q, want %q\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"returns an error if a server doesn't respond within 10s\", func(t *testing.T) {\n\t\tserverA := makeDelayedServer(11 * time.Second)\n\t\tserverB := makeDelayedServer(12 * time.Second)\n\n\t\tdefer serverA.Close()\n\t\tdefer serverB.Close()\n\n\t\t_, err := Racer(serverA.URL, serverB.URL)\n\n\t\tif err == nil {\n\t\t\tt.Error(\"expected an error but didn't get one\")\n\t\t}\n\t})\n}\n```\n\nWe've made our test servers take longer than 10s to return to exercise this scenario and we are expecting `Racer` to return two values now, the winning URL (which we ignore in this test with `_`) and an `error`.\n\nNote that we've also handled the error return in our original test, we're using\t`_` for now to ensure the tests will run.\n\n## Try to run the test\n\n`./racer_test.go:37:10: assignment mismatch: 2 variables but Racer returns 1 value`\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\n```go\nfunc Racer(a, b string) (winner string, error error) {\n\tselect {\n\tcase <-ping(a):\n\t\treturn a, nil\n\tcase <-ping(b):\n\t\treturn b, nil\n\t}\n}\n```\n\nChange the signature of `Racer` to return the winner and an `error`. Return `nil` for our happy cases.\n\nThe compiler will complain about your _first test_ only looking for one value so change that line to `got, err := Racer(slowURL, fastURL)`, knowing that we should check we _don't_ get an error in our happy scenario.\n\nIf you run it now after 11 seconds it will fail.\n\n```\n--- FAIL: TestRacer (12.00s)\n    --- FAIL: TestRacer/returns_an_error_if_a_server_doesn't_respond_within_10s (12.00s)\n        racer_test.go:40: expected an error but didn't get one\n```\n\n## Write enough code to make it pass\n\n```go\nfunc Racer(a, b string) (winner string, error error) {\n\tselect {\n\tcase <-ping(a):\n\t\treturn a, nil\n\tcase <-ping(b):\n\t\treturn b, nil\n\tcase <-time.After(10 * time.Second):\n\t\treturn \"\", fmt.Errorf(\"timed out waiting for %s and %s\", a, b)\n\t}\n}\n```\n\n`time.After` is a very handy function when using `select`. Although it didn't happen in our case you can potentially write code that blocks forever if the channels you're listening on never return a value. `time.After` returns a `chan` (like `ping`) and will send a signal down it after the amount of time you define.\n\nFor us this is perfect; if `a` or `b` manage to return they win, but if we get to 10 seconds then our `time.After` will send a signal and we'll return an `error`.\n\n### Slow tests\n\nThe problem we have is that this test takes 10 seconds to run. For such a simple bit of logic, this doesn't feel great.\n\nWhat we can do is make the timeout configurable. So in our test, we can have a very short timeout and then when the code is used in the real world it can be set to 10 seconds.\n\n```go\nfunc Racer(a, b string, timeout time.Duration) (winner string, error error) {\n\tselect {\n\tcase <-ping(a):\n\t\treturn a, nil\n\tcase <-ping(b):\n\t\treturn b, nil\n\tcase <-time.After(timeout):\n\t\treturn \"\", fmt.Errorf(\"timed out waiting for %s and %s\", a, b)\n\t}\n}\n```\n\nOur tests now won't compile because we're not supplying a timeout.\n\nBefore rushing in to add this default value to both our tests let's _listen to them_.\n\n- Do we care about the timeout in the \"happy\" test?\n- The requirements were explicit about the timeout.\n\nGiven this knowledge, let's do a little refactoring to be sympathetic to both our tests and the users of our code.\n\n```go\nvar tenSecondTimeout = 10 * time.Second\n\nfunc Racer(a, b string) (winner string, error error) {\n\treturn ConfigurableRacer(a, b, tenSecondTimeout)\n}\n\nfunc ConfigurableRacer(a, b string, timeout time.Duration) (winner string, error error) {\n\tselect {\n\tcase <-ping(a):\n\t\treturn a, nil\n\tcase <-ping(b):\n\t\treturn b, nil\n\tcase <-time.After(timeout):\n\t\treturn \"\", fmt.Errorf(\"timed out waiting for %s and %s\", a, b)\n\t}\n}\n```\n\nOur users and our first test can use `Racer` (which uses `ConfigurableRacer` under the hood) and our sad path test can use `ConfigurableRacer`.\n\n```go\nfunc TestRacer(t *testing.T) {\n\n\tt.Run(\"compares speeds of servers, returning the url of the fastest one\", func(t *testing.T) {\n\t\tslowServer := makeDelayedServer(20 * time.Millisecond)\n\t\tfastServer := makeDelayedServer(0 * time.Millisecond)\n\n\t\tdefer slowServer.Close()\n\t\tdefer fastServer.Close()\n\n\t\tslowURL := slowServer.URL\n\t\tfastURL := fastServer.URL\n\n\t\twant := fastURL\n\t\tgot, err := Racer(slowURL, fastURL)\n\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"did not expect an error but got one %v\", err)\n\t\t}\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %q, want %q\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"returns an error if a server doesn't respond within the specified time\", func(t *testing.T) {\n\t\tserver := makeDelayedServer(25 * time.Millisecond)\n\n\t\tdefer server.Close()\n\n\t\t_, err := ConfigurableRacer(server.URL, server.URL, 20*time.Millisecond)\n\n\t\tif err == nil {\n\t\t\tt.Error(\"expected an error but didn't get one\")\n\t\t}\n\t})\n}\n```\n\nI added one final check on the first test to verify we don't get an `error`.\n\n## Wrapping up\n\n### `select`\n\n- Helps you wait on multiple channels.\n- Sometimes you'll want to include `time.After` in one of your `cases` to prevent your system blocking forever.\n\n### `httptest`\n\n- A convenient way of creating test servers so you can have reliable and controllable tests.\n- Uses the same interfaces as the \"real\" `net/http` servers which is consistent and less for you to learn.\n"
  },
  {
    "path": "structs/v1/shapes.go",
    "content": "package main\n\n// Perimeter returns the perimeter of a rectangle.\nfunc Perimeter(width float64, height float64) float64 {\n\treturn 2 * (width + height)\n}\n"
  },
  {
    "path": "structs/v1/shapes_test.go",
    "content": "package main\n\nimport \"testing\"\n\nfunc TestPerimeter(t *testing.T) {\n\tgot := Perimeter(10.0, 10.0)\n\twant := 40.0\n\n\tif got != want {\n\t\tt.Errorf(\"got %.2f want %.2f\", got, want)\n\t}\n}\n"
  },
  {
    "path": "structs/v2/shapes.go",
    "content": "package main\n\n// Perimeter returns the perimeter of a rectangle.\nfunc Perimeter(width float64, height float64) float64 {\n\treturn 2 * (width + height)\n}\n\n// Area returns the area of a rectangle.\nfunc Area(width float64, height float64) float64 {\n\treturn width * height\n}\n"
  },
  {
    "path": "structs/v2/shapes_test.go",
    "content": "package main\n\nimport \"testing\"\n\nfunc TestPerimeter(t *testing.T) {\n\tgot := Perimeter(10.0, 10.0)\n\twant := 40.0\n\n\tif got != want {\n\t\tt.Errorf(\"got %.2f want %.2f\", got, want)\n\t}\n}\n\nfunc TestArea(t *testing.T) {\n\tgot := Area(12.0, 6.0)\n\twant := 72.0\n\n\tif got != want {\n\t\tt.Errorf(\"got %.2f want %.2f\", got, want)\n\t}\n}\n"
  },
  {
    "path": "structs/v3/shapes.go",
    "content": "package main\n\n// Rectangle has the dimensions of a rectangle.\ntype Rectangle struct {\n\tWidth  float64\n\tHeight float64\n}\n\n// Perimeter returns the perimeter of the rectangle.\nfunc Perimeter(rectangle Rectangle) float64 {\n\treturn 2 * (rectangle.Width + rectangle.Height)\n}\n\n// Area returns the area of the rectangle.\nfunc Area(rectangle Rectangle) float64 {\n\treturn rectangle.Width * rectangle.Height\n}\n"
  },
  {
    "path": "structs/v3/shapes_test.go",
    "content": "package main\n\nimport \"testing\"\n\nfunc TestPerimeter(t *testing.T) {\n\trectangle := Rectangle{10.0, 10.0}\n\tgot := Perimeter(rectangle)\n\twant := 40.0\n\n\tif got != want {\n\t\tt.Errorf(\"got %.2f want %.2f\", got, want)\n\t}\n}\n\nfunc TestArea(t *testing.T) {\n\trectangle := Rectangle{12.0, 6.0}\n\tgot := Area(rectangle)\n\twant := 72.0\n\n\tif got != want {\n\t\tt.Errorf(\"got %.2f want %.2f\", got, want)\n\t}\n}\n"
  },
  {
    "path": "structs/v4/shapes.go",
    "content": "package main\n\nimport \"math\"\n\n// Rectangle has the dimensions of a rectangle.\ntype Rectangle struct {\n\tWidth  float64\n\tHeight float64\n}\n\n// Area returns the area of the rectangle.\nfunc (r Rectangle) Area() float64 {\n\treturn r.Width * r.Height\n}\n\n// Perimeter returns the perimeter of a rectangle.\nfunc Perimeter(rectangle Rectangle) float64 {\n\treturn 2 * (rectangle.Width + rectangle.Height)\n}\n\n// Circle represents a circle...\ntype Circle struct {\n\tRadius float64\n}\n\n// Area returns the area of the circle.\nfunc (c Circle) Area() float64 {\n\treturn math.Pi * c.Radius * c.Radius\n}\n"
  },
  {
    "path": "structs/v4/shapes_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n)\n\nfunc TestPerimeter(t *testing.T) {\n\trectangle := Rectangle{10.0, 10.0}\n\tgot := Perimeter(rectangle)\n\twant := 40.0\n\n\tif got != want {\n\t\tt.Errorf(\"got %g want %g\", got, want)\n\t}\n}\n\nfunc TestArea(t *testing.T) {\n\n\tt.Run(\"rectangles\", func(t *testing.T) {\n\t\trectangle := Rectangle{12, 6}\n\t\tgot := rectangle.Area()\n\t\twant := 72.0\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %g want %g\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"circles\", func(t *testing.T) {\n\t\tcircle := Circle{10}\n\t\tgot := circle.Area()\n\t\twant := 314.1592653589793\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %g want %g\", got, want)\n\t\t}\n\t})\n\n}\n"
  },
  {
    "path": "structs/v5/shapes.go",
    "content": "package main\n\nimport \"math\"\n\n// Shape is implemented by anything that can tell us its Area.\ntype Shape interface {\n\tArea() float64\n}\n\n// Rectangle has the dimensions of a rectangle.\ntype Rectangle struct {\n\tWidth  float64\n\tHeight float64\n}\n\n// Area returns the area of the rectangle.\nfunc (r Rectangle) Area() float64 {\n\treturn r.Width * r.Height\n}\n\n// Perimeter returns the perimeter of a rectangle.\nfunc Perimeter(rectangle Rectangle) float64 {\n\treturn 2 * (rectangle.Width + rectangle.Height)\n}\n\n// Circle represents a circle...\ntype Circle struct {\n\tRadius float64\n}\n\n// Area returns the area of the circle.\nfunc (c Circle) Area() float64 {\n\treturn math.Pi * c.Radius * c.Radius\n}\n"
  },
  {
    "path": "structs/v5/shapes_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n)\n\nfunc TestPerimeter(t *testing.T) {\n\trectangle := Rectangle{10.0, 10.0}\n\tgot := Perimeter(rectangle)\n\twant := 40.0\n\n\tif got != want {\n\t\tt.Errorf(\"got %g want %g\", got, want)\n\t}\n}\n\nfunc TestArea(t *testing.T) {\n\n\tcheckArea := func(t testing.TB, shape Shape, want float64) {\n\t\tt.Helper()\n\t\tgot := shape.Area()\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %g want %g\", got, want)\n\t\t}\n\t}\n\n\tt.Run(\"rectangles\", func(t *testing.T) {\n\t\trectangle := Rectangle{12, 6}\n\t\tcheckArea(t, rectangle, 72.0)\n\t})\n\n\tt.Run(\"circles\", func(t *testing.T) {\n\t\tcircle := Circle{10}\n\t\tcheckArea(t, circle, 314.1592653589793)\n\t})\n\n}\n"
  },
  {
    "path": "structs/v6/shapes.go",
    "content": "package main\n\nimport \"math\"\n\n// Shape is implemented by anything that can tell us its Area.\ntype Shape interface {\n\tArea() float64\n}\n\n// Rectangle has the dimensions of a rectangle.\ntype Rectangle struct {\n\tWidth  float64\n\tHeight float64\n}\n\n// Area returns the area of the rectangle.\nfunc (r Rectangle) Area() float64 {\n\treturn r.Width * r.Height\n}\n\n// Perimeter returns the perimeter of a rectangle.\nfunc Perimeter(rectangle Rectangle) float64 {\n\treturn 2 * (rectangle.Width + rectangle.Height)\n}\n\n// Circle represents a circle...\ntype Circle struct {\n\tRadius float64\n}\n\n// Area returns the area of the circle.\nfunc (c Circle) Area() float64 {\n\treturn math.Pi * c.Radius * c.Radius\n}\n"
  },
  {
    "path": "structs/v6/shapes_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n)\n\nfunc TestPerimeter(t *testing.T) {\n\trectangle := Rectangle{10.0, 10.0}\n\tgot := Perimeter(rectangle)\n\twant := 40.0\n\n\tif got != want {\n\t\tt.Errorf(\"got %g want %g\", got, want)\n\t}\n}\n\nfunc TestArea(t *testing.T) {\n\n\tareaTests := []struct {\n\t\tshape Shape\n\t\twant  float64\n\t}{\n\t\t{Rectangle{12, 6}, 72.0},\n\t\t{Circle{10}, 314.1592653589793},\n\t}\n\n\tfor _, tt := range areaTests {\n\t\tgot := tt.shape.Area()\n\t\tif got != tt.want {\n\t\t\tt.Errorf(\"got %g want %g\", got, tt.want)\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "structs/v7/shapes.go",
    "content": "package main\n\nimport \"math\"\n\n// Shape is implemented by anything that can tell us its Area.\ntype Shape interface {\n\tArea() float64\n}\n\n// Rectangle has the dimensions of a rectangle.\ntype Rectangle struct {\n\tWidth  float64\n\tHeight float64\n}\n\n// Area returns the area of the rectangle.\nfunc (r Rectangle) Area() float64 {\n\treturn r.Width * r.Height\n}\n\n// Perimeter returns the perimeter of a rectangle.\nfunc Perimeter(rectangle Rectangle) float64 {\n\treturn 2 * (rectangle.Width + rectangle.Height)\n}\n\n// Circle represents a circle...\ntype Circle struct {\n\tRadius float64\n}\n\n// Area returns the area of the circle.\nfunc (c Circle) Area() float64 {\n\treturn math.Pi * c.Radius * c.Radius\n}\n\n// Triangle represents the dimensions of a triangle.\ntype Triangle struct {\n\tBase   float64\n\tHeight float64\n}\n\n// Area returns the area of the triangle.\nfunc (c Triangle) Area() float64 {\n\treturn (c.Base * c.Height) * 0.5\n}\n"
  },
  {
    "path": "structs/v7/shapes_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n)\n\nfunc TestPerimeter(t *testing.T) {\n\trectangle := Rectangle{10.0, 10.0}\n\tgot := Perimeter(rectangle)\n\twant := 40.0\n\n\tif got != want {\n\t\tt.Errorf(\"got %g want %g\", got, want)\n\t}\n}\n\nfunc TestArea(t *testing.T) {\n\n\tareaTests := []struct {\n\t\tshape Shape\n\t\twant  float64\n\t}{\n\t\t{Rectangle{12, 6}, 72.0},\n\t\t{Circle{10}, 314.1592653589793},\n\t\t{Triangle{12, 6}, 36.0},\n\t}\n\n\tfor _, tt := range areaTests {\n\t\tgot := tt.shape.Area()\n\t\tif got != tt.want {\n\t\t\tt.Errorf(\"got %g want %g\", got, tt.want)\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "structs/v8/shapes.go",
    "content": "package main\n\nimport \"math\"\n\n// Shape is implemented by anything that can tell us its Area.\ntype Shape interface {\n\tArea() float64\n}\n\n// Rectangle has the dimensions of a rectangle.\ntype Rectangle struct {\n\tWidth  float64\n\tHeight float64\n}\n\n// Area returns the area of the rectangle.\nfunc (r Rectangle) Area() float64 {\n\treturn r.Width * r.Height\n}\n\n// Perimeter returns the perimeter of a rectangle.\nfunc Perimeter(rectangle Rectangle) float64 {\n\treturn 2 * (rectangle.Width + rectangle.Height)\n}\n\n// Circle represents a circle...\ntype Circle struct {\n\tRadius float64\n}\n\n// Area returns the area of the circle.\nfunc (c Circle) Area() float64 {\n\treturn math.Pi * c.Radius * c.Radius\n}\n\n// Triangle represents the dimensions of a triangle.\ntype Triangle struct {\n\tBase   float64\n\tHeight float64\n}\n\n// Area returns the area of the triangle.\nfunc (t Triangle) Area() float64 {\n\treturn (t.Base * t.Height) * 0.5\n}\n"
  },
  {
    "path": "structs/v8/shapes_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n)\n\nfunc TestPerimeter(t *testing.T) {\n\trectangle := Rectangle{10.0, 10.0}\n\tgot := Perimeter(rectangle)\n\twant := 40.0\n\n\tif got != want {\n\t\tt.Errorf(\"got %g want %g\", got, want)\n\t}\n}\n\nfunc TestArea(t *testing.T) {\n\n\tareaTests := []struct {\n\t\tname    string\n\t\tshape   Shape\n\t\thasArea float64\n\t}{\n\t\t{name: \"Rectangle\", shape: Rectangle{Width: 12, Height: 6}, hasArea: 72.0},\n\t\t{name: \"Circle\", shape: Circle{Radius: 10}, hasArea: 314.1592653589793},\n\t\t{name: \"Triangle\", shape: Triangle{Base: 12, Height: 6}, hasArea: 36.0},\n\t}\n\n\tfor _, tt := range areaTests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot := tt.shape.Area()\n\t\t\tif got != tt.hasArea {\n\t\t\t\tt.Errorf(\"%#v got %g want %g\", tt.shape, got, tt.hasArea)\n\t\t\t}\n\t\t})\n\n\t}\n\n}\n"
  },
  {
    "path": "structs-methods-and-interfaces.md",
    "content": "# Structs, methods & interfaces\n\n**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/main/structs)**\n\nSuppose that we need some geometry code to calculate the perimeter of a rectangle given a height and width. We can write a `Perimeter(width float64, height float64)` function, where `float64` is for floating-point numbers like `123.45`.\n\nThe TDD cycle should be pretty familiar to you by now.\n\n## Write the test first\n\n```go\nfunc TestPerimeter(t *testing.T) {\n\tgot := Perimeter(10.0, 10.0)\n\twant := 40.0\n\n\tif got != want {\n\t\tt.Errorf(\"got %.2f want %.2f\", got, want)\n\t}\n}\n```\n\nNotice the new format string? The `f` is for our `float64` and the `.2` means print 2 decimal places.\n\n## Try to run the test\n\n`./shapes_test.go:6:9: undefined: Perimeter`\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\n```go\nfunc Perimeter(width float64, height float64) float64 {\n\treturn 0\n}\n```\n\nResults in `shapes_test.go:10: got 0.00 want 40.00`.\n\n## Write enough code to make it pass\n\n```go\nfunc Perimeter(width float64, height float64) float64 {\n\treturn 2 * (width + height)\n}\n```\n\nSo far, so easy. Now let's create a function called `Area(width, height float64)` which returns the area of a rectangle.\n\nTry to do it yourself, following the TDD cycle.\n\nYou should end up with tests like this\n\n```go\nfunc TestPerimeter(t *testing.T) {\n\tgot := Perimeter(10.0, 10.0)\n\twant := 40.0\n\n\tif got != want {\n\t\tt.Errorf(\"got %.2f want %.2f\", got, want)\n\t}\n}\n\nfunc TestArea(t *testing.T) {\n\tgot := Area(12.0, 6.0)\n\twant := 72.0\n\n\tif got != want {\n\t\tt.Errorf(\"got %.2f want %.2f\", got, want)\n\t}\n}\n```\n\nAnd code like this\n\n```go\nfunc Perimeter(width float64, height float64) float64 {\n\treturn 2 * (width + height)\n}\n\nfunc Area(width float64, height float64) float64 {\n\treturn width * height\n}\n```\n\n## Refactor\n\nOur code does the job, but it doesn't contain anything explicit about rectangles. An unwary developer might try to supply the width and height of a triangle to these functions without realising they will return the wrong answer.\n\nWe could just give the functions more specific names like `RectangleArea`. A neater solution is to define our own _type_ called `Rectangle` which encapsulates this concept for us.\n\nWe can create a simple type using a **struct**. [A struct](https://golang.org/ref/spec#Struct_types) is just a named collection of fields where you can store data.\n\nDeclare a struct in your `shapes.go` file like this\n\n```go\ntype Rectangle struct {\n\tWidth  float64\n\tHeight float64\n}\n```\n\nNow let's refactor the tests to use `Rectangle` instead of plain `float64`s.\n\n```go\nfunc TestPerimeter(t *testing.T) {\n\trectangle := Rectangle{10.0, 10.0}\n\tgot := Perimeter(rectangle)\n\twant := 40.0\n\n\tif got != want {\n\t\tt.Errorf(\"got %.2f want %.2f\", got, want)\n\t}\n}\n\nfunc TestArea(t *testing.T) {\n\trectangle := Rectangle{12.0, 6.0}\n\tgot := Area(rectangle)\n\twant := 72.0\n\n\tif got != want {\n\t\tt.Errorf(\"got %.2f want %.2f\", got, want)\n\t}\n}\n```\n\nRemember to run your tests before attempting to fix. The tests should show a helpful error like\n\n```text\n./shapes_test.go:7:18: not enough arguments in call to Perimeter\n    have (Rectangle)\n    want (float64, float64)\n```\n\nYou can access the fields of a struct with the syntax of `myStruct.field`.\n\nChange the two functions to fix the test.\n\n```go\nfunc Perimeter(rectangle Rectangle) float64 {\n\treturn 2 * (rectangle.Width + rectangle.Height)\n}\n\nfunc Area(rectangle Rectangle) float64 {\n\treturn rectangle.Width * rectangle.Height\n}\n```\n\nI hope you'll agree that passing a `Rectangle` to a function conveys our intent more clearly, but there are more benefits of using structs that we will cover later.\n\nOur next requirement is to write an `Area` function for circles.\n\n## Write the test first\n\n```go\nfunc TestArea(t *testing.T) {\n\n\tt.Run(\"rectangles\", func(t *testing.T) {\n\t\trectangle := Rectangle{12, 6}\n\t\tgot := Area(rectangle)\n\t\twant := 72.0\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %g want %g\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"circles\", func(t *testing.T) {\n\t\tcircle := Circle{10}\n\t\tgot := Area(circle)\n\t\twant := 314.1592653589793\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %g want %g\", got, want)\n\t\t}\n\t})\n\n}\n```\n\nAs you can see, the `f` has been replaced by `g`, with good reason.\nUse of `g` will print a more precise decimal number in the error message \\([fmt options](https://golang.org/pkg/fmt/)\\).\nFor example, using a radius of 1.5 in a circle area calculation, `f` would show `7.068583` whereas `g` would show `7.0685834705770345`.\n\n## Try to run the test\n\n`./shapes_test.go:28:13: undefined: Circle`\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\nWe need to define our `Circle` type.\n\n```go\ntype Circle struct {\n\tRadius float64\n}\n```\n\nNow try to run the tests again\n\n`./shapes_test.go:29:14: cannot use circle (type Circle) as type Rectangle in argument to Area`\n\nSome programming languages allow you to do something like this:\n\n```go\nfunc Area(circle Circle) float64       {}\nfunc Area(rectangle Rectangle) float64 {}\n```\n\nBut you cannot in Go\n\n`./shapes.go:20:32: Area redeclared in this block`\n\nWe have two choices:\n\n* You can have functions with the same name declared in different _packages_. So we could create our `Area(Circle)` in a new package, but that feels overkill here.\n* We can define [_methods_](https://golang.org/ref/spec#Method_declarations) on our newly defined types instead.\n\n### What are methods?\n\nSo far we have only been writing _functions_ but we have been using some methods. When we call `t.Errorf` we are calling the method `Errorf` on the instance of our `t` \\(`testing.T`\\).\n\nA method is a function with a receiver.\nA method declaration binds an identifier, the method name, to a method, and associates the method with the receiver's base type.\n\nMethods are very similar to functions but they are called by invoking them on an instance of a particular type. Where you can just call functions wherever you like, such as `Area(rectangle)` you can only call methods on \"things\".\n\nAn example will help so let's change our tests first to call methods instead and then fix the code.\n\n```go\nfunc TestArea(t *testing.T) {\n\n\tt.Run(\"rectangles\", func(t *testing.T) {\n\t\trectangle := Rectangle{12, 6}\n\t\tgot := rectangle.Area()\n\t\twant := 72.0\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %g want %g\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"circles\", func(t *testing.T) {\n\t\tcircle := Circle{10}\n\t\tgot := circle.Area()\n\t\twant := 314.1592653589793\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %g want %g\", got, want)\n\t\t}\n\t})\n\n}\n```\n\nIf we try to run the tests, we get\n\n```text\n./shapes_test.go:19:19: rectangle.Area undefined (type Rectangle has no field or method Area)\n./shapes_test.go:29:16: circle.Area undefined (type Circle has no field or method Area)\n```\n\n> type Circle has no field or method Area\n\nI would like to reiterate how great the compiler is here. It is so important to take the time to slowly read the error messages you get, it will help you in the long run.\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\nLet's add some methods to our types\n\n```go\ntype Rectangle struct {\n\tWidth  float64\n\tHeight float64\n}\n\nfunc (r Rectangle) Area() float64 {\n\treturn 0\n}\n\ntype Circle struct {\n\tRadius float64\n}\n\nfunc (c Circle) Area() float64 {\n\treturn 0\n}\n```\n\nThe syntax for declaring methods is almost the same as functions and that's because they're so similar. The only difference is the syntax of the method receiver `func (receiverName ReceiverType) MethodName(args)`.\n\nWhen your method is called on a variable of that type, you get your reference to its data via the `receiverName` variable. In many other programming languages this is done implicitly and you access the receiver via `this`.\n\nIt is a convention in Go to have the receiver variable be the first letter of the type.\n\n```\nr Rectangle\n```\n\nIf you try to re-run the tests they should now compile and give you some failing output.\n\n## Write enough code to make it pass\n\nNow let's make our rectangle tests pass by fixing our new method\n\n```go\nfunc (r Rectangle) Area() float64 {\n\treturn r.Width * r.Height\n}\n```\n\nIf you re-run the tests the rectangle tests should be passing but circle should still be failing.\n\nTo make circle's `Area` function pass we will borrow the `Pi` constant from the `math` package \\(remember to import it\\).\n\n```go\nfunc (c Circle) Area() float64 {\n\treturn math.Pi * c.Radius * c.Radius\n}\n```\n\n## Refactor\n\nThere is some duplication in our tests.\n\nAll we want to do is take a collection of _shapes_, call the `Area()` method on them and then check the result.\n\nWe want to be able to write some kind of `checkArea` function that we can pass both `Rectangle`s and `Circle`s to, but fail to compile if we try to pass in something that isn't a shape.\n\nWith Go, we can codify this intent with **interfaces**.\n\n[Interfaces](https://golang.org/ref/spec#Interface_types) are a very powerful concept in statically typed languages like Go because they allow you to make functions that can be used with different types and create highly-decoupled code whilst still maintaining type-safety.\n\nLet's introduce this by refactoring our tests.\n\n```go\nfunc TestArea(t *testing.T) {\n\n\tcheckArea := func(t testing.TB, shape Shape, want float64) {\n\t\tt.Helper()\n\t\tgot := shape.Area()\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %g want %g\", got, want)\n\t\t}\n\t}\n\n\tt.Run(\"rectangles\", func(t *testing.T) {\n\t\trectangle := Rectangle{12, 6}\n\t\tcheckArea(t, rectangle, 72.0)\n\t})\n\n\tt.Run(\"circles\", func(t *testing.T) {\n\t\tcircle := Circle{10}\n\t\tcheckArea(t, circle, 314.1592653589793)\n\t})\n\n}\n```\n\nWe are creating a helper function like we have in other exercises but this time we are asking for a `Shape` to be passed in. If we try to call this with something that isn't a shape, then it will not compile.\n\nHow does something become a shape? We just tell Go what a `Shape` is using an interface declaration\n\n```go\ntype Shape interface {\n\tArea() float64\n}\n```\n\nWe're creating a new `type` just like we did with `Rectangle` and `Circle` but this time it is an `interface` rather than a `struct`.\n\nOnce you add this to the code, the tests will pass.\n\n### Wait, what?\n\nThis is quite different to interfaces in most other programming languages. Normally you have to write code to say `My type Foo implements interface Bar`.\n\nBut in our case\n\n* `Rectangle` has a method called `Area` that returns a `float64` so it satisfies the `Shape` interface\n* `Circle` has a method called `Area` that returns a `float64` so it satisfies the `Shape` interface\n* `string` does not have such a method, so it doesn't satisfy the interface\n* etc.\n\nIn Go **interface resolution is implicit**. If the type you pass in matches what the interface is asking for, it will compile.\n\n### Decoupling\n\nNotice how our helper does not need to concern itself with whether the shape is a `Rectangle` or a `Circle` or a `Triangle`. By declaring an interface, the helper is _decoupled_ from the concrete types and only has the method it needs to do its job.\n\nThis kind of approach of using interfaces to declare **only what you need** is very important in software design and will be covered in more detail in later sections.\n\n## Further refactoring\n\nNow that you have some understanding of structs we can introduce \"table driven tests\".\n\n[Table driven tests](https://go.dev/wiki/TableDrivenTests) are useful when you want to build a list of test cases that can be tested in the same manner.\n\n```go\nfunc TestArea(t *testing.T) {\n\n\tareaTests := []struct {\n\t\tshape Shape\n\t\twant  float64\n\t}{\n\t\t{Rectangle{12, 6}, 72.0},\n\t\t{Circle{10}, 314.1592653589793},\n\t}\n\n\tfor _, tt := range areaTests {\n\t\tgot := tt.shape.Area()\n\t\tif got != tt.want {\n\t\t\tt.Errorf(\"got %g want %g\", got, tt.want)\n\t\t}\n\t}\n\n}\n```\n\nThe only new syntax here is creating an \"anonymous struct\", `areaTests`. We are declaring a slice of structs by using `[]struct` with two fields, the `shape` and the `want`. Then we fill the slice with cases.\n\nWe then iterate over them just like we do any other slice, using the struct fields to run our tests.\n\nYou can see how it would be very easy for a developer to introduce a new shape, implement `Area` and then add it to the test cases. In addition, if a bug is found with `Area` it is very easy to add a new test case to exercise it before fixing it.\n\nTable driven tests can be a great item in your toolbox, but be sure that you have a need for the extra noise in the tests.\nThey are a great fit when you wish to test various implementations of an interface, or if the data being passed in to a function has lots of different requirements that need testing.\n\nLet's demonstrate all this by adding another shape and testing it; a triangle.\n\n## Write the test first\n\nAdding a new test for our new shape is very easy. Just add `{Triangle{12, 6}, 36.0},` to our list.\n\n```go\nfunc TestArea(t *testing.T) {\n\n\tareaTests := []struct {\n\t\tshape Shape\n\t\twant  float64\n\t}{\n\t\t{Rectangle{12, 6}, 72.0},\n\t\t{Circle{10}, 314.1592653589793},\n\t\t{Triangle{12, 6}, 36.0},\n\t}\n\n\tfor _, tt := range areaTests {\n\t\tgot := tt.shape.Area()\n\t\tif got != tt.want {\n\t\t\tt.Errorf(\"got %g want %g\", got, tt.want)\n\t\t}\n\t}\n\n}\n```\n\n## Try to run the test\n\nRemember, keep trying to run the test and let the compiler guide you toward a solution.\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\n`./shapes_test.go:25:4: undefined: Triangle`\n\nWe have not defined `Triangle` yet\n\n```go\ntype Triangle struct {\n\tBase   float64\n\tHeight float64\n}\n```\n\nTry again\n\n```text\n./shapes_test.go:25:8: cannot use Triangle literal (type Triangle) as type Shape in field value:\n    Triangle does not implement Shape (missing Area method)\n```\n\nIt's telling us we cannot use a `Triangle` as a shape because it does not have an `Area()` method, so add an empty implementation to get the test working\n\n```go\nfunc (t Triangle) Area() float64 {\n\treturn 0\n}\n```\n\nFinally the code compiles and we get our error\n\n`shapes_test.go:31: got 0.00 want 36.00`\n\n## Write enough code to make it pass\n\n```go\nfunc (t Triangle) Area() float64 {\n\treturn (t.Base * t.Height) * 0.5\n}\n```\n\nAnd our tests pass!\n\n## Refactor\n\nAgain, the implementation is fine but our tests could do with some improvement.\n\nWhen you scan this\n\n```\n{Rectangle{12, 6}, 72.0},\n{Circle{10}, 314.1592653589793},\n{Triangle{12, 6}, 36.0},\n```\n\nIt's not immediately clear what all the numbers represent and you should be aiming for your tests to be easily understood.\n\nSo far you've only been shown syntax for creating instances of structs `MyStruct{val1, val2}` but you can optionally name the fields.\n\nLet's see what it looks like\n\n```\n        {shape: Rectangle{Width: 12, Height: 6}, want: 72.0},\n        {shape: Circle{Radius: 10}, want: 314.1592653589793},\n        {shape: Triangle{Base: 12, Height: 6}, want: 36.0},\n```\n\nIn [Test-Driven Development by Example](https://g.co/kgs/yCzDLF) Kent Beck refactors some tests to a point and asserts:\n\n> The test speaks to us more clearly, as if it were an assertion of truth, **not a sequence of operations**\n\n\\(emphasis in the quote is mine\\)\n\nNow our tests - rather, the list of test cases - make assertions of truth about shapes and their areas.\n\n## Make sure your test output is helpful\n\nRemember earlier when we were implementing `Triangle` and we had the failing test? It printed `shapes_test.go:31: got 0.00 want 36.00`.\n\nWe knew this was in relation to `Triangle` because we were just working with it.\nBut what if a bug slipped in to the system in one of 20 cases in the table?\nHow would a developer know which case failed?\nThis is not a great experience for the developer, they will have to manually look through the cases to find out which case actually failed.\n\nWe can change our error message into `%#v got %g want %g`. The `%#v` format string will print out our struct with the values in its field, so the developer can see at a glance the properties that are being tested.\n\nTo increase the readability of our test cases further, we can rename the `want` field into something more descriptive like `hasArea`.\n\nOne final tip with table driven tests is to use `t.Run` and to name the test cases.\n\nBy wrapping each case in a `t.Run` you will have clearer test output on failures as it will print the name of the case\n\n```text\n--- FAIL: TestArea (0.00s)\n    --- FAIL: TestArea/Rectangle (0.00s)\n        shapes_test.go:33: main.Rectangle{Width:12, Height:6} got 72.00 want 72.10\n```\n\nAnd you can run specific tests within your table with `go test -run TestArea/Rectangle`.\n\nHere is our final test code which captures this\n\n```go\nfunc TestArea(t *testing.T) {\n\n\tareaTests := []struct {\n\t\tname    string\n\t\tshape   Shape\n\t\thasArea float64\n\t}{\n\t\t{name: \"Rectangle\", shape: Rectangle{Width: 12, Height: 6}, hasArea: 72.0},\n\t\t{name: \"Circle\", shape: Circle{Radius: 10}, hasArea: 314.1592653589793},\n\t\t{name: \"Triangle\", shape: Triangle{Base: 12, Height: 6}, hasArea: 36.0},\n\t}\n\n\tfor _, tt := range areaTests {\n\t\t// using tt.name from the case to use it as the `t.Run` test name\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot := tt.shape.Area()\n\t\t\tif got != tt.hasArea {\n\t\t\t\tt.Errorf(\"%#v got %g want %g\", tt.shape, got, tt.hasArea)\n\t\t\t}\n\t\t})\n\n\t}\n\n}\n```\n\n## Wrapping up\n\nThis was more TDD practice, iterating over our solutions to basic mathematic problems and learning new language features motivated by our tests.\n\n* Declaring structs to create your own data types which lets you bundle related data together and make the intent of your code clearer\n* Declaring interfaces so you can define functions that can be used by different types \\([ad hoc polymorphism](https://en.wikipedia.org/wiki/Ad_hoc_polymorphism)\\)\n* Adding methods so you can add functionality to your data types and so you can implement interfaces\n* Table driven tests to make your assertions clearer and your test suites easier to extend & maintain\n\nThis was an important chapter because we are now starting to define our own types. In statically typed languages like Go, being able to design your own types is essential for building software that is easy to understand, to piece together and to test.\n\nInterfaces are a great tool for hiding complexity away from other parts of the system. In our case our test helper _code_ did not need to know the exact shape it was asserting on, only how to \"ask\" for its area.\n\nAs you become more familiar with Go you will start to see the real strength of interfaces and the standard library. You'll learn about interfaces defined in the standard library that are used _everywhere_ and by implementing them against your own types, you can very quickly re-use a lot of great functionality.\n"
  },
  {
    "path": "sync/v1/sync.go",
    "content": "package v1\n\n// Counter will increment a number.\ntype Counter struct {\n\tvalue int\n}\n\n// Inc the count.\nfunc (c *Counter) Inc() {\n\tc.value++\n}\n\n// Value returns the current count.\nfunc (c *Counter) Value() int {\n\treturn c.value\n}\n"
  },
  {
    "path": "sync/v1/sync_test.go",
    "content": "package v1\n\nimport (\n\t\"testing\"\n)\n\nfunc TestCounter(t *testing.T) {\n\n\tt.Run(\"incrementing the counter 3 times leaves it at 3\", func(t *testing.T) {\n\t\tcounter := Counter{}\n\t\tcounter.Inc()\n\t\tcounter.Inc()\n\t\tcounter.Inc()\n\n\t\tassertCounter(t, counter, 3)\n\t})\n}\n\nfunc assertCounter(t testing.TB, got Counter, want int) {\n\tt.Helper()\n\tif got.Value() != want {\n\t\tt.Errorf(\"got %d, want %d\", got.Value(), want)\n\t}\n}\n"
  },
  {
    "path": "sync/v2/sync.go",
    "content": "package v1\n\nimport \"sync\"\n\n// Counter will increment a number.\ntype Counter struct {\n\tmu    sync.Mutex\n\tvalue int\n}\n\n// NewCounter returns a new Counter.\nfunc NewCounter() *Counter {\n\treturn &Counter{}\n}\n\n// Inc the count.\nfunc (c *Counter) Inc() {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\tc.value++\n}\n\n// Value returns the current count.\nfunc (c *Counter) Value() int {\n\treturn c.value\n}\n"
  },
  {
    "path": "sync/v2/sync_test.go",
    "content": "package v1\n\nimport (\n\t\"sync\"\n\t\"testing\"\n)\n\nfunc TestCounter(t *testing.T) {\n\n\tt.Run(\"incrementing the counter 3 times leaves it at 3\", func(t *testing.T) {\n\t\tcounter := NewCounter()\n\t\tcounter.Inc()\n\t\tcounter.Inc()\n\t\tcounter.Inc()\n\n\t\tassertCounter(t, counter, 3)\n\t})\n\n\tt.Run(\"it runs safely concurrently\", func(t *testing.T) {\n\t\twantedCount := 1000\n\t\tcounter := NewCounter()\n\n\t\tvar wg sync.WaitGroup\n\t\twg.Add(wantedCount)\n\n\t\tfor i := 0; i < wantedCount; i++ {\n\t\t\tgo func() {\n\t\t\t\tcounter.Inc()\n\t\t\t\twg.Done()\n\t\t\t}()\n\t\t}\n\t\twg.Wait()\n\n\t\tassertCounter(t, counter, wantedCount)\n\t})\n\n}\n\nfunc assertCounter(t testing.TB, got *Counter, want int) {\n\tt.Helper()\n\tif got.Value() != want {\n\t\tt.Errorf(\"got %d, want %d\", got.Value(), want)\n\t}\n}\n"
  },
  {
    "path": "sync.md",
    "content": "# Sync\n\n**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/main/sync)**\n\nWe want to make a counter which is safe to use concurrently.\n\nWe'll start with an unsafe counter and verify its behaviour works in a single-threaded environment.\n\nThen we'll exercise its unsafeness, with multiple goroutines trying to use the counter via a test, and fix it.\n\n## Write the test first\n\nWe want our API to give us a method to increment the counter and then retrieve its value.\n\n```go\nfunc TestCounter(t *testing.T) {\n\tt.Run(\"incrementing the counter 3 times leaves it at 3\", func(t *testing.T) {\n\t\tcounter := Counter{}\n\t\tcounter.Inc()\n\t\tcounter.Inc()\n\t\tcounter.Inc()\n\n\t\tif counter.Value() != 3 {\n\t\t\tt.Errorf(\"got %d, want %d\", counter.Value(), 3)\n\t\t}\n\t})\n}\n```\n\n## Try to run the test\n\n```\n./sync_test.go:9:14: undefined: Counter\n```\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\nLet's define `Counter`.\n\n```go\ntype Counter struct {\n}\n```\n\nTry again and it fails with the following\n\n```\n./sync_test.go:14:10: counter.Inc undefined (type Counter has no field or method Inc)\n./sync_test.go:18:13: counter.Value undefined (type Counter has no field or method Value)\n```\n\nSo to finally make the test run we can define those methods\n\n```go\nfunc (c *Counter) Inc() {\n\n}\n\nfunc (c *Counter) Value() int {\n\treturn 0\n}\n```\n\nIt should now run and fail\n\n```\n=== RUN   TestCounter\n=== RUN   TestCounter/incrementing_the_counter_3_times_leaves_it_at_3\n--- FAIL: TestCounter (0.00s)\n    --- FAIL: TestCounter/incrementing_the_counter_3_times_leaves_it_at_3 (0.00s)\n    \tsync_test.go:27: got 0, want 3\n```\n\n## Write enough code to make it pass\n\nThis should be trivial for Go experts like us. We need to keep some state for the counter in our datatype and then increment it on every `Inc` call\n\n```go\ntype Counter struct {\n\tvalue int\n}\n\nfunc (c *Counter) Inc() {\n\tc.value++\n}\n\nfunc (c *Counter) Value() int {\n\treturn c.value\n}\n```\n\n## Refactor\n\nThere's not a lot to refactor but given we're going to write more tests around `Counter` we'll write a small assertion function `assertCount` so the test reads a bit clearer.\n\n```go\nt.Run(\"incrementing the counter 3 times leaves it at 3\", func(t *testing.T) {\n\tcounter := Counter{}\n\tcounter.Inc()\n\tcounter.Inc()\n\tcounter.Inc()\n\n\tassertCounter(t, counter, 3)\n})\n```\n```go\nfunc assertCounter(t testing.TB, got Counter, want int) {\n\tt.Helper()\n\tif got.Value() != want {\n\t\tt.Errorf(\"got %d, want %d\", got.Value(), want)\n\t}\n}\n```\n\n## Next steps\n\nThat was easy enough but now we have a requirement that it must be safe to use in a concurrent environment. We will need to write a failing test to exercise this.\n\n## Write the test first\n\n```go\nt.Run(\"it runs safely concurrently\", func(t *testing.T) {\n\twantedCount := 1000\n\tcounter := Counter{}\n\n\tvar wg sync.WaitGroup\n\twg.Add(wantedCount)\n\n\tfor i := 0; i < wantedCount; i++ {\n\t\tgo func() {\n\t\t\tcounter.Inc()\n\t\t\twg.Done()\n\t\t}()\n\t}\n\twg.Wait()\n\n\tassertCounter(t, counter, wantedCount)\n})\n```\n\nThis will loop through our `wantedCount` and fire a goroutine to call `counter.Inc()`.\n\nWe are using [`sync.WaitGroup`](https://golang.org/pkg/sync/#WaitGroup) which is a convenient way of synchronising concurrent processes.\n\n> A WaitGroup waits for a collection of goroutines to finish. The main goroutine calls Add to set the number of goroutines to wait for. Then each of the goroutines runs and calls Done when finished. At the same time, Wait can be used to block until all goroutines have finished.\n\nBy waiting for `wg.Wait()` to finish before making our assertions we can be sure all of our goroutines have attempted to `Inc` the `Counter`.\n\n## Try to run the test\n\n```\n=== RUN   TestCounter/it_runs_safely_in_a_concurrent_envionment\n--- FAIL: TestCounter (0.00s)\n    --- FAIL: TestCounter/it_runs_safely_in_a_concurrent_envionment (0.00s)\n    \tsync_test.go:26: got 939, want 1000\nFAIL\n```\n\nThe test will _probably_ fail with a different number, but nonetheless it demonstrates it does not work when multiple goroutines are trying to mutate the value of the counter at the same time.\n\n## Write enough code to make it pass\n\nA simple solution is to add a lock to our `Counter`, ensuring only one goroutine can increment the counter at a time. Go's [`Mutex`](https://golang.org/pkg/sync/#Mutex) provides such a lock:\n\n>A Mutex is a mutual exclusion lock. The zero value for a Mutex is an unlocked mutex.\n\n```go\ntype Counter struct {\n\tmu    sync.Mutex\n\tvalue int\n}\n\nfunc (c *Counter) Inc() {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\tc.value++\n}\n```\n\nWhat this means is any goroutine calling `Inc` will acquire the lock on `Counter` if they are first. All the other goroutines will have to wait for it to be `Unlock`ed before getting access.\n\nIf you now re-run the test it should now pass because each goroutine has to wait its turn before making a change.\n\n## I've seen other examples where the `sync.Mutex` is embedded into the struct.\n\nYou may see examples like this\n\n```go\ntype Counter struct {\n\tsync.Mutex\n\tvalue int\n}\n```\n\nIt can be argued that it can make the code a bit more elegant.\n\n```go\nfunc (c *Counter) Inc() {\n\tc.Lock()\n\tdefer c.Unlock()\n\tc.value++\n}\n```\n\nThis _looks_ nice but while programming is a hugely subjective discipline, this is **bad and wrong**.\n\nSometimes people forget that embedding types means the methods of that type become _part of the public interface_; and you often will not want that. Remember that we should be very careful with our public APIs, the moment we make something public is the moment other code can couple themselves to it. We always want to avoid unnecessary coupling.\n\nExposing `Lock` and `Unlock` is at best confusing but at worst potentially very harmful to your software if callers of your type start calling these methods.\n\n![Showing how a user of this API can wrongly change the state of the lock](https://i.imgur.com/SWYNpwm.png)\n\n_This seems like a really bad idea_\n\n## Copying mutexes\n\nOur test passes but our code is still a bit dangerous\n\nIf you run `go vet` on your code you should get an error like the following\n\n```\nsync/v2/sync_test.go:16: call of assertCounter copies lock value: v1.Counter contains sync.Mutex\nsync/v2/sync_test.go:39: assertCounter passes lock by value: v1.Counter contains sync.Mutex\n```\n\nA look at the documentation of [`sync.Mutex`](https://golang.org/pkg/sync/#Mutex) tells us why\n\n> A Mutex must not be copied after first use.\n\nWhen we pass our `Counter` (by value) to `assertCounter` it will try and create a copy of the mutex.\n\nTo solve this we should pass in a pointer to our `Counter` instead, so change the signature of `assertCounter`\n\n```go\nfunc assertCounter(t testing.TB, got *Counter, want int)\n```\n\nOur tests will no longer compile because we are trying to pass in a `Counter` rather than a `*Counter`. To solve this I prefer to create a constructor which shows readers of your API that it would be better to not initialise the type yourself.\n\n```go\nfunc NewCounter() *Counter {\n\treturn &Counter{}\n}\n```\n\nUse this function in your tests when initialising `Counter`.\n\n## Wrapping up\n\nWe've covered a few things from the [sync package](https://golang.org/pkg/sync/)\n\n- `Mutex` allows us to add locks to our data\n- `WaitGroup` is a means of waiting for goroutines to finish jobs\n\n### When to use locks over channels and goroutines?\n\n[We've previously covered goroutines in the first concurrency chapter](concurrency.md) which let us write safe concurrent code so why would you use locks?\n[The go wiki has a page dedicated to this topic; Mutex Or Channel](https://go.dev/wiki/MutexOrChannel)\n\n> A common Go newbie mistake is to over-use channels and goroutines just because it's possible, and/or because it's fun. Don't be afraid to use a sync.Mutex if that fits your problem best. Go is pragmatic in letting you use the tools that solve your problem best and not forcing you into one style of code.\n\nParaphrasing:\n\n- **Use channels when passing ownership of data**\n- **Use mutexes for managing state**\n\n### go vet\n\nRemember to use go vet in your build scripts as it can alert you to some subtle bugs in your code before they hit your poor users.\n\n### Don't use embedding because it's convenient\n\n- Think about the effect embedding has on your public API.\n- Do you _really_ want to expose these methods and have people coupling their own code to them?\n- With respect to mutexes, this could be potentially disastrous in very unpredictable and weird ways, imagine some nefarious code unlocking a mutex when it shouldn't be; this would cause some very strange bugs that will be hard to track down.\n"
  },
  {
    "path": "template.md",
    "content": "# Chapter template\n\nSome intro\n\n## Write the test first\n## Try to run the test\n## Write the minimal amount of code for the test to run and check the failing test output\n## Write enough code to make it pass\n## Refactor\n\n## Repeat for new requirements\n## Wrapping up\n"
  },
  {
    "path": "time/v1/CLI.go",
    "content": "package poker\n\nimport (\n\t\"bufio\"\n\t\"io\"\n\t\"strings\"\n\t\"time\"\n)\n\n// CLI helps players through a game of poker.\ntype CLI struct {\n\tplayerStore PlayerStore\n\tin          *bufio.Scanner\n\talerter     BlindAlerter\n}\n\n// NewCLI creates a CLI for playing poker.\nfunc NewCLI(store PlayerStore, in io.Reader, alerter BlindAlerter) *CLI {\n\treturn &CLI{\n\t\tplayerStore: store,\n\t\tin:          bufio.NewScanner(in),\n\t\talerter:     alerter,\n\t}\n}\n\n// PlayPoker starts the game.\nfunc (cli *CLI) PlayPoker() {\n\tcli.scheduleBlindAlerts()\n\tuserInput := cli.readLine()\n\tcli.playerStore.RecordWin(extractWinner(userInput))\n}\n\nfunc (cli *CLI) scheduleBlindAlerts() {\n\tblinds := []int{100, 200, 300, 400, 500, 600, 800, 1000, 2000, 4000, 8000}\n\tblindTime := 0 * time.Second\n\tfor _, blind := range blinds {\n\t\tcli.alerter.ScheduleAlertAt(blindTime, blind)\n\t\tblindTime = blindTime + 10*time.Minute\n\t}\n}\n\nfunc extractWinner(userInput string) string {\n\treturn strings.Replace(userInput, \" wins\", \"\", 1)\n}\n\nfunc (cli *CLI) readLine() string {\n\tcli.in.Scan()\n\treturn cli.in.Text()\n}\n"
  },
  {
    "path": "time/v1/CLI_test.go",
    "content": "package poker_test\n\nimport (\n\t\"fmt\"\n\t\"github.com/quii/learn-go-with-tests/time/v1\"\n\t\"io\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n)\n\ntype scheduledAlert struct {\n\tat     time.Duration\n\tamount int\n}\n\nfunc (s scheduledAlert) String() string {\n\treturn fmt.Sprintf(\"%d chips at %v\", s.amount, s.at)\n}\n\ntype SpyBlindAlerter struct {\n\talerts []scheduledAlert\n}\n\nfunc (s *SpyBlindAlerter) ScheduleAlertAt(at time.Duration, amount int) {\n\ts.alerts = append(s.alerts, scheduledAlert{at, amount})\n}\n\nvar dummySpyAlerter = &SpyBlindAlerter{}\n\nfunc TestCLI(t *testing.T) {\n\n\tt.Run(\"it schedules printing of blind values\", func(t *testing.T) {\n\t\tin := strings.NewReader(\"Chris wins\\n\")\n\t\tplayerStore := &poker.StubPlayerStore{}\n\t\tblindAlerter := &SpyBlindAlerter{}\n\n\t\tcli := poker.NewCLI(playerStore, in, blindAlerter)\n\t\tcli.PlayPoker()\n\n\t\tcases := []scheduledAlert{\n\t\t\t{0 * time.Second, 100},\n\t\t\t{10 * time.Minute, 200},\n\t\t\t{20 * time.Minute, 300},\n\t\t\t{30 * time.Minute, 400},\n\t\t\t{40 * time.Minute, 500},\n\t\t\t{50 * time.Minute, 600},\n\t\t\t{60 * time.Minute, 800},\n\t\t\t{70 * time.Minute, 1000},\n\t\t\t{80 * time.Minute, 2000},\n\t\t\t{90 * time.Minute, 4000},\n\t\t\t{100 * time.Minute, 8000},\n\t\t}\n\n\t\tfor i, want := range cases {\n\t\t\tt.Run(fmt.Sprint(want), func(t *testing.T) {\n\n\t\t\t\tif len(blindAlerter.alerts) <= i {\n\t\t\t\t\tt.Fatalf(\"alert %d was not scheduled %v\", i, blindAlerter.alerts)\n\t\t\t\t}\n\n\t\t\t\tgot := blindAlerter.alerts[i]\n\t\t\t\tassertScheduledAlert(t, got, want)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"record chris win from user input\", func(t *testing.T) {\n\t\tin := strings.NewReader(\"Chris wins\\n\")\n\t\tplayerStore := &poker.StubPlayerStore{}\n\n\t\tcli := poker.NewCLI(playerStore, in, dummySpyAlerter)\n\t\tcli.PlayPoker()\n\n\t\tpoker.AssertPlayerWin(t, playerStore, \"Chris\")\n\t})\n\n\tt.Run(\"record cleo win from user input\", func(t *testing.T) {\n\t\tin := strings.NewReader(\"Cleo wins\\n\")\n\t\tplayerStore := &poker.StubPlayerStore{}\n\n\t\tcli := poker.NewCLI(playerStore, in, dummySpyAlerter)\n\t\tcli.PlayPoker()\n\n\t\tpoker.AssertPlayerWin(t, playerStore, \"Cleo\")\n\t})\n\n\tt.Run(\"do not read beyond the first newline\", func(t *testing.T) {\n\t\tin := failOnEndReader{\n\t\t\tt,\n\t\t\tstrings.NewReader(\"Chris wins\\n hello there\"),\n\t\t}\n\n\t\tplayerStore := &poker.StubPlayerStore{}\n\n\t\tcli := poker.NewCLI(playerStore, in, dummySpyAlerter)\n\t\tcli.PlayPoker()\n\t})\n\n}\n\ntype failOnEndReader struct {\n\tt   *testing.T\n\trdr io.Reader\n}\n\nfunc (m failOnEndReader) Read(p []byte) (n int, err error) {\n\n\tn, err = m.rdr.Read(p)\n\n\tif n == 0 || err == io.EOF {\n\t\tm.t.Fatal(\"Read to the end when you shouldn't have\")\n\t}\n\n\treturn n, err\n}\n\nfunc assertScheduledAlert(t testing.TB, got, want scheduledAlert) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"got %+v, want %+v\", got, want)\n\t}\n}\n"
  },
  {
    "path": "time/v1/blind_alerter.go",
    "content": "package poker\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"time\"\n)\n\n// BlindAlerter schedules alerts for blind amounts.\ntype BlindAlerter interface {\n\tScheduleAlertAt(duration time.Duration, amount int)\n}\n\n// BlindAlerterFunc allows you to implement BlindAlerter with a function.\ntype BlindAlerterFunc func(duration time.Duration, amount int)\n\n// ScheduleAlertAt is BlindAlerterFunc implementation of BlindAlerter.\nfunc (a BlindAlerterFunc) ScheduleAlertAt(duration time.Duration, amount int) {\n\ta(duration, amount)\n}\n\n// StdOutAlerter will schedule alerts and print them to os.Stdout.\nfunc StdOutAlerter(duration time.Duration, amount int) {\n\ttime.AfterFunc(duration, func() {\n\t\tfmt.Fprintf(os.Stdout, \"Blind is now %d\\n\", amount)\n\t})\n}\n"
  },
  {
    "path": "time/v1/cmd/cli/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tpoker \"github.com/quii/learn-go-with-tests/time/v1\"\n)\n\nconst dbFileName = \"game.db.json\"\n\nfunc main() {\n\tstore, close, err := poker.FileSystemPlayerStoreFromFile(dbFileName)\n\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer close()\n\n\tfmt.Println(\"Let's play poker\")\n\tfmt.Println(\"Type {Name} wins to record a win\")\n\tpoker.NewCLI(store, os.Stdin, poker.BlindAlerterFunc(poker.StdOutAlerter)).PlayPoker()\n}\n"
  },
  {
    "path": "time/v1/cmd/webserver/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/quii/learn-go-with-tests/time/v1\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n)\n\nconst dbFileName = \"game.db.json\"\n\nfunc main() {\n\tdb, err := os.OpenFile(dbFileName, os.O_RDWR|os.O_CREATE, 0666)\n\n\tif err != nil {\n\t\tlog.Fatalf(\"problem opening %s %v\", dbFileName, err)\n\t}\n\n\tstore, err := poker.NewFileSystemPlayerStore(db)\n\n\tif err != nil {\n\t\tlog.Fatalf(\"problem creating file system player store, %v \", err)\n\t}\n\n\tserver := poker.NewPlayerServer(store)\n\tlog.Fatal(http.ListenAndServe(\":5000\", server))\n}\n"
  },
  {
    "path": "time/v1/file_system_store.go",
    "content": "package poker\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"sort\"\n)\n\n// FileSystemPlayerStore stores players in the filesystem.\ntype FileSystemPlayerStore struct {\n\tdatabase *json.Encoder\n\tleague   League\n}\n\n// NewFileSystemPlayerStore creates a FileSystemPlayerStore initialising the store if needed.\nfunc NewFileSystemPlayerStore(file *os.File) (*FileSystemPlayerStore, error) {\n\n\terr := initialisePlayerDBFile(file)\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"problem initialising player db file, %v\", err)\n\t}\n\n\tleague, err := NewLeague(file)\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"problem loading player store from file %s, %v\", file.Name(), err)\n\t}\n\n\treturn &FileSystemPlayerStore{\n\t\tdatabase: json.NewEncoder(&tape{file}),\n\t\tleague:   league,\n\t}, nil\n}\n\n// FileSystemPlayerStoreFromFile creates a PlayerStore from the contents of a JSON file found at path.\nfunc FileSystemPlayerStoreFromFile(path string) (*FileSystemPlayerStore, func(), error) {\n\tdb, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0666)\n\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"problem opening %s %v\", path, err)\n\t}\n\n\tcloseFunc := func() {\n\t\tdb.Close()\n\t}\n\n\tstore, err := NewFileSystemPlayerStore(db)\n\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"problem creating file system player store, %v \", err)\n\t}\n\n\treturn store, closeFunc, nil\n}\n\nfunc initialisePlayerDBFile(file *os.File) error {\n\tfile.Seek(0, io.SeekStart)\n\n\tinfo, err := file.Stat()\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"problem getting file info from file %s, %v\", file.Name(), err)\n\t}\n\n\tif info.Size() == 0 {\n\t\tfile.Write([]byte(\"[]\"))\n\t\tfile.Seek(0, io.SeekStart)\n\t}\n\n\treturn nil\n}\n\n// GetLeague returns the scores of all the players.\nfunc (f *FileSystemPlayerStore) GetLeague() League {\n\tsort.Slice(f.league, func(i, j int) bool {\n\t\treturn f.league[i].Wins > f.league[j].Wins\n\t})\n\treturn f.league\n}\n\n// GetPlayerScore retrieves a player's score.\nfunc (f *FileSystemPlayerStore) GetPlayerScore(name string) int {\n\n\tplayer := f.league.Find(name)\n\n\tif player != nil {\n\t\treturn player.Wins\n\t}\n\n\treturn 0\n}\n\n// RecordWin will store a win for a player, incrementing wins if already known.\nfunc (f *FileSystemPlayerStore) RecordWin(name string) {\n\tplayer := f.league.Find(name)\n\n\tif player != nil {\n\t\tplayer.Wins++\n\t} else {\n\t\tf.league = append(f.league, Player{name, 1})\n\t}\n\n\tf.database.Encode(f.league)\n}\n"
  },
  {
    "path": "time/v1/file_system_store_test.go",
    "content": "package poker\n\nimport (\n\t\"os\"\n\t\"testing\"\n)\n\nfunc createTempFile(t testing.TB, initialData string) (*os.File, func()) {\n\tt.Helper()\n\n\ttmpfile, err := os.CreateTemp(\"\", \"db\")\n\n\tif err != nil {\n\t\tt.Fatalf(\"could not create temp file %v\", err)\n\t}\n\n\ttmpfile.Write([]byte(initialData))\n\n\tremoveFile := func() {\n\t\ttmpfile.Close()\n\t\tos.Remove(tmpfile.Name())\n\t}\n\n\treturn tmpfile, removeFile\n}\n\nfunc TestFileSystemStore(t *testing.T) {\n\n\tt.Run(\"league sorted\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tgot := store.GetLeague()\n\n\t\twant := []Player{\n\t\t\t{\"Chris\", 33},\n\t\t\t{\"Cleo\", 10},\n\t\t}\n\n\t\tassertLeague(t, got, want)\n\n\t\t// read again\n\t\tgot = store.GetLeague()\n\t\tassertLeague(t, got, want)\n\t})\n\n\tt.Run(\"get player score\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tgot := store.GetPlayerScore(\"Chris\")\n\t\twant := 33\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"store wins for existing players\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tstore.RecordWin(\"Chris\")\n\n\t\tgot := store.GetPlayerScore(\"Chris\")\n\t\twant := 34\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"store wins for existing players\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tstore.RecordWin(\"Pepper\")\n\n\t\tgot := store.GetPlayerScore(\"Pepper\")\n\t\twant := 1\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"works with an empty file\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, \"\")\n\t\tdefer cleanDatabase()\n\n\t\t_, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\t})\n}\n\nfunc assertScoreEquals(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"got %d want %d\", got, want)\n\t}\n}\n\nfunc assertNoError(t testing.TB, err error) {\n\tt.Helper()\n\tif err != nil {\n\t\tt.Fatalf(\"didn't expect an error but got one, %v\", err)\n\t}\n}\n"
  },
  {
    "path": "time/v1/league.go",
    "content": "package poker\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n)\n\n// League stores a collection of players.\ntype League []Player\n\n// Find tries to return a player from a league.\nfunc (l League) Find(name string) *Player {\n\tfor i, p := range l {\n\t\tif p.Name == name {\n\t\t\treturn &l[i]\n\t\t}\n\t}\n\treturn nil\n}\n\n// NewLeague creates a league from JSON.\nfunc NewLeague(rdr io.Reader) (League, error) {\n\tvar league []Player\n\terr := json.NewDecoder(rdr).Decode(&league)\n\n\tif err != nil {\n\t\terr = fmt.Errorf(\"problem parsing league, %v\", err)\n\t}\n\n\treturn league, err\n}\n"
  },
  {
    "path": "time/v1/server.go",
    "content": "package poker\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n)\n\n// PlayerStore stores score information about players.\ntype PlayerStore interface {\n\tGetPlayerScore(name string) int\n\tRecordWin(name string)\n\tGetLeague() League\n}\n\n// Player stores a name with a number of wins.\ntype Player struct {\n\tName string\n\tWins int\n}\n\n// PlayerServer is a HTTP interface for player information.\ntype PlayerServer struct {\n\tstore PlayerStore\n\thttp.Handler\n}\n\nconst jsonContentType = \"application/json\"\n\n// NewPlayerServer creates a PlayerServer with routing configured.\nfunc NewPlayerServer(store PlayerStore) *PlayerServer {\n\tp := new(PlayerServer)\n\n\tp.store = store\n\n\trouter := http.NewServeMux()\n\trouter.Handle(\"/league\", http.HandlerFunc(p.leagueHandler))\n\trouter.Handle(\"/players/\", http.HandlerFunc(p.playersHandler))\n\n\tp.Handler = router\n\n\treturn p\n}\n\nfunc (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) {\n\tw.Header().Set(\"content-type\", jsonContentType)\n\tjson.NewEncoder(w).Encode(p.store.GetLeague())\n}\n\nfunc (p *PlayerServer) playersHandler(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\n\tswitch r.Method {\n\tcase http.MethodPost:\n\t\tp.processWin(w, player)\n\tcase http.MethodGet:\n\t\tp.showScore(w, player)\n\t}\n}\n\nfunc (p *PlayerServer) showScore(w http.ResponseWriter, player string) {\n\tscore := p.store.GetPlayerScore(player)\n\n\tif score == 0 {\n\t\tw.WriteHeader(http.StatusNotFound)\n\t}\n\n\tfmt.Fprint(w, score)\n}\n\nfunc (p *PlayerServer) processWin(w http.ResponseWriter, player string) {\n\tp.store.RecordWin(player)\n\tw.WriteHeader(http.StatusAccepted)\n}\n"
  },
  {
    "path": "time/v1/server_integration_test.go",
    "content": "package poker\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestRecordingWinsAndRetrievingThem(t *testing.T) {\n\tdatabase, cleanDatabase := createTempFile(t, `[]`)\n\tdefer cleanDatabase()\n\tstore, err := NewFileSystemPlayerStore(database)\n\n\tassertNoError(t, err)\n\n\tserver := NewPlayerServer(store)\n\tplayer := \"Pepper\"\n\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\n\tt.Run(\"get score\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newGetScoreRequest(player))\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\n\t\tassertResponseBody(t, response.Body.String(), \"3\")\n\t})\n\n\tt.Run(\"get league\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newLeagueRequest())\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\t\twant := []Player{\n\t\t\t{\"Pepper\", 3},\n\t\t}\n\t\tassertLeague(t, got, want)\n\t})\n}\n"
  },
  {
    "path": "time/v1/server_test.go",
    "content": "package poker\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestGETPlayers(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{\n\t\t\t\"Pepper\": 20,\n\t\t\t\"Floyd\":  10,\n\t\t},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"returns Pepper's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Pepper\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"20\")\n\t})\n\n\tt.Run(\"returns Floyd's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Floyd\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"10\")\n\t})\n\n\tt.Run(\"returns 404 on missing players\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Apollo\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusNotFound)\n\t})\n}\n\nfunc TestStoreWins(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"it records wins on POST\", func(t *testing.T) {\n\t\tplayer := \"Pepper\"\n\n\t\trequest := newPostWinRequest(player)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusAccepted)\n\t\tAssertPlayerWin(t, &store, player)\n\t})\n}\n\nfunc TestLeague(t *testing.T) {\n\n\tt.Run(\"it returns the league table as JSON\", func(t *testing.T) {\n\t\twantedLeague := []Player{\n\t\t\t{\"Cleo\", 32},\n\t\t\t{\"Chris\", 20},\n\t\t\t{\"Tiest\", 14},\n\t\t}\n\n\t\tstore := StubPlayerStore{nil, nil, wantedLeague}\n\t\tserver := NewPlayerServer(&store)\n\n\t\trequest := newLeagueRequest()\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertLeague(t, got, wantedLeague)\n\t\tassertContentType(t, response, jsonContentType)\n\n\t})\n}\n\nfunc assertContentType(t testing.TB, response *httptest.ResponseRecorder, want string) {\n\tt.Helper()\n\tif response.Header().Get(\"content-type\") != want {\n\t\tt.Errorf(\"response did not have content-type of %s, got %v\", want, response.Result().Header)\n\t}\n}\n\nfunc getLeagueFromResponse(t testing.TB, body io.Reader) []Player {\n\tt.Helper()\n\tleague, err := NewLeague(body)\n\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to parse response from server %q into slice of Player, '%v'\", body, err)\n\t}\n\n\treturn league\n}\n\nfunc assertLeague(t testing.TB, got, want []Player) {\n\tt.Helper()\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %v want %v\", got, want)\n\t}\n}\n\nfunc assertStatus(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"did not get correct status, got %d, want %d\", got, want)\n\t}\n}\n\nfunc newLeagueRequest() *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, \"/league\", nil)\n\treturn req\n}\n\nfunc newGetScoreRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc newPostWinRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodPost, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc assertResponseBody(t testing.TB, got, want string) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"response body is wrong, got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "time/v1/tape.go",
    "content": "package poker\n\nimport (\n\t\"io\"\n\t\"os\"\n)\n\ntype tape struct {\n\tfile *os.File\n}\n\nfunc (t *tape) Write(p []byte) (n int, err error) {\n\tt.file.Truncate(0)\n\tt.file.Seek(0, io.SeekStart)\n\treturn t.file.Write(p)\n}\n"
  },
  {
    "path": "time/v1/tape_test.go",
    "content": "package poker\n\nimport (\n\t\"io\"\n\t\"testing\"\n)\n\nfunc TestTape_Write(t *testing.T) {\n\tfile, clean := createTempFile(t, \"12345\")\n\tdefer clean()\n\n\ttape := &tape{file}\n\n\ttape.Write([]byte(\"abc\"))\n\n\tfile.Seek(0, io.SeekStart)\n\tnewFileContents, _ := io.ReadAll(file)\n\n\tgot := string(newFileContents)\n\twant := \"abc\"\n\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "time/v1/testing.go",
    "content": "package poker\n\nimport \"testing\"\n\n// StubPlayerStore implements PlayerStore for testing purposes.\ntype StubPlayerStore struct {\n\tScores   map[string]int\n\tWinCalls []string\n\tLeague   []Player\n}\n\n// GetPlayerScore returns a score from Scores.\nfunc (s *StubPlayerStore) GetPlayerScore(name string) int {\n\tscore := s.Scores[name]\n\treturn score\n}\n\n// RecordWin will record a win to WinCalls.\nfunc (s *StubPlayerStore) RecordWin(name string) {\n\ts.WinCalls = append(s.WinCalls, name)\n}\n\n// GetLeague returns League.\nfunc (s *StubPlayerStore) GetLeague() League {\n\treturn s.League\n}\n\n// AssertPlayerWin allows you to spy on the store's calls to RecordWin.\nfunc AssertPlayerWin(t testing.TB, store *StubPlayerStore, winner string) {\n\tt.Helper()\n\n\tif len(store.WinCalls) != 1 {\n\t\tt.Fatalf(\"got %d calls to RecordWin want %d\", len(store.WinCalls), 1)\n\t}\n\n\tif store.WinCalls[0] != winner {\n\t\tt.Errorf(\"did not store correct winner got %q want %q\", store.WinCalls[0], winner)\n\t}\n}\n"
  },
  {
    "path": "time/v2/CLI.go",
    "content": "package poker\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"io\"\n\t\"strconv\"\n\t\"strings\"\n)\n\n// Game manages the state of a game.\ntype Game interface {\n\tStart(numberOfPlayers int)\n\tFinish(winner string)\n}\n\n// CLI helps players through a game of poker.\ntype CLI struct {\n\tplayerStore PlayerStore\n\tin          *bufio.Scanner\n\tout         io.Writer\n\tgame        Game\n}\n\n// NewCLI creates a CLI for playing poker.\nfunc NewCLI(in io.Reader, out io.Writer, game Game) *CLI {\n\treturn &CLI{\n\t\tin:   bufio.NewScanner(in),\n\t\tout:  out,\n\t\tgame: game,\n\t}\n}\n\n// PlayerPrompt is the text asking the user for the number of players.\nconst PlayerPrompt = \"Please enter the number of players: \"\n\n// ErrorPlayerNumberPrompt tells the user they entered in the value wrong.\nconst ErrorPlayerNumberPrompt = \"ERROR: Please enter the number of players as a number: \"\n\n// PlayPoker starts the game.\nfunc (cli *CLI) PlayPoker() {\n\tfmt.Fprint(cli.out, PlayerPrompt)\n\n\tnumberOfPlayers, _ := strconv.Atoi(cli.readLine())\n\n\tcli.game.Start(numberOfPlayers)\n\n\twinnerInput := cli.readLine()\n\twinner := extractWinner(winnerInput)\n\n\tcli.game.Finish(winner)\n}\n\nfunc extractWinner(userInput string) string {\n\treturn strings.Replace(userInput, \" wins\", \"\", 1)\n}\n\nfunc (cli *CLI) readLine() string {\n\tcli.in.Scan()\n\treturn cli.in.Text()\n}\n"
  },
  {
    "path": "time/v2/CLI_test.go",
    "content": "package poker_test\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"strings\"\n\t\"testing\"\n\n\tpoker \"github.com/quii/learn-go-with-tests/time/v2\"\n)\n\nvar dummyBlindAlerter = &poker.SpyBlindAlerter{}\nvar dummyPlayerStore = &poker.StubPlayerStore{}\nvar dummyStdIn = &bytes.Buffer{}\nvar dummyStdOut = &bytes.Buffer{}\n\ntype GameSpy struct {\n\tStartCalledWith  int\n\tFinishCalledWith string\n}\n\nfunc (g *GameSpy) Start(numberOfPlayers int) {\n\tg.StartCalledWith = numberOfPlayers\n}\n\nfunc (g *GameSpy) Finish(winner string) {\n\tg.FinishCalledWith = winner\n}\n\nfunc TestCLI(t *testing.T) {\n\n\tt.Run(\"it prompts the user to enter the number of players and starts the game\", func(t *testing.T) {\n\t\tstdout := &bytes.Buffer{}\n\t\tin := strings.NewReader(\"7\\n\")\n\t\tgame := &GameSpy{}\n\n\t\tcli := poker.NewCLI(in, stdout, game)\n\t\tcli.PlayPoker()\n\n\t\tgotPrompt := stdout.String()\n\t\twantPrompt := poker.PlayerPrompt\n\n\t\tif gotPrompt != wantPrompt {\n\t\t\tt.Errorf(\"got %q, want %q\", gotPrompt, wantPrompt)\n\t\t}\n\n\t\tif game.StartCalledWith != 7 {\n\t\t\tt.Errorf(\"wanted Start called with 7 but got %d\", game.StartCalledWith)\n\t\t}\n\t})\n\n\tt.Run(\"finish game with 'Chris' as winner\", func(t *testing.T) {\n\t\tin := strings.NewReader(\"1\\nChris wins\\n\")\n\t\tgame := &GameSpy{}\n\t\tcli := poker.NewCLI(in, dummyStdOut, game)\n\n\t\tcli.PlayPoker()\n\n\t\tif game.FinishCalledWith != \"Chris\" {\n\t\t\tt.Errorf(\"expected finish called with 'Chris' but got %q\", game.FinishCalledWith)\n\t\t}\n\t})\n\n\tt.Run(\"record 'Cleo' win from user input\", func(t *testing.T) {\n\t\tin := strings.NewReader(\"1\\nCleo wins\\n\")\n\t\tgame := &GameSpy{}\n\t\tcli := poker.NewCLI(in, dummyStdOut, game)\n\n\t\tcli.PlayPoker()\n\n\t\tif game.FinishCalledWith != \"Cleo\" {\n\t\t\tt.Errorf(\"expected finish called with 'Cleo' but got %q\", game.FinishCalledWith)\n\t\t}\n\t})\n\n\tt.Run(\"do not read beyond the first newline\", func(t *testing.T) {\n\t\tin := failOnEndReader{\n\t\t\tt,\n\t\t\tstrings.NewReader(\"1\\nChris wins\\n hello there\"),\n\t\t}\n\n\t\tgame := poker.NewTexasHoldem(dummyBlindAlerter, dummyPlayerStore)\n\n\t\tcli := poker.NewCLI(in, dummyStdOut, game)\n\t\tcli.PlayPoker()\n\t})\n\n}\n\ntype failOnEndReader struct {\n\tt   *testing.T\n\trdr io.Reader\n}\n\nfunc (m failOnEndReader) Read(p []byte) (n int, err error) {\n\n\tn, err = m.rdr.Read(p)\n\n\tif n == 0 || err == io.EOF {\n\t\tm.t.Fatal(\"Read to the end when you shouldn't have\")\n\t}\n\n\treturn n, err\n}\n\nfunc assertScheduledAlert(t testing.TB, got, want poker.ScheduledAlert) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"got %+v, want %+v\", got, want)\n\t}\n}\n"
  },
  {
    "path": "time/v2/blind_alerter.go",
    "content": "package poker\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"time\"\n)\n\n// BlindAlerter schedules alerts for blind amounts.\ntype BlindAlerter interface {\n\tScheduleAlertAt(duration time.Duration, amount int)\n}\n\n// BlindAlerterFunc allows you to implement BlindAlerter with a function.\ntype BlindAlerterFunc func(duration time.Duration, amount int)\n\n// ScheduleAlertAt is BlindAlerterFunc implementation of BlindAlerter.\nfunc (a BlindAlerterFunc) ScheduleAlertAt(duration time.Duration, amount int) {\n\ta(duration, amount)\n}\n\n// StdOutAlerter will schedule alerts and print them to os.Stdout.\nfunc StdOutAlerter(duration time.Duration, amount int) {\n\ttime.AfterFunc(duration, func() {\n\t\tfmt.Fprintf(os.Stdout, \"Blind is now %d\\n\", amount)\n\t})\n}\n"
  },
  {
    "path": "time/v2/cmd/cli/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tpoker \"github.com/quii/learn-go-with-tests/time/v2\"\n)\n\nconst dbFileName = \"game.db.json\"\n\nfunc main() {\n\tstore, close, err := poker.FileSystemPlayerStoreFromFile(dbFileName)\n\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer close()\n\n\tgame := poker.NewTexasHoldem(poker.BlindAlerterFunc(poker.StdOutAlerter), store)\n\tcli := poker.NewCLI(os.Stdin, os.Stdout, game)\n\n\tfmt.Println(\"Let's play poker\")\n\tfmt.Println(\"Type {Name} wins to record a win\")\n\tcli.PlayPoker()\n}\n"
  },
  {
    "path": "time/v2/cmd/webserver/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/quii/learn-go-with-tests/command-line/v2\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n)\n\nconst dbFileName = \"game.db.json\"\n\nfunc main() {\n\tdb, err := os.OpenFile(dbFileName, os.O_RDWR|os.O_CREATE, 0666)\n\n\tif err != nil {\n\t\tlog.Fatalf(\"problem opening %s %v\", dbFileName, err)\n\t}\n\n\tstore, err := poker.NewFileSystemPlayerStore(db)\n\n\tif err != nil {\n\t\tlog.Fatalf(\"problem creating file system player store, %v \", err)\n\t}\n\n\tserver := poker.NewPlayerServer(store)\n\tlog.Fatal(http.ListenAndServe(\":5000\", server))\n}\n"
  },
  {
    "path": "time/v2/file_system_store.go",
    "content": "package poker\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"sort\"\n)\n\n// FileSystemPlayerStore stores players in the filesystem.\ntype FileSystemPlayerStore struct {\n\tdatabase *json.Encoder\n\tleague   League\n}\n\n// NewFileSystemPlayerStore creates a FileSystemPlayerStore initialising the store if needed.\nfunc NewFileSystemPlayerStore(file *os.File) (*FileSystemPlayerStore, error) {\n\n\terr := initialisePlayerDBFile(file)\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"problem initialising player db file, %v\", err)\n\t}\n\n\tleague, err := NewLeague(file)\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"problem loading player store from file %s, %v\", file.Name(), err)\n\t}\n\n\treturn &FileSystemPlayerStore{\n\t\tdatabase: json.NewEncoder(&tape{file}),\n\t\tleague:   league,\n\t}, nil\n}\n\n// FileSystemPlayerStoreFromFile creates a PlayerStore from the contents of a JSON file found at path.\nfunc FileSystemPlayerStoreFromFile(path string) (*FileSystemPlayerStore, func(), error) {\n\tdb, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0666)\n\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"problem opening %s %v\", path, err)\n\t}\n\n\tcloseFunc := func() {\n\t\tdb.Close()\n\t}\n\n\tstore, err := NewFileSystemPlayerStore(db)\n\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"problem creating file system player store, %v \", err)\n\t}\n\n\treturn store, closeFunc, nil\n}\n\nfunc initialisePlayerDBFile(file *os.File) error {\n\tfile.Seek(0, io.SeekStart)\n\n\tinfo, err := file.Stat()\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"problem getting file info from file %s, %v\", file.Name(), err)\n\t}\n\n\tif info.Size() == 0 {\n\t\tfile.Write([]byte(\"[]\"))\n\t\tfile.Seek(0, io.SeekStart)\n\t}\n\n\treturn nil\n}\n\n// GetLeague returns the Scores of all the players.\nfunc (f *FileSystemPlayerStore) GetLeague() League {\n\tsort.Slice(f.league, func(i, j int) bool {\n\t\treturn f.league[i].Wins > f.league[j].Wins\n\t})\n\treturn f.league\n}\n\n// GetPlayerScore retrieves a player's score.\nfunc (f *FileSystemPlayerStore) GetPlayerScore(name string) int {\n\n\tplayer := f.league.Find(name)\n\n\tif player != nil {\n\t\treturn player.Wins\n\t}\n\n\treturn 0\n}\n\n// RecordWin will store a win for a player, incrementing wins if already known.\nfunc (f *FileSystemPlayerStore) RecordWin(name string) {\n\tplayer := f.league.Find(name)\n\n\tif player != nil {\n\t\tplayer.Wins++\n\t} else {\n\t\tf.league = append(f.league, Player{name, 1})\n\t}\n\n\tf.database.Encode(f.league)\n}\n"
  },
  {
    "path": "time/v2/file_system_store_test.go",
    "content": "package poker\n\nimport (\n\t\"os\"\n\t\"testing\"\n)\n\nfunc createTempFile(t testing.TB, initialData string) (*os.File, func()) {\n\tt.Helper()\n\n\ttmpfile, err := os.CreateTemp(\"\", \"db\")\n\n\tif err != nil {\n\t\tt.Fatalf(\"could not create temp file %v\", err)\n\t}\n\n\ttmpfile.Write([]byte(initialData))\n\n\tremoveFile := func() {\n\t\ttmpfile.Close()\n\t\tos.Remove(tmpfile.Name())\n\t}\n\n\treturn tmpfile, removeFile\n}\n\nfunc TestFileSystemStore(t *testing.T) {\n\n\tt.Run(\"League sorted\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tgot := store.GetLeague()\n\n\t\twant := []Player{\n\t\t\t{\"Chris\", 33},\n\t\t\t{\"Cleo\", 10},\n\t\t}\n\n\t\tassertLeague(t, got, want)\n\n\t\t// read again\n\t\tgot = store.GetLeague()\n\t\tassertLeague(t, got, want)\n\t})\n\n\tt.Run(\"get player score\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tgot := store.GetPlayerScore(\"Chris\")\n\t\twant := 33\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"store wins for existing players\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tstore.RecordWin(\"Chris\")\n\n\t\tgot := store.GetPlayerScore(\"Chris\")\n\t\twant := 34\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"store wins for existing players\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tstore.RecordWin(\"Pepper\")\n\n\t\tgot := store.GetPlayerScore(\"Pepper\")\n\t\twant := 1\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"works with an empty file\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, \"\")\n\t\tdefer cleanDatabase()\n\n\t\t_, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\t})\n}\n\nfunc assertScoreEquals(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"got %d want %d\", got, want)\n\t}\n}\n\nfunc assertNoError(t testing.TB, err error) {\n\tt.Helper()\n\tif err != nil {\n\t\tt.Fatalf(\"didn't expect an error but got one, %v\", err)\n\t}\n}\n"
  },
  {
    "path": "time/v2/league.go",
    "content": "package poker\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n)\n\n// League stores a collection of players.\ntype League []Player\n\n// Find tries to return a player from a League.\nfunc (l League) Find(name string) *Player {\n\tfor i, p := range l {\n\t\tif p.Name == name {\n\t\t\treturn &l[i]\n\t\t}\n\t}\n\treturn nil\n}\n\n// NewLeague creates a League from JSON.\nfunc NewLeague(rdr io.Reader) (League, error) {\n\tvar league []Player\n\terr := json.NewDecoder(rdr).Decode(&league)\n\n\tif err != nil {\n\t\terr = fmt.Errorf(\"problem parsing League, %v\", err)\n\t}\n\n\treturn league, err\n}\n"
  },
  {
    "path": "time/v2/server.go",
    "content": "package poker\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n)\n\n// PlayerStore stores score information about players.\ntype PlayerStore interface {\n\tGetPlayerScore(name string) int\n\tRecordWin(name string)\n\tGetLeague() League\n}\n\n// Player stores a name with a number of wins.\ntype Player struct {\n\tName string\n\tWins int\n}\n\n// PlayerServer is a HTTP interface for player information.\ntype PlayerServer struct {\n\tstore PlayerStore\n\thttp.Handler\n}\n\nconst jsonContentType = \"application/json\"\n\n// NewPlayerServer creates a PlayerServer with routing configured.\nfunc NewPlayerServer(store PlayerStore) *PlayerServer {\n\tp := new(PlayerServer)\n\n\tp.store = store\n\n\trouter := http.NewServeMux()\n\trouter.Handle(\"/League\", http.HandlerFunc(p.leagueHandler))\n\trouter.Handle(\"/players/\", http.HandlerFunc(p.playersHandler))\n\n\tp.Handler = router\n\n\treturn p\n}\n\nfunc (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) {\n\tw.Header().Set(\"content-type\", jsonContentType)\n\tjson.NewEncoder(w).Encode(p.store.GetLeague())\n}\n\nfunc (p *PlayerServer) playersHandler(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\n\tswitch r.Method {\n\tcase http.MethodPost:\n\t\tp.processWin(w, player)\n\tcase http.MethodGet:\n\t\tp.showScore(w, player)\n\t}\n}\n\nfunc (p *PlayerServer) showScore(w http.ResponseWriter, player string) {\n\tscore := p.store.GetPlayerScore(player)\n\n\tif score == 0 {\n\t\tw.WriteHeader(http.StatusNotFound)\n\t}\n\n\tfmt.Fprint(w, score)\n}\n\nfunc (p *PlayerServer) processWin(w http.ResponseWriter, player string) {\n\tp.store.RecordWin(player)\n\tw.WriteHeader(http.StatusAccepted)\n}\n"
  },
  {
    "path": "time/v2/server_integration_test.go",
    "content": "package poker\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestRecordingWinsAndRetrievingThem(t *testing.T) {\n\tdatabase, cleanDatabase := createTempFile(t, `[]`)\n\tdefer cleanDatabase()\n\tstore, err := NewFileSystemPlayerStore(database)\n\n\tassertNoError(t, err)\n\n\tserver := NewPlayerServer(store)\n\tplayer := \"Pepper\"\n\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\n\tt.Run(\"get score\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newGetScoreRequest(player))\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\n\t\tassertResponseBody(t, response.Body.String(), \"3\")\n\t})\n\n\tt.Run(\"get League\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newLeagueRequest())\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\t\twant := []Player{\n\t\t\t{\"Pepper\", 3},\n\t\t}\n\t\tassertLeague(t, got, want)\n\t})\n}\n"
  },
  {
    "path": "time/v2/server_test.go",
    "content": "package poker\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestGETPlayers(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{\n\t\t\t\"Pepper\": 20,\n\t\t\t\"Floyd\":  10,\n\t\t},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"returns Pepper's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Pepper\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"20\")\n\t})\n\n\tt.Run(\"returns Floyd's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Floyd\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"10\")\n\t})\n\n\tt.Run(\"returns 404 on missing players\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Apollo\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusNotFound)\n\t})\n}\n\nfunc TestStoreWins(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"it records wins on POST\", func(t *testing.T) {\n\t\tplayer := \"Pepper\"\n\n\t\trequest := newPostWinRequest(player)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusAccepted)\n\t\tAssertPlayerWin(t, &store, player)\n\t})\n}\n\nfunc TestLeague(t *testing.T) {\n\n\tt.Run(\"it returns the League table as JSON\", func(t *testing.T) {\n\t\twantedLeague := []Player{\n\t\t\t{\"Cleo\", 32},\n\t\t\t{\"Chris\", 20},\n\t\t\t{\"Tiest\", 14},\n\t\t}\n\n\t\tstore := StubPlayerStore{nil, nil, wantedLeague}\n\t\tserver := NewPlayerServer(&store)\n\n\t\trequest := newLeagueRequest()\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertLeague(t, got, wantedLeague)\n\t\tassertContentType(t, response, jsonContentType)\n\n\t})\n}\n\nfunc assertContentType(t testing.TB, response *httptest.ResponseRecorder, want string) {\n\tt.Helper()\n\tif response.Header().Get(\"content-type\") != want {\n\t\tt.Errorf(\"response did not have content-type of %s, got %v\", want, response.Result().Header)\n\t}\n}\n\nfunc getLeagueFromResponse(t testing.TB, body io.Reader) []Player {\n\tt.Helper()\n\tleague, err := NewLeague(body)\n\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to parse response from server %q into slice of Player, '%v'\", body, err)\n\t}\n\n\treturn league\n}\n\nfunc assertLeague(t testing.TB, got, want []Player) {\n\tt.Helper()\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %v want %v\", got, want)\n\t}\n}\n\nfunc assertStatus(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"did not get correct status, got %d, want %d\", got, want)\n\t}\n}\n\nfunc newLeagueRequest() *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, \"/League\", nil)\n\treturn req\n}\n\nfunc newGetScoreRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc newPostWinRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodPost, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc assertResponseBody(t testing.TB, got, want string) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"response body is wrong, got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "time/v2/tape.go",
    "content": "package poker\n\nimport (\n\t\"io\"\n\t\"os\"\n)\n\ntype tape struct {\n\tfile *os.File\n}\n\nfunc (t *tape) Write(p []byte) (n int, err error) {\n\tt.file.Truncate(0)\n\tt.file.Seek(0, io.SeekStart)\n\treturn t.file.Write(p)\n}\n"
  },
  {
    "path": "time/v2/tape_test.go",
    "content": "package poker\n\nimport (\n\t\"io\"\n\t\"testing\"\n)\n\nfunc TestTape_Write(t *testing.T) {\n\tfile, clean := createTempFile(t, \"12345\")\n\tdefer clean()\n\n\ttape := &tape{file}\n\n\ttape.Write([]byte(\"abc\"))\n\n\tfile.Seek(0, io.SeekStart)\n\tnewFileContents, _ := io.ReadAll(file)\n\n\tgot := string(newFileContents)\n\twant := \"abc\"\n\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "time/v2/testing.go",
    "content": "package poker\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n)\n\n// StubPlayerStore implements PlayerStore for testing purposes.\ntype StubPlayerStore struct {\n\tScores   map[string]int\n\tWinCalls []string\n\tLeague   []Player\n}\n\n// GetPlayerScore returns a score from Scores.\nfunc (s *StubPlayerStore) GetPlayerScore(name string) int {\n\tscore := s.Scores[name]\n\treturn score\n}\n\n// RecordWin will record a win to WinCalls.\nfunc (s *StubPlayerStore) RecordWin(name string) {\n\ts.WinCalls = append(s.WinCalls, name)\n}\n\n// GetLeague returns League.\nfunc (s *StubPlayerStore) GetLeague() League {\n\treturn s.League\n}\n\n// AssertPlayerWin allows you to spy on the store's calls to RecordWin.\nfunc AssertPlayerWin(t testing.TB, store *StubPlayerStore, winner string) {\n\tt.Helper()\n\n\tif len(store.WinCalls) != 1 {\n\t\tt.Fatalf(\"got %d calls to RecordWin want %d\", len(store.WinCalls), 1)\n\t}\n\n\tif store.WinCalls[0] != winner {\n\t\tt.Errorf(\"did not store correct winner got %q want %q\", store.WinCalls[0], winner)\n\t}\n}\n\n// ScheduledAlert holds information about when an alert is scheduled.\ntype ScheduledAlert struct {\n\tAt     time.Duration\n\tAmount int\n}\n\nfunc (s ScheduledAlert) String() string {\n\treturn fmt.Sprintf(\"%d chips at %v\", s.Amount, s.At)\n}\n\n// SpyBlindAlerter allows you to spy on ScheduleAlertAt calls.\ntype SpyBlindAlerter struct {\n\tAlerts []ScheduledAlert\n}\n\n// ScheduleAlertAt records alerts that have been scheduled.\nfunc (s *SpyBlindAlerter) ScheduleAlertAt(at time.Duration, amount int) {\n\ts.Alerts = append(s.Alerts, ScheduledAlert{at, amount})\n}\n"
  },
  {
    "path": "time/v2/texas_holdem.go",
    "content": "package poker\n\nimport \"time\"\n\n// TexasHoldem manages a game of poker.\ntype TexasHoldem struct {\n\talerter BlindAlerter\n\tstore   PlayerStore\n}\n\n// NewTexasHoldem returns a new game.\nfunc NewTexasHoldem(alerter BlindAlerter, store PlayerStore) *TexasHoldem {\n\treturn &TexasHoldem{\n\t\talerter: alerter,\n\t\tstore:   store,\n\t}\n}\n\n// Start will schedule blind alerts dependant on the number of players.\nfunc (p *TexasHoldem) Start(numberOfPlayers int) {\n\tblindIncrement := time.Duration(5+numberOfPlayers) * time.Minute\n\n\tblinds := []int{100, 200, 300, 400, 500, 600, 800, 1000, 2000, 4000, 8000}\n\tblindTime := 0 * time.Second\n\tfor _, blind := range blinds {\n\t\tp.alerter.ScheduleAlertAt(blindTime, blind)\n\t\tblindTime = blindTime + blindIncrement\n\t}\n}\n\n// Finish ends the game, recording the winner.\nfunc (p *TexasHoldem) Finish(winner string) {\n\tp.store.RecordWin(winner)\n}\n"
  },
  {
    "path": "time/v2/texas_holdem_test.go",
    "content": "package poker_test\n\nimport (\n\t\"fmt\"\n\t\"github.com/quii/learn-go-with-tests/time/v2\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestGame_Start(t *testing.T) {\n\tt.Run(\"schedules alerts on game start for 5 players\", func(t *testing.T) {\n\t\tblindAlerter := &poker.SpyBlindAlerter{}\n\t\tgame := poker.NewTexasHoldem(blindAlerter, dummyPlayerStore)\n\n\t\tgame.Start(5)\n\n\t\tcases := []poker.ScheduledAlert{\n\t\t\t{At: 0 * time.Second, Amount: 100},\n\t\t\t{At: 10 * time.Minute, Amount: 200},\n\t\t\t{At: 20 * time.Minute, Amount: 300},\n\t\t\t{At: 30 * time.Minute, Amount: 400},\n\t\t\t{At: 40 * time.Minute, Amount: 500},\n\t\t\t{At: 50 * time.Minute, Amount: 600},\n\t\t\t{At: 60 * time.Minute, Amount: 800},\n\t\t\t{At: 70 * time.Minute, Amount: 1000},\n\t\t\t{At: 80 * time.Minute, Amount: 2000},\n\t\t\t{At: 90 * time.Minute, Amount: 4000},\n\t\t\t{At: 100 * time.Minute, Amount: 8000},\n\t\t}\n\n\t\tcheckSchedulingCases(cases, t, blindAlerter)\n\t})\n\n\tt.Run(\"schedules alerts on game start for 7 players\", func(t *testing.T) {\n\t\tblindAlerter := &poker.SpyBlindAlerter{}\n\t\tgame := poker.NewTexasHoldem(blindAlerter, dummyPlayerStore)\n\n\t\tgame.Start(7)\n\n\t\tcases := []poker.ScheduledAlert{\n\t\t\t{At: 0 * time.Second, Amount: 100},\n\t\t\t{At: 12 * time.Minute, Amount: 200},\n\t\t\t{At: 24 * time.Minute, Amount: 300},\n\t\t\t{At: 36 * time.Minute, Amount: 400},\n\t\t}\n\n\t\tcheckSchedulingCases(cases, t, blindAlerter)\n\t})\n\n}\n\nfunc TestGame_Finish(t *testing.T) {\n\tstore := &poker.StubPlayerStore{}\n\tgame := poker.NewTexasHoldem(dummyBlindAlerter, store)\n\twinner := \"Ruth\"\n\n\tgame.Finish(winner)\n\tpoker.AssertPlayerWin(t, store, winner)\n}\n\nfunc checkSchedulingCases(cases []poker.ScheduledAlert, t *testing.T, blindAlerter *poker.SpyBlindAlerter) {\n\tfor i, want := range cases {\n\t\tt.Run(fmt.Sprint(want), func(t *testing.T) {\n\n\t\t\tif len(blindAlerter.Alerts) <= i {\n\t\t\t\tt.Fatalf(\"alert %d was not scheduled %v\", i, blindAlerter.Alerts)\n\t\t\t}\n\n\t\t\tgot := blindAlerter.Alerts[i]\n\t\t\tassertScheduledAlert(t, got, want)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "time/v3/BlindAlerter.go",
    "content": "package poker\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"time\"\n)\n\n// BlindAlerter schedules alerts for blind amounts.\ntype BlindAlerter interface {\n\tScheduleAlertAt(duration time.Duration, amount int)\n}\n\n// BlindAlerterFunc allows you to implement BlindAlerter with a function.\ntype BlindAlerterFunc func(duration time.Duration, amount int)\n\n// ScheduleAlertAt is BlindAlerterFunc implementation of BlindAlerter.\nfunc (a BlindAlerterFunc) ScheduleAlertAt(duration time.Duration, amount int) {\n\ta(duration, amount)\n}\n\n// StdOutAlerter will schedule alerts and print them to os.Stdout.\nfunc StdOutAlerter(duration time.Duration, amount int) {\n\ttime.AfterFunc(duration, func() {\n\t\tfmt.Fprintf(os.Stdout, \"Blind is now %d\\n\", amount)\n\t})\n}\n"
  },
  {
    "path": "time/v3/CLI.go",
    "content": "package poker\n\nimport (\n\t\"bufio\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"strconv\"\n\t\"strings\"\n)\n\n// CLI helps players through a game of poker.\ntype CLI struct {\n\tplayerStore PlayerStore\n\tin          *bufio.Scanner\n\tout         io.Writer\n\tgame        Game\n}\n\n// NewCLI creates a CLI for playing poker.\nfunc NewCLI(in io.Reader, out io.Writer, game Game) *CLI {\n\treturn &CLI{\n\t\tin:   bufio.NewScanner(in),\n\t\tout:  out,\n\t\tgame: game,\n\t}\n}\n\n// PlayerPrompt is the text asking the user for the number of players.\nconst PlayerPrompt = \"Please enter the number of players: \"\n\n// BadPlayerInputErrMsg is the text telling the user they did bad things.\nconst BadPlayerInputErrMsg = \"Bad value received for number of players, please try again with a number\"\n\n// BadWinnerInputMsg is the text telling the user they declared the winner wrong.\nconst BadWinnerInputMsg = \"invalid winner input, expect format of 'PlayerName wins'\"\n\n// PlayPoker starts the game.\nfunc (cli *CLI) PlayPoker() {\n\tfmt.Fprint(cli.out, PlayerPrompt)\n\n\tnumberOfPlayers, err := strconv.Atoi(cli.readLine())\n\n\tif err != nil {\n\t\tfmt.Fprint(cli.out, BadPlayerInputErrMsg)\n\t\treturn\n\t}\n\n\tcli.game.Start(numberOfPlayers)\n\n\twinnerInput := cli.readLine()\n\twinner, err := extractWinner(winnerInput)\n\n\tif err != nil {\n\t\tfmt.Fprint(cli.out, BadWinnerInputMsg)\n\t\treturn\n\t}\n\n\tcli.game.Finish(winner)\n}\n\nfunc extractWinner(userInput string) (string, error) {\n\tif !strings.Contains(userInput, \" wins\") {\n\t\treturn \"\", errors.New(BadWinnerInputMsg)\n\t}\n\treturn strings.Replace(userInput, \" wins\", \"\", 1), nil\n}\n\nfunc (cli *CLI) readLine() string {\n\tcli.in.Scan()\n\treturn cli.in.Text()\n}\n"
  },
  {
    "path": "time/v3/CLI_test.go",
    "content": "package poker_test\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"strings\"\n\t\"testing\"\n\n\tpoker \"github.com/quii/learn-go-with-tests/time/v3\"\n)\n\nvar dummyBlindAlerter = &poker.SpyBlindAlerter{}\nvar dummyPlayerStore = &poker.StubPlayerStore{}\nvar dummyStdIn = &bytes.Buffer{}\nvar dummyStdOut = &bytes.Buffer{}\n\ntype GameSpy struct {\n\tStartCalled     bool\n\tStartCalledWith int\n\n\tFinishedCalled   bool\n\tFinishCalledWith string\n}\n\nfunc (g *GameSpy) Start(numberOfPlayers int) {\n\tg.StartCalled = true\n\tg.StartCalledWith = numberOfPlayers\n}\n\nfunc (g *GameSpy) Finish(winner string) {\n\tg.FinishedCalled = true\n\tg.FinishCalledWith = winner\n}\n\nfunc userSends(messages ...string) io.Reader {\n\treturn strings.NewReader(strings.Join(messages, \"\\n\"))\n}\n\nfunc TestCLI(t *testing.T) {\n\n\tt.Run(\"start game with 3 players and finish game with 'Chris' as winner\", func(t *testing.T) {\n\t\tgame := &GameSpy{}\n\t\tstdout := &bytes.Buffer{}\n\n\t\tin := userSends(\"3\", \"Chris wins\")\n\t\tcli := poker.NewCLI(in, stdout, game)\n\n\t\tcli.PlayPoker()\n\n\t\tassertMessagesSentToUser(t, stdout, poker.PlayerPrompt)\n\t\tassertGameStartedWith(t, game, 3)\n\t\tassertFinishCalledWith(t, game, \"Chris\")\n\t})\n\n\tt.Run(\"start game with 8 players and record 'Cleo' as winner\", func(t *testing.T) {\n\t\tgame := &GameSpy{}\n\n\t\tin := userSends(\"8\", \"Cleo wins\")\n\t\tcli := poker.NewCLI(in, dummyStdOut, game)\n\n\t\tcli.PlayPoker()\n\n\t\tassertGameStartedWith(t, game, 8)\n\t\tassertFinishCalledWith(t, game, \"Cleo\")\n\t})\n\n\tt.Run(\"it prints an error when a non numeric value is entered and does not start the game\", func(t *testing.T) {\n\t\tgame := &GameSpy{}\n\n\t\tstdout := &bytes.Buffer{}\n\t\tin := userSends(\"pies\")\n\n\t\tcli := poker.NewCLI(in, stdout, game)\n\t\tcli.PlayPoker()\n\n\t\tassertGameNotStarted(t, game)\n\t\tassertMessagesSentToUser(t, stdout, poker.PlayerPrompt, poker.BadPlayerInputErrMsg)\n\t})\n\n\tt.Run(\"it prints an error when the winner is declared incorrectly\", func(t *testing.T) {\n\t\tgame := &GameSpy{}\n\t\tstdout := &bytes.Buffer{}\n\n\t\tin := userSends(\"8\", \"Lloyd is a killer\")\n\t\tcli := poker.NewCLI(in, stdout, game)\n\n\t\tcli.PlayPoker()\n\n\t\tassertGameNotFinished(t, game)\n\t\tassertMessagesSentToUser(t, stdout, poker.PlayerPrompt, poker.BadWinnerInputMsg)\n\t})\n}\n\nfunc assertGameStartedWith(t testing.TB, game *GameSpy, numberOfPlayersWanted int) {\n\tt.Helper()\n\tif game.StartCalledWith != numberOfPlayersWanted {\n\t\tt.Errorf(\"wanted Start called with %d but got %d\", numberOfPlayersWanted, game.StartCalledWith)\n\t}\n}\n\nfunc assertGameNotFinished(t testing.TB, game *GameSpy) {\n\tt.Helper()\n\tif game.FinishedCalled {\n\t\tt.Errorf(\"game should not have finished\")\n\t}\n}\n\nfunc assertGameNotStarted(t testing.TB, game *GameSpy) {\n\tt.Helper()\n\tif game.StartCalled {\n\t\tt.Errorf(\"game should not have started\")\n\t}\n}\n\nfunc assertFinishCalledWith(t testing.TB, game *GameSpy, winner string) {\n\tt.Helper()\n\tif game.FinishCalledWith != winner {\n\t\tt.Errorf(\"expected finish called with %q but got %q\", winner, game.FinishCalledWith)\n\t}\n}\n\nfunc assertMessagesSentToUser(t testing.TB, stdout *bytes.Buffer, messages ...string) {\n\tt.Helper()\n\twant := strings.Join(messages, \"\")\n\tgot := stdout.String()\n\tif got != want {\n\t\tt.Errorf(\"got %q sent to stdout but expected %+v\", got, messages)\n\t}\n}\n\nfunc assertScheduledAlert(t testing.TB, got, want poker.ScheduledAlert) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"got %+v, want %+v\", got, want)\n\t}\n}\n"
  },
  {
    "path": "time/v3/cmd/cli/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\tpoker \"github.com/quii/learn-go-with-tests/time/v3\"\n\t\"log\"\n\t\"os\"\n)\n\nconst dbFileName = \"game.db.json\"\n\nfunc main() {\n\tstore, close, err := poker.FileSystemPlayerStoreFromFile(dbFileName)\n\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer close()\n\n\tgame := poker.NewTexasHoldem(poker.BlindAlerterFunc(poker.StdOutAlerter), store)\n\tcli := poker.NewCLI(os.Stdin, os.Stdout, game)\n\n\tfmt.Println(\"Let's play poker\")\n\tfmt.Println(\"Type {Name} wins to record a win\")\n\tcli.PlayPoker()\n}\n"
  },
  {
    "path": "time/v3/cmd/webserver/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/quii/learn-go-with-tests/command-line/v3\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n)\n\nconst dbFileName = \"game.db.json\"\n\nfunc main() {\n\tdb, err := os.OpenFile(dbFileName, os.O_RDWR|os.O_CREATE, 0666)\n\n\tif err != nil {\n\t\tlog.Fatalf(\"problem opening %s %v\", dbFileName, err)\n\t}\n\n\tstore, err := poker.NewFileSystemPlayerStore(db)\n\n\tif err != nil {\n\t\tlog.Fatalf(\"problem creating file system player store, %v \", err)\n\t}\n\n\tserver := poker.NewPlayerServer(store)\n\n\tlog.Fatal(http.ListenAndServe(\":5000\", server))\n}\n"
  },
  {
    "path": "time/v3/file_system_store.go",
    "content": "package poker\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"sort\"\n)\n\n// FileSystemPlayerStore stores players in the filesystem.\ntype FileSystemPlayerStore struct {\n\tdatabase *json.Encoder\n\tleague   League\n}\n\n// NewFileSystemPlayerStore creates a FileSystemPlayerStore initialising the store if needed.\nfunc NewFileSystemPlayerStore(file *os.File) (*FileSystemPlayerStore, error) {\n\n\terr := initialisePlayerDBFile(file)\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"problem initialising player db file, %v\", err)\n\t}\n\n\tleague, err := NewLeague(file)\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"problem loading player store from file %s, %v\", file.Name(), err)\n\t}\n\n\treturn &FileSystemPlayerStore{\n\t\tdatabase: json.NewEncoder(&tape{file}),\n\t\tleague:   league,\n\t}, nil\n}\n\n// FileSystemPlayerStoreFromFile creates a PlayerStore from the contents of a JSON file found at path.\nfunc FileSystemPlayerStoreFromFile(path string) (*FileSystemPlayerStore, func(), error) {\n\tdb, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0666)\n\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"problem opening %s %v\", path, err)\n\t}\n\n\tcloseFunc := func() {\n\t\tdb.Close()\n\t}\n\n\tstore, err := NewFileSystemPlayerStore(db)\n\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"problem creating file system player store, %v \", err)\n\t}\n\n\treturn store, closeFunc, nil\n}\n\nfunc initialisePlayerDBFile(file *os.File) error {\n\tfile.Seek(0, io.SeekStart)\n\n\tinfo, err := file.Stat()\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"problem getting file info from file %s, %v\", file.Name(), err)\n\t}\n\n\tif info.Size() == 0 {\n\t\tfile.Write([]byte(\"[]\"))\n\t\tfile.Seek(0, io.SeekStart)\n\t}\n\n\treturn nil\n}\n\n// GetLeague returns the Scores of all the players.\nfunc (f *FileSystemPlayerStore) GetLeague() League {\n\tsort.Slice(f.league, func(i, j int) bool {\n\t\treturn f.league[i].Wins > f.league[j].Wins\n\t})\n\treturn f.league\n}\n\n// GetPlayerScore retrieves a player's score.\nfunc (f *FileSystemPlayerStore) GetPlayerScore(name string) int {\n\n\tplayer := f.league.Find(name)\n\n\tif player != nil {\n\t\treturn player.Wins\n\t}\n\n\treturn 0\n}\n\n// RecordWin will store a win for a player, incrementing wins if already known.\nfunc (f *FileSystemPlayerStore) RecordWin(name string) {\n\tplayer := f.league.Find(name)\n\n\tif player != nil {\n\t\tplayer.Wins++\n\t} else {\n\t\tf.league = append(f.league, Player{name, 1})\n\t}\n\n\tf.database.Encode(f.league)\n}\n"
  },
  {
    "path": "time/v3/file_system_store_test.go",
    "content": "package poker\n\nimport (\n\t\"os\"\n\t\"testing\"\n)\n\nfunc createTempFile(t testing.TB, initialData string) (*os.File, func()) {\n\tt.Helper()\n\n\ttmpfile, err := os.CreateTemp(\"\", \"db\")\n\n\tif err != nil {\n\t\tt.Fatalf(\"could not create temp file %v\", err)\n\t}\n\n\ttmpfile.Write([]byte(initialData))\n\n\tremoveFile := func() {\n\t\ttmpfile.Close()\n\t\tos.Remove(tmpfile.Name())\n\t}\n\n\treturn tmpfile, removeFile\n}\n\nfunc TestFileSystemStore(t *testing.T) {\n\n\tt.Run(\"League sorted\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tgot := store.GetLeague()\n\n\t\twant := []Player{\n\t\t\t{\"Chris\", 33},\n\t\t\t{\"Cleo\", 10},\n\t\t}\n\n\t\tassertLeague(t, got, want)\n\n\t\t// read again\n\t\tgot = store.GetLeague()\n\t\tassertLeague(t, got, want)\n\t})\n\n\tt.Run(\"get player score\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tgot := store.GetPlayerScore(\"Chris\")\n\t\twant := 33\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"store wins for existing players\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tstore.RecordWin(\"Chris\")\n\n\t\tgot := store.GetPlayerScore(\"Chris\")\n\t\twant := 34\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"store wins for existing players\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tstore.RecordWin(\"Pepper\")\n\n\t\tgot := store.GetPlayerScore(\"Pepper\")\n\t\twant := 1\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"works with an empty file\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, \"\")\n\t\tdefer cleanDatabase()\n\n\t\t_, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\t})\n}\n\nfunc assertScoreEquals(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"got %d want %d\", got, want)\n\t}\n}\n\nfunc assertNoError(t testing.TB, err error) {\n\tt.Helper()\n\tif err != nil {\n\t\tt.Fatalf(\"didn't expect an error but got one, %v\", err)\n\t}\n}\n"
  },
  {
    "path": "time/v3/game.go",
    "content": "package poker\n\n// Game manages the state of a game.\ntype Game interface {\n\tStart(numberOfPlayers int)\n\tFinish(winner string)\n}\n"
  },
  {
    "path": "time/v3/league.go",
    "content": "package poker\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n)\n\n// League stores a collection of players.\ntype League []Player\n\n// Find tries to return a player from a League.\nfunc (l League) Find(name string) *Player {\n\tfor i, p := range l {\n\t\tif p.Name == name {\n\t\t\treturn &l[i]\n\t\t}\n\t}\n\treturn nil\n}\n\n// NewLeague creates a League from JSON.\nfunc NewLeague(rdr io.Reader) (League, error) {\n\tvar league []Player\n\terr := json.NewDecoder(rdr).Decode(&league)\n\n\tif err != nil {\n\t\terr = fmt.Errorf(\"problem parsing League, %v\", err)\n\t}\n\n\treturn league, err\n}\n"
  },
  {
    "path": "time/v3/server.go",
    "content": "package poker\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n)\n\n// PlayerStore stores score information about players.\ntype PlayerStore interface {\n\tGetPlayerScore(name string) int\n\tRecordWin(name string)\n\tGetLeague() League\n}\n\n// Player stores a name with a number of wins.\ntype Player struct {\n\tName string\n\tWins int\n}\n\n// PlayerServer is a HTTP interface for player information.\ntype PlayerServer struct {\n\tstore PlayerStore\n\thttp.Handler\n}\n\nconst jsonContentType = \"application/json\"\n\n// NewPlayerServer creates a PlayerServer with routing configured.\nfunc NewPlayerServer(store PlayerStore) *PlayerServer {\n\tp := new(PlayerServer)\n\n\tp.store = store\n\n\trouter := http.NewServeMux()\n\trouter.Handle(\"/League\", http.HandlerFunc(p.leagueHandler))\n\trouter.Handle(\"/players/\", http.HandlerFunc(p.playersHandler))\n\n\tp.Handler = router\n\n\treturn p\n}\n\nfunc (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) {\n\tw.Header().Set(\"content-type\", jsonContentType)\n\tjson.NewEncoder(w).Encode(p.store.GetLeague())\n}\n\nfunc (p *PlayerServer) playersHandler(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\n\tswitch r.Method {\n\tcase http.MethodPost:\n\t\tp.processWin(w, player)\n\tcase http.MethodGet:\n\t\tp.showScore(w, player)\n\t}\n}\n\nfunc (p *PlayerServer) showScore(w http.ResponseWriter, player string) {\n\tscore := p.store.GetPlayerScore(player)\n\n\tif score == 0 {\n\t\tw.WriteHeader(http.StatusNotFound)\n\t}\n\n\tfmt.Fprint(w, score)\n}\n\nfunc (p *PlayerServer) processWin(w http.ResponseWriter, player string) {\n\tp.store.RecordWin(player)\n\tw.WriteHeader(http.StatusAccepted)\n}\n"
  },
  {
    "path": "time/v3/server_integration_test.go",
    "content": "package poker\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestRecordingWinsAndRetrievingThem(t *testing.T) {\n\tdatabase, cleanDatabase := createTempFile(t, `[]`)\n\tdefer cleanDatabase()\n\tstore, err := NewFileSystemPlayerStore(database)\n\n\tassertNoError(t, err)\n\n\tserver := NewPlayerServer(store)\n\tplayer := \"Pepper\"\n\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\n\tt.Run(\"get score\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newGetScoreRequest(player))\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\n\t\tassertResponseBody(t, response.Body.String(), \"3\")\n\t})\n\n\tt.Run(\"get League\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newLeagueRequest())\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\t\twant := []Player{\n\t\t\t{\"Pepper\", 3},\n\t\t}\n\t\tassertLeague(t, got, want)\n\t})\n}\n"
  },
  {
    "path": "time/v3/server_test.go",
    "content": "package poker\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestGETPlayers(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{\n\t\t\t\"Pepper\": 20,\n\t\t\t\"Floyd\":  10,\n\t\t},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"returns Pepper's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Pepper\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"20\")\n\t})\n\n\tt.Run(\"returns Floyd's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Floyd\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"10\")\n\t})\n\n\tt.Run(\"returns 404 on missing players\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Apollo\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusNotFound)\n\t})\n}\n\nfunc TestStoreWins(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := NewPlayerServer(&store)\n\n\tt.Run(\"it records wins on POST\", func(t *testing.T) {\n\t\tplayer := \"Pepper\"\n\n\t\trequest := newPostWinRequest(player)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusAccepted)\n\t\tAssertPlayerWin(t, &store, player)\n\t})\n}\n\nfunc TestLeague(t *testing.T) {\n\n\tt.Run(\"it returns the League table as JSON\", func(t *testing.T) {\n\t\twantedLeague := []Player{\n\t\t\t{\"Cleo\", 32},\n\t\t\t{\"Chris\", 20},\n\t\t\t{\"Tiest\", 14},\n\t\t}\n\n\t\tstore := StubPlayerStore{nil, nil, wantedLeague}\n\t\tserver := NewPlayerServer(&store)\n\n\t\trequest := newLeagueRequest()\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t\tassertLeague(t, got, wantedLeague)\n\t\tassertContentType(t, response, jsonContentType)\n\n\t})\n}\n\nfunc assertContentType(t testing.TB, response *httptest.ResponseRecorder, want string) {\n\tt.Helper()\n\tif response.Header().Get(\"content-type\") != want {\n\t\tt.Errorf(\"response did not have content-type of %s, got %v\", want, response.Result().Header)\n\t}\n}\n\nfunc getLeagueFromResponse(t testing.TB, body io.Reader) []Player {\n\tt.Helper()\n\tleague, err := NewLeague(body)\n\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to parse response from server %q into slice of Player, '%v'\", body, err)\n\t}\n\n\treturn league\n}\n\nfunc assertLeague(t testing.TB, got, want []Player) {\n\tt.Helper()\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %v want %v\", got, want)\n\t}\n}\n\nfunc assertStatus(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"did not get correct status, got %d, want %d\", got, want)\n\t}\n}\n\nfunc newLeagueRequest() *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, \"/League\", nil)\n\treturn req\n}\n\nfunc newGetScoreRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc newPostWinRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodPost, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc assertResponseBody(t testing.TB, got, want string) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"response body is wrong, got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "time/v3/tape.go",
    "content": "package poker\n\nimport (\n\t\"io\"\n\t\"os\"\n)\n\ntype tape struct {\n\tfile *os.File\n}\n\nfunc (t *tape) Write(p []byte) (n int, err error) {\n\tt.file.Truncate(0)\n\tt.file.Seek(0, io.SeekStart)\n\treturn t.file.Write(p)\n}\n"
  },
  {
    "path": "time/v3/tape_test.go",
    "content": "package poker\n\nimport (\n\t\"io\"\n\t\"testing\"\n)\n\nfunc TestTape_Write(t *testing.T) {\n\tfile, clean := createTempFile(t, \"12345\")\n\tdefer clean()\n\n\ttape := &tape{file}\n\n\ttape.Write([]byte(\"abc\"))\n\n\tfile.Seek(0, io.SeekStart)\n\tnewFileContents, _ := io.ReadAll(file)\n\n\tgot := string(newFileContents)\n\twant := \"abc\"\n\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "time/v3/testing.go",
    "content": "package poker\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n)\n\n// StubPlayerStore implements PlayerStore for testing purposes.\ntype StubPlayerStore struct {\n\tScores   map[string]int\n\tWinCalls []string\n\tLeague   []Player\n}\n\n// GetPlayerScore returns a score from Scores.\nfunc (s *StubPlayerStore) GetPlayerScore(name string) int {\n\tscore := s.Scores[name]\n\treturn score\n}\n\n// RecordWin will record a win to WinCalls.\nfunc (s *StubPlayerStore) RecordWin(name string) {\n\ts.WinCalls = append(s.WinCalls, name)\n}\n\n// GetLeague returns League.\nfunc (s *StubPlayerStore) GetLeague() League {\n\treturn s.League\n}\n\n// AssertPlayerWin allows you to spy on the store's calls to RecordWin.\nfunc AssertPlayerWin(t testing.TB, store *StubPlayerStore, winner string) {\n\tt.Helper()\n\n\tif len(store.WinCalls) != 1 {\n\t\tt.Fatalf(\"got %d calls to RecordWin want %d\", len(store.WinCalls), 1)\n\t}\n\n\tif store.WinCalls[0] != winner {\n\t\tt.Errorf(\"did not store correct winner got %q want %q\", store.WinCalls[0], winner)\n\t}\n}\n\n// ScheduledAlert holds information about when an alert is scheduled.\ntype ScheduledAlert struct {\n\tAt     time.Duration\n\tAmount int\n}\n\nfunc (s ScheduledAlert) String() string {\n\treturn fmt.Sprintf(\"%d chips at %v\", s.Amount, s.At)\n}\n\n// SpyBlindAlerter allows you to spy on ScheduleAlertAt calls.\ntype SpyBlindAlerter struct {\n\tAlerts []ScheduledAlert\n}\n\n// ScheduleAlertAt records alerts that have been scheduled.\nfunc (s *SpyBlindAlerter) ScheduleAlertAt(at time.Duration, amount int) {\n\ts.Alerts = append(s.Alerts, ScheduledAlert{at, amount})\n}\n"
  },
  {
    "path": "time/v3/texas_holdem.go",
    "content": "package poker\n\nimport \"time\"\n\n// TexasHoldem manages a game of poker.\ntype TexasHoldem struct {\n\talerter BlindAlerter\n\tstore   PlayerStore\n}\n\n// NewTexasHoldem returns a new game.\nfunc NewTexasHoldem(alerter BlindAlerter, store PlayerStore) *TexasHoldem {\n\treturn &TexasHoldem{\n\t\talerter: alerter,\n\t\tstore:   store,\n\t}\n}\n\n// Start will schedule blind alerts dependant on the number of players.\nfunc (p *TexasHoldem) Start(numberOfPlayers int) {\n\tblindIncrement := time.Duration(5+numberOfPlayers) * time.Minute\n\n\tblinds := []int{100, 200, 300, 400, 500, 600, 800, 1000, 2000, 4000, 8000}\n\tblindTime := 0 * time.Second\n\tfor _, blind := range blinds {\n\t\tp.alerter.ScheduleAlertAt(blindTime, blind)\n\t\tblindTime = blindTime + blindIncrement\n\t}\n}\n\n// Finish ends the game, recording the winner.\nfunc (p *TexasHoldem) Finish(winner string) {\n\tp.store.RecordWin(winner)\n}\n"
  },
  {
    "path": "time/v3/texas_holdem_test.go",
    "content": "package poker_test\n\nimport (\n\t\"fmt\"\n\t\"github.com/quii/learn-go-with-tests/time/v3\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestGame_Start(t *testing.T) {\n\tt.Run(\"schedules alerts on game start for 5 players\", func(t *testing.T) {\n\t\tblindAlerter := &poker.SpyBlindAlerter{}\n\t\tgame := poker.NewTexasHoldem(blindAlerter, dummyPlayerStore)\n\n\t\tgame.Start(5)\n\n\t\tcases := []poker.ScheduledAlert{\n\t\t\t{At: 0 * time.Second, Amount: 100},\n\t\t\t{At: 10 * time.Minute, Amount: 200},\n\t\t\t{At: 20 * time.Minute, Amount: 300},\n\t\t\t{At: 30 * time.Minute, Amount: 400},\n\t\t\t{At: 40 * time.Minute, Amount: 500},\n\t\t\t{At: 50 * time.Minute, Amount: 600},\n\t\t\t{At: 60 * time.Minute, Amount: 800},\n\t\t\t{At: 70 * time.Minute, Amount: 1000},\n\t\t\t{At: 80 * time.Minute, Amount: 2000},\n\t\t\t{At: 90 * time.Minute, Amount: 4000},\n\t\t\t{At: 100 * time.Minute, Amount: 8000},\n\t\t}\n\n\t\tcheckSchedulingCases(cases, t, blindAlerter)\n\t})\n\n\tt.Run(\"schedules alerts on game start for 7 players\", func(t *testing.T) {\n\t\tblindAlerter := &poker.SpyBlindAlerter{}\n\t\tgame := poker.NewTexasHoldem(blindAlerter, dummyPlayerStore)\n\n\t\tgame.Start(7)\n\n\t\tcases := []poker.ScheduledAlert{\n\t\t\t{At: 0 * time.Second, Amount: 100},\n\t\t\t{At: 12 * time.Minute, Amount: 200},\n\t\t\t{At: 24 * time.Minute, Amount: 300},\n\t\t\t{At: 36 * time.Minute, Amount: 400},\n\t\t}\n\n\t\tcheckSchedulingCases(cases, t, blindAlerter)\n\t})\n\n}\n\nfunc TestGame_Finish(t *testing.T) {\n\tstore := &poker.StubPlayerStore{}\n\tgame := poker.NewTexasHoldem(dummyBlindAlerter, store)\n\twinner := \"Ruth\"\n\n\tgame.Finish(winner)\n\tpoker.AssertPlayerWin(t, store, winner)\n}\n\nfunc checkSchedulingCases(cases []poker.ScheduledAlert, t *testing.T, blindAlerter *poker.SpyBlindAlerter) {\n\tfor i, want := range cases {\n\t\tt.Run(fmt.Sprint(want), func(t *testing.T) {\n\n\t\t\tif len(blindAlerter.Alerts) <= i {\n\t\t\t\tt.Fatalf(\"alert %d was not scheduled %v\", i, blindAlerter.Alerts)\n\t\t\t}\n\n\t\t\tgot := blindAlerter.Alerts[i]\n\t\t\tassertScheduledAlert(t, got, want)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "time.md",
    "content": "# Time\n\n[**You can find all the code for this chapter here**](https://github.com/quii/learn-go-with-tests/tree/main/time)\n\nThe product owner wants us to expand the functionality of our command line application by helping a group of people play Texas-Holdem Poker.\n\n## Just enough information on poker\n\nYou won't need to know much about poker, only that at certain time intervals all the players need to be informed of a steadily increasing \"blind\" value.\n\nOur application will help keep track of when the blind should go up, and how much it should be.\n\n* When it starts it asks how many players are playing. This determines the amount of time there is before the \"blind\" bet goes up.\n  * There is a base amount of time of 5 minutes.\n  * For every player, 1 minute is added.\n  * e.g 6 players equals 11 minutes for the blind.\n* After the blind time expires the game should alert the players the new amount the blind bet is.\n* The blind starts at 100 chips, then 200, 400, 600, 1000, 2000 and continue to double until the game ends (our previous functionality of \"Ruth wins\" should still finish the game)\n\n## Reminder of the code\n\nIn the previous chapter we made our start to the command line application which already accepts a command of `{name} wins`. Here is what the current `CLI` code looks like, but be sure to familiarise yourself with the other code too before starting.\n\n```go\ntype CLI struct {\n\tplayerStore PlayerStore\n\tin          *bufio.Scanner\n}\n\nfunc NewCLI(store PlayerStore, in io.Reader) *CLI {\n\treturn &CLI{\n\t\tplayerStore: store,\n\t\tin:          bufio.NewScanner(in),\n\t}\n}\n\nfunc (cli *CLI) PlayPoker() {\n\tuserInput := cli.readLine()\n\tcli.playerStore.RecordWin(extractWinner(userInput))\n}\n\nfunc extractWinner(userInput string) string {\n\treturn strings.Replace(userInput, \" wins\", \"\", 1)\n}\n\nfunc (cli *CLI) readLine() string {\n\tcli.in.Scan()\n\treturn cli.in.Text()\n}\n```\n\n### `time.AfterFunc`\n\nWe want to be able to schedule our program to print the blind bet values at certain durations dependant on the number of players.\n\nTo limit the scope of what we need to do, we'll forget about the number of players part for now and just assume there are 5 players so we'll test that _every 10 minutes the new value of the blind bet is printed_.\n\nAs usual the standard library has us covered with [`func AfterFunc(d Duration, f func()) *Timer`](https://golang.org/pkg/time/#AfterFunc)\n\n> `AfterFunc` waits for the duration to elapse and then calls f in its own goroutine. It returns a `Timer` that can be used to cancel the call using its Stop method.\n\n### [`time.Duration`](https://golang.org/pkg/time/#Duration)\n\n> A Duration represents the elapsed time between two instants as an int64 nanosecond count.\n\nThe time library has a number of constants to let you multiply those nanoseconds so they're a bit more readable for the kind of scenarios we'll be doing\n\n```\n5 * time.Second\n```\n\nWhen we call `PlayPoker` we'll schedule all of our blind alerts.\n\nTesting this may be a little tricky though. We'll want to verify that each time period is scheduled with the correct blind amount but if you look at the signature of `time.AfterFunc` its second argument is the function it will run. You cannot compare functions in Go so we'd be unable to test what function has been sent in. So we'll need to write some kind of wrapper around `time.AfterFunc` which will take the time to run and the amount to print so we can spy on that.\n\n## Write the test first\n\nAdd a new test to our suite\n\n```go\nt.Run(\"it schedules printing of blind values\", func(t *testing.T) {\n\tin := strings.NewReader(\"Chris wins\\n\")\n\tplayerStore := &poker.StubPlayerStore{}\n\tblindAlerter := &SpyBlindAlerter{}\n\n\tcli := poker.NewCLI(playerStore, in, blindAlerter)\n\tcli.PlayPoker()\n\n\tif len(blindAlerter.alerts) != 1 {\n\t\tt.Fatal(\"expected a blind alert to be scheduled\")\n\t}\n})\n```\n\nYou'll notice we've made a `SpyBlindAlerter` which we are trying to inject into our `CLI` and then checking that after we call `PlayPoker` that an alert is scheduled.\n\n(Remember we are just going for the simplest scenario first and then we'll iterate.)\n\nHere's the definition of `SpyBlindAlerter`\n\n```go\ntype SpyBlindAlerter struct {\n\talerts []struct {\n\t\tscheduledAt time.Duration\n\t\tamount      int\n\t}\n}\n\nfunc (s *SpyBlindAlerter) ScheduleAlertAt(duration time.Duration, amount int) {\n\ts.alerts = append(s.alerts, struct {\n\t\tscheduledAt time.Duration\n\t\tamount      int\n\t}{duration, amount})\n}\n```\n\n## Try to run the test\n\n```\n./CLI_test.go:32:27: too many arguments in call to poker.NewCLI\n\thave (*poker.StubPlayerStore, *strings.Reader, *SpyBlindAlerter)\n\twant (poker.PlayerStore, io.Reader)\n```\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\nWe have added a new argument and the compiler is complaining. _Strictly speaking_ the minimal amount of code is to make `NewCLI` accept a `*SpyBlindAlerter` but let's cheat a little and just define the dependency as an interface.\n\n```go\ntype BlindAlerter interface {\n\tScheduleAlertAt(duration time.Duration, amount int)\n}\n```\n\nAnd then add it to the constructor\n\n```go\nfunc NewCLI(store PlayerStore, in io.Reader, alerter BlindAlerter) *CLI\n```\n\nYour other tests will now fail as they don't have a `BlindAlerter` passed in to `NewCLI`.\n\nSpying on BlindAlerter is not relevant for the other tests so in the test file add\n\n```go\nvar dummySpyAlerter = &SpyBlindAlerter{}\n```\n\nThen use that in the other tests to fix the compilation problems. By labelling it as a \"dummy\" it is clear to the reader of the test that it is not important.\n\n[> Dummy objects are passed around but never actually used. Usually they are just used to fill parameter lists.](https://martinfowler.com/articles/mocksArentStubs.html)\n\nThe tests should now compile and our new test fails.\n\n```\n=== RUN   TestCLI\n=== RUN   TestCLI/it_schedules_printing_of_blind_values\n--- FAIL: TestCLI (0.00s)\n    --- FAIL: TestCLI/it_schedules_printing_of_blind_values (0.00s)\n    \tCLI_test.go:38: expected a blind alert to be scheduled\n```\n\n## Write enough code to make it pass\n\nWe'll need to add the `BlindAlerter` as a field on our `CLI` so we can reference it in our `PlayPoker` method.\n\n```go\ntype CLI struct {\n\tplayerStore PlayerStore\n\tin          *bufio.Scanner\n\talerter     BlindAlerter\n}\n\nfunc NewCLI(store PlayerStore, in io.Reader, alerter BlindAlerter) *CLI {\n\treturn &CLI{\n\t\tplayerStore: store,\n\t\tin:          bufio.NewScanner(in),\n\t\talerter:     alerter,\n\t}\n}\n```\n\nTo make the test pass, we can call our `BlindAlerter` with anything we like\n\n```go\nfunc (cli *CLI) PlayPoker() {\n\tcli.alerter.ScheduleAlertAt(5*time.Second, 100)\n\tuserInput := cli.readLine()\n\tcli.playerStore.RecordWin(extractWinner(userInput))\n}\n```\n\nNext we'll want to check it schedules all the alerts we'd hope for, for 5 players\n\n## Write the test first\n\n```go\n\tt.Run(\"it schedules printing of blind values\", func(t *testing.T) {\n\t\tin := strings.NewReader(\"Chris wins\\n\")\n\t\tplayerStore := &poker.StubPlayerStore{}\n\t\tblindAlerter := &SpyBlindAlerter{}\n\n\t\tcli := poker.NewCLI(playerStore, in, blindAlerter)\n\t\tcli.PlayPoker()\n\n\t\tcases := []struct {\n\t\t\texpectedScheduleTime time.Duration\n\t\t\texpectedAmount       int\n\t\t}{\n\t\t\t{0 * time.Second, 100},\n\t\t\t{10 * time.Minute, 200},\n\t\t\t{20 * time.Minute, 300},\n\t\t\t{30 * time.Minute, 400},\n\t\t\t{40 * time.Minute, 500},\n\t\t\t{50 * time.Minute, 600},\n\t\t\t{60 * time.Minute, 800},\n\t\t\t{70 * time.Minute, 1000},\n\t\t\t{80 * time.Minute, 2000},\n\t\t\t{90 * time.Minute, 4000},\n\t\t\t{100 * time.Minute, 8000},\n\t\t}\n\n\t\tfor i, c := range cases {\n\t\t\tt.Run(fmt.Sprintf(\"%d scheduled for %v\", c.expectedAmount, c.expectedScheduleTime), func(t *testing.T) {\n\n\t\t\t\tif len(blindAlerter.alerts) <= i {\n\t\t\t\t\tt.Fatalf(\"alert %d was not scheduled %v\", i, blindAlerter.alerts)\n\t\t\t\t}\n\n\t\t\t\talert := blindAlerter.alerts[i]\n\n\t\t\t\tamountGot := alert.amount\n\t\t\t\tif amountGot != c.expectedAmount {\n\t\t\t\t\tt.Errorf(\"got amount %d, want %d\", amountGot, c.expectedAmount)\n\t\t\t\t}\n\n\t\t\t\tgotScheduledTime := alert.scheduledAt\n\t\t\t\tif gotScheduledTime != c.expectedScheduleTime {\n\t\t\t\t\tt.Errorf(\"got scheduled time of %v, want %v\", gotScheduledTime, c.expectedScheduleTime)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n```\n\nTable-based test works nicely here and clearly illustrate what our requirements are. We run through the table and check the `SpyBlindAlerter` to see if the alert has been scheduled with the correct values.\n\n## Try to run the test\n\nYou should have a lot of failures looking like this\n\n```\n=== RUN   TestCLI\n--- FAIL: TestCLI (0.00s)\n=== RUN   TestCLI/it_schedules_printing_of_blind_values\n    --- FAIL: TestCLI/it_schedules_printing_of_blind_values (0.00s)\n=== RUN   TestCLI/it_schedules_printing_of_blind_values/100_scheduled_for_0s\n        --- FAIL: TestCLI/it_schedules_printing_of_blind_values/100_scheduled_for_0s (0.00s)\n        \tCLI_test.go:71: got scheduled time of 5s, want 0s\n=== RUN   TestCLI/it_schedules_printing_of_blind_values/200_scheduled_for_10m0s\n        --- FAIL: TestCLI/it_schedules_printing_of_blind_values/200_scheduled_for_10m0s (0.00s)\n        \tCLI_test.go:59: alert 1 was not scheduled [{5000000000 100}]\n```\n\n## Write enough code to make it pass\n\n```go\nfunc (cli *CLI) PlayPoker() {\n\n\tblinds := []int{100, 200, 300, 400, 500, 600, 800, 1000, 2000, 4000, 8000}\n\tblindTime := 0 * time.Second\n\tfor _, blind := range blinds {\n\t\tcli.alerter.ScheduleAlertAt(blindTime, blind)\n\t\tblindTime = blindTime + 10*time.Minute\n\t}\n\n\tuserInput := cli.readLine()\n\tcli.playerStore.RecordWin(extractWinner(userInput))\n}\n```\n\nIt's not a lot more complicated than what we already had. We're just now iterating over an array of `blinds` and calling the scheduler on an increasing `blindTime`\n\n## Refactor\n\nWe can encapsulate our scheduled alerts into a method just to make `PlayPoker` read a little clearer.\n\n```go\nfunc (cli *CLI) PlayPoker() {\n\tcli.scheduleBlindAlerts()\n\tuserInput := cli.readLine()\n\tcli.playerStore.RecordWin(extractWinner(userInput))\n}\n\nfunc (cli *CLI) scheduleBlindAlerts() {\n\tblinds := []int{100, 200, 300, 400, 500, 600, 800, 1000, 2000, 4000, 8000}\n\tblindTime := 0 * time.Second\n\tfor _, blind := range blinds {\n\t\tcli.alerter.ScheduleAlertAt(blindTime, blind)\n\t\tblindTime = blindTime + 10*time.Minute\n\t}\n}\n```\n\nFinally our tests are looking a little clunky. We have two anonymous structs representing the same thing, a `ScheduledAlert`. Let's refactor that into a new type and then make some helpers to compare them.\n\n```go\ntype scheduledAlert struct {\n\tat     time.Duration\n\tamount int\n}\n\nfunc (s scheduledAlert) String() string {\n\treturn fmt.Sprintf(\"%d chips at %v\", s.amount, s.at)\n}\n\ntype SpyBlindAlerter struct {\n\talerts []scheduledAlert\n}\n\nfunc (s *SpyBlindAlerter) ScheduleAlertAt(at time.Duration, amount int) {\n\ts.alerts = append(s.alerts, scheduledAlert{at, amount})\n}\n```\n\nWe've added a `String()` method to our type so it prints nicely if the test fails\n\nUpdate our test to use our new type\n\n```go\nt.Run(\"it schedules printing of blind values\", func(t *testing.T) {\n\tin := strings.NewReader(\"Chris wins\\n\")\n\tplayerStore := &poker.StubPlayerStore{}\n\tblindAlerter := &SpyBlindAlerter{}\n\n\tcli := poker.NewCLI(playerStore, in, blindAlerter)\n\tcli.PlayPoker()\n\n\tcases := []scheduledAlert{\n\t\t{0 * time.Second, 100},\n\t\t{10 * time.Minute, 200},\n\t\t{20 * time.Minute, 300},\n\t\t{30 * time.Minute, 400},\n\t\t{40 * time.Minute, 500},\n\t\t{50 * time.Minute, 600},\n\t\t{60 * time.Minute, 800},\n\t\t{70 * time.Minute, 1000},\n\t\t{80 * time.Minute, 2000},\n\t\t{90 * time.Minute, 4000},\n\t\t{100 * time.Minute, 8000},\n\t}\n\n\tfor i, want := range cases {\n\t\tt.Run(fmt.Sprint(want), func(t *testing.T) {\n\n\t\t\tif len(blindAlerter.alerts) <= i {\n\t\t\t\tt.Fatalf(\"alert %d was not scheduled %v\", i, blindAlerter.alerts)\n\t\t\t}\n\n\t\t\tgot := blindAlerter.alerts[i]\n\t\t\tassertScheduledAlert(t, got, want)\n\t\t})\n\t}\n})\n```\n\nImplement `assertScheduledAlert` yourself.\n\nWe've spent a fair amount of time here writing tests and have been somewhat naughty not integrating with our application. Let's address that before we pile on any more requirements.\n\nTry running the app and it won't compile, complaining about not enough args to `NewCLI`.\n\nLet's create an implementation of `BlindAlerter` that we can use in our application.\n\nCreate `blind_alerter.go` and move our `BlindAlerter` interface and add the new things below\n\n```go\npackage poker\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"time\"\n)\n\ntype BlindAlerter interface {\n\tScheduleAlertAt(duration time.Duration, amount int)\n}\n\ntype BlindAlerterFunc func(duration time.Duration, amount int)\n\nfunc (a BlindAlerterFunc) ScheduleAlertAt(duration time.Duration, amount int) {\n\ta(duration, amount)\n}\n\nfunc StdOutAlerter(duration time.Duration, amount int) {\n\ttime.AfterFunc(duration, func() {\n\t\tfmt.Fprintf(os.Stdout, \"Blind is now %d\\n\", amount)\n\t})\n}\n```\n\nRemember that any _type_ can implement an interface, not just `structs`. If you are making a library that exposes an interface with one function defined it is a common idiom to also expose a `MyInterfaceFunc` type.\n\nThis type will be a `func` which will also implement your interface. That way users of your interface have the option to implement your interface with just a function; rather than having to create an empty `struct` type.\n\nWe then create the function `StdOutAlerter` which has the same signature as the function and just use `time.AfterFunc` to schedule it to print to `os.Stdout`.\n\nUpdate `main` where we create `NewCLI` to see this in action\n\n```go\npoker.NewCLI(store, os.Stdin, poker.BlindAlerterFunc(poker.StdOutAlerter)).PlayPoker()\n```\n\nBefore running you might want to change the `blindTime` increment in `CLI` to be 10 seconds rather than 10 minutes just so you can see it in action.\n\nYou should see it print the blind values as we'd expect every 10 seconds. Notice how you can still type `Shaun wins` into the CLI and it will stop the program how we'd expect.\n\nThe game won't always be played with 5 people so we need to prompt the user to enter a number of players before the game starts.\n\n## Write the test first\n\nTo check we are prompting for the number of players we'll want to record what is written to StdOut. We've done this a few times now, we know that `os.Stdout` is an `io.Writer` so we can check what is written if we use dependency injection to pass in a `bytes.Buffer` in our test and see what our code will write.\n\nWe don't care about our other collaborators in this test just yet so we've made some dummies in our test file.\n\nWe should be a little wary that we now have 4 dependencies for `CLI`, that feels like maybe it is starting to have too many responsibilities. Let's live with it for now and see if a refactoring emerges as we add this new functionality.\n\n```go\nvar dummyBlindAlerter = &SpyBlindAlerter{}\nvar dummyPlayerStore = &poker.StubPlayerStore{}\nvar dummyStdIn = &bytes.Buffer{}\nvar dummyStdOut = &bytes.Buffer{}\n```\n\nHere is our new test\n\n```go\nt.Run(\"it prompts the user to enter the number of players\", func(t *testing.T) {\n\tstdout := &bytes.Buffer{}\n\tcli := poker.NewCLI(dummyPlayerStore, dummyStdIn, stdout, dummyBlindAlerter)\n\tcli.PlayPoker()\n\n\tgot := stdout.String()\n\twant := \"Please enter the number of players: \"\n\n\tif got != want {\n\t\tt.Errorf(\"got %q, want %q\", got, want)\n\t}\n})\n```\n\nWe pass in what will be `os.Stdout` in `main` and see what is written.\n\n## Try to run the test\n\n```\n./CLI_test.go:38:27: too many arguments in call to poker.NewCLI\n\thave (*poker.StubPlayerStore, *bytes.Buffer, *bytes.Buffer, *SpyBlindAlerter)\n\twant (poker.PlayerStore, io.Reader, poker.BlindAlerter)\n```\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\nWe have a new dependency so we'll have to update `NewCLI`\n\n```go\nfunc NewCLI(store PlayerStore, in io.Reader, out io.Writer, alerter BlindAlerter) *CLI\n```\n\nNow the _other_ tests will fail to compile because they don't have an `io.Writer` being passed into `NewCLI`.\n\nAdd `dummyStdout` for the other tests.\n\nThe new test should fail like so\n\n```\n=== RUN   TestCLI\n--- FAIL: TestCLI (0.00s)\n=== RUN   TestCLI/it_prompts_the_user_to_enter_the_number_of_players\n    --- FAIL: TestCLI/it_prompts_the_user_to_enter_the_number_of_players (0.00s)\n    \tCLI_test.go:46: got '', want 'Please enter the number of players: '\nFAIL\n```\n\n## Write enough code to make it pass\n\nWe need to add our new dependency to our `CLI` so we can reference it in `PlayPoker`\n\n```go\ntype CLI struct {\n\tplayerStore PlayerStore\n\tin          *bufio.Scanner\n\tout         io.Writer\n\talerter     BlindAlerter\n}\n\nfunc NewCLI(store PlayerStore, in io.Reader, out io.Writer, alerter BlindAlerter) *CLI {\n\treturn &CLI{\n\t\tplayerStore: store,\n\t\tin:          bufio.NewScanner(in),\n\t\tout:         out,\n\t\talerter:     alerter,\n\t}\n}\n```\n\nThen finally we can write our prompt at the start of the game\n\n```go\nfunc (cli *CLI) PlayPoker() {\n\tfmt.Fprint(cli.out, \"Please enter the number of players: \")\n\tcli.scheduleBlindAlerts()\n\tuserInput := cli.readLine()\n\tcli.playerStore.RecordWin(extractWinner(userInput))\n}\n```\n\n## Refactor\n\nWe have a duplicate string for the prompt which we should extract into a constant\n\n```go\nconst PlayerPrompt = \"Please enter the number of players: \"\n```\n\nUse this in both the test code and `CLI`.\n\nNow we need to send in a number and extract it out. The only way we'll know if it has had the desired effect is by seeing what blind alerts were scheduled.\n\n## Write the test first\n\n```go\nt.Run(\"it prompts the user to enter the number of players\", func(t *testing.T) {\n\tstdout := &bytes.Buffer{}\n\tin := strings.NewReader(\"7\\n\")\n\tblindAlerter := &SpyBlindAlerter{}\n\n\tcli := poker.NewCLI(dummyPlayerStore, in, stdout, blindAlerter)\n\tcli.PlayPoker()\n\n\tgot := stdout.String()\n\twant := poker.PlayerPrompt\n\n\tif got != want {\n\t\tt.Errorf(\"got %q, want %q\", got, want)\n\t}\n\n\tcases := []scheduledAlert{\n\t\t{0 * time.Second, 100},\n\t\t{12 * time.Minute, 200},\n\t\t{24 * time.Minute, 300},\n\t\t{36 * time.Minute, 400},\n\t}\n\n\tfor i, want := range cases {\n\t\tt.Run(fmt.Sprint(want), func(t *testing.T) {\n\n\t\t\tif len(blindAlerter.alerts) <= i {\n\t\t\t\tt.Fatalf(\"alert %d was not scheduled %v\", i, blindAlerter.alerts)\n\t\t\t}\n\n\t\t\tgot := blindAlerter.alerts[i]\n\t\t\tassertScheduledAlert(t, got, want)\n\t\t})\n\t}\n})\n```\n\nOuch! A lot of changes.\n\n* We remove our dummy for StdIn and instead send in a mocked version representing our user entering 7\n* We also remove our dummy on the blind alerter so we can see that the number of players has had an effect on the scheduling\n* We test what alerts are scheduled\n\n## Try to run the test\n\nThe test should still compile and fail reporting that the scheduled times are wrong because we've hard-coded for the game to be based on having 5 players\n\n```\n=== RUN   TestCLI\n--- FAIL: TestCLI (0.00s)\n=== RUN   TestCLI/it_prompts_the_user_to_enter_the_number_of_players\n    --- FAIL: TestCLI/it_prompts_the_user_to_enter_the_number_of_players (0.00s)\n=== RUN   TestCLI/it_prompts_the_user_to_enter_the_number_of_players/100_chips_at_0s\n        --- PASS: TestCLI/it_prompts_the_user_to_enter_the_number_of_players/100_chips_at_0s (0.00s)\n=== RUN   TestCLI/it_prompts_the_user_to_enter_the_number_of_players/200_chips_at_12m0s\n```\n\n## Write enough code to make it pass\n\nRemember, we are free to commit whatever sins we need to make this work. Once we have working software we can then work on refactoring the mess we're about to make!\n\n```go\nfunc (cli *CLI) PlayPoker() {\n\tfmt.Fprint(cli.out, PlayerPrompt)\n\n\tnumberOfPlayers, _ := strconv.Atoi(cli.readLine())\n\n\tcli.scheduleBlindAlerts(numberOfPlayers)\n\n\tuserInput := cli.readLine()\n\tcli.playerStore.RecordWin(extractWinner(userInput))\n}\n\nfunc (cli *CLI) scheduleBlindAlerts(numberOfPlayers int) {\n\tblindIncrement := time.Duration(5+numberOfPlayers) * time.Minute\n\n\tblinds := []int{100, 200, 300, 400, 500, 600, 800, 1000, 2000, 4000, 8000}\n\tblindTime := 0 * time.Second\n\tfor _, blind := range blinds {\n\t\tcli.alerter.ScheduleAlertAt(blindTime, blind)\n\t\tblindTime = blindTime + blindIncrement\n\t}\n}\n```\n\n* We read in the `numberOfPlayersInput` into a string\n* We use `cli.readLine()` to get the input from the user and then call `Atoi` to convert it into an integer - ignoring any error scenarios. We'll need to write a test for that scenario later.\n* From here we change `scheduleBlindAlerts` to accept a number of players. We then calculate a `blindIncrement` time to use to add to `blindTime` as we iterate over the blind amounts\n\nWhile our new test has been fixed, a lot of others have failed because now our system only works if the game starts with a user entering a number. You'll need to fix the tests by changing the user inputs so that a number followed by a newline is added (this is highlighting yet more flaws in our approach right now).\n\n## Refactor\n\nThis all feels a bit horrible right? Let's **listen to our tests**.\n\n* In order to test that we are scheduling some alerts we set up 4 different dependencies. Whenever you have a lot of dependencies for a _thing_ in your system, it implies it's doing too much. Visually we can see it in how cluttered our test is.\n* To me it feels like **we need to make a cleaner abstraction between reading user input and the business logic we want to do**\n* A better test would be _given this user input, do we call a new type `Game` with the correct number of players_.\n* We would then extract the testing of the scheduling into the tests for our new `Game`.\n\nWe can refactor toward our `Game` first and our test should continue to pass. Once we've made the structural changes we want we can think about how we can refactor the tests to reflect our new separation of concerns\n\nRemember when making changes in refactoring try to keep them as small as possible and keep re-running the tests.\n\nTry it yourself first. Think about the boundaries of what a `Game` would offer and what our `CLI` should be doing.\n\nFor now **don't** change the external interface of `NewCLI` as we don't want to change the test code and the client code at the same time as that is too much to juggle and we could end up breaking things.\n\nThis is what I came up with:\n\n```go\n// game.go\ntype Game struct {\n\talerter BlindAlerter\n\tstore   PlayerStore\n}\n\nfunc (p *Game) Start(numberOfPlayers int) {\n\tblindIncrement := time.Duration(5+numberOfPlayers) * time.Minute\n\n\tblinds := []int{100, 200, 300, 400, 500, 600, 800, 1000, 2000, 4000, 8000}\n\tblindTime := 0 * time.Second\n\tfor _, blind := range blinds {\n\t\tp.alerter.ScheduleAlertAt(blindTime, blind)\n\t\tblindTime = blindTime + blindIncrement\n\t}\n}\n\nfunc (p *Game) Finish(winner string) {\n\tp.store.RecordWin(winner)\n}\n\n// cli.go\ntype CLI struct {\n\tin   *bufio.Scanner\n\tout  io.Writer\n\tgame *Game\n}\n\nfunc NewCLI(store PlayerStore, in io.Reader, out io.Writer, alerter BlindAlerter) *CLI {\n\treturn &CLI{\n\t\tin:  bufio.NewScanner(in),\n\t\tout: out,\n\t\tgame: &Game{\n\t\t\talerter: alerter,\n\t\t\tstore:   store,\n\t\t},\n\t}\n}\n\nconst PlayerPrompt = \"Please enter the number of players: \"\n\nfunc (cli *CLI) PlayPoker() {\n\tfmt.Fprint(cli.out, PlayerPrompt)\n\n\tnumberOfPlayersInput := cli.readLine()\n\tnumberOfPlayers, _ := strconv.Atoi(strings.Trim(numberOfPlayersInput, \"\\n\"))\n\n\tcli.game.Start(numberOfPlayers)\n\n\twinnerInput := cli.readLine()\n\twinner := extractWinner(winnerInput)\n\n\tcli.game.Finish(winner)\n}\n\nfunc extractWinner(userInput string) string {\n\treturn strings.Replace(userInput, \" wins\\n\", \"\", 1)\n}\n\nfunc (cli *CLI) readLine() string {\n\tcli.in.Scan()\n\treturn cli.in.Text()\n}\n```\n\nFrom a \"domain\" perspective:\n\n* We want to `Start` a `Game`, indicating how many people are playing\n* We want to `Finish` a `Game`, declaring the winner\n\nThe new `Game` type encapsulates this for us.\n\nWith this change we've passed `BlindAlerter` and `PlayerStore` to `Game` as it is now responsible for alerting and storing results.\n\nOur `CLI` is now just concerned with:\n\n* Constructing `Game` with its existing dependencies (which we'll refactor next)\n* Interpreting user input as method invocations for `Game`\n\nWe want to try to avoid doing \"big\" refactors which leave us in a state of failing tests for extended periods as that increases the chances of mistakes. (If you are working in a large/distributed team this is extra important)\n\nThe first thing we'll do is refactor `Game` so that we inject it into `CLI`. We'll do the smallest changes in our tests to facilitate that and then we'll see how we can break up the tests into the themes of parsing user input and game management.\n\nAll we need to do right now is change `NewCLI`\n\n```go\nfunc NewCLI(in io.Reader, out io.Writer, game *Game) *CLI {\n\treturn &CLI{\n\t\tin:   bufio.NewScanner(in),\n\t\tout:  out,\n\t\tgame: game,\n\t}\n}\n```\n\nThis feels like an improvement already. We have less dependencies and _our dependency list is reflecting our overall design goal_ of CLI being concerned with input/output and delegating game specific actions to a `Game`.\n\nIf you try and compile there are problems. You should be able to fix these problems yourself. Don't worry about making any mocks for `Game` right now, just initialise _real_ `Game`s just to get everything compiling and tests green.\n\nTo do this you'll need to make a constructor\n\n```go\nfunc NewGame(alerter BlindAlerter, store PlayerStore) *Game {\n\treturn &Game{\n\t\talerter: alerter,\n\t\tstore:   store,\n\t}\n}\n```\n\nHere's an example of one of the setups for the tests being fixed\n\n```go\nstdout := &bytes.Buffer{}\nin := strings.NewReader(\"7\\n\")\nblindAlerter := &SpyBlindAlerter{}\ngame := poker.NewGame(blindAlerter, dummyPlayerStore)\n\ncli := poker.NewCLI(in, stdout, game)\ncli.PlayPoker()\n```\n\nIt shouldn't take much effort to fix the tests and be back to green again (that's the point!) but make sure you fix `main.go` too before the next stage.\n\n```go\n// main.go\ngame := poker.NewGame(poker.BlindAlerterFunc(poker.StdOutAlerter), store)\ncli := poker.NewCLI(os.Stdin, os.Stdout, game)\ncli.PlayPoker()\n```\n\nNow that we have extracted out `Game` we should move our game specific assertions into tests separate from CLI.\n\nThis is just an exercise in copying our `CLI` tests but with less dependencies\n\n```go\nfunc TestGame_Start(t *testing.T) {\n\tt.Run(\"schedules alerts on game start for 5 players\", func(t *testing.T) {\n\t\tblindAlerter := &poker.SpyBlindAlerter{}\n\t\tgame := poker.NewGame(blindAlerter, dummyPlayerStore)\n\n\t\tgame.Start(5)\n\n\t\tcases := []poker.ScheduledAlert{\n\t\t\t{At: 0 * time.Second, Amount: 100},\n\t\t\t{At: 10 * time.Minute, Amount: 200},\n\t\t\t{At: 20 * time.Minute, Amount: 300},\n\t\t\t{At: 30 * time.Minute, Amount: 400},\n\t\t\t{At: 40 * time.Minute, Amount: 500},\n\t\t\t{At: 50 * time.Minute, Amount: 600},\n\t\t\t{At: 60 * time.Minute, Amount: 800},\n\t\t\t{At: 70 * time.Minute, Amount: 1000},\n\t\t\t{At: 80 * time.Minute, Amount: 2000},\n\t\t\t{At: 90 * time.Minute, Amount: 4000},\n\t\t\t{At: 100 * time.Minute, Amount: 8000},\n\t\t}\n\n\t\tcheckSchedulingCases(cases, t, blindAlerter)\n\t})\n\n\tt.Run(\"schedules alerts on game start for 7 players\", func(t *testing.T) {\n\t\tblindAlerter := &poker.SpyBlindAlerter{}\n\t\tgame := poker.NewGame(blindAlerter, dummyPlayerStore)\n\n\t\tgame.Start(7)\n\n\t\tcases := []poker.ScheduledAlert{\n\t\t\t{At: 0 * time.Second, Amount: 100},\n\t\t\t{At: 12 * time.Minute, Amount: 200},\n\t\t\t{At: 24 * time.Minute, Amount: 300},\n\t\t\t{At: 36 * time.Minute, Amount: 400},\n\t\t}\n\n\t\tcheckSchedulingCases(cases, t, blindAlerter)\n\t})\n\n}\n\nfunc TestGame_Finish(t *testing.T) {\n\tstore := &poker.StubPlayerStore{}\n\tgame := poker.NewGame(dummyBlindAlerter, store)\n\twinner := \"Ruth\"\n\n\tgame.Finish(winner)\n\tpoker.AssertPlayerWin(t, store, winner)\n}\n```\n\nThe intent behind what happens when a game of poker starts is now much clearer.\n\nMake sure to also move over the test for when the game ends.\n\nOnce we are happy we have moved the tests over for game logic we can simplify our CLI tests so they reflect our intended responsibilities clearer\n\n* Process user input and call `Game`'s methods when appropriate\n* Send output\n* Crucially it doesn't know about the actual workings of how games work\n\nTo do this we'll have to make it so `CLI` no longer relies on a concrete `Game` type but instead accepts an interface with `Start(numberOfPlayers)` and `Finish(winner)`. We can then create a spy of that type and verify the correct calls are made.\n\nIt's here we realise that naming is awkward sometimes. Rename `Game` to `TexasHoldem` (as that's the _kind_ of game we're playing) and the new interface will be called `Game`. This keeps faithful to the notion that our CLI is oblivious to the actual game we're playing and what happens when you `Start` and `Finish`.\n\n```go\ntype Game interface {\n\tStart(numberOfPlayers int)\n\tFinish(winner string)\n}\n```\n\nReplace all references to `*Game` inside `CLI` and replace them with `Game` (our new interface). As always keep re-running tests to check everything is green while we are refactoring.\n\nNow that we have decoupled `CLI` from `TexasHoldem` we can use spies to check that `Start` and `Finish` are called when we expect them to, with the correct arguments.\n\nCreate a spy that implements `Game`\n\n```go\ntype GameSpy struct {\n\tStartedWith  int\n\tFinishedWith string\n}\n\nfunc (g *GameSpy) Start(numberOfPlayers int) {\n\tg.StartedWith = numberOfPlayers\n}\n\nfunc (g *GameSpy) Finish(winner string) {\n\tg.FinishedWith = winner\n}\n```\n\nReplace any `CLI` test which is testing any game specific logic with checks on how our `GameSpy` is called. This will then reflect the responsibilities of CLI in our tests clearly.\n\nHere is an example of one of the tests being fixed; try and do the rest yourself and check the source code if you get stuck.\n\n```go\n\tt.Run(\"it prompts the user to enter the number of players and starts the game\", func(t *testing.T) {\n\t\tstdout := &bytes.Buffer{}\n\t\tin := strings.NewReader(\"7\\n\")\n\t\tgame := &GameSpy{}\n\n\t\tcli := poker.NewCLI(in, stdout, game)\n\t\tcli.PlayPoker()\n\n\t\tgotPrompt := stdout.String()\n\t\twantPrompt := poker.PlayerPrompt\n\n\t\tif gotPrompt != wantPrompt {\n\t\t\tt.Errorf(\"got %q, want %q\", gotPrompt, wantPrompt)\n\t\t}\n\n\t\tif game.StartedWith != 7 {\n\t\t\tt.Errorf(\"wanted Start called with 7 but got %d\", game.StartedWith)\n\t\t}\n\t})\n```\n\nNow that we have a clean separation of concerns, checking edge cases around IO in our `CLI` should be easier.\n\nWe need to address the scenario where a user puts a non numeric value when prompted for the number of players:\n\nOur code should not start the game and it should print a handy error to the user and then exit.\n\n## Write the test first\n\nWe'll start by making sure the game doesn't start\n\n```go\nt.Run(\"it prints an error when a non numeric value is entered and does not start the game\", func(t *testing.T) {\n\tstdout := &bytes.Buffer{}\n\tin := strings.NewReader(\"Pies\\n\")\n\tgame := &GameSpy{}\n\n\tcli := poker.NewCLI(in, stdout, game)\n\tcli.PlayPoker()\n\n\tif game.StartCalled {\n\t\tt.Errorf(\"game should not have started\")\n\t}\n})\n```\n\nYou'll need to add to our `GameSpy` a field `StartCalled` which only gets set if `Start` is called\n\n## Try to run the test\n\n```\n=== RUN   TestCLI/it_prints_an_error_when_a_non_numeric_value_is_entered_and_does_not_start_the_game\n    --- FAIL: TestCLI/it_prints_an_error_when_a_non_numeric_value_is_entered_and_does_not_start_the_game (0.00s)\n        CLI_test.go:62: game should not have started\n```\n\n## Write enough code to make it pass\n\nAround where we call `Atoi` we just need to check for the error\n\n```go\nnumberOfPlayers, err := strconv.Atoi(cli.readLine())\n\nif err != nil {\n\treturn\n}\n```\n\nNext we need to inform the user of what they did wrong so we'll assert on what is printed to `stdout`.\n\n## Write the test first\n\nWe've asserted on what was printed to `stdout` before so we can copy that code for now\n\n```go\ngotPrompt := stdout.String()\n\nwantPrompt := poker.PlayerPrompt + \"you're so silly\"\n\nif gotPrompt != wantPrompt {\n\tt.Errorf(\"got %q, want %q\", gotPrompt, wantPrompt)\n}\n```\n\nWe are storing _everything_ that gets written to stdout so we still expect the `poker.PlayerPrompt`. We then just check an additional thing gets printed. We're not too bothered about the exact wording for now, we'll address it when we refactor.\n\n## Try to run the test\n\n```\n=== RUN   TestCLI/it_prints_an_error_when_a_non_numeric_value_is_entered_and_does_not_start_the_game\n    --- FAIL: TestCLI/it_prints_an_error_when_a_non_numeric_value_is_entered_and_does_not_start_the_game (0.00s)\n        CLI_test.go:70: got 'Please enter the number of players: ', want 'Please enter the number of players: you're so silly'\n```\n\n## Write enough code to make it pass\n\nChange the error handling code\n\n```go\nif err != nil {\n\tfmt.Fprint(cli.out, \"you're so silly\")\n\treturn\n}\n```\n\n## Refactor\n\nNow refactor the message into a constant like `PlayerPrompt`\n\n```go\nwantPrompt := poker.PlayerPrompt + poker.BadPlayerInputErrMsg\n```\n\nand put in a more appropriate message\n\n```go\nconst BadPlayerInputErrMsg = \"Bad value received for number of players, please try again with a number\"\n```\n\nFinally our testing around what has been sent to `stdout` is quite verbose, let's write an assert function to clean it up.\n\n```go\nfunc assertMessagesSentToUser(t testing.TB, stdout *bytes.Buffer, messages ...string) {\n\tt.Helper()\n\twant := strings.Join(messages, \"\")\n\tgot := stdout.String()\n\tif got != want {\n\t\tt.Errorf(\"got %q sent to stdout but expected %+v\", got, messages)\n\t}\n}\n```\n\nUsing the vararg syntax (`...string`) is handy here because we need to assert on varying amounts of messages.\n\nUse this helper in both of the tests where we assert on messages sent to the user.\n\nThere are a number of tests that could be helped with some `assertX` functions so practice your refactoring by cleaning up our tests so they read nicely.\n\nTake some time and think about the value of some of the tests we've driven out. Remember we don't want more tests than necessary, can you refactor/remove some of them _and still be confident it all works_ ?\n\nHere is what I came up with\n\n```go\nfunc TestCLI(t *testing.T) {\n\n\tt.Run(\"start game with 3 players and finish game with 'Chris' as winner\", func(t *testing.T) {\n\t\tgame := &GameSpy{}\n\t\tstdout := &bytes.Buffer{}\n\n\t\tin := userSends(\"3\", \"Chris wins\")\n\t\tcli := poker.NewCLI(in, stdout, game)\n\n\t\tcli.PlayPoker()\n\n\t\tassertMessagesSentToUser(t, stdout, poker.PlayerPrompt)\n\t\tassertGameStartedWith(t, game, 3)\n\t\tassertFinishCalledWith(t, game, \"Chris\")\n\t})\n\n\tt.Run(\"start game with 8 players and record 'Cleo' as winner\", func(t *testing.T) {\n\t\tgame := &GameSpy{}\n\n\t\tin := userSends(\"8\", \"Cleo wins\")\n\t\tcli := poker.NewCLI(in, dummyStdOut, game)\n\n\t\tcli.PlayPoker()\n\n\t\tassertGameStartedWith(t, game, 8)\n\t\tassertFinishCalledWith(t, game, \"Cleo\")\n\t})\n\n\tt.Run(\"it prints an error when a non numeric value is entered and does not start the game\", func(t *testing.T) {\n\t\tgame := &GameSpy{}\n\n\t\tstdout := &bytes.Buffer{}\n\t\tin := userSends(\"pies\")\n\n\t\tcli := poker.NewCLI(in, stdout, game)\n\t\tcli.PlayPoker()\n\n\t\tassertGameNotStarted(t, game)\n\t\tassertMessagesSentToUser(t, stdout, poker.PlayerPrompt, poker.BadPlayerInputErrMsg)\n\t})\n}\n```\n\nThe tests now reflect the main capabilities of CLI, it is able to read user input in terms of how many people are playing and who won and handles when a bad value is entered for number of players. By doing this it is clear to the reader what `CLI` does, but also what it doesn't do.\n\nWhat happens if instead of putting `Ruth wins` the user puts in `Lloyd is a killer` ?\n\nFinish this chapter by writing a test for this scenario and making it pass.\n\n## Wrapping up\n\n### A quick project recap\n\nFor the past 5 chapters we have slowly TDD'd a fair amount of code\n\n* We have two applications, a command line application and a web server.\n* Both these applications rely on a `PlayerStore` to record winners\n* The web server can also display a league table of who is winning the most games\n* The command line app helps players play a game of poker by tracking what the current blind value is.\n\n### time.Afterfunc\n\nA very handy way of scheduling a function call after a specific duration. It is well worth investing time [looking at the documentation for `time`](https://golang.org/pkg/time/) as it has a lot of time saving functions and methods for you to work with.\n\nSome of my favourites are\n\n* `time.After(duration)` returns a `chan Time` when the duration has expired. So if you wish to do something _after_ a specific time, this can help.\n* `time.NewTicker(duration)` returns a `Ticker` which is similar to the above in that it returns a channel but this one \"ticks\" every duration, rather than just once. This is very handy if you want to execute some code every `N duration`.\n\n### More examples of good separation of concerns\n\n_Generally_ it is good practice to separate the responsibilities of dealing with user input and responses away from domain code. You see that here in our command line application and also our web server.\n\nOur tests got messy. We had too many assertions (check this input, schedules these alerts, etc) and too many dependencies. We could visually see it was cluttered; it is **so important to listen to your tests**.\n\n* If your tests look messy try and refactor them.\n* If you've done this and they're still a mess it is very likely pointing to a flaw in your design\n* This is one of the real strengths of tests.\n\nEven though the tests and the production code was a bit cluttered we could freely refactor backed by our tests.\n\nRemember when you get into these situations to always take small steps and re-run the tests after every change.\n\nIt would've been dangerous to refactor both the test code _and_ the production code at the same time, so we first refactored the production code (in the current state we couldn't improve the tests much) without changing its interface so we could rely on our tests as much as we could while changing things. _Then_ we refactored the tests after the design improved.\n\nAfter refactoring the dependency list reflected our design goal. This is another benefit of DI in that it often documents intent. When you rely on global variables responsibilities become very unclear.\n\n## An example of a function implementing an interface\n\nWhen you define an interface with one method in it you might want to consider defining a `MyInterfaceFunc` type to complement it so users can implement your interface with just a function.\n\n```go\ntype BlindAlerter interface {\n\tScheduleAlertAt(duration time.Duration, amount int)\n}\n\n// BlindAlerterFunc allows you to implement BlindAlerter with a function\ntype BlindAlerterFunc func(duration time.Duration, amount int)\n\n// ScheduleAlertAt is BlindAlerterFunc implementation of BlindAlerter\nfunc (a BlindAlerterFunc) ScheduleAlertAt(duration time.Duration, amount int) {\n\ta(duration, amount)\n}\n```\n\nBy doing this, people using your library can implement your interface with just a function. They can use [Type Conversion](https://go.dev/tour/basics/13) to convert their function into a `BlindAlerterFunc` and then use it as a BlindAlerter (as `BlindAlerterFunc` implements `BlindAlerter`).\n\n```go\ngame := poker.NewTexasHoldem(poker.BlindAlerterFunc(poker.StdOutAlerter), store)\n```\n\nThe broader point here is, in Go you can add methods to _types_, not just structs. This is a very powerful feature, and you can use it to implement interfaces in more convenient ways.\n\nConsider that you can not only define types of functions, but also define types around other types, so that you can add methods to them.\n\n```go\ntype Blog map[string]string\n\nfunc (b Blog) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\tfmt.Fprintln(w, b[r.URL.Path])\n}\n```\n\nHere we've created an HTTP handler that implements a very simple \"blog\" where it will use URL paths as keys to posts stored in a map.\n"
  },
  {
    "path": "title.txt",
    "content": "---\ntitle: Learn Go with Tests\nauthor: Chris James\nrights:  MIT License\nlang: en-UK\ncover-image: epub-cover.png\n...\n"
  },
  {
    "path": "todo/todo1_test.go",
    "content": "package todo\n\nimport \"testing\"\n\ntype TodoList struct {\n\titems []string\n}\n\nfunc New() *TodoList {\n\treturn &TodoList{}\n}\n\nfunc (t *TodoList) Pending() []string {\n\treturn t.items\n}\n\nfunc (t *TodoList) Put(s string) {\n\tt.items = append(t.items, s)\n}\n\nfunc (t *TodoList) MarkAsDone(s string) {\n\tvar newList []string\n\tfor _, item := range t.items {\n\t\tif item != s {\n\t\t\tnewList = append(newList, item)\n\t\t}\n\t}\n\tt.items = newList\n}\n\nfunc TestToDo(t *testing.T) {\n\tt.Run(\"empty todo returns empty\", func(t *testing.T) {\n\t\ttodoList := New()\n\t\ttodos := todoList.Pending()\n\n\t\tif len(todos) != 0 {\n\t\t\tt.Error(\"expected todos to be empty\")\n\t\t}\n\t})\n\n\tt.Run(\"returns pending item\", func(t *testing.T) {\n\t\ttodoList := New()\n\t\titem := \"stroke the cat\"\n\t\ttodoList.Put(item)\n\t\ttodos := todoList.Pending()\n\n\t\tassertTodoLength(t, todos, 1)\n\t\tassertFirstTodoEquaL(t, todos, item)\n\t})\n\n\tt.Run(\"mark as done\", func(t *testing.T) {\n\t\ttodoList := New()\n\t\titem := \"stroke the cat\"\n\t\ttodoList.Put(item)\n\t\ttodoList.MarkAsDone(item)\n\n\t\tassertTodoLength(t, todoList.Pending(), 0)\n\t})\n}\n\nfunc assertTodoLength(t testing.TB, list []string, want int) {\n\tt.Helper()\n\tgot := len(list)\n\tif got != want {\n\t\tt.Errorf(\"expected list of size %d, got %d\", want, got)\n\t}\n}\n\nfunc assertFirstTodoEquaL(t testing.TB, todos []string, item string) {\n\tt.Helper()\n\tif todos[0] != item {\n\t\tt.Errorf(\"want %s, got %s\", item, todos[0])\n\t}\n}\n"
  },
  {
    "path": "websockets/v1/CLI.go",
    "content": "package poker\n\nimport (\n\t\"bufio\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"strconv\"\n\t\"strings\"\n)\n\n// CLI helps players through a game of poker.\ntype CLI struct {\n\tplayerStore PlayerStore\n\tin          *bufio.Scanner\n\tout         io.Writer\n\tgame        Game\n}\n\n// NewCLI creates a CLI for playing poker.\nfunc NewCLI(in io.Reader, out io.Writer, game Game) *CLI {\n\treturn &CLI{\n\t\tin:   bufio.NewScanner(in),\n\t\tout:  out,\n\t\tgame: game,\n\t}\n}\n\n// PlayerPrompt is the text asking the user for the number of players.\nconst PlayerPrompt = \"Please enter the number of players: \"\n\n// BadPlayerInputErrMsg is the text telling the user they did bad things.\nconst BadPlayerInputErrMsg = \"Bad value received for number of players, please try again with a number\"\n\n// BadWinnerInputMsg is the text telling the user they declared the winner wrong.\nconst BadWinnerInputMsg = \"invalid winner input, expect format of 'PlayerName wins'\"\n\n// PlayPoker starts the game.\nfunc (cli *CLI) PlayPoker() {\n\tfmt.Fprint(cli.out, PlayerPrompt)\n\n\tnumberOfPlayers, err := strconv.Atoi(cli.readLine())\n\n\tif err != nil {\n\t\tfmt.Fprint(cli.out, BadPlayerInputErrMsg)\n\t\treturn\n\t}\n\n\tcli.game.Start(numberOfPlayers)\n\n\twinnerInput := cli.readLine()\n\twinner, err := extractWinner(winnerInput)\n\n\tif err != nil {\n\t\tfmt.Fprint(cli.out, BadWinnerInputMsg)\n\t\treturn\n\t}\n\n\tcli.game.Finish(winner)\n}\n\nfunc extractWinner(userInput string) (string, error) {\n\tif !strings.Contains(userInput, \" wins\") {\n\t\treturn \"\", errors.New(BadWinnerInputMsg)\n\t}\n\treturn strings.Replace(userInput, \" wins\", \"\", 1), nil\n}\n\nfunc (cli *CLI) readLine() string {\n\tcli.in.Scan()\n\treturn cli.in.Text()\n}\n"
  },
  {
    "path": "websockets/v1/CLI_test.go",
    "content": "package poker_test\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"strings\"\n\t\"testing\"\n\n\tpoker \"github.com/quii/learn-go-with-tests/websockets/v1\"\n)\n\nvar dummyBlindAlerter = &poker.SpyBlindAlerter{}\nvar dummyPlayerStore = &poker.StubPlayerStore{}\nvar dummyStdIn = &bytes.Buffer{}\nvar dummyStdOut = &bytes.Buffer{}\n\ntype GameSpy struct {\n\tStartCalled     bool\n\tStartCalledWith int\n\n\tFinishedCalled   bool\n\tFinishCalledWith string\n}\n\nfunc (g *GameSpy) Start(numberOfPlayers int) {\n\tg.StartCalled = true\n\tg.StartCalledWith = numberOfPlayers\n}\n\nfunc (g *GameSpy) Finish(winner string) {\n\tg.FinishedCalled = true\n\tg.FinishCalledWith = winner\n}\n\nfunc userSends(messages ...string) io.Reader {\n\treturn strings.NewReader(strings.Join(messages, \"\\n\"))\n}\n\nfunc TestCLI(t *testing.T) {\n\n\tt.Run(\"start game with 3 players and finish game with 'Chris' as winner\", func(t *testing.T) {\n\t\tgame := &GameSpy{}\n\t\tstdout := &bytes.Buffer{}\n\n\t\tin := userSends(\"3\", \"Chris wins\")\n\t\tcli := poker.NewCLI(in, stdout, game)\n\n\t\tcli.PlayPoker()\n\n\t\tassertMessagesSentToUser(t, stdout, poker.PlayerPrompt)\n\t\tassertGameStartedWith(t, game, 3)\n\t\tassertFinishCalledWith(t, game, \"Chris\")\n\t})\n\n\tt.Run(\"start game with 8 players and record 'Cleo' as winner\", func(t *testing.T) {\n\t\tgame := &GameSpy{}\n\n\t\tin := userSends(\"8\", \"Cleo wins\")\n\t\tcli := poker.NewCLI(in, dummyStdOut, game)\n\n\t\tcli.PlayPoker()\n\n\t\tassertGameStartedWith(t, game, 8)\n\t\tassertFinishCalledWith(t, game, \"Cleo\")\n\t})\n\n\tt.Run(\"it prints an error when a non numeric value is entered and does not start the game\", func(t *testing.T) {\n\t\tgame := &GameSpy{}\n\n\t\tstdout := &bytes.Buffer{}\n\t\tin := userSends(\"pies\")\n\n\t\tcli := poker.NewCLI(in, stdout, game)\n\t\tcli.PlayPoker()\n\n\t\tassertGameNotStarted(t, game)\n\t\tassertMessagesSentToUser(t, stdout, poker.PlayerPrompt, poker.BadPlayerInputErrMsg)\n\t})\n\n\tt.Run(\"it prints an error when the winner is declared incorrectly\", func(t *testing.T) {\n\t\tgame := &GameSpy{}\n\t\tstdout := &bytes.Buffer{}\n\n\t\tin := userSends(\"8\", \"Lloyd is a killer\")\n\t\tcli := poker.NewCLI(in, stdout, game)\n\n\t\tcli.PlayPoker()\n\n\t\tassertGameNotFinished(t, game)\n\t\tassertMessagesSentToUser(t, stdout, poker.PlayerPrompt, poker.BadWinnerInputMsg)\n\t})\n}\n\nfunc assertGameStartedWith(t testing.TB, game *GameSpy, numberOfPlayersWanted int) {\n\tt.Helper()\n\tif game.StartCalledWith != numberOfPlayersWanted {\n\t\tt.Errorf(\"wanted Start called with %d but got %d\", numberOfPlayersWanted, game.StartCalledWith)\n\t}\n}\n\nfunc assertGameNotFinished(t testing.TB, game *GameSpy) {\n\tt.Helper()\n\tif game.FinishedCalled {\n\t\tt.Errorf(\"game should not have finished\")\n\t}\n}\n\nfunc assertGameNotStarted(t testing.TB, game *GameSpy) {\n\tt.Helper()\n\tif game.StartCalled {\n\t\tt.Errorf(\"game should not have started\")\n\t}\n}\n\nfunc assertFinishCalledWith(t testing.TB, game *GameSpy, winner string) {\n\tt.Helper()\n\tif game.FinishCalledWith != winner {\n\t\tt.Errorf(\"expected finish called with %q but got %q\", winner, game.FinishCalledWith)\n\t}\n}\n\nfunc assertMessagesSentToUser(t testing.TB, stdout *bytes.Buffer, messages ...string) {\n\tt.Helper()\n\twant := strings.Join(messages, \"\")\n\tgot := stdout.String()\n\tif got != want {\n\t\tt.Errorf(\"got %q sent to stdout but expected %+v\", got, messages)\n\t}\n}\n\nfunc assertScheduledAlert(t testing.TB, got, want poker.ScheduledAlert) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"got %+v, want %+v\", got, want)\n\t}\n}\n"
  },
  {
    "path": "websockets/v1/Gopkg.toml",
    "content": "# Gopkg.toml example\n#\n# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html\n# for detailed Gopkg.toml documentation.\n#\n# required = [\"github.com/user/thing/cmd/thing\"]\n# ignored = [\"github.com/user/project/pkgX\", \"bitbucket.org/user/project/pkgA/pkgY\"]\n#\n# [[constraint]]\n#   name = \"github.com/user/project\"\n#   version = \"1.0.0\"\n#\n# [[constraint]]\n#   name = \"github.com/user/project2\"\n#   branch = \"dev\"\n#   source = \"github.com/myfork/project2\"\n#\n# [[override]]\n#   name = \"github.com/x/y\"\n#   version = \"2.4.0\"\n#\n# [prune]\n#   non-go = false\n#   go-tests = true\n#   unused-packages = true\n\n\n[[constraint]]\n  name = \"github.com/gorilla/websocket\"\n  version = \"1.4.0\"\n\n[prune]\n  go-tests = true\n  unused-packages = true\n"
  },
  {
    "path": "websockets/v1/blind_alerter.go",
    "content": "package poker\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"time\"\n)\n\n// BlindAlerter schedules alerts for blind amounts.\ntype BlindAlerter interface {\n\tScheduleAlertAt(duration time.Duration, amount int)\n}\n\n// BlindAlerterFunc allows you to implement BlindAlerter with a function.\ntype BlindAlerterFunc func(duration time.Duration, amount int)\n\n// ScheduleAlertAt is BlindAlerterFunc implementation of BlindAlerter.\nfunc (a BlindAlerterFunc) ScheduleAlertAt(duration time.Duration, amount int) {\n\ta(duration, amount)\n}\n\n// StdOutAlerter will schedule alerts and print them to os.Stdout.\nfunc StdOutAlerter(duration time.Duration, amount int) {\n\ttime.AfterFunc(duration, func() {\n\t\tfmt.Fprintf(os.Stdout, \"Blind is now %d\\n\", amount)\n\t})\n}\n"
  },
  {
    "path": "websockets/v1/cmd/cli/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tpoker \"github.com/quii/learn-go-with-tests/websockets/v1\"\n)\n\nconst dbFileName = \"game.db.json\"\n\nfunc main() {\n\tstore, close, err := poker.FileSystemPlayerStoreFromFile(dbFileName)\n\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer close()\n\n\tgame := poker.NewTexasHoldem(poker.BlindAlerterFunc(poker.StdOutAlerter), store)\n\tcli := poker.NewCLI(os.Stdin, os.Stdout, game)\n\n\tfmt.Println(\"Let's play poker\")\n\tfmt.Println(\"Type {Name} wins to record a win\")\n\tcli.PlayPoker()\n}\n"
  },
  {
    "path": "websockets/v1/cmd/webserver/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/quii/learn-go-with-tests/websockets/v1\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n)\n\nconst dbFileName = \"game.db.json\"\n\nfunc main() {\n\tdb, err := os.OpenFile(dbFileName, os.O_RDWR|os.O_CREATE, 0666)\n\n\tif err != nil {\n\t\tlog.Fatalf(\"problem opening %s %v\", dbFileName, err)\n\t}\n\n\tstore, err := poker.NewFileSystemPlayerStore(db)\n\n\tif err != nil {\n\t\tlog.Fatalf(\"problem creating file system player store, %v \", err)\n\t}\n\n\tserver, err := poker.NewPlayerServer(store)\n\n\tif err != nil {\n\t\tlog.Fatalf(\"problem creating player server %v\", err)\n\t}\n\n\tlog.Fatal(http.ListenAndServe(\":5000\", server))\n}\n"
  },
  {
    "path": "websockets/v1/file_system_store.go",
    "content": "package poker\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"sort\"\n)\n\n// FileSystemPlayerStore stores players in the filesystem.\ntype FileSystemPlayerStore struct {\n\tdatabase *json.Encoder\n\tleague   League\n}\n\n// NewFileSystemPlayerStore creates a FileSystemPlayerStore initialising the store if needed.\nfunc NewFileSystemPlayerStore(file *os.File) (*FileSystemPlayerStore, error) {\n\n\terr := initialisePlayerDBFile(file)\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"problem initialising player db file, %v\", err)\n\t}\n\n\tleague, err := NewLeague(file)\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"problem loading player store from file %s, %v\", file.Name(), err)\n\t}\n\n\treturn &FileSystemPlayerStore{\n\t\tdatabase: json.NewEncoder(&tape{file}),\n\t\tleague:   league,\n\t}, nil\n}\n\n// FileSystemPlayerStoreFromFile creates a PlayerStore from the contents of a JSON file found at path.\nfunc FileSystemPlayerStoreFromFile(path string) (*FileSystemPlayerStore, func(), error) {\n\tdb, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0666)\n\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"problem opening %s %v\", path, err)\n\t}\n\n\tcloseFunc := func() {\n\t\tdb.Close()\n\t}\n\n\tstore, err := NewFileSystemPlayerStore(db)\n\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"problem creating file system player store, %v \", err)\n\t}\n\n\treturn store, closeFunc, nil\n}\n\nfunc initialisePlayerDBFile(file *os.File) error {\n\tfile.Seek(0, io.SeekStart)\n\n\tinfo, err := file.Stat()\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"problem getting file info from file %s, %v\", file.Name(), err)\n\t}\n\n\tif info.Size() == 0 {\n\t\tfile.Write([]byte(\"[]\"))\n\t\tfile.Seek(0, io.SeekStart)\n\t}\n\n\treturn nil\n}\n\n// GetLeague returns the Scores of all the players.\nfunc (f *FileSystemPlayerStore) GetLeague() League {\n\tsort.Slice(f.league, func(i, j int) bool {\n\t\treturn f.league[i].Wins > f.league[j].Wins\n\t})\n\treturn f.league\n}\n\n// GetPlayerScore retrieves a player's score.\nfunc (f *FileSystemPlayerStore) GetPlayerScore(name string) int {\n\n\tplayer := f.league.Find(name)\n\n\tif player != nil {\n\t\treturn player.Wins\n\t}\n\n\treturn 0\n}\n\n// RecordWin will store a win for a player, incrementing wins if already known.\nfunc (f *FileSystemPlayerStore) RecordWin(name string) {\n\tplayer := f.league.Find(name)\n\n\tif player != nil {\n\t\tplayer.Wins++\n\t} else {\n\t\tf.league = append(f.league, Player{name, 1})\n\t}\n\n\tf.database.Encode(f.league)\n}\n"
  },
  {
    "path": "websockets/v1/file_system_store_test.go",
    "content": "package poker\n\nimport (\n\t\"os\"\n\t\"testing\"\n)\n\nfunc createTempFile(t testing.TB, initialData string) (*os.File, func()) {\n\tt.Helper()\n\n\ttmpfile, err := os.CreateTemp(\"\", \"db\")\n\n\tif err != nil {\n\t\tt.Fatalf(\"could not create temp file %v\", err)\n\t}\n\n\ttmpfile.Write([]byte(initialData))\n\n\tremoveFile := func() {\n\t\ttmpfile.Close()\n\t\tos.Remove(tmpfile.Name())\n\t}\n\n\treturn tmpfile, removeFile\n}\n\nfunc TestFileSystemStore(t *testing.T) {\n\n\tt.Run(\"League sorted\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tgot := store.GetLeague()\n\n\t\twant := []Player{\n\t\t\t{\"Chris\", 33},\n\t\t\t{\"Cleo\", 10},\n\t\t}\n\n\t\tassertLeague(t, got, want)\n\n\t\t// read again\n\t\tgot = store.GetLeague()\n\t\tassertLeague(t, got, want)\n\t})\n\n\tt.Run(\"get player score\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tgot := store.GetPlayerScore(\"Chris\")\n\t\twant := 33\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"store wins for existing players\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tstore.RecordWin(\"Chris\")\n\n\t\tgot := store.GetPlayerScore(\"Chris\")\n\t\twant := 34\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"store wins for existing players\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tstore.RecordWin(\"Pepper\")\n\n\t\tgot := store.GetPlayerScore(\"Pepper\")\n\t\twant := 1\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"works with an empty file\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, \"\")\n\t\tdefer cleanDatabase()\n\n\t\t_, err := NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\t})\n}\n\nfunc assertScoreEquals(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"got %d want %d\", got, want)\n\t}\n}\n\nfunc assertNoError(t testing.TB, err error) {\n\tt.Helper()\n\tif err != nil {\n\t\tt.Fatalf(\"didn't expect an error but got one, %v\", err)\n\t}\n}\n"
  },
  {
    "path": "websockets/v1/game.go",
    "content": "package poker\n\n// Game manages the state of a game.\ntype Game interface {\n\tStart(numberOfPlayers int)\n\tFinish(winner string)\n}\n"
  },
  {
    "path": "websockets/v1/game.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>Lets play poker</title>\n</head>\n<body>\n<section id=\"game\">\n    <div id=\"declare-winner\">\n        <label for=\"winner\">Winner</label>\n        <input type=\"text\" id=\"winner\"/>\n        <button id=\"winner-button\">Declare winner</button>\n    </div>\n</section>\n</body>\n<script type=\"application/javascript\">\n\n    const submitWinnerButton = document.getElementById('winner-button')\n    const winnerInput = document.getElementById('winner')\n\n    if (window['WebSocket']) {\n        const conn = new WebSocket('ws://' + document.location.host + '/ws')\n\n        submitWinnerButton.onclick = event => {\n            conn.send(winnerInput.value)\n        }\n    }\n</script>\n</html>\n"
  },
  {
    "path": "websockets/v1/league.go",
    "content": "package poker\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n)\n\n// League stores a collection of players.\ntype League []Player\n\n// Find tries to return a player from a League.\nfunc (l League) Find(name string) *Player {\n\tfor i, p := range l {\n\t\tif p.Name == name {\n\t\t\treturn &l[i]\n\t\t}\n\t}\n\treturn nil\n}\n\n// NewLeague creates a League from JSON.\nfunc NewLeague(rdr io.Reader) (League, error) {\n\tvar league []Player\n\terr := json.NewDecoder(rdr).Decode(&league)\n\n\tif err != nil {\n\t\terr = fmt.Errorf(\"problem parsing League, %v\", err)\n\t}\n\n\treturn league, err\n}\n"
  },
  {
    "path": "websockets/v1/server.go",
    "content": "package poker\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"html/template\"\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/gorilla/websocket\"\n)\n\n// PlayerStore stores score information about players.\ntype PlayerStore interface {\n\tGetPlayerScore(name string) int\n\tRecordWin(name string)\n\tGetLeague() League\n}\n\n// Player stores a name with a number of wins.\ntype Player struct {\n\tName string\n\tWins int\n}\n\n// PlayerServer is a HTTP interface for player information.\ntype PlayerServer struct {\n\tstore PlayerStore\n\thttp.Handler\n\ttemplate *template.Template\n}\n\nconst jsonContentType = \"application/json\"\nconst htmlTemplatePath = \"game.html\"\n\n// NewPlayerServer creates a PlayerServer with routing configured.\nfunc NewPlayerServer(store PlayerStore) (*PlayerServer, error) {\n\tp := new(PlayerServer)\n\n\ttmpl, err := template.ParseFiles(htmlTemplatePath)\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"problem opening %s %v\", htmlTemplatePath, err)\n\t}\n\n\tp.template = tmpl\n\tp.store = store\n\n\trouter := http.NewServeMux()\n\trouter.Handle(\"/league\", http.HandlerFunc(p.leagueHandler))\n\trouter.Handle(\"/players/\", http.HandlerFunc(p.playersHandler))\n\trouter.Handle(\"/game\", http.HandlerFunc(p.game))\n\trouter.Handle(\"/ws\", http.HandlerFunc(p.webSocket))\n\n\tp.Handler = router\n\n\treturn p, nil\n}\n\nvar wsUpgrader = websocket.Upgrader{\n\tReadBufferSize:  1024,\n\tWriteBufferSize: 1024,\n}\n\nfunc (p *PlayerServer) webSocket(w http.ResponseWriter, r *http.Request) {\n\tconn, _ := wsUpgrader.Upgrade(w, r, nil)\n\t_, winnerMsg, _ := conn.ReadMessage()\n\tp.store.RecordWin(string(winnerMsg))\n}\n\nfunc (p *PlayerServer) game(w http.ResponseWriter, r *http.Request) {\n\tp.template.Execute(w, nil)\n}\n\nfunc (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) {\n\tw.Header().Set(\"content-type\", jsonContentType)\n\tjson.NewEncoder(w).Encode(p.store.GetLeague())\n}\n\nfunc (p *PlayerServer) playersHandler(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\n\tswitch r.Method {\n\tcase http.MethodPost:\n\t\tp.processWin(w, player)\n\tcase http.MethodGet:\n\t\tp.showScore(w, player)\n\t}\n}\n\nfunc (p *PlayerServer) showScore(w http.ResponseWriter, player string) {\n\tscore := p.store.GetPlayerScore(player)\n\n\tif score == 0 {\n\t\tw.WriteHeader(http.StatusNotFound)\n\t}\n\n\tfmt.Fprint(w, score)\n}\n\nfunc (p *PlayerServer) processWin(w http.ResponseWriter, player string) {\n\tp.store.RecordWin(player)\n\tw.WriteHeader(http.StatusAccepted)\n}\n"
  },
  {
    "path": "websockets/v1/server_integration_test.go",
    "content": "package poker\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestRecordingWinsAndRetrievingThem(t *testing.T) {\n\tdatabase, cleanDatabase := createTempFile(t, `[]`)\n\tdefer cleanDatabase()\n\tstore, err := NewFileSystemPlayerStore(database)\n\n\tassertNoError(t, err)\n\n\tserver := mustMakePlayerServer(t, store)\n\tplayer := \"Pepper\"\n\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\n\tt.Run(\"get score\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newGetScoreRequest(player))\n\t\tassertStatus(t, response, http.StatusOK)\n\n\t\tassertResponseBody(t, response.Body.String(), \"3\")\n\t})\n\n\tt.Run(\"get League\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newLeagueRequest())\n\t\tassertStatus(t, response, http.StatusOK)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\t\twant := []Player{\n\t\t\t{\"Pepper\", 3},\n\t\t}\n\t\tassertLeague(t, got, want)\n\t})\n}\n"
  },
  {
    "path": "websockets/v1/server_test.go",
    "content": "package poker\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/gorilla/websocket\"\n)\n\nfunc mustMakePlayerServer(t *testing.T, store PlayerStore) *PlayerServer {\n\tserver, err := NewPlayerServer(store)\n\tif err != nil {\n\t\tt.Fatal(\"problem creating player server\", err)\n\t}\n\treturn server\n}\n\nfunc TestGETPlayers(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{\n\t\t\t\"Pepper\": 20,\n\t\t\t\"Floyd\":  10,\n\t\t},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := mustMakePlayerServer(t, &store)\n\n\tt.Run(\"returns Pepper's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Pepper\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"20\")\n\t})\n\n\tt.Run(\"returns Floyd's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Floyd\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"10\")\n\t})\n\n\tt.Run(\"returns 404 on missing players\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Apollo\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response, http.StatusNotFound)\n\t})\n}\n\nfunc TestStoreWins(t *testing.T) {\n\tstore := StubPlayerStore{\n\t\tmap[string]int{},\n\t\tnil,\n\t\tnil,\n\t}\n\tserver := mustMakePlayerServer(t, &store)\n\n\tt.Run(\"it records wins on POST\", func(t *testing.T) {\n\t\tplayer := \"Pepper\"\n\n\t\trequest := newPostWinRequest(player)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response, http.StatusAccepted)\n\t\tAssertPlayerWin(t, &store, player)\n\t})\n}\n\nfunc TestLeague(t *testing.T) {\n\n\tt.Run(\"it returns the League table as JSON\", func(t *testing.T) {\n\t\twantedLeague := []Player{\n\t\t\t{\"Cleo\", 32},\n\t\t\t{\"Chris\", 20},\n\t\t\t{\"Tiest\", 14},\n\t\t}\n\n\t\tstore := StubPlayerStore{nil, nil, wantedLeague}\n\t\tserver := mustMakePlayerServer(t, &store)\n\n\t\trequest := newLeagueRequest()\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\n\t\tassertStatus(t, response, http.StatusOK)\n\t\tassertLeague(t, got, wantedLeague)\n\t\tassertContentType(t, response, jsonContentType)\n\n\t})\n}\n\nfunc TestGame(t *testing.T) {\n\tt.Run(\"GET /game returns 200\", func(t *testing.T) {\n\t\tserver := mustMakePlayerServer(t, &StubPlayerStore{})\n\n\t\trequest := newGameRequest()\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response, http.StatusOK)\n\t})\n\n\tt.Run(\"when we get a message over a websocket it is a winner of a game\", func(t *testing.T) {\n\t\tstore := &StubPlayerStore{}\n\t\twinner := \"Ruth\"\n\t\tserver := httptest.NewServer(mustMakePlayerServer(t, store))\n\t\tdefer server.Close()\n\n\t\twsURL := \"ws\" + strings.TrimPrefix(server.URL, \"http\") + \"/ws\"\n\n\t\tws, _, err := websocket.DefaultDialer.Dial(wsURL, nil)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"could not open a ws connection on %s %v\", wsURL, err)\n\t\t}\n\t\tdefer ws.Close()\n\n\t\twriteWSMessage(t, ws, winner)\n\n\t\ttime.Sleep(10 * time.Millisecond)\n\t\tAssertPlayerWin(t, store, winner)\n\t})\n}\n\nfunc writeWSMessage(t testing.TB, conn *websocket.Conn, message string) {\n\tt.Helper()\n\tif err := conn.WriteMessage(websocket.TextMessage, []byte(message)); err != nil {\n\t\tt.Fatalf(\"could not send message over ws connection %v\", err)\n\t}\n}\n\nfunc assertContentType(t testing.TB, response *httptest.ResponseRecorder, want string) {\n\tt.Helper()\n\tif response.Header().Get(\"content-type\") != want {\n\t\tt.Errorf(\"response did not have content-type of %s, got %v\", want, response.Result().Header)\n\t}\n}\n\nfunc getLeagueFromResponse(t testing.TB, body io.Reader) []Player {\n\tt.Helper()\n\tleague, err := NewLeague(body)\n\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to parse response from server %q into slice of Player, '%v'\", body, err)\n\t}\n\n\treturn league\n}\n\nfunc assertLeague(t testing.TB, got, want []Player) {\n\tt.Helper()\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %v want %v\", got, want)\n\t}\n}\n\nfunc assertStatus(t testing.TB, got *httptest.ResponseRecorder, want int) {\n\tt.Helper()\n\tif got.Code != want {\n\t\tt.Errorf(\"did not get correct status, got %d, want %d\", got.Code, want)\n\t}\n}\n\nfunc newGameRequest() *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, \"/game\", nil)\n\treturn req\n}\n\nfunc newLeagueRequest() *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, \"/league\", nil)\n\treturn req\n}\n\nfunc newGetScoreRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc newPostWinRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodPost, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc assertResponseBody(t testing.TB, got, want string) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"response body is wrong, got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "websockets/v1/tape.go",
    "content": "package poker\n\nimport (\n\t\"io\"\n\t\"os\"\n)\n\ntype tape struct {\n\tfile *os.File\n}\n\nfunc (t *tape) Write(p []byte) (n int, err error) {\n\tt.file.Truncate(0)\n\tt.file.Seek(0, io.SeekStart)\n\treturn t.file.Write(p)\n}\n"
  },
  {
    "path": "websockets/v1/tape_test.go",
    "content": "package poker\n\nimport (\n\t\"io\"\n\t\"testing\"\n)\n\nfunc TestTape_Write(t *testing.T) {\n\tfile, clean := createTempFile(t, \"12345\")\n\tdefer clean()\n\n\ttape := &tape{file}\n\n\ttape.Write([]byte(\"abc\"))\n\n\tfile.Seek(0, io.SeekStart)\n\tnewFileContents, _ := io.ReadAll(file)\n\n\tgot := string(newFileContents)\n\twant := \"abc\"\n\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "websockets/v1/testing.go",
    "content": "package poker\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n)\n\n// StubPlayerStore implements PlayerStore for testing purposes.\ntype StubPlayerStore struct {\n\tScores   map[string]int\n\tWinCalls []string\n\tLeague   []Player\n}\n\n// GetPlayerScore returns a score from Scores.\nfunc (s *StubPlayerStore) GetPlayerScore(name string) int {\n\tscore := s.Scores[name]\n\treturn score\n}\n\n// RecordWin will record a win to WinCalls.\nfunc (s *StubPlayerStore) RecordWin(name string) {\n\ts.WinCalls = append(s.WinCalls, name)\n}\n\n// GetLeague returns League.\nfunc (s *StubPlayerStore) GetLeague() League {\n\treturn s.League\n}\n\n// AssertPlayerWin allows you to spy on the store's calls to RecordWin.\nfunc AssertPlayerWin(t testing.TB, store *StubPlayerStore, winner string) {\n\tt.Helper()\n\n\tif len(store.WinCalls) != 1 {\n\t\tt.Fatalf(\"got %d calls to RecordWin want %d\", len(store.WinCalls), 1)\n\t}\n\n\tif store.WinCalls[0] != winner {\n\t\tt.Errorf(\"did not store correct winner got %q want %q\", store.WinCalls[0], winner)\n\t}\n}\n\n// ScheduledAlert holds information about when an alert is scheduled.\ntype ScheduledAlert struct {\n\tAt     time.Duration\n\tAmount int\n}\n\nfunc (s ScheduledAlert) String() string {\n\treturn fmt.Sprintf(\"%d chips at %v\", s.Amount, s.At)\n}\n\n// SpyBlindAlerter allows you to spy on ScheduleAlertAt calls.\ntype SpyBlindAlerter struct {\n\tAlerts []ScheduledAlert\n}\n\n// ScheduleAlertAt records alerts that have been scheduled.\nfunc (s *SpyBlindAlerter) ScheduleAlertAt(at time.Duration, amount int) {\n\ts.Alerts = append(s.Alerts, ScheduledAlert{at, amount})\n}\n"
  },
  {
    "path": "websockets/v1/texas_holdem.go",
    "content": "package poker\n\nimport \"time\"\n\n// TexasHoldem manages a game of poker.\ntype TexasHoldem struct {\n\talerter BlindAlerter\n\tstore   PlayerStore\n}\n\n// NewTexasHoldem returns a new game.\nfunc NewTexasHoldem(alerter BlindAlerter, store PlayerStore) *TexasHoldem {\n\treturn &TexasHoldem{\n\t\talerter: alerter,\n\t\tstore:   store,\n\t}\n}\n\n// Start will schedule blind alerts dependant on the number of players.\nfunc (p *TexasHoldem) Start(numberOfPlayers int) {\n\tblindIncrement := time.Duration(5+numberOfPlayers) * time.Minute\n\n\tblinds := []int{100, 200, 300, 400, 500, 600, 800, 1000, 2000, 4000, 8000}\n\tblindTime := 0 * time.Second\n\tfor _, blind := range blinds {\n\t\tp.alerter.ScheduleAlertAt(blindTime, blind)\n\t\tblindTime = blindTime + blindIncrement\n\t}\n}\n\n// Finish ends the game, recording the winner.\nfunc (p *TexasHoldem) Finish(winner string) {\n\tp.store.RecordWin(winner)\n}\n"
  },
  {
    "path": "websockets/v1/texas_holdem_test.go",
    "content": "package poker_test\n\nimport (\n\t\"fmt\"\n\t\"github.com/quii/learn-go-with-tests/websockets/v1\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestGame_Start(t *testing.T) {\n\tt.Run(\"schedules alerts on game start for 5 players\", func(t *testing.T) {\n\t\tblindAlerter := &poker.SpyBlindAlerter{}\n\t\tgame := poker.NewTexasHoldem(blindAlerter, dummyPlayerStore)\n\n\t\tgame.Start(5)\n\n\t\tcases := []poker.ScheduledAlert{\n\t\t\t{At: 0 * time.Second, Amount: 100},\n\t\t\t{At: 10 * time.Minute, Amount: 200},\n\t\t\t{At: 20 * time.Minute, Amount: 300},\n\t\t\t{At: 30 * time.Minute, Amount: 400},\n\t\t\t{At: 40 * time.Minute, Amount: 500},\n\t\t\t{At: 50 * time.Minute, Amount: 600},\n\t\t\t{At: 60 * time.Minute, Amount: 800},\n\t\t\t{At: 70 * time.Minute, Amount: 1000},\n\t\t\t{At: 80 * time.Minute, Amount: 2000},\n\t\t\t{At: 90 * time.Minute, Amount: 4000},\n\t\t\t{At: 100 * time.Minute, Amount: 8000},\n\t\t}\n\n\t\tcheckSchedulingCases(cases, t, blindAlerter)\n\t})\n\n\tt.Run(\"schedules alerts on game start for 7 players\", func(t *testing.T) {\n\t\tblindAlerter := &poker.SpyBlindAlerter{}\n\t\tgame := poker.NewTexasHoldem(blindAlerter, dummyPlayerStore)\n\n\t\tgame.Start(7)\n\n\t\tcases := []poker.ScheduledAlert{\n\t\t\t{At: 0 * time.Second, Amount: 100},\n\t\t\t{At: 12 * time.Minute, Amount: 200},\n\t\t\t{At: 24 * time.Minute, Amount: 300},\n\t\t\t{At: 36 * time.Minute, Amount: 400},\n\t\t}\n\n\t\tcheckSchedulingCases(cases, t, blindAlerter)\n\t})\n\n}\n\nfunc TestGame_Finish(t *testing.T) {\n\tstore := &poker.StubPlayerStore{}\n\tgame := poker.NewTexasHoldem(dummyBlindAlerter, store)\n\twinner := \"Ruth\"\n\n\tgame.Finish(winner)\n\tpoker.AssertPlayerWin(t, store, winner)\n}\n\nfunc checkSchedulingCases(cases []poker.ScheduledAlert, t *testing.T, blindAlerter *poker.SpyBlindAlerter) {\n\tfor i, want := range cases {\n\t\tt.Run(fmt.Sprint(want), func(t *testing.T) {\n\n\t\t\tif len(blindAlerter.Alerts) <= i {\n\t\t\t\tt.Fatalf(\"alert %d was not scheduled %v\", i, blindAlerter.Alerts)\n\t\t\t}\n\n\t\t\tgot := blindAlerter.Alerts[i]\n\t\t\tassertScheduledAlert(t, got, want)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "websockets/v1/vendor/github.com/gorilla/websocket/.gitignore",
    "content": "# Compiled Object files, Static and Dynamic libs (Shared Objects)\n*.o\n*.a\n*.so\n\n# Folders\n_obj\n_test\n\n# Architecture specific extensions/prefixes\n*.[568vq]\n[568vq].out\n\n*.cgo1.go\n*.cgo2.c\n_cgo_defun.c\n_cgo_gotypes.go\n_cgo_export.*\n\n_testmain.go\n\n*.exe\n\n.idea/\n*.iml\n"
  },
  {
    "path": "websockets/v1/vendor/github.com/gorilla/websocket/.travis.yml",
    "content": "language: go\nsudo: false\n\nmatrix:\n  include:\n    - go: 1.7.x\n    - go: 1.8.x\n    - go: 1.9.x\n    - go: 1.10.x\n    - go: 1.11.x\n    - go: tip\n  allow_failures:\n    - go: tip\n\nscript:\n  - go get -t -v ./...\n  - diff -u <(echo -n) <(gofmt -d .)\n  - go vet $(go list ./... | grep -v /vendor/)\n  - go test -v -race ./...\n"
  },
  {
    "path": "websockets/v1/vendor/github.com/gorilla/websocket/AUTHORS",
    "content": "# This is the official list of Gorilla WebSocket authors for copyright\n# purposes.\n#\n# Please keep the list sorted.\n\nGary Burd <gary@beagledreams.com>\nGoogle LLC (https://opensource.google.com/)\nJoachim Bauch <mail@joachim-bauch.de>\n\n"
  },
  {
    "path": "websockets/v1/vendor/github.com/gorilla/websocket/LICENSE",
    "content": "Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n  Redistributions of source code must retain the above copyright notice, this\n  list of conditions and the following disclaimer.\n\n  Redistributions in binary form must reproduce the above copyright notice,\n  this list of conditions and the following disclaimer in the documentation\n  and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "websockets/v1/vendor/github.com/gorilla/websocket/README.md",
    "content": "# Gorilla WebSocket\n\nGorilla WebSocket is a [Go](http://golang.org/) implementation of the\n[WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol.\n\n[![Build Status](https://travis-ci.org/gorilla/websocket.svg?branch=master)](https://travis-ci.org/gorilla/websocket)\n[![GoDoc](https://pkg.go.dev/badge/github.com/gorilla/websocket)](https://pkg.go.dev/github.com/gorilla/websocket)\n\n### Documentation\n\n* [API Reference](https://pkg.go.dev/github.com/gorilla/websocket)\n* [Chat example](https://github.com/gorilla/websocket/tree/master/examples/chat)\n* [Command example](https://github.com/gorilla/websocket/tree/master/examples/command)\n* [Client and server example](https://github.com/gorilla/websocket/tree/master/examples/echo)\n* [File watch example](https://github.com/gorilla/websocket/tree/master/examples/filewatch)\n\n### Status\n\nThe Gorilla WebSocket package provides a complete and tested implementation of\nthe [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. The\npackage API is stable.\n\n### Installation\n\n    go get github.com/gorilla/websocket\n\n### Protocol Compliance\n\nThe Gorilla WebSocket package passes the server tests in the [Autobahn Test\nSuite](http://autobahn.ws/testsuite) using the application in the [examples/autobahn\nsubdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn).\n\n### Gorilla WebSocket compared with other packages\n\n<table>\n<tr>\n<th></th>\n<th><a href=\"https://pkg.go.dev/github.com/gorilla/websocket\">github.com/gorilla</a></th>\n<th><a href=\"https://pkg.go.dev/golang.org/x/net/websocket\">golang.org/x/net</a></th>\n</tr>\n<tr>\n<tr><td colspan=\"3\"><a href=\"http://tools.ietf.org/html/rfc6455\">RFC 6455</a> Features</td></tr>\n<tr><td>Passes <a href=\"http://autobahn.ws/testsuite/\">Autobahn Test Suite</a></td><td><a href=\"https://github.com/gorilla/websocket/tree/master/examples/autobahn\">Yes</a></td><td>No</td></tr>\n<tr><td>Receive <a href=\"https://tools.ietf.org/html/rfc6455#section-5.4\">fragmented</a> message<td>Yes</td><td><a href=\"https://code.google.com/p/go/issues/detail?id=7632\">No</a>, see note 1</td></tr>\n<tr><td>Send <a href=\"https://tools.ietf.org/html/rfc6455#section-5.5.1\">close</a> message</td><td><a href=\"https://pkg.go.dev/github.com/gorilla/websocket#hdr-Control_Messages\">Yes</a></td><td><a href=\"https://code.google.com/p/go/issues/detail?id=4588\">No</a></td></tr>\n<tr><td>Send <a href=\"https://tools.ietf.org/html/rfc6455#section-5.5.2\">pings</a> and receive <a href=\"https://tools.ietf.org/html/rfc6455#section-5.5.3\">pongs</a></td><td><a href=\"https://pkg.go.dev/github.com/gorilla/websocket#hdr-Control_Messages\">Yes</a></td><td>No</td></tr>\n<tr><td>Get the <a href=\"https://tools.ietf.org/html/rfc6455#section-5.6\">type</a> of a received data message</td><td>Yes</td><td>Yes, see note 2</td></tr>\n<tr><td colspan=\"3\">Other Features</tr></td>\n<tr><td><a href=\"https://tools.ietf.org/html/rfc7692\">Compression Extensions</a></td><td>Experimental</td><td>No</td></tr>\n<tr><td>Read message using io.Reader</td><td><a href=\"https://pkg.go.dev/github.com/gorilla/websocket#Conn.NextReader\">Yes</a></td><td>No, see note 3</td></tr>\n<tr><td>Write message using io.WriteCloser</td><td><a href=\"https://pkg.go.dev/github.com/gorilla/websocket#Conn.NextWriter\">Yes</a></td><td>No, see note 3</td></tr>\n</table>\n\nNotes:\n\n1. Large messages are fragmented in [Chrome's new WebSocket implementation](http://www.ietf.org/mail-archive/web/hybi/current/msg10503.html).\n2. The application can get the type of a received data message by implementing\n   a [Codec marshal](https://pkg.go.dev/golang.org/x/net/websocket#Codec.Marshal)\n   function.\n3. The go.net io.Reader and io.Writer operate across WebSocket frame boundaries.\n  Read returns when the input buffer is full or a frame boundary is\n  encountered. Each call to Write sends a single frame message. The Gorilla\n  io.Reader and io.WriteCloser operate on a single WebSocket message.\n\n"
  },
  {
    "path": "websockets/v1/vendor/github.com/gorilla/websocket/client.go",
    "content": "// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage websocket\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/httptrace\"\n\t\"net/url\"\n\t\"strings\"\n\t\"time\"\n)\n\n// ErrBadHandshake is returned when the server response to opening handshake is\n// invalid.\nvar ErrBadHandshake = errors.New(\"websocket: bad handshake\")\n\nvar errInvalidCompression = errors.New(\"websocket: invalid compression negotiation\")\n\n// NewClient creates a new client connection using the given net connection.\n// The URL u specifies the host and request URI. Use requestHeader to specify\n// the origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies\n// (Cookie). Use the response.Header to get the selected subprotocol\n// (Sec-WebSocket-Protocol) and cookies (Set-Cookie).\n//\n// If the WebSocket handshake fails, ErrBadHandshake is returned along with a\n// non-nil *http.Response so that callers can handle redirects, authentication,\n// etc.\n//\n// Deprecated: Use Dialer instead.\nfunc NewClient(netConn net.Conn, u *url.URL, requestHeader http.Header, readBufSize, writeBufSize int) (c *Conn, response *http.Response, err error) {\n\td := Dialer{\n\t\tReadBufferSize:  readBufSize,\n\t\tWriteBufferSize: writeBufSize,\n\t\tNetDial: func(net, addr string) (net.Conn, error) {\n\t\t\treturn netConn, nil\n\t\t},\n\t}\n\treturn d.Dial(u.String(), requestHeader)\n}\n\n// A Dialer contains options for connecting to WebSocket server.\ntype Dialer struct {\n\t// NetDial specifies the dial function for creating TCP connections. If\n\t// NetDial is nil, net.Dial is used.\n\tNetDial func(network, addr string) (net.Conn, error)\n\n\t// NetDialContext specifies the dial function for creating TCP connections. If\n\t// NetDialContext is nil, net.DialContext is used.\n\tNetDialContext func(ctx context.Context, network, addr string) (net.Conn, error)\n\n\t// Proxy specifies a function to return a proxy for a given\n\t// Request. If the function returns a non-nil error, the\n\t// request is aborted with the provided error.\n\t// If Proxy is nil or returns a nil *URL, no proxy is used.\n\tProxy func(*http.Request) (*url.URL, error)\n\n\t// TLSClientConfig specifies the TLS configuration to use with tls.Client.\n\t// If nil, the default configuration is used.\n\tTLSClientConfig *tls.Config\n\n\t// HandshakeTimeout specifies the duration for the handshake to complete.\n\tHandshakeTimeout time.Duration\n\n\t// ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer\n\t// size is zero, then a useful default size is used. The I/O buffer sizes\n\t// do not limit the size of the messages that can be sent or received.\n\tReadBufferSize, WriteBufferSize int\n\n\t// WriteBufferPool is a pool of buffers for write operations. If the value\n\t// is not set, then write buffers are allocated to the connection for the\n\t// lifetime of the connection.\n\t//\n\t// A pool is most useful when the application has a modest volume of writes\n\t// across a large number of connections.\n\t//\n\t// Applications should use a single pool for each unique value of\n\t// WriteBufferSize.\n\tWriteBufferPool BufferPool\n\n\t// Subprotocols specifies the client's requested subprotocols.\n\tSubprotocols []string\n\n\t// EnableCompression specifies if the client should attempt to negotiate\n\t// per message compression (RFC 7692). Setting this value to true does not\n\t// guarantee that compression will be supported. Currently only \"no context\n\t// takeover\" modes are supported.\n\tEnableCompression bool\n\n\t// Jar specifies the cookie jar.\n\t// If Jar is nil, cookies are not sent in requests and ignored\n\t// in responses.\n\tJar http.CookieJar\n}\n\n// Dial creates a new client connection by calling DialContext with a background context.\nfunc (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) {\n\treturn d.DialContext(context.Background(), urlStr, requestHeader)\n}\n\nvar errMalformedURL = errors.New(\"malformed ws or wss URL\")\n\nfunc hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) {\n\thostPort = u.Host\n\thostNoPort = u.Host\n\tif i := strings.LastIndex(u.Host, \":\"); i > strings.LastIndex(u.Host, \"]\") {\n\t\thostNoPort = hostNoPort[:i]\n\t} else {\n\t\tswitch u.Scheme {\n\t\tcase \"wss\":\n\t\t\thostPort += \":443\"\n\t\tcase \"https\":\n\t\t\thostPort += \":443\"\n\t\tdefault:\n\t\t\thostPort += \":80\"\n\t\t}\n\t}\n\treturn hostPort, hostNoPort\n}\n\n// DefaultDialer is a dialer with all fields set to the default values.\nvar DefaultDialer = &Dialer{\n\tProxy:            http.ProxyFromEnvironment,\n\tHandshakeTimeout: 45 * time.Second,\n}\n\n// nilDialer is dialer to use when receiver is nil.\nvar nilDialer = *DefaultDialer\n\n// DialContext creates a new client connection. Use requestHeader to specify the\n// origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies (Cookie).\n// Use the response.Header to get the selected subprotocol\n// (Sec-WebSocket-Protocol) and cookies (Set-Cookie).\n//\n// The context will be used in the request and in the Dialer\n//\n// If the WebSocket handshake fails, ErrBadHandshake is returned along with a\n// non-nil *http.Response so that callers can handle redirects, authentication,\n// etcetera. The response body may not contain the entire response and does not\n// need to be closed by the application.\nfunc (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) {\n\tif d == nil {\n\t\td = &nilDialer\n\t}\n\n\tchallengeKey, err := generateChallengeKey()\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tu, err := url.Parse(urlStr)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tswitch u.Scheme {\n\tcase \"ws\":\n\t\tu.Scheme = \"http\"\n\tcase \"wss\":\n\t\tu.Scheme = \"https\"\n\tdefault:\n\t\treturn nil, nil, errMalformedURL\n\t}\n\n\tif u.User != nil {\n\t\t// User name and password are not allowed in websocket URIs.\n\t\treturn nil, nil, errMalformedURL\n\t}\n\n\treq := &http.Request{\n\t\tMethod:     \"GET\",\n\t\tURL:        u,\n\t\tProto:      \"HTTP/1.1\",\n\t\tProtoMajor: 1,\n\t\tProtoMinor: 1,\n\t\tHeader:     make(http.Header),\n\t\tHost:       u.Host,\n\t}\n\treq = req.WithContext(ctx)\n\n\t// Set the cookies present in the cookie jar of the dialer\n\tif d.Jar != nil {\n\t\tfor _, cookie := range d.Jar.Cookies(u) {\n\t\t\treq.AddCookie(cookie)\n\t\t}\n\t}\n\n\t// Set the request headers using the capitalization for names and values in\n\t// RFC examples. Although the capitalization shouldn't matter, there are\n\t// servers that depend on it. The Header.Set method is not used because the\n\t// method canonicalizes the header names.\n\treq.Header[\"Upgrade\"] = []string{\"websocket\"}\n\treq.Header[\"Connection\"] = []string{\"Upgrade\"}\n\treq.Header[\"Sec-WebSocket-Key\"] = []string{challengeKey}\n\treq.Header[\"Sec-WebSocket-Version\"] = []string{\"13\"}\n\tif len(d.Subprotocols) > 0 {\n\t\treq.Header[\"Sec-WebSocket-Protocol\"] = []string{strings.Join(d.Subprotocols, \", \")}\n\t}\n\tfor k, vs := range requestHeader {\n\t\tswitch {\n\t\tcase k == \"Host\":\n\t\t\tif len(vs) > 0 {\n\t\t\t\treq.Host = vs[0]\n\t\t\t}\n\t\tcase k == \"Upgrade\" ||\n\t\t\tk == \"Connection\" ||\n\t\t\tk == \"Sec-Websocket-Key\" ||\n\t\t\tk == \"Sec-Websocket-Version\" ||\n\t\t\tk == \"Sec-Websocket-Extensions\" ||\n\t\t\t(k == \"Sec-Websocket-Protocol\" && len(d.Subprotocols) > 0):\n\t\t\treturn nil, nil, errors.New(\"websocket: duplicate header not allowed: \" + k)\n\t\tcase k == \"Sec-Websocket-Protocol\":\n\t\t\treq.Header[\"Sec-WebSocket-Protocol\"] = vs\n\t\tdefault:\n\t\t\treq.Header[k] = vs\n\t\t}\n\t}\n\n\tif d.EnableCompression {\n\t\treq.Header[\"Sec-WebSocket-Extensions\"] = []string{\"permessage-deflate; server_no_context_takeover; client_no_context_takeover\"}\n\t}\n\n\tif d.HandshakeTimeout != 0 {\n\t\tvar cancel func()\n\t\tctx, cancel = context.WithTimeout(ctx, d.HandshakeTimeout)\n\t\tdefer cancel()\n\t}\n\n\t// Get network dial function.\n\tvar netDial func(network, add string) (net.Conn, error)\n\n\tif d.NetDialContext != nil {\n\t\tnetDial = func(network, addr string) (net.Conn, error) {\n\t\t\treturn d.NetDialContext(ctx, network, addr)\n\t\t}\n\t} else if d.NetDial != nil {\n\t\tnetDial = d.NetDial\n\t} else {\n\t\tnetDialer := &net.Dialer{}\n\t\tnetDial = func(network, addr string) (net.Conn, error) {\n\t\t\treturn netDialer.DialContext(ctx, network, addr)\n\t\t}\n\t}\n\n\t// If needed, wrap the dial function to set the connection deadline.\n\tif deadline, ok := ctx.Deadline(); ok {\n\t\tforwardDial := netDial\n\t\tnetDial = func(network, addr string) (net.Conn, error) {\n\t\t\tc, err := forwardDial(network, addr)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\terr = c.SetDeadline(deadline)\n\t\t\tif err != nil {\n\t\t\t\tc.Close()\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn c, nil\n\t\t}\n\t}\n\n\t// If needed, wrap the dial function to connect through a proxy.\n\tif d.Proxy != nil {\n\t\tproxyURL, err := d.Proxy(req)\n\t\tif err != nil {\n\t\t\treturn nil, nil, err\n\t\t}\n\t\tif proxyURL != nil {\n\t\t\tdialer, err := proxy_FromURL(proxyURL, netDialerFunc(netDial))\n\t\t\tif err != nil {\n\t\t\t\treturn nil, nil, err\n\t\t\t}\n\t\t\tnetDial = dialer.Dial\n\t\t}\n\t}\n\n\thostPort, hostNoPort := hostPortNoPort(u)\n\ttrace := httptrace.ContextClientTrace(ctx)\n\tif trace != nil && trace.GetConn != nil {\n\t\ttrace.GetConn(hostPort)\n\t}\n\n\tnetConn, err := netDial(\"tcp\", hostPort)\n\tif trace != nil && trace.GotConn != nil {\n\t\ttrace.GotConn(httptrace.GotConnInfo{\n\t\t\tConn: netConn,\n\t\t})\n\t}\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tdefer func() {\n\t\tif netConn != nil {\n\t\t\tnetConn.Close()\n\t\t}\n\t}()\n\n\tif u.Scheme == \"https\" {\n\t\tcfg := cloneTLSConfig(d.TLSClientConfig)\n\t\tif cfg.ServerName == \"\" {\n\t\t\tcfg.ServerName = hostNoPort\n\t\t}\n\t\ttlsConn := tls.Client(netConn, cfg)\n\t\tnetConn = tlsConn\n\n\t\tvar err error\n\t\tif trace != nil {\n\t\t\terr = doHandshakeWithTrace(trace, tlsConn, cfg)\n\t\t} else {\n\t\t\terr = doHandshake(tlsConn, cfg)\n\t\t}\n\n\t\tif err != nil {\n\t\t\treturn nil, nil, err\n\t\t}\n\t}\n\n\tconn := newConn(netConn, false, d.ReadBufferSize, d.WriteBufferSize, d.WriteBufferPool, nil, nil)\n\n\tif err := req.Write(netConn); err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tif trace != nil && trace.GotFirstResponseByte != nil {\n\t\tif peek, err := conn.br.Peek(1); err == nil && len(peek) == 1 {\n\t\t\ttrace.GotFirstResponseByte()\n\t\t}\n\t}\n\n\tresp, err := http.ReadResponse(conn.br, req)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tif d.Jar != nil {\n\t\tif rc := resp.Cookies(); len(rc) > 0 {\n\t\t\td.Jar.SetCookies(u, rc)\n\t\t}\n\t}\n\n\tif resp.StatusCode != 101 ||\n\t\t!strings.EqualFold(resp.Header.Get(\"Upgrade\"), \"websocket\") ||\n\t\t!strings.EqualFold(resp.Header.Get(\"Connection\"), \"upgrade\") ||\n\t\tresp.Header.Get(\"Sec-Websocket-Accept\") != computeAcceptKey(challengeKey) {\n\t\t// Before closing the network connection on return from this\n\t\t// function, slurp up some of the response to aid application\n\t\t// debugging.\n\t\tbuf := make([]byte, 1024)\n\t\tn, _ := io.ReadFull(resp.Body, buf)\n\t\tresp.Body = ioutil.NopCloser(bytes.NewReader(buf[:n]))\n\t\treturn nil, resp, ErrBadHandshake\n\t}\n\n\tfor _, ext := range parseExtensions(resp.Header) {\n\t\tif ext[\"\"] != \"permessage-deflate\" {\n\t\t\tcontinue\n\t\t}\n\t\t_, snct := ext[\"server_no_context_takeover\"]\n\t\t_, cnct := ext[\"client_no_context_takeover\"]\n\t\tif !snct || !cnct {\n\t\t\treturn nil, resp, errInvalidCompression\n\t\t}\n\t\tconn.newCompressionWriter = compressNoContextTakeover\n\t\tconn.newDecompressionReader = decompressNoContextTakeover\n\t\tbreak\n\t}\n\n\tresp.Body = ioutil.NopCloser(bytes.NewReader([]byte{}))\n\tconn.subprotocol = resp.Header.Get(\"Sec-Websocket-Protocol\")\n\n\tnetConn.SetDeadline(time.Time{})\n\tnetConn = nil // to avoid close in defer.\n\treturn conn, resp, nil\n}\n\nfunc doHandshake(tlsConn *tls.Conn, cfg *tls.Config) error {\n\tif err := tlsConn.Handshake(); err != nil {\n\t\treturn err\n\t}\n\tif !cfg.InsecureSkipVerify {\n\t\tif err := tlsConn.VerifyHostname(cfg.ServerName); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "websockets/v1/vendor/github.com/gorilla/websocket/client_clone.go",
    "content": "// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// +build go1.8\n\npackage websocket\n\nimport \"crypto/tls\"\n\nfunc cloneTLSConfig(cfg *tls.Config) *tls.Config {\n\tif cfg == nil {\n\t\treturn &tls.Config{}\n\t}\n\treturn cfg.Clone()\n}\n"
  },
  {
    "path": "websockets/v1/vendor/github.com/gorilla/websocket/client_clone_legacy.go",
    "content": "// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// +build !go1.8\n\npackage websocket\n\nimport \"crypto/tls\"\n\n// cloneTLSConfig clones all public fields except the fields\n// SessionTicketsDisabled and SessionTicketKey. This avoids copying the\n// sync.Mutex in the sync.Once and makes it safe to call cloneTLSConfig on a\n// config in active use.\nfunc cloneTLSConfig(cfg *tls.Config) *tls.Config {\n\tif cfg == nil {\n\t\treturn &tls.Config{}\n\t}\n\treturn &tls.Config{\n\t\tRand:                     cfg.Rand,\n\t\tTime:                     cfg.Time,\n\t\tCertificates:             cfg.Certificates,\n\t\tNameToCertificate:        cfg.NameToCertificate,\n\t\tGetCertificate:           cfg.GetCertificate,\n\t\tRootCAs:                  cfg.RootCAs,\n\t\tNextProtos:               cfg.NextProtos,\n\t\tServerName:               cfg.ServerName,\n\t\tClientAuth:               cfg.ClientAuth,\n\t\tClientCAs:                cfg.ClientCAs,\n\t\tInsecureSkipVerify:       cfg.InsecureSkipVerify,\n\t\tCipherSuites:             cfg.CipherSuites,\n\t\tPreferServerCipherSuites: cfg.PreferServerCipherSuites,\n\t\tClientSessionCache:       cfg.ClientSessionCache,\n\t\tMinVersion:               cfg.MinVersion,\n\t\tMaxVersion:               cfg.MaxVersion,\n\t\tCurvePreferences:         cfg.CurvePreferences,\n\t}\n}\n"
  },
  {
    "path": "websockets/v1/vendor/github.com/gorilla/websocket/compression.go",
    "content": "// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage websocket\n\nimport (\n\t\"compress/flate\"\n\t\"errors\"\n\t\"io\"\n\t\"strings\"\n\t\"sync\"\n)\n\nconst (\n\tminCompressionLevel     = -2 // flate.HuffmanOnly not defined in Go < 1.6\n\tmaxCompressionLevel     = flate.BestCompression\n\tdefaultCompressionLevel = 1\n)\n\nvar (\n\tflateWriterPools [maxCompressionLevel - minCompressionLevel + 1]sync.Pool\n\tflateReaderPool  = sync.Pool{New: func() interface{} {\n\t\treturn flate.NewReader(nil)\n\t}}\n)\n\nfunc decompressNoContextTakeover(r io.Reader) io.ReadCloser {\n\tconst tail =\n\t// Add four bytes as specified in RFC\n\t\"\\x00\\x00\\xff\\xff\" +\n\t\t// Add final block to squelch unexpected EOF error from flate reader.\n\t\t\"\\x01\\x00\\x00\\xff\\xff\"\n\n\tfr, _ := flateReaderPool.Get().(io.ReadCloser)\n\tfr.(flate.Resetter).Reset(io.MultiReader(r, strings.NewReader(tail)), nil)\n\treturn &flateReadWrapper{fr}\n}\n\nfunc isValidCompressionLevel(level int) bool {\n\treturn minCompressionLevel <= level && level <= maxCompressionLevel\n}\n\nfunc compressNoContextTakeover(w io.WriteCloser, level int) io.WriteCloser {\n\tp := &flateWriterPools[level-minCompressionLevel]\n\ttw := &truncWriter{w: w}\n\tfw, _ := p.Get().(*flate.Writer)\n\tif fw == nil {\n\t\tfw, _ = flate.NewWriter(tw, level)\n\t} else {\n\t\tfw.Reset(tw)\n\t}\n\treturn &flateWriteWrapper{fw: fw, tw: tw, p: p}\n}\n\n// truncWriter is an io.Writer that writes all but the last four bytes of the\n// stream to another io.Writer.\ntype truncWriter struct {\n\tw io.WriteCloser\n\tn int\n\tp [4]byte\n}\n\nfunc (w *truncWriter) Write(p []byte) (int, error) {\n\tn := 0\n\n\t// fill buffer first for simplicity.\n\tif w.n < len(w.p) {\n\t\tn = copy(w.p[w.n:], p)\n\t\tp = p[n:]\n\t\tw.n += n\n\t\tif len(p) == 0 {\n\t\t\treturn n, nil\n\t\t}\n\t}\n\n\tm := len(p)\n\tif m > len(w.p) {\n\t\tm = len(w.p)\n\t}\n\n\tif nn, err := w.w.Write(w.p[:m]); err != nil {\n\t\treturn n + nn, err\n\t}\n\n\tcopy(w.p[:], w.p[m:])\n\tcopy(w.p[len(w.p)-m:], p[len(p)-m:])\n\tnn, err := w.w.Write(p[:len(p)-m])\n\treturn n + nn, err\n}\n\ntype flateWriteWrapper struct {\n\tfw *flate.Writer\n\ttw *truncWriter\n\tp  *sync.Pool\n}\n\nfunc (w *flateWriteWrapper) Write(p []byte) (int, error) {\n\tif w.fw == nil {\n\t\treturn 0, errWriteClosed\n\t}\n\treturn w.fw.Write(p)\n}\n\nfunc (w *flateWriteWrapper) Close() error {\n\tif w.fw == nil {\n\t\treturn errWriteClosed\n\t}\n\terr1 := w.fw.Flush()\n\tw.p.Put(w.fw)\n\tw.fw = nil\n\tif w.tw.p != [4]byte{0, 0, 0xff, 0xff} {\n\t\treturn errors.New(\"websocket: internal error, unexpected bytes at end of flate stream\")\n\t}\n\terr2 := w.tw.w.Close()\n\tif err1 != nil {\n\t\treturn err1\n\t}\n\treturn err2\n}\n\ntype flateReadWrapper struct {\n\tfr io.ReadCloser\n}\n\nfunc (r *flateReadWrapper) Read(p []byte) (int, error) {\n\tif r.fr == nil {\n\t\treturn 0, io.ErrClosedPipe\n\t}\n\tn, err := r.fr.Read(p)\n\tif err == io.EOF {\n\t\t// Preemptively place the reader back in the pool. This helps with\n\t\t// scenarios where the application does not call NextReader() soon after\n\t\t// this final read.\n\t\tr.Close()\n\t}\n\treturn n, err\n}\n\nfunc (r *flateReadWrapper) Close() error {\n\tif r.fr == nil {\n\t\treturn io.ErrClosedPipe\n\t}\n\terr := r.fr.Close()\n\tflateReaderPool.Put(r.fr)\n\tr.fr = nil\n\treturn err\n}\n"
  },
  {
    "path": "websockets/v1/vendor/github.com/gorilla/websocket/conn.go",
    "content": "// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage websocket\n\nimport (\n\t\"bufio\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"math/rand\"\n\t\"net\"\n\t\"strconv\"\n\t\"sync\"\n\t\"time\"\n\t\"unicode/utf8\"\n)\n\nconst (\n\t// Frame header byte 0 bits from Section 5.2 of RFC 6455\n\tfinalBit = 1 << 7\n\trsv1Bit  = 1 << 6\n\trsv2Bit  = 1 << 5\n\trsv3Bit  = 1 << 4\n\n\t// Frame header byte 1 bits from Section 5.2 of RFC 6455\n\tmaskBit = 1 << 7\n\n\tmaxFrameHeaderSize         = 2 + 8 + 4 // Fixed header + length + mask\n\tmaxControlFramePayloadSize = 125\n\n\twriteWait = time.Second\n\n\tdefaultReadBufferSize  = 4096\n\tdefaultWriteBufferSize = 4096\n\n\tcontinuationFrame = 0\n\tnoFrame           = -1\n)\n\n// Close codes defined in RFC 6455, section 11.7.\nconst (\n\tCloseNormalClosure           = 1000\n\tCloseGoingAway               = 1001\n\tCloseProtocolError           = 1002\n\tCloseUnsupportedData         = 1003\n\tCloseNoStatusReceived        = 1005\n\tCloseAbnormalClosure         = 1006\n\tCloseInvalidFramePayloadData = 1007\n\tClosePolicyViolation         = 1008\n\tCloseMessageTooBig           = 1009\n\tCloseMandatoryExtension      = 1010\n\tCloseInternalServerErr       = 1011\n\tCloseServiceRestart          = 1012\n\tCloseTryAgainLater           = 1013\n\tCloseTLSHandshake            = 1015\n)\n\n// The message types are defined in RFC 6455, section 11.8.\nconst (\n\t// TextMessage denotes a text data message. The text message payload is\n\t// interpreted as UTF-8 encoded text data.\n\tTextMessage = 1\n\n\t// BinaryMessage denotes a binary data message.\n\tBinaryMessage = 2\n\n\t// CloseMessage denotes a close control message. The optional message\n\t// payload contains a numeric code and text. Use the FormatCloseMessage\n\t// function to format a close message payload.\n\tCloseMessage = 8\n\n\t// PingMessage denotes a ping control message. The optional message payload\n\t// is UTF-8 encoded text.\n\tPingMessage = 9\n\n\t// PongMessage denotes a pong control message. The optional message payload\n\t// is UTF-8 encoded text.\n\tPongMessage = 10\n)\n\n// ErrCloseSent is returned when the application writes a message to the\n// connection after sending a close message.\nvar ErrCloseSent = errors.New(\"websocket: close sent\")\n\n// ErrReadLimit is returned when reading a message that is larger than the\n// read limit set for the connection.\nvar ErrReadLimit = errors.New(\"websocket: read limit exceeded\")\n\n// netError satisfies the net Error interface.\ntype netError struct {\n\tmsg       string\n\ttemporary bool\n\ttimeout   bool\n}\n\nfunc (e *netError) Error() string   { return e.msg }\nfunc (e *netError) Temporary() bool { return e.temporary }\nfunc (e *netError) Timeout() bool   { return e.timeout }\n\n// CloseError represents a close message.\ntype CloseError struct {\n\t// Code is defined in RFC 6455, section 11.7.\n\tCode int\n\n\t// Text is the optional text payload.\n\tText string\n}\n\nfunc (e *CloseError) Error() string {\n\ts := []byte(\"websocket: close \")\n\ts = strconv.AppendInt(s, int64(e.Code), 10)\n\tswitch e.Code {\n\tcase CloseNormalClosure:\n\t\ts = append(s, \" (normal)\"...)\n\tcase CloseGoingAway:\n\t\ts = append(s, \" (going away)\"...)\n\tcase CloseProtocolError:\n\t\ts = append(s, \" (protocol error)\"...)\n\tcase CloseUnsupportedData:\n\t\ts = append(s, \" (unsupported data)\"...)\n\tcase CloseNoStatusReceived:\n\t\ts = append(s, \" (no status)\"...)\n\tcase CloseAbnormalClosure:\n\t\ts = append(s, \" (abnormal closure)\"...)\n\tcase CloseInvalidFramePayloadData:\n\t\ts = append(s, \" (invalid payload data)\"...)\n\tcase ClosePolicyViolation:\n\t\ts = append(s, \" (policy violation)\"...)\n\tcase CloseMessageTooBig:\n\t\ts = append(s, \" (message too big)\"...)\n\tcase CloseMandatoryExtension:\n\t\ts = append(s, \" (mandatory extension missing)\"...)\n\tcase CloseInternalServerErr:\n\t\ts = append(s, \" (internal server error)\"...)\n\tcase CloseTLSHandshake:\n\t\ts = append(s, \" (TLS handshake error)\"...)\n\t}\n\tif e.Text != \"\" {\n\t\ts = append(s, \": \"...)\n\t\ts = append(s, e.Text...)\n\t}\n\treturn string(s)\n}\n\n// IsCloseError returns boolean indicating whether the error is a *CloseError\n// with one of the specified codes.\nfunc IsCloseError(err error, codes ...int) bool {\n\tif e, ok := err.(*CloseError); ok {\n\t\tfor _, code := range codes {\n\t\t\tif e.Code == code {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\treturn false\n}\n\n// IsUnexpectedCloseError returns boolean indicating whether the error is a\n// *CloseError with a code not in the list of expected codes.\nfunc IsUnexpectedCloseError(err error, expectedCodes ...int) bool {\n\tif e, ok := err.(*CloseError); ok {\n\t\tfor _, code := range expectedCodes {\n\t\t\tif e.Code == code {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\treturn true\n\t}\n\treturn false\n}\n\nvar (\n\terrWriteTimeout        = &netError{msg: \"websocket: write timeout\", timeout: true, temporary: true}\n\terrUnexpectedEOF       = &CloseError{Code: CloseAbnormalClosure, Text: io.ErrUnexpectedEOF.Error()}\n\terrBadWriteOpCode      = errors.New(\"websocket: bad write message type\")\n\terrWriteClosed         = errors.New(\"websocket: write closed\")\n\terrInvalidControlFrame = errors.New(\"websocket: invalid control frame\")\n)\n\nfunc newMaskKey() [4]byte {\n\tn := rand.Uint32()\n\treturn [4]byte{byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24)}\n}\n\nfunc hideTempErr(err error) error {\n\tif e, ok := err.(net.Error); ok && e.Temporary() {\n\t\terr = &netError{msg: e.Error(), timeout: e.Timeout()}\n\t}\n\treturn err\n}\n\nfunc isControl(frameType int) bool {\n\treturn frameType == CloseMessage || frameType == PingMessage || frameType == PongMessage\n}\n\nfunc isData(frameType int) bool {\n\treturn frameType == TextMessage || frameType == BinaryMessage\n}\n\nvar validReceivedCloseCodes = map[int]bool{\n\t// see http://www.iana.org/assignments/websocket/websocket.xhtml#close-code-number\n\n\tCloseNormalClosure:           true,\n\tCloseGoingAway:               true,\n\tCloseProtocolError:           true,\n\tCloseUnsupportedData:         true,\n\tCloseNoStatusReceived:        false,\n\tCloseAbnormalClosure:         false,\n\tCloseInvalidFramePayloadData: true,\n\tClosePolicyViolation:         true,\n\tCloseMessageTooBig:           true,\n\tCloseMandatoryExtension:      true,\n\tCloseInternalServerErr:       true,\n\tCloseServiceRestart:          true,\n\tCloseTryAgainLater:           true,\n\tCloseTLSHandshake:            false,\n}\n\nfunc isValidReceivedCloseCode(code int) bool {\n\treturn validReceivedCloseCodes[code] || (code >= 3000 && code <= 4999)\n}\n\n// BufferPool represents a pool of buffers. The *sync.Pool type satisfies this\n// interface.  The type of the value stored in a pool is not specified.\ntype BufferPool interface {\n\t// Get gets a value from the pool or returns nil if the pool is empty.\n\tGet() interface{}\n\t// Put adds a value to the pool.\n\tPut(interface{})\n}\n\n// writePoolData is the type added to the write buffer pool. This wrapper is\n// used to prevent applications from peeking at and depending on the values\n// added to the pool.\ntype writePoolData struct{ buf []byte }\n\n// The Conn type represents a WebSocket connection.\ntype Conn struct {\n\tconn        net.Conn\n\tisServer    bool\n\tsubprotocol string\n\n\t// Write fields\n\tmu            chan bool // used as mutex to protect write to conn\n\twriteBuf      []byte    // frame is constructed in this buffer.\n\twritePool     BufferPool\n\twriteBufSize  int\n\twriteDeadline time.Time\n\twriter        io.WriteCloser // the current writer returned to the application\n\tisWriting     bool           // for best-effort concurrent write detection\n\n\twriteErrMu sync.Mutex\n\twriteErr   error\n\n\tenableWriteCompression bool\n\tcompressionLevel       int\n\tnewCompressionWriter   func(io.WriteCloser, int) io.WriteCloser\n\n\t// Read fields\n\treader        io.ReadCloser // the current reader returned to the application\n\treadErr       error\n\tbr            *bufio.Reader\n\treadRemaining int64 // bytes remaining in current frame.\n\treadFinal     bool  // true the current message has more frames.\n\treadLength    int64 // Message size.\n\treadLimit     int64 // Maximum message size.\n\treadMaskPos   int\n\treadMaskKey   [4]byte\n\thandlePong    func(string) error\n\thandlePing    func(string) error\n\thandleClose   func(int, string) error\n\treadErrCount  int\n\tmessageReader *messageReader // the current low-level reader\n\n\treadDecompress         bool // whether last read frame had RSV1 set\n\tnewDecompressionReader func(io.Reader) io.ReadCloser\n}\n\nfunc newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int, writeBufferPool BufferPool, br *bufio.Reader, writeBuf []byte) *Conn {\n\n\tif br == nil {\n\t\tif readBufferSize == 0 {\n\t\t\treadBufferSize = defaultReadBufferSize\n\t\t} else if readBufferSize < maxControlFramePayloadSize {\n\t\t\t// must be large enough for control frame\n\t\t\treadBufferSize = maxControlFramePayloadSize\n\t\t}\n\t\tbr = bufio.NewReaderSize(conn, readBufferSize)\n\t}\n\n\tif writeBufferSize <= 0 {\n\t\twriteBufferSize = defaultWriteBufferSize\n\t}\n\twriteBufferSize += maxFrameHeaderSize\n\n\tif writeBuf == nil && writeBufferPool == nil {\n\t\twriteBuf = make([]byte, writeBufferSize)\n\t}\n\n\tmu := make(chan bool, 1)\n\tmu <- true\n\tc := &Conn{\n\t\tisServer:               isServer,\n\t\tbr:                     br,\n\t\tconn:                   conn,\n\t\tmu:                     mu,\n\t\treadFinal:              true,\n\t\twriteBuf:               writeBuf,\n\t\twritePool:              writeBufferPool,\n\t\twriteBufSize:           writeBufferSize,\n\t\tenableWriteCompression: true,\n\t\tcompressionLevel:       defaultCompressionLevel,\n\t}\n\tc.SetCloseHandler(nil)\n\tc.SetPingHandler(nil)\n\tc.SetPongHandler(nil)\n\treturn c\n}\n\n// Subprotocol returns the negotiated protocol for the connection.\nfunc (c *Conn) Subprotocol() string {\n\treturn c.subprotocol\n}\n\n// Close closes the underlying network connection without sending or waiting\n// for a close message.\nfunc (c *Conn) Close() error {\n\treturn c.conn.Close()\n}\n\n// LocalAddr returns the local network address.\nfunc (c *Conn) LocalAddr() net.Addr {\n\treturn c.conn.LocalAddr()\n}\n\n// RemoteAddr returns the remote network address.\nfunc (c *Conn) RemoteAddr() net.Addr {\n\treturn c.conn.RemoteAddr()\n}\n\n// Write methods\n\nfunc (c *Conn) writeFatal(err error) error {\n\terr = hideTempErr(err)\n\tc.writeErrMu.Lock()\n\tif c.writeErr == nil {\n\t\tc.writeErr = err\n\t}\n\tc.writeErrMu.Unlock()\n\treturn err\n}\n\nfunc (c *Conn) read(n int) ([]byte, error) {\n\tp, err := c.br.Peek(n)\n\tif err == io.EOF {\n\t\terr = errUnexpectedEOF\n\t}\n\tc.br.Discard(len(p))\n\treturn p, err\n}\n\nfunc (c *Conn) write(frameType int, deadline time.Time, buf0, buf1 []byte) error {\n\t<-c.mu\n\tdefer func() { c.mu <- true }()\n\n\tc.writeErrMu.Lock()\n\terr := c.writeErr\n\tc.writeErrMu.Unlock()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tc.conn.SetWriteDeadline(deadline)\n\tif len(buf1) == 0 {\n\t\t_, err = c.conn.Write(buf0)\n\t} else {\n\t\terr = c.writeBufs(buf0, buf1)\n\t}\n\tif err != nil {\n\t\treturn c.writeFatal(err)\n\t}\n\tif frameType == CloseMessage {\n\t\tc.writeFatal(ErrCloseSent)\n\t}\n\treturn nil\n}\n\n// WriteControl writes a control message with the given deadline. The allowed\n// message types are CloseMessage, PingMessage and PongMessage.\nfunc (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) error {\n\tif !isControl(messageType) {\n\t\treturn errBadWriteOpCode\n\t}\n\tif len(data) > maxControlFramePayloadSize {\n\t\treturn errInvalidControlFrame\n\t}\n\n\tb0 := byte(messageType) | finalBit\n\tb1 := byte(len(data))\n\tif !c.isServer {\n\t\tb1 |= maskBit\n\t}\n\n\tbuf := make([]byte, 0, maxFrameHeaderSize+maxControlFramePayloadSize)\n\tbuf = append(buf, b0, b1)\n\n\tif c.isServer {\n\t\tbuf = append(buf, data...)\n\t} else {\n\t\tkey := newMaskKey()\n\t\tbuf = append(buf, key[:]...)\n\t\tbuf = append(buf, data...)\n\t\tmaskBytes(key, 0, buf[6:])\n\t}\n\n\td := time.Hour * 1000\n\tif !deadline.IsZero() {\n\t\td = deadline.Sub(time.Now())\n\t\tif d < 0 {\n\t\t\treturn errWriteTimeout\n\t\t}\n\t}\n\n\ttimer := time.NewTimer(d)\n\tselect {\n\tcase <-c.mu:\n\t\ttimer.Stop()\n\tcase <-timer.C:\n\t\treturn errWriteTimeout\n\t}\n\tdefer func() { c.mu <- true }()\n\n\tc.writeErrMu.Lock()\n\terr := c.writeErr\n\tc.writeErrMu.Unlock()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tc.conn.SetWriteDeadline(deadline)\n\t_, err = c.conn.Write(buf)\n\tif err != nil {\n\t\treturn c.writeFatal(err)\n\t}\n\tif messageType == CloseMessage {\n\t\tc.writeFatal(ErrCloseSent)\n\t}\n\treturn err\n}\n\nfunc (c *Conn) prepWrite(messageType int) error {\n\t// Close previous writer if not already closed by the application. It's\n\t// probably better to return an error in this situation, but we cannot\n\t// change this without breaking existing applications.\n\tif c.writer != nil {\n\t\tc.writer.Close()\n\t\tc.writer = nil\n\t}\n\n\tif !isControl(messageType) && !isData(messageType) {\n\t\treturn errBadWriteOpCode\n\t}\n\n\tc.writeErrMu.Lock()\n\terr := c.writeErr\n\tc.writeErrMu.Unlock()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif c.writeBuf == nil {\n\t\twpd, ok := c.writePool.Get().(writePoolData)\n\t\tif ok {\n\t\t\tc.writeBuf = wpd.buf\n\t\t} else {\n\t\t\tc.writeBuf = make([]byte, c.writeBufSize)\n\t\t}\n\t}\n\treturn nil\n}\n\n// NextWriter returns a writer for the next message to send. The writer's Close\n// method flushes the complete message to the network.\n//\n// There can be at most one open writer on a connection. NextWriter closes the\n// previous writer if the application has not already done so.\n//\n// All message types (TextMessage, BinaryMessage, CloseMessage, PingMessage and\n// PongMessage) are supported.\nfunc (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) {\n\tif err := c.prepWrite(messageType); err != nil {\n\t\treturn nil, err\n\t}\n\n\tmw := &messageWriter{\n\t\tc:         c,\n\t\tframeType: messageType,\n\t\tpos:       maxFrameHeaderSize,\n\t}\n\tc.writer = mw\n\tif c.newCompressionWriter != nil && c.enableWriteCompression && isData(messageType) {\n\t\tw := c.newCompressionWriter(c.writer, c.compressionLevel)\n\t\tmw.compress = true\n\t\tc.writer = w\n\t}\n\treturn c.writer, nil\n}\n\ntype messageWriter struct {\n\tc         *Conn\n\tcompress  bool // whether next call to flushFrame should set RSV1\n\tpos       int  // end of data in writeBuf.\n\tframeType int  // type of the current frame.\n\terr       error\n}\n\nfunc (w *messageWriter) fatal(err error) error {\n\tif w.err != nil {\n\t\tw.err = err\n\t\tw.c.writer = nil\n\t}\n\treturn err\n}\n\n// flushFrame writes buffered data and extra as a frame to the network. The\n// final argument indicates that this is the last frame in the message.\nfunc (w *messageWriter) flushFrame(final bool, extra []byte) error {\n\tc := w.c\n\tlength := w.pos - maxFrameHeaderSize + len(extra)\n\n\t// Check for invalid control frames.\n\tif isControl(w.frameType) &&\n\t\t(!final || length > maxControlFramePayloadSize) {\n\t\treturn w.fatal(errInvalidControlFrame)\n\t}\n\n\tb0 := byte(w.frameType)\n\tif final {\n\t\tb0 |= finalBit\n\t}\n\tif w.compress {\n\t\tb0 |= rsv1Bit\n\t}\n\tw.compress = false\n\n\tb1 := byte(0)\n\tif !c.isServer {\n\t\tb1 |= maskBit\n\t}\n\n\t// Assume that the frame starts at beginning of c.writeBuf.\n\tframePos := 0\n\tif c.isServer {\n\t\t// Adjust up if mask not included in the header.\n\t\tframePos = 4\n\t}\n\n\tswitch {\n\tcase length >= 65536:\n\t\tc.writeBuf[framePos] = b0\n\t\tc.writeBuf[framePos+1] = b1 | 127\n\t\tbinary.BigEndian.PutUint64(c.writeBuf[framePos+2:], uint64(length))\n\tcase length > 125:\n\t\tframePos += 6\n\t\tc.writeBuf[framePos] = b0\n\t\tc.writeBuf[framePos+1] = b1 | 126\n\t\tbinary.BigEndian.PutUint16(c.writeBuf[framePos+2:], uint16(length))\n\tdefault:\n\t\tframePos += 8\n\t\tc.writeBuf[framePos] = b0\n\t\tc.writeBuf[framePos+1] = b1 | byte(length)\n\t}\n\n\tif !c.isServer {\n\t\tkey := newMaskKey()\n\t\tcopy(c.writeBuf[maxFrameHeaderSize-4:], key[:])\n\t\tmaskBytes(key, 0, c.writeBuf[maxFrameHeaderSize:w.pos])\n\t\tif len(extra) > 0 {\n\t\t\treturn c.writeFatal(errors.New(\"websocket: internal error, extra used in client mode\"))\n\t\t}\n\t}\n\n\t// Write the buffers to the connection with best-effort detection of\n\t// concurrent writes. See the concurrency section in the package\n\t// documentation for more info.\n\n\tif c.isWriting {\n\t\tpanic(\"concurrent write to websocket connection\")\n\t}\n\tc.isWriting = true\n\n\terr := c.write(w.frameType, c.writeDeadline, c.writeBuf[framePos:w.pos], extra)\n\n\tif !c.isWriting {\n\t\tpanic(\"concurrent write to websocket connection\")\n\t}\n\tc.isWriting = false\n\n\tif err != nil {\n\t\treturn w.fatal(err)\n\t}\n\n\tif final {\n\t\tc.writer = nil\n\t\tif c.writePool != nil {\n\t\t\tc.writePool.Put(writePoolData{buf: c.writeBuf})\n\t\t\tc.writeBuf = nil\n\t\t}\n\t\treturn nil\n\t}\n\n\t// Setup for next frame.\n\tw.pos = maxFrameHeaderSize\n\tw.frameType = continuationFrame\n\treturn nil\n}\n\nfunc (w *messageWriter) ncopy(max int) (int, error) {\n\tn := len(w.c.writeBuf) - w.pos\n\tif n <= 0 {\n\t\tif err := w.flushFrame(false, nil); err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tn = len(w.c.writeBuf) - w.pos\n\t}\n\tif n > max {\n\t\tn = max\n\t}\n\treturn n, nil\n}\n\nfunc (w *messageWriter) Write(p []byte) (int, error) {\n\tif w.err != nil {\n\t\treturn 0, w.err\n\t}\n\n\tif len(p) > 2*len(w.c.writeBuf) && w.c.isServer {\n\t\t// Don't buffer large messages.\n\t\terr := w.flushFrame(false, p)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\treturn len(p), nil\n\t}\n\n\tnn := len(p)\n\tfor len(p) > 0 {\n\t\tn, err := w.ncopy(len(p))\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tcopy(w.c.writeBuf[w.pos:], p[:n])\n\t\tw.pos += n\n\t\tp = p[n:]\n\t}\n\treturn nn, nil\n}\n\nfunc (w *messageWriter) WriteString(p string) (int, error) {\n\tif w.err != nil {\n\t\treturn 0, w.err\n\t}\n\n\tnn := len(p)\n\tfor len(p) > 0 {\n\t\tn, err := w.ncopy(len(p))\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tcopy(w.c.writeBuf[w.pos:], p[:n])\n\t\tw.pos += n\n\t\tp = p[n:]\n\t}\n\treturn nn, nil\n}\n\nfunc (w *messageWriter) ReadFrom(r io.Reader) (nn int64, err error) {\n\tif w.err != nil {\n\t\treturn 0, w.err\n\t}\n\tfor {\n\t\tif w.pos == len(w.c.writeBuf) {\n\t\t\terr = w.flushFrame(false, nil)\n\t\t\tif err != nil {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tvar n int\n\t\tn, err = r.Read(w.c.writeBuf[w.pos:])\n\t\tw.pos += n\n\t\tnn += int64(n)\n\t\tif err != nil {\n\t\t\tif err == io.EOF {\n\t\t\t\terr = nil\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t}\n\treturn nn, err\n}\n\nfunc (w *messageWriter) Close() error {\n\tif w.err != nil {\n\t\treturn w.err\n\t}\n\tif err := w.flushFrame(true, nil); err != nil {\n\t\treturn err\n\t}\n\tw.err = errWriteClosed\n\treturn nil\n}\n\n// WritePreparedMessage writes prepared message into connection.\nfunc (c *Conn) WritePreparedMessage(pm *PreparedMessage) error {\n\tframeType, frameData, err := pm.frame(prepareKey{\n\t\tisServer:         c.isServer,\n\t\tcompress:         c.newCompressionWriter != nil && c.enableWriteCompression && isData(pm.messageType),\n\t\tcompressionLevel: c.compressionLevel,\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\tif c.isWriting {\n\t\tpanic(\"concurrent write to websocket connection\")\n\t}\n\tc.isWriting = true\n\terr = c.write(frameType, c.writeDeadline, frameData, nil)\n\tif !c.isWriting {\n\t\tpanic(\"concurrent write to websocket connection\")\n\t}\n\tc.isWriting = false\n\treturn err\n}\n\n// WriteMessage is a helper method for getting a writer using NextWriter,\n// writing the message and closing the writer.\nfunc (c *Conn) WriteMessage(messageType int, data []byte) error {\n\n\tif c.isServer && (c.newCompressionWriter == nil || !c.enableWriteCompression) {\n\t\t// Fast path with no allocations and single frame.\n\n\t\tif err := c.prepWrite(messageType); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tmw := messageWriter{c: c, frameType: messageType, pos: maxFrameHeaderSize}\n\t\tn := copy(c.writeBuf[mw.pos:], data)\n\t\tmw.pos += n\n\t\tdata = data[n:]\n\t\treturn mw.flushFrame(true, data)\n\t}\n\n\tw, err := c.NextWriter(messageType)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif _, err = w.Write(data); err != nil {\n\t\treturn err\n\t}\n\treturn w.Close()\n}\n\n// SetWriteDeadline sets the write deadline on the underlying network\n// connection. After a write has timed out, the websocket state is corrupt and\n// all future writes will return an error. A zero value for t means writes will\n// not time out.\nfunc (c *Conn) SetWriteDeadline(t time.Time) error {\n\tc.writeDeadline = t\n\treturn nil\n}\n\n// Read methods\n\nfunc (c *Conn) advanceFrame() (int, error) {\n\t// 1. Skip remainder of previous frame.\n\n\tif c.readRemaining > 0 {\n\t\tif _, err := io.CopyN(ioutil.Discard, c.br, c.readRemaining); err != nil {\n\t\t\treturn noFrame, err\n\t\t}\n\t}\n\n\t// 2. Read and parse first two bytes of frame header.\n\n\tp, err := c.read(2)\n\tif err != nil {\n\t\treturn noFrame, err\n\t}\n\n\tfinal := p[0]&finalBit != 0\n\tframeType := int(p[0] & 0xf)\n\tmask := p[1]&maskBit != 0\n\tc.readRemaining = int64(p[1] & 0x7f)\n\n\tc.readDecompress = false\n\tif c.newDecompressionReader != nil && (p[0]&rsv1Bit) != 0 {\n\t\tc.readDecompress = true\n\t\tp[0] &^= rsv1Bit\n\t}\n\n\tif rsv := p[0] & (rsv1Bit | rsv2Bit | rsv3Bit); rsv != 0 {\n\t\treturn noFrame, c.handleProtocolError(\"unexpected reserved bits 0x\" + strconv.FormatInt(int64(rsv), 16))\n\t}\n\n\tswitch frameType {\n\tcase CloseMessage, PingMessage, PongMessage:\n\t\tif c.readRemaining > maxControlFramePayloadSize {\n\t\t\treturn noFrame, c.handleProtocolError(\"control frame length > 125\")\n\t\t}\n\t\tif !final {\n\t\t\treturn noFrame, c.handleProtocolError(\"control frame not final\")\n\t\t}\n\tcase TextMessage, BinaryMessage:\n\t\tif !c.readFinal {\n\t\t\treturn noFrame, c.handleProtocolError(\"message start before final message frame\")\n\t\t}\n\t\tc.readFinal = final\n\tcase continuationFrame:\n\t\tif c.readFinal {\n\t\t\treturn noFrame, c.handleProtocolError(\"continuation after final message frame\")\n\t\t}\n\t\tc.readFinal = final\n\tdefault:\n\t\treturn noFrame, c.handleProtocolError(\"unknown opcode \" + strconv.Itoa(frameType))\n\t}\n\n\t// 3. Read and parse frame length.\n\n\tswitch c.readRemaining {\n\tcase 126:\n\t\tp, err := c.read(2)\n\t\tif err != nil {\n\t\t\treturn noFrame, err\n\t\t}\n\t\tc.readRemaining = int64(binary.BigEndian.Uint16(p))\n\tcase 127:\n\t\tp, err := c.read(8)\n\t\tif err != nil {\n\t\t\treturn noFrame, err\n\t\t}\n\t\tc.readRemaining = int64(binary.BigEndian.Uint64(p))\n\t}\n\n\t// 4. Handle frame masking.\n\n\tif mask != c.isServer {\n\t\treturn noFrame, c.handleProtocolError(\"incorrect mask flag\")\n\t}\n\n\tif mask {\n\t\tc.readMaskPos = 0\n\t\tp, err := c.read(len(c.readMaskKey))\n\t\tif err != nil {\n\t\t\treturn noFrame, err\n\t\t}\n\t\tcopy(c.readMaskKey[:], p)\n\t}\n\n\t// 5. For text and binary messages, enforce read limit and return.\n\n\tif frameType == continuationFrame || frameType == TextMessage || frameType == BinaryMessage {\n\n\t\tc.readLength += c.readRemaining\n\t\tif c.readLimit > 0 && c.readLength > c.readLimit {\n\t\t\tc.WriteControl(CloseMessage, FormatCloseMessage(CloseMessageTooBig, \"\"), time.Now().Add(writeWait))\n\t\t\treturn noFrame, ErrReadLimit\n\t\t}\n\n\t\treturn frameType, nil\n\t}\n\n\t// 6. Read control frame payload.\n\n\tvar payload []byte\n\tif c.readRemaining > 0 {\n\t\tpayload, err = c.read(int(c.readRemaining))\n\t\tc.readRemaining = 0\n\t\tif err != nil {\n\t\t\treturn noFrame, err\n\t\t}\n\t\tif c.isServer {\n\t\t\tmaskBytes(c.readMaskKey, 0, payload)\n\t\t}\n\t}\n\n\t// 7. Process control frame payload.\n\n\tswitch frameType {\n\tcase PongMessage:\n\t\tif err := c.handlePong(string(payload)); err != nil {\n\t\t\treturn noFrame, err\n\t\t}\n\tcase PingMessage:\n\t\tif err := c.handlePing(string(payload)); err != nil {\n\t\t\treturn noFrame, err\n\t\t}\n\tcase CloseMessage:\n\t\tcloseCode := CloseNoStatusReceived\n\t\tcloseText := \"\"\n\t\tif len(payload) >= 2 {\n\t\t\tcloseCode = int(binary.BigEndian.Uint16(payload))\n\t\t\tif !isValidReceivedCloseCode(closeCode) {\n\t\t\t\treturn noFrame, c.handleProtocolError(\"invalid close code\")\n\t\t\t}\n\t\t\tcloseText = string(payload[2:])\n\t\t\tif !utf8.ValidString(closeText) {\n\t\t\t\treturn noFrame, c.handleProtocolError(\"invalid utf8 payload in close frame\")\n\t\t\t}\n\t\t}\n\t\tif err := c.handleClose(closeCode, closeText); err != nil {\n\t\t\treturn noFrame, err\n\t\t}\n\t\treturn noFrame, &CloseError{Code: closeCode, Text: closeText}\n\t}\n\n\treturn frameType, nil\n}\n\nfunc (c *Conn) handleProtocolError(message string) error {\n\tc.WriteControl(CloseMessage, FormatCloseMessage(CloseProtocolError, message), time.Now().Add(writeWait))\n\treturn errors.New(\"websocket: \" + message)\n}\n\n// NextReader returns the next data message received from the peer. The\n// returned messageType is either TextMessage or BinaryMessage.\n//\n// There can be at most one open reader on a connection. NextReader discards\n// the previous message if the application has not already consumed it.\n//\n// Applications must break out of the application's read loop when this method\n// returns a non-nil error value. Errors returned from this method are\n// permanent. Once this method returns a non-nil error, all subsequent calls to\n// this method return the same error.\nfunc (c *Conn) NextReader() (messageType int, r io.Reader, err error) {\n\t// Close previous reader, only relevant for decompression.\n\tif c.reader != nil {\n\t\tc.reader.Close()\n\t\tc.reader = nil\n\t}\n\n\tc.messageReader = nil\n\tc.readLength = 0\n\n\tfor c.readErr == nil {\n\t\tframeType, err := c.advanceFrame()\n\t\tif err != nil {\n\t\t\tc.readErr = hideTempErr(err)\n\t\t\tbreak\n\t\t}\n\t\tif frameType == TextMessage || frameType == BinaryMessage {\n\t\t\tc.messageReader = &messageReader{c}\n\t\t\tc.reader = c.messageReader\n\t\t\tif c.readDecompress {\n\t\t\t\tc.reader = c.newDecompressionReader(c.reader)\n\t\t\t}\n\t\t\treturn frameType, c.reader, nil\n\t\t}\n\t}\n\n\t// Applications that do handle the error returned from this method spin in\n\t// tight loop on connection failure. To help application developers detect\n\t// this error, panic on repeated reads to the failed connection.\n\tc.readErrCount++\n\tif c.readErrCount >= 1000 {\n\t\tpanic(\"repeated read on failed websocket connection\")\n\t}\n\n\treturn noFrame, nil, c.readErr\n}\n\ntype messageReader struct{ c *Conn }\n\nfunc (r *messageReader) Read(b []byte) (int, error) {\n\tc := r.c\n\tif c.messageReader != r {\n\t\treturn 0, io.EOF\n\t}\n\n\tfor c.readErr == nil {\n\n\t\tif c.readRemaining > 0 {\n\t\t\tif int64(len(b)) > c.readRemaining {\n\t\t\t\tb = b[:c.readRemaining]\n\t\t\t}\n\t\t\tn, err := c.br.Read(b)\n\t\t\tc.readErr = hideTempErr(err)\n\t\t\tif c.isServer {\n\t\t\t\tc.readMaskPos = maskBytes(c.readMaskKey, c.readMaskPos, b[:n])\n\t\t\t}\n\t\t\tc.readRemaining -= int64(n)\n\t\t\tif c.readRemaining > 0 && c.readErr == io.EOF {\n\t\t\t\tc.readErr = errUnexpectedEOF\n\t\t\t}\n\t\t\treturn n, c.readErr\n\t\t}\n\n\t\tif c.readFinal {\n\t\t\tc.messageReader = nil\n\t\t\treturn 0, io.EOF\n\t\t}\n\n\t\tframeType, err := c.advanceFrame()\n\t\tswitch {\n\t\tcase err != nil:\n\t\t\tc.readErr = hideTempErr(err)\n\t\tcase frameType == TextMessage || frameType == BinaryMessage:\n\t\t\tc.readErr = errors.New(\"websocket: internal error, unexpected text or binary in Reader\")\n\t\t}\n\t}\n\n\terr := c.readErr\n\tif err == io.EOF && c.messageReader == r {\n\t\terr = errUnexpectedEOF\n\t}\n\treturn 0, err\n}\n\nfunc (r *messageReader) Close() error {\n\treturn nil\n}\n\n// ReadMessage is a helper method for getting a reader using NextReader and\n// reading from that reader to a buffer.\nfunc (c *Conn) ReadMessage() (messageType int, p []byte, err error) {\n\tvar r io.Reader\n\tmessageType, r, err = c.NextReader()\n\tif err != nil {\n\t\treturn messageType, nil, err\n\t}\n\tp, err = ioutil.ReadAll(r)\n\treturn messageType, p, err\n}\n\n// SetReadDeadline sets the read deadline on the underlying network connection.\n// After a read has timed out, the websocket connection state is corrupt and\n// all future reads will return an error. A zero value for t means reads will\n// not time out.\nfunc (c *Conn) SetReadDeadline(t time.Time) error {\n\treturn c.conn.SetReadDeadline(t)\n}\n\n// SetReadLimit sets the maximum size for a message read from the peer. If a\n// message exceeds the limit, the connection sends a close message to the peer\n// and returns ErrReadLimit to the application.\nfunc (c *Conn) SetReadLimit(limit int64) {\n\tc.readLimit = limit\n}\n\n// CloseHandler returns the current close handler\nfunc (c *Conn) CloseHandler() func(code int, text string) error {\n\treturn c.handleClose\n}\n\n// SetCloseHandler sets the handler for close messages received from the peer.\n// The code argument to h is the received close code or CloseNoStatusReceived\n// if the close message is empty. The default close handler sends a close\n// message back to the peer.\n//\n// The handler function is called from the NextReader, ReadMessage and message\n// reader Read methods. The application must read the connection to process\n// close messages as described in the section on Control Messages above.\n//\n// The connection read methods return a CloseError when a close message is\n// received. Most applications should handle close messages as part of their\n// normal error handling. Applications should only set a close handler when the\n// application must perform some action before sending a close message back to\n// the peer.\nfunc (c *Conn) SetCloseHandler(h func(code int, text string) error) {\n\tif h == nil {\n\t\th = func(code int, text string) error {\n\t\t\tmessage := FormatCloseMessage(code, \"\")\n\t\t\tc.WriteControl(CloseMessage, message, time.Now().Add(writeWait))\n\t\t\treturn nil\n\t\t}\n\t}\n\tc.handleClose = h\n}\n\n// PingHandler returns the current ping handler\nfunc (c *Conn) PingHandler() func(appData string) error {\n\treturn c.handlePing\n}\n\n// SetPingHandler sets the handler for ping messages received from the peer.\n// The appData argument to h is the PING message application data. The default\n// ping handler sends a pong to the peer.\n//\n// The handler function is called from the NextReader, ReadMessage and message\n// reader Read methods. The application must read the connection to process\n// ping messages as described in the section on Control Messages above.\nfunc (c *Conn) SetPingHandler(h func(appData string) error) {\n\tif h == nil {\n\t\th = func(message string) error {\n\t\t\terr := c.WriteControl(PongMessage, []byte(message), time.Now().Add(writeWait))\n\t\t\tif err == ErrCloseSent {\n\t\t\t\treturn nil\n\t\t\t} else if e, ok := err.(net.Error); ok && e.Temporary() {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t}\n\tc.handlePing = h\n}\n\n// PongHandler returns the current pong handler\nfunc (c *Conn) PongHandler() func(appData string) error {\n\treturn c.handlePong\n}\n\n// SetPongHandler sets the handler for pong messages received from the peer.\n// The appData argument to h is the PONG message application data. The default\n// pong handler does nothing.\n//\n// The handler function is called from the NextReader, ReadMessage and message\n// reader Read methods. The application must read the connection to process\n// pong messages as described in the section on Control Messages above.\nfunc (c *Conn) SetPongHandler(h func(appData string) error) {\n\tif h == nil {\n\t\th = func(string) error { return nil }\n\t}\n\tc.handlePong = h\n}\n\n// UnderlyingConn returns the internal net.Conn. This can be used to further\n// modifications to connection specific flags.\nfunc (c *Conn) UnderlyingConn() net.Conn {\n\treturn c.conn\n}\n\n// EnableWriteCompression enables and disables write compression of\n// subsequent text and binary messages. This function is a noop if\n// compression was not negotiated with the peer.\nfunc (c *Conn) EnableWriteCompression(enable bool) {\n\tc.enableWriteCompression = enable\n}\n\n// SetCompressionLevel sets the flate compression level for subsequent text and\n// binary messages. This function is a noop if compression was not negotiated\n// with the peer. See the compress/flate package for a description of\n// compression levels.\nfunc (c *Conn) SetCompressionLevel(level int) error {\n\tif !isValidCompressionLevel(level) {\n\t\treturn errors.New(\"websocket: invalid compression level\")\n\t}\n\tc.compressionLevel = level\n\treturn nil\n}\n\n// FormatCloseMessage formats closeCode and text as a WebSocket close message.\n// An empty message is returned for code CloseNoStatusReceived.\nfunc FormatCloseMessage(closeCode int, text string) []byte {\n\tif closeCode == CloseNoStatusReceived {\n\t\t// Return empty message because it's illegal to send\n\t\t// CloseNoStatusReceived. Return non-nil value in case application\n\t\t// checks for nil.\n\t\treturn []byte{}\n\t}\n\tbuf := make([]byte, 2+len(text))\n\tbinary.BigEndian.PutUint16(buf, uint16(closeCode))\n\tcopy(buf[2:], text)\n\treturn buf\n}\n"
  },
  {
    "path": "websockets/v1/vendor/github.com/gorilla/websocket/conn_write.go",
    "content": "// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// +build go1.8\n\npackage websocket\n\nimport \"net\"\n\nfunc (c *Conn) writeBufs(bufs ...[]byte) error {\n\tb := net.Buffers(bufs)\n\t_, err := b.WriteTo(c.conn)\n\treturn err\n}\n"
  },
  {
    "path": "websockets/v1/vendor/github.com/gorilla/websocket/conn_write_legacy.go",
    "content": "// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// +build !go1.8\n\npackage websocket\n\nfunc (c *Conn) writeBufs(bufs ...[]byte) error {\n\tfor _, buf := range bufs {\n\t\tif len(buf) > 0 {\n\t\t\tif _, err := c.conn.Write(buf); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "websockets/v1/vendor/github.com/gorilla/websocket/doc.go",
    "content": "// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// Package websocket implements the WebSocket protocol defined in RFC 6455.\n//\n// Overview\n//\n// The Conn type represents a WebSocket connection. A server application calls\n// the Upgrader.Upgrade method from an HTTP request handler to get a *Conn:\n//\n//  var upgrader = websocket.Upgrader{\n//      ReadBufferSize:  1024,\n//      WriteBufferSize: 1024,\n//  }\n//\n//  func handler(w http.ResponseWriter, r *http.Request) {\n//      conn, err := upgrader.Upgrade(w, r, nil)\n//      if err != nil {\n//          log.Println(err)\n//          return\n//      }\n//      ... Use conn to send and receive messages.\n//  }\n//\n// Call the connection's WriteMessage and ReadMessage methods to send and\n// receive messages as a slice of bytes. This snippet of code shows how to echo\n// messages using these methods:\n//\n//  for {\n//      messageType, p, err := conn.ReadMessage()\n//      if err != nil {\n//          log.Println(err)\n//          return\n//      }\n//      if err := conn.WriteMessage(messageType, p); err != nil {\n//          log.Println(err)\n//          return\n//      }\n//  }\n//\n// In above snippet of code, p is a []byte and messageType is an int with value\n// websocket.BinaryMessage or websocket.TextMessage.\n//\n// An application can also send and receive messages using the io.WriteCloser\n// and io.Reader interfaces. To send a message, call the connection NextWriter\n// method to get an io.WriteCloser, write the message to the writer and close\n// the writer when done. To receive a message, call the connection NextReader\n// method to get an io.Reader and read until io.EOF is returned. This snippet\n// shows how to echo messages using the NextWriter and NextReader methods:\n//\n//  for {\n//      messageType, r, err := conn.NextReader()\n//      if err != nil {\n//          return\n//      }\n//      w, err := conn.NextWriter(messageType)\n//      if err != nil {\n//          return err\n//      }\n//      if _, err := io.Copy(w, r); err != nil {\n//          return err\n//      }\n//      if err := w.Close(); err != nil {\n//          return err\n//      }\n//  }\n//\n// Data Messages\n//\n// The WebSocket protocol distinguishes between text and binary data messages.\n// Text messages are interpreted as UTF-8 encoded text. The interpretation of\n// binary messages is left to the application.\n//\n// This package uses the TextMessage and BinaryMessage integer constants to\n// identify the two data message types. The ReadMessage and NextReader methods\n// return the type of the received message. The messageType argument to the\n// WriteMessage and NextWriter methods specifies the type of a sent message.\n//\n// It is the application's responsibility to ensure that text messages are\n// valid UTF-8 encoded text.\n//\n// Control Messages\n//\n// The WebSocket protocol defines three types of control messages: close, ping\n// and pong. Call the connection WriteControl, WriteMessage or NextWriter\n// methods to send a control message to the peer.\n//\n// Connections handle received close messages by calling the handler function\n// set with the SetCloseHandler method and by returning a *CloseError from the\n// NextReader, ReadMessage or the message Read method. The default close\n// handler sends a close message to the peer.\n//\n// Connections handle received ping messages by calling the handler function\n// set with the SetPingHandler method. The default ping handler sends a pong\n// message to the peer.\n//\n// Connections handle received pong messages by calling the handler function\n// set with the SetPongHandler method. The default pong handler does nothing.\n// If an application sends ping messages, then the application should set a\n// pong handler to receive the corresponding pong.\n//\n// The control message handler functions are called from the NextReader,\n// ReadMessage and message reader Read methods. The default close and ping\n// handlers can block these methods for a short time when the handler writes to\n// the connection.\n//\n// The application must read the connection to process close, ping and pong\n// messages sent from the peer. If the application is not otherwise interested\n// in messages from the peer, then the application should start a goroutine to\n// read and discard messages from the peer. A simple example is:\n//\n//  func readLoop(c *websocket.Conn) {\n//      for {\n//          if _, _, err := c.NextReader(); err != nil {\n//              c.Close()\n//              break\n//          }\n//      }\n//  }\n//\n// Concurrency\n//\n// Connections support one concurrent reader and one concurrent writer.\n//\n// Applications are responsible for ensuring that no more than one goroutine\n// calls the write methods (NextWriter, SetWriteDeadline, WriteMessage,\n// WriteJSON, EnableWriteCompression, SetCompressionLevel) concurrently and\n// that no more than one goroutine calls the read methods (NextReader,\n// SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler, SetPingHandler)\n// concurrently.\n//\n// The Close and WriteControl methods can be called concurrently with all other\n// methods.\n//\n// Origin Considerations\n//\n// Web browsers allow Javascript applications to open a WebSocket connection to\n// any host. It's up to the server to enforce an origin policy using the Origin\n// request header sent by the browser.\n//\n// The Upgrader calls the function specified in the CheckOrigin field to check\n// the origin. If the CheckOrigin function returns false, then the Upgrade\n// method fails the WebSocket handshake with HTTP status 403.\n//\n// If the CheckOrigin field is nil, then the Upgrader uses a safe default: fail\n// the handshake if the Origin request header is present and the Origin host is\n// not equal to the Host request header.\n//\n// The deprecated package-level Upgrade function does not perform origin\n// checking. The application is responsible for checking the Origin header\n// before calling the Upgrade function.\n//\n// Compression EXPERIMENTAL\n//\n// Per message compression extensions (RFC 7692) are experimentally supported\n// by this package in a limited capacity. Setting the EnableCompression option\n// to true in Dialer or Upgrader will attempt to negotiate per message deflate\n// support.\n//\n//  var upgrader = websocket.Upgrader{\n//      EnableCompression: true,\n//  }\n//\n// If compression was successfully negotiated with the connection's peer, any\n// message received in compressed form will be automatically decompressed.\n// All Read methods will return uncompressed bytes.\n//\n// Per message compression of messages written to a connection can be enabled\n// or disabled by calling the corresponding Conn method:\n//\n//  conn.EnableWriteCompression(false)\n//\n// Currently this package does not support compression with \"context takeover\".\n// This means that messages must be compressed and decompressed in isolation,\n// without retaining sliding window or dictionary state across messages. For\n// more details refer to RFC 7692.\n//\n// Use of compression is experimental and may result in decreased performance.\npackage websocket\n"
  },
  {
    "path": "websockets/v1/vendor/github.com/gorilla/websocket/json.go",
    "content": "// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage websocket\n\nimport (\n\t\"encoding/json\"\n\t\"io\"\n)\n\n// WriteJSON writes the JSON encoding of v as a message.\n//\n// Deprecated: Use c.WriteJSON instead.\nfunc WriteJSON(c *Conn, v interface{}) error {\n\treturn c.WriteJSON(v)\n}\n\n// WriteJSON writes the JSON encoding of v as a message.\n//\n// See the documentation for encoding/json Marshal for details about the\n// conversion of Go values to JSON.\nfunc (c *Conn) WriteJSON(v interface{}) error {\n\tw, err := c.NextWriter(TextMessage)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr1 := json.NewEncoder(w).Encode(v)\n\terr2 := w.Close()\n\tif err1 != nil {\n\t\treturn err1\n\t}\n\treturn err2\n}\n\n// ReadJSON reads the next JSON-encoded message from the connection and stores\n// it in the value pointed to by v.\n//\n// Deprecated: Use c.ReadJSON instead.\nfunc ReadJSON(c *Conn, v interface{}) error {\n\treturn c.ReadJSON(v)\n}\n\n// ReadJSON reads the next JSON-encoded message from the connection and stores\n// it in the value pointed to by v.\n//\n// See the documentation for the encoding/json Unmarshal function for details\n// about the conversion of JSON to a Go value.\nfunc (c *Conn) ReadJSON(v interface{}) error {\n\t_, r, err := c.NextReader()\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = json.NewDecoder(r).Decode(v)\n\tif err == io.EOF {\n\t\t// One value is expected in the message.\n\t\terr = io.ErrUnexpectedEOF\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "websockets/v1/vendor/github.com/gorilla/websocket/mask.go",
    "content": "// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved.  Use of\n// this source code is governed by a BSD-style license that can be found in the\n// LICENSE file.\n\n// +build !appengine\n\npackage websocket\n\nimport \"unsafe\"\n\nconst wordSize = int(unsafe.Sizeof(uintptr(0)))\n\nfunc maskBytes(key [4]byte, pos int, b []byte) int {\n\t// Mask one byte at a time for small buffers.\n\tif len(b) < 2*wordSize {\n\t\tfor i := range b {\n\t\t\tb[i] ^= key[pos&3]\n\t\t\tpos++\n\t\t}\n\t\treturn pos & 3\n\t}\n\n\t// Mask one byte at a time to word boundary.\n\tif n := int(uintptr(unsafe.Pointer(&b[0]))) % wordSize; n != 0 {\n\t\tn = wordSize - n\n\t\tfor i := range b[:n] {\n\t\t\tb[i] ^= key[pos&3]\n\t\t\tpos++\n\t\t}\n\t\tb = b[n:]\n\t}\n\n\t// Create aligned word size key.\n\tvar k [wordSize]byte\n\tfor i := range k {\n\t\tk[i] = key[(pos+i)&3]\n\t}\n\tkw := *(*uintptr)(unsafe.Pointer(&k))\n\n\t// Mask one word at a time.\n\tn := (len(b) / wordSize) * wordSize\n\tfor i := 0; i < n; i += wordSize {\n\t\t*(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(i))) ^= kw\n\t}\n\n\t// Mask one byte at a time for remaining bytes.\n\tb = b[n:]\n\tfor i := range b {\n\t\tb[i] ^= key[pos&3]\n\t\tpos++\n\t}\n\n\treturn pos & 3\n}\n"
  },
  {
    "path": "websockets/v1/vendor/github.com/gorilla/websocket/mask_safe.go",
    "content": "// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved.  Use of\n// this source code is governed by a BSD-style license that can be found in the\n// LICENSE file.\n\n// +build appengine\n\npackage websocket\n\nfunc maskBytes(key [4]byte, pos int, b []byte) int {\n\tfor i := range b {\n\t\tb[i] ^= key[pos&3]\n\t\tpos++\n\t}\n\treturn pos & 3\n}\n"
  },
  {
    "path": "websockets/v1/vendor/github.com/gorilla/websocket/prepared.go",
    "content": "// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage websocket\n\nimport (\n\t\"bytes\"\n\t\"net\"\n\t\"sync\"\n\t\"time\"\n)\n\n// PreparedMessage caches on the wire representations of a message payload.\n// Use PreparedMessage to efficiently send a message payload to multiple\n// connections. PreparedMessage is especially useful when compression is used\n// because the CPU and memory expensive compression operation can be executed\n// once for a given set of compression options.\ntype PreparedMessage struct {\n\tmessageType int\n\tdata        []byte\n\tmu          sync.Mutex\n\tframes      map[prepareKey]*preparedFrame\n}\n\n// prepareKey defines a unique set of options to cache prepared frames in PreparedMessage.\ntype prepareKey struct {\n\tisServer         bool\n\tcompress         bool\n\tcompressionLevel int\n}\n\n// preparedFrame contains data in wire representation.\ntype preparedFrame struct {\n\tonce sync.Once\n\tdata []byte\n}\n\n// NewPreparedMessage returns an initialized PreparedMessage. You can then send\n// it to connection using WritePreparedMessage method. Valid wire\n// representation will be calculated lazily only once for a set of current\n// connection options.\nfunc NewPreparedMessage(messageType int, data []byte) (*PreparedMessage, error) {\n\tpm := &PreparedMessage{\n\t\tmessageType: messageType,\n\t\tframes:      make(map[prepareKey]*preparedFrame),\n\t\tdata:        data,\n\t}\n\n\t// Prepare a plain server frame.\n\t_, frameData, err := pm.frame(prepareKey{isServer: true, compress: false})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// To protect against caller modifying the data argument, remember the data\n\t// copied to the plain server frame.\n\tpm.data = frameData[len(frameData)-len(data):]\n\treturn pm, nil\n}\n\nfunc (pm *PreparedMessage) frame(key prepareKey) (int, []byte, error) {\n\tpm.mu.Lock()\n\tframe, ok := pm.frames[key]\n\tif !ok {\n\t\tframe = &preparedFrame{}\n\t\tpm.frames[key] = frame\n\t}\n\tpm.mu.Unlock()\n\n\tvar err error\n\tframe.once.Do(func() {\n\t\t// Prepare a frame using a 'fake' connection.\n\t\t// TODO: Refactor code in conn.go to allow more direct construction of\n\t\t// the frame.\n\t\tmu := make(chan bool, 1)\n\t\tmu <- true\n\t\tvar nc prepareConn\n\t\tc := &Conn{\n\t\t\tconn:                   &nc,\n\t\t\tmu:                     mu,\n\t\t\tisServer:               key.isServer,\n\t\t\tcompressionLevel:       key.compressionLevel,\n\t\t\tenableWriteCompression: true,\n\t\t\twriteBuf:               make([]byte, defaultWriteBufferSize+maxFrameHeaderSize),\n\t\t}\n\t\tif key.compress {\n\t\t\tc.newCompressionWriter = compressNoContextTakeover\n\t\t}\n\t\terr = c.WriteMessage(pm.messageType, pm.data)\n\t\tframe.data = nc.buf.Bytes()\n\t})\n\treturn pm.messageType, frame.data, err\n}\n\ntype prepareConn struct {\n\tbuf bytes.Buffer\n\tnet.Conn\n}\n\nfunc (pc *prepareConn) Write(p []byte) (int, error)        { return pc.buf.Write(p) }\nfunc (pc *prepareConn) SetWriteDeadline(t time.Time) error { return nil }\n"
  },
  {
    "path": "websockets/v1/vendor/github.com/gorilla/websocket/proxy.go",
    "content": "// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage websocket\n\nimport (\n\t\"bufio\"\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n)\n\ntype netDialerFunc func(network, addr string) (net.Conn, error)\n\nfunc (fn netDialerFunc) Dial(network, addr string) (net.Conn, error) {\n\treturn fn(network, addr)\n}\n\nfunc init() {\n\tproxy_RegisterDialerType(\"http\", func(proxyURL *url.URL, forwardDialer proxy_Dialer) (proxy_Dialer, error) {\n\t\treturn &httpProxyDialer{proxyURL: proxyURL, fowardDial: forwardDialer.Dial}, nil\n\t})\n}\n\ntype httpProxyDialer struct {\n\tproxyURL   *url.URL\n\tfowardDial func(network, addr string) (net.Conn, error)\n}\n\nfunc (hpd *httpProxyDialer) Dial(network string, addr string) (net.Conn, error) {\n\thostPort, _ := hostPortNoPort(hpd.proxyURL)\n\tconn, err := hpd.fowardDial(network, hostPort)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tconnectHeader := make(http.Header)\n\tif user := hpd.proxyURL.User; user != nil {\n\t\tproxyUser := user.Username()\n\t\tif proxyPassword, passwordSet := user.Password(); passwordSet {\n\t\t\tcredential := base64.StdEncoding.EncodeToString([]byte(proxyUser + \":\" + proxyPassword))\n\t\t\tconnectHeader.Set(\"Proxy-Authorization\", \"Basic \"+credential)\n\t\t}\n\t}\n\n\tconnectReq := &http.Request{\n\t\tMethod: \"CONNECT\",\n\t\tURL:    &url.URL{Opaque: addr},\n\t\tHost:   addr,\n\t\tHeader: connectHeader,\n\t}\n\n\tif err := connectReq.Write(conn); err != nil {\n\t\tconn.Close()\n\t\treturn nil, err\n\t}\n\n\t// Read response. It's OK to use and discard buffered reader here because\n\t// the remote server does not speak until spoken to.\n\tbr := bufio.NewReader(conn)\n\tresp, err := http.ReadResponse(br, connectReq)\n\tif err != nil {\n\t\tconn.Close()\n\t\treturn nil, err\n\t}\n\n\tif resp.StatusCode != 200 {\n\t\tconn.Close()\n\t\tf := strings.SplitN(resp.Status, \" \", 2)\n\t\treturn nil, errors.New(f[1])\n\t}\n\treturn conn, nil\n}\n"
  },
  {
    "path": "websockets/v1/vendor/github.com/gorilla/websocket/server.go",
    "content": "// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage websocket\n\nimport (\n\t\"bufio\"\n\t\"errors\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\t\"time\"\n)\n\n// HandshakeError describes an error with the handshake from the peer.\ntype HandshakeError struct {\n\tmessage string\n}\n\nfunc (e HandshakeError) Error() string { return e.message }\n\n// Upgrader specifies parameters for upgrading an HTTP connection to a\n// WebSocket connection.\ntype Upgrader struct {\n\t// HandshakeTimeout specifies the duration for the handshake to complete.\n\tHandshakeTimeout time.Duration\n\n\t// ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer\n\t// size is zero, then buffers allocated by the HTTP server are used. The\n\t// I/O buffer sizes do not limit the size of the messages that can be sent\n\t// or received.\n\tReadBufferSize, WriteBufferSize int\n\n\t// WriteBufferPool is a pool of buffers for write operations. If the value\n\t// is not set, then write buffers are allocated to the connection for the\n\t// lifetime of the connection.\n\t//\n\t// A pool is most useful when the application has a modest volume of writes\n\t// across a large number of connections.\n\t//\n\t// Applications should use a single pool for each unique value of\n\t// WriteBufferSize.\n\tWriteBufferPool BufferPool\n\n\t// Subprotocols specifies the server's supported protocols in order of\n\t// preference. If this field is not nil, then the Upgrade method negotiates a\n\t// subprotocol by selecting the first match in this list with a protocol\n\t// requested by the client. If there's no match, then no protocol is\n\t// negotiated (the Sec-Websocket-Protocol header is not included in the\n\t// handshake response).\n\tSubprotocols []string\n\n\t// Error specifies the function for generating HTTP error responses. If Error\n\t// is nil, then http.Error is used to generate the HTTP response.\n\tError func(w http.ResponseWriter, r *http.Request, status int, reason error)\n\n\t// CheckOrigin returns true if the request Origin header is acceptable. If\n\t// CheckOrigin is nil, then a safe default is used: return false if the\n\t// Origin request header is present and the origin host is not equal to\n\t// request Host header.\n\t//\n\t// A CheckOrigin function should carefully validate the request origin to\n\t// prevent cross-site request forgery.\n\tCheckOrigin func(r *http.Request) bool\n\n\t// EnableCompression specify if the server should attempt to negotiate per\n\t// message compression (RFC 7692). Setting this value to true does not\n\t// guarantee that compression will be supported. Currently only \"no context\n\t// takeover\" modes are supported.\n\tEnableCompression bool\n}\n\nfunc (u *Upgrader) returnError(w http.ResponseWriter, r *http.Request, status int, reason string) (*Conn, error) {\n\terr := HandshakeError{reason}\n\tif u.Error != nil {\n\t\tu.Error(w, r, status, err)\n\t} else {\n\t\tw.Header().Set(\"Sec-Websocket-Version\", \"13\")\n\t\thttp.Error(w, http.StatusText(status), status)\n\t}\n\treturn nil, err\n}\n\n// checkSameOrigin returns true if the origin is not set or is equal to the request host.\nfunc checkSameOrigin(r *http.Request) bool {\n\torigin := r.Header[\"Origin\"]\n\tif len(origin) == 0 {\n\t\treturn true\n\t}\n\tu, err := url.Parse(origin[0])\n\tif err != nil {\n\t\treturn false\n\t}\n\treturn equalASCIIFold(u.Host, r.Host)\n}\n\nfunc (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header) string {\n\tif u.Subprotocols != nil {\n\t\tclientProtocols := Subprotocols(r)\n\t\tfor _, serverProtocol := range u.Subprotocols {\n\t\t\tfor _, clientProtocol := range clientProtocols {\n\t\t\t\tif clientProtocol == serverProtocol {\n\t\t\t\t\treturn clientProtocol\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else if responseHeader != nil {\n\t\treturn responseHeader.Get(\"Sec-Websocket-Protocol\")\n\t}\n\treturn \"\"\n}\n\n// Upgrade upgrades the HTTP server connection to the WebSocket protocol.\n//\n// The responseHeader is included in the response to the client's upgrade\n// request. Use the responseHeader to specify cookies (Set-Cookie) and the\n// application negotiated subprotocol (Sec-WebSocket-Protocol).\n//\n// If the upgrade fails, then Upgrade replies to the client with an HTTP error\n// response.\nfunc (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) {\n\tconst badHandshake = \"websocket: the client is not using the websocket protocol: \"\n\n\tif !tokenListContainsValue(r.Header, \"Connection\", \"upgrade\") {\n\t\treturn u.returnError(w, r, http.StatusBadRequest, badHandshake+\"'upgrade' token not found in 'Connection' header\")\n\t}\n\n\tif !tokenListContainsValue(r.Header, \"Upgrade\", \"websocket\") {\n\t\treturn u.returnError(w, r, http.StatusBadRequest, badHandshake+\"'websocket' token not found in 'Upgrade' header\")\n\t}\n\n\tif r.Method != \"GET\" {\n\t\treturn u.returnError(w, r, http.StatusMethodNotAllowed, badHandshake+\"request method is not GET\")\n\t}\n\n\tif !tokenListContainsValue(r.Header, \"Sec-Websocket-Version\", \"13\") {\n\t\treturn u.returnError(w, r, http.StatusBadRequest, \"websocket: unsupported version: 13 not found in 'Sec-Websocket-Version' header\")\n\t}\n\n\tif _, ok := responseHeader[\"Sec-Websocket-Extensions\"]; ok {\n\t\treturn u.returnError(w, r, http.StatusInternalServerError, \"websocket: application specific 'Sec-WebSocket-Extensions' headers are unsupported\")\n\t}\n\n\tcheckOrigin := u.CheckOrigin\n\tif checkOrigin == nil {\n\t\tcheckOrigin = checkSameOrigin\n\t}\n\tif !checkOrigin(r) {\n\t\treturn u.returnError(w, r, http.StatusForbidden, \"websocket: request origin not allowed by Upgrader.CheckOrigin\")\n\t}\n\n\tchallengeKey := r.Header.Get(\"Sec-Websocket-Key\")\n\tif challengeKey == \"\" {\n\t\treturn u.returnError(w, r, http.StatusBadRequest, \"websocket: not a websocket handshake: `Sec-WebSocket-Key' header is missing or blank\")\n\t}\n\n\tsubprotocol := u.selectSubprotocol(r, responseHeader)\n\n\t// Negotiate PMCE\n\tvar compress bool\n\tif u.EnableCompression {\n\t\tfor _, ext := range parseExtensions(r.Header) {\n\t\t\tif ext[\"\"] != \"permessage-deflate\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tcompress = true\n\t\t\tbreak\n\t\t}\n\t}\n\n\th, ok := w.(http.Hijacker)\n\tif !ok {\n\t\treturn u.returnError(w, r, http.StatusInternalServerError, \"websocket: response does not implement http.Hijacker\")\n\t}\n\tvar brw *bufio.ReadWriter\n\tnetConn, brw, err := h.Hijack()\n\tif err != nil {\n\t\treturn u.returnError(w, r, http.StatusInternalServerError, err.Error())\n\t}\n\n\tif brw.Reader.Buffered() > 0 {\n\t\tnetConn.Close()\n\t\treturn nil, errors.New(\"websocket: client sent data before handshake is complete\")\n\t}\n\n\tvar br *bufio.Reader\n\tif u.ReadBufferSize == 0 && bufioReaderSize(netConn, brw.Reader) > 256 {\n\t\t// Reuse hijacked buffered reader as connection reader.\n\t\tbr = brw.Reader\n\t}\n\n\tbuf := bufioWriterBuffer(netConn, brw.Writer)\n\n\tvar writeBuf []byte\n\tif u.WriteBufferPool == nil && u.WriteBufferSize == 0 && len(buf) >= maxFrameHeaderSize+256 {\n\t\t// Reuse hijacked write buffer as connection buffer.\n\t\twriteBuf = buf\n\t}\n\n\tc := newConn(netConn, true, u.ReadBufferSize, u.WriteBufferSize, u.WriteBufferPool, br, writeBuf)\n\tc.subprotocol = subprotocol\n\n\tif compress {\n\t\tc.newCompressionWriter = compressNoContextTakeover\n\t\tc.newDecompressionReader = decompressNoContextTakeover\n\t}\n\n\t// Use larger of hijacked buffer and connection write buffer for header.\n\tp := buf\n\tif len(c.writeBuf) > len(p) {\n\t\tp = c.writeBuf\n\t}\n\tp = p[:0]\n\n\tp = append(p, \"HTTP/1.1 101 Switching Protocols\\r\\nUpgrade: websocket\\r\\nConnection: Upgrade\\r\\nSec-WebSocket-Accept: \"...)\n\tp = append(p, computeAcceptKey(challengeKey)...)\n\tp = append(p, \"\\r\\n\"...)\n\tif c.subprotocol != \"\" {\n\t\tp = append(p, \"Sec-WebSocket-Protocol: \"...)\n\t\tp = append(p, c.subprotocol...)\n\t\tp = append(p, \"\\r\\n\"...)\n\t}\n\tif compress {\n\t\tp = append(p, \"Sec-WebSocket-Extensions: permessage-deflate; server_no_context_takeover; client_no_context_takeover\\r\\n\"...)\n\t}\n\tfor k, vs := range responseHeader {\n\t\tif k == \"Sec-Websocket-Protocol\" {\n\t\t\tcontinue\n\t\t}\n\t\tfor _, v := range vs {\n\t\t\tp = append(p, k...)\n\t\t\tp = append(p, \": \"...)\n\t\t\tfor i := 0; i < len(v); i++ {\n\t\t\t\tb := v[i]\n\t\t\t\tif b <= 31 {\n\t\t\t\t\t// prevent response splitting.\n\t\t\t\t\tb = ' '\n\t\t\t\t}\n\t\t\t\tp = append(p, b)\n\t\t\t}\n\t\t\tp = append(p, \"\\r\\n\"...)\n\t\t}\n\t}\n\tp = append(p, \"\\r\\n\"...)\n\n\t// Clear deadlines set by HTTP server.\n\tnetConn.SetDeadline(time.Time{})\n\n\tif u.HandshakeTimeout > 0 {\n\t\tnetConn.SetWriteDeadline(time.Now().Add(u.HandshakeTimeout))\n\t}\n\tif _, err = netConn.Write(p); err != nil {\n\t\tnetConn.Close()\n\t\treturn nil, err\n\t}\n\tif u.HandshakeTimeout > 0 {\n\t\tnetConn.SetWriteDeadline(time.Time{})\n\t}\n\n\treturn c, nil\n}\n\n// Upgrade upgrades the HTTP server connection to the WebSocket protocol.\n//\n// Deprecated: Use websocket.Upgrader instead.\n//\n// Upgrade does not perform origin checking. The application is responsible for\n// checking the Origin header before calling Upgrade. An example implementation\n// of the same origin policy check is:\n//\n//\tif req.Header.Get(\"Origin\") != \"http://\"+req.Host {\n//\t\thttp.Error(w, \"Origin not allowed\", http.StatusForbidden)\n//\t\treturn\n//\t}\n//\n// If the endpoint supports subprotocols, then the application is responsible\n// for negotiating the protocol used on the connection. Use the Subprotocols()\n// function to get the subprotocols requested by the client. Use the\n// Sec-Websocket-Protocol response header to specify the subprotocol selected\n// by the application.\n//\n// The responseHeader is included in the response to the client's upgrade\n// request. Use the responseHeader to specify cookies (Set-Cookie) and the\n// negotiated subprotocol (Sec-Websocket-Protocol).\n//\n// The connection buffers IO to the underlying network connection. The\n// readBufSize and writeBufSize parameters specify the size of the buffers to\n// use. Messages can be larger than the buffers.\n//\n// If the request is not a valid WebSocket handshake, then Upgrade returns an\n// error of type HandshakeError. Applications should handle this error by\n// replying to the client with an HTTP error response.\nfunc Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header, readBufSize, writeBufSize int) (*Conn, error) {\n\tu := Upgrader{ReadBufferSize: readBufSize, WriteBufferSize: writeBufSize}\n\tu.Error = func(w http.ResponseWriter, r *http.Request, status int, reason error) {\n\t\t// don't return errors to maintain backwards compatibility\n\t}\n\tu.CheckOrigin = func(r *http.Request) bool {\n\t\t// allow all connections by default\n\t\treturn true\n\t}\n\treturn u.Upgrade(w, r, responseHeader)\n}\n\n// Subprotocols returns the subprotocols requested by the client in the\n// Sec-Websocket-Protocol header.\nfunc Subprotocols(r *http.Request) []string {\n\th := strings.TrimSpace(r.Header.Get(\"Sec-Websocket-Protocol\"))\n\tif h == \"\" {\n\t\treturn nil\n\t}\n\tprotocols := strings.Split(h, \",\")\n\tfor i := range protocols {\n\t\tprotocols[i] = strings.TrimSpace(protocols[i])\n\t}\n\treturn protocols\n}\n\n// IsWebSocketUpgrade returns true if the client requested upgrade to the\n// WebSocket protocol.\nfunc IsWebSocketUpgrade(r *http.Request) bool {\n\treturn tokenListContainsValue(r.Header, \"Connection\", \"upgrade\") &&\n\t\ttokenListContainsValue(r.Header, \"Upgrade\", \"websocket\")\n}\n\n// bufioReaderSize size returns the size of a bufio.Reader.\nfunc bufioReaderSize(originalReader io.Reader, br *bufio.Reader) int {\n\t// This code assumes that peek on a reset reader returns\n\t// bufio.Reader.buf[:0].\n\t// TODO: Use bufio.Reader.Size() after Go 1.10\n\tbr.Reset(originalReader)\n\tif p, err := br.Peek(0); err == nil {\n\t\treturn cap(p)\n\t}\n\treturn 0\n}\n\n// writeHook is an io.Writer that records the last slice passed to it vio\n// io.Writer.Write.\ntype writeHook struct {\n\tp []byte\n}\n\nfunc (wh *writeHook) Write(p []byte) (int, error) {\n\twh.p = p\n\treturn len(p), nil\n}\n\n// bufioWriterBuffer grabs the buffer from a bufio.Writer.\nfunc bufioWriterBuffer(originalWriter io.Writer, bw *bufio.Writer) []byte {\n\t// This code assumes that bufio.Writer.buf[:1] is passed to the\n\t// bufio.Writer's underlying writer.\n\tvar wh writeHook\n\tbw.Reset(&wh)\n\tbw.WriteByte(0)\n\tbw.Flush()\n\n\tbw.Reset(originalWriter)\n\n\treturn wh.p[:cap(wh.p)]\n}\n"
  },
  {
    "path": "websockets/v1/vendor/github.com/gorilla/websocket/trace.go",
    "content": "// +build go1.8\n\npackage websocket\n\nimport (\n\t\"crypto/tls\"\n\t\"net/http/httptrace\"\n)\n\nfunc doHandshakeWithTrace(trace *httptrace.ClientTrace, tlsConn *tls.Conn, cfg *tls.Config) error {\n\tif trace.TLSHandshakeStart != nil {\n\t\ttrace.TLSHandshakeStart()\n\t}\n\terr := doHandshake(tlsConn, cfg)\n\tif trace.TLSHandshakeDone != nil {\n\t\ttrace.TLSHandshakeDone(tlsConn.ConnectionState(), err)\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "websockets/v1/vendor/github.com/gorilla/websocket/trace_17.go",
    "content": "// +build !go1.8\n\npackage websocket\n\nimport (\n\t\"crypto/tls\"\n\t\"net/http/httptrace\"\n)\n\nfunc doHandshakeWithTrace(trace *httptrace.ClientTrace, tlsConn *tls.Conn, cfg *tls.Config) error {\n\treturn doHandshake(tlsConn, cfg)\n}\n"
  },
  {
    "path": "websockets/v1/vendor/github.com/gorilla/websocket/util.go",
    "content": "// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage websocket\n\nimport (\n\t\"crypto/rand\"\n\t\"crypto/sha1\"\n\t\"encoding/base64\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n\t\"unicode/utf8\"\n)\n\nvar keyGUID = []byte(\"258EAFA5-E914-47DA-95CA-C5AB0DC85B11\")\n\nfunc computeAcceptKey(challengeKey string) string {\n\th := sha1.New()\n\th.Write([]byte(challengeKey))\n\th.Write(keyGUID)\n\treturn base64.StdEncoding.EncodeToString(h.Sum(nil))\n}\n\nfunc generateChallengeKey() (string, error) {\n\tp := make([]byte, 16)\n\tif _, err := io.ReadFull(rand.Reader, p); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn base64.StdEncoding.EncodeToString(p), nil\n}\n\n// Octet types from RFC 2616.\nvar octetTypes [256]byte\n\nconst (\n\tisTokenOctet = 1 << iota\n\tisSpaceOctet\n)\n\nfunc init() {\n\t// From RFC 2616\n\t//\n\t// OCTET      = <any 8-bit sequence of data>\n\t// CHAR       = <any US-ASCII character (octets 0 - 127)>\n\t// CTL        = <any US-ASCII control character (octets 0 - 31) and DEL (127)>\n\t// CR         = <US-ASCII CR, carriage return (13)>\n\t// LF         = <US-ASCII LF, linefeed (10)>\n\t// SP         = <US-ASCII SP, space (32)>\n\t// HT         = <US-ASCII HT, horizontal-tab (9)>\n\t// <\">        = <US-ASCII double-quote mark (34)>\n\t// CRLF       = CR LF\n\t// LWS        = [CRLF] 1*( SP | HT )\n\t// TEXT       = <any OCTET except CTLs, but including LWS>\n\t// separators = \"(\" | \")\" | \"<\" | \">\" | \"@\" | \",\" | \";\" | \":\" | \"\\\" | <\">\n\t//              | \"/\" | \"[\" | \"]\" | \"?\" | \"=\" | \"{\" | \"}\" | SP | HT\n\t// token      = 1*<any CHAR except CTLs or separators>\n\t// qdtext     = <any TEXT except <\">>\n\n\tfor c := 0; c < 256; c++ {\n\t\tvar t byte\n\t\tisCtl := c <= 31 || c == 127\n\t\tisChar := 0 <= c && c <= 127\n\t\tisSeparator := strings.IndexRune(\" \\t\\\"(),/:;<=>?@[]\\\\{}\", rune(c)) >= 0\n\t\tif strings.IndexRune(\" \\t\\r\\n\", rune(c)) >= 0 {\n\t\t\tt |= isSpaceOctet\n\t\t}\n\t\tif isChar && !isCtl && !isSeparator {\n\t\t\tt |= isTokenOctet\n\t\t}\n\t\toctetTypes[c] = t\n\t}\n}\n\nfunc skipSpace(s string) (rest string) {\n\ti := 0\n\tfor ; i < len(s); i++ {\n\t\tif octetTypes[s[i]]&isSpaceOctet == 0 {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn s[i:]\n}\n\nfunc nextToken(s string) (token, rest string) {\n\ti := 0\n\tfor ; i < len(s); i++ {\n\t\tif octetTypes[s[i]]&isTokenOctet == 0 {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn s[:i], s[i:]\n}\n\nfunc nextTokenOrQuoted(s string) (value string, rest string) {\n\tif !strings.HasPrefix(s, \"\\\"\") {\n\t\treturn nextToken(s)\n\t}\n\ts = s[1:]\n\tfor i := 0; i < len(s); i++ {\n\t\tswitch s[i] {\n\t\tcase '\"':\n\t\t\treturn s[:i], s[i+1:]\n\t\tcase '\\\\':\n\t\t\tp := make([]byte, len(s)-1)\n\t\t\tj := copy(p, s[:i])\n\t\t\tescape := true\n\t\t\tfor i = i + 1; i < len(s); i++ {\n\t\t\t\tb := s[i]\n\t\t\t\tswitch {\n\t\t\t\tcase escape:\n\t\t\t\t\tescape = false\n\t\t\t\t\tp[j] = b\n\t\t\t\t\tj++\n\t\t\t\tcase b == '\\\\':\n\t\t\t\t\tescape = true\n\t\t\t\tcase b == '\"':\n\t\t\t\t\treturn string(p[:j]), s[i+1:]\n\t\t\t\tdefault:\n\t\t\t\t\tp[j] = b\n\t\t\t\t\tj++\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn \"\", \"\"\n\t\t}\n\t}\n\treturn \"\", \"\"\n}\n\n// equalASCIIFold returns true if s is equal to t with ASCII case folding.\nfunc equalASCIIFold(s, t string) bool {\n\tfor s != \"\" && t != \"\" {\n\t\tsr, size := utf8.DecodeRuneInString(s)\n\t\ts = s[size:]\n\t\ttr, size := utf8.DecodeRuneInString(t)\n\t\tt = t[size:]\n\t\tif sr == tr {\n\t\t\tcontinue\n\t\t}\n\t\tif 'A' <= sr && sr <= 'Z' {\n\t\t\tsr = sr + 'a' - 'A'\n\t\t}\n\t\tif 'A' <= tr && tr <= 'Z' {\n\t\t\ttr = tr + 'a' - 'A'\n\t\t}\n\t\tif sr != tr {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn s == t\n}\n\n// tokenListContainsValue returns true if the 1#token header with the given\n// name contains a token equal to value with ASCII case folding.\nfunc tokenListContainsValue(header http.Header, name string, value string) bool {\nheaders:\n\tfor _, s := range header[name] {\n\t\tfor {\n\t\t\tvar t string\n\t\t\tt, s = nextToken(skipSpace(s))\n\t\t\tif t == \"\" {\n\t\t\t\tcontinue headers\n\t\t\t}\n\t\t\ts = skipSpace(s)\n\t\t\tif s != \"\" && s[0] != ',' {\n\t\t\t\tcontinue headers\n\t\t\t}\n\t\t\tif equalASCIIFold(t, value) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif s == \"\" {\n\t\t\t\tcontinue headers\n\t\t\t}\n\t\t\ts = s[1:]\n\t\t}\n\t}\n\treturn false\n}\n\n// parseExtensions parses WebSocket extensions from a header.\nfunc parseExtensions(header http.Header) []map[string]string {\n\t// From RFC 6455:\n\t//\n\t//  Sec-WebSocket-Extensions = extension-list\n\t//  extension-list = 1#extension\n\t//  extension = extension-token *( \";\" extension-param )\n\t//  extension-token = registered-token\n\t//  registered-token = token\n\t//  extension-param = token [ \"=\" (token | quoted-string) ]\n\t//     ;When using the quoted-string syntax variant, the value\n\t//     ;after quoted-string unescaping MUST conform to the\n\t//     ;'token' ABNF.\n\n\tvar result []map[string]string\nheaders:\n\tfor _, s := range header[\"Sec-Websocket-Extensions\"] {\n\t\tfor {\n\t\t\tvar t string\n\t\t\tt, s = nextToken(skipSpace(s))\n\t\t\tif t == \"\" {\n\t\t\t\tcontinue headers\n\t\t\t}\n\t\t\text := map[string]string{\"\": t}\n\t\t\tfor {\n\t\t\t\ts = skipSpace(s)\n\t\t\t\tif !strings.HasPrefix(s, \";\") {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tvar k string\n\t\t\t\tk, s = nextToken(skipSpace(s[1:]))\n\t\t\t\tif k == \"\" {\n\t\t\t\t\tcontinue headers\n\t\t\t\t}\n\t\t\t\ts = skipSpace(s)\n\t\t\t\tvar v string\n\t\t\t\tif strings.HasPrefix(s, \"=\") {\n\t\t\t\t\tv, s = nextTokenOrQuoted(skipSpace(s[1:]))\n\t\t\t\t\ts = skipSpace(s)\n\t\t\t\t}\n\t\t\t\tif s != \"\" && s[0] != ',' && s[0] != ';' {\n\t\t\t\t\tcontinue headers\n\t\t\t\t}\n\t\t\t\text[k] = v\n\t\t\t}\n\t\t\tif s != \"\" && s[0] != ',' {\n\t\t\t\tcontinue headers\n\t\t\t}\n\t\t\tresult = append(result, ext)\n\t\t\tif s == \"\" {\n\t\t\t\tcontinue headers\n\t\t\t}\n\t\t\ts = s[1:]\n\t\t}\n\t}\n\treturn result\n}\n"
  },
  {
    "path": "websockets/v1/vendor/github.com/gorilla/websocket/x_net_proxy.go",
    "content": "// Code generated by golang.org/x/tools/cmd/bundle. DO NOT EDIT.\n//go:generate bundle -o x_net_proxy.go golang.org/x/net/proxy\n\n// Package proxy provides support for a variety of protocols to proxy network\n// data.\n//\n\npackage websocket\n\nimport (\n\t\"errors\"\n\t\"io\"\n\t\"net\"\n\t\"net/url\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n)\n\ntype proxy_direct struct{}\n\n// Direct is a direct proxy: one that makes network connections directly.\nvar proxy_Direct = proxy_direct{}\n\nfunc (proxy_direct) Dial(network, addr string) (net.Conn, error) {\n\treturn net.Dial(network, addr)\n}\n\n// A PerHost directs connections to a default Dialer unless the host name\n// requested matches one of a number of exceptions.\ntype proxy_PerHost struct {\n\tdef, bypass proxy_Dialer\n\n\tbypassNetworks []*net.IPNet\n\tbypassIPs      []net.IP\n\tbypassZones    []string\n\tbypassHosts    []string\n}\n\n// NewPerHost returns a PerHost Dialer that directs connections to either\n// defaultDialer or bypass, depending on whether the connection matches one of\n// the configured rules.\nfunc proxy_NewPerHost(defaultDialer, bypass proxy_Dialer) *proxy_PerHost {\n\treturn &proxy_PerHost{\n\t\tdef:    defaultDialer,\n\t\tbypass: bypass,\n\t}\n}\n\n// Dial connects to the address addr on the given network through either\n// defaultDialer or bypass.\nfunc (p *proxy_PerHost) Dial(network, addr string) (c net.Conn, err error) {\n\thost, _, err := net.SplitHostPort(addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn p.dialerForRequest(host).Dial(network, addr)\n}\n\nfunc (p *proxy_PerHost) dialerForRequest(host string) proxy_Dialer {\n\tif ip := net.ParseIP(host); ip != nil {\n\t\tfor _, net := range p.bypassNetworks {\n\t\t\tif net.Contains(ip) {\n\t\t\t\treturn p.bypass\n\t\t\t}\n\t\t}\n\t\tfor _, bypassIP := range p.bypassIPs {\n\t\t\tif bypassIP.Equal(ip) {\n\t\t\t\treturn p.bypass\n\t\t\t}\n\t\t}\n\t\treturn p.def\n\t}\n\n\tfor _, zone := range p.bypassZones {\n\t\tif strings.HasSuffix(host, zone) {\n\t\t\treturn p.bypass\n\t\t}\n\t\tif host == zone[1:] {\n\t\t\t// For a zone \".example.com\", we match \"example.com\"\n\t\t\t// too.\n\t\t\treturn p.bypass\n\t\t}\n\t}\n\tfor _, bypassHost := range p.bypassHosts {\n\t\tif bypassHost == host {\n\t\t\treturn p.bypass\n\t\t}\n\t}\n\treturn p.def\n}\n\n// AddFromString parses a string that contains comma-separated values\n// specifying hosts that should use the bypass proxy. Each value is either an\n// IP address, a CIDR range, a zone (*.example.com) or a host name\n// (localhost). A best effort is made to parse the string and errors are\n// ignored.\nfunc (p *proxy_PerHost) AddFromString(s string) {\n\thosts := strings.Split(s, \",\")\n\tfor _, host := range hosts {\n\t\thost = strings.TrimSpace(host)\n\t\tif len(host) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tif strings.Contains(host, \"/\") {\n\t\t\t// We assume that it's a CIDR address like 127.0.0.0/8\n\t\t\tif _, net, err := net.ParseCIDR(host); err == nil {\n\t\t\t\tp.AddNetwork(net)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tif ip := net.ParseIP(host); ip != nil {\n\t\t\tp.AddIP(ip)\n\t\t\tcontinue\n\t\t}\n\t\tif strings.HasPrefix(host, \"*.\") {\n\t\t\tp.AddZone(host[1:])\n\t\t\tcontinue\n\t\t}\n\t\tp.AddHost(host)\n\t}\n}\n\n// AddIP specifies an IP address that will use the bypass proxy. Note that\n// this will only take effect if a literal IP address is dialed. A connection\n// to a named host will never match an IP.\nfunc (p *proxy_PerHost) AddIP(ip net.IP) {\n\tp.bypassIPs = append(p.bypassIPs, ip)\n}\n\n// AddNetwork specifies an IP range that will use the bypass proxy. Note that\n// this will only take effect if a literal IP address is dialed. A connection\n// to a named host will never match.\nfunc (p *proxy_PerHost) AddNetwork(net *net.IPNet) {\n\tp.bypassNetworks = append(p.bypassNetworks, net)\n}\n\n// AddZone specifies a DNS suffix that will use the bypass proxy. A zone of\n// \"example.com\" matches \"example.com\" and all of its subdomains.\nfunc (p *proxy_PerHost) AddZone(zone string) {\n\tif strings.HasSuffix(zone, \".\") {\n\t\tzone = zone[:len(zone)-1]\n\t}\n\tif !strings.HasPrefix(zone, \".\") {\n\t\tzone = \".\" + zone\n\t}\n\tp.bypassZones = append(p.bypassZones, zone)\n}\n\n// AddHost specifies a host name that will use the bypass proxy.\nfunc (p *proxy_PerHost) AddHost(host string) {\n\tif strings.HasSuffix(host, \".\") {\n\t\thost = host[:len(host)-1]\n\t}\n\tp.bypassHosts = append(p.bypassHosts, host)\n}\n\n// A Dialer is a means to establish a connection.\ntype proxy_Dialer interface {\n\t// Dial connects to the given address via the proxy.\n\tDial(network, addr string) (c net.Conn, err error)\n}\n\n// Auth contains authentication parameters that specific Dialers may require.\ntype proxy_Auth struct {\n\tUser, Password string\n}\n\n// FromEnvironment returns the dialer specified by the proxy related variables in\n// the environment.\nfunc proxy_FromEnvironment() proxy_Dialer {\n\tallProxy := proxy_allProxyEnv.Get()\n\tif len(allProxy) == 0 {\n\t\treturn proxy_Direct\n\t}\n\n\tproxyURL, err := url.Parse(allProxy)\n\tif err != nil {\n\t\treturn proxy_Direct\n\t}\n\tproxy, err := proxy_FromURL(proxyURL, proxy_Direct)\n\tif err != nil {\n\t\treturn proxy_Direct\n\t}\n\n\tnoProxy := proxy_noProxyEnv.Get()\n\tif len(noProxy) == 0 {\n\t\treturn proxy\n\t}\n\n\tperHost := proxy_NewPerHost(proxy, proxy_Direct)\n\tperHost.AddFromString(noProxy)\n\treturn perHost\n}\n\n// proxySchemes is a map from URL schemes to a function that creates a Dialer\n// from a URL with such a scheme.\nvar proxy_proxySchemes map[string]func(*url.URL, proxy_Dialer) (proxy_Dialer, error)\n\n// RegisterDialerType takes a URL scheme and a function to generate Dialers from\n// a URL with that scheme and a forwarding Dialer. Registered schemes are used\n// by FromURL.\nfunc proxy_RegisterDialerType(scheme string, f func(*url.URL, proxy_Dialer) (proxy_Dialer, error)) {\n\tif proxy_proxySchemes == nil {\n\t\tproxy_proxySchemes = make(map[string]func(*url.URL, proxy_Dialer) (proxy_Dialer, error))\n\t}\n\tproxy_proxySchemes[scheme] = f\n}\n\n// FromURL returns a Dialer given a URL specification and an underlying\n// Dialer for it to make network requests.\nfunc proxy_FromURL(u *url.URL, forward proxy_Dialer) (proxy_Dialer, error) {\n\tvar auth *proxy_Auth\n\tif u.User != nil {\n\t\tauth = new(proxy_Auth)\n\t\tauth.User = u.User.Username()\n\t\tif p, ok := u.User.Password(); ok {\n\t\t\tauth.Password = p\n\t\t}\n\t}\n\n\tswitch u.Scheme {\n\tcase \"socks5\":\n\t\treturn proxy_SOCKS5(\"tcp\", u.Host, auth, forward)\n\t}\n\n\t// If the scheme doesn't match any of the built-in schemes, see if it\n\t// was registered by another package.\n\tif proxy_proxySchemes != nil {\n\t\tif f, ok := proxy_proxySchemes[u.Scheme]; ok {\n\t\t\treturn f(u, forward)\n\t\t}\n\t}\n\n\treturn nil, errors.New(\"proxy: unknown scheme: \" + u.Scheme)\n}\n\nvar (\n\tproxy_allProxyEnv = &proxy_envOnce{\n\t\tnames: []string{\"ALL_PROXY\", \"all_proxy\"},\n\t}\n\tproxy_noProxyEnv = &proxy_envOnce{\n\t\tnames: []string{\"NO_PROXY\", \"no_proxy\"},\n\t}\n)\n\n// envOnce looks up an environment variable (optionally by multiple\n// names) once. It mitigates expensive lookups on some platforms\n// (e.g. Windows).\n// (Borrowed from net/http/transport.go)\ntype proxy_envOnce struct {\n\tnames []string\n\tonce  sync.Once\n\tval   string\n}\n\nfunc (e *proxy_envOnce) Get() string {\n\te.once.Do(e.init)\n\treturn e.val\n}\n\nfunc (e *proxy_envOnce) init() {\n\tfor _, n := range e.names {\n\t\te.val = os.Getenv(n)\n\t\tif e.val != \"\" {\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given address\n// with an optional username and password. See RFC 1928 and RFC 1929.\nfunc proxy_SOCKS5(network, addr string, auth *proxy_Auth, forward proxy_Dialer) (proxy_Dialer, error) {\n\ts := &proxy_socks5{\n\t\tnetwork: network,\n\t\taddr:    addr,\n\t\tforward: forward,\n\t}\n\tif auth != nil {\n\t\ts.user = auth.User\n\t\ts.password = auth.Password\n\t}\n\n\treturn s, nil\n}\n\ntype proxy_socks5 struct {\n\tuser, password string\n\tnetwork, addr  string\n\tforward        proxy_Dialer\n}\n\nconst proxy_socks5Version = 5\n\nconst (\n\tproxy_socks5AuthNone     = 0\n\tproxy_socks5AuthPassword = 2\n)\n\nconst proxy_socks5Connect = 1\n\nconst (\n\tproxy_socks5IP4    = 1\n\tproxy_socks5Domain = 3\n\tproxy_socks5IP6    = 4\n)\n\nvar proxy_socks5Errors = []string{\n\t\"\",\n\t\"general failure\",\n\t\"connection forbidden\",\n\t\"network unreachable\",\n\t\"host unreachable\",\n\t\"connection refused\",\n\t\"TTL expired\",\n\t\"command not supported\",\n\t\"address type not supported\",\n}\n\n// Dial connects to the address addr on the given network via the SOCKS5 proxy.\nfunc (s *proxy_socks5) Dial(network, addr string) (net.Conn, error) {\n\tswitch network {\n\tcase \"tcp\", \"tcp6\", \"tcp4\":\n\tdefault:\n\t\treturn nil, errors.New(\"proxy: no support for SOCKS5 proxy connections of type \" + network)\n\t}\n\n\tconn, err := s.forward.Dial(s.network, s.addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif err := s.connect(conn, addr); err != nil {\n\t\tconn.Close()\n\t\treturn nil, err\n\t}\n\treturn conn, nil\n}\n\n// connect takes an existing connection to a socks5 proxy server,\n// and commands the server to extend that connection to target,\n// which must be a canonical address with a host and port.\nfunc (s *proxy_socks5) connect(conn net.Conn, target string) error {\n\thost, portStr, err := net.SplitHostPort(target)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tport, err := strconv.Atoi(portStr)\n\tif err != nil {\n\t\treturn errors.New(\"proxy: failed to parse port number: \" + portStr)\n\t}\n\tif port < 1 || port > 0xffff {\n\t\treturn errors.New(\"proxy: port number out of range: \" + portStr)\n\t}\n\n\t// the size here is just an estimate\n\tbuf := make([]byte, 0, 6+len(host))\n\n\tbuf = append(buf, proxy_socks5Version)\n\tif len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 {\n\t\tbuf = append(buf, 2 /* num auth methods */, proxy_socks5AuthNone, proxy_socks5AuthPassword)\n\t} else {\n\t\tbuf = append(buf, 1 /* num auth methods */, proxy_socks5AuthNone)\n\t}\n\n\tif _, err := conn.Write(buf); err != nil {\n\t\treturn errors.New(\"proxy: failed to write greeting to SOCKS5 proxy at \" + s.addr + \": \" + err.Error())\n\t}\n\n\tif _, err := io.ReadFull(conn, buf[:2]); err != nil {\n\t\treturn errors.New(\"proxy: failed to read greeting from SOCKS5 proxy at \" + s.addr + \": \" + err.Error())\n\t}\n\tif buf[0] != 5 {\n\t\treturn errors.New(\"proxy: SOCKS5 proxy at \" + s.addr + \" has unexpected version \" + strconv.Itoa(int(buf[0])))\n\t}\n\tif buf[1] == 0xff {\n\t\treturn errors.New(\"proxy: SOCKS5 proxy at \" + s.addr + \" requires authentication\")\n\t}\n\n\t// See RFC 1929\n\tif buf[1] == proxy_socks5AuthPassword {\n\t\tbuf = buf[:0]\n\t\tbuf = append(buf, 1 /* password protocol version */)\n\t\tbuf = append(buf, uint8(len(s.user)))\n\t\tbuf = append(buf, s.user...)\n\t\tbuf = append(buf, uint8(len(s.password)))\n\t\tbuf = append(buf, s.password...)\n\n\t\tif _, err := conn.Write(buf); err != nil {\n\t\t\treturn errors.New(\"proxy: failed to write authentication request to SOCKS5 proxy at \" + s.addr + \": \" + err.Error())\n\t\t}\n\n\t\tif _, err := io.ReadFull(conn, buf[:2]); err != nil {\n\t\t\treturn errors.New(\"proxy: failed to read authentication reply from SOCKS5 proxy at \" + s.addr + \": \" + err.Error())\n\t\t}\n\n\t\tif buf[1] != 0 {\n\t\t\treturn errors.New(\"proxy: SOCKS5 proxy at \" + s.addr + \" rejected username/password\")\n\t\t}\n\t}\n\n\tbuf = buf[:0]\n\tbuf = append(buf, proxy_socks5Version, proxy_socks5Connect, 0 /* reserved */)\n\n\tif ip := net.ParseIP(host); ip != nil {\n\t\tif ip4 := ip.To4(); ip4 != nil {\n\t\t\tbuf = append(buf, proxy_socks5IP4)\n\t\t\tip = ip4\n\t\t} else {\n\t\t\tbuf = append(buf, proxy_socks5IP6)\n\t\t}\n\t\tbuf = append(buf, ip...)\n\t} else {\n\t\tif len(host) > 255 {\n\t\t\treturn errors.New(\"proxy: destination host name too long: \" + host)\n\t\t}\n\t\tbuf = append(buf, proxy_socks5Domain)\n\t\tbuf = append(buf, byte(len(host)))\n\t\tbuf = append(buf, host...)\n\t}\n\tbuf = append(buf, byte(port>>8), byte(port))\n\n\tif _, err := conn.Write(buf); err != nil {\n\t\treturn errors.New(\"proxy: failed to write connect request to SOCKS5 proxy at \" + s.addr + \": \" + err.Error())\n\t}\n\n\tif _, err := io.ReadFull(conn, buf[:4]); err != nil {\n\t\treturn errors.New(\"proxy: failed to read connect reply from SOCKS5 proxy at \" + s.addr + \": \" + err.Error())\n\t}\n\n\tfailure := \"unknown error\"\n\tif int(buf[1]) < len(proxy_socks5Errors) {\n\t\tfailure = proxy_socks5Errors[buf[1]]\n\t}\n\n\tif len(failure) > 0 {\n\t\treturn errors.New(\"proxy: SOCKS5 proxy at \" + s.addr + \" failed to connect: \" + failure)\n\t}\n\n\tbytesToDiscard := 0\n\tswitch buf[3] {\n\tcase proxy_socks5IP4:\n\t\tbytesToDiscard = net.IPv4len\n\tcase proxy_socks5IP6:\n\t\tbytesToDiscard = net.IPv6len\n\tcase proxy_socks5Domain:\n\t\t_, err := io.ReadFull(conn, buf[:1])\n\t\tif err != nil {\n\t\t\treturn errors.New(\"proxy: failed to read domain length from SOCKS5 proxy at \" + s.addr + \": \" + err.Error())\n\t\t}\n\t\tbytesToDiscard = int(buf[0])\n\tdefault:\n\t\treturn errors.New(\"proxy: got unknown address type \" + strconv.Itoa(int(buf[3])) + \" from SOCKS5 proxy at \" + s.addr)\n\t}\n\n\tif cap(buf) < bytesToDiscard {\n\t\tbuf = make([]byte, bytesToDiscard)\n\t} else {\n\t\tbuf = buf[:bytesToDiscard]\n\t}\n\tif _, err := io.ReadFull(conn, buf); err != nil {\n\t\treturn errors.New(\"proxy: failed to read address from SOCKS5 proxy at \" + s.addr + \": \" + err.Error())\n\t}\n\n\t// Also need to discard the port number\n\tif _, err := io.ReadFull(conn, buf[:2]); err != nil {\n\t\treturn errors.New(\"proxy: failed to read port from SOCKS5 proxy at \" + s.addr + \": \" + err.Error())\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "websockets/v2/CLI.go",
    "content": "package poker\n\nimport (\n\t\"bufio\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"strconv\"\n\t\"strings\"\n)\n\n// CLI helps players through a game of poker.\ntype CLI struct {\n\tplayerStore PlayerStore\n\tin          *bufio.Scanner\n\tout         io.Writer\n\tgame        Game\n}\n\n// NewCLI creates a CLI for playing poker.\nfunc NewCLI(in io.Reader, out io.Writer, game Game) *CLI {\n\treturn &CLI{\n\t\tin:   bufio.NewScanner(in),\n\t\tout:  out,\n\t\tgame: game,\n\t}\n}\n\n// PlayerPrompt is the text asking the user for the number of players.\nconst PlayerPrompt = \"Please enter the number of players: \"\n\n// BadPlayerInputErrMsg is the text telling the user they did bad things.\nconst BadPlayerInputErrMsg = \"Bad value received for number of players, please try again with a number\"\n\n// BadWinnerInputMsg is the text telling the user they declared the winner wrong.\nconst BadWinnerInputMsg = \"invalid winner input, expect format of 'PlayerName wins'\"\n\n// PlayPoker starts the game.\nfunc (cli *CLI) PlayPoker() {\n\tfmt.Fprint(cli.out, PlayerPrompt)\n\n\tnumberOfPlayers, err := strconv.Atoi(cli.readLine())\n\n\tif err != nil {\n\t\tfmt.Fprint(cli.out, BadPlayerInputErrMsg)\n\t\treturn\n\t}\n\n\tcli.game.Start(numberOfPlayers, cli.out)\n\n\twinnerInput := cli.readLine()\n\twinner, err := extractWinner(winnerInput)\n\n\tif err != nil {\n\t\tfmt.Fprint(cli.out, BadWinnerInputMsg)\n\t\treturn\n\t}\n\n\tcli.game.Finish(winner)\n}\n\nfunc extractWinner(userInput string) (string, error) {\n\tif !strings.Contains(userInput, \" wins\") {\n\t\treturn \"\", errors.New(BadWinnerInputMsg)\n\t}\n\treturn strings.Replace(userInput, \" wins\", \"\", 1), nil\n}\n\nfunc (cli *CLI) readLine() string {\n\tcli.in.Scan()\n\treturn cli.in.Text()\n}\n"
  },
  {
    "path": "websockets/v2/CLI_test.go",
    "content": "package poker_test\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\tpoker \"github.com/quii/learn-go-with-tests/websockets/v2\"\n)\n\nvar dummyBlindAlerter = &poker.SpyBlindAlerter{}\nvar dummyPlayerStore = &poker.StubPlayerStore{}\nvar dummyStdIn = &bytes.Buffer{}\nvar dummyStdOut = &bytes.Buffer{}\n\ntype GameSpy struct {\n\tStartCalled     bool\n\tStartCalledWith int\n\tBlindAlert      []byte\n\n\tFinishedCalled   bool\n\tFinishCalledWith string\n}\n\nfunc (g *GameSpy) Start(numberOfPlayers int, out io.Writer) {\n\tg.StartCalled = true\n\tg.StartCalledWith = numberOfPlayers\n\tout.Write(g.BlindAlert)\n}\n\nfunc (g *GameSpy) Finish(winner string) {\n\tg.FinishedCalled = true\n\tg.FinishCalledWith = winner\n}\n\nfunc userSends(messages ...string) io.Reader {\n\treturn strings.NewReader(strings.Join(messages, \"\\n\"))\n}\n\nfunc TestCLI(t *testing.T) {\n\n\tt.Run(\"start game with 3 players and finish game with 'Chris' as winner\", func(t *testing.T) {\n\t\tgame := &GameSpy{}\n\n\t\tout := &bytes.Buffer{}\n\t\tin := userSends(\"3\", \"Chris wins\")\n\n\t\tpoker.NewCLI(in, out, game).PlayPoker()\n\n\t\tassertMessagesSentToUser(t, out, poker.PlayerPrompt)\n\t\tassertGameStartedWith(t, game, 3)\n\t\tassertFinishCalledWith(t, game, \"Chris\")\n\t})\n\n\tt.Run(\"start game with 8 players and record 'Cleo' as winner\", func(t *testing.T) {\n\t\tgame := &GameSpy{}\n\n\t\tin := userSends(\"8\", \"Cleo wins\")\n\n\t\tpoker.NewCLI(in, dummyStdOut, game).PlayPoker()\n\n\t\tassertGameStartedWith(t, game, 8)\n\t\tassertFinishCalledWith(t, game, \"Cleo\")\n\t})\n\n\tt.Run(\"it prints an error when a non numeric value is entered and does not start the game\", func(t *testing.T) {\n\t\tgame := &GameSpy{}\n\n\t\tout := &bytes.Buffer{}\n\t\tin := userSends(\"pies\")\n\n\t\tpoker.NewCLI(in, out, game).PlayPoker()\n\n\t\tassertGameNotStarted(t, game)\n\t\tassertMessagesSentToUser(t, out, poker.PlayerPrompt, poker.BadPlayerInputErrMsg)\n\t})\n\n\tt.Run(\"it prints an error when the winner is declared incorrectly\", func(t *testing.T) {\n\t\tgame := &GameSpy{}\n\n\t\tout := &bytes.Buffer{}\n\t\tin := userSends(\"8\", \"Lloyd is a killer\")\n\n\t\tpoker.NewCLI(in, out, game).PlayPoker()\n\n\t\tassertGameNotFinished(t, game)\n\t\tassertMessagesSentToUser(t, out, poker.PlayerPrompt, poker.BadWinnerInputMsg)\n\t})\n}\n\nfunc assertGameStartedWith(t testing.TB, game *GameSpy, numberOfPlayersWanted int) {\n\tt.Helper()\n\n\tpassed := retryUntil(500*time.Millisecond, func() bool {\n\t\treturn game.StartCalledWith == numberOfPlayersWanted\n\t})\n\n\tif !passed {\n\t\tt.Errorf(\"wanted Start called with %d but got %d\", numberOfPlayersWanted, game.StartCalledWith)\n\t}\n}\n\nfunc assertGameNotFinished(t testing.TB, game *GameSpy) {\n\tt.Helper()\n\tif game.FinishedCalled {\n\t\tt.Errorf(\"game should not have finished\")\n\t}\n}\n\nfunc assertGameNotStarted(t testing.TB, game *GameSpy) {\n\tt.Helper()\n\tif game.StartCalled {\n\t\tt.Errorf(\"game should not have started\")\n\t}\n}\n\nfunc assertFinishCalledWith(t testing.TB, game *GameSpy, winner string) {\n\tt.Helper()\n\n\tpassed := retryUntil(500*time.Millisecond, func() bool {\n\t\treturn game.FinishCalledWith == winner\n\t})\n\n\tif !passed {\n\t\tt.Errorf(\"expected finish called with %q but got %q\", winner, game.FinishCalledWith)\n\t}\n}\n\nfunc assertMessagesSentToUser(t testing.TB, stdout *bytes.Buffer, messages ...string) {\n\tt.Helper()\n\twant := strings.Join(messages, \"\")\n\tgot := stdout.String()\n\tif got != want {\n\t\tt.Errorf(\"got %q sent to stdout but expected %+v\", got, messages)\n\t}\n}\n\nfunc assertScheduledAlert(t testing.TB, got, want poker.ScheduledAlert) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"got %+v, want %+v\", got, want)\n\t}\n}\n"
  },
  {
    "path": "websockets/v2/Gopkg.toml",
    "content": "# Gopkg.toml example\n#\n# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html\n# for detailed Gopkg.toml documentation.\n#\n# required = [\"github.com/user/thing/cmd/thing\"]\n# ignored = [\"github.com/user/project/pkgX\", \"bitbucket.org/user/project/pkgA/pkgY\"]\n#\n# [[constraint]]\n#   name = \"github.com/user/project\"\n#   version = \"1.0.0\"\n#\n# [[constraint]]\n#   name = \"github.com/user/project2\"\n#   branch = \"dev\"\n#   source = \"github.com/myfork/project2\"\n#\n# [[override]]\n#   name = \"github.com/x/y\"\n#   version = \"2.4.0\"\n#\n# [prune]\n#   non-go = false\n#   go-tests = true\n#   unused-packages = true\n\n\n[[constraint]]\n  name = \"github.com/gorilla/websocket\"\n  version = \"1.4.0\"\n\n[prune]\n  go-tests = true\n  unused-packages = true\n"
  },
  {
    "path": "websockets/v2/blind_alerter.go",
    "content": "package poker\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"time\"\n)\n\n// BlindAlerter schedules alerts for blind amounts.\ntype BlindAlerter interface {\n\tScheduleAlertAt(duration time.Duration, amount int, to io.Writer)\n}\n\n// BlindAlerterFunc allows you to implement BlindAlerter with a function.\ntype BlindAlerterFunc func(duration time.Duration, amount int, to io.Writer)\n\n// ScheduleAlertAt is BlindAlerterFunc implementation of BlindAlerter.\nfunc (a BlindAlerterFunc) ScheduleAlertAt(duration time.Duration, amount int, to io.Writer) {\n\ta(duration, amount, to)\n}\n\n// Alerter will schedule alerts and print them to \"to\".\nfunc Alerter(duration time.Duration, amount int, to io.Writer) {\n\ttime.AfterFunc(duration, func() {\n\t\tfmt.Fprintf(to, \"Blind is now %d\\n\", amount)\n\t})\n}\n"
  },
  {
    "path": "websockets/v2/cmd/cli/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tpoker \"github.com/quii/learn-go-with-tests/websockets/v2\"\n)\n\nconst dbFileName = \"game.db.json\"\n\nfunc main() {\n\tstore, close, err := poker.FileSystemPlayerStoreFromFile(dbFileName)\n\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer close()\n\n\tgame := poker.NewTexasHoldem(poker.BlindAlerterFunc(poker.Alerter), store)\n\tcli := poker.NewCLI(os.Stdin, os.Stdout, game)\n\n\tfmt.Println(\"Let's play poker\")\n\tfmt.Println(\"Type {Name} wins to record a win\")\n\tcli.PlayPoker()\n}\n"
  },
  {
    "path": "websockets/v2/cmd/webserver/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/quii/learn-go-with-tests/websockets/v2\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n)\n\nconst dbFileName = \"game.db.json\"\n\nfunc main() {\n\tdb, err := os.OpenFile(dbFileName, os.O_RDWR|os.O_CREATE, 0666)\n\n\tif err != nil {\n\t\tlog.Fatalf(\"problem opening %s %v\", dbFileName, err)\n\t}\n\n\tstore, err := poker.NewFileSystemPlayerStore(db)\n\n\tif err != nil {\n\t\tlog.Fatalf(\"problem creating file system player store, %v \", err)\n\t}\n\n\tgame := poker.NewTexasHoldem(poker.BlindAlerterFunc(poker.Alerter), store)\n\n\tserver, err := poker.NewPlayerServer(store, game)\n\n\tif err != nil {\n\t\tlog.Fatalf(\"problem creating player server %v\", err)\n\t}\n\n\tlog.Fatal(http.ListenAndServe(\":5000\", server))\n}\n"
  },
  {
    "path": "websockets/v2/file_system_store.go",
    "content": "package poker\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"sort\"\n)\n\n// FileSystemPlayerStore stores players in the filesystem.\ntype FileSystemPlayerStore struct {\n\tdatabase *json.Encoder\n\tleague   League\n}\n\n// NewFileSystemPlayerStore creates a FileSystemPlayerStore initialising the store if needed.\nfunc NewFileSystemPlayerStore(file *os.File) (*FileSystemPlayerStore, error) {\n\n\terr := initialisePlayerDBFile(file)\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"problem initialising player db file, %v\", err)\n\t}\n\n\tleague, err := NewLeague(file)\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"problem loading player store from file %s, %v\", file.Name(), err)\n\t}\n\n\treturn &FileSystemPlayerStore{\n\t\tdatabase: json.NewEncoder(&Tape{file}),\n\t\tleague:   league,\n\t}, nil\n}\n\n// FileSystemPlayerStoreFromFile creates a PlayerStore from the contents of a JSON file found at path.\nfunc FileSystemPlayerStoreFromFile(path string) (*FileSystemPlayerStore, func(), error) {\n\tdb, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0666)\n\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"problem opening %s %v\", path, err)\n\t}\n\n\tcloseFunc := func() {\n\t\tdb.Close()\n\t}\n\n\tstore, err := NewFileSystemPlayerStore(db)\n\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"problem creating file system player store, %v \", err)\n\t}\n\n\treturn store, closeFunc, nil\n}\n\nfunc initialisePlayerDBFile(file *os.File) error {\n\tfile.Seek(0, io.SeekStart)\n\n\tinfo, err := file.Stat()\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"problem getting file info from file %s, %v\", file.Name(), err)\n\t}\n\n\tif info.Size() == 0 {\n\t\tfile.Write([]byte(\"[]\"))\n\t\tfile.Seek(0, io.SeekStart)\n\t}\n\n\treturn nil\n}\n\n// GetLeague returns the Scores of all the players.\nfunc (f *FileSystemPlayerStore) GetLeague() League {\n\tsort.Slice(f.league, func(i, j int) bool {\n\t\treturn f.league[i].Wins > f.league[j].Wins\n\t})\n\treturn f.league\n}\n\n// GetPlayerScore retrieves a player's score.\nfunc (f *FileSystemPlayerStore) GetPlayerScore(name string) int {\n\n\tplayer := f.league.Find(name)\n\n\tif player != nil {\n\t\treturn player.Wins\n\t}\n\n\treturn 0\n}\n\n// RecordWin will store a win for a player, incrementing wins if already known.\nfunc (f *FileSystemPlayerStore) RecordWin(name string) {\n\tplayer := f.league.Find(name)\n\n\tif player != nil {\n\t\tplayer.Wins++\n\t} else {\n\t\tf.league = append(f.league, Player{name, 1})\n\t}\n\n\tf.database.Encode(f.league)\n}\n"
  },
  {
    "path": "websockets/v2/file_system_store_test.go",
    "content": "package poker_test\n\nimport (\n\t\"os\"\n\t\"testing\"\n\n\tpoker \"github.com/quii/learn-go-with-tests/websockets/v2\"\n)\n\nfunc createTempFile(t testing.TB, initialData string) (*os.File, func()) {\n\tt.Helper()\n\n\ttmpfile, err := os.CreateTemp(\"\", \"db\")\n\n\tif err != nil {\n\t\tt.Fatalf(\"could not create temp file %v\", err)\n\t}\n\n\ttmpfile.Write([]byte(initialData))\n\n\tremoveFile := func() {\n\t\ttmpfile.Close()\n\t\tos.Remove(tmpfile.Name())\n\t}\n\n\treturn tmpfile, removeFile\n}\n\nfunc TestFileSystemStore(t *testing.T) {\n\n\tt.Run(\"League sorted\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := poker.NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tgot := store.GetLeague()\n\n\t\twant := []poker.Player{\n\t\t\t{Name: \"Chris\", Wins: 33},\n\t\t\t{Name: \"Cleo\", Wins: 10},\n\t\t}\n\n\t\tassertLeague(t, got, want)\n\n\t\t// read again\n\t\tgot = store.GetLeague()\n\t\tassertLeague(t, got, want)\n\t})\n\n\tt.Run(\"get player score\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := poker.NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tgot := store.GetPlayerScore(\"Chris\")\n\t\twant := 33\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"store wins for existing players\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := poker.NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tstore.RecordWin(\"Chris\")\n\n\t\tgot := store.GetPlayerScore(\"Chris\")\n\t\twant := 34\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"store wins for existing players\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, `[\n\t\t\t{\"Name\": \"Cleo\", \"Wins\": 10},\n\t\t\t{\"Name\": \"Chris\", \"Wins\": 33}]`)\n\t\tdefer cleanDatabase()\n\n\t\tstore, err := poker.NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\n\t\tstore.RecordWin(\"Pepper\")\n\n\t\tgot := store.GetPlayerScore(\"Pepper\")\n\t\twant := 1\n\t\tassertScoreEquals(t, got, want)\n\t})\n\n\tt.Run(\"works with an empty file\", func(t *testing.T) {\n\t\tdatabase, cleanDatabase := createTempFile(t, \"\")\n\t\tdefer cleanDatabase()\n\n\t\t_, err := poker.NewFileSystemPlayerStore(database)\n\n\t\tassertNoError(t, err)\n\t})\n}\n\nfunc assertScoreEquals(t testing.TB, got, want int) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"got %d want %d\", got, want)\n\t}\n}\n\nfunc assertNoError(t testing.TB, err error) {\n\tt.Helper()\n\tif err != nil {\n\t\tt.Fatalf(\"didn't expect an error but got one, %v\", err)\n\t}\n}\n"
  },
  {
    "path": "websockets/v2/game.go",
    "content": "package poker\n\nimport \"io\"\n\n// Game manages the state of a game.\ntype Game interface {\n\tStart(numberOfPlayers int, alertsDestination io.Writer)\n\tFinish(winner string)\n}\n"
  },
  {
    "path": "websockets/v2/game.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>Let's play poker</title>\n</head>\n<body>\n<section id=\"game\">\n    <div id=\"game-start\">\n        <label for=\"player-count\">Number of players</label>\n        <input type=\"number\" id=\"player-count\"/>\n        <button id=\"start-game\">Start</button>\n    </div>\n\n    <div id=\"declare-winner\">\n        <label for=\"winner\">Winner</label>\n        <input type=\"text\" id=\"winner\"/>\n        <button id=\"winner-button\">Declare winner</button>\n    </div>\n\n    <div id=\"blind-value\"/>\n</section>\n\n<section id=\"game-end\">\n    <h1>Another great game of poker everyone!</h1>\n    <p><a href=\"/league\">Go check the league table</a></p>\n</section>\n\n</body>\n<script type=\"application/javascript\">\n    const startGame = document.getElementById('game-start')\n\n    const declareWinner = document.getElementById('declare-winner')\n    const submitWinnerButton = document.getElementById('winner-button')\n    const winnerInput = document.getElementById('winner')\n\n    const blindContainer = document.getElementById('blind-value')\n\n    const gameContainer = document.getElementById('game')\n    const gameEndContainer = document.getElementById('game-end')\n\n    declareWinner.hidden = true\n    gameEndContainer.hidden = true\n\n    document.getElementById('start-game').addEventListener('click', event => {\n        startGame.hidden = true\n        declareWinner.hidden = false\n\n        const numberOfPlayers = document.getElementById('player-count').value\n\n        if (window['WebSocket']) {\n            const conn = new WebSocket('ws://' + document.location.host + '/ws')\n\n            submitWinnerButton.onclick = event => {\n                conn.send(winnerInput.value)\n                gameEndContainer.hidden = false\n                gameContainer.hidden = true\n            }\n\n            conn.onclose = evt => {\n                blindContainer.innerText = 'Connection closed'\n            }\n\n            conn.onmessage = evt => {\n                blindContainer.innerText = evt.data\n            }\n\n            conn.onopen = function () {\n                conn.send(numberOfPlayers)\n            }\n        }\n    })\n</script>\n</html>\n"
  },
  {
    "path": "websockets/v2/league.go",
    "content": "package poker\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n)\n\n// League stores a collection of players.\ntype League []Player\n\n// Find tries to return a player from a League.\nfunc (l League) Find(name string) *Player {\n\tfor i, p := range l {\n\t\tif p.Name == name {\n\t\t\treturn &l[i]\n\t\t}\n\t}\n\treturn nil\n}\n\n// NewLeague creates a League from JSON.\nfunc NewLeague(rdr io.Reader) (League, error) {\n\tvar league []Player\n\terr := json.NewDecoder(rdr).Decode(&league)\n\n\tif err != nil {\n\t\terr = fmt.Errorf(\"problem parsing League, %v\", err)\n\t}\n\n\treturn league, err\n}\n"
  },
  {
    "path": "websockets/v2/player_server_ws.go",
    "content": "package poker\n\nimport (\n\t\"github.com/gorilla/websocket\"\n\t\"log\"\n\t\"net/http\"\n)\n\ntype playerServerWS struct {\n\t*websocket.Conn\n}\n\nfunc (w *playerServerWS) Write(p []byte) (n int, err error) {\n\terr = w.WriteMessage(websocket.TextMessage, p)\n\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn len(p), nil\n}\n\nfunc newPlayerServerWS(w http.ResponseWriter, r *http.Request) *playerServerWS {\n\tconn, err := wsUpgrader.Upgrade(w, r, nil)\n\n\tif err != nil {\n\t\tlog.Printf(\"problem upgrading connection to websockets %v\\n\", err)\n\t}\n\n\treturn &playerServerWS{conn}\n}\n\nfunc (w *playerServerWS) WaitForMsg() string {\n\t_, msg, err := w.ReadMessage()\n\tif err != nil {\n\t\tlog.Printf(\"error reading from websocket %v\\n\", err)\n\t}\n\treturn string(msg)\n}\n"
  },
  {
    "path": "websockets/v2/server.go",
    "content": "package poker\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"html/template\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/gorilla/websocket\"\n)\n\n// PlayerStore stores score information about players.\ntype PlayerStore interface {\n\tGetPlayerScore(name string) int\n\tRecordWin(name string)\n\tGetLeague() League\n}\n\n// Player stores a name with a number of wins.\ntype Player struct {\n\tName string\n\tWins int\n}\n\n// PlayerServer is a HTTP interface for player information.\ntype PlayerServer struct {\n\tstore PlayerStore\n\thttp.Handler\n\ttemplate *template.Template\n\tgame     Game\n}\n\nconst jsonContentType = \"application/json\"\nconst htmlTemplatePath = \"game.html\"\n\n// NewPlayerServer creates a PlayerServer with routing configured.\nfunc NewPlayerServer(store PlayerStore, game Game) (*PlayerServer, error) {\n\tp := new(PlayerServer)\n\n\ttmpl, err := template.ParseFiles(htmlTemplatePath)\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"problem opening %s %v\", htmlTemplatePath, err)\n\t}\n\n\tp.game = game\n\tp.template = tmpl\n\tp.store = store\n\n\trouter := http.NewServeMux()\n\trouter.Handle(\"/league\", http.HandlerFunc(p.leagueHandler))\n\trouter.Handle(\"/players/\", http.HandlerFunc(p.playersHandler))\n\trouter.Handle(\"/game\", http.HandlerFunc(p.playGame))\n\trouter.Handle(\"/ws\", http.HandlerFunc(p.webSocket))\n\n\tp.Handler = router\n\n\treturn p, nil\n}\n\nvar wsUpgrader = websocket.Upgrader{\n\tReadBufferSize:  1024,\n\tWriteBufferSize: 1024,\n}\n\nfunc (p *PlayerServer) webSocket(w http.ResponseWriter, r *http.Request) {\n\tws := newPlayerServerWS(w, r)\n\n\tnumberOfPlayersMsg := ws.WaitForMsg()\n\tnumberOfPlayers, _ := strconv.Atoi(numberOfPlayersMsg)\n\tp.game.Start(numberOfPlayers, ws)\n\n\twinner := ws.WaitForMsg()\n\tp.game.Finish(winner)\n}\n\nfunc (p *PlayerServer) playGame(w http.ResponseWriter, r *http.Request) {\n\tp.template.Execute(w, nil)\n}\n\nfunc (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) {\n\tw.Header().Set(\"content-type\", jsonContentType)\n\tjson.NewEncoder(w).Encode(p.store.GetLeague())\n}\n\nfunc (p *PlayerServer) playersHandler(w http.ResponseWriter, r *http.Request) {\n\tplayer := strings.TrimPrefix(r.URL.Path, \"/players/\")\n\n\tswitch r.Method {\n\tcase http.MethodPost:\n\t\tp.processWin(w, player)\n\tcase http.MethodGet:\n\t\tp.showScore(w, player)\n\t}\n}\n\nfunc (p *PlayerServer) showScore(w http.ResponseWriter, player string) {\n\tscore := p.store.GetPlayerScore(player)\n\n\tif score == 0 {\n\t\tw.WriteHeader(http.StatusNotFound)\n\t}\n\n\tfmt.Fprint(w, score)\n}\n\nfunc (p *PlayerServer) processWin(w http.ResponseWriter, player string) {\n\tp.store.RecordWin(player)\n\tw.WriteHeader(http.StatusAccepted)\n}\n"
  },
  {
    "path": "websockets/v2/server_integration_test.go",
    "content": "package poker_test\n\nimport (\n\t\"github.com/quii/learn-go-with-tests/websockets/v2\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestRecordingWinsAndRetrievingThem(t *testing.T) {\n\tdatabase, cleanDatabase := createTempFile(t, `[]`)\n\tdefer cleanDatabase()\n\tstore, err := poker.NewFileSystemPlayerStore(database)\n\n\tassertNoError(t, err)\n\n\tserver := mustMakePlayerServer(t, store, dummyGame)\n\tplayer := \"Pepper\"\n\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\tserver.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))\n\n\tt.Run(\"get score\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newGetScoreRequest(player))\n\t\tassertStatus(t, response, http.StatusOK)\n\n\t\tassertResponseBody(t, response.Body.String(), \"3\")\n\t})\n\n\tt.Run(\"get League\", func(t *testing.T) {\n\t\tresponse := httptest.NewRecorder()\n\t\tserver.ServeHTTP(response, newLeagueRequest())\n\t\tassertStatus(t, response, http.StatusOK)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\t\twant := []poker.Player{\n\t\t\t{Name: \"Pepper\", Wins: 3},\n\t\t}\n\t\tassertLeague(t, got, want)\n\t})\n}\n"
  },
  {
    "path": "websockets/v2/server_test.go",
    "content": "package poker_test\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/gorilla/websocket\"\n\tpoker \"github.com/quii/learn-go-with-tests/websockets/v2\"\n)\n\nvar (\n\tdummyGame = &GameSpy{}\n\ttenMS     = 10 * time.Millisecond\n)\n\nfunc mustMakePlayerServer(t *testing.T, store poker.PlayerStore, game poker.Game) *poker.PlayerServer {\n\tserver, err := poker.NewPlayerServer(store, game)\n\tif err != nil {\n\t\tt.Fatal(\"problem creating player server\", err)\n\t}\n\treturn server\n}\n\nfunc TestGETPlayers(t *testing.T) {\n\tstore := poker.StubPlayerStore{\n\t\tScores: map[string]int{\n\t\t\t\"Pepper\": 20,\n\t\t\t\"Floyd\":  10,\n\t\t},\n\t}\n\tserver := mustMakePlayerServer(t, &store, dummyGame)\n\n\tt.Run(\"returns Pepper's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Pepper\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"20\")\n\t})\n\n\tt.Run(\"returns Floyd's score\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Floyd\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response, http.StatusOK)\n\t\tassertResponseBody(t, response.Body.String(), \"10\")\n\t})\n\n\tt.Run(\"returns 404 on missing players\", func(t *testing.T) {\n\t\trequest := newGetScoreRequest(\"Apollo\")\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response, http.StatusNotFound)\n\t})\n}\n\nfunc TestStoreWins(t *testing.T) {\n\tstore := poker.StubPlayerStore{\n\t\tScores: map[string]int{},\n\t}\n\tserver := mustMakePlayerServer(t, &store, dummyGame)\n\n\tt.Run(\"it records wins on POST\", func(t *testing.T) {\n\t\tplayer := \"Pepper\"\n\n\t\trequest := newPostWinRequest(player)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response, http.StatusAccepted)\n\t\tpoker.AssertPlayerWin(t, &store, player)\n\t})\n}\n\nfunc TestLeague(t *testing.T) {\n\n\tt.Run(\"it returns the League table as JSON\", func(t *testing.T) {\n\t\twantedLeague := []poker.Player{\n\t\t\t{Name: \"Cleo\", Wins: 32},\n\t\t\t{Name: \"Chris\", Wins: 20},\n\t\t\t{Name: \"Tiest\", Wins: 14},\n\t\t}\n\n\t\tstore := poker.StubPlayerStore{League: wantedLeague}\n\t\tserver := mustMakePlayerServer(t, &store, dummyGame)\n\n\t\trequest := newLeagueRequest()\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tgot := getLeagueFromResponse(t, response.Body)\n\n\t\tassertStatus(t, response, http.StatusOK)\n\t\tassertLeague(t, got, wantedLeague)\n\t\tassertContentType(t, response, \"application/json\")\n\n\t})\n}\n\nfunc TestGame(t *testing.T) {\n\tt.Run(\"GET /game returns 200\", func(t *testing.T) {\n\t\tserver := mustMakePlayerServer(t, &poker.StubPlayerStore{}, dummyGame)\n\n\t\trequest := newGameRequest()\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response, http.StatusOK)\n\t})\n\n\tt.Run(\"start a game with 3 players, send some blind alerts down WS and declare Ruth the winner\", func(t *testing.T) {\n\t\twantedBlindAlert := \"Blind is 100\"\n\t\twinner := \"Ruth\"\n\n\t\tgame := &GameSpy{BlindAlert: []byte(wantedBlindAlert)}\n\t\tserver := httptest.NewServer(mustMakePlayerServer(t, dummyPlayerStore, game))\n\t\tws := mustDialWS(t, \"ws\"+strings.TrimPrefix(server.URL, \"http\")+\"/ws\")\n\n\t\tdefer server.Close()\n\t\tdefer ws.Close()\n\n\t\twriteWSMessage(t, ws, \"3\")\n\t\twriteWSMessage(t, ws, winner)\n\n\t\tassertGameStartedWith(t, game, 3)\n\t\tassertFinishCalledWith(t, game, winner)\n\t\twithin(t, tenMS, func() { assertWebsocketGotMsg(t, ws, wantedBlindAlert) })\n\t})\n}\n\nfunc assertWebsocketGotMsg(t *testing.T, ws *websocket.Conn, want string) {\n\t_, msg, _ := ws.ReadMessage()\n\tif string(msg) != want {\n\t\tt.Errorf(`got \"%s\", want \"%s\"`, string(msg), want)\n\t}\n}\n\nfunc retryUntil(d time.Duration, f func() bool) bool {\n\tdeadline := time.Now().Add(d)\n\tfor time.Now().Before(deadline) {\n\t\tif f() {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc within(t testing.TB, d time.Duration, assert func()) {\n\tt.Helper()\n\n\tdone := make(chan struct{}, 1)\n\n\tgo func() {\n\t\tassert()\n\t\tdone <- struct{}{}\n\t}()\n\n\tselect {\n\tcase <-time.After(d):\n\t\tt.Error(\"timed out\")\n\tcase <-done:\n\t}\n}\n\nfunc writeWSMessage(t testing.TB, conn *websocket.Conn, message string) {\n\tt.Helper()\n\tif err := conn.WriteMessage(websocket.TextMessage, []byte(message)); err != nil {\n\t\tt.Fatalf(\"could not send message over ws connection %v\", err)\n\t}\n}\n\nfunc assertContentType(t testing.TB, response *httptest.ResponseRecorder, want string) {\n\tt.Helper()\n\tif response.Header().Get(\"content-type\") != want {\n\t\tt.Errorf(\"response did not have content-type of %s, got %v\", want, response.Result().Header)\n\t}\n}\n\nfunc getLeagueFromResponse(t *testing.T, body io.Reader) []poker.Player {\n\tt.Helper()\n\tleague, err := poker.NewLeague(body)\n\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to parse response from server %q into slice of Player, '%v'\", body, err)\n\t}\n\n\treturn league\n}\n\nfunc assertLeague(t *testing.T, got, want []poker.Player) {\n\tt.Helper()\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %v want %v\", got, want)\n\t}\n}\n\nfunc assertStatus(t *testing.T, got *httptest.ResponseRecorder, want int) {\n\tt.Helper()\n\tif got.Code != want {\n\t\tt.Errorf(\"did not get correct status, got %d, want %d\", got.Code, want)\n\t}\n}\n\nfunc newGameRequest() *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, \"/game\", nil)\n\treturn req\n}\n\nfunc newLeagueRequest() *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, \"/league\", nil)\n\treturn req\n}\n\nfunc newGetScoreRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodGet, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc newPostWinRequest(name string) *http.Request {\n\treq, _ := http.NewRequest(http.MethodPost, fmt.Sprintf(\"/players/%s\", name), nil)\n\treturn req\n}\n\nfunc assertResponseBody(t *testing.T, got, want string) {\n\tt.Helper()\n\tif got != want {\n\t\tt.Errorf(\"response body is wrong, got %q want %q\", got, want)\n\t}\n}\n\nfunc mustDialWS(t *testing.T, url string) *websocket.Conn {\n\tws, _, err := websocket.DefaultDialer.Dial(url, nil)\n\n\tif err != nil {\n\t\tt.Fatalf(\"could not open a ws connection on %s %v\", url, err)\n\t}\n\n\treturn ws\n}\n"
  },
  {
    "path": "websockets/v2/tape.go",
    "content": "package poker\n\nimport (\n\t\"io\"\n\t\"os\"\n)\n\n// Tape represents an os.File that will re-write from the start on every Write call.\ntype Tape struct {\n\tFile *os.File\n}\n\nfunc (t *Tape) Write(p []byte) (n int, err error) {\n\tt.File.Truncate(0)\n\tt.File.Seek(0, io.SeekStart)\n\treturn t.File.Write(p)\n}\n"
  },
  {
    "path": "websockets/v2/tape_test.go",
    "content": "package poker_test\n\nimport (\n\t\"io\"\n\t\"testing\"\n\n\tpoker \"github.com/quii/learn-go-with-tests/websockets/v2\"\n)\n\nfunc TestTape_Write(t *testing.T) {\n\tfile, clean := createTempFile(t, \"12345\")\n\tdefer clean()\n\n\ttape := &poker.Tape{File: file}\n\n\ttape.Write([]byte(\"abc\"))\n\n\tfile.Seek(0, io.SeekStart)\n\tnewFileContents, _ := io.ReadAll(file)\n\n\tgot := string(newFileContents)\n\twant := \"abc\"\n\n\tif got != want {\n\t\tt.Errorf(\"got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "websockets/v2/testing.go",
    "content": "package poker\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"testing\"\n\t\"time\"\n)\n\n// StubPlayerStore implements PlayerStore for testing purposes.\ntype StubPlayerStore struct {\n\tScores   map[string]int\n\tWinCalls []string\n\tLeague   []Player\n}\n\n// GetPlayerScore returns a score from Scores.\nfunc (s *StubPlayerStore) GetPlayerScore(name string) int {\n\tscore := s.Scores[name]\n\treturn score\n}\n\n// RecordWin will record a win to WinCalls.\nfunc (s *StubPlayerStore) RecordWin(name string) {\n\ts.WinCalls = append(s.WinCalls, name)\n}\n\n// GetLeague returns League.\nfunc (s *StubPlayerStore) GetLeague() League {\n\treturn s.League\n}\n\n// AssertPlayerWin allows you to spy on the store's calls to RecordWin.\nfunc AssertPlayerWin(t testing.TB, store *StubPlayerStore, winner string) {\n\tt.Helper()\n\n\tif len(store.WinCalls) != 1 {\n\t\tt.Fatalf(\"got %d calls to RecordWin want %d\", len(store.WinCalls), 1)\n\t}\n\n\tif store.WinCalls[0] != winner {\n\t\tt.Errorf(\"did not store correct winner got %q want %q\", store.WinCalls[0], winner)\n\t}\n}\n\n// ScheduledAlert holds information about when an alert is scheduled.\ntype ScheduledAlert struct {\n\tAt     time.Duration\n\tAmount int\n}\n\nfunc (s ScheduledAlert) String() string {\n\treturn fmt.Sprintf(\"%d chips at %v\", s.Amount, s.At)\n}\n\n// SpyBlindAlerter allows you to spy on ScheduleAlertAt calls.\ntype SpyBlindAlerter struct {\n\tAlerts []ScheduledAlert\n}\n\n// ScheduleAlertAt records alerts that have been scheduled.\nfunc (s *SpyBlindAlerter) ScheduleAlertAt(at time.Duration, amount int, to io.Writer) {\n\ts.Alerts = append(s.Alerts, ScheduledAlert{at, amount})\n}\n"
  },
  {
    "path": "websockets/v2/texas_holdem.go",
    "content": "package poker\n\nimport (\n\t\"io\"\n\t\"time\"\n)\n\n// TexasHoldem manages a game of poker.\ntype TexasHoldem struct {\n\talerter BlindAlerter\n\tstore   PlayerStore\n}\n\n// NewTexasHoldem returns a new game.\nfunc NewTexasHoldem(alerter BlindAlerter, store PlayerStore) *TexasHoldem {\n\treturn &TexasHoldem{\n\t\talerter: alerter,\n\t\tstore:   store,\n\t}\n}\n\n// Start will schedule blind alerts dependant on the number of players.\nfunc (p *TexasHoldem) Start(numberOfPlayers int, alertsDestination io.Writer) {\n\tblindIncrement := time.Duration(5+numberOfPlayers) * time.Minute\n\n\tblinds := []int{100, 200, 300, 400, 500, 600, 800, 1000, 2000, 4000, 8000}\n\tblindTime := 0 * time.Second\n\tfor _, blind := range blinds {\n\t\tp.alerter.ScheduleAlertAt(blindTime, blind, alertsDestination)\n\t\tblindTime = blindTime + blindIncrement\n\t}\n}\n\n// Finish ends the game, recording the winner.\nfunc (p *TexasHoldem) Finish(winner string) {\n\tp.store.RecordWin(winner)\n}\n"
  },
  {
    "path": "websockets/v2/texas_holdem_test.go",
    "content": "package poker_test\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"testing\"\n\t\"time\"\n\n\tpoker \"github.com/quii/learn-go-with-tests/websockets/v2\"\n)\n\nfunc TestGame_Start(t *testing.T) {\n\tt.Run(\"schedules alerts on game start for 5 players\", func(t *testing.T) {\n\t\tblindAlerter := &poker.SpyBlindAlerter{}\n\t\tgame := poker.NewTexasHoldem(blindAlerter, dummyPlayerStore)\n\n\t\tgame.Start(5, io.Discard)\n\n\t\tcases := []poker.ScheduledAlert{\n\t\t\t{At: 0 * time.Second, Amount: 100},\n\t\t\t{At: 10 * time.Minute, Amount: 200},\n\t\t\t{At: 20 * time.Minute, Amount: 300},\n\t\t\t{At: 30 * time.Minute, Amount: 400},\n\t\t\t{At: 40 * time.Minute, Amount: 500},\n\t\t\t{At: 50 * time.Minute, Amount: 600},\n\t\t\t{At: 60 * time.Minute, Amount: 800},\n\t\t\t{At: 70 * time.Minute, Amount: 1000},\n\t\t\t{At: 80 * time.Minute, Amount: 2000},\n\t\t\t{At: 90 * time.Minute, Amount: 4000},\n\t\t\t{At: 100 * time.Minute, Amount: 8000},\n\t\t}\n\n\t\tcheckSchedulingCases(cases, t, blindAlerter)\n\t})\n\n\tt.Run(\"schedules alerts on game start for 7 players\", func(t *testing.T) {\n\t\tblindAlerter := &poker.SpyBlindAlerter{}\n\t\tgame := poker.NewTexasHoldem(blindAlerter, dummyPlayerStore)\n\n\t\tgame.Start(7, io.Discard)\n\n\t\tcases := []poker.ScheduledAlert{\n\t\t\t{At: 0 * time.Second, Amount: 100},\n\t\t\t{At: 12 * time.Minute, Amount: 200},\n\t\t\t{At: 24 * time.Minute, Amount: 300},\n\t\t\t{At: 36 * time.Minute, Amount: 400},\n\t\t}\n\n\t\tcheckSchedulingCases(cases, t, blindAlerter)\n\t})\n\n}\n\nfunc TestGame_Finish(t *testing.T) {\n\tstore := &poker.StubPlayerStore{}\n\tgame := poker.NewTexasHoldem(dummyBlindAlerter, store)\n\twinner := \"Ruth\"\n\n\tgame.Finish(winner)\n\tpoker.AssertPlayerWin(t, store, winner)\n}\n\nfunc checkSchedulingCases(cases []poker.ScheduledAlert, t *testing.T, blindAlerter *poker.SpyBlindAlerter) {\n\tfor i, want := range cases {\n\t\tt.Run(fmt.Sprint(want), func(t *testing.T) {\n\n\t\t\tif len(blindAlerter.Alerts) <= i {\n\t\t\t\tt.Fatalf(\"alert %d was not scheduled %v\", i, blindAlerter.Alerts)\n\t\t\t}\n\n\t\t\tgot := blindAlerter.Alerts[i]\n\t\t\tassertScheduledAlert(t, got, want)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "websockets/v2/vendor/github.com/gorilla/websocket/.gitignore",
    "content": "# Compiled Object files, Static and Dynamic libs (Shared Objects)\n*.o\n*.a\n*.so\n\n# Folders\n_obj\n_test\n\n# Architecture specific extensions/prefixes\n*.[568vq]\n[568vq].out\n\n*.cgo1.go\n*.cgo2.c\n_cgo_defun.c\n_cgo_gotypes.go\n_cgo_export.*\n\n_testmain.go\n\n*.exe\n\n.idea/\n*.iml\n"
  },
  {
    "path": "websockets/v2/vendor/github.com/gorilla/websocket/.travis.yml",
    "content": "language: go\nsudo: false\n\nmatrix:\n  include:\n    - go: 1.7.x\n    - go: 1.8.x\n    - go: 1.9.x\n    - go: 1.10.x\n    - go: 1.11.x\n    - go: tip\n  allow_failures:\n    - go: tip\n\nscript:\n  - go get -t -v ./...\n  - diff -u <(echo -n) <(gofmt -d .)\n  - go vet $(go list ./... | grep -v /vendor/)\n  - go test -v -race ./...\n"
  },
  {
    "path": "websockets/v2/vendor/github.com/gorilla/websocket/AUTHORS",
    "content": "# This is the official list of Gorilla WebSocket authors for copyright\n# purposes.\n#\n# Please keep the list sorted.\n\nGary Burd <gary@beagledreams.com>\nGoogle LLC (https://opensource.google.com/)\nJoachim Bauch <mail@joachim-bauch.de>\n\n"
  },
  {
    "path": "websockets/v2/vendor/github.com/gorilla/websocket/LICENSE",
    "content": "Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n  Redistributions of source code must retain the above copyright notice, this\n  list of conditions and the following disclaimer.\n\n  Redistributions in binary form must reproduce the above copyright notice,\n  this list of conditions and the following disclaimer in the documentation\n  and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "websockets/v2/vendor/github.com/gorilla/websocket/README.md",
    "content": "# Gorilla WebSocket\n\nGorilla WebSocket is a [Go](http://golang.org/) implementation of the\n[WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol.\n\n[![Build Status](https://travis-ci.org/gorilla/websocket.svg?branch=master)](https://travis-ci.org/gorilla/websocket)\n[![GoDoc](https://pkg.go.dev/badge/github.com/gorilla/websocket)](https://pkg.go.dev/github.com/gorilla/websocket)\n\n### Documentation\n\n* [API Reference](https://pkg.go.dev/github.com/gorilla/websocket)\n* [Chat example](https://github.com/gorilla/websocket/tree/master/examples/chat)\n* [Command example](https://github.com/gorilla/websocket/tree/master/examples/command)\n* [Client and server example](https://github.com/gorilla/websocket/tree/master/examples/echo)\n* [File watch example](https://github.com/gorilla/websocket/tree/master/examples/filewatch)\n\n### Status\n\nThe Gorilla WebSocket package provides a complete and tested implementation of\nthe [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. The\npackage API is stable.\n\n### Installation\n\n    go get github.com/gorilla/websocket\n\n### Protocol Compliance\n\nThe Gorilla WebSocket package passes the server tests in the [Autobahn Test\nSuite](http://autobahn.ws/testsuite) using the application in the [examples/autobahn\nsubdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn).\n\n### Gorilla WebSocket compared with other packages\n\n<table>\n<tr>\n<th></th>\n<th><a href=\"https://pkg.go.dev/github.com/gorilla/websocket\">github.com/gorilla</a></th>\n<th><a href=\"https://pkg.go.dev/golang.org/x/net/websocket\">golang.org/x/net</a></th>\n</tr>\n<tr>\n<tr><td colspan=\"3\"><a href=\"http://tools.ietf.org/html/rfc6455\">RFC 6455</a> Features</td></tr>\n<tr><td>Passes <a href=\"http://autobahn.ws/testsuite/\">Autobahn Test Suite</a></td><td><a href=\"https://github.com/gorilla/websocket/tree/master/examples/autobahn\">Yes</a></td><td>No</td></tr>\n<tr><td>Receive <a href=\"https://tools.ietf.org/html/rfc6455#section-5.4\">fragmented</a> message<td>Yes</td><td><a href=\"https://code.google.com/p/go/issues/detail?id=7632\">No</a>, see note 1</td></tr>\n<tr><td>Send <a href=\"https://tools.ietf.org/html/rfc6455#section-5.5.1\">close</a> message</td><td><a href=\"https://pkg.go.dev/github.com/gorilla/websocket#hdr-Control_Messages\">Yes</a></td><td><a href=\"https://code.google.com/p/go/issues/detail?id=4588\">No</a></td></tr>\n<tr><td>Send <a href=\"https://tools.ietf.org/html/rfc6455#section-5.5.2\">pings</a> and receive <a href=\"https://tools.ietf.org/html/rfc6455#section-5.5.3\">pongs</a></td><td><a href=\"https://pkg.go.dev/github.com/gorilla/websocket#hdr-Control_Messages\">Yes</a></td><td>No</td></tr>\n<tr><td>Get the <a href=\"https://tools.ietf.org/html/rfc6455#section-5.6\">type</a> of a received data message</td><td>Yes</td><td>Yes, see note 2</td></tr>\n<tr><td colspan=\"3\">Other Features</tr></td>\n<tr><td><a href=\"https://tools.ietf.org/html/rfc7692\">Compression Extensions</a></td><td>Experimental</td><td>No</td></tr>\n<tr><td>Read message using io.Reader</td><td><a href=\"https://pkg.go.dev/github.com/gorilla/websocket#Conn.NextReader\">Yes</a></td><td>No, see note 3</td></tr>\n<tr><td>Write message using io.WriteCloser</td><td><a href=\"https://pkg.go.dev/github.com/gorilla/websocket#Conn.NextWriter\">Yes</a></td><td>No, see note 3</td></tr>\n</table>\n\nNotes:\n\n1. Large messages are fragmented in [Chrome's new WebSocket implementation](http://www.ietf.org/mail-archive/web/hybi/current/msg10503.html).\n2. The application can get the type of a received data message by implementing\n   a [Codec marshal](https://pkg.go.dev/golang.org/x/net/websocket#Codec.Marshal)\n   function.\n3. The go.net io.Reader and io.Writer operate across WebSocket frame boundaries.\n  Read returns when the input buffer is full or a frame boundary is\n  encountered. Each call to Write sends a single frame message. The Gorilla\n  io.Reader and io.WriteCloser operate on a single WebSocket message.\n\n"
  },
  {
    "path": "websockets/v2/vendor/github.com/gorilla/websocket/client.go",
    "content": "// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage websocket\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/httptrace\"\n\t\"net/url\"\n\t\"strings\"\n\t\"time\"\n)\n\n// ErrBadHandshake is returned when the server response to opening handshake is\n// invalid.\nvar ErrBadHandshake = errors.New(\"websocket: bad handshake\")\n\nvar errInvalidCompression = errors.New(\"websocket: invalid compression negotiation\")\n\n// NewClient creates a new client connection using the given net connection.\n// The URL u specifies the host and request URI. Use requestHeader to specify\n// the origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies\n// (Cookie). Use the response.Header to get the selected subprotocol\n// (Sec-WebSocket-Protocol) and cookies (Set-Cookie).\n//\n// If the WebSocket handshake fails, ErrBadHandshake is returned along with a\n// non-nil *http.Response so that callers can handle redirects, authentication,\n// etc.\n//\n// Deprecated: Use Dialer instead.\nfunc NewClient(netConn net.Conn, u *url.URL, requestHeader http.Header, readBufSize, writeBufSize int) (c *Conn, response *http.Response, err error) {\n\td := Dialer{\n\t\tReadBufferSize:  readBufSize,\n\t\tWriteBufferSize: writeBufSize,\n\t\tNetDial: func(net, addr string) (net.Conn, error) {\n\t\t\treturn netConn, nil\n\t\t},\n\t}\n\treturn d.Dial(u.String(), requestHeader)\n}\n\n// A Dialer contains options for connecting to WebSocket server.\ntype Dialer struct {\n\t// NetDial specifies the dial function for creating TCP connections. If\n\t// NetDial is nil, net.Dial is used.\n\tNetDial func(network, addr string) (net.Conn, error)\n\n\t// NetDialContext specifies the dial function for creating TCP connections. If\n\t// NetDialContext is nil, net.DialContext is used.\n\tNetDialContext func(ctx context.Context, network, addr string) (net.Conn, error)\n\n\t// Proxy specifies a function to return a proxy for a given\n\t// Request. If the function returns a non-nil error, the\n\t// request is aborted with the provided error.\n\t// If Proxy is nil or returns a nil *URL, no proxy is used.\n\tProxy func(*http.Request) (*url.URL, error)\n\n\t// TLSClientConfig specifies the TLS configuration to use with tls.Client.\n\t// If nil, the default configuration is used.\n\tTLSClientConfig *tls.Config\n\n\t// HandshakeTimeout specifies the duration for the handshake to complete.\n\tHandshakeTimeout time.Duration\n\n\t// ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer\n\t// size is zero, then a useful default size is used. The I/O buffer sizes\n\t// do not limit the size of the messages that can be sent or received.\n\tReadBufferSize, WriteBufferSize int\n\n\t// WriteBufferPool is a pool of buffers for write operations. If the value\n\t// is not set, then write buffers are allocated to the connection for the\n\t// lifetime of the connection.\n\t//\n\t// A pool is most useful when the application has a modest volume of writes\n\t// across a large number of connections.\n\t//\n\t// Applications should use a single pool for each unique value of\n\t// WriteBufferSize.\n\tWriteBufferPool BufferPool\n\n\t// Subprotocols specifies the client's requested subprotocols.\n\tSubprotocols []string\n\n\t// EnableCompression specifies if the client should attempt to negotiate\n\t// per message compression (RFC 7692). Setting this value to true does not\n\t// guarantee that compression will be supported. Currently only \"no context\n\t// takeover\" modes are supported.\n\tEnableCompression bool\n\n\t// Jar specifies the cookie jar.\n\t// If Jar is nil, cookies are not sent in requests and ignored\n\t// in responses.\n\tJar http.CookieJar\n}\n\n// Dial creates a new client connection by calling DialContext with a background context.\nfunc (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) {\n\treturn d.DialContext(context.Background(), urlStr, requestHeader)\n}\n\nvar errMalformedURL = errors.New(\"malformed ws or wss URL\")\n\nfunc hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) {\n\thostPort = u.Host\n\thostNoPort = u.Host\n\tif i := strings.LastIndex(u.Host, \":\"); i > strings.LastIndex(u.Host, \"]\") {\n\t\thostNoPort = hostNoPort[:i]\n\t} else {\n\t\tswitch u.Scheme {\n\t\tcase \"wss\":\n\t\t\thostPort += \":443\"\n\t\tcase \"https\":\n\t\t\thostPort += \":443\"\n\t\tdefault:\n\t\t\thostPort += \":80\"\n\t\t}\n\t}\n\treturn hostPort, hostNoPort\n}\n\n// DefaultDialer is a dialer with all fields set to the default values.\nvar DefaultDialer = &Dialer{\n\tProxy:            http.ProxyFromEnvironment,\n\tHandshakeTimeout: 45 * time.Second,\n}\n\n// nilDialer is dialer to use when receiver is nil.\nvar nilDialer = *DefaultDialer\n\n// DialContext creates a new client connection. Use requestHeader to specify the\n// origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies (Cookie).\n// Use the response.Header to get the selected subprotocol\n// (Sec-WebSocket-Protocol) and cookies (Set-Cookie).\n//\n// The context will be used in the request and in the Dialer\n//\n// If the WebSocket handshake fails, ErrBadHandshake is returned along with a\n// non-nil *http.Response so that callers can handle redirects, authentication,\n// etcetera. The response body may not contain the entire response and does not\n// need to be closed by the application.\nfunc (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) {\n\tif d == nil {\n\t\td = &nilDialer\n\t}\n\n\tchallengeKey, err := generateChallengeKey()\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tu, err := url.Parse(urlStr)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tswitch u.Scheme {\n\tcase \"ws\":\n\t\tu.Scheme = \"http\"\n\tcase \"wss\":\n\t\tu.Scheme = \"https\"\n\tdefault:\n\t\treturn nil, nil, errMalformedURL\n\t}\n\n\tif u.User != nil {\n\t\t// User name and password are not allowed in websocket URIs.\n\t\treturn nil, nil, errMalformedURL\n\t}\n\n\treq := &http.Request{\n\t\tMethod:     \"GET\",\n\t\tURL:        u,\n\t\tProto:      \"HTTP/1.1\",\n\t\tProtoMajor: 1,\n\t\tProtoMinor: 1,\n\t\tHeader:     make(http.Header),\n\t\tHost:       u.Host,\n\t}\n\treq = req.WithContext(ctx)\n\n\t// Set the cookies present in the cookie jar of the dialer\n\tif d.Jar != nil {\n\t\tfor _, cookie := range d.Jar.Cookies(u) {\n\t\t\treq.AddCookie(cookie)\n\t\t}\n\t}\n\n\t// Set the request headers using the capitalization for names and values in\n\t// RFC examples. Although the capitalization shouldn't matter, there are\n\t// servers that depend on it. The Header.Set method is not used because the\n\t// method canonicalizes the header names.\n\treq.Header[\"Upgrade\"] = []string{\"websocket\"}\n\treq.Header[\"Connection\"] = []string{\"Upgrade\"}\n\treq.Header[\"Sec-WebSocket-Key\"] = []string{challengeKey}\n\treq.Header[\"Sec-WebSocket-Version\"] = []string{\"13\"}\n\tif len(d.Subprotocols) > 0 {\n\t\treq.Header[\"Sec-WebSocket-Protocol\"] = []string{strings.Join(d.Subprotocols, \", \")}\n\t}\n\tfor k, vs := range requestHeader {\n\t\tswitch {\n\t\tcase k == \"Host\":\n\t\t\tif len(vs) > 0 {\n\t\t\t\treq.Host = vs[0]\n\t\t\t}\n\t\tcase k == \"Upgrade\" ||\n\t\t\tk == \"Connection\" ||\n\t\t\tk == \"Sec-Websocket-Key\" ||\n\t\t\tk == \"Sec-Websocket-Version\" ||\n\t\t\tk == \"Sec-Websocket-Extensions\" ||\n\t\t\t(k == \"Sec-Websocket-Protocol\" && len(d.Subprotocols) > 0):\n\t\t\treturn nil, nil, errors.New(\"websocket: duplicate header not allowed: \" + k)\n\t\tcase k == \"Sec-Websocket-Protocol\":\n\t\t\treq.Header[\"Sec-WebSocket-Protocol\"] = vs\n\t\tdefault:\n\t\t\treq.Header[k] = vs\n\t\t}\n\t}\n\n\tif d.EnableCompression {\n\t\treq.Header[\"Sec-WebSocket-Extensions\"] = []string{\"permessage-deflate; server_no_context_takeover; client_no_context_takeover\"}\n\t}\n\n\tif d.HandshakeTimeout != 0 {\n\t\tvar cancel func()\n\t\tctx, cancel = context.WithTimeout(ctx, d.HandshakeTimeout)\n\t\tdefer cancel()\n\t}\n\n\t// Get network dial function.\n\tvar netDial func(network, add string) (net.Conn, error)\n\n\tif d.NetDialContext != nil {\n\t\tnetDial = func(network, addr string) (net.Conn, error) {\n\t\t\treturn d.NetDialContext(ctx, network, addr)\n\t\t}\n\t} else if d.NetDial != nil {\n\t\tnetDial = d.NetDial\n\t} else {\n\t\tnetDialer := &net.Dialer{}\n\t\tnetDial = func(network, addr string) (net.Conn, error) {\n\t\t\treturn netDialer.DialContext(ctx, network, addr)\n\t\t}\n\t}\n\n\t// If needed, wrap the dial function to set the connection deadline.\n\tif deadline, ok := ctx.Deadline(); ok {\n\t\tforwardDial := netDial\n\t\tnetDial = func(network, addr string) (net.Conn, error) {\n\t\t\tc, err := forwardDial(network, addr)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\terr = c.SetDeadline(deadline)\n\t\t\tif err != nil {\n\t\t\t\tc.Close()\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn c, nil\n\t\t}\n\t}\n\n\t// If needed, wrap the dial function to connect through a proxy.\n\tif d.Proxy != nil {\n\t\tproxyURL, err := d.Proxy(req)\n\t\tif err != nil {\n\t\t\treturn nil, nil, err\n\t\t}\n\t\tif proxyURL != nil {\n\t\t\tdialer, err := proxy_FromURL(proxyURL, netDialerFunc(netDial))\n\t\t\tif err != nil {\n\t\t\t\treturn nil, nil, err\n\t\t\t}\n\t\t\tnetDial = dialer.Dial\n\t\t}\n\t}\n\n\thostPort, hostNoPort := hostPortNoPort(u)\n\ttrace := httptrace.ContextClientTrace(ctx)\n\tif trace != nil && trace.GetConn != nil {\n\t\ttrace.GetConn(hostPort)\n\t}\n\n\tnetConn, err := netDial(\"tcp\", hostPort)\n\tif trace != nil && trace.GotConn != nil {\n\t\ttrace.GotConn(httptrace.GotConnInfo{\n\t\t\tConn: netConn,\n\t\t})\n\t}\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tdefer func() {\n\t\tif netConn != nil {\n\t\t\tnetConn.Close()\n\t\t}\n\t}()\n\n\tif u.Scheme == \"https\" {\n\t\tcfg := cloneTLSConfig(d.TLSClientConfig)\n\t\tif cfg.ServerName == \"\" {\n\t\t\tcfg.ServerName = hostNoPort\n\t\t}\n\t\ttlsConn := tls.Client(netConn, cfg)\n\t\tnetConn = tlsConn\n\n\t\tvar err error\n\t\tif trace != nil {\n\t\t\terr = doHandshakeWithTrace(trace, tlsConn, cfg)\n\t\t} else {\n\t\t\terr = doHandshake(tlsConn, cfg)\n\t\t}\n\n\t\tif err != nil {\n\t\t\treturn nil, nil, err\n\t\t}\n\t}\n\n\tconn := newConn(netConn, false, d.ReadBufferSize, d.WriteBufferSize, d.WriteBufferPool, nil, nil)\n\n\tif err := req.Write(netConn); err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tif trace != nil && trace.GotFirstResponseByte != nil {\n\t\tif peek, err := conn.br.Peek(1); err == nil && len(peek) == 1 {\n\t\t\ttrace.GotFirstResponseByte()\n\t\t}\n\t}\n\n\tresp, err := http.ReadResponse(conn.br, req)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tif d.Jar != nil {\n\t\tif rc := resp.Cookies(); len(rc) > 0 {\n\t\t\td.Jar.SetCookies(u, rc)\n\t\t}\n\t}\n\n\tif resp.StatusCode != 101 ||\n\t\t!strings.EqualFold(resp.Header.Get(\"Upgrade\"), \"websocket\") ||\n\t\t!strings.EqualFold(resp.Header.Get(\"Connection\"), \"upgrade\") ||\n\t\tresp.Header.Get(\"Sec-Websocket-Accept\") != computeAcceptKey(challengeKey) {\n\t\t// Before closing the network connection on return from this\n\t\t// function, slurp up some of the response to aid application\n\t\t// debugging.\n\t\tbuf := make([]byte, 1024)\n\t\tn, _ := io.ReadFull(resp.Body, buf)\n\t\tresp.Body = ioutil.NopCloser(bytes.NewReader(buf[:n]))\n\t\treturn nil, resp, ErrBadHandshake\n\t}\n\n\tfor _, ext := range parseExtensions(resp.Header) {\n\t\tif ext[\"\"] != \"permessage-deflate\" {\n\t\t\tcontinue\n\t\t}\n\t\t_, snct := ext[\"server_no_context_takeover\"]\n\t\t_, cnct := ext[\"client_no_context_takeover\"]\n\t\tif !snct || !cnct {\n\t\t\treturn nil, resp, errInvalidCompression\n\t\t}\n\t\tconn.newCompressionWriter = compressNoContextTakeover\n\t\tconn.newDecompressionReader = decompressNoContextTakeover\n\t\tbreak\n\t}\n\n\tresp.Body = ioutil.NopCloser(bytes.NewReader([]byte{}))\n\tconn.subprotocol = resp.Header.Get(\"Sec-Websocket-Protocol\")\n\n\tnetConn.SetDeadline(time.Time{})\n\tnetConn = nil // to avoid close in defer.\n\treturn conn, resp, nil\n}\n\nfunc doHandshake(tlsConn *tls.Conn, cfg *tls.Config) error {\n\tif err := tlsConn.Handshake(); err != nil {\n\t\treturn err\n\t}\n\tif !cfg.InsecureSkipVerify {\n\t\tif err := tlsConn.VerifyHostname(cfg.ServerName); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "websockets/v2/vendor/github.com/gorilla/websocket/client_clone.go",
    "content": "// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// +build go1.8\n\npackage websocket\n\nimport \"crypto/tls\"\n\nfunc cloneTLSConfig(cfg *tls.Config) *tls.Config {\n\tif cfg == nil {\n\t\treturn &tls.Config{}\n\t}\n\treturn cfg.Clone()\n}\n"
  },
  {
    "path": "websockets/v2/vendor/github.com/gorilla/websocket/client_clone_legacy.go",
    "content": "// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// +build !go1.8\n\npackage websocket\n\nimport \"crypto/tls\"\n\n// cloneTLSConfig clones all public fields except the fields\n// SessionTicketsDisabled and SessionTicketKey. This avoids copying the\n// sync.Mutex in the sync.Once and makes it safe to call cloneTLSConfig on a\n// config in active use.\nfunc cloneTLSConfig(cfg *tls.Config) *tls.Config {\n\tif cfg == nil {\n\t\treturn &tls.Config{}\n\t}\n\treturn &tls.Config{\n\t\tRand:                     cfg.Rand,\n\t\tTime:                     cfg.Time,\n\t\tCertificates:             cfg.Certificates,\n\t\tNameToCertificate:        cfg.NameToCertificate,\n\t\tGetCertificate:           cfg.GetCertificate,\n\t\tRootCAs:                  cfg.RootCAs,\n\t\tNextProtos:               cfg.NextProtos,\n\t\tServerName:               cfg.ServerName,\n\t\tClientAuth:               cfg.ClientAuth,\n\t\tClientCAs:                cfg.ClientCAs,\n\t\tInsecureSkipVerify:       cfg.InsecureSkipVerify,\n\t\tCipherSuites:             cfg.CipherSuites,\n\t\tPreferServerCipherSuites: cfg.PreferServerCipherSuites,\n\t\tClientSessionCache:       cfg.ClientSessionCache,\n\t\tMinVersion:               cfg.MinVersion,\n\t\tMaxVersion:               cfg.MaxVersion,\n\t\tCurvePreferences:         cfg.CurvePreferences,\n\t}\n}\n"
  },
  {
    "path": "websockets/v2/vendor/github.com/gorilla/websocket/compression.go",
    "content": "// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage websocket\n\nimport (\n\t\"compress/flate\"\n\t\"errors\"\n\t\"io\"\n\t\"strings\"\n\t\"sync\"\n)\n\nconst (\n\tminCompressionLevel     = -2 // flate.HuffmanOnly not defined in Go < 1.6\n\tmaxCompressionLevel     = flate.BestCompression\n\tdefaultCompressionLevel = 1\n)\n\nvar (\n\tflateWriterPools [maxCompressionLevel - minCompressionLevel + 1]sync.Pool\n\tflateReaderPool  = sync.Pool{New: func() interface{} {\n\t\treturn flate.NewReader(nil)\n\t}}\n)\n\nfunc decompressNoContextTakeover(r io.Reader) io.ReadCloser {\n\tconst tail =\n\t// Add four bytes as specified in RFC\n\t\"\\x00\\x00\\xff\\xff\" +\n\t\t// Add final block to squelch unexpected EOF error from flate reader.\n\t\t\"\\x01\\x00\\x00\\xff\\xff\"\n\n\tfr, _ := flateReaderPool.Get().(io.ReadCloser)\n\tfr.(flate.Resetter).Reset(io.MultiReader(r, strings.NewReader(tail)), nil)\n\treturn &flateReadWrapper{fr}\n}\n\nfunc isValidCompressionLevel(level int) bool {\n\treturn minCompressionLevel <= level && level <= maxCompressionLevel\n}\n\nfunc compressNoContextTakeover(w io.WriteCloser, level int) io.WriteCloser {\n\tp := &flateWriterPools[level-minCompressionLevel]\n\ttw := &truncWriter{w: w}\n\tfw, _ := p.Get().(*flate.Writer)\n\tif fw == nil {\n\t\tfw, _ = flate.NewWriter(tw, level)\n\t} else {\n\t\tfw.Reset(tw)\n\t}\n\treturn &flateWriteWrapper{fw: fw, tw: tw, p: p}\n}\n\n// truncWriter is an io.Writer that writes all but the last four bytes of the\n// stream to another io.Writer.\ntype truncWriter struct {\n\tw io.WriteCloser\n\tn int\n\tp [4]byte\n}\n\nfunc (w *truncWriter) Write(p []byte) (int, error) {\n\tn := 0\n\n\t// fill buffer first for simplicity.\n\tif w.n < len(w.p) {\n\t\tn = copy(w.p[w.n:], p)\n\t\tp = p[n:]\n\t\tw.n += n\n\t\tif len(p) == 0 {\n\t\t\treturn n, nil\n\t\t}\n\t}\n\n\tm := len(p)\n\tif m > len(w.p) {\n\t\tm = len(w.p)\n\t}\n\n\tif nn, err := w.w.Write(w.p[:m]); err != nil {\n\t\treturn n + nn, err\n\t}\n\n\tcopy(w.p[:], w.p[m:])\n\tcopy(w.p[len(w.p)-m:], p[len(p)-m:])\n\tnn, err := w.w.Write(p[:len(p)-m])\n\treturn n + nn, err\n}\n\ntype flateWriteWrapper struct {\n\tfw *flate.Writer\n\ttw *truncWriter\n\tp  *sync.Pool\n}\n\nfunc (w *flateWriteWrapper) Write(p []byte) (int, error) {\n\tif w.fw == nil {\n\t\treturn 0, errWriteClosed\n\t}\n\treturn w.fw.Write(p)\n}\n\nfunc (w *flateWriteWrapper) Close() error {\n\tif w.fw == nil {\n\t\treturn errWriteClosed\n\t}\n\terr1 := w.fw.Flush()\n\tw.p.Put(w.fw)\n\tw.fw = nil\n\tif w.tw.p != [4]byte{0, 0, 0xff, 0xff} {\n\t\treturn errors.New(\"websocket: internal error, unexpected bytes at end of flate stream\")\n\t}\n\terr2 := w.tw.w.Close()\n\tif err1 != nil {\n\t\treturn err1\n\t}\n\treturn err2\n}\n\ntype flateReadWrapper struct {\n\tfr io.ReadCloser\n}\n\nfunc (r *flateReadWrapper) Read(p []byte) (int, error) {\n\tif r.fr == nil {\n\t\treturn 0, io.ErrClosedPipe\n\t}\n\tn, err := r.fr.Read(p)\n\tif err == io.EOF {\n\t\t// Preemptively place the reader back in the pool. This helps with\n\t\t// scenarios where the application does not call NextReader() soon after\n\t\t// this final read.\n\t\tr.Close()\n\t}\n\treturn n, err\n}\n\nfunc (r *flateReadWrapper) Close() error {\n\tif r.fr == nil {\n\t\treturn io.ErrClosedPipe\n\t}\n\terr := r.fr.Close()\n\tflateReaderPool.Put(r.fr)\n\tr.fr = nil\n\treturn err\n}\n"
  },
  {
    "path": "websockets/v2/vendor/github.com/gorilla/websocket/conn.go",
    "content": "// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage websocket\n\nimport (\n\t\"bufio\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"math/rand\"\n\t\"net\"\n\t\"strconv\"\n\t\"sync\"\n\t\"time\"\n\t\"unicode/utf8\"\n)\n\nconst (\n\t// Frame header byte 0 bits from Section 5.2 of RFC 6455\n\tfinalBit = 1 << 7\n\trsv1Bit  = 1 << 6\n\trsv2Bit  = 1 << 5\n\trsv3Bit  = 1 << 4\n\n\t// Frame header byte 1 bits from Section 5.2 of RFC 6455\n\tmaskBit = 1 << 7\n\n\tmaxFrameHeaderSize         = 2 + 8 + 4 // Fixed header + length + mask\n\tmaxControlFramePayloadSize = 125\n\n\twriteWait = time.Second\n\n\tdefaultReadBufferSize  = 4096\n\tdefaultWriteBufferSize = 4096\n\n\tcontinuationFrame = 0\n\tnoFrame           = -1\n)\n\n// Close codes defined in RFC 6455, section 11.7.\nconst (\n\tCloseNormalClosure           = 1000\n\tCloseGoingAway               = 1001\n\tCloseProtocolError           = 1002\n\tCloseUnsupportedData         = 1003\n\tCloseNoStatusReceived        = 1005\n\tCloseAbnormalClosure         = 1006\n\tCloseInvalidFramePayloadData = 1007\n\tClosePolicyViolation         = 1008\n\tCloseMessageTooBig           = 1009\n\tCloseMandatoryExtension      = 1010\n\tCloseInternalServerErr       = 1011\n\tCloseServiceRestart          = 1012\n\tCloseTryAgainLater           = 1013\n\tCloseTLSHandshake            = 1015\n)\n\n// The message types are defined in RFC 6455, section 11.8.\nconst (\n\t// TextMessage denotes a text data message. The text message payload is\n\t// interpreted as UTF-8 encoded text data.\n\tTextMessage = 1\n\n\t// BinaryMessage denotes a binary data message.\n\tBinaryMessage = 2\n\n\t// CloseMessage denotes a close control message. The optional message\n\t// payload contains a numeric code and text. Use the FormatCloseMessage\n\t// function to format a close message payload.\n\tCloseMessage = 8\n\n\t// PingMessage denotes a ping control message. The optional message payload\n\t// is UTF-8 encoded text.\n\tPingMessage = 9\n\n\t// PongMessage denotes a pong control message. The optional message payload\n\t// is UTF-8 encoded text.\n\tPongMessage = 10\n)\n\n// ErrCloseSent is returned when the application writes a message to the\n// connection after sending a close message.\nvar ErrCloseSent = errors.New(\"websocket: close sent\")\n\n// ErrReadLimit is returned when reading a message that is larger than the\n// read limit set for the connection.\nvar ErrReadLimit = errors.New(\"websocket: read limit exceeded\")\n\n// netError satisfies the net Error interface.\ntype netError struct {\n\tmsg       string\n\ttemporary bool\n\ttimeout   bool\n}\n\nfunc (e *netError) Error() string   { return e.msg }\nfunc (e *netError) Temporary() bool { return e.temporary }\nfunc (e *netError) Timeout() bool   { return e.timeout }\n\n// CloseError represents a close message.\ntype CloseError struct {\n\t// Code is defined in RFC 6455, section 11.7.\n\tCode int\n\n\t// Text is the optional text payload.\n\tText string\n}\n\nfunc (e *CloseError) Error() string {\n\ts := []byte(\"websocket: close \")\n\ts = strconv.AppendInt(s, int64(e.Code), 10)\n\tswitch e.Code {\n\tcase CloseNormalClosure:\n\t\ts = append(s, \" (normal)\"...)\n\tcase CloseGoingAway:\n\t\ts = append(s, \" (going away)\"...)\n\tcase CloseProtocolError:\n\t\ts = append(s, \" (protocol error)\"...)\n\tcase CloseUnsupportedData:\n\t\ts = append(s, \" (unsupported data)\"...)\n\tcase CloseNoStatusReceived:\n\t\ts = append(s, \" (no status)\"...)\n\tcase CloseAbnormalClosure:\n\t\ts = append(s, \" (abnormal closure)\"...)\n\tcase CloseInvalidFramePayloadData:\n\t\ts = append(s, \" (invalid payload data)\"...)\n\tcase ClosePolicyViolation:\n\t\ts = append(s, \" (policy violation)\"...)\n\tcase CloseMessageTooBig:\n\t\ts = append(s, \" (message too big)\"...)\n\tcase CloseMandatoryExtension:\n\t\ts = append(s, \" (mandatory extension missing)\"...)\n\tcase CloseInternalServerErr:\n\t\ts = append(s, \" (internal server error)\"...)\n\tcase CloseTLSHandshake:\n\t\ts = append(s, \" (TLS handshake error)\"...)\n\t}\n\tif e.Text != \"\" {\n\t\ts = append(s, \": \"...)\n\t\ts = append(s, e.Text...)\n\t}\n\treturn string(s)\n}\n\n// IsCloseError returns boolean indicating whether the error is a *CloseError\n// with one of the specified codes.\nfunc IsCloseError(err error, codes ...int) bool {\n\tif e, ok := err.(*CloseError); ok {\n\t\tfor _, code := range codes {\n\t\t\tif e.Code == code {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\treturn false\n}\n\n// IsUnexpectedCloseError returns boolean indicating whether the error is a\n// *CloseError with a code not in the list of expected codes.\nfunc IsUnexpectedCloseError(err error, expectedCodes ...int) bool {\n\tif e, ok := err.(*CloseError); ok {\n\t\tfor _, code := range expectedCodes {\n\t\t\tif e.Code == code {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\treturn true\n\t}\n\treturn false\n}\n\nvar (\n\terrWriteTimeout        = &netError{msg: \"websocket: write timeout\", timeout: true, temporary: true}\n\terrUnexpectedEOF       = &CloseError{Code: CloseAbnormalClosure, Text: io.ErrUnexpectedEOF.Error()}\n\terrBadWriteOpCode      = errors.New(\"websocket: bad write message type\")\n\terrWriteClosed         = errors.New(\"websocket: write closed\")\n\terrInvalidControlFrame = errors.New(\"websocket: invalid control frame\")\n)\n\nfunc newMaskKey() [4]byte {\n\tn := rand.Uint32()\n\treturn [4]byte{byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24)}\n}\n\nfunc hideTempErr(err error) error {\n\tif e, ok := err.(net.Error); ok && e.Temporary() {\n\t\terr = &netError{msg: e.Error(), timeout: e.Timeout()}\n\t}\n\treturn err\n}\n\nfunc isControl(frameType int) bool {\n\treturn frameType == CloseMessage || frameType == PingMessage || frameType == PongMessage\n}\n\nfunc isData(frameType int) bool {\n\treturn frameType == TextMessage || frameType == BinaryMessage\n}\n\nvar validReceivedCloseCodes = map[int]bool{\n\t// see http://www.iana.org/assignments/websocket/websocket.xhtml#close-code-number\n\n\tCloseNormalClosure:           true,\n\tCloseGoingAway:               true,\n\tCloseProtocolError:           true,\n\tCloseUnsupportedData:         true,\n\tCloseNoStatusReceived:        false,\n\tCloseAbnormalClosure:         false,\n\tCloseInvalidFramePayloadData: true,\n\tClosePolicyViolation:         true,\n\tCloseMessageTooBig:           true,\n\tCloseMandatoryExtension:      true,\n\tCloseInternalServerErr:       true,\n\tCloseServiceRestart:          true,\n\tCloseTryAgainLater:           true,\n\tCloseTLSHandshake:            false,\n}\n\nfunc isValidReceivedCloseCode(code int) bool {\n\treturn validReceivedCloseCodes[code] || (code >= 3000 && code <= 4999)\n}\n\n// BufferPool represents a pool of buffers. The *sync.Pool type satisfies this\n// interface.  The type of the value stored in a pool is not specified.\ntype BufferPool interface {\n\t// Get gets a value from the pool or returns nil if the pool is empty.\n\tGet() interface{}\n\t// Put adds a value to the pool.\n\tPut(interface{})\n}\n\n// writePoolData is the type added to the write buffer pool. This wrapper is\n// used to prevent applications from peeking at and depending on the values\n// added to the pool.\ntype writePoolData struct{ buf []byte }\n\n// The Conn type represents a WebSocket connection.\ntype Conn struct {\n\tconn        net.Conn\n\tisServer    bool\n\tsubprotocol string\n\n\t// Write fields\n\tmu            chan bool // used as mutex to protect write to conn\n\twriteBuf      []byte    // frame is constructed in this buffer.\n\twritePool     BufferPool\n\twriteBufSize  int\n\twriteDeadline time.Time\n\twriter        io.WriteCloser // the current writer returned to the application\n\tisWriting     bool           // for best-effort concurrent write detection\n\n\twriteErrMu sync.Mutex\n\twriteErr   error\n\n\tenableWriteCompression bool\n\tcompressionLevel       int\n\tnewCompressionWriter   func(io.WriteCloser, int) io.WriteCloser\n\n\t// Read fields\n\treader        io.ReadCloser // the current reader returned to the application\n\treadErr       error\n\tbr            *bufio.Reader\n\treadRemaining int64 // bytes remaining in current frame.\n\treadFinal     bool  // true the current message has more frames.\n\treadLength    int64 // Message size.\n\treadLimit     int64 // Maximum message size.\n\treadMaskPos   int\n\treadMaskKey   [4]byte\n\thandlePong    func(string) error\n\thandlePing    func(string) error\n\thandleClose   func(int, string) error\n\treadErrCount  int\n\tmessageReader *messageReader // the current low-level reader\n\n\treadDecompress         bool // whether last read frame had RSV1 set\n\tnewDecompressionReader func(io.Reader) io.ReadCloser\n}\n\nfunc newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int, writeBufferPool BufferPool, br *bufio.Reader, writeBuf []byte) *Conn {\n\n\tif br == nil {\n\t\tif readBufferSize == 0 {\n\t\t\treadBufferSize = defaultReadBufferSize\n\t\t} else if readBufferSize < maxControlFramePayloadSize {\n\t\t\t// must be large enough for control frame\n\t\t\treadBufferSize = maxControlFramePayloadSize\n\t\t}\n\t\tbr = bufio.NewReaderSize(conn, readBufferSize)\n\t}\n\n\tif writeBufferSize <= 0 {\n\t\twriteBufferSize = defaultWriteBufferSize\n\t}\n\twriteBufferSize += maxFrameHeaderSize\n\n\tif writeBuf == nil && writeBufferPool == nil {\n\t\twriteBuf = make([]byte, writeBufferSize)\n\t}\n\n\tmu := make(chan bool, 1)\n\tmu <- true\n\tc := &Conn{\n\t\tisServer:               isServer,\n\t\tbr:                     br,\n\t\tconn:                   conn,\n\t\tmu:                     mu,\n\t\treadFinal:              true,\n\t\twriteBuf:               writeBuf,\n\t\twritePool:              writeBufferPool,\n\t\twriteBufSize:           writeBufferSize,\n\t\tenableWriteCompression: true,\n\t\tcompressionLevel:       defaultCompressionLevel,\n\t}\n\tc.SetCloseHandler(nil)\n\tc.SetPingHandler(nil)\n\tc.SetPongHandler(nil)\n\treturn c\n}\n\n// Subprotocol returns the negotiated protocol for the connection.\nfunc (c *Conn) Subprotocol() string {\n\treturn c.subprotocol\n}\n\n// Close closes the underlying network connection without sending or waiting\n// for a close message.\nfunc (c *Conn) Close() error {\n\treturn c.conn.Close()\n}\n\n// LocalAddr returns the local network address.\nfunc (c *Conn) LocalAddr() net.Addr {\n\treturn c.conn.LocalAddr()\n}\n\n// RemoteAddr returns the remote network address.\nfunc (c *Conn) RemoteAddr() net.Addr {\n\treturn c.conn.RemoteAddr()\n}\n\n// Write methods\n\nfunc (c *Conn) writeFatal(err error) error {\n\terr = hideTempErr(err)\n\tc.writeErrMu.Lock()\n\tif c.writeErr == nil {\n\t\tc.writeErr = err\n\t}\n\tc.writeErrMu.Unlock()\n\treturn err\n}\n\nfunc (c *Conn) read(n int) ([]byte, error) {\n\tp, err := c.br.Peek(n)\n\tif err == io.EOF {\n\t\terr = errUnexpectedEOF\n\t}\n\tc.br.Discard(len(p))\n\treturn p, err\n}\n\nfunc (c *Conn) write(frameType int, deadline time.Time, buf0, buf1 []byte) error {\n\t<-c.mu\n\tdefer func() { c.mu <- true }()\n\n\tc.writeErrMu.Lock()\n\terr := c.writeErr\n\tc.writeErrMu.Unlock()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tc.conn.SetWriteDeadline(deadline)\n\tif len(buf1) == 0 {\n\t\t_, err = c.conn.Write(buf0)\n\t} else {\n\t\terr = c.writeBufs(buf0, buf1)\n\t}\n\tif err != nil {\n\t\treturn c.writeFatal(err)\n\t}\n\tif frameType == CloseMessage {\n\t\tc.writeFatal(ErrCloseSent)\n\t}\n\treturn nil\n}\n\n// WriteControl writes a control message with the given deadline. The allowed\n// message types are CloseMessage, PingMessage and PongMessage.\nfunc (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) error {\n\tif !isControl(messageType) {\n\t\treturn errBadWriteOpCode\n\t}\n\tif len(data) > maxControlFramePayloadSize {\n\t\treturn errInvalidControlFrame\n\t}\n\n\tb0 := byte(messageType) | finalBit\n\tb1 := byte(len(data))\n\tif !c.isServer {\n\t\tb1 |= maskBit\n\t}\n\n\tbuf := make([]byte, 0, maxFrameHeaderSize+maxControlFramePayloadSize)\n\tbuf = append(buf, b0, b1)\n\n\tif c.isServer {\n\t\tbuf = append(buf, data...)\n\t} else {\n\t\tkey := newMaskKey()\n\t\tbuf = append(buf, key[:]...)\n\t\tbuf = append(buf, data...)\n\t\tmaskBytes(key, 0, buf[6:])\n\t}\n\n\td := time.Hour * 1000\n\tif !deadline.IsZero() {\n\t\td = deadline.Sub(time.Now())\n\t\tif d < 0 {\n\t\t\treturn errWriteTimeout\n\t\t}\n\t}\n\n\ttimer := time.NewTimer(d)\n\tselect {\n\tcase <-c.mu:\n\t\ttimer.Stop()\n\tcase <-timer.C:\n\t\treturn errWriteTimeout\n\t}\n\tdefer func() { c.mu <- true }()\n\n\tc.writeErrMu.Lock()\n\terr := c.writeErr\n\tc.writeErrMu.Unlock()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tc.conn.SetWriteDeadline(deadline)\n\t_, err = c.conn.Write(buf)\n\tif err != nil {\n\t\treturn c.writeFatal(err)\n\t}\n\tif messageType == CloseMessage {\n\t\tc.writeFatal(ErrCloseSent)\n\t}\n\treturn err\n}\n\nfunc (c *Conn) prepWrite(messageType int) error {\n\t// Close previous writer if not already closed by the application. It's\n\t// probably better to return an error in this situation, but we cannot\n\t// change this without breaking existing applications.\n\tif c.writer != nil {\n\t\tc.writer.Close()\n\t\tc.writer = nil\n\t}\n\n\tif !isControl(messageType) && !isData(messageType) {\n\t\treturn errBadWriteOpCode\n\t}\n\n\tc.writeErrMu.Lock()\n\terr := c.writeErr\n\tc.writeErrMu.Unlock()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif c.writeBuf == nil {\n\t\twpd, ok := c.writePool.Get().(writePoolData)\n\t\tif ok {\n\t\t\tc.writeBuf = wpd.buf\n\t\t} else {\n\t\t\tc.writeBuf = make([]byte, c.writeBufSize)\n\t\t}\n\t}\n\treturn nil\n}\n\n// NextWriter returns a writer for the next message to send. The writer's Close\n// method flushes the complete message to the network.\n//\n// There can be at most one open writer on a connection. NextWriter closes the\n// previous writer if the application has not already done so.\n//\n// All message types (TextMessage, BinaryMessage, CloseMessage, PingMessage and\n// PongMessage) are supported.\nfunc (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) {\n\tif err := c.prepWrite(messageType); err != nil {\n\t\treturn nil, err\n\t}\n\n\tmw := &messageWriter{\n\t\tc:         c,\n\t\tframeType: messageType,\n\t\tpos:       maxFrameHeaderSize,\n\t}\n\tc.writer = mw\n\tif c.newCompressionWriter != nil && c.enableWriteCompression && isData(messageType) {\n\t\tw := c.newCompressionWriter(c.writer, c.compressionLevel)\n\t\tmw.compress = true\n\t\tc.writer = w\n\t}\n\treturn c.writer, nil\n}\n\ntype messageWriter struct {\n\tc         *Conn\n\tcompress  bool // whether next call to flushFrame should set RSV1\n\tpos       int  // end of data in writeBuf.\n\tframeType int  // type of the current frame.\n\terr       error\n}\n\nfunc (w *messageWriter) fatal(err error) error {\n\tif w.err != nil {\n\t\tw.err = err\n\t\tw.c.writer = nil\n\t}\n\treturn err\n}\n\n// flushFrame writes buffered data and extra as a frame to the network. The\n// final argument indicates that this is the last frame in the message.\nfunc (w *messageWriter) flushFrame(final bool, extra []byte) error {\n\tc := w.c\n\tlength := w.pos - maxFrameHeaderSize + len(extra)\n\n\t// Check for invalid control frames.\n\tif isControl(w.frameType) &&\n\t\t(!final || length > maxControlFramePayloadSize) {\n\t\treturn w.fatal(errInvalidControlFrame)\n\t}\n\n\tb0 := byte(w.frameType)\n\tif final {\n\t\tb0 |= finalBit\n\t}\n\tif w.compress {\n\t\tb0 |= rsv1Bit\n\t}\n\tw.compress = false\n\n\tb1 := byte(0)\n\tif !c.isServer {\n\t\tb1 |= maskBit\n\t}\n\n\t// Assume that the frame starts at beginning of c.writeBuf.\n\tframePos := 0\n\tif c.isServer {\n\t\t// Adjust up if mask not included in the header.\n\t\tframePos = 4\n\t}\n\n\tswitch {\n\tcase length >= 65536:\n\t\tc.writeBuf[framePos] = b0\n\t\tc.writeBuf[framePos+1] = b1 | 127\n\t\tbinary.BigEndian.PutUint64(c.writeBuf[framePos+2:], uint64(length))\n\tcase length > 125:\n\t\tframePos += 6\n\t\tc.writeBuf[framePos] = b0\n\t\tc.writeBuf[framePos+1] = b1 | 126\n\t\tbinary.BigEndian.PutUint16(c.writeBuf[framePos+2:], uint16(length))\n\tdefault:\n\t\tframePos += 8\n\t\tc.writeBuf[framePos] = b0\n\t\tc.writeBuf[framePos+1] = b1 | byte(length)\n\t}\n\n\tif !c.isServer {\n\t\tkey := newMaskKey()\n\t\tcopy(c.writeBuf[maxFrameHeaderSize-4:], key[:])\n\t\tmaskBytes(key, 0, c.writeBuf[maxFrameHeaderSize:w.pos])\n\t\tif len(extra) > 0 {\n\t\t\treturn c.writeFatal(errors.New(\"websocket: internal error, extra used in client mode\"))\n\t\t}\n\t}\n\n\t// Write the buffers to the connection with best-effort detection of\n\t// concurrent writes. See the concurrency section in the package\n\t// documentation for more info.\n\n\tif c.isWriting {\n\t\tpanic(\"concurrent write to websocket connection\")\n\t}\n\tc.isWriting = true\n\n\terr := c.write(w.frameType, c.writeDeadline, c.writeBuf[framePos:w.pos], extra)\n\n\tif !c.isWriting {\n\t\tpanic(\"concurrent write to websocket connection\")\n\t}\n\tc.isWriting = false\n\n\tif err != nil {\n\t\treturn w.fatal(err)\n\t}\n\n\tif final {\n\t\tc.writer = nil\n\t\tif c.writePool != nil {\n\t\t\tc.writePool.Put(writePoolData{buf: c.writeBuf})\n\t\t\tc.writeBuf = nil\n\t\t}\n\t\treturn nil\n\t}\n\n\t// Setup for next frame.\n\tw.pos = maxFrameHeaderSize\n\tw.frameType = continuationFrame\n\treturn nil\n}\n\nfunc (w *messageWriter) ncopy(max int) (int, error) {\n\tn := len(w.c.writeBuf) - w.pos\n\tif n <= 0 {\n\t\tif err := w.flushFrame(false, nil); err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tn = len(w.c.writeBuf) - w.pos\n\t}\n\tif n > max {\n\t\tn = max\n\t}\n\treturn n, nil\n}\n\nfunc (w *messageWriter) Write(p []byte) (int, error) {\n\tif w.err != nil {\n\t\treturn 0, w.err\n\t}\n\n\tif len(p) > 2*len(w.c.writeBuf) && w.c.isServer {\n\t\t// Don't buffer large messages.\n\t\terr := w.flushFrame(false, p)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\treturn len(p), nil\n\t}\n\n\tnn := len(p)\n\tfor len(p) > 0 {\n\t\tn, err := w.ncopy(len(p))\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tcopy(w.c.writeBuf[w.pos:], p[:n])\n\t\tw.pos += n\n\t\tp = p[n:]\n\t}\n\treturn nn, nil\n}\n\nfunc (w *messageWriter) WriteString(p string) (int, error) {\n\tif w.err != nil {\n\t\treturn 0, w.err\n\t}\n\n\tnn := len(p)\n\tfor len(p) > 0 {\n\t\tn, err := w.ncopy(len(p))\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tcopy(w.c.writeBuf[w.pos:], p[:n])\n\t\tw.pos += n\n\t\tp = p[n:]\n\t}\n\treturn nn, nil\n}\n\nfunc (w *messageWriter) ReadFrom(r io.Reader) (nn int64, err error) {\n\tif w.err != nil {\n\t\treturn 0, w.err\n\t}\n\tfor {\n\t\tif w.pos == len(w.c.writeBuf) {\n\t\t\terr = w.flushFrame(false, nil)\n\t\t\tif err != nil {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tvar n int\n\t\tn, err = r.Read(w.c.writeBuf[w.pos:])\n\t\tw.pos += n\n\t\tnn += int64(n)\n\t\tif err != nil {\n\t\t\tif err == io.EOF {\n\t\t\t\terr = nil\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t}\n\treturn nn, err\n}\n\nfunc (w *messageWriter) Close() error {\n\tif w.err != nil {\n\t\treturn w.err\n\t}\n\tif err := w.flushFrame(true, nil); err != nil {\n\t\treturn err\n\t}\n\tw.err = errWriteClosed\n\treturn nil\n}\n\n// WritePreparedMessage writes prepared message into connection.\nfunc (c *Conn) WritePreparedMessage(pm *PreparedMessage) error {\n\tframeType, frameData, err := pm.frame(prepareKey{\n\t\tisServer:         c.isServer,\n\t\tcompress:         c.newCompressionWriter != nil && c.enableWriteCompression && isData(pm.messageType),\n\t\tcompressionLevel: c.compressionLevel,\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\tif c.isWriting {\n\t\tpanic(\"concurrent write to websocket connection\")\n\t}\n\tc.isWriting = true\n\terr = c.write(frameType, c.writeDeadline, frameData, nil)\n\tif !c.isWriting {\n\t\tpanic(\"concurrent write to websocket connection\")\n\t}\n\tc.isWriting = false\n\treturn err\n}\n\n// WriteMessage is a helper method for getting a writer using NextWriter,\n// writing the message and closing the writer.\nfunc (c *Conn) WriteMessage(messageType int, data []byte) error {\n\n\tif c.isServer && (c.newCompressionWriter == nil || !c.enableWriteCompression) {\n\t\t// Fast path with no allocations and single frame.\n\n\t\tif err := c.prepWrite(messageType); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tmw := messageWriter{c: c, frameType: messageType, pos: maxFrameHeaderSize}\n\t\tn := copy(c.writeBuf[mw.pos:], data)\n\t\tmw.pos += n\n\t\tdata = data[n:]\n\t\treturn mw.flushFrame(true, data)\n\t}\n\n\tw, err := c.NextWriter(messageType)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif _, err = w.Write(data); err != nil {\n\t\treturn err\n\t}\n\treturn w.Close()\n}\n\n// SetWriteDeadline sets the write deadline on the underlying network\n// connection. After a write has timed out, the websocket state is corrupt and\n// all future writes will return an error. A zero value for t means writes will\n// not time out.\nfunc (c *Conn) SetWriteDeadline(t time.Time) error {\n\tc.writeDeadline = t\n\treturn nil\n}\n\n// Read methods\n\nfunc (c *Conn) advanceFrame() (int, error) {\n\t// 1. Skip remainder of previous frame.\n\n\tif c.readRemaining > 0 {\n\t\tif _, err := io.CopyN(ioutil.Discard, c.br, c.readRemaining); err != nil {\n\t\t\treturn noFrame, err\n\t\t}\n\t}\n\n\t// 2. Read and parse first two bytes of frame header.\n\n\tp, err := c.read(2)\n\tif err != nil {\n\t\treturn noFrame, err\n\t}\n\n\tfinal := p[0]&finalBit != 0\n\tframeType := int(p[0] & 0xf)\n\tmask := p[1]&maskBit != 0\n\tc.readRemaining = int64(p[1] & 0x7f)\n\n\tc.readDecompress = false\n\tif c.newDecompressionReader != nil && (p[0]&rsv1Bit) != 0 {\n\t\tc.readDecompress = true\n\t\tp[0] &^= rsv1Bit\n\t}\n\n\tif rsv := p[0] & (rsv1Bit | rsv2Bit | rsv3Bit); rsv != 0 {\n\t\treturn noFrame, c.handleProtocolError(\"unexpected reserved bits 0x\" + strconv.FormatInt(int64(rsv), 16))\n\t}\n\n\tswitch frameType {\n\tcase CloseMessage, PingMessage, PongMessage:\n\t\tif c.readRemaining > maxControlFramePayloadSize {\n\t\t\treturn noFrame, c.handleProtocolError(\"control frame length > 125\")\n\t\t}\n\t\tif !final {\n\t\t\treturn noFrame, c.handleProtocolError(\"control frame not final\")\n\t\t}\n\tcase TextMessage, BinaryMessage:\n\t\tif !c.readFinal {\n\t\t\treturn noFrame, c.handleProtocolError(\"message start before final message frame\")\n\t\t}\n\t\tc.readFinal = final\n\tcase continuationFrame:\n\t\tif c.readFinal {\n\t\t\treturn noFrame, c.handleProtocolError(\"continuation after final message frame\")\n\t\t}\n\t\tc.readFinal = final\n\tdefault:\n\t\treturn noFrame, c.handleProtocolError(\"unknown opcode \" + strconv.Itoa(frameType))\n\t}\n\n\t// 3. Read and parse frame length.\n\n\tswitch c.readRemaining {\n\tcase 126:\n\t\tp, err := c.read(2)\n\t\tif err != nil {\n\t\t\treturn noFrame, err\n\t\t}\n\t\tc.readRemaining = int64(binary.BigEndian.Uint16(p))\n\tcase 127:\n\t\tp, err := c.read(8)\n\t\tif err != nil {\n\t\t\treturn noFrame, err\n\t\t}\n\t\tc.readRemaining = int64(binary.BigEndian.Uint64(p))\n\t}\n\n\t// 4. Handle frame masking.\n\n\tif mask != c.isServer {\n\t\treturn noFrame, c.handleProtocolError(\"incorrect mask flag\")\n\t}\n\n\tif mask {\n\t\tc.readMaskPos = 0\n\t\tp, err := c.read(len(c.readMaskKey))\n\t\tif err != nil {\n\t\t\treturn noFrame, err\n\t\t}\n\t\tcopy(c.readMaskKey[:], p)\n\t}\n\n\t// 5. For text and binary messages, enforce read limit and return.\n\n\tif frameType == continuationFrame || frameType == TextMessage || frameType == BinaryMessage {\n\n\t\tc.readLength += c.readRemaining\n\t\tif c.readLimit > 0 && c.readLength > c.readLimit {\n\t\t\tc.WriteControl(CloseMessage, FormatCloseMessage(CloseMessageTooBig, \"\"), time.Now().Add(writeWait))\n\t\t\treturn noFrame, ErrReadLimit\n\t\t}\n\n\t\treturn frameType, nil\n\t}\n\n\t// 6. Read control frame payload.\n\n\tvar payload []byte\n\tif c.readRemaining > 0 {\n\t\tpayload, err = c.read(int(c.readRemaining))\n\t\tc.readRemaining = 0\n\t\tif err != nil {\n\t\t\treturn noFrame, err\n\t\t}\n\t\tif c.isServer {\n\t\t\tmaskBytes(c.readMaskKey, 0, payload)\n\t\t}\n\t}\n\n\t// 7. Process control frame payload.\n\n\tswitch frameType {\n\tcase PongMessage:\n\t\tif err := c.handlePong(string(payload)); err != nil {\n\t\t\treturn noFrame, err\n\t\t}\n\tcase PingMessage:\n\t\tif err := c.handlePing(string(payload)); err != nil {\n\t\t\treturn noFrame, err\n\t\t}\n\tcase CloseMessage:\n\t\tcloseCode := CloseNoStatusReceived\n\t\tcloseText := \"\"\n\t\tif len(payload) >= 2 {\n\t\t\tcloseCode = int(binary.BigEndian.Uint16(payload))\n\t\t\tif !isValidReceivedCloseCode(closeCode) {\n\t\t\t\treturn noFrame, c.handleProtocolError(\"invalid close code\")\n\t\t\t}\n\t\t\tcloseText = string(payload[2:])\n\t\t\tif !utf8.ValidString(closeText) {\n\t\t\t\treturn noFrame, c.handleProtocolError(\"invalid utf8 payload in close frame\")\n\t\t\t}\n\t\t}\n\t\tif err := c.handleClose(closeCode, closeText); err != nil {\n\t\t\treturn noFrame, err\n\t\t}\n\t\treturn noFrame, &CloseError{Code: closeCode, Text: closeText}\n\t}\n\n\treturn frameType, nil\n}\n\nfunc (c *Conn) handleProtocolError(message string) error {\n\tc.WriteControl(CloseMessage, FormatCloseMessage(CloseProtocolError, message), time.Now().Add(writeWait))\n\treturn errors.New(\"websocket: \" + message)\n}\n\n// NextReader returns the next data message received from the peer. The\n// returned messageType is either TextMessage or BinaryMessage.\n//\n// There can be at most one open reader on a connection. NextReader discards\n// the previous message if the application has not already consumed it.\n//\n// Applications must break out of the application's read loop when this method\n// returns a non-nil error value. Errors returned from this method are\n// permanent. Once this method returns a non-nil error, all subsequent calls to\n// this method return the same error.\nfunc (c *Conn) NextReader() (messageType int, r io.Reader, err error) {\n\t// Close previous reader, only relevant for decompression.\n\tif c.reader != nil {\n\t\tc.reader.Close()\n\t\tc.reader = nil\n\t}\n\n\tc.messageReader = nil\n\tc.readLength = 0\n\n\tfor c.readErr == nil {\n\t\tframeType, err := c.advanceFrame()\n\t\tif err != nil {\n\t\t\tc.readErr = hideTempErr(err)\n\t\t\tbreak\n\t\t}\n\t\tif frameType == TextMessage || frameType == BinaryMessage {\n\t\t\tc.messageReader = &messageReader{c}\n\t\t\tc.reader = c.messageReader\n\t\t\tif c.readDecompress {\n\t\t\t\tc.reader = c.newDecompressionReader(c.reader)\n\t\t\t}\n\t\t\treturn frameType, c.reader, nil\n\t\t}\n\t}\n\n\t// Applications that do handle the error returned from this method spin in\n\t// tight loop on connection failure. To help application developers detect\n\t// this error, panic on repeated reads to the failed connection.\n\tc.readErrCount++\n\tif c.readErrCount >= 1000 {\n\t\tpanic(\"repeated read on failed websocket connection\")\n\t}\n\n\treturn noFrame, nil, c.readErr\n}\n\ntype messageReader struct{ c *Conn }\n\nfunc (r *messageReader) Read(b []byte) (int, error) {\n\tc := r.c\n\tif c.messageReader != r {\n\t\treturn 0, io.EOF\n\t}\n\n\tfor c.readErr == nil {\n\n\t\tif c.readRemaining > 0 {\n\t\t\tif int64(len(b)) > c.readRemaining {\n\t\t\t\tb = b[:c.readRemaining]\n\t\t\t}\n\t\t\tn, err := c.br.Read(b)\n\t\t\tc.readErr = hideTempErr(err)\n\t\t\tif c.isServer {\n\t\t\t\tc.readMaskPos = maskBytes(c.readMaskKey, c.readMaskPos, b[:n])\n\t\t\t}\n\t\t\tc.readRemaining -= int64(n)\n\t\t\tif c.readRemaining > 0 && c.readErr == io.EOF {\n\t\t\t\tc.readErr = errUnexpectedEOF\n\t\t\t}\n\t\t\treturn n, c.readErr\n\t\t}\n\n\t\tif c.readFinal {\n\t\t\tc.messageReader = nil\n\t\t\treturn 0, io.EOF\n\t\t}\n\n\t\tframeType, err := c.advanceFrame()\n\t\tswitch {\n\t\tcase err != nil:\n\t\t\tc.readErr = hideTempErr(err)\n\t\tcase frameType == TextMessage || frameType == BinaryMessage:\n\t\t\tc.readErr = errors.New(\"websocket: internal error, unexpected text or binary in Reader\")\n\t\t}\n\t}\n\n\terr := c.readErr\n\tif err == io.EOF && c.messageReader == r {\n\t\terr = errUnexpectedEOF\n\t}\n\treturn 0, err\n}\n\nfunc (r *messageReader) Close() error {\n\treturn nil\n}\n\n// ReadMessage is a helper method for getting a reader using NextReader and\n// reading from that reader to a buffer.\nfunc (c *Conn) ReadMessage() (messageType int, p []byte, err error) {\n\tvar r io.Reader\n\tmessageType, r, err = c.NextReader()\n\tif err != nil {\n\t\treturn messageType, nil, err\n\t}\n\tp, err = ioutil.ReadAll(r)\n\treturn messageType, p, err\n}\n\n// SetReadDeadline sets the read deadline on the underlying network connection.\n// After a read has timed out, the websocket connection state is corrupt and\n// all future reads will return an error. A zero value for t means reads will\n// not time out.\nfunc (c *Conn) SetReadDeadline(t time.Time) error {\n\treturn c.conn.SetReadDeadline(t)\n}\n\n// SetReadLimit sets the maximum size for a message read from the peer. If a\n// message exceeds the limit, the connection sends a close message to the peer\n// and returns ErrReadLimit to the application.\nfunc (c *Conn) SetReadLimit(limit int64) {\n\tc.readLimit = limit\n}\n\n// CloseHandler returns the current close handler\nfunc (c *Conn) CloseHandler() func(code int, text string) error {\n\treturn c.handleClose\n}\n\n// SetCloseHandler sets the handler for close messages received from the peer.\n// The code argument to h is the received close code or CloseNoStatusReceived\n// if the close message is empty. The default close handler sends a close\n// message back to the peer.\n//\n// The handler function is called from the NextReader, ReadMessage and message\n// reader Read methods. The application must read the connection to process\n// close messages as described in the section on Control Messages above.\n//\n// The connection read methods return a CloseError when a close message is\n// received. Most applications should handle close messages as part of their\n// normal error handling. Applications should only set a close handler when the\n// application must perform some action before sending a close message back to\n// the peer.\nfunc (c *Conn) SetCloseHandler(h func(code int, text string) error) {\n\tif h == nil {\n\t\th = func(code int, text string) error {\n\t\t\tmessage := FormatCloseMessage(code, \"\")\n\t\t\tc.WriteControl(CloseMessage, message, time.Now().Add(writeWait))\n\t\t\treturn nil\n\t\t}\n\t}\n\tc.handleClose = h\n}\n\n// PingHandler returns the current ping handler\nfunc (c *Conn) PingHandler() func(appData string) error {\n\treturn c.handlePing\n}\n\n// SetPingHandler sets the handler for ping messages received from the peer.\n// The appData argument to h is the PING message application data. The default\n// ping handler sends a pong to the peer.\n//\n// The handler function is called from the NextReader, ReadMessage and message\n// reader Read methods. The application must read the connection to process\n// ping messages as described in the section on Control Messages above.\nfunc (c *Conn) SetPingHandler(h func(appData string) error) {\n\tif h == nil {\n\t\th = func(message string) error {\n\t\t\terr := c.WriteControl(PongMessage, []byte(message), time.Now().Add(writeWait))\n\t\t\tif err == ErrCloseSent {\n\t\t\t\treturn nil\n\t\t\t} else if e, ok := err.(net.Error); ok && e.Temporary() {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t}\n\tc.handlePing = h\n}\n\n// PongHandler returns the current pong handler\nfunc (c *Conn) PongHandler() func(appData string) error {\n\treturn c.handlePong\n}\n\n// SetPongHandler sets the handler for pong messages received from the peer.\n// The appData argument to h is the PONG message application data. The default\n// pong handler does nothing.\n//\n// The handler function is called from the NextReader, ReadMessage and message\n// reader Read methods. The application must read the connection to process\n// pong messages as described in the section on Control Messages above.\nfunc (c *Conn) SetPongHandler(h func(appData string) error) {\n\tif h == nil {\n\t\th = func(string) error { return nil }\n\t}\n\tc.handlePong = h\n}\n\n// UnderlyingConn returns the internal net.Conn. This can be used to further\n// modifications to connection specific flags.\nfunc (c *Conn) UnderlyingConn() net.Conn {\n\treturn c.conn\n}\n\n// EnableWriteCompression enables and disables write compression of\n// subsequent text and binary messages. This function is a noop if\n// compression was not negotiated with the peer.\nfunc (c *Conn) EnableWriteCompression(enable bool) {\n\tc.enableWriteCompression = enable\n}\n\n// SetCompressionLevel sets the flate compression level for subsequent text and\n// binary messages. This function is a noop if compression was not negotiated\n// with the peer. See the compress/flate package for a description of\n// compression levels.\nfunc (c *Conn) SetCompressionLevel(level int) error {\n\tif !isValidCompressionLevel(level) {\n\t\treturn errors.New(\"websocket: invalid compression level\")\n\t}\n\tc.compressionLevel = level\n\treturn nil\n}\n\n// FormatCloseMessage formats closeCode and text as a WebSocket close message.\n// An empty message is returned for code CloseNoStatusReceived.\nfunc FormatCloseMessage(closeCode int, text string) []byte {\n\tif closeCode == CloseNoStatusReceived {\n\t\t// Return empty message because it's illegal to send\n\t\t// CloseNoStatusReceived. Return non-nil value in case application\n\t\t// checks for nil.\n\t\treturn []byte{}\n\t}\n\tbuf := make([]byte, 2+len(text))\n\tbinary.BigEndian.PutUint16(buf, uint16(closeCode))\n\tcopy(buf[2:], text)\n\treturn buf\n}\n"
  },
  {
    "path": "websockets/v2/vendor/github.com/gorilla/websocket/conn_write.go",
    "content": "// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// +build go1.8\n\npackage websocket\n\nimport \"net\"\n\nfunc (c *Conn) writeBufs(bufs ...[]byte) error {\n\tb := net.Buffers(bufs)\n\t_, err := b.WriteTo(c.conn)\n\treturn err\n}\n"
  },
  {
    "path": "websockets/v2/vendor/github.com/gorilla/websocket/conn_write_legacy.go",
    "content": "// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// +build !go1.8\n\npackage websocket\n\nfunc (c *Conn) writeBufs(bufs ...[]byte) error {\n\tfor _, buf := range bufs {\n\t\tif len(buf) > 0 {\n\t\t\tif _, err := c.conn.Write(buf); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "websockets/v2/vendor/github.com/gorilla/websocket/doc.go",
    "content": "// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// Package websocket implements the WebSocket protocol defined in RFC 6455.\n//\n// Overview\n//\n// The Conn type represents a WebSocket connection. A server application calls\n// the Upgrader.Upgrade method from an HTTP request handler to get a *Conn:\n//\n//  var upgrader = websocket.Upgrader{\n//      ReadBufferSize:  1024,\n//      WriteBufferSize: 1024,\n//  }\n//\n//  func handler(w http.ResponseWriter, r *http.Request) {\n//      conn, err := upgrader.Upgrade(w, r, nil)\n//      if err != nil {\n//          log.Println(err)\n//          return\n//      }\n//      ... Use conn to send and receive messages.\n//  }\n//\n// Call the connection's WriteMessage and ReadMessage methods to send and\n// receive messages as a slice of bytes. This snippet of code shows how to echo\n// messages using these methods:\n//\n//  for {\n//      messageType, p, err := conn.ReadMessage()\n//      if err != nil {\n//          log.Println(err)\n//          return\n//      }\n//      if err := conn.WriteMessage(messageType, p); err != nil {\n//          log.Println(err)\n//          return\n//      }\n//  }\n//\n// In above snippet of code, p is a []byte and messageType is an int with value\n// websocket.BinaryMessage or websocket.TextMessage.\n//\n// An application can also send and receive messages using the io.WriteCloser\n// and io.Reader interfaces. To send a message, call the connection NextWriter\n// method to get an io.WriteCloser, write the message to the writer and close\n// the writer when done. To receive a message, call the connection NextReader\n// method to get an io.Reader and read until io.EOF is returned. This snippet\n// shows how to echo messages using the NextWriter and NextReader methods:\n//\n//  for {\n//      messageType, r, err := conn.NextReader()\n//      if err != nil {\n//          return\n//      }\n//      w, err := conn.NextWriter(messageType)\n//      if err != nil {\n//          return err\n//      }\n//      if _, err := io.Copy(w, r); err != nil {\n//          return err\n//      }\n//      if err := w.Close(); err != nil {\n//          return err\n//      }\n//  }\n//\n// Data Messages\n//\n// The WebSocket protocol distinguishes between text and binary data messages.\n// Text messages are interpreted as UTF-8 encoded text. The interpretation of\n// binary messages is left to the application.\n//\n// This package uses the TextMessage and BinaryMessage integer constants to\n// identify the two data message types. The ReadMessage and NextReader methods\n// return the type of the received message. The messageType argument to the\n// WriteMessage and NextWriter methods specifies the type of a sent message.\n//\n// It is the application's responsibility to ensure that text messages are\n// valid UTF-8 encoded text.\n//\n// Control Messages\n//\n// The WebSocket protocol defines three types of control messages: close, ping\n// and pong. Call the connection WriteControl, WriteMessage or NextWriter\n// methods to send a control message to the peer.\n//\n// Connections handle received close messages by calling the handler function\n// set with the SetCloseHandler method and by returning a *CloseError from the\n// NextReader, ReadMessage or the message Read method. The default close\n// handler sends a close message to the peer.\n//\n// Connections handle received ping messages by calling the handler function\n// set with the SetPingHandler method. The default ping handler sends a pong\n// message to the peer.\n//\n// Connections handle received pong messages by calling the handler function\n// set with the SetPongHandler method. The default pong handler does nothing.\n// If an application sends ping messages, then the application should set a\n// pong handler to receive the corresponding pong.\n//\n// The control message handler functions are called from the NextReader,\n// ReadMessage and message reader Read methods. The default close and ping\n// handlers can block these methods for a short time when the handler writes to\n// the connection.\n//\n// The application must read the connection to process close, ping and pong\n// messages sent from the peer. If the application is not otherwise interested\n// in messages from the peer, then the application should start a goroutine to\n// read and discard messages from the peer. A simple example is:\n//\n//  func readLoop(c *websocket.Conn) {\n//      for {\n//          if _, _, err := c.NextReader(); err != nil {\n//              c.Close()\n//              break\n//          }\n//      }\n//  }\n//\n// Concurrency\n//\n// Connections support one concurrent reader and one concurrent writer.\n//\n// Applications are responsible for ensuring that no more than one goroutine\n// calls the write methods (NextWriter, SetWriteDeadline, WriteMessage,\n// WriteJSON, EnableWriteCompression, SetCompressionLevel) concurrently and\n// that no more than one goroutine calls the read methods (NextReader,\n// SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler, SetPingHandler)\n// concurrently.\n//\n// The Close and WriteControl methods can be called concurrently with all other\n// methods.\n//\n// Origin Considerations\n//\n// Web browsers allow Javascript applications to open a WebSocket connection to\n// any host. It's up to the server to enforce an origin policy using the Origin\n// request header sent by the browser.\n//\n// The Upgrader calls the function specified in the CheckOrigin field to check\n// the origin. If the CheckOrigin function returns false, then the Upgrade\n// method fails the WebSocket handshake with HTTP status 403.\n//\n// If the CheckOrigin field is nil, then the Upgrader uses a safe default: fail\n// the handshake if the Origin request header is present and the Origin host is\n// not equal to the Host request header.\n//\n// The deprecated package-level Upgrade function does not perform origin\n// checking. The application is responsible for checking the Origin header\n// before calling the Upgrade function.\n//\n// Compression EXPERIMENTAL\n//\n// Per message compression extensions (RFC 7692) are experimentally supported\n// by this package in a limited capacity. Setting the EnableCompression option\n// to true in Dialer or Upgrader will attempt to negotiate per message deflate\n// support.\n//\n//  var upgrader = websocket.Upgrader{\n//      EnableCompression: true,\n//  }\n//\n// If compression was successfully negotiated with the connection's peer, any\n// message received in compressed form will be automatically decompressed.\n// All Read methods will return uncompressed bytes.\n//\n// Per message compression of messages written to a connection can be enabled\n// or disabled by calling the corresponding Conn method:\n//\n//  conn.EnableWriteCompression(false)\n//\n// Currently this package does not support compression with \"context takeover\".\n// This means that messages must be compressed and decompressed in isolation,\n// without retaining sliding window or dictionary state across messages. For\n// more details refer to RFC 7692.\n//\n// Use of compression is experimental and may result in decreased performance.\npackage websocket\n"
  },
  {
    "path": "websockets/v2/vendor/github.com/gorilla/websocket/json.go",
    "content": "// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage websocket\n\nimport (\n\t\"encoding/json\"\n\t\"io\"\n)\n\n// WriteJSON writes the JSON encoding of v as a message.\n//\n// Deprecated: Use c.WriteJSON instead.\nfunc WriteJSON(c *Conn, v interface{}) error {\n\treturn c.WriteJSON(v)\n}\n\n// WriteJSON writes the JSON encoding of v as a message.\n//\n// See the documentation for encoding/json Marshal for details about the\n// conversion of Go values to JSON.\nfunc (c *Conn) WriteJSON(v interface{}) error {\n\tw, err := c.NextWriter(TextMessage)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr1 := json.NewEncoder(w).Encode(v)\n\terr2 := w.Close()\n\tif err1 != nil {\n\t\treturn err1\n\t}\n\treturn err2\n}\n\n// ReadJSON reads the next JSON-encoded message from the connection and stores\n// it in the value pointed to by v.\n//\n// Deprecated: Use c.ReadJSON instead.\nfunc ReadJSON(c *Conn, v interface{}) error {\n\treturn c.ReadJSON(v)\n}\n\n// ReadJSON reads the next JSON-encoded message from the connection and stores\n// it in the value pointed to by v.\n//\n// See the documentation for the encoding/json Unmarshal function for details\n// about the conversion of JSON to a Go value.\nfunc (c *Conn) ReadJSON(v interface{}) error {\n\t_, r, err := c.NextReader()\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = json.NewDecoder(r).Decode(v)\n\tif err == io.EOF {\n\t\t// One value is expected in the message.\n\t\terr = io.ErrUnexpectedEOF\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "websockets/v2/vendor/github.com/gorilla/websocket/mask.go",
    "content": "// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved.  Use of\n// this source code is governed by a BSD-style license that can be found in the\n// LICENSE file.\n\n// +build !appengine\n\npackage websocket\n\nimport \"unsafe\"\n\nconst wordSize = int(unsafe.Sizeof(uintptr(0)))\n\nfunc maskBytes(key [4]byte, pos int, b []byte) int {\n\t// Mask one byte at a time for small buffers.\n\tif len(b) < 2*wordSize {\n\t\tfor i := range b {\n\t\t\tb[i] ^= key[pos&3]\n\t\t\tpos++\n\t\t}\n\t\treturn pos & 3\n\t}\n\n\t// Mask one byte at a time to word boundary.\n\tif n := int(uintptr(unsafe.Pointer(&b[0]))) % wordSize; n != 0 {\n\t\tn = wordSize - n\n\t\tfor i := range b[:n] {\n\t\t\tb[i] ^= key[pos&3]\n\t\t\tpos++\n\t\t}\n\t\tb = b[n:]\n\t}\n\n\t// Create aligned word size key.\n\tvar k [wordSize]byte\n\tfor i := range k {\n\t\tk[i] = key[(pos+i)&3]\n\t}\n\tkw := *(*uintptr)(unsafe.Pointer(&k))\n\n\t// Mask one word at a time.\n\tn := (len(b) / wordSize) * wordSize\n\tfor i := 0; i < n; i += wordSize {\n\t\t*(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(i))) ^= kw\n\t}\n\n\t// Mask one byte at a time for remaining bytes.\n\tb = b[n:]\n\tfor i := range b {\n\t\tb[i] ^= key[pos&3]\n\t\tpos++\n\t}\n\n\treturn pos & 3\n}\n"
  },
  {
    "path": "websockets/v2/vendor/github.com/gorilla/websocket/mask_safe.go",
    "content": "// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved.  Use of\n// this source code is governed by a BSD-style license that can be found in the\n// LICENSE file.\n\n// +build appengine\n\npackage websocket\n\nfunc maskBytes(key [4]byte, pos int, b []byte) int {\n\tfor i := range b {\n\t\tb[i] ^= key[pos&3]\n\t\tpos++\n\t}\n\treturn pos & 3\n}\n"
  },
  {
    "path": "websockets/v2/vendor/github.com/gorilla/websocket/prepared.go",
    "content": "// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage websocket\n\nimport (\n\t\"bytes\"\n\t\"net\"\n\t\"sync\"\n\t\"time\"\n)\n\n// PreparedMessage caches on the wire representations of a message payload.\n// Use PreparedMessage to efficiently send a message payload to multiple\n// connections. PreparedMessage is especially useful when compression is used\n// because the CPU and memory expensive compression operation can be executed\n// once for a given set of compression options.\ntype PreparedMessage struct {\n\tmessageType int\n\tdata        []byte\n\tmu          sync.Mutex\n\tframes      map[prepareKey]*preparedFrame\n}\n\n// prepareKey defines a unique set of options to cache prepared frames in PreparedMessage.\ntype prepareKey struct {\n\tisServer         bool\n\tcompress         bool\n\tcompressionLevel int\n}\n\n// preparedFrame contains data in wire representation.\ntype preparedFrame struct {\n\tonce sync.Once\n\tdata []byte\n}\n\n// NewPreparedMessage returns an initialized PreparedMessage. You can then send\n// it to connection using WritePreparedMessage method. Valid wire\n// representation will be calculated lazily only once for a set of current\n// connection options.\nfunc NewPreparedMessage(messageType int, data []byte) (*PreparedMessage, error) {\n\tpm := &PreparedMessage{\n\t\tmessageType: messageType,\n\t\tframes:      make(map[prepareKey]*preparedFrame),\n\t\tdata:        data,\n\t}\n\n\t// Prepare a plain server frame.\n\t_, frameData, err := pm.frame(prepareKey{isServer: true, compress: false})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// To protect against caller modifying the data argument, remember the data\n\t// copied to the plain server frame.\n\tpm.data = frameData[len(frameData)-len(data):]\n\treturn pm, nil\n}\n\nfunc (pm *PreparedMessage) frame(key prepareKey) (int, []byte, error) {\n\tpm.mu.Lock()\n\tframe, ok := pm.frames[key]\n\tif !ok {\n\t\tframe = &preparedFrame{}\n\t\tpm.frames[key] = frame\n\t}\n\tpm.mu.Unlock()\n\n\tvar err error\n\tframe.once.Do(func() {\n\t\t// Prepare a frame using a 'fake' connection.\n\t\t// TODO: Refactor code in conn.go to allow more direct construction of\n\t\t// the frame.\n\t\tmu := make(chan bool, 1)\n\t\tmu <- true\n\t\tvar nc prepareConn\n\t\tc := &Conn{\n\t\t\tconn:                   &nc,\n\t\t\tmu:                     mu,\n\t\t\tisServer:               key.isServer,\n\t\t\tcompressionLevel:       key.compressionLevel,\n\t\t\tenableWriteCompression: true,\n\t\t\twriteBuf:               make([]byte, defaultWriteBufferSize+maxFrameHeaderSize),\n\t\t}\n\t\tif key.compress {\n\t\t\tc.newCompressionWriter = compressNoContextTakeover\n\t\t}\n\t\terr = c.WriteMessage(pm.messageType, pm.data)\n\t\tframe.data = nc.buf.Bytes()\n\t})\n\treturn pm.messageType, frame.data, err\n}\n\ntype prepareConn struct {\n\tbuf bytes.Buffer\n\tnet.Conn\n}\n\nfunc (pc *prepareConn) Write(p []byte) (int, error)        { return pc.buf.Write(p) }\nfunc (pc *prepareConn) SetWriteDeadline(t time.Time) error { return nil }\n"
  },
  {
    "path": "websockets/v2/vendor/github.com/gorilla/websocket/proxy.go",
    "content": "// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage websocket\n\nimport (\n\t\"bufio\"\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n)\n\ntype netDialerFunc func(network, addr string) (net.Conn, error)\n\nfunc (fn netDialerFunc) Dial(network, addr string) (net.Conn, error) {\n\treturn fn(network, addr)\n}\n\nfunc init() {\n\tproxy_RegisterDialerType(\"http\", func(proxyURL *url.URL, forwardDialer proxy_Dialer) (proxy_Dialer, error) {\n\t\treturn &httpProxyDialer{proxyURL: proxyURL, fowardDial: forwardDialer.Dial}, nil\n\t})\n}\n\ntype httpProxyDialer struct {\n\tproxyURL   *url.URL\n\tfowardDial func(network, addr string) (net.Conn, error)\n}\n\nfunc (hpd *httpProxyDialer) Dial(network string, addr string) (net.Conn, error) {\n\thostPort, _ := hostPortNoPort(hpd.proxyURL)\n\tconn, err := hpd.fowardDial(network, hostPort)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tconnectHeader := make(http.Header)\n\tif user := hpd.proxyURL.User; user != nil {\n\t\tproxyUser := user.Username()\n\t\tif proxyPassword, passwordSet := user.Password(); passwordSet {\n\t\t\tcredential := base64.StdEncoding.EncodeToString([]byte(proxyUser + \":\" + proxyPassword))\n\t\t\tconnectHeader.Set(\"Proxy-Authorization\", \"Basic \"+credential)\n\t\t}\n\t}\n\n\tconnectReq := &http.Request{\n\t\tMethod: \"CONNECT\",\n\t\tURL:    &url.URL{Opaque: addr},\n\t\tHost:   addr,\n\t\tHeader: connectHeader,\n\t}\n\n\tif err := connectReq.Write(conn); err != nil {\n\t\tconn.Close()\n\t\treturn nil, err\n\t}\n\n\t// Read response. It's OK to use and discard buffered reader here because\n\t// the remote server does not speak until spoken to.\n\tbr := bufio.NewReader(conn)\n\tresp, err := http.ReadResponse(br, connectReq)\n\tif err != nil {\n\t\tconn.Close()\n\t\treturn nil, err\n\t}\n\n\tif resp.StatusCode != 200 {\n\t\tconn.Close()\n\t\tf := strings.SplitN(resp.Status, \" \", 2)\n\t\treturn nil, errors.New(f[1])\n\t}\n\treturn conn, nil\n}\n"
  },
  {
    "path": "websockets/v2/vendor/github.com/gorilla/websocket/server.go",
    "content": "// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage websocket\n\nimport (\n\t\"bufio\"\n\t\"errors\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\t\"time\"\n)\n\n// HandshakeError describes an error with the handshake from the peer.\ntype HandshakeError struct {\n\tmessage string\n}\n\nfunc (e HandshakeError) Error() string { return e.message }\n\n// Upgrader specifies parameters for upgrading an HTTP connection to a\n// WebSocket connection.\ntype Upgrader struct {\n\t// HandshakeTimeout specifies the duration for the handshake to complete.\n\tHandshakeTimeout time.Duration\n\n\t// ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer\n\t// size is zero, then buffers allocated by the HTTP server are used. The\n\t// I/O buffer sizes do not limit the size of the messages that can be sent\n\t// or received.\n\tReadBufferSize, WriteBufferSize int\n\n\t// WriteBufferPool is a pool of buffers for write operations. If the value\n\t// is not set, then write buffers are allocated to the connection for the\n\t// lifetime of the connection.\n\t//\n\t// A pool is most useful when the application has a modest volume of writes\n\t// across a large number of connections.\n\t//\n\t// Applications should use a single pool for each unique value of\n\t// WriteBufferSize.\n\tWriteBufferPool BufferPool\n\n\t// Subprotocols specifies the server's supported protocols in order of\n\t// preference. If this field is not nil, then the Upgrade method negotiates a\n\t// subprotocol by selecting the first match in this list with a protocol\n\t// requested by the client. If there's no match, then no protocol is\n\t// negotiated (the Sec-Websocket-Protocol header is not included in the\n\t// handshake response).\n\tSubprotocols []string\n\n\t// Error specifies the function for generating HTTP error responses. If Error\n\t// is nil, then http.Error is used to generate the HTTP response.\n\tError func(w http.ResponseWriter, r *http.Request, status int, reason error)\n\n\t// CheckOrigin returns true if the request Origin header is acceptable. If\n\t// CheckOrigin is nil, then a safe default is used: return false if the\n\t// Origin request header is present and the origin host is not equal to\n\t// request Host header.\n\t//\n\t// A CheckOrigin function should carefully validate the request origin to\n\t// prevent cross-site request forgery.\n\tCheckOrigin func(r *http.Request) bool\n\n\t// EnableCompression specify if the server should attempt to negotiate per\n\t// message compression (RFC 7692). Setting this value to true does not\n\t// guarantee that compression will be supported. Currently only \"no context\n\t// takeover\" modes are supported.\n\tEnableCompression bool\n}\n\nfunc (u *Upgrader) returnError(w http.ResponseWriter, r *http.Request, status int, reason string) (*Conn, error) {\n\terr := HandshakeError{reason}\n\tif u.Error != nil {\n\t\tu.Error(w, r, status, err)\n\t} else {\n\t\tw.Header().Set(\"Sec-Websocket-Version\", \"13\")\n\t\thttp.Error(w, http.StatusText(status), status)\n\t}\n\treturn nil, err\n}\n\n// checkSameOrigin returns true if the origin is not set or is equal to the request host.\nfunc checkSameOrigin(r *http.Request) bool {\n\torigin := r.Header[\"Origin\"]\n\tif len(origin) == 0 {\n\t\treturn true\n\t}\n\tu, err := url.Parse(origin[0])\n\tif err != nil {\n\t\treturn false\n\t}\n\treturn equalASCIIFold(u.Host, r.Host)\n}\n\nfunc (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header) string {\n\tif u.Subprotocols != nil {\n\t\tclientProtocols := Subprotocols(r)\n\t\tfor _, serverProtocol := range u.Subprotocols {\n\t\t\tfor _, clientProtocol := range clientProtocols {\n\t\t\t\tif clientProtocol == serverProtocol {\n\t\t\t\t\treturn clientProtocol\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else if responseHeader != nil {\n\t\treturn responseHeader.Get(\"Sec-Websocket-Protocol\")\n\t}\n\treturn \"\"\n}\n\n// Upgrade upgrades the HTTP server connection to the WebSocket protocol.\n//\n// The responseHeader is included in the response to the client's upgrade\n// request. Use the responseHeader to specify cookies (Set-Cookie) and the\n// application negotiated subprotocol (Sec-WebSocket-Protocol).\n//\n// If the upgrade fails, then Upgrade replies to the client with an HTTP error\n// response.\nfunc (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) {\n\tconst badHandshake = \"websocket: the client is not using the websocket protocol: \"\n\n\tif !tokenListContainsValue(r.Header, \"Connection\", \"upgrade\") {\n\t\treturn u.returnError(w, r, http.StatusBadRequest, badHandshake+\"'upgrade' token not found in 'Connection' header\")\n\t}\n\n\tif !tokenListContainsValue(r.Header, \"Upgrade\", \"websocket\") {\n\t\treturn u.returnError(w, r, http.StatusBadRequest, badHandshake+\"'websocket' token not found in 'Upgrade' header\")\n\t}\n\n\tif r.Method != \"GET\" {\n\t\treturn u.returnError(w, r, http.StatusMethodNotAllowed, badHandshake+\"request method is not GET\")\n\t}\n\n\tif !tokenListContainsValue(r.Header, \"Sec-Websocket-Version\", \"13\") {\n\t\treturn u.returnError(w, r, http.StatusBadRequest, \"websocket: unsupported version: 13 not found in 'Sec-Websocket-Version' header\")\n\t}\n\n\tif _, ok := responseHeader[\"Sec-Websocket-Extensions\"]; ok {\n\t\treturn u.returnError(w, r, http.StatusInternalServerError, \"websocket: application specific 'Sec-WebSocket-Extensions' headers are unsupported\")\n\t}\n\n\tcheckOrigin := u.CheckOrigin\n\tif checkOrigin == nil {\n\t\tcheckOrigin = checkSameOrigin\n\t}\n\tif !checkOrigin(r) {\n\t\treturn u.returnError(w, r, http.StatusForbidden, \"websocket: request origin not allowed by Upgrader.CheckOrigin\")\n\t}\n\n\tchallengeKey := r.Header.Get(\"Sec-Websocket-Key\")\n\tif challengeKey == \"\" {\n\t\treturn u.returnError(w, r, http.StatusBadRequest, \"websocket: not a websocket handshake: `Sec-WebSocket-Key' header is missing or blank\")\n\t}\n\n\tsubprotocol := u.selectSubprotocol(r, responseHeader)\n\n\t// Negotiate PMCE\n\tvar compress bool\n\tif u.EnableCompression {\n\t\tfor _, ext := range parseExtensions(r.Header) {\n\t\t\tif ext[\"\"] != \"permessage-deflate\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tcompress = true\n\t\t\tbreak\n\t\t}\n\t}\n\n\th, ok := w.(http.Hijacker)\n\tif !ok {\n\t\treturn u.returnError(w, r, http.StatusInternalServerError, \"websocket: response does not implement http.Hijacker\")\n\t}\n\tvar brw *bufio.ReadWriter\n\tnetConn, brw, err := h.Hijack()\n\tif err != nil {\n\t\treturn u.returnError(w, r, http.StatusInternalServerError, err.Error())\n\t}\n\n\tif brw.Reader.Buffered() > 0 {\n\t\tnetConn.Close()\n\t\treturn nil, errors.New(\"websocket: client sent data before handshake is complete\")\n\t}\n\n\tvar br *bufio.Reader\n\tif u.ReadBufferSize == 0 && bufioReaderSize(netConn, brw.Reader) > 256 {\n\t\t// Reuse hijacked buffered reader as connection reader.\n\t\tbr = brw.Reader\n\t}\n\n\tbuf := bufioWriterBuffer(netConn, brw.Writer)\n\n\tvar writeBuf []byte\n\tif u.WriteBufferPool == nil && u.WriteBufferSize == 0 && len(buf) >= maxFrameHeaderSize+256 {\n\t\t// Reuse hijacked write buffer as connection buffer.\n\t\twriteBuf = buf\n\t}\n\n\tc := newConn(netConn, true, u.ReadBufferSize, u.WriteBufferSize, u.WriteBufferPool, br, writeBuf)\n\tc.subprotocol = subprotocol\n\n\tif compress {\n\t\tc.newCompressionWriter = compressNoContextTakeover\n\t\tc.newDecompressionReader = decompressNoContextTakeover\n\t}\n\n\t// Use larger of hijacked buffer and connection write buffer for header.\n\tp := buf\n\tif len(c.writeBuf) > len(p) {\n\t\tp = c.writeBuf\n\t}\n\tp = p[:0]\n\n\tp = append(p, \"HTTP/1.1 101 Switching Protocols\\r\\nUpgrade: websocket\\r\\nConnection: Upgrade\\r\\nSec-WebSocket-Accept: \"...)\n\tp = append(p, computeAcceptKey(challengeKey)...)\n\tp = append(p, \"\\r\\n\"...)\n\tif c.subprotocol != \"\" {\n\t\tp = append(p, \"Sec-WebSocket-Protocol: \"...)\n\t\tp = append(p, c.subprotocol...)\n\t\tp = append(p, \"\\r\\n\"...)\n\t}\n\tif compress {\n\t\tp = append(p, \"Sec-WebSocket-Extensions: permessage-deflate; server_no_context_takeover; client_no_context_takeover\\r\\n\"...)\n\t}\n\tfor k, vs := range responseHeader {\n\t\tif k == \"Sec-Websocket-Protocol\" {\n\t\t\tcontinue\n\t\t}\n\t\tfor _, v := range vs {\n\t\t\tp = append(p, k...)\n\t\t\tp = append(p, \": \"...)\n\t\t\tfor i := 0; i < len(v); i++ {\n\t\t\t\tb := v[i]\n\t\t\t\tif b <= 31 {\n\t\t\t\t\t// prevent response splitting.\n\t\t\t\t\tb = ' '\n\t\t\t\t}\n\t\t\t\tp = append(p, b)\n\t\t\t}\n\t\t\tp = append(p, \"\\r\\n\"...)\n\t\t}\n\t}\n\tp = append(p, \"\\r\\n\"...)\n\n\t// Clear deadlines set by HTTP server.\n\tnetConn.SetDeadline(time.Time{})\n\n\tif u.HandshakeTimeout > 0 {\n\t\tnetConn.SetWriteDeadline(time.Now().Add(u.HandshakeTimeout))\n\t}\n\tif _, err = netConn.Write(p); err != nil {\n\t\tnetConn.Close()\n\t\treturn nil, err\n\t}\n\tif u.HandshakeTimeout > 0 {\n\t\tnetConn.SetWriteDeadline(time.Time{})\n\t}\n\n\treturn c, nil\n}\n\n// Upgrade upgrades the HTTP server connection to the WebSocket protocol.\n//\n// Deprecated: Use websocket.Upgrader instead.\n//\n// Upgrade does not perform origin checking. The application is responsible for\n// checking the Origin header before calling Upgrade. An example implementation\n// of the same origin policy check is:\n//\n//\tif req.Header.Get(\"Origin\") != \"http://\"+req.Host {\n//\t\thttp.Error(w, \"Origin not allowed\", http.StatusForbidden)\n//\t\treturn\n//\t}\n//\n// If the endpoint supports subprotocols, then the application is responsible\n// for negotiating the protocol used on the connection. Use the Subprotocols()\n// function to get the subprotocols requested by the client. Use the\n// Sec-Websocket-Protocol response header to specify the subprotocol selected\n// by the application.\n//\n// The responseHeader is included in the response to the client's upgrade\n// request. Use the responseHeader to specify cookies (Set-Cookie) and the\n// negotiated subprotocol (Sec-Websocket-Protocol).\n//\n// The connection buffers IO to the underlying network connection. The\n// readBufSize and writeBufSize parameters specify the size of the buffers to\n// use. Messages can be larger than the buffers.\n//\n// If the request is not a valid WebSocket handshake, then Upgrade returns an\n// error of type HandshakeError. Applications should handle this error by\n// replying to the client with an HTTP error response.\nfunc Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header, readBufSize, writeBufSize int) (*Conn, error) {\n\tu := Upgrader{ReadBufferSize: readBufSize, WriteBufferSize: writeBufSize}\n\tu.Error = func(w http.ResponseWriter, r *http.Request, status int, reason error) {\n\t\t// don't return errors to maintain backwards compatibility\n\t}\n\tu.CheckOrigin = func(r *http.Request) bool {\n\t\t// allow all connections by default\n\t\treturn true\n\t}\n\treturn u.Upgrade(w, r, responseHeader)\n}\n\n// Subprotocols returns the subprotocols requested by the client in the\n// Sec-Websocket-Protocol header.\nfunc Subprotocols(r *http.Request) []string {\n\th := strings.TrimSpace(r.Header.Get(\"Sec-Websocket-Protocol\"))\n\tif h == \"\" {\n\t\treturn nil\n\t}\n\tprotocols := strings.Split(h, \",\")\n\tfor i := range protocols {\n\t\tprotocols[i] = strings.TrimSpace(protocols[i])\n\t}\n\treturn protocols\n}\n\n// IsWebSocketUpgrade returns true if the client requested upgrade to the\n// WebSocket protocol.\nfunc IsWebSocketUpgrade(r *http.Request) bool {\n\treturn tokenListContainsValue(r.Header, \"Connection\", \"upgrade\") &&\n\t\ttokenListContainsValue(r.Header, \"Upgrade\", \"websocket\")\n}\n\n// bufioReaderSize size returns the size of a bufio.Reader.\nfunc bufioReaderSize(originalReader io.Reader, br *bufio.Reader) int {\n\t// This code assumes that peek on a reset reader returns\n\t// bufio.Reader.buf[:0].\n\t// TODO: Use bufio.Reader.Size() after Go 1.10\n\tbr.Reset(originalReader)\n\tif p, err := br.Peek(0); err == nil {\n\t\treturn cap(p)\n\t}\n\treturn 0\n}\n\n// writeHook is an io.Writer that records the last slice passed to it vio\n// io.Writer.Write.\ntype writeHook struct {\n\tp []byte\n}\n\nfunc (wh *writeHook) Write(p []byte) (int, error) {\n\twh.p = p\n\treturn len(p), nil\n}\n\n// bufioWriterBuffer grabs the buffer from a bufio.Writer.\nfunc bufioWriterBuffer(originalWriter io.Writer, bw *bufio.Writer) []byte {\n\t// This code assumes that bufio.Writer.buf[:1] is passed to the\n\t// bufio.Writer's underlying writer.\n\tvar wh writeHook\n\tbw.Reset(&wh)\n\tbw.WriteByte(0)\n\tbw.Flush()\n\n\tbw.Reset(originalWriter)\n\n\treturn wh.p[:cap(wh.p)]\n}\n"
  },
  {
    "path": "websockets/v2/vendor/github.com/gorilla/websocket/trace.go",
    "content": "// +build go1.8\n\npackage websocket\n\nimport (\n\t\"crypto/tls\"\n\t\"net/http/httptrace\"\n)\n\nfunc doHandshakeWithTrace(trace *httptrace.ClientTrace, tlsConn *tls.Conn, cfg *tls.Config) error {\n\tif trace.TLSHandshakeStart != nil {\n\t\ttrace.TLSHandshakeStart()\n\t}\n\terr := doHandshake(tlsConn, cfg)\n\tif trace.TLSHandshakeDone != nil {\n\t\ttrace.TLSHandshakeDone(tlsConn.ConnectionState(), err)\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "websockets/v2/vendor/github.com/gorilla/websocket/trace_17.go",
    "content": "// +build !go1.8\n\npackage websocket\n\nimport (\n\t\"crypto/tls\"\n\t\"net/http/httptrace\"\n)\n\nfunc doHandshakeWithTrace(trace *httptrace.ClientTrace, tlsConn *tls.Conn, cfg *tls.Config) error {\n\treturn doHandshake(tlsConn, cfg)\n}\n"
  },
  {
    "path": "websockets/v2/vendor/github.com/gorilla/websocket/util.go",
    "content": "// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage websocket\n\nimport (\n\t\"crypto/rand\"\n\t\"crypto/sha1\"\n\t\"encoding/base64\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n\t\"unicode/utf8\"\n)\n\nvar keyGUID = []byte(\"258EAFA5-E914-47DA-95CA-C5AB0DC85B11\")\n\nfunc computeAcceptKey(challengeKey string) string {\n\th := sha1.New()\n\th.Write([]byte(challengeKey))\n\th.Write(keyGUID)\n\treturn base64.StdEncoding.EncodeToString(h.Sum(nil))\n}\n\nfunc generateChallengeKey() (string, error) {\n\tp := make([]byte, 16)\n\tif _, err := io.ReadFull(rand.Reader, p); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn base64.StdEncoding.EncodeToString(p), nil\n}\n\n// Octet types from RFC 2616.\nvar octetTypes [256]byte\n\nconst (\n\tisTokenOctet = 1 << iota\n\tisSpaceOctet\n)\n\nfunc init() {\n\t// From RFC 2616\n\t//\n\t// OCTET      = <any 8-bit sequence of data>\n\t// CHAR       = <any US-ASCII character (octets 0 - 127)>\n\t// CTL        = <any US-ASCII control character (octets 0 - 31) and DEL (127)>\n\t// CR         = <US-ASCII CR, carriage return (13)>\n\t// LF         = <US-ASCII LF, linefeed (10)>\n\t// SP         = <US-ASCII SP, space (32)>\n\t// HT         = <US-ASCII HT, horizontal-tab (9)>\n\t// <\">        = <US-ASCII double-quote mark (34)>\n\t// CRLF       = CR LF\n\t// LWS        = [CRLF] 1*( SP | HT )\n\t// TEXT       = <any OCTET except CTLs, but including LWS>\n\t// separators = \"(\" | \")\" | \"<\" | \">\" | \"@\" | \",\" | \";\" | \":\" | \"\\\" | <\">\n\t//              | \"/\" | \"[\" | \"]\" | \"?\" | \"=\" | \"{\" | \"}\" | SP | HT\n\t// token      = 1*<any CHAR except CTLs or separators>\n\t// qdtext     = <any TEXT except <\">>\n\n\tfor c := 0; c < 256; c++ {\n\t\tvar t byte\n\t\tisCtl := c <= 31 || c == 127\n\t\tisChar := 0 <= c && c <= 127\n\t\tisSeparator := strings.IndexRune(\" \\t\\\"(),/:;<=>?@[]\\\\{}\", rune(c)) >= 0\n\t\tif strings.IndexRune(\" \\t\\r\\n\", rune(c)) >= 0 {\n\t\t\tt |= isSpaceOctet\n\t\t}\n\t\tif isChar && !isCtl && !isSeparator {\n\t\t\tt |= isTokenOctet\n\t\t}\n\t\toctetTypes[c] = t\n\t}\n}\n\nfunc skipSpace(s string) (rest string) {\n\ti := 0\n\tfor ; i < len(s); i++ {\n\t\tif octetTypes[s[i]]&isSpaceOctet == 0 {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn s[i:]\n}\n\nfunc nextToken(s string) (token, rest string) {\n\ti := 0\n\tfor ; i < len(s); i++ {\n\t\tif octetTypes[s[i]]&isTokenOctet == 0 {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn s[:i], s[i:]\n}\n\nfunc nextTokenOrQuoted(s string) (value string, rest string) {\n\tif !strings.HasPrefix(s, \"\\\"\") {\n\t\treturn nextToken(s)\n\t}\n\ts = s[1:]\n\tfor i := 0; i < len(s); i++ {\n\t\tswitch s[i] {\n\t\tcase '\"':\n\t\t\treturn s[:i], s[i+1:]\n\t\tcase '\\\\':\n\t\t\tp := make([]byte, len(s)-1)\n\t\t\tj := copy(p, s[:i])\n\t\t\tescape := true\n\t\t\tfor i = i + 1; i < len(s); i++ {\n\t\t\t\tb := s[i]\n\t\t\t\tswitch {\n\t\t\t\tcase escape:\n\t\t\t\t\tescape = false\n\t\t\t\t\tp[j] = b\n\t\t\t\t\tj++\n\t\t\t\tcase b == '\\\\':\n\t\t\t\t\tescape = true\n\t\t\t\tcase b == '\"':\n\t\t\t\t\treturn string(p[:j]), s[i+1:]\n\t\t\t\tdefault:\n\t\t\t\t\tp[j] = b\n\t\t\t\t\tj++\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn \"\", \"\"\n\t\t}\n\t}\n\treturn \"\", \"\"\n}\n\n// equalASCIIFold returns true if s is equal to t with ASCII case folding.\nfunc equalASCIIFold(s, t string) bool {\n\tfor s != \"\" && t != \"\" {\n\t\tsr, size := utf8.DecodeRuneInString(s)\n\t\ts = s[size:]\n\t\ttr, size := utf8.DecodeRuneInString(t)\n\t\tt = t[size:]\n\t\tif sr == tr {\n\t\t\tcontinue\n\t\t}\n\t\tif 'A' <= sr && sr <= 'Z' {\n\t\t\tsr = sr + 'a' - 'A'\n\t\t}\n\t\tif 'A' <= tr && tr <= 'Z' {\n\t\t\ttr = tr + 'a' - 'A'\n\t\t}\n\t\tif sr != tr {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn s == t\n}\n\n// tokenListContainsValue returns true if the 1#token header with the given\n// name contains a token equal to value with ASCII case folding.\nfunc tokenListContainsValue(header http.Header, name string, value string) bool {\nheaders:\n\tfor _, s := range header[name] {\n\t\tfor {\n\t\t\tvar t string\n\t\t\tt, s = nextToken(skipSpace(s))\n\t\t\tif t == \"\" {\n\t\t\t\tcontinue headers\n\t\t\t}\n\t\t\ts = skipSpace(s)\n\t\t\tif s != \"\" && s[0] != ',' {\n\t\t\t\tcontinue headers\n\t\t\t}\n\t\t\tif equalASCIIFold(t, value) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif s == \"\" {\n\t\t\t\tcontinue headers\n\t\t\t}\n\t\t\ts = s[1:]\n\t\t}\n\t}\n\treturn false\n}\n\n// parseExtensions parses WebSocket extensions from a header.\nfunc parseExtensions(header http.Header) []map[string]string {\n\t// From RFC 6455:\n\t//\n\t//  Sec-WebSocket-Extensions = extension-list\n\t//  extension-list = 1#extension\n\t//  extension = extension-token *( \";\" extension-param )\n\t//  extension-token = registered-token\n\t//  registered-token = token\n\t//  extension-param = token [ \"=\" (token | quoted-string) ]\n\t//     ;When using the quoted-string syntax variant, the value\n\t//     ;after quoted-string unescaping MUST conform to the\n\t//     ;'token' ABNF.\n\n\tvar result []map[string]string\nheaders:\n\tfor _, s := range header[\"Sec-Websocket-Extensions\"] {\n\t\tfor {\n\t\t\tvar t string\n\t\t\tt, s = nextToken(skipSpace(s))\n\t\t\tif t == \"\" {\n\t\t\t\tcontinue headers\n\t\t\t}\n\t\t\text := map[string]string{\"\": t}\n\t\t\tfor {\n\t\t\t\ts = skipSpace(s)\n\t\t\t\tif !strings.HasPrefix(s, \";\") {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tvar k string\n\t\t\t\tk, s = nextToken(skipSpace(s[1:]))\n\t\t\t\tif k == \"\" {\n\t\t\t\t\tcontinue headers\n\t\t\t\t}\n\t\t\t\ts = skipSpace(s)\n\t\t\t\tvar v string\n\t\t\t\tif strings.HasPrefix(s, \"=\") {\n\t\t\t\t\tv, s = nextTokenOrQuoted(skipSpace(s[1:]))\n\t\t\t\t\ts = skipSpace(s)\n\t\t\t\t}\n\t\t\t\tif s != \"\" && s[0] != ',' && s[0] != ';' {\n\t\t\t\t\tcontinue headers\n\t\t\t\t}\n\t\t\t\text[k] = v\n\t\t\t}\n\t\t\tif s != \"\" && s[0] != ',' {\n\t\t\t\tcontinue headers\n\t\t\t}\n\t\t\tresult = append(result, ext)\n\t\t\tif s == \"\" {\n\t\t\t\tcontinue headers\n\t\t\t}\n\t\t\ts = s[1:]\n\t\t}\n\t}\n\treturn result\n}\n"
  },
  {
    "path": "websockets/v2/vendor/github.com/gorilla/websocket/x_net_proxy.go",
    "content": "// Code generated by golang.org/x/tools/cmd/bundle. DO NOT EDIT.\n//go:generate bundle -o x_net_proxy.go golang.org/x/net/proxy\n\n// Package proxy provides support for a variety of protocols to proxy network\n// data.\n//\n\npackage websocket\n\nimport (\n\t\"errors\"\n\t\"io\"\n\t\"net\"\n\t\"net/url\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n)\n\ntype proxy_direct struct{}\n\n// Direct is a direct proxy: one that makes network connections directly.\nvar proxy_Direct = proxy_direct{}\n\nfunc (proxy_direct) Dial(network, addr string) (net.Conn, error) {\n\treturn net.Dial(network, addr)\n}\n\n// A PerHost directs connections to a default Dialer unless the host name\n// requested matches one of a number of exceptions.\ntype proxy_PerHost struct {\n\tdef, bypass proxy_Dialer\n\n\tbypassNetworks []*net.IPNet\n\tbypassIPs      []net.IP\n\tbypassZones    []string\n\tbypassHosts    []string\n}\n\n// NewPerHost returns a PerHost Dialer that directs connections to either\n// defaultDialer or bypass, depending on whether the connection matches one of\n// the configured rules.\nfunc proxy_NewPerHost(defaultDialer, bypass proxy_Dialer) *proxy_PerHost {\n\treturn &proxy_PerHost{\n\t\tdef:    defaultDialer,\n\t\tbypass: bypass,\n\t}\n}\n\n// Dial connects to the address addr on the given network through either\n// defaultDialer or bypass.\nfunc (p *proxy_PerHost) Dial(network, addr string) (c net.Conn, err error) {\n\thost, _, err := net.SplitHostPort(addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn p.dialerForRequest(host).Dial(network, addr)\n}\n\nfunc (p *proxy_PerHost) dialerForRequest(host string) proxy_Dialer {\n\tif ip := net.ParseIP(host); ip != nil {\n\t\tfor _, net := range p.bypassNetworks {\n\t\t\tif net.Contains(ip) {\n\t\t\t\treturn p.bypass\n\t\t\t}\n\t\t}\n\t\tfor _, bypassIP := range p.bypassIPs {\n\t\t\tif bypassIP.Equal(ip) {\n\t\t\t\treturn p.bypass\n\t\t\t}\n\t\t}\n\t\treturn p.def\n\t}\n\n\tfor _, zone := range p.bypassZones {\n\t\tif strings.HasSuffix(host, zone) {\n\t\t\treturn p.bypass\n\t\t}\n\t\tif host == zone[1:] {\n\t\t\t// For a zone \".example.com\", we match \"example.com\"\n\t\t\t// too.\n\t\t\treturn p.bypass\n\t\t}\n\t}\n\tfor _, bypassHost := range p.bypassHosts {\n\t\tif bypassHost == host {\n\t\t\treturn p.bypass\n\t\t}\n\t}\n\treturn p.def\n}\n\n// AddFromString parses a string that contains comma-separated values\n// specifying hosts that should use the bypass proxy. Each value is either an\n// IP address, a CIDR range, a zone (*.example.com) or a host name\n// (localhost). A best effort is made to parse the string and errors are\n// ignored.\nfunc (p *proxy_PerHost) AddFromString(s string) {\n\thosts := strings.Split(s, \",\")\n\tfor _, host := range hosts {\n\t\thost = strings.TrimSpace(host)\n\t\tif len(host) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tif strings.Contains(host, \"/\") {\n\t\t\t// We assume that it's a CIDR address like 127.0.0.0/8\n\t\t\tif _, net, err := net.ParseCIDR(host); err == nil {\n\t\t\t\tp.AddNetwork(net)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tif ip := net.ParseIP(host); ip != nil {\n\t\t\tp.AddIP(ip)\n\t\t\tcontinue\n\t\t}\n\t\tif strings.HasPrefix(host, \"*.\") {\n\t\t\tp.AddZone(host[1:])\n\t\t\tcontinue\n\t\t}\n\t\tp.AddHost(host)\n\t}\n}\n\n// AddIP specifies an IP address that will use the bypass proxy. Note that\n// this will only take effect if a literal IP address is dialed. A connection\n// to a named host will never match an IP.\nfunc (p *proxy_PerHost) AddIP(ip net.IP) {\n\tp.bypassIPs = append(p.bypassIPs, ip)\n}\n\n// AddNetwork specifies an IP range that will use the bypass proxy. Note that\n// this will only take effect if a literal IP address is dialed. A connection\n// to a named host will never match.\nfunc (p *proxy_PerHost) AddNetwork(net *net.IPNet) {\n\tp.bypassNetworks = append(p.bypassNetworks, net)\n}\n\n// AddZone specifies a DNS suffix that will use the bypass proxy. A zone of\n// \"example.com\" matches \"example.com\" and all of its subdomains.\nfunc (p *proxy_PerHost) AddZone(zone string) {\n\tif strings.HasSuffix(zone, \".\") {\n\t\tzone = zone[:len(zone)-1]\n\t}\n\tif !strings.HasPrefix(zone, \".\") {\n\t\tzone = \".\" + zone\n\t}\n\tp.bypassZones = append(p.bypassZones, zone)\n}\n\n// AddHost specifies a host name that will use the bypass proxy.\nfunc (p *proxy_PerHost) AddHost(host string) {\n\tif strings.HasSuffix(host, \".\") {\n\t\thost = host[:len(host)-1]\n\t}\n\tp.bypassHosts = append(p.bypassHosts, host)\n}\n\n// A Dialer is a means to establish a connection.\ntype proxy_Dialer interface {\n\t// Dial connects to the given address via the proxy.\n\tDial(network, addr string) (c net.Conn, err error)\n}\n\n// Auth contains authentication parameters that specific Dialers may require.\ntype proxy_Auth struct {\n\tUser, Password string\n}\n\n// FromEnvironment returns the dialer specified by the proxy related variables in\n// the environment.\nfunc proxy_FromEnvironment() proxy_Dialer {\n\tallProxy := proxy_allProxyEnv.Get()\n\tif len(allProxy) == 0 {\n\t\treturn proxy_Direct\n\t}\n\n\tproxyURL, err := url.Parse(allProxy)\n\tif err != nil {\n\t\treturn proxy_Direct\n\t}\n\tproxy, err := proxy_FromURL(proxyURL, proxy_Direct)\n\tif err != nil {\n\t\treturn proxy_Direct\n\t}\n\n\tnoProxy := proxy_noProxyEnv.Get()\n\tif len(noProxy) == 0 {\n\t\treturn proxy\n\t}\n\n\tperHost := proxy_NewPerHost(proxy, proxy_Direct)\n\tperHost.AddFromString(noProxy)\n\treturn perHost\n}\n\n// proxySchemes is a map from URL schemes to a function that creates a Dialer\n// from a URL with such a scheme.\nvar proxy_proxySchemes map[string]func(*url.URL, proxy_Dialer) (proxy_Dialer, error)\n\n// RegisterDialerType takes a URL scheme and a function to generate Dialers from\n// a URL with that scheme and a forwarding Dialer. Registered schemes are used\n// by FromURL.\nfunc proxy_RegisterDialerType(scheme string, f func(*url.URL, proxy_Dialer) (proxy_Dialer, error)) {\n\tif proxy_proxySchemes == nil {\n\t\tproxy_proxySchemes = make(map[string]func(*url.URL, proxy_Dialer) (proxy_Dialer, error))\n\t}\n\tproxy_proxySchemes[scheme] = f\n}\n\n// FromURL returns a Dialer given a URL specification and an underlying\n// Dialer for it to make network requests.\nfunc proxy_FromURL(u *url.URL, forward proxy_Dialer) (proxy_Dialer, error) {\n\tvar auth *proxy_Auth\n\tif u.User != nil {\n\t\tauth = new(proxy_Auth)\n\t\tauth.User = u.User.Username()\n\t\tif p, ok := u.User.Password(); ok {\n\t\t\tauth.Password = p\n\t\t}\n\t}\n\n\tswitch u.Scheme {\n\tcase \"socks5\":\n\t\treturn proxy_SOCKS5(\"tcp\", u.Host, auth, forward)\n\t}\n\n\t// If the scheme doesn't match any of the built-in schemes, see if it\n\t// was registered by another package.\n\tif proxy_proxySchemes != nil {\n\t\tif f, ok := proxy_proxySchemes[u.Scheme]; ok {\n\t\t\treturn f(u, forward)\n\t\t}\n\t}\n\n\treturn nil, errors.New(\"proxy: unknown scheme: \" + u.Scheme)\n}\n\nvar (\n\tproxy_allProxyEnv = &proxy_envOnce{\n\t\tnames: []string{\"ALL_PROXY\", \"all_proxy\"},\n\t}\n\tproxy_noProxyEnv = &proxy_envOnce{\n\t\tnames: []string{\"NO_PROXY\", \"no_proxy\"},\n\t}\n)\n\n// envOnce looks up an environment variable (optionally by multiple\n// names) once. It mitigates expensive lookups on some platforms\n// (e.g. Windows).\n// (Borrowed from net/http/transport.go)\ntype proxy_envOnce struct {\n\tnames []string\n\tonce  sync.Once\n\tval   string\n}\n\nfunc (e *proxy_envOnce) Get() string {\n\te.once.Do(e.init)\n\treturn e.val\n}\n\nfunc (e *proxy_envOnce) init() {\n\tfor _, n := range e.names {\n\t\te.val = os.Getenv(n)\n\t\tif e.val != \"\" {\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given address\n// with an optional username and password. See RFC 1928 and RFC 1929.\nfunc proxy_SOCKS5(network, addr string, auth *proxy_Auth, forward proxy_Dialer) (proxy_Dialer, error) {\n\ts := &proxy_socks5{\n\t\tnetwork: network,\n\t\taddr:    addr,\n\t\tforward: forward,\n\t}\n\tif auth != nil {\n\t\ts.user = auth.User\n\t\ts.password = auth.Password\n\t}\n\n\treturn s, nil\n}\n\ntype proxy_socks5 struct {\n\tuser, password string\n\tnetwork, addr  string\n\tforward        proxy_Dialer\n}\n\nconst proxy_socks5Version = 5\n\nconst (\n\tproxy_socks5AuthNone     = 0\n\tproxy_socks5AuthPassword = 2\n)\n\nconst proxy_socks5Connect = 1\n\nconst (\n\tproxy_socks5IP4    = 1\n\tproxy_socks5Domain = 3\n\tproxy_socks5IP6    = 4\n)\n\nvar proxy_socks5Errors = []string{\n\t\"\",\n\t\"general failure\",\n\t\"connection forbidden\",\n\t\"network unreachable\",\n\t\"host unreachable\",\n\t\"connection refused\",\n\t\"TTL expired\",\n\t\"command not supported\",\n\t\"address type not supported\",\n}\n\n// Dial connects to the address addr on the given network via the SOCKS5 proxy.\nfunc (s *proxy_socks5) Dial(network, addr string) (net.Conn, error) {\n\tswitch network {\n\tcase \"tcp\", \"tcp6\", \"tcp4\":\n\tdefault:\n\t\treturn nil, errors.New(\"proxy: no support for SOCKS5 proxy connections of type \" + network)\n\t}\n\n\tconn, err := s.forward.Dial(s.network, s.addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif err := s.connect(conn, addr); err != nil {\n\t\tconn.Close()\n\t\treturn nil, err\n\t}\n\treturn conn, nil\n}\n\n// connect takes an existing connection to a socks5 proxy server,\n// and commands the server to extend that connection to target,\n// which must be a canonical address with a host and port.\nfunc (s *proxy_socks5) connect(conn net.Conn, target string) error {\n\thost, portStr, err := net.SplitHostPort(target)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tport, err := strconv.Atoi(portStr)\n\tif err != nil {\n\t\treturn errors.New(\"proxy: failed to parse port number: \" + portStr)\n\t}\n\tif port < 1 || port > 0xffff {\n\t\treturn errors.New(\"proxy: port number out of range: \" + portStr)\n\t}\n\n\t// the size here is just an estimate\n\tbuf := make([]byte, 0, 6+len(host))\n\n\tbuf = append(buf, proxy_socks5Version)\n\tif len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 {\n\t\tbuf = append(buf, 2 /* num auth methods */, proxy_socks5AuthNone, proxy_socks5AuthPassword)\n\t} else {\n\t\tbuf = append(buf, 1 /* num auth methods */, proxy_socks5AuthNone)\n\t}\n\n\tif _, err := conn.Write(buf); err != nil {\n\t\treturn errors.New(\"proxy: failed to write greeting to SOCKS5 proxy at \" + s.addr + \": \" + err.Error())\n\t}\n\n\tif _, err := io.ReadFull(conn, buf[:2]); err != nil {\n\t\treturn errors.New(\"proxy: failed to read greeting from SOCKS5 proxy at \" + s.addr + \": \" + err.Error())\n\t}\n\tif buf[0] != 5 {\n\t\treturn errors.New(\"proxy: SOCKS5 proxy at \" + s.addr + \" has unexpected version \" + strconv.Itoa(int(buf[0])))\n\t}\n\tif buf[1] == 0xff {\n\t\treturn errors.New(\"proxy: SOCKS5 proxy at \" + s.addr + \" requires authentication\")\n\t}\n\n\t// See RFC 1929\n\tif buf[1] == proxy_socks5AuthPassword {\n\t\tbuf = buf[:0]\n\t\tbuf = append(buf, 1 /* password protocol version */)\n\t\tbuf = append(buf, uint8(len(s.user)))\n\t\tbuf = append(buf, s.user...)\n\t\tbuf = append(buf, uint8(len(s.password)))\n\t\tbuf = append(buf, s.password...)\n\n\t\tif _, err := conn.Write(buf); err != nil {\n\t\t\treturn errors.New(\"proxy: failed to write authentication request to SOCKS5 proxy at \" + s.addr + \": \" + err.Error())\n\t\t}\n\n\t\tif _, err := io.ReadFull(conn, buf[:2]); err != nil {\n\t\t\treturn errors.New(\"proxy: failed to read authentication reply from SOCKS5 proxy at \" + s.addr + \": \" + err.Error())\n\t\t}\n\n\t\tif buf[1] != 0 {\n\t\t\treturn errors.New(\"proxy: SOCKS5 proxy at \" + s.addr + \" rejected username/password\")\n\t\t}\n\t}\n\n\tbuf = buf[:0]\n\tbuf = append(buf, proxy_socks5Version, proxy_socks5Connect, 0 /* reserved */)\n\n\tif ip := net.ParseIP(host); ip != nil {\n\t\tif ip4 := ip.To4(); ip4 != nil {\n\t\t\tbuf = append(buf, proxy_socks5IP4)\n\t\t\tip = ip4\n\t\t} else {\n\t\t\tbuf = append(buf, proxy_socks5IP6)\n\t\t}\n\t\tbuf = append(buf, ip...)\n\t} else {\n\t\tif len(host) > 255 {\n\t\t\treturn errors.New(\"proxy: destination host name too long: \" + host)\n\t\t}\n\t\tbuf = append(buf, proxy_socks5Domain)\n\t\tbuf = append(buf, byte(len(host)))\n\t\tbuf = append(buf, host...)\n\t}\n\tbuf = append(buf, byte(port>>8), byte(port))\n\n\tif _, err := conn.Write(buf); err != nil {\n\t\treturn errors.New(\"proxy: failed to write connect request to SOCKS5 proxy at \" + s.addr + \": \" + err.Error())\n\t}\n\n\tif _, err := io.ReadFull(conn, buf[:4]); err != nil {\n\t\treturn errors.New(\"proxy: failed to read connect reply from SOCKS5 proxy at \" + s.addr + \": \" + err.Error())\n\t}\n\n\tfailure := \"unknown error\"\n\tif int(buf[1]) < len(proxy_socks5Errors) {\n\t\tfailure = proxy_socks5Errors[buf[1]]\n\t}\n\n\tif len(failure) > 0 {\n\t\treturn errors.New(\"proxy: SOCKS5 proxy at \" + s.addr + \" failed to connect: \" + failure)\n\t}\n\n\tbytesToDiscard := 0\n\tswitch buf[3] {\n\tcase proxy_socks5IP4:\n\t\tbytesToDiscard = net.IPv4len\n\tcase proxy_socks5IP6:\n\t\tbytesToDiscard = net.IPv6len\n\tcase proxy_socks5Domain:\n\t\t_, err := io.ReadFull(conn, buf[:1])\n\t\tif err != nil {\n\t\t\treturn errors.New(\"proxy: failed to read domain length from SOCKS5 proxy at \" + s.addr + \": \" + err.Error())\n\t\t}\n\t\tbytesToDiscard = int(buf[0])\n\tdefault:\n\t\treturn errors.New(\"proxy: got unknown address type \" + strconv.Itoa(int(buf[3])) + \" from SOCKS5 proxy at \" + s.addr)\n\t}\n\n\tif cap(buf) < bytesToDiscard {\n\t\tbuf = make([]byte, bytesToDiscard)\n\t} else {\n\t\tbuf = buf[:bytesToDiscard]\n\t}\n\tif _, err := io.ReadFull(conn, buf); err != nil {\n\t\treturn errors.New(\"proxy: failed to read address from SOCKS5 proxy at \" + s.addr + \": \" + err.Error())\n\t}\n\n\t// Also need to discard the port number\n\tif _, err := io.ReadFull(conn, buf[:2]); err != nil {\n\t\treturn errors.New(\"proxy: failed to read port from SOCKS5 proxy at \" + s.addr + \": \" + err.Error())\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "websockets.md",
    "content": "# WebSockets\n\n[**You can find all the code for this chapter here**](https://github.com/quii/learn-go-with-tests/tree/main/websockets)\n\nIn this chapter we'll learn how to use WebSockets to improve our application.\n\n## Project recap\n\nWe have two applications in our poker codebase\n\n* _Command line app_. Prompts the user to enter the number of players in a game. From then on informs the players of what the \"blind bet\" value is, which increases over time. At any point a user can enter `\"{Playername} wins\"` to finish the game and record the victor in a store.\n* _Web app_. Allows users to record winners of games and displays a league table. Shares the same store as the command line app.\n\n## Next steps\n\nThe product owner is thrilled with the command line application but would prefer it if we could bring that functionality to the browser. She imagines a web page with a text box that allows the user to enter the number of players and when they submit the form the page displays the blind value and automatically updates it when appropriate. Like the command line application the user can declare the winner and it'll get saved in the database.\n\nOn the face of it, it sounds quite simple but as always we must emphasise taking an _iterative_ approach to writing software.\n\nFirst we will need to serve HTML. So far all of our HTTP endpoints have returned either plaintext or JSON. We _could_ use the same techniques we know (as they're all ultimately strings) but we can also use the [html/template](https://golang.org/pkg/html/template/) package for a cleaner solution.\n\nWe also need to be able to asynchronously send messages to the user saying `The blind is now *y*` without having to refresh the browser. We can use [WebSockets](https://en.wikipedia.org/wiki/WebSocket) to facilitate this.\n\n> WebSocket is a computer communications protocol, providing full-duplex communication channels over a single TCP connection\n\nGiven we are taking on a number of techniques it's even more important we do the smallest amount of useful work possible first and then iterate.\n\nFor that reason the first thing we'll do is create a web page with a form for the user to record a winner. Rather than using a plain form, we will use WebSockets to send that data to our server for it to record.\n\nAfter that we'll work on the blind alerts by which point we will have a bit of infrastructure code set up.\n\n### What about tests for the JavaScript ?\n\nThere will be some JavaScript written to do this but I won't go in to writing tests.\n\nIt is of course possible but for the sake of brevity I won't be including any explanations for it.\n\nSorry folks. Lobby O'Reilly to pay me to make a \"Learn JavaScript with tests\".\n\n## Write the test first\n\nFirst thing we need to do is serve up some HTML to users when they hit `/game`.\n\nHere's a reminder of the pertinent code in our web server\n\n```go\ntype PlayerServer struct {\n\tstore PlayerStore\n\thttp.Handler\n}\n\nconst jsonContentType = \"application/json\"\n\nfunc NewPlayerServer(store PlayerStore) *PlayerServer {\n\tp := new(PlayerServer)\n\n\tp.store = store\n\n\trouter := http.NewServeMux()\n\trouter.Handle(\"/league\", http.HandlerFunc(p.leagueHandler))\n\trouter.Handle(\"/players/\", http.HandlerFunc(p.playersHandler))\n\n\tp.Handler = router\n\n\treturn p\n}\n```\n\nThe _easiest_ thing we can do for now is check when we `GET /game` that we get a `200`.\n\n```go\nfunc TestGame(t *testing.T) {\n\tt.Run(\"GET /game returns 200\", func(t *testing.T) {\n\t\tserver := NewPlayerServer(&StubPlayerStore{})\n\n\t\trequest, _ := http.NewRequest(http.MethodGet, \"/game\", nil)\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response.Code, http.StatusOK)\n\t})\n}\n```\n\n## Try to run the test\n\n```\n--- FAIL: TestGame (0.00s)\n=== RUN   TestGame/GET_/game_returns_200\n    --- FAIL: TestGame/GET_/game_returns_200 (0.00s)\n    \tserver_test.go:109: did not get correct status, got 404, want 200\n```\n\n## Write enough code to make it pass\n\nOur server has a router setup so it's relatively easy to fix.\n\nTo our router add\n\n```go\nrouter.Handle(\"/game\", http.HandlerFunc(p.game))\n```\n\nAnd then write the `game` method\n\n```go\nfunc (p *PlayerServer) game(w http.ResponseWriter, r *http.Request) {\n\tw.WriteHeader(http.StatusOK)\n}\n```\n\n## Refactor\n\nThe server code is already fine due to us slotting in more code into the existing well-factored code very easily.\n\nWe can tidy up the test a little by adding a test helper function `newGameRequest` to make the request to `/game`. Try writing this yourself.\n\n```go\nfunc TestGame(t *testing.T) {\n\tt.Run(\"GET /game returns 200\", func(t *testing.T) {\n\t\tserver := NewPlayerServer(&StubPlayerStore{})\n\n\t\trequest := newGameRequest()\n\t\tresponse := httptest.NewRecorder()\n\n\t\tserver.ServeHTTP(response, request)\n\n\t\tassertStatus(t, response, http.StatusOK)\n\t})\n}\n```\n\nYou'll also notice I changed `assertStatus` to accept `response` rather than `response.Code` as I feel it reads better.\n\nNow we need to make the endpoint return some HTML, here it is\n\n```html\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>Let's play poker</title>\n</head>\n<body>\n<section id=\"game\">\n    <div id=\"declare-winner\">\n        <label for=\"winner\">Winner</label>\n        <input type=\"text\" id=\"winner\"/>\n        <button id=\"winner-button\">Declare winner</button>\n    </div>\n</section>\n</body>\n<script type=\"application/javascript\">\n\n    const submitWinnerButton = document.getElementById('winner-button')\n    const winnerInput = document.getElementById('winner')\n\n    if (window['WebSocket']) {\n        const conn = new WebSocket('ws://' + document.location.host + '/ws')\n\n        submitWinnerButton.onclick = event => {\n            conn.send(winnerInput.value)\n        }\n    }\n</script>\n</html>\n```\n\nWe have a very simple web page\n\n* A text input for the user to enter the winner into\n* A button they can click to declare the winner.\n* Some JavaScript to open a WebSocket connection to our server and handle the submit button being pressed\n\n`WebSocket` is built into most modern browsers so we don't need to worry about bringing in any libraries. The web page won't work for older browsers, but we're ok with that for this scenario.\n\n### How do we test we return the correct markup?\n\nThere are a few ways. As has been emphasised throughout the book, it is important that the tests you write have sufficient value to justify the cost.\n\n1. Write a browser based test, using something like Selenium. These tests are the most \"realistic\" of all approaches because they start an actual web browser of some kind and simulates a user interacting with it. These tests can give you a lot of confidence your system works but are more difficult to write than unit tests and much slower to run. For the purposes of our product this is overkill.\n2. Do an exact string match. This _can_ be ok but these kind of tests end up being very brittle. The moment someone changes the markup you will have a test failing when in practice nothing has _actually broken_.\n3. Check we call the correct template. We will be using a templating library from the standard lib to serve the HTML (discussed shortly) and we could inject in the _thing_ to generate the HTML and spy on its call to check we're doing it right. This would have an impact on our code's design but doesn't actually test a great deal; other than we're calling it with the correct template file. Given we will only have the one template in our project the chance of failure here seems low.\n\nSo in the book \"Learn Go with Tests\" for the first time, we're not going to write a test.\n\nPut the markup in a file called `game.html`\n\nNext change the endpoint we just wrote to the following\n\n```go\nfunc (p *PlayerServer) game(w http.ResponseWriter, r *http.Request) {\n\ttmpl, err := template.ParseFiles(\"game.html\")\n\n\tif err != nil {\n\t\thttp.Error(w, fmt.Sprintf(\"problem loading template %s\", err.Error()), http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\ttmpl.Execute(w, nil)\n}\n```\n\n[`html/template`](https://golang.org/pkg/html/template/) is a Go package for creating HTML. In our case we call `template.ParseFiles`, giving the path of our html file. Assuming there is no error you can then `Execute` the template, which writes it to an `io.Writer`. In our case we want it to `Write` to the internet, so we give it our `http.ResponseWriter`.\n\nAs we have not written a test, it would be prudent to manually test our web server just to make sure things are working as we'd hope. Go to `cmd/webserver` and run the `main.go` file. Visit `http://localhost:5000/game`.\n\nYou _should_ have got an error about not being able to find the template. You can either change the path to be relative to your folder, or you can have a copy of the `game.html` in the `cmd/webserver` directory. I chose to create a symlink (`ln -s ../../game.html game.html`) to the file inside the root of the project so if I make changes they are reflected when running the server.\n\nIf you make this change and run again you should see our UI.\n\nNow we need to test that when we get a string over a WebSocket connection to our server that we declare it as a winner of a game.\n\n## Write the test first\n\nFor the first time we are going to use an external library so that we can work with WebSockets.\n\nRun `go get github.com/gorilla/websocket`\n\nThis will fetch the code for the excellent [Gorilla WebSocket](https://github.com/gorilla/websocket) library. Now we can update our tests for our new requirement.\n\n```go\nt.Run(\"when we get a message over a websocket it is a winner of a game\", func(t *testing.T) {\n\tstore := &StubPlayerStore{}\n\twinner := \"Ruth\"\n\tserver := httptest.NewServer(NewPlayerServer(store))\n\tdefer server.Close()\n\n\twsURL := \"ws\" + strings.TrimPrefix(server.URL, \"http\") + \"/ws\"\n\n\tws, _, err := websocket.DefaultDialer.Dial(wsURL, nil)\n\tif err != nil {\n\t\tt.Fatalf(\"could not open a ws connection on %s %v\", wsURL, err)\n\t}\n\tdefer ws.Close()\n\n\tif err := ws.WriteMessage(websocket.TextMessage, []byte(winner)); err != nil {\n\t\tt.Fatalf(\"could not send message over ws connection %v\", err)\n\t}\n\n\tAssertPlayerWin(t, store, winner)\n})\n```\n\nMake sure that you have an import for the `websocket` library. My IDE automatically did it for me, so should yours.\n\nTo test what happens from the browser we have to open up our own WebSocket connection and write to it.\n\nOur previous tests around our server just called methods on our server but now we need to have a persistent connection to our server. To do that we use `httptest.NewServer` which takes a `http.Handler` and will spin it up and listen for connections.\n\nUsing `websocket.DefaultDialer.Dial` we try to dial in to our server and then we'll try and send a message with our `winner`.\n\nFinally, we assert on the player store to check the winner was recorded.\n\n## Try to run the test\n\n```\n=== RUN   TestGame/when_we_get_a_message_over_a_websocket_it_is_a_winner_of_a_game\n    --- FAIL: TestGame/when_we_get_a_message_over_a_websocket_it_is_a_winner_of_a_game (0.00s)\n        server_test.go:124: could not open a ws connection on ws://127.0.0.1:55838/ws websocket: bad handshake\n```\n\nWe have not changed our server to accept WebSocket connections on `/ws` so we're not shaking hands yet.\n\n## Write enough code to make it pass\n\nAdd another listing to our router\n\n```go\nrouter.Handle(\"/ws\", http.HandlerFunc(p.webSocket))\n```\n\nThen add our new `webSocket` handler\n\n```go\nfunc (p *PlayerServer) webSocket(w http.ResponseWriter, r *http.Request) {\n\tupgrader := websocket.Upgrader{\n\t\tReadBufferSize:  1024,\n\t\tWriteBufferSize: 1024,\n\t}\n\tupgrader.Upgrade(w, r, nil)\n}\n```\n\nTo accept a WebSocket connection we `Upgrade` the request. If you now re-run the test you should move on to the next error.\n\n```\n=== RUN   TestGame/when_we_get_a_message_over_a_websocket_it_is_a_winner_of_a_game\n    --- FAIL: TestGame/when_we_get_a_message_over_a_websocket_it_is_a_winner_of_a_game (0.00s)\n        server_test.go:132: got 0 calls to RecordWin want 1\n```\n\nNow that we have a connection opened, we'll want to listen for a message and then record it as the winner.\n\n```go\nfunc (p *PlayerServer) webSocket(w http.ResponseWriter, r *http.Request) {\n\tupgrader := websocket.Upgrader{\n\t\tReadBufferSize:  1024,\n\t\tWriteBufferSize: 1024,\n\t}\n\tconn, _ := upgrader.Upgrade(w, r, nil)\n\t_, winnerMsg, _ := conn.ReadMessage()\n\tp.store.RecordWin(string(winnerMsg))\n}\n```\n\n(Yes, we're ignoring a lot of errors right now!)\n\n`conn.ReadMessage()` blocks on waiting for a message on the connection. Once we get one we use it to `RecordWin`. This would finally close the WebSocket connection.\n\nIf you try and run the test, it's still failing.\n\nThe issue is timing. There is a delay between our WebSocket connection reading the message and recording the win and our test finishes before it happens. You can test this by putting a short `time.Sleep` before the final assertion.\n\nLet's go with that for now but acknowledge that putting in arbitrary sleeps into tests **is very bad practice**.\n\n```go\ntime.Sleep(10 * time.Millisecond)\nAssertPlayerWin(t, store, winner)\n```\n\n## Refactor\n\nWe committed many sins to make this test work both in the server code and the test code but remember this is the easiest way for us to work.\n\nWe have nasty, horrible, _working_ software backed by a test, so now we are free to make it nice and know we won't break anything accidentally.\n\nLet's start with the server code.\n\nWe can move the `upgrader` to a private value inside our package because we don't need to redeclare it on every WebSocket connection request\n\n```go\nvar wsUpgrader = websocket.Upgrader{\n\tReadBufferSize:  1024,\n\tWriteBufferSize: 1024,\n}\n\nfunc (p *PlayerServer) webSocket(w http.ResponseWriter, r *http.Request) {\n\tconn, _ := wsUpgrader.Upgrade(w, r, nil)\n\t_, winnerMsg, _ := conn.ReadMessage()\n\tp.store.RecordWin(string(winnerMsg))\n}\n```\n\nOur call to `template.ParseFiles(\"game.html\")` will run on every `GET /game` which means we'll go to the file system on every request even though we have no need to re-parse the template. Let's refactor our code so that we parse the template once in `NewPlayerServer` instead. We'll have to make it so this function can now return an error in case we have problems fetching the template from disk or parsing it.\n\nHere's the relevant changes to `PlayerServer`\n\n```go\ntype PlayerServer struct {\n\tstore PlayerStore\n\thttp.Handler\n\ttemplate *template.Template\n}\n\nconst htmlTemplatePath = \"game.html\"\n\nfunc NewPlayerServer(store PlayerStore) (*PlayerServer, error) {\n\tp := new(PlayerServer)\n\n\ttmpl, err := template.ParseFiles(htmlTemplatePath)\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"problem opening %s %v\", htmlTemplatePath, err)\n\t}\n\n\tp.template = tmpl\n\tp.store = store\n\n\trouter := http.NewServeMux()\n\trouter.Handle(\"/league\", http.HandlerFunc(p.leagueHandler))\n\trouter.Handle(\"/players/\", http.HandlerFunc(p.playersHandler))\n\trouter.Handle(\"/game\", http.HandlerFunc(p.game))\n\trouter.Handle(\"/ws\", http.HandlerFunc(p.webSocket))\n\n\tp.Handler = router\n\n\treturn p, nil\n}\n\nfunc (p *PlayerServer) game(w http.ResponseWriter, r *http.Request) {\n\tp.template.Execute(w, nil)\n}\n```\n\nBy changing the signature of `NewPlayerServer` we now have compilation problems. Try and fix them yourself or refer to the source code if you struggle.\n\nFor the test code I made a helper called `mustMakePlayerServer(t *testing.T, store PlayerStore) *PlayerServer` so that I could hide the error noise away from the tests.\n\n```go\nfunc mustMakePlayerServer(t *testing.T, store PlayerStore) *PlayerServer {\n\tserver, err := NewPlayerServer(store)\n\tif err != nil {\n\t\tt.Fatal(\"problem creating player server\", err)\n\t}\n\treturn server\n}\n```\n\nSimilarly, I created another helper `mustDialWS` so that I could hide nasty error noise when creating the WebSocket connection.\n\n```go\nfunc mustDialWS(t *testing.T, url string) *websocket.Conn {\n\tws, _, err := websocket.DefaultDialer.Dial(url, nil)\n\n\tif err != nil {\n\t\tt.Fatalf(\"could not open a ws connection on %s %v\", url, err)\n\t}\n\n\treturn ws\n}\n```\n\nFinally, in our test code we can create a helper to tidy up sending messages\n\n```go\nfunc writeWSMessage(t testing.TB, conn *websocket.Conn, message string) {\n\tt.Helper()\n\tif err := conn.WriteMessage(websocket.TextMessage, []byte(message)); err != nil {\n\t\tt.Fatalf(\"could not send message over ws connection %v\", err)\n\t}\n}\n```\n\nNow the tests are passing try running the server and declare some winners in `/game`. You should see them recorded in `/league`. Remember that every time we get a winner we _close the connection_, you will need to refresh the page to open the connection again.\n\nWe've made a trivial web form that lets users record the winner of a game. Let's iterate on it to make it so the user can start a game by providing a number of players and the server will push messages to the client informing them of what the blind value is as time passes.\n\nFirst update `game.html` to update our client side code for the new requirements\n\n```html\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>Lets play poker</title>\n</head>\n<body>\n<section id=\"game\">\n    <div id=\"game-start\">\n        <label for=\"player-count\">Number of players</label>\n        <input type=\"number\" id=\"player-count\"/>\n        <button id=\"start-game\">Start</button>\n    </div>\n\n    <div id=\"declare-winner\">\n        <label for=\"winner\">Winner</label>\n        <input type=\"text\" id=\"winner\"/>\n        <button id=\"winner-button\">Declare winner</button>\n    </div>\n\n    <div id=\"blind-value\"/>\n</section>\n\n<section id=\"game-end\">\n    <h1>Another great game of poker everyone!</h1>\n    <p><a href=\"/league\">Go check the league table</a></p>\n</section>\n\n</body>\n<script type=\"application/javascript\">\n    const startGame = document.getElementById('game-start')\n\n    const declareWinner = document.getElementById('declare-winner')\n    const submitWinnerButton = document.getElementById('winner-button')\n    const winnerInput = document.getElementById('winner')\n\n    const blindContainer = document.getElementById('blind-value')\n\n    const gameContainer = document.getElementById('game')\n    const gameEndContainer = document.getElementById('game-end')\n\n    declareWinner.hidden = true\n    gameEndContainer.hidden = true\n\n    document.getElementById('start-game').addEventListener('click', event => {\n        startGame.hidden = true\n        declareWinner.hidden = false\n\n        const numberOfPlayers = document.getElementById('player-count').value\n\n        if (window['WebSocket']) {\n            const conn = new WebSocket('ws://' + document.location.host + '/ws')\n\n            submitWinnerButton.onclick = event => {\n                conn.send(winnerInput.value)\n                gameEndContainer.hidden = false\n                gameContainer.hidden = true\n            }\n\n            conn.onclose = evt => {\n                blindContainer.innerText = 'Connection closed'\n            }\n\n            conn.onmessage = evt => {\n                blindContainer.innerText = evt.data\n            }\n\n            conn.onopen = function () {\n                conn.send(numberOfPlayers)\n            }\n        }\n    })\n</script>\n</html>\n```\n\nThe main changes is bringing in a section to enter the number of players and a section to display the blind value. We have a little logic to show/hide the user interface depending on the stage of the game.\n\nAny message we receive via `conn.onmessage` we assume to be blind alerts and so we set the `blindContainer.innerText` accordingly.\n\nHow do we go about sending the blind alerts? In the previous chapter we introduced the idea of `Game` so our CLI code could call a `Game` and everything else would be taken care of including scheduling blind alerts. This turned out to be a good separation of concern.\n\n```go\ntype Game interface {\n\tStart(numberOfPlayers int)\n\tFinish(winner string)\n}\n```\n\nWhen the user was prompted in the CLI for number of players it would `Start` the game which would kick off the blind alerts and when the user declared the winner they would `Finish`. This is the same requirements we have now, just a different way of getting the inputs; so we should look to re-use this concept if we can.\n\nOur \"real\" implementation of `Game` is `TexasHoldem`\n\n```go\ntype TexasHoldem struct {\n\talerter BlindAlerter\n\tstore   PlayerStore\n}\n```\n\nBy sending in a `BlindAlerter` `TexasHoldem` can schedule blind alerts to be sent to _wherever_\n\n```go\ntype BlindAlerter interface {\n\tScheduleAlertAt(duration time.Duration, amount int)\n}\n```\n\nAnd as a reminder, here is our implementation of the `BlindAlerter` we use in the CLI.\n\n```go\nfunc StdOutAlerter(duration time.Duration, amount int) {\n\ttime.AfterFunc(duration, func() {\n\t\tfmt.Fprintf(os.Stdout, \"Blind is now %d\\n\", amount)\n\t})\n}\n```\n\nThis works in CLI because we _always want to send the alerts to `os.Stdout`_ but this won't work for our web server. For every request we get a new `http.ResponseWriter` which we then upgrade to `*websocket.Conn`. So we can't know when constructing our dependencies where our alerts need to go.\n\nFor that reason we need to change `BlindAlerter.ScheduleAlertAt` so that it takes a destination for the alerts so that we can re-use it in our webserver.\n\nOpen `blind_alerter.go` and add the parameter to `io.Writer`\n\n```go\ntype BlindAlerter interface {\n\tScheduleAlertAt(duration time.Duration, amount int, to io.Writer)\n}\n\ntype BlindAlerterFunc func(duration time.Duration, amount int, to io.Writer)\n\nfunc (a BlindAlerterFunc) ScheduleAlertAt(duration time.Duration, amount int, to io.Writer) {\n\ta(duration, amount, to)\n}\n```\n\nThe idea of a `StdoutAlerter` doesn't fit our new model so just rename it to `Alerter`\n\n```go\nfunc Alerter(duration time.Duration, amount int, to io.Writer) {\n\ttime.AfterFunc(duration, func() {\n\t\tfmt.Fprintf(to, \"Blind is now %d\\n\", amount)\n\t})\n}\n```\n\nIf you try and compile, it will fail in `TexasHoldem` because it is calling `ScheduleAlertAt` without a destination, to get things compiling again _for now_ hard-code it to `os.Stdout`.\n\nTry and run the tests and they will fail because `SpyBlindAlerter` no longer implements `BlindAlerter`, fix this by updating the signature of `ScheduleAlertAt`, run the tests and we should still be green.\n\nIt doesn't make any sense for `TexasHoldem` to know where to send blind alerts. Let's now update `Game` so that when you start a game you declare _where_ the alerts should go.\n\n```go\ntype Game interface {\n\tStart(numberOfPlayers int, alertsDestination io.Writer)\n\tFinish(winner string)\n}\n```\n\nLet the compiler tell you what you need to fix. The change isn't so bad:\n\n* Update `TexasHoldem` so it properly implements `Game`\n* In `CLI` when we start the game, pass in our `out` property (`cli.game.Start(numberOfPlayers, cli.out)`)\n* In `TexasHoldem`'s test i use `game.Start(5, io.Discard)` to fix the compilation problem and configure the alert output to be discarded\n\nIf you've got everything right, everything should be green! Now we can try and use `Game` within `Server`.\n\n## Write the test first\n\nThe requirements of `CLI` and `Server` are the same! It's just the delivery mechanism is different.\n\nLet's take a look at our `CLI` test for inspiration.\n\n```go\nt.Run(\"start game with 3 players and finish game with 'Chris' as winner\", func(t *testing.T) {\n\tgame := &GameSpy{}\n\n\tout := &bytes.Buffer{}\n\tin := userSends(\"3\", \"Chris wins\")\n\n\tpoker.NewCLI(in, out, game).PlayPoker()\n\n\tassertMessagesSentToUser(t, out, poker.PlayerPrompt)\n\tassertGameStartedWith(t, game, 3)\n\tassertFinishCalledWith(t, game, \"Chris\")\n})\n```\n\nIt looks like we should be able to test drive out a similar outcome using `GameSpy`\n\nReplace the old websocket test with the following\n\n```go\nt.Run(\"start a game with 3 players and declare Ruth the winner\", func(t *testing.T) {\n\tgame := &poker.GameSpy{}\n\twinner := \"Ruth\"\n\tserver := httptest.NewServer(mustMakePlayerServer(t, dummyPlayerStore, game))\n\tws := mustDialWS(t, \"ws\"+strings.TrimPrefix(server.URL, \"http\")+\"/ws\")\n\n\tdefer server.Close()\n\tdefer ws.Close()\n\n\twriteWSMessage(t, ws, \"3\")\n\twriteWSMessage(t, ws, winner)\n\n\ttime.Sleep(10 * time.Millisecond)\n\tassertGameStartedWith(t, game, 3)\n\tassertFinishCalledWith(t, game, winner)\n})\n```\n\n* As discussed we create a spy `Game` and pass it into `mustMakePlayerServer` (be sure to update the helper to support this).\n* We then send the web socket messages for a game.\n* Finally we assert that the game is started and finished with what we expect.\n\n## Try to run the test\n\nYou'll have a number of compilation errors around `mustMakePlayerServer` in other tests. Introduce an unexported variable `dummyGame` and use it through all the tests that aren't compiling\n\n```go\nvar (\n\tdummyGame = &GameSpy{}\n)\n```\n\nThe final error is where we are trying to pass in `Game` to `NewPlayerServer` but it doesn't support it yet\n\n```\n./server_test.go:21:38: too many arguments in call to \"github.com/quii/learn-go-with-tests/WebSockets/v2\".NewPlayerServer\n\thave (\"github.com/quii/learn-go-with-tests/WebSockets/v2\".PlayerStore, \"github.com/quii/learn-go-with-tests/WebSockets/v2\".Game)\n\twant (\"github.com/quii/learn-go-with-tests/WebSockets/v2\".PlayerStore)\n```\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\nJust add it as an argument for now just to get the test running\n\n```go\nfunc NewPlayerServer(store PlayerStore, game Game) (*PlayerServer, error)\n```\n\nFinally!\n\n```\n=== RUN   TestGame/start_a_game_with_3_players_and_declare_Ruth_the_winner\n--- FAIL: TestGame (0.01s)\n    --- FAIL: TestGame/start_a_game_with_3_players_and_declare_Ruth_the_winner (0.01s)\n    \tserver_test.go:146: wanted Start called with 3 but got 0\n    \tserver_test.go:147: expected finish called with 'Ruth' but got ''\nFAIL\n```\n\n## Write enough code to make it pass\n\nWe need to add `Game` as a field to `PlayerServer` so that it can use it when it gets requests.\n\n```go\ntype PlayerServer struct {\n\tstore PlayerStore\n\thttp.Handler\n\ttemplate *template.Template\n\tgame     Game\n}\n```\n\n(We already have a method called `game` so rename that to `playGame`)\n\nNext lets assign it in our constructor\n\n```go\nfunc NewPlayerServer(store PlayerStore, game Game) (*PlayerServer, error) {\n\tp := new(PlayerServer)\n\n\ttmpl, err := template.ParseFiles(htmlTemplatePath)\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"problem opening %s %v\", htmlTemplatePath, err)\n\t}\n\n\tp.game = game\n\n\t// etc\n}\n```\n\nNow we can use our `Game` within `webSocket`.\n\n```go\nfunc (p *PlayerServer) webSocket(w http.ResponseWriter, r *http.Request) {\n\tconn, _ := wsUpgrader.Upgrade(w, r, nil)\n\n\t_, numberOfPlayersMsg, _ := conn.ReadMessage()\n\tnumberOfPlayers, _ := strconv.Atoi(string(numberOfPlayersMsg))\n\tp.game.Start(numberOfPlayers, io.Discard) //todo: Don't discard the blinds messages!\n\n\t_, winner, _ := conn.ReadMessage()\n\tp.game.Finish(string(winner))\n}\n```\n\nHooray! The tests pass.\n\nWe are not going to send the blind messages anywhere _just yet_ as we need to have a think about that. When we call `game.Start` we send in `io.Discard` which will just discard any messages written to it.\n\nFor now start the web server up. You'll need to update the `main.go` to pass a `Game` to the `PlayerServer`\n\n```go\nfunc main() {\n\tdb, err := os.OpenFile(dbFileName, os.O_RDWR|os.O_CREATE, 0666)\n\n\tif err != nil {\n\t\tlog.Fatalf(\"problem opening %s %v\", dbFileName, err)\n\t}\n\n\tstore, err := poker.NewFileSystemPlayerStore(db)\n\n\tif err != nil {\n\t\tlog.Fatalf(\"problem creating file system player store, %v \", err)\n\t}\n\n\tgame := poker.NewTexasHoldem(poker.BlindAlerterFunc(poker.Alerter), store)\n\n\tserver, err := poker.NewPlayerServer(store, game)\n\n\tif err != nil {\n\t\tlog.Fatalf(\"problem creating player server %v\", err)\n\t}\n\n\tlog.Fatal(http.ListenAndServe(\":5000\", server))\n}\n```\n\nDiscounting the fact we're not getting blind alerts yet, the app does work! We've managed to re-use `Game` with `PlayerServer` and it has taken care of all the details. Once we figure out how to send our blind alerts through to the web sockets rather than discarding them it _should_ all work.\n\nBefore that though, let's tidy up some code.\n\n## Refactor\n\nThe way we're using WebSockets is fairly basic and the error handling is fairly naive, so I wanted to encapsulate that in a type just to remove that messiness from the server code. We may wish to revisit it later but for now this'll tidy things up a bit\n\n```go\ntype playerServerWS struct {\n\t*websocket.Conn\n}\n\nfunc newPlayerServerWS(w http.ResponseWriter, r *http.Request) *playerServerWS {\n\tconn, err := wsUpgrader.Upgrade(w, r, nil)\n\n\tif err != nil {\n\t\tlog.Printf(\"problem upgrading connection to WebSockets %v\\n\", err)\n\t}\n\n\treturn &playerServerWS{conn}\n}\n\nfunc (w *playerServerWS) WaitForMsg() string {\n\t_, msg, err := w.ReadMessage()\n\tif err != nil {\n\t\tlog.Printf(\"error reading from websocket %v\\n\", err)\n\t}\n\treturn string(msg)\n}\n```\n\nNow the server code is a bit simplified\n\n```go\nfunc (p *PlayerServer) webSocket(w http.ResponseWriter, r *http.Request) {\n\tws := newPlayerServerWS(w, r)\n\n\tnumberOfPlayersMsg := ws.WaitForMsg()\n\tnumberOfPlayers, _ := strconv.Atoi(numberOfPlayersMsg)\n\tp.game.Start(numberOfPlayers, io.Discard) //todo: Don't discard the blinds messages!\n\n\twinner := ws.WaitForMsg()\n\tp.game.Finish(winner)\n}\n```\n\nOnce we figure out how to not discard the blind messages we're done.\n\n### Let's _not_ write a test!\n\nSometimes when we're not sure how to do something, it's best just to play around and try things out! Make sure your work is committed first because once we've figured out a way we should drive it through a test.\n\nThe problematic line of code we have is\n\n```go\np.game.Start(numberOfPlayers, io.Discard) //todo: Don't discard the blinds messages!\n```\n\nWe need to pass in an `io.Writer` for the game to write the blind alerts to.\n\nWouldn't it be nice if we could pass in our `playerServerWS` from before? It's our wrapper around our WebSocket so it _feels_ like we should be able to send that to our `Game` to send messages to.\n\nGive it a go:\n\n```go\nfunc (p *PlayerServer) webSocket(w http.ResponseWriter, r *http.Request) {\n\tws := newPlayerServerWS(w, r)\n\n\tnumberOfPlayersMsg := ws.WaitForMsg()\n\tnumberOfPlayers, _ := strconv.Atoi(numberOfPlayersMsg)\n\tp.game.Start(numberOfPlayers, ws)\n\t//etc...\n}\n```\n\nThe compiler complains\n\n```\n./server.go:71:14: cannot use ws (type *playerServerWS) as type io.Writer in argument to p.game.Start:\n\t*playerServerWS does not implement io.Writer (missing Write method)\n```\n\nIt seems the obvious thing to do, would be to make it so `playerServerWS` _does_ implement `io.Writer`. To do so we use the underlying `*websocket.Conn` to use `WriteMessage` to send the message down the websocket\n\n```go\nfunc (w *playerServerWS) Write(p []byte) (n int, err error) {\n\terr = w.WriteMessage(websocket.TextMessage, p)\n\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn len(p), nil\n}\n```\n\nThis seems too easy! Try and run the application and see if it works.\n\nBeforehand edit `TexasHoldem` so that the blind increment time is shorter so you can see it in action\n\n```go\nblindIncrement := time.Duration(5+numberOfPlayers) * time.Second // (rather than a minute)\n```\n\nYou should see it working! The blind amount increments in the browser as if by magic.\n\nNow let's revert the code and think how to test it. In order to _implement_ it all we did was pass through to `StartGame` was `playerServerWS` rather than `io.Discard` so that might make you think we should perhaps spy on the call to verify it works.\n\nSpying is great and helps us check implementation details but we should always try and favour testing the _real_ behaviour if we can because when you decide to refactor it's often spy tests that start failing because they are usually checking implementation details that you're trying to change.\n\nOur test currently opens a websocket connection to our running server and sends messages to make it do things. Equally we should be able to test the messages our server sends back over the websocket connection.\n\n## Write the test first\n\nWe'll edit our existing test.\n\nCurrently, our `GameSpy` does not send any data to `out` when you call `Start`. We should change it so we can configure it to send a canned message and then we can check that message gets sent to the websocket. This should give us confidence that we have configured things correctly whilst still exercising the real behaviour we want.\n\n```go\ntype GameSpy struct {\n\tStartCalled     bool\n\tStartCalledWith int\n\tBlindAlert      []byte\n\n\tFinishedCalled   bool\n\tFinishCalledWith string\n}\n```\n\nAdd `BlindAlert` field.\n\nUpdate `GameSpy` `Start` to send the canned message to `out`.\n\n```go\nfunc (g *GameSpy) Start(numberOfPlayers int, out io.Writer) {\n\tg.StartCalled = true\n\tg.StartCalledWith = numberOfPlayers\n\tout.Write(g.BlindAlert)\n}\n```\n\nThis now means when we exercise `PlayerServer` when it tries to `Start` the game it should end up sending messages through the websocket if things are working right.\n\nFinally, we can update the test\n\n```go\nt.Run(\"start a game with 3 players, send some blind alerts down WS and declare Ruth the winner\", func(t *testing.T) {\n\twantedBlindAlert := \"Blind is 100\"\n\twinner := \"Ruth\"\n\n\tgame := &GameSpy{BlindAlert: []byte(wantedBlindAlert)}\n\tserver := httptest.NewServer(mustMakePlayerServer(t, dummyPlayerStore, game))\n\tws := mustDialWS(t, \"ws\"+strings.TrimPrefix(server.URL, \"http\")+\"/ws\")\n\n\tdefer server.Close()\n\tdefer ws.Close()\n\n\twriteWSMessage(t, ws, \"3\")\n\twriteWSMessage(t, ws, winner)\n\n\ttime.Sleep(10 * time.Millisecond)\n\tassertGameStartedWith(t, game, 3)\n\tassertFinishCalledWith(t, game, winner)\n\n\t_, gotBlindAlert, _ := ws.ReadMessage()\n\n\tif string(gotBlindAlert) != wantedBlindAlert {\n\t\tt.Errorf(\"got blind alert %q, want %q\", string(gotBlindAlert), wantedBlindAlert)\n\t}\n})\n```\n\n* We've added a `wantedBlindAlert` and configured our `GameSpy` to send it to `out` if `Start` is called.\n* We hope it gets sent in the websocket connection so we've added a call to `ws.ReadMessage()` to wait for a message to be sent and then check it's the one we expected.\n\n## Try to run the test\n\nYou should find the test hangs forever. This is because `ws.ReadMessage()` will block until it gets a message, which it never will.\n\n## Write the minimal amount of code for the test to run and check the failing test output\n\nWe should never have tests that hang so let's introduce a way of handling code that we want to timeout.\n\n```go\nfunc within(t testing.TB, d time.Duration, assert func()) {\n\tt.Helper()\n\n\tdone := make(chan struct{}, 1)\n\n\tgo func() {\n\t\tassert()\n\t\tdone <- struct{}{}\n\t}()\n\n\tselect {\n\tcase <-time.After(d):\n\t\tt.Error(\"timed out\")\n\tcase <-done:\n\t}\n}\n```\n\nWhat `within` does is take a function `assert` as an argument and then runs it in a go routine. If/When the function finishes it will signal it is done via the `done` channel.\n\nWhile that happens we use a `select` statement which lets us wait for a channel to send a message. From here it is a race between the `assert` function and `time.After` which will send a signal when the duration has occurred.\n\nFinally, I made a helper function for our assertion just to make things a bit neater\n\n```go\nfunc assertWebsocketGotMsg(t *testing.T, ws *websocket.Conn, want string) {\n\t_, msg, _ := ws.ReadMessage()\n\tif string(msg) != want {\n\t\tt.Errorf(`got \"%s\", want \"%s\"`, string(msg), want)\n\t}\n}\n```\n\nHere's how the test reads now\n\n```go\nt.Run(\"start a game with 3 players, send some blind alerts down WS and declare Ruth the winner\", func(t *testing.T) {\n\twantedBlindAlert := \"Blind is 100\"\n\twinner := \"Ruth\"\n\n\tgame := &GameSpy{BlindAlert: []byte(wantedBlindAlert)}\n\tserver := httptest.NewServer(mustMakePlayerServer(t, dummyPlayerStore, game))\n\tws := mustDialWS(t, \"ws\"+strings.TrimPrefix(server.URL, \"http\")+\"/ws\")\n\n\tdefer server.Close()\n\tdefer ws.Close()\n\n\twriteWSMessage(t, ws, \"3\")\n\twriteWSMessage(t, ws, winner)\n\n\ttime.Sleep(tenMS)\n\n\tassertGameStartedWith(t, game, 3)\n\tassertFinishCalledWith(t, game, winner)\n\twithin(t, tenMS, func() { assertWebsocketGotMsg(t, ws, wantedBlindAlert) })\n})\n```\n\nNow if you run the test...\n\n```\n=== RUN   TestGame\n=== RUN   TestGame/start_a_game_with_3_players,_send_some_blind_alerts_down_WS_and_declare_Ruth_the_winner\n--- FAIL: TestGame (0.02s)\n    --- FAIL: TestGame/start_a_game_with_3_players,_send_some_blind_alerts_down_WS_and_declare_Ruth_the_winner (0.02s)\n    \tserver_test.go:143: timed out\n    \tserver_test.go:150: got \"\", want \"Blind is 100\"\n```\n\n## Write enough code to make it pass\n\nFinally, we can now change our server code, so it sends our WebSocket connection to the game when it starts\n\n```go\nfunc (p *PlayerServer) webSocket(w http.ResponseWriter, r *http.Request) {\n\tws := newPlayerServerWS(w, r)\n\n\tnumberOfPlayersMsg := ws.WaitForMsg()\n\tnumberOfPlayers, _ := strconv.Atoi(numberOfPlayersMsg)\n\tp.game.Start(numberOfPlayers, ws)\n\n\twinner := ws.WaitForMsg()\n\tp.game.Finish(winner)\n}\n```\n\n## Refactor\n\nThe server code was a very small change so there's not a lot to change here but the test code still has a `time.Sleep` call because we have to wait for our server to do its work asynchronously.\n\nWe can refactor our helpers `assertGameStartedWith` and `assertFinishCalledWith` so that they can retry their assertions for a short period before failing.\n\nHere's how you can do it for `assertFinishCalledWith` and you can use the same approach for the other helper.\n\n```go\nfunc assertFinishCalledWith(t testing.TB, game *GameSpy, winner string) {\n\tt.Helper()\n\n\tpassed := retryUntil(500*time.Millisecond, func() bool {\n\t\treturn game.FinishCalledWith == winner\n\t})\n\n\tif !passed {\n\t\tt.Errorf(\"expected finish called with %q but got %q\", winner, game.FinishCalledWith)\n\t}\n}\n```\n\nHere is how `retryUntil` is defined\n\n```go\nfunc retryUntil(d time.Duration, f func() bool) bool {\n\tdeadline := time.Now().Add(d)\n\tfor time.Now().Before(deadline) {\n\t\tif f() {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n```\n\n## Wrapping up\n\nOur application is now complete. A game of poker can be started via a web browser and the users are informed of the blind bet value as time goes by via WebSockets. When the game finishes they can record the winner which is persisted using code we wrote a few chapters ago. The players can find out who is the best (or luckiest) poker player using the website's `/league` endpoint.\n\nThrough the journey we have made mistakes but with the TDD flow we have never been very far away from working software. We were free to keep iterating and experimenting.\n\nThe final chapter will retrospect on the approach, the design we've arrived at and tie up some loose ends.\n\nWe covered a few things in this chapter\n\n### WebSockets\n\n* Convenient way of sending messages between clients and servers that does not require the client to keep polling the server. Both the client and server code we have is very simple.\n* Trivial to test, but you have to be wary of the asynchronous nature of the tests\n\n### Handling code in tests that can be delayed or never finish\n\n* Create helper functions to retry assertions and add timeouts.\n* We can use go routines to ensure the assertions don't block anything and then use channels to let them signal that they have finished, or not.\n* The `time` package has some helpful functions which also send signals via channels about events in time so we can set timeouts\n"
  },
  {
    "path": "why.md",
    "content": "# Why unit tests and how to make them work for you\n\n[Here's a link to a video of me chatting about this topic](https://www.youtube.com/watch?v=Kwtit8ZEK7U)\n\nIf you're not into videos, here's wordy version of it.\n\n## Software \n\nThe promise of software is that it can change. This is why it is called _soft_ ware, it is malleable compared to hardware. A great engineering team should be an amazing asset to a company, writing systems that can evolve with a business to keep delivering value. \n\nSo why are we so bad at it? How many projects do you hear about that outright fail? Or become \"legacy\" and have to be entirely re-written (and the re-writes often fail too!) \n\nHow does a software system \"fail\" anyway? Can't it just be changed until it's correct? That's what we're promised!\n\nA lot of people are choosing Go to build systems because it has made a number of choices which one hopes will make it more legacy-proof. \n\n- Compared to my previous life of Scala where [I described how it has enough rope to hang yourself](http://www.quii.dev/Scala_-_Just_enough_rope_to_hang_yourself), Go has only 25 keywords and _a lot_ of systems can be built from the standard library and a few other small libraries. The hope is that with Go you can write code and come back to it in 6 months time and it'll still make sense.\n- The tooling in respect to testing, benchmarking, linting & shipping is first class compared to most alternatives.\n- The standard library is brilliant.\n- Very fast compilation speed for tight feedback loops\n- The Go backward compatibility promise. It looks like Go will get generics and other features in the future but the designers have promised that even Go code you wrote 5 years ago will still build. I literally spent weeks upgrading a project from Scala 2.8 to 2.10. \n\nEven with all these great properties we can still make terrible systems, so we should look to the past and understand lessons in software engineering that apply no matter how shiny (or not) your language is.\n\nIn 1974 a clever software engineer called [Manny Lehman](https://en.wikipedia.org/wiki/Manny_Lehman_%28computer_scientist%29) wrote [Lehman's laws of software evolution](https://en.wikipedia.org/wiki/Lehman%27s_laws_of_software_evolution).\n\n> The laws describe a balance between forces driving new developments on one hand, and forces that slow down progress on the other hand.\n\nThese forces seem like important things to understand if we have any hope of not being in an endless cycle of shipping systems that turn into legacy and then get re-written over and over again.\n\n## The Law of Continuous Change\n\n> Any software system used in the real-world must change or become less and less useful in the environment\n\nIt feels obvious that a system _has_ to change or it becomes less useful but how often is this ignored? \n\nMany teams are incentivised to deliver a project on a particular date and then move on to the next project. If the software is \"lucky\" there is at least some kind of hand-off to another set of individuals to maintain it, but they didn't write it of course. \n\nPeople often concern themselves with trying to pick a framework which will help them \"deliver quickly\" but not focusing on the longevity of the system in terms of how it needs to evolve.\n\nEven if you're an incredible software engineer, you will still fall victim to not knowing the future needs of your system. As the business changes some of the brilliant code you wrote is now no longer relevant.\n\nLehman was on a roll in the 70s because he gave us another law to chew on.\n\n## The Law of Increasing Complexity\n\n> As a system evolves, its complexity increases unless work is done to reduce it\n\nWhat he's saying here is we can't have software teams as blind feature factories, piling more and more features on to software in the hope it will survive in the long run. \n\nWe **have** to keep managing the complexity of the system as the knowledge of our domain changes. \n\n## Refactoring\n\nThere are _many_ facets of software engineering that keeps software malleable, such as:\n\n- Developer empowerment\n- Generally \"good\" code. Sensible separation of concerns, etc etc\n- Communication skills\n- Architecture\n- Observability\n- Deployability\n- Automated tests\n- Feedback loops\n\nI am going to focus on refactoring. It's a phrase that gets thrown around a lot \"we need to refactor this\" - said to a developer on their first day of programming without a second thought. \n\nWhere does the phrase come from? How is refactoring just different from writing code?\n\nI know that I and many others have _thought_ we were doing refactoring but we were mistaken\n\n[Martin Fowler describes how people are getting it wrong](https://martinfowler.com/bliki/RefactoringMalapropism.html)\n\n> However the term \"refactoring\" is often used when it's not appropriate. If somebody talks about a system being broken for a couple of days while they are refactoring, you can be pretty sure they are not refactoring.\n\nSo what is it?\n\n### Factorisation\n\nWhen learning maths at school you probably learned about factorisation. Here's a very simple example\n\nCalculate `1/2 + 1/4`\n\nTo do this you _factorise_ the denominators, turning the expression into \n\n`2/4 + 1/4` which you can then turn into `3/4`. \n\nWe can take some important lessons from this. When we _factorise the expression_ we have **not changed the meaning of the expression**. Both of them equal `3/4` but we have made it easier for us to work with; by changing `1/2` to `2/4` it fits into our \"domain\" easier. \n\nWhen you refactor your code, you are trying to find ways of making your code easier to understand and \"fit\" into your current understanding of what the system needs to do. Crucially **you should not be changing behaviour**. \n\n#### An example in Go\n\nHere is a function which greets `name` in a particular `language`\n\n    func Hello(name, language string) string {\n    \n      if language == \"es\" {\n         return \"Hola, \" + name\n      }\n    \n      if language == \"fr\" {\n         return \"Bonjour, \" + name\n      }\n      \n      // imagine dozens more languages\n    \n      return \"Hello, \" + name\n    }\n\nHaving dozens of `if` statements doesn't feel good and we have a duplication of concatenating a language specific greeting with `, ` and the `name.` So I'll refactor the code.\n\n    func Hello(name, language string) string {\n      \treturn fmt.Sprintf(\n      \t\t\"%s, %s\",\n      \t\tgreeting(language),\n      \t\tname,\n      \t)\n    }\n    \n    var greetings = map[string]string {\n      \"es\": \"Hola\",\n      \"fr\": \"Bonjour\",\n      //etc..\n    }\n    \n    func greeting(language string) string {\n      greeting, exists := greetings[language]\n      \n      if exists {\n         return greeting\n      }\n      \n      return \"Hello\"\n    }\n\nThe nature of this refactor isn't actually important, what's important is I haven't changed behaviour. \n\nWhen refactoring you can do whatever you like, add interfaces, new types, functions, methods etc. The only rule is you don't change behaviour\n\n### When refactoring code you must not be changing behaviour\n\nThis is very important. If you are changing behaviour at the same time you are doing _two_ things at once. As software engineers we learn to break systems up into different files/packages/functions/etc because we know trying to understand a big blob of stuff is hard. \n\nWe don't want to have to be thinking about lots of things at once because that's when we make mistakes. I've witnessed so many refactoring endeavours fail because the developers are biting off more than they can chew.  \n\nWhen I was doing factorisations in maths classes with pen and paper I would have to manually check that I hadn't changed the meaning of the expressions in my head. How do we know we aren't changing behaviour when refactoring when working with code, especially on a system that is non-trivial?\n\nThose who choose not to write tests will typically be reliant on manual testing. For anything other than a small project this will be a tremendous time-sink and does not scale in the long run. \n \n**In order to safely refactor you need unit tests** because they provide\n\n- Confidence you can reshape code without worrying about changing behaviour\n- Documentation for humans as to how the system should behave\n- Much faster and more reliable feedback than manual testing\n\n#### An example in Go\n\nA unit test for our `Hello` function could look like this\n\n    func TestHello(t *testing.T) {\n      got := Hello(“Chris”, es)\n      want := \"Hola, Chris\"\n    \n      if got != want {\n         t.Errorf(\"got %q want %q\", got, want)\n      }\n    }\n\nAt the command line I can run `go test` and get immediate feedback as to whether my refactoring efforts have altered behaviour. In practice it's best to learn the magic button to run your tests within your editor/IDE. \n\nYou want to get in to a state where you are doing \n\n- Small refactor\n- Run tests\n- Repeat\n\nAll within a very tight feedback loop so you don't go down rabbit holes and make mistakes.\n\nHaving a project where all your key behaviours are unit tested and give you feedback well under a second is a very empowering safety net to do bold refactoring when you need to. This helps us manage the incoming force of complexity that Lehman describes.\n\n## If unit tests are so great, why is there sometimes resistance to writing them?\n\nOn the one hand you have people (like me) saying that unit tests are important for the long term health of your system because they ensure you can keep refactoring with confidence. \n\nOn the other you have people describing experiences of unit tests actually _hindering_ refactoring.\n\nAsk yourself, how often do you have to change your tests when refactoring? Over the years I have been on many projects with very good test coverage and yet the engineers are reluctant to refactor because of the perceived effort of changing tests.\n\nThis is the opposite of what we are promised!\n\n### Why is this happening?\n\nImagine you were asked to develop a square and we thought the best way to accomplish that would be stick two triangles together. \n\n![Two right-angled triangles to form a square](https://i.imgur.com/ela7SVf.jpg)\n\nWe write our unit tests around our square to make sure the sides are equal and then we write some tests around our triangles. We want to make sure our triangles render correctly so we assert that the angles sum up to 180 degrees, perhaps check we make 2 of them, etc etc. Test coverage is really important and writing these tests is pretty easy so why not? \n\nA few weeks later The Law of Continuous Change strikes our system and a new developer makes some changes. She now believes it would be better if squares were formed with 2 rectangles instead of 2 triangles. \n\n![Two rectangles to form a square](https://i.imgur.com/1G6rYqD.jpg)\n\nShe tries to do this refactor and gets mixed signals from a number of failing tests. Has she actually broken important behaviours here? She now has to dig through these triangle tests and try and understand what's going on. \n\n_It's not actually important that the square was formed out of triangles_ but **our tests have falsely elevated the importance of our implementation details**. \n\n## Favour testing behaviour rather than implementation detail\n\nWhen I hear people complaining about unit tests it is often because the tests are at the wrong abstraction level. They're testing implementation details, overly spying on collaborators and mocking too much. \n\nI believe it stems from a misunderstanding of what unit tests are and chasing vanity metrics (test coverage). \n\nIf I am saying just test behaviour, should we not just only write system/black-box tests? These kind of tests do have lots of value in terms of verifying key user journeys but they are typically expensive to write and slow to run. For that reason they're not too helpful for _refactoring_ because the feedback loop is slow. In addition black box tests don't tend to help you very much with root causes compared to unit tests. \n\nSo what _is_ the right abstraction level?\n\n## Writing effective unit tests is a design problem\n\nForgetting about tests for a moment, it is desirable to have within your system self-contained, decoupled \"units\" centered around key concepts in your domain. \n\nI like to imagine these units as simple Lego bricks which have coherent APIs that I can combine with other bricks to make bigger systems. Underneath these APIs there could be dozens of things (types, functions et al) collaborating to make them work how they need to.\n\nFor instance if you were writing a bank in Go, you might have an \"account\" package. It will present an API that does not leak implementation detail and is easy to integrate with.\n\nIf you have these units that follow these properties you can write unit tests against their public APIs. _By definition_ these tests can only be testing useful behaviour. Underneath these units I am free to refactor the implementation as much as I need to and the tests for the most part should not get in the way.\n\n### Are these unit tests?\n\n**YES**. Unit tests are against \"units\" like I described. They were _never_ about only being against a single class/function/whatever.\n\n## Bringing these concepts together\n\nWe've covered\n\n- Refactoring\n- Unit tests\n- Unit design\n\nWhat we can start to see is that these facets of software design reinforce each other. \n\n### Refactoring\n\n- Gives us signals about our unit tests. If we have to do manual checks, we need more tests. If tests are wrongly failing then our tests are at the wrong abstraction level (or have no value and should be deleted).\n- Helps us handle the complexities within and between our units.\n\n### Unit tests\n\n- Give a safety net to refactor.\n- Verify and document the behaviour of our units.\n\n### (Well designed) units\n\n- Easy to write _meaningful_ unit tests.\n- Easy to refactor.\n\nIs there a process to help us arrive at a point where we can constantly refactor our code to manage complexity and keep our systems malleable?\n\n## Why Test Driven Development (TDD)\n\nSome people might take Lehman's quotes about how software has to change and overthink elaborate designs, wasting lots of time upfront trying to create the \"perfect\" extensible system and end up getting it wrong and going nowhere. \n\nThis is the bad old days of software where an analyst team would spend 6 months writing a requirements document and an architect team would spend another 6 months coming up with a design and a few years later the whole project fails.\n\nI say bad old days but this still happens! \n\nAgile teaches us that we need to work iteratively, starting small and evolving the software so that we get fast feedback on the design of our software and how it works with real users;  TDD enforces this approach.\n\nTDD addresses the laws that Lehman talks about and other lessons hard learned through history by encouraging a methodology of constantly refactoring and delivering iteratively.\n\n### Small steps\n\n- Write a small test for a small amount of desired behaviour\n- Check the test fails with a clear error (red)\n- Write the minimal amount of code to make the test pass (green)\n- Refactor\n- Repeat\n\nAs you become proficient, this way of working will become natural and fast.\n\nYou'll come to expect this feedback loop to not take very long and feel uneasy if you're in a state where the system isn't \"green\" because it indicates you may be down a rabbit hole. \n\nYou'll always be driving small & useful functionality comfortably backed by the feedback from your tests.\n\n## Wrapping up \n\n- The strength of software is that we can change it. _Most_ software will require change over time in unpredictable ways; but don't try and over-engineer because it's too hard to predict the future.\n- Instead we need to make it so we can keep our software malleable. In order to change software we have to refactor it as it evolves or it will turn into a mess\n- A good test suite can help you refactor quicker and in a less stressful manner\n- Writing good unit tests is a design problem so think about structuring your code so you have meaningful units that you can integrate together like Lego bricks.\n- TDD can help and force you to design well factored software iteratively, backed by tests to help future work as it arrives.\n"
  },
  {
    "path": "working-without-mocks.md",
    "content": "# Working without mocks, stubs and spies\n\nThis chapter delves into the world of test doubles and explores how they influence the testing and development process. We'll uncover the limitations of traditional mocks, stubs, and spies and introduce a more efficient and adaptable approach using fakes and contracts.\n\n## tl;dr\n\n- Mocks, spies and stubs encourage you to encode assumptions of the behaviour of your dependencies ad-hocly in each test.\n- These assumptions are usually not validated beyond manual checking, so they threaten your test suite's usefulness.\n- Fakes and contracts give us a more sustainable method for creating test doubles with validated assumptions and better reuse than the alternatives.\n\nThis is a longer chapter than normal, so as a palette cleanser, you should explore an [example repo first](https://github.com/quii/go-fakes-and-contracts). In particular, check out the [planner test](https://github.com/quii/go-fakes-and-contracts/blob/main/domain/planner/planner_test.go).\n\n---\n\nIn [Mocking,](https://quii.gitbook.io/learn-go-with-tests/go-fundamentals/mocking) we learned how mocks, stubs and spies are useful tools for controlling and inspecting the behaviour of units of code in conjunction with [Dependency Injection](https://quii.gitbook.io/learn-go-with-tests/go-fundamentals/dependency-injection).\n\nAs a project grows, though, these kinds of test doubles *can* become a maintenance burden, and we should instead look to other design ideas to keep our system easy to reason and test.\n\n**Fakes** and **contracts** allow developers to test their systems with more realistic scenarios, improve local development experience with faster and more accurate feedback loops, and manage the complexity of evolving dependencies.\n\n### A primer on test doubles\n\nIt's easy to roll your eyes when people like me are pedantic about the nomenclature of test doubles, but the distinctive kinds of test doubles help us talk about this topic and the trade-offs we're making with clarity.\n\n**Test doubles** is the collective noun for the different ways you can construct dependencies that you can control for a **subject under test** **(SUT)**, the thing you're testing. Test doubles are often a better alternative than using the real dependency as it can avoid issues like\n\n- Needing the internet to use an API\n- Avoid latency and other performance issues\n- Unable to exercise non-happy path cases\n- Decoupling your build from another team's.\n  - You wouldn't want to prevent deployments if an engineer in another team accidentally shipped a bug\n\nIn Go, you'll typically model a dependency with an interface, then implement your version to control the behaviour in a test. **Here are the kinds of test doubles covered in this post**.\n\nGiven this interface of a hypothetical recipe API:\n\n```go\ntype RecipeBook interface {\n\tGetRecipes() ([]Recipe, error)\n\tAddRecipes(...Recipe) error\n}\n```\n\nWe can construct test doubles in various ways, depending on how we're trying to test something that uses a `RecipeBook`.\n\n**Stubs** return the same canned data every time they are called\n\n```go\ntype StubRecipeStore struct {\n\trecipes []Recipe\n\terr     error\n}\n\nfunc (s *StubRecipeStore) GetRecipes() ([]Recipe, error) {\n\treturn s.recipes, s.err\n}\n\n// AddRecipes omitted for brevity\n```\n\n```go\n// in test, we can set up the stub to always return specific recipes, or an error\nstubStore := &StubRecipeStore{\n\trecipes: someRecipes,\n}\n```\n\n**Spies** are like stubs but also record how they were called so the test can assert that the SUT calls the dependencies in specific ways.\n\n```go\ntype SpyRecipeStore struct {\n\tAddCalls [][]Recipe\n\terr      error\n}\n\nfunc (s *SpyRecipeStore) AddRecipes(r ...Recipe) error {\n\ts.AddCalls = append(s.AddCalls, r)\n\treturn s.err\n}\n\n// GetRecipes omitted for brevity\n```\n\n```go\n// in test\nspyStore := &SpyRecipeStore{}\nsut := NewThing(spyStore)\nsut.DoStuff()\n\n// now we can check the store had the right recipes added by inspectiong spyStore.AddCalls\n```\n\n**Mocks** are like a superset of the above, but they only respond with specific data to specific invocations. If the SUT calls the dependencies with the wrong arguments, it'll typically panic.\n\n```go\n// set up the mock with expected calls\nmockStore := &MockRecipeStore{}\nmockStore.WhenCalledWith(someRecipes).Return(someError)\n\n// when the sut uses the dependency, if it doesn't call it with someRecipes, usually mocks will panic\n```\n\n**Fakes** are like a genuine version of the dependency but implemented in a way more suited to fast running, reliable tests and local development. Often, your system will have some abstraction around persistence, which will be implemented with a database, but in your tests, you could use an in-memory fake instead.\n\n```go\ntype FakeRecipeStore struct {\n\trecipes []Recipe\n}\n\nfunc (f *FakeRecipeStore) GetRecipes() ([]Recipe, error) {\n\treturn f.recipes, nil\n}\n\nfunc (f *FakeRecipeStore) AddRecipes(r ...Recipe) error {\n\tf.recipes = append(f.recipes, r...)\n\treturn nil\n}\n```\n\nFakes are useful because:\n\n- Their statefulness is useful for tests involving multiple subjects and invocations, such as an integration test. Managing state with the other kinds of test doubles is generally discouraged.\n- If they have a sensible API, offer a more natural way of asserting state. Rather than spying on specific calls to a dependency, you can query its final state to see if the real effect you want happened.\n- You can use them to run your application locally without spinning up or depending on real dependencies. This will usually improve developer experience (DX) because the fakes will be faster and more reliable than their real counterparts.\n\nSpies, Mocks and Stubs can typically be autogenerated from an interface using a tool or using reflection. However, as Fakes encode the behaviour of the dependency you're trying to make a double for, you'll have to write at least most of the implementation yourself\n\n## The problem with stubs and mocks\n\nIn [Anti-patterns,](https://quii.gitbook.io/learn-go-with-tests/meta/anti-patterns) there are details on how using test doubles must be done carefully. Creating a messy test suite is easy if you don't use them tastefully. As a project grows though, other problems can creep in.\n\nWhen you encode behaviour into test doubles, you are adding your assumptions as to how the real dependency works into the test. If there is a discrepancy between the behaviour of the double and the real dependency, or if one happens over time (e.g. the real dependency changes, which *has* to be expected), **you may have passing tests but failing software**.\n\nStubs, spies and mocks, in particular, represent other challenges, mainly as a project grows. To illustrate this, I will describe a project I worked on.\n\n### Example case study\n\n*Some details are changed compared to what really happened, and it has been simplified greatly for brevity. **Any resemblance to actual persons, living or dead, is purely coincidental.***\n\nI worked on a system that had to call **six** different APIs, written and maintained by other teams across the globe. They were _REST-ish_, and the job of our system was to create and manage resources in them all. When we called all the APIs correctly for each system, _magic_ (business value) would happen.\n\nOur application was structured in a hexagonal / ports & adapters architecture. Our domain code was decoupled from the mess of the outside world we had to deal with. Our \"adapters\" were, in effect, Go clients that encapsulated calling the various APIs.\n\n![the system architecture](https://i.imgur.com/6bqovl8.png)\n\n#### Troubles\n\nNaturally, we took a test-driven approach to building the system. We leveraged stubs to simulate the downstream API responses and had a handful of acceptance tests to reassure ourselves everything should work.\n\nThe APIs we had to call for the most part, though, were:\n\n- poorly documented\n- run by teams who had lots of other conflicting priorities and pressures, so it wasn't easy to get time with them\n- often lacking test coverage, so would break in fun and unexpected ways, regress, etc\n- were still being built and evolved\n\nThis led to **a lot of flaky tests** and a lot of headaches. A _significant_ amount of our time was spent pinging lots of busy people on Slack trying to get answers as to:\n\n- Why has the API started doing `x`?\n- Why is the API doing something different when we do `y`?\n\nSoftware development is rarely as straightforward as you'd hope; it's a learning exercise. We had to continuously learn how the external APIs worked. As we learned and adapted, we had to update and add to our test suite, in particular, **changing our stubs to match the actual behaviour of the APIs.**\n\nThe trouble is, this took up much of our time and led to more mistakes. When your knowledge of a dependency changes, you must find the **right** test to update to change the stub's behaviour, and there's a real risk of neglecting to update it in other stubs representing the same dependency.\n\n#### Test strategy\n\nOn top of this, as the system was growing and requirements were changing, we realised that our test strategy was unsuitable. We had a handful of acceptance tests that would give us confidence the system as a whole worked and then a large number of unit tests for the various packages we wrote.\n\n<u>We needed something in between</u>; we often wanted to change the behaviour of various system parts together **but not have to spin up the *entire* system for an acceptance test**. Unit tests alone did not give us confidence that the various components worked as a whole; they couldn't tell (and verify) the story of what we were trying to achieve. **We wanted integration tests**.\n\n#### Integration tests\n\nIntegration tests prove that two or more \"units\" work correctly when combined (or integrated!). These units can be the code you write or the code you write integrated with someone else's code, such as a database.\n\nAs a project grows, you want to write more integration tests to prove large parts of your system \"hang together\" - or integrates!\n\nYou may be tempted to write more black-box acceptance tests, but they quickly become costly regarding your build time and maintenance costs. It can be too expensive to spin up an entire system when you only want to check a *subset* of the system (but not just a single unit) behaves how it should. Writing expensive black-box tests for every bit of functionality you do is not sustainable for larger systems.\n\n#### Enter: Fakes\n\nThe problem was the way our units were tested was reliant on stubs, which are, for the most part, *stateless*. We wanted to write tests covering multiple, *stateful* API calls, where we may create a resource at the start and then edit it later.\n\nThe following is a cut-down version of a test we want to do.\n\nThe SUT is a \"service layer\" dealing with \"use case\" requests. We want to prove if a customer is created, when their details change, we successfully update the resources we made in the respective APIs.\n\nHere are the requirements given to the team as a user story.\n\n> ***Given*** a user is registered with API 1, 2 and 3\n>\n> ***When*** the customer's social security number is changed\n>\n> ***Then**,* the change is propagated into APIs 1, 2 and 3\n\n```mermaid\nsequenceDiagram\n\tUser->>SUT: Create customer\n\tSUT->>API1: Create resource for customer\n\tAPI1->>SUT: Response with generated ID\n\tSUT->>API2: Create resource for customer\n\tAPI2->>SUT: Response with generated ID\n\tSUT->>Storage: Persist identifiers for customer\n\tUser->>SUT: Change customer's social security number\n\tSUT->>Storage: Get customer\n\tStorage->>SUT: Details, including IDs generated by the APIs\n\tSUT->>API1: Update resource\n\tSUT->>API2: Update resource\n```\n\nTests that cut across multiple units are usually incompatible with stubs **because they're not suited to maintaining state**. We _could_ write a black-box acceptance test, but the costs of these tests would quickly spiral out of control.\n\nIn addition, it is complicated to test edge cases with a black-box test because you cannot control the dependencies. For instance, we wanted to prove that a rollback mechanism would be fired if one API call failed.\n\nWe needed to use **fakes**. By modelling our dependencies as stateful APIs with in-memory fakes, we were able to write integration tests with a much broader scope, **to allow us to test real use cases worked**, again *without* having to spin up the whole system, and instead have almost the same speed as unit tests.\n\n![integration tests with fakes](https://i.imgur.com/9Q6FMpw.png)\n\nUsing fakes, **we can make assertions based on the final states of the respective systems rather than relying on complicated spying**. We'd ask each fake what records it held for the customer and assert they were updated. This feels more natural; if we manually checked our system, we would query those APIs to check their state, not inspect our request logs to see if we sent particular JSON payloads.\n\n```go\n// take our lego-bricks and assemble the system for the test\nfakeAPI1 := fakes.NewAPI1()\nfakeAPI2 := fakes.NewAPI2() // etc..\ncustomerService := customer.NewService(fakeAPI1, fakeAPI2, etc...)\n\n// create new customer\nnewCustomerRequest := NewCustomerReq{\n\t// ...\n}\ncreatedCustomer, err := customerService.New(newCustomerRequest)\nassert.NoErr(t, err)\n\n// we can verify all the details are as expected in the various fakes in a natural way, as if they're normal APIs\nfakeAPI1Customer := fakeAPI1.Get(createdCustomer.FakeAPI1Details.ID)\nassert.Equal(t, fakeAPI1Customer.SocialSecurityNumber, newCustomerRequest.SocialSecurityNumber)\n\n// repeat for the other apis we care about\n\n// update customer\nupdatedCustomerRequest := NewUpdateReq{SocialSecurityNumber: \"123\", InternalID: createdCustomer.InternalID}\nassert.NoErr(t, customerService.Update(updatedCustomerRequest))\n\n// again we can check the various fakes to see if the state ends up how we want it\nupdatedFakeAPICustomer := fakeAPI1.Get(createdCustomer.FakeAPI1Details.ID)\nassert.Equal(t, updatedFakeAPICustomer.SocialSecurityNumber, updatedCustomerRequest.SocialSecurityNumber)\n```\n\nThis is simpler to write and easier to read than checking various function call arguments made via spies.\n\nThis approach lets us have tests that cut across broad parts of our system, letting us write more **meaningful** tests about the use cases we'd be discussing at stand-up whilst still executing exceptionally quickly.\n\n#### Fakes bring more of the benefits of encapsulation\n\nIn the example above, the tests were not concerned with how the dependencies behaved beyond verifying their end state. We created the fake versions of the dependencies and injected them into the part of the system we're testing.\n\nWith mocks/stubs, we'd have to set up each dependency to handle certain scenarios, return certain data, etc. This brings behaviour and implementation detail into your tests, weakening the benefits of encapsulation. \n\nWe model dependencies behind interfaces so that, as clients, _we don't have to care how it works_, but with a \"mockist\" approach, _we do have to care **in every test**_. \n\n#### The maintenance costs of fakes\n\nFakes are costlier than other test doubles, at least in terms of code written; they must carry state and simulate the behaviour of whatever they're faking. Any discrepancies in behaviour between your fake and the real thing **carry a risk** that your tests aren't in line with reality. This leads to the scenario where you have passing tests but broken software.\n\nWhenever you integrate with another system, be it another team's API or a database, you'll make assumptions based on its behaviour. These could be captured from API docs, in-person conversations, emails, Slack threads, etc.\n\nWouldn't it be helpful if we could **codify our assumptions** to run them against both our fake *and* the actual system to see if our knowledge is correct in a repeatable and documented way?\n\n**Contracts** are the means to this end. They helped us manage the assumptions we made on the other team's systems and make them explicit. Way more explicit and useful than email exchanges or endless Slack threads!\n\n![fakes and contracts illustrated](https://i.imgur.com/l9aTe2x.png)\n\nBy having a contract, we can assume that we can use a fake and an actual dependency interchangeably. This is not only useful for constructing tests but also for local development.\n\nHere is an example of a contract for one of the APIs the system depends on\n\n```go\ntype API1Customer struct {\n\tName string\n\tID   string\n}\n\ntype API1 interface {\n\tCreateCustomer(ctx context.Context, name string) (API1Customer, error)\n\tGetCustomer(ctx context.Context, id string) (API1Customer, error)\n\tUpdateCustomer(ctx context.Context, id string, name string) error\n}\n\ntype API1Contract struct {\n\tNewAPI1 func() API1\n}\n\nfunc (c API1Contract) Test(t *testing.T) {\n\tt.Run(\"can create, get and update a customer\", func(t *testing.T) {\n\t\tvar (\n\t\t\tctx  = context.Background()\n\t\t\tsut  = c.NewAPI1()\n\t\t\tname = \"Bob\"\n\t\t)\n\n\t\tcustomer, err := sut.CreateCustomer(ctx, name)\n\t\texpect.NoErr(t, err)\n\n\t\tgot, err := sut.GetCustomer(ctx, customer.ID)\n\t\texpect.NoErr(t, err)\n\t\texpect.Equal(t, customer, got)\n\n\t\tnewName := \"Robert\"\n\t\texpect.NoErr(t, sut.UpdateCustomer(ctx, customer.ID, newName))\n\n\t\tgot, err = sut.GetCustomer(ctx, customer.ID)\n\t\texpect.NoErr(t, err)\n\t\texpect.Equal(t, newName, got.Name)\n\t})\n\n\t// example of strange behaviours we didn't expect\n\tt.Run(\"the system will not allow you to add 'Dave' as a customer\", func(t *testing.T) {\n\t\tvar (\n\t\t\tctx  = context.Background()\n\t\t\tsut  = c.NewAPI1()\n\t\t\tname = \"Dave\"\n\t\t)\n\n\t\t_, err := sut.CreateCustomer(ctx, name)\n\t\texpect.Err(t, ErrDaveIsForbidden)\n\t})\n}\n```\n\nAs discussed in [Scaling Acceptance Tests](https://quii.gitbook.io/learn-go-with-tests/testing-fundamentals/scaling-acceptance-tests), by testing against an interface rather than a concrete type, the test becomes:\n\n- Decoupled from implementation detail\n- Can be re-used in different contexts.\n\nWhich are the requirements for a contract. It allows us to verify and develop our fake _and_ test it against the actual implementation.\n\nTo create our in-memory fake, we can use the contract in a test.\n\n```go\nfunc TestInMemoryAPI1(t *testing.T) {\n\tAPI1Contract{NewAPI1: func() API1 {\n\t\treturn inmemory.NewAPI1()\n\t}}.Test(t)\n}\n```\n\nAnd here is the fake's code\n\n```go\nfunc NewAPI1() *API1 {\n\treturn &API1{customers: make(map[string]planner.API1Customer)}\n}\n\ntype API1 struct {\n\ti         int\n\tcustomers map[string]planner.API1Customer\n}\n\nfunc (a *API1) CreateCustomer(ctx context.Context, name string) (planner.API1Customer, error) {\n\tif name == \"Dave\" {\n\t\treturn planner.API1Customer{}, ErrDaveIsForbidden\n\t}\n\n\tnewCustomer := planner.API1Customer{\n\t\tName: name,\n\t\tID:   strconv.Itoa(a.i),\n\t}\n\ta.customers[newCustomer.ID] = newCustomer\n\ta.i++\n\treturn newCustomer, nil\n}\n\nfunc (a *API1) GetCustomer(ctx context.Context, id string) (planner.API1Customer, error) {\n\treturn a.customers[id], nil\n}\n\nfunc (a *API1) UpdateCustomer(ctx context.Context, id string, name string) error {\n\tcustomer := a.customers[id]\n\tcustomer.Name = name\n\ta.customers[id] = customer\n\treturn nil\n}\n```\n\n### Evolving software\n\nMost software is not built and \"finished\" forever, in one release.\n\nIt's an incremental learning exercise, adapting to customer demands and other external changes. In the example, the APIs we were calling were also evolving and changing; plus, as we developed _our_ software, we learned more about what system we _really_ needed to make. Assumptions we made in our contracts turned out to be wrong or _became_ wrong.\n\nThankfully, once the setup for the contracts was made, we had a simple way to deal with change. Once we learned something new, as a result of a bug being fixed or a colleague informing us that the API was changing, we'd:\n\n1. Write a test to exercise the new scenario. A part of this will involve changing the contract to **drive** you to simulate the behaviour in the fake\n2. Running the test should fail, but before anything else, run the contract against the real dependency to ensure the change to the contract is valid.\n3. Update the fake so it conforms to the contract.\n4. Make the test pass.\n5. Refactor.\n6. Run all the tests and ship.\n\nRunning the _full_ test suite before checking in _may_ result in other tests failing due to the fake having a different behaviour. This is a **good thing**!  You can now fix all the other areas of the system depending on the changed system; confident they will also handle this scenario in production. Without this approach, you'd have to *remember* to find all the relevant tests and update the stubs. Error-prone, labourious and boring.\n\n### Superior developer experience\n\nHaving the suite of fakes with corresponding contracts felt like a superpower. We could finally tame the complexity of the APIs we had to deal with.\n\nWriting tests for various scenarios became much simpler. We no longer had to assemble a series of stubs and spies for every test; we could take our set of units or modules (the fakes, our own \"services\") and assemble them very easily to exercise the various weird and wonderful scenarios we needed.\n\nEvery test with a stub, spy or mock has to _care_ about how the external system behaves, due to the ad-hoc setup. On the other hand, fakes can be treated like any other well-encapsulated unit of code, where the details are hidden away from you, and you can just use them.\n\nWe could run a very realistic version of the system locally, and as it was all in memory, it would start and run extremely quickly. This meant our test times were extremely fast, which felt very impressive, given how comprehensive the suite was.\n\nIf our acceptance tests failed in our staging environment, our first step was to run our contracts against the APIs we depended on. We often identified issues **before the other systems' developers did**.\n\n### Off the happy path with decorators\n\nFor error scenarios, stubs are more convenient because you have direct access to *how* it behaves in the test, whereas fakes tend to be fairly black-box. This is a deliberate design choice, as we want the users of them (e.g. tests) not to be concerned with how they work; they should trust they do the right thing due to the backing of the contract.\n\nHow do we make the fakes fail, to exercise non-happy path concerns?\n\nThere are plenty of scenarios where, as a developer, you need to modify the behaviour of some code without changing its source. The **decorator pattern** is often a way to take a unit of code and add things like logging, telemetry, retries and more. We can use it to wrap our fakes to override behaviours when necessary.\n\nReturning to the `API1` example, we can create a type that implements the needed interface and wraps around the fake.\n\n```go\ntype API1Decorator struct {\n\tdelegate           API1\n\tCreateCustomerFunc func(ctx context.Context, name string) (API1Customer, error)\n\tGetCustomerFunc    func(ctx context.Context, id string) (API1Customer, error)\n\tUpdateCustomerFunc func(ctx context.Context, id string, name string) error\n}\n\n// assert API1Decorator implements API1\nvar _ API1 = &API1Decorator{}\n\nfunc NewAPI1Decorator(delegate API1) *API1Decorator {\n\treturn &API1Decorator{delegate: delegate}\n}\n\nfunc (a *API1Decorator) CreateCustomer(ctx context.Context, name string) (API1Customer, error) {\n\tif a.CreateCustomerFunc != nil {\n\t\treturn a.CreateCustomerFunc(ctx, name)\n\t}\n\treturn a.delegate.CreateCustomer(ctx, name)\n}\n\nfunc (a *API1Decorator) GetCustomer(ctx context.Context, id string) (API1Customer, error) {\n\tif a.GetCustomerFunc != nil {\n\t\treturn a.GetCustomerFunc(ctx, id)\n\t}\n\treturn a.delegate.GetCustomer(ctx, id)\n}\n\nfunc (a *API1Decorator) UpdateCustomer(ctx context.Context, id string, name string) error {\n\tif a.UpdateCustomerFunc != nil {\n\t\treturn a.UpdateCustomerFunc(ctx, id, name)\n\t}\n\treturn a.delegate.UpdateCustomer(ctx, id, name)\n}\n```\n\nIn our tests, we can then use the `XXXFunc` field to modify the behaviour of the test-double, just like you would with stubs, spies or mocks.\n\n```go\nfailingAPI1 = NewAPI1Decorator(inmemory.NewAPI1())\nfailingAPI1.UpdateCustomerFunc = func(ctx context.Context, id string, name string) error {\n\treturn errors.New(\"failed to update customer\")\n}\n```\n\nHowever, this _is_ awkward and requires you to exercise some judgement. With this approach, you are losing the guarantees from your contract as you are introducing ad-hoc behaviour to your fake in tests.\n\nIt would be best to examine your context, you may conclude it would be simpler to test specific unhappy paths at the unit test level using a stub.\n\n### Isn't this extra code waste?\n\nIt is wishful thinking to believe we should only ever write code that serves customers and expect a system we can build on efficiently. People have a very warped opinion of what waste is (see my post: [The ghost of Henry Ford is ruining your development team](https://quii.dev/The_ghost_of_Henry_Ford_is_ruining_your_development_team)).\n\nAutomated tests do not directly benefit customers, but we write them to make ourselves more efficient with our work (you don't write tests to chase coverage scores, right?).\n\nEngineers must easily simulate scenarios (in a repeatable fashion, not ad-hocly) to debug, test, and fix issues. **In-memory fakes and good modular design allow us to isolate the relevant actors for a scenario to write fast, appropriate tests extremely cheaply**. This flexibility enables developers to iterate on a system far more manageably than a tangled mess, tested via expensive to-write and run black-box tests or, worse, manual testing on a shared environment.\n\nThis is an example of [simple vs. easy](https://www.youtube.com/watch?v=SxdOUGdseq4). Of course, fakes and contracts will result in more code being written than stubs and spies in the short term, but the result is a more straightforward and cheaper-to-maintain system in the longer run. Updating spies, stubs and mocks piecemeal is labour-intensive and error-prone, as you won't have corresponding contracts to check your test doubles behave correctly.\n\nThis approach represents a _slightly_ increased upfront cost but with far lower costs once the contracts and fakes are set up. Fakes are more reusable and reliable than ad-hoc test doubles like stubs.\n\nIt feels *very* liberating and gives you **confidence** when using an existing, battle-tested fake rather than setting up a stub when writing a new test.\n\n### How does this fit into TDD?\n\nI wouldn't recommend _starting_ with a contract; that's bottom-up design, which, in general, I find I need to be more clever for, and there's a danger I'll overthink hypothetical requirements.\n\nThis technique is compatible with the \"acceptance test driven approach\" as discussed in earlier chapters, [The Why of TDD](https://quii.dev/The_Why_of_TDD) and in [GOOS](http://www.growing-object-oriented-software.com)\n\n- Write a failing [acceptance test](https://quii.gitbook.io/learn-go-with-tests/testing-fundamentals/scaling-acceptance-tests).\n- Drive out enough code to make it pass, which usually will result in some \"service layer\" that'll depend on an API, a database, or whatever. Usually, you will have business logic code decoupled from external concerns (such as persistence, calling a database, etc.) via an interface.\n- Implement the interface with an in-memory fake at first to make all the tests pass locally and validate the initial design.\n- To push to production, you can't use in-memory! Encode the assumptions you made against the fake into a contract.\n- Use the contract to create the actual dependency, such as a MySQL version of a store.\n- Ship.\n\n##  Where's the chapter on testing databases?\n\nThis has been a common request that I have put off for over five years. The reason is this chapter will always be my answer.\n\n<u>Don't mock the database driver and spy on calls</u>. These tests are difficult to write and potentially bring very little value. You shouldn't assert whether a particular `SQL` statement was sent to the database, that is, implementation detail; **your tests should only care about behaviour**. Proving a specific SQL statement was compiled _does not_ prove your code _behaves_ how you need it to.\n\n**Contracts** force you to decouple your tests from implementation details and focus on behaviour.\n\nFollow the TDD approach described above to drive out your persistence needs.\n\n[The example repository](https://github.com/quii/go-fakes-and-contracts) has some examples of contracts, and how they're used to test in-memory and SQLite implementations of some persistence needs.\n\n```go\npackage inmemory_test\n\nimport (\n\t\"github.com/quii/go-fakes-and-contracts/adapters/driven/persistence/inmemory\"\n\t\"github.com/quii/go-fakes-and-contracts/domain/planner\"\n\t\"testing\"\n)\n\nfunc TestInMemoryPantry(t *testing.T) {\n\tplanner.PantryContract{\n\t\tNewPantry: func() planner.Pantry {\n\t\t\treturn inmemory.NewPantry()\n\t\t},\n\t}.Test(t)\n}\n```\n\n```go\npackage sqlite_test\n\nimport (\n\t\"github.com/quii/go-fakes-and-contracts/adapters/driven/persistence/sqlite\"\n\t\"github.com/quii/go-fakes-and-contracts/domain/planner\"\n\t\"testing\"\n)\n\nfunc TestSQLitePantry(t *testing.T) {\n\tclient := sqlite.NewSQLiteClient()\n\tt.Cleanup(func() {\n\t\tif err := client.Close(); err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t})\n\n\tplanner.PantryContract{\n\t\tNewPantry: func() planner.Pantry {\n\t\t\treturn sqlite.NewPantry(client)\n\t\t},\n\t}.Test(t)\n}\n```\n\nWhilst Docker et al. _do_ make running databases locally easier, they can still carry a significant performance overhead. Fakes with contracts allow you to use restrict the need to use the \"heavier\" dependency to only when you're validating the contract, and not needed for other kinds of tests.\n\nUsing in-memory fakes for acceptance and integration tests for *the rest* of the system provides a much faster and simpler developer experience.\n\n## Wrapping up\n\nIt’s common for software projects to be organised with various teams building systems concurrently to try to reach a common goal.\n\nThis method of work requires a high degree of collaboration and communication. Many feel with an \"API first\" approach, we can define some API contracts (often on a wiki page!) and then work independently for six months and stick it all together. This rarely works well in practice because as we start writing code, we understand the domain and the problem better, which challenges our assumptions. We have to react to these changes in knowledge, which often require cross-team changes.\n\nSo, if you're in this situation, you need to structure and test your system optimally to deal with unpredictable changes, both inside and outside of the system you're working on.\n\n> “One of the defining characteristics of high-performing teams in software development is their ability to make progress and to change their minds, without asking for permission from any person or group outside of their small team.”\n>\n> Modern Software Engineering\n> David Farley\n\nDon't rely on weekly meetings or Slack threads to flesh out changes. **Codify your assumptions in contracts**. Run those contracts against the systems in your build pipelines so you get fast feedback if new information comes to light. These contracts, in conjunction with **fakes,** mean you can work independently and manage external changes sustainably.\n\n### Your system as a collection of modules\n\nReferring back to Farley's book, I'm describing the idea of **incrementalism**. Building software is a *constant learning exercise*. Understanding the requirements we must solve for a given system to deliver value up-front is unrealistic. So, we have to optimise our systems and ways of work to **gather feedback quickly and experiment**.\n\nYou need a **modular system** to take advantage of the ideas discussed in this chapter. If you have modular code with reliable fakes, it allows you to experiment with your system via automated tests cheaply.\n\nWe found it extremely easy to translate weird, hypothetical (but possible) scenarios into self-contained tests to help us understand the problem and drive out more robust software by composing our modules together and trying out different data in different order, with some APIs failing, etc.\n\nWell-defined, well-tested modules allow you to increment your system without changing and understanding _everything_ at once.\n\n### But I'm working on something small with stable APIs\n\nEven with stable APIs, you do not want your developer experience, builds and so on to be tightly coupled to other people’s code. When you get this approach right, you end up with a composable set of modules to piece together your system for production, running locally and writing different kinds of tests with doubles you trust.\n\nIt allows you to isolate the parts of your system you're concerned about and write meaningful tests about the real problem you're trying to solve.\n\n### Make your dependencies first-class citizens.\n\nOf course, stubs and spies have their place. Simulating different behaviours of your dependencies ad-hocly in tests will always have its use, but be careful not to let the costs go out of control.\n\nSo many times in my career, I have seen carefully written software written by talented devs fall apart due to integration problems. Integration is challenging for engineers _because_ it's hard to reproduce the exact behaviours of a system written by other engineers, who also change it simultaneously.\n\nSome teams rely on everyone deploying to a shared environment and testing there. The problem is this doesn't give you **isolated** feedback, and the **feedback is slow**. You still won't be able to construct different experiments with how your system works with other dependencies, at least not efficiently.\n\n**We have to tame this complexity by adopting more sophisticated ways of modelling our dependencies** to quickly test/experiment on our dev machines before it gets to production. Create realistic and manageable fakes of your dependencies, verified by contracts. Then, you can start writing more meaningful tests and experimenting with your system, making you more likely to succeed.\n"
  }
]