{"id":15034019,"url":"https://github.com/mtumilowicz/go-chi-gorilla-wire-workshop","last_synced_at":"2026-02-10T23:31:51.427Z","repository":{"id":247259300,"uuid":"823800609","full_name":"mtumilowicz/go-chi-gorilla-wire-workshop","owner":"mtumilowicz","description":"Introduction into golang and http ecosystem: chi and gorilla.","archived":false,"fork":false,"pushed_at":"2024-10-21T18:49:26.000Z","size":140,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-08-24T12:52:00.298Z","etag":null,"topics":["go","go-chi","go-http","go-http-middleware","go-wire","golang","golang-application","golang-examples","golang-library","golang-package","gorilla","workshop","workshop-materials","workshops"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mtumilowicz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-07-03T18:39:06.000Z","updated_at":"2024-10-21T18:49:30.000Z","dependencies_parsed_at":"2024-11-19T22:36:57.274Z","dependency_job_id":null,"html_url":"https://github.com/mtumilowicz/go-chi-gorilla-wire-workshop","commit_stats":null,"previous_names":["mtumilowicz/go-chi-gorilla-wire-workshop"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/mtumilowicz/go-chi-gorilla-wire-workshop","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtumilowicz%2Fgo-chi-gorilla-wire-workshop","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtumilowicz%2Fgo-chi-gorilla-wire-workshop/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtumilowicz%2Fgo-chi-gorilla-wire-workshop/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtumilowicz%2Fgo-chi-gorilla-wire-workshop/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mtumilowicz","download_url":"https://codeload.github.com/mtumilowicz/go-chi-gorilla-wire-workshop/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtumilowicz%2Fgo-chi-gorilla-wire-workshop/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29321339,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-10T20:44:44.282Z","status":"ssl_error","status_checked_at":"2026-02-10T20:44:43.393Z","response_time":65,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["go","go-chi","go-http","go-http-middleware","go-wire","golang","golang-application","golang-examples","golang-library","golang-package","gorilla","workshop","workshop-materials","workshops"],"created_at":"2024-09-24T20:23:38.144Z","updated_at":"2026-02-10T23:31:51.409Z","avatar_url":"https://github.com/mtumilowicz.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build Status](https://app.travis-ci.com/mtumilowicz/go-chi-gorilla-wire-workshop.svg?branch=main)](https://app.travis-ci.com/mtumilowicz/go-chi-gorilla-wire-workshop)\n[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)\n\n# go-chi-gorilla-wire-workshop\n\n* references\n    * https://www.oreilly.com/library/view/learning-go/9781492077206/\n\n## preface\n* goals of this workshop\n    * understanding basics of golang\n        * data types\n        * syntax\n        * error handling\n        * code organisation\n        * testing\n    * introduction to go ecosystem\n        * chi\n        * gorilla\n        * wire\n* workshop task: implement endpoint for deleting customer\n\n## golang\n* 25 keywords\n    * example: `break`, `continue`, `if`, `for`, etc\n    * not keywords: predeclared identifiers in universe block\n        * example\n            * built-in types (like `int` and `string`)\n            * constants (like `true` and `false`)\n            * functions (like `make` or `close`)\n            * `nil`\n        * can be shadowed in other scopes\n            * example: `true := 10`\n* Go runtime is compiled into every Go binary\n    * different from languages using a virtual machine\n        * VM must be installed separately to allow programs to run\n    * avoids worries about compatibility issues between the runtime and the program\n    * drawback: even the simplest Go program produces a binary that’s about 2 MB\n* every type in Go is a value type\n    * sometimes the value is a pointer\n    * variables are passed by value\n* uses GC\n* standard library\n    * has a compatibility promise\n        * programs written for Go 1.x will continue to compile and run correctly with any future 1.x version of Go\n    * File I/O: `io.Reader` and `io.Writer`\n    * time\n        * period = `time.Duration`\n        * moment of time = `time.Time`\n            * with a time zone\n        * `time.After` - channel that outputs once specified duration elapses\n        * `time.Tick` - returns a new value every time the specified duration elapses\n    * `json.Unmarshal`, `json.Marshal`\n        * specification: struct tags\n            * strings that are written after the fields\n    * net/http\n        * production-quality HTTP/2 client and server\n    * log/slog\n        * since Go 1.21\n        * zap, logrus, go-kit log, and many others.\n        * slog.Debug(\"debug log message\")\n          slog.Info(\"info log message\")\n          slog.Warn(\"warning log message\")\n          slog.Error(\"error log message\")\n        * userID := \"fred\"\n          loginCount := 20\n          slog.Info(\"user login\",\n          \"id\", userID,\n          \"login_count\", loginCount)\n\n          2023/04/20 23:36:38 INFO user login id=fred login_count=20\n        * json\n            * options := \u0026slog.HandlerOptions{Level: slog.LevelDebug}\n              handler := slog.NewJSONHandler(os.Stderr, options)\n              mySlog := slog.New(handler)\n              lastLogin := time.Date(2023, 01, 01, 11, 50, 00, 00, time.UTC)\n              mySlog.Debug(\"debug message\",\n              \"id\", userID,\n              \"last_login\", lastLogin)\n            * {\"time\":\"2023-04-22T23:30:01.170243-04:00\",\"level\":\"DEBUG\",\n              \"msg\":\"debug message\",\"id\":\"fred\",\"last_login\":\"2023-01-01T11:50:00Z\"}\n        * For\n          improved performance with fewer allocations, use the LogAttrs method instead:\n          mySlog.LogAttrs(ctx, slog.LevelInfo, \"faster logging\",\n          slog.String(\"id\", userID),\n          slog.Time(\"last_login\", lastLogin))\n\n## collections\n* `make` method\n* array\n    * size part of the type\n* slice\n    * example\n        ```\n        var x = []int{1, 2} // slice\n        var y = [2]int{1, 2} // array\n        var z = y[:] // conversion: array -\u003e slice\n        var zz = [2]int(x) // conversion: slice -\u003e array\n        ```\n    * used most of the time instead of array\n    * has a capacity (number of consecutive memory locations reserved)\n        * if length = capacity, `append` function uses the Go runtime to allocate a new backing array\n          with a larger capacity\n    * add - append\n        ```\n        var x = []int{1, 2}\n        var y = append(x, 3) // usually shadowed: x = append(x, 4)\n        fmt.Println(x) // 1, 2\n        fmt.Println(y) // 1, 2, 3\n        ```\n    * emptying - clear\n    * delete - involves more than just removing it, as slices are views into arrays\n        * `s = append(s[:i], s[i+1:]...)`\n    * isn’t comparable\n        * `slices.Equal` returns true if slices are the same length and all of the elements are equal\n        * `slices.EqualFunc` lets you pass in a function to compare elements\n    * zero-length slice: `var x = []int{}`\n        * is useful only when converting a slice to JSON\n        * favor nil slices: `var x []int`\n    * subslicing: `e := x[a:b]`\n        * no starting offset =\u003e 0 is assumed\n        * no ending offset =\u003e end of the slice is substituted\n        * not making a copy of the data\n            * changes to an element affect all slices that share that element\n            * if needed =\u003e built-in copy function\n        * extra confusing when combined with append\n            * example\n                ```\n                original := []int{1, 2, 3, 4, 5}\n                subslice := original[1:3]\n                subslice = append(subslice, 6)\n                fmt.Println(original) // [1 2 3 6 5]\n                fmt.Println(subslice) // [2 3 6]\n                ```\n            * never use append with a subslice\n                * or use full slice expression - if `append` exceeds the capacity =\u003e new array for slice is allocated (preserving the original slice)\n                    ```\n                    subslice := original[1:3:3]\n                    ```\n    * slice is implemented as a struct with three fields\n        * an int field for length, an int field for capacity, and a pointer to a block of memory\n        * when a slice is copied, copy is made of the length, capacity, and the pointer\n        * ideal for reusable buffers\n            * slice that’s passed to a function can have its contents modified\n            * slice can’t be resized\n        * by default, you should assume that a slice is not modified by a function\n* string\n    * Go uses a sequence of bytes to represent a string\n* map\n    * if key is not in the map =\u003e map returns the zero value\n        * ok idiom to differentiate between a key in the map vs not in the map\n    * used to simulate set\n        * ok idiom + `map[string]bool` or `map[keyType]struct{}`\n            ```\n            if _, exists := map[key]; exists {\n                ...\n            }\n            for key := range map {\n                ...\n            }\n            ```\n        * `m := make(map[string]bool)`\n            * boolean uses one byte\n        * `m := make(map[keyType]struct{})`\n            * empty struct uses zero bytes\n    * operations\n        * set: `map[key] = value`\n        * get: `map[key]`\n        * delete: `delete(map, key)`\n        * clear(map)\n    * isn't comparable\n        * `maps.Equal`, `maps.EqualFunc`\n    * to mitigate Hash DoS attacks, Go's map implementation includes a random component in its hash function\n        * no way for attacker to send many requests with keys designed to hash to the same bucket\n        * new map is created =\u003e Go generates a random number and incorporates it into the hash function\n            * the same keys will hash to different buckets in different instances of maps\n    * map is implemented as a pointer to a struct\n        * avoid using maps for input parameters or return values, especially on public APIs\n\n## struct\n* no inheritance =\u003e no classes\n* example\n    ```\n    type person struct {\n        name string\n        age int\n    }\n    ```\n* anonymous structs\n    * common in two situations: unmarshaling and marshaling\n        * example\n            ```\n            var pet struct {\n                Name string `json:\"name\"`\n                Kind string `json:\"kind\"`\n            }\n\n            err := json.Unmarshal(jsonData, \u0026pet)\n            ```\n\n## syntax\n* function - `func`\n    * is a type\n        * example\n            ```\n            var f func(string) int\n            f := func(s string) { // anonymous function\n                ...\n            }\n            ```\n    * emulate named and optional parameters =\u003e define a struct\n    * supports variadic parameters: `func max(first, rest ...int) int`\n        * converted to a slice - slice can be supplied as the input\n    * allows for multiple return values\n        * example\n            ```\n            func sum(vals []int) (int, error) {\n                if len(vals) == 0 {\n                    return 0, errors.New(\"empty slice\") // Return an error for empty slice\n                }\n\n                total := 0\n                for _, v := range vals {\n                    total += v\n                }\n                return total, nil // Return sum and nil error indicating success\n            }\n            ```\n        * convention: last return value from a function is an error\n            * no error =\u003e nil is returned for the error parameter\n    * closures = functions declared inside functions\n* Go uses capitalization to determine whether a package-level identifier is visible outside the package\n    * identifier whose name starts with an uppercase letter is exported\n* defer\n    * example\n        ```\n        file, err := os.Open(\"example.txt\")\n        if err != nil {\n        \tfmt.Println(\"Error:\", err)\n        \treturn\n        }\n        defer file.Close()\n        ```\n    * code within defer functions runs after the return statement\n        * LIFO order - the last defer registered runs first\n    * common pattern: function that allocates a resource returns also closure that cleans up the resource\n        * Go doesn’t allow unused variables =\u003e program will not compile if the function is not called\n* no enumeration type\n    * solution: `iota`\n        * assign an increasing value to a set of constants\n        * \"internal\" purposes only\n            * new identifier =\u003e all subsequent ones will be renumbered\n    * example\n        ```\n        type Status int\n\n        const (\n            Pending Status = iota // 0\n            Running               // 1 (implicitly repeated from iota)\n            Paused                // 2\n            Finished              // 3\n            Failed                // 4\n        )\n        ```\n* methods\n    * are functions associated with a particular type\n        * can be defined only at the package block level\n        * functions can be defined inside any block\n    * example\n        ```\n        type Rectangle struct {\n            width  float64\n            height float64\n        }\n\n        func (r Rectangle) Area() float64 {\n            return r.width * r.height\n        }\n        ```\n    * must be declared in the same package as their associated type\n        * Go doesn’t allow you to add methods to types you don’t control\n    * convention: use pointer receiver to indicate that a parameter might be modified by the function\n        * type has a pointer receiver =\u003e common practice is to be consistent and use pointer receivers for all methods\n    * Go automatically takes the address of the local variable when calling the method\n        * example: `c.Method()` is converted to `(\u0026c).Method()`\n    * Go automatically dereferences the pointer when calling the method\n        * example: `c.Method()` is converted to `(*c).Method()`\n    * Go allows to call a method on a nil receiver\n        * most of the time =\u003e not very useful\n        * use case: initializing data structure when nil\n            * example: insert value into a tree (or create one when nil)\n* interfaces\n    * type-safe duck typing\n        * type does not declare that it implements an interface\n        * type implements the interface = method set contains all interface's methods\n    * Go added any as a type alias for interface{}\n        * use case: data placeholder\n    * rule: accept interfaces, return structs\n    * interfaces are implemented as a struct with two pointer fields: value and type of the value\n        * type field is non-nil =\u003e interface is non-nil\n        * value pointer is non-nil =\u003e type pointer is non-nil\n            * you cannot have a variable without a type\n        * interface is `nil` \u003c=\u003e both the type and the value must be nil\n            * interface variable is nil =\u003e invoking any methods triggers a panic\n            * interface variable is not nil but value is nil =\u003e invoking any methods triggers a panic\n                * assuming that methods of the assigned type don’t properly handle nil\n        * two instances of an interface type are equal \u003c=\u003e their types are equal and their values are equal\n            * type isn’t comparable =\u003e panic\n                * example: interface as a map key\n    * accept interfaces, return structs\n        * exception: returning error interface\n        * concrete type is returned =\u003e new methods and fields can be added without breaking existing code\n            * new fields and methods can be ignored if call-sites using interfaces\n            * example: database/sql/driver in std lib\n                * defines a set of interfaces that define what a database driver must provide\n                    * responsibility of the database driver author to provide concrete implementations\n                    * almost all methods on all interfaces return interfaces\n                * problem: starting in Go 1.8, database drivers are expected to support additional features\n                    * existing interfaces can’t be updated with new method\n                    * existing methods on these interfaces can’t be updated to return different types\n                    * solution: define new interfaces and tell database driver authors that they should implement both\n        * invoking a function with parameters of interface types =\u003e heap allocation occurs for each interface parameter\n* embedding\n    * form of composition\n        * is not inheritance - cannot assign a variable of one type to another\n    * allows to invoke directly fields and methods of one struct from another\n    * example\n        ```\n        type Manager struct {\n            Employee\n            Reports []Employee\n        }\n        ```\n    * type assertion\n        * check whether the concrete type behind an interface value also implements another interface\n        * useful for specifying optional interfaces\n            * type might implement additional methods beyond those required by the primary interface\n        * example: `if p, ok := a.(SomeInterface); ok { ... }`\n            * without \"comma-ok\" idiom: panic\n    * type switch\n        * used when interface could be one of multiple possible types\n        * one of the few places where shadowing is a good idea\n        * example\n            ```\n            switch a := a.(type) { // shadowing\n                case Dog:\n                    fmt.Printf(\"This is a dog named %s and it says %s\\n\", a.Name, a.Sound())\n                case Cat:\n                    fmt.Printf(\"This is a cat named %s and it says %s\\n\", a.Name, a.Sound())\n                default:\n                    fmt.Printf(\"Unknown animal\\n\")\n                }\n            ```\n* ok idiom\n    * check if operation was successful without causing a panic or error\n        ```\n        if value, ok := m[\"foo\"]; ok {\n        ```\n\n## pointers\n* variable that holds the location in memory where a value is stored\n    * zero value for a pointer is nil\n    * example\n        ```\n        x := 1\n        pointerToX := \u0026x\n        fmt.Println(pointerToX) // memory address\n        fmt.Println(*pointerToX) // value\n        ```\n* before dereferencing =\u003e make sure that the pointer is non-nil\n    * panic if you attempt to dereference a nil pointer\n* pointer type: written with a * before a type\n* primitive literal (numbers, booleans, and strings) or a constant don’t have memory addresses\n    * when you need a pointer to a primitive type, declare a variable and point to it\n* lack of immutable declarations in Go might seem problematic\n    * ability to choose between value and pointer parameter types addresses the issue\n* are a last resort\n* use cases\n    * if a struct is large enough\n        * time to pass a pointer into a function is constant for all data sizes\n    * returning a pointer versus returning a value\n        * memory for the object must be allocated on the heap\n        * data structures that are smaller than 10 megabytes =\u003e slower to return a pointer\n    * code predating generics\n        * no way to know what type of value to create and return\n        * example: `json.Unmarshal([]byte(`{\"name\": \"Bob\", \"age\": 30}`), \u0026f)`\n        * disclaimer: when returning values from a function, favor value types\n    * control over memory allocation\n        * if function returned value, calling it in a loop =\u003e one value would be created on each loop iteration\n    * indicate the difference between variable/field that hasn’t been assigned a value at all\n        * vs assigning the zero value\n        * disclaimer: be careful when using this pattern\n            * pointers indicate mutability\n* escape analysis\n    * when the compiler determines that the data can’t be stored on the stack compiler stores the data on the heap\n        * called: data escapes the stack\n    * isn’t perfect\n        * sometimes data that could be stored on the stack escapes to the heap\n* generics\n    * since go 1.18\n    * implemented using type parameters\n    * type constraints\n        * example\n            ```\n            type Number interface {\n            \tint | int8 | int16 | int32 | int64 | float32 | float64\n            }\n\n            func Add[T Number](a, b T) T {\n            \treturn a + b\n            }\n            ```\n        * allowed operators are the ones that are valid for all of the listed types\n        * `~` denotes type constraint to work with any type that has a specific underlying type\n            * example\n                ```\n                type Integer interface {\n                \t~int\n                }\n\n                type MyInt int\n\n                func Multiply[T Integer](a, b T) T {\n                \treturn a * b\n                }\n\n                func main() {\n                \tvar x MyInt = 10\n                \tvar y MyInt = 20\n                \tfmt.Println(Multiply(x, y))  // without ~ it is not working\n                }\n                ```\n    * how Go compiler handles the generation of functions for different types\n        * separate versions for each unique underlying type\n            * example: generic function works on both `int` and `float64` =\u003e compiler generates\n              two separate functions: one for `int` and another for `float64`\n        * shared functions for pointer types\n            * operates on `unsafe.Pointer`\n                * has to perform extra checks to handle different pointer types correctly\n                    * example: runtime check to determine the actual type of the pointer\n            * example: generic function that takes a pointer =\u003e same generated function for `*int`, `*float64`, `*string`\n\n## error\n* is a built-in interface\n    ```\n    type error interface {\n        Error() string\n    }\n    ```\n* two ways to create an error from a string\n    * `errors.New(\"...\")`\n    * `fmt.Errorf(\"%d ...\", i)`\n* sentinel errors = predefined errors signaling that processing cannot continue\n    * convention\n        * declared at the package level\n        * naming that starts with \"Err\"\n    * used to check with `errors.Is(err, ...)` for specific error conditions and handle them accordingly\n* wrapping = add more context while preserving the original error\n    * new message without wrapping: `fmt.Errorf(\"...: %v\", err)`\n        * can't retrieve the original error from the new one\n    * new message with wrapping: `fmt.Errorf(\"...: %w\", err)`\n        * includes both the new context and the original error\n        * unwrapping: `if originalErr := errors.Unwrap(wrappedErr); originalErr != nil { ... } `\n    * custom error types: needs to implement the method `Unwrap() error`\n    * multiple wrapped errors: `errors.Join(errs...)`\n        * example: validating fields in a struct\n        * custom error type: needs to implement the method `Unwrap() []error`\n    * wrapped sentinel error\n        * problem: cannot use `==`, type assertion or type switch to check for it\n        * solution\n            * `errors.Is(err, ...)`\n                * checks if a given error is or wraps a specific target error\n            * `errors.As(err, \u0026...)`\n                * checks if a given error is or matches a specific target error\n                * second parameter anything other than a pointer to an error or a pointer to an interface =\u003e the method panics\n* panic\n    * as soon as a panic happens, the current function exits immediately\n        * defers attached to the current function start running\n    * starting with Go 1.21, a `panic(nil)` is identical to `panic(new(runtime.PanicNilError))`\n    * unrecoverable error\n        * built-in `recover` function\n            * recommended in one situation\n                * do not let panics escape the boundaries of your public API\n            * way to capture a panic to provide a more graceful shutdown (or prevent shutdown at all)\n            * called from within a defer to check whether a panic happened\n                * once a panic happens, only deferred functions are run\n                * example\n                    ```\n                    defer func() {\n                        if v := recover(); v != nil {\n                            ....\n                        }\n                    }()\n                    ```\n\n## code organisation\n* module = bundle of Go source code distributed and versioned as a single unit\n    * consists of one or more packages (directories of source code)\n    * `require` section lists the dependencies\n    * conventional name: `go.mod`\n    * example\n        ```\n        module example.com/myproject\n\n        go 1.17  // Minimum Go version required by the module\n\n        // lists direct dependencies\n        require (\n            github.com/some/dependency v1.2.3\n            github.com/another/dependency v2.0.0\n            ...\n        )\n\n        // lists indirect dependencies\n        require (\n            github.com/some/dependency v1.2.3 // indirect\n            ...\n        )\n        ```\n* modules can be grouped into two categories\n    * intended as a single application\n        * root of the project = main package\n        * code in the main package should be minimal\n            * all logic in an internal directory\n                * code in the main function invokes code within internal\n            * forbids to create a module that have dependency on application\n    * intended as libraries\n        * root of the project should have a package name that matches the repository name\n* Go always builds applications from source code into a single binary file\n    * includes the source code of module and all dependencies\n* module system uses the principle of minimal version selection\n    * assumption: all minor and patch versions of a module must be backward compatible\n        * otherwise: bug\n    * selects the lowest version of each dependency that satisfies the version requirements declared\n      across all `go.mod` files involved\n* vendoring = keeping copies of dependencies inside their module\n    * ensures that a module always builds with identical dependencies\n    * can make building your code faster on CI/CD\n* workspace\n    * introduced in Go 1.18\n    * useful for development and testing\n        * allows to make changes to one module and see the effects in another\n            * without having to publish or tag new versions\n        * allows you to use local import paths for your modules\n    * workspace file (`go.work`) is used to define a set of modules that you are working on together\n        * you can replace the dependencies of a module with local versions of those dependencies\n\n## tests\n* same directory and the same package as the production code\n    * able to access and test unexported functions and variables\n* written in a file whose name ends with `_test.go`\n* test case start with the word `Test` and take single parameter of type `*testing.T`\n    * use `t.Error` or `t.Errorf` to report a test failure and continue with the test execution\n        * example\n            ```\n            if got != want {\n                t.Error(\"Test failed: got\", got, \"want\", want)\n            }\n            ```\n    * use `t.Fatalf` to report a test failure and stop further execution of the test\n* executing code before/after all tests: `TestMain` function\n    1. `go test` calls it instead of the test functions\n        * `go test ./...` run all tests\n    1. `TestMain` function calls the `Run` method on `*testing.M`\n        * runs the test functions in the package\n        * `Run` method returns the exit code\n            * 0 indicates that all tests passed\n    1. `TestMain` function must call `os.Exit` with the exit code returned from `Run`\n* temporary files: `TempDir` method on `*testing.T`\n    * creates a new temporary directory every time it is invoked\n    * returns the full path of the directory\n    * registers a handler with Cleanup to delete the directory and its contents when the test exits\n* setting environment variable for particular test: `t.Setenv()`\n    * calls Cleanup to revert the environment variable to its previous state when the test exits\n* sample data: subdirectory named `testdata`\n    * used to keep fixtures, input files, and other data necessary for running tests\n    * each package accesses its own `testdata` via a relative file path\n        ```\n        func TestReadFile(t *testing.T) {\n            path := filepath.Join(\"testdata\", \"input.txt\")\n            ...\n        }\n        ```\n* testing public API of the package: `packagename_test`\n    * same directory as the production source code\n* by default, unit tests are run sequentially\n    * `t.Parallel()` makes tests run concurrently with other tests marked as parallel\n* table-driven tests\n    * allows you to test multiple scenarios with a single test function\n    * example: define a table (slice) of test cases and iterate over them within a single test function\n        ```\n        type Person struct {\n            Name    string\n            Age     int\n            Address string\n        }\n\n        func (p *Person) IsAdult() bool {\n            return p.Age \u003e= 18\n        }\n\n        func TestIsAdult(t *testing.T) {\n            tests := []struct {\n                name     string\n                person   Person\n                expected bool\n            }{\n                {\n                    name:     \"Adult\",\n                    person:   Person{Name: \"Alice\", Age: 20, Address: \"123 Main St\"},\n                    expected: true,\n                },\n                {\n                    name:     \"Minor\",\n                    person:   Person{Name: \"Bob\", Age: 17, Address: \"456 Elm St\"},\n                    expected: false,\n                },\n                {\n                    name:     \"Edge case - Exactly 18\",\n                    person:   Person{Name: \"Charlie\", Age: 18, Address: \"789 Oak St\"},\n                    expected: true,\n                },\n                {\n                    name:     \"Negative Age\",\n                    person:   Person{Name: \"Dave\", Age: -1, Address: \"000 Zero St\"},\n                    expected: false,\n                },\n            }\n\n            for _, tt := range tests {\n                t.Run(tt.name, func(t *testing.T) {\n                    result := tt.person.IsAdult()\n                    if result != tt.expected {\n                        t.Errorf(\"isAdult(%+v) = %v; want %v\", tt.person, result, tt.expected)\n                    }\n                })\n            }\n        }\n        ```\n* generating random data: fuzzing\n    * can handle various types of input, such as integers, floats, structs, and more complex data types\n    * `f.Add`: seeds the fuzzer with initial inputs\n        * inputs are used as starting points for the fuzzing process\n            * guides its exploration of the input space\n        * no initial seeds =\u003e fuzzer starts with entirely random inputs\n            * less likely to quickly hit edge cases or meaningful test scenarios\n    * `f.Fuzz`: repeatedly called with different inputs generated by the fuzzer\n    * example\n        ```\n        type Person struct {\n            Name    string\n            Age     int\n            Address string\n        }\n\n        func (p *Person) IsAdult() bool {\n            return p.Age \u003e= 18\n        }\n\n        func FuzzIsAdult(f *testing.F) {\n            f.Add(\"Alice\", 20)\n            f.Add(\"Bob\", 17)\n            f.Add(\"\", 0)\n            f.Add(\"Charlie\", -1)\n\n            f.Fuzz(func(t *testing.T, name string, age int, address string) {\n                if !utf8.ValidString(name) {\n                    t.Skip(\"invalid UTF-8 string\")\n                }\n\n                person := \u0026Person{\n                    Name:    name,\n                    Age:     age,\n                    Address: address,\n                }\n\n                legal := person.IsAdult()\n\n                expected := age \u003e= 18\n\n                if legal != expected {\n                    t.Errorf(\"isAdult(%+v) = %v; want %v\", person, legal, expected)\n                }\n            })\n        }\n        ```\n\n## commands\n* go install\n    * compiles and installs packages and dependencies\n* go generate\n    * runs commands specified in specially formatted comments in the source code\n    * example: protobufs\n    * good idea to automate calling go generate before go build\n* go test\n    * allows you to specify which packages to test\n* go get\n    * fetches, compiles, and installs packages\n    * by default doesn’t fetch code directly from source code repositories\n        * sends requests to a proxy server run by Google and checks its cache\n            * Google also maintains a checksum database\n* go build\n    * generates binary executables\n\n\n## libs\n* https://github.com/shopspring/decimal\n    * arbitrary-precision fixed-point decimal numbers in Go\n* https://pkg.go.dev/github.com/qfornaguera/goimports\n    * same as gofmt + fixes imports\n* https://github.com/dominikh/go-tools\n    * staticcheck - The advanced Go linter\n    * another linter: https://github.com/mgechev/revive\n* https://pkg.go.dev/golang.org/x/tools/cmd/stringer\n    * add `String()` method to enumeration’s values\n* github.com/samber/lo\n    * map, filter, contains, find...\n* https://github.com/go-chi/chi\n    * router for building Go HTTP services\n* https://github.com/gorilla/handlers\n    * middleware for Go HTTP\n* https://github.com/google/go-cmp\n    * package for comparing Go values in tests\n    * returns a detailed description of what does not match (diffs)\n* https://github.com/IBM/sarama\n    * kafka client\n* https://github.com/google/wire\n    * compile-time dependency injection\n* https://github.com/google/uuid\n    * UUIDs based on RFC 4122 and DCE 1.1\n* https://github.com/stretchr/testify\n    * common assertions and mocks\n* https://github.com/go-playground/validator\n    * Go Struct and Field validation\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmtumilowicz%2Fgo-chi-gorilla-wire-workshop","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmtumilowicz%2Fgo-chi-gorilla-wire-workshop","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmtumilowicz%2Fgo-chi-gorilla-wire-workshop/lists"}