Naming, layout and comments: descriptive full-word names, one item per line in wrapped calls and literals, and doc comments on exported names
Short and single-letter names in application code become full words (tests keep theirs). Wrapped calls, signatures and composite literals put one item per line, and one-line function bodies are expanded. Exported names and groups of constants get doc comments, and comments refer to the renamed identifiers.
This commit is contained in:
1 parent
043e307663
commit
465358609a
78 files changed
+2374
-1402
No files matched your search
@@ -37,11 +37,16 @@ func TestVersion(t *testing.T) {
|
||||
// flags and hooks, and the subcommand beneath it.
|
||||
func TestRootRunsCommands(t *testing.T) {
|
||||
cmdtest.Setup(t, "logging:\n level: info\n")
|
||||
cmdtest.Contains(t, cmdtest.MustRun(t, newRootCmd, "config", "get", "logging.level"),
|
||||
"info <string>")
|
||||
cmdtest.Contains(t,
|
||||
cmdtest.Contains(
|
||||
t,
|
||||
cmdtest.MustRun(t, newRootCmd, "config", "get", "logging.level"),
|
||||
"info <string>",
|
||||
)
|
||||
cmdtest.Contains(
|
||||
t,
|
||||
cmdtest.MustRun(t, newRootCmd, "config", "get", "logging.level", "--debug"),
|
||||
"debug <string>")
|
||||
"debug <string>",
|
||||
)
|
||||
}
|
||||
|
||||
func TestUnknownCommand(t *testing.T) {
|
||||
|
||||
+76
-52
@@ -107,7 +107,7 @@ func main() {
|
||||
}
|
||||
|
||||
func newRootCmd() *cobra.Command {
|
||||
o := &options{}
|
||||
opts := &options{}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "fts-mock",
|
||||
@@ -120,53 +120,68 @@ func newRootCmd() *cobra.Command {
|
||||
fts-mock --data-dir ./mock-data --reset
|
||||
fts-mock --min-gap 5s --max-gap 10s --rate-limit 1000`,
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
return o.validate()
|
||||
return opts.validate()
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return run(cmd.Context(), o)
|
||||
return run(cmd.Context(), opts)
|
||||
},
|
||||
}
|
||||
|
||||
f := cmd.Flags()
|
||||
f.SortFlags = false // show flags in the order defined here
|
||||
flags := cmd.Flags()
|
||||
flags.SortFlags = false // show flags in the order defined here
|
||||
|
||||
f.StringVar(&o.addr, "addr", ":8080", "listen address")
|
||||
f.StringVar(
|
||||
&o.baseURL,
|
||||
flags.StringVar(&opts.addr, "addr", ":8080", "listen address")
|
||||
flags.StringVar(
|
||||
&opts.baseURL,
|
||||
"base-url",
|
||||
"http://localhost:8080",
|
||||
"public base URL used in uri and links.next",
|
||||
)
|
||||
f.StringVar(
|
||||
&o.dataDir,
|
||||
flags.StringVar(
|
||||
&opts.dataDir,
|
||||
"data-dir",
|
||||
"./mock-data",
|
||||
"directory where releases.jsonl will be stored",
|
||||
)
|
||||
|
||||
f.BoolVar(
|
||||
&o.reset,
|
||||
flags.BoolVar(
|
||||
&opts.reset,
|
||||
"reset",
|
||||
false,
|
||||
"delete stored releases and regenerate the --backfill history",
|
||||
)
|
||||
|
||||
f.DurationVar(
|
||||
&o.backfill,
|
||||
flags.DurationVar(
|
||||
&opts.backfill,
|
||||
"backfill",
|
||||
72*time.Hour,
|
||||
"history to generate when the data directory is empty",
|
||||
)
|
||||
f.DurationVar(&o.minGap, "min-gap", 30*time.Second, "minimum time between generated releases")
|
||||
f.DurationVar(&o.maxGap, "max-gap", 5*time.Minute, "maximum time between generated releases")
|
||||
flags.DurationVar(
|
||||
&opts.minGap,
|
||||
"min-gap",
|
||||
30*time.Second,
|
||||
"minimum time between generated releases",
|
||||
)
|
||||
flags.DurationVar(
|
||||
&opts.maxGap,
|
||||
"max-gap",
|
||||
5*time.Minute,
|
||||
"maximum time between generated releases",
|
||||
)
|
||||
|
||||
f.IntVar(&o.rateLimit, "rate-limit", 12, "requests allowed per window per client")
|
||||
f.DurationVar(&o.rateWindow, "rate-window", 5*time.Minute, "rate limit window length")
|
||||
flags.IntVar(&opts.rateLimit, "rate-limit", 12, "requests allowed per window per client")
|
||||
flags.DurationVar(&opts.rateWindow, "rate-window", 5*time.Minute, "rate limit window length")
|
||||
|
||||
f.StringVar(&o.accessLogPath, "access-log", "-", `access log file, or "-" for stdout`)
|
||||
f.StringVar(&o.logFormat, "log-format", "combined", `access log format: "combined" or "common"`)
|
||||
f.StringVar(
|
||||
&o.tz,
|
||||
flags.StringVar(&opts.accessLogPath, "access-log", "-", `access log file, or "-" for stdout`)
|
||||
flags.StringVar(
|
||||
&opts.logFormat,
|
||||
"log-format",
|
||||
"combined",
|
||||
`access log format: "combined" or "common"`,
|
||||
)
|
||||
flags.StringVar(
|
||||
&opts.tz,
|
||||
"tz",
|
||||
"Europe/London",
|
||||
`time zone for updatedFrom/updatedTo without an offset ("Local" for this machine's)`,
|
||||
@@ -193,32 +208,32 @@ func (o *options) validate() error {
|
||||
}
|
||||
|
||||
// run starts the mock and blocks until ctx is cancelled or the server fails.
|
||||
func run(ctx context.Context, o *options) (err error) {
|
||||
loc, err := time.LoadLocation(o.tz) // already validated
|
||||
func run(ctx context.Context, opts *options) (err error) {
|
||||
location, err := time.LoadLocation(opts.tz) // already validated
|
||||
if err != nil {
|
||||
return fmt.Errorf("--tz: %w", err)
|
||||
}
|
||||
if err := os.MkdirAll(o.dataDir, 0o750); err != nil {
|
||||
if err := os.MkdirAll(opts.dataDir, 0o750); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// ----- catalog -----
|
||||
cat, err := mock.LoadCatalog()
|
||||
// ---------- catalog ----------
|
||||
catalog, err := mock.LoadCatalog()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
slog.Info(
|
||||
"catalog loaded",
|
||||
"buyers", len(cat.Buyers),
|
||||
"suppliers", len(cat.Suppliers),
|
||||
"subjects", len(cat.Subjects),
|
||||
"methods", len(cat.Methods),
|
||||
"classifications", len(cat.Classifications),
|
||||
"buyers", len(catalog.Buyers),
|
||||
"suppliers", len(catalog.Suppliers),
|
||||
"subjects", len(catalog.Subjects),
|
||||
"methods", len(catalog.Methods),
|
||||
"classifications", len(catalog.Classifications),
|
||||
)
|
||||
|
||||
// ----- store -----
|
||||
storePath := filepath.Join(o.dataDir, mock.ReleasesFile)
|
||||
if o.reset {
|
||||
// ---------- store ----------
|
||||
storePath := filepath.Join(opts.dataDir, mock.ReleasesFile)
|
||||
if opts.reset {
|
||||
if err := os.Remove(storePath); err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
@@ -228,16 +243,18 @@ func run(ctx context.Context, o *options) (err error) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() { err = errors.Join(err, store.Close()) }()
|
||||
defer func() {
|
||||
err = errors.Join(err, store.Close())
|
||||
}()
|
||||
|
||||
// ----- generator -----
|
||||
gen := mock.NewGenerator(cat, store, time.Now().UnixNano())
|
||||
n, err := gen.CatchUp(time.Now(), o.backfill, o.minGap, o.maxGap)
|
||||
// ---------- generator ----------
|
||||
generator := mock.NewGenerator(catalog, store, time.Now().UnixNano())
|
||||
generated, err := generator.CatchUp(time.Now(), opts.backfill, opts.minGap, opts.maxGap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
slog.Info("store ready", "releases", store.Len(), "generated", n)
|
||||
slog.Info("store ready", "releases", store.Len(), "generated", generated)
|
||||
|
||||
// The generator and server share a context that also ends if the server
|
||||
// fails, so the generator is never left running after run returns.
|
||||
@@ -247,34 +264,36 @@ func run(ctx context.Context, o *options) (err error) {
|
||||
workerDone := make(chan struct{})
|
||||
go func() {
|
||||
defer close(workerDone)
|
||||
gen.Run(ctx, o.minGap, o.maxGap)
|
||||
generator.Run(ctx, opts.minGap, opts.maxGap)
|
||||
}()
|
||||
|
||||
// ----- HTTP -----
|
||||
// ---------- HTTP ----------
|
||||
out := os.Stdout
|
||||
if o.accessLogPath != "-" {
|
||||
f, err := os.OpenFile(
|
||||
o.accessLogPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o600,
|
||||
if opts.accessLogPath != "-" {
|
||||
logFile, err := os.OpenFile(
|
||||
opts.accessLogPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o600,
|
||||
)
|
||||
if err != nil {
|
||||
cancel()
|
||||
<-workerDone
|
||||
return fmt.Errorf("opening access log: %w", err)
|
||||
}
|
||||
defer func() { err = errors.Join(err, f.Close()) }()
|
||||
out = f
|
||||
defer func() {
|
||||
err = errors.Join(err, logFile.Close())
|
||||
}()
|
||||
out = logFile
|
||||
}
|
||||
accessLogger := log.New(out, "", 0)
|
||||
|
||||
rl := mock.NewRateLimiter(o.rateLimit, o.rateWindow)
|
||||
rateLimiter := mock.NewRateLimiter(opts.rateLimit, opts.rateWindow)
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc(
|
||||
mock.BasePath, rl.Middleware(mock.NewHandler(store, o.baseURL, loc)),
|
||||
mock.BasePath, rateLimiter.Middleware(mock.NewHandler(store, opts.baseURL, location)),
|
||||
)
|
||||
|
||||
httpSrv := &http.Server{
|
||||
Addr: o.addr,
|
||||
Handler: logging.AccessLog(accessLogger, o.logFormat == "combined", mux),
|
||||
Addr: opts.addr,
|
||||
Handler: logging.AccessLog(accessLogger, opts.logFormat == "combined", mux),
|
||||
ReadHeaderTimeout: 10 * time.Second,
|
||||
}
|
||||
go func() {
|
||||
@@ -289,7 +308,12 @@ func run(ctx context.Context, o *options) (err error) {
|
||||
}
|
||||
}()
|
||||
|
||||
slog.Info("mock listening", "addr", o.addr, "path", mock.BasePath, "time_zone", loc.String())
|
||||
slog.Info(
|
||||
"mock listening",
|
||||
"addr", opts.addr,
|
||||
"path", mock.BasePath,
|
||||
"time_zone", location.String(),
|
||||
)
|
||||
serveErr := httpSrv.ListenAndServe()
|
||||
|
||||
cancel() // stop the worker (and the shutdown goroutine) if the server failed
|
||||
|
||||
Reference in new issue
Block a user