diff --git a/CODE_CONVENTIONS.md b/CODE_CONVENTIONS.md index 7c23936..3d99031 100644 --- a/CODE_CONVENTIONS.md +++ b/CODE_CONVENTIONS.md @@ -589,10 +589,36 @@ Two kinds of error are exempt and have no prefix: an internal package name. - Error text that is part of a protocol sent to a client, such as the mock Find a Tender API's error responses, which match the real API's. -- Errors that callers test for are sentinel values named `Err…` - (exported) or `err…` (unexported), checked with `errors.Is`. A - package's sentinels go in one commented `var` block (see - [Grouped declarations](#grouped-declarations)). + +### Sentinel errors + +Errors that callers test for are sentinel values named `Err…` (exported) +or `err…` (unexported), checked with `errors.Is`. The sentinels in a file +go in one commented `var` block (see +[Grouped declarations](#grouped-declarations)). + +They are declared near the top of the file: straight after the imports +and any constants, before the file's types and functions. A reader then +sees what the file can fail with before reading how, and finds a +sentinel without searching the file. This applies to every package-level +error variable, in tests as well. + +```go +import ( + "errors" + "time" +) + +// ErrWonAndLost is returned by OpportunityMarks.Validate when both Won and +// Lost are set. +var ErrWonAndLost = errors.New("types: an opportunity cannot be both won and lost") + +// Opportunity is a procurement opportunity ... +type Opportunity struct { +``` + +Not declared next to the type or function that returns it, partway down +the file. ### Ignoring an error diff --git a/internal/cli/migrate/common_test.go b/internal/cli/migrate/common_test.go index 5c6d28d..16fbf6b 100644 --- a/internal/cli/migrate/common_test.go +++ b/internal/cli/migrate/common_test.go @@ -10,6 +10,9 @@ import ( "github.com/pressly/goose/v3" ) +// errWrite is the error failingWriter fails every write with. +var errWrite = errors.New("write failed") + // The commands are tested through the real command tree in cmd/jobsearch. // These tests cover reporting a failed migration, which a working set of // migrations can't trigger. @@ -92,8 +95,6 @@ func TestPrintResults(t *testing.T) { // failingWriter fails every write. type failingWriter struct{} -var errWrite = errors.New("write failed") - func (failingWriter) Write([]byte) (int, error) { return 0, errWrite } // A write error is returned, and the failed migration is still reported. diff --git a/internal/config/section_test.go b/internal/config/section_test.go index c222d7a..a4b0fe2 100644 --- a/internal/config/section_test.go +++ b/internal/config/section_test.go @@ -10,6 +10,9 @@ import ( "git.avsdev.uk/Go/job-search/internal/util" ) +// errRejected is the error testValidated.validate rejects "bad" with. +var errRejected = errors.New("rejected") + // Test sections exercising every field shape section.go supports. type testInner struct { Name string `cfg:"name" default:"inner"` @@ -51,8 +54,6 @@ type testValidated struct { Value string `cfg:"value"` } -var errRejected = errors.New("rejected") - func (c *testValidated) validate(key string, value any) error { if key == "value" && value.(string) == "bad" { return errRejected diff --git a/internal/types/opportunities.go b/internal/types/opportunities.go index c56f0ec..85bb0d6 100644 --- a/internal/types/opportunities.go +++ b/internal/types/opportunities.go @@ -5,6 +5,10 @@ import ( "time" ) +// ErrWonAndLost is returned by OpportunityMarks.Validate when both Won and +// Lost are set. +var ErrWonAndLost = errors.New("types: an opportunity cannot be both won and lost") + // Opportunity is a procurement opportunity (an OCDS contracting process, // identified by UUID) as of its latest release, with its classifications, // releases and the user's marks. Fields tagged db:"-" are stored in their @@ -51,10 +55,6 @@ type OpportunityReleaseTag struct { Tag string `db:"tag" json:"tag"` } -// ErrWonAndLost is returned by OpportunityMarks.Validate when both Won and -// Lost are set. -var ErrWonAndLost = errors.New("types: an opportunity cannot be both won and lost") - // OpportunityMarks are the user's own marks on an opportunity, kept apart // from the fetched data so saving a newer release never changes them. The // marks are independent except that Won and Lost exclude each other (see diff --git a/internal/websocket/client.go b/internal/websocket/client.go index 5504f3e..c491c21 100644 --- a/internal/websocket/client.go +++ b/internal/websocket/client.go @@ -24,6 +24,10 @@ const clientSendBuffer = 256 // cut short and logged as truncated. const logMessageLimit = 1 << 10 +// errUnsupportedData is returned by readLoop (and a tcpConn's Write) for a +// binary message. +var errUnsupportedData = errors.New("websocket: binary frames are not supported") + // msgConn is the connection a client reads and writes messages on: a // *websocket.Conn, or a tcpConn for plain TCP clients (see tcp.go). type msgConn interface { @@ -51,10 +55,6 @@ type client struct { closeOnce sync.Once } -// errUnsupportedData is returned by readLoop (and a tcpConn's Write) for a -// binary message. -var errUnsupportedData = errors.New("websocket: binary frames are not supported") - // newClient creates a client for conn. cancel cancels the context the client // runs with; the client calls it when it finishes or is closed. The client's // messages are limited by websocket.message_rate_limit if it is enabled.