Unusable DATA_DIR error names DATA_DIR; report file number is atomic.Int64 (closes #148, closes #161)
check / check (push) Waiting to run
check / check (push) Waiting to run
When the backend runs on its own, a DATA_DIR that could not be created or read stopped the start with an error that named the path but not DATA_DIR, and one where no file could be created let it start, to fail at the first write of a report file. The start now creates a file in DATA_DIR and removes it, and all three cases fail with DATA_DIR "<value>": <reason>, the form config.New uses for the other settings. Tests start the buffer with DATA_DIR below a regular file and with DATA_DIR set to /proc. The report file sequence counter is atomic.Int64 instead of atomic.Uint64, as the Go style guide asks for int types. File names are unchanged. Model: opus-5-5
This commit is contained in:
@@ -21,6 +21,17 @@ Run `make frontend-viewport-test` in CI as its own step; it is not part of
|
||||
|
||||
# Completed Steps
|
||||
|
||||
- 2026-10-18: when the backend runs on its own, a `DATA_DIR` that cannot be
|
||||
created or read, or where no file can be created, stops the start with an
|
||||
error naming `DATA_DIR` and its value, as the other settings' errors do
|
||||
([#148](https://git.eeqj.de/sneak/netwatch/issues/148)); the first two named
|
||||
only the path, and the third was found only at the first write of a report
|
||||
file. At start the server creates a file in `DATA_DIR` and removes it. Unit
|
||||
tests start the buffer with `DATA_DIR` below a regular file and with
|
||||
`DATA_DIR` set to `/proc`, and check the error. The report file number is an
|
||||
`atomic.Int64`, as the Go style guide asks for int types
|
||||
([#161](https://git.eeqj.de/sneak/netwatch/issues/161)); file names are
|
||||
unchanged
|
||||
- 2026-10-18: a browser that cannot format the header's clocks still probes
|
||||
([#149](https://git.eeqj.de/sneak/netwatch/issues/149)). The clocks name the
|
||||
local time zone through `Intl.DateTimeFormat`'s `shortGeneric` option, which
|
||||
|
||||
@@ -88,7 +88,7 @@ type Buffer struct {
|
||||
now func() time.Time
|
||||
// seq numbers the report files, so that two named in the same
|
||||
// millisecond still get different names.
|
||||
seq atomic.Uint64
|
||||
seq atomic.Int64
|
||||
stopOnce sync.Once
|
||||
// usedBytes is what Append checks against maxBytes: the size
|
||||
// of the report files in dataDir, plus the reports waiting to
|
||||
@@ -127,16 +127,33 @@ func New(
|
||||
|
||||
lc.Append(fx.Hook{
|
||||
OnStart: func(_ context.Context) error {
|
||||
// The errors name DATA_DIR, as config.New's do for the
|
||||
// settings it checks.
|
||||
err := os.MkdirAll(b.dataDir, dirPerms)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create data dir: %w", err)
|
||||
return fmt.Errorf("DATA_DIR %q: %w", b.dataDir, err)
|
||||
}
|
||||
|
||||
// Creating and removing a file stops the start when no
|
||||
// file can be created in DATA_DIR, instead of the first
|
||||
// write of a report file failing after reports were
|
||||
// answered 200. A full disk is not caught: the empty
|
||||
// file is still created.
|
||||
f, err := os.CreateTemp(b.dataDir, "")
|
||||
if err != nil {
|
||||
return fmt.Errorf("DATA_DIR %q: %w", b.dataDir, err)
|
||||
}
|
||||
|
||||
err = errors.Join(f.Close(), os.Remove(f.Name()))
|
||||
if err != nil {
|
||||
return fmt.Errorf("DATA_DIR %q: %w", b.dataDir, err)
|
||||
}
|
||||
|
||||
// Report files left by earlier runs count too, and are
|
||||
// the first to be deleted to make room.
|
||||
files, err := reportFiles(b.dataDir)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("DATA_DIR %q: %w", b.dataDir, err)
|
||||
}
|
||||
|
||||
b.mu.Lock()
|
||||
|
||||
@@ -108,6 +108,68 @@ func TestFailedFinalFlushFailsStop(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestUncreatableDataDirStopsStart sets DATA_DIR below a regular file,
|
||||
// where no directory can be created, even by root, as the tests run in
|
||||
// the backend image. The start must fail with an error naming DATA_DIR
|
||||
// and its value, as the errors for the other settings do.
|
||||
func TestUncreatableDataDirStopsStart(t *testing.T) {
|
||||
file := filepath.Join(t.TempDir(), "file")
|
||||
writeBytes(t, file, 1)
|
||||
|
||||
dir := filepath.Join(file, "reports")
|
||||
t.Setenv("DATA_DIR", dir)
|
||||
|
||||
app := fxtest.New(t,
|
||||
fx.Provide(
|
||||
globals.New,
|
||||
logger.New,
|
||||
config.New,
|
||||
reportbuf.New,
|
||||
),
|
||||
fx.Invoke(func(*reportbuf.Buffer) {}),
|
||||
)
|
||||
|
||||
err := app.Start(t.Context())
|
||||
if err == nil {
|
||||
t.Fatal("start succeeded with DATA_DIR below a regular file")
|
||||
}
|
||||
|
||||
want := fmt.Sprintf("DATA_DIR %q: ", dir)
|
||||
if !strings.Contains(err.Error(), want) {
|
||||
t.Fatalf("start error = %q, want it to contain %q", err, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestUnwritableDataDirStopsStart sets DATA_DIR to /proc, which exists
|
||||
// and can be read, but where no file can be created, even by root, as
|
||||
// the tests run in the backend image: root ignores a directory the test
|
||||
// makes read-only. The start must fail with an error naming DATA_DIR and
|
||||
// its value, rather than the first write of a report file.
|
||||
func TestUnwritableDataDirStopsStart(t *testing.T) {
|
||||
dir := "/proc"
|
||||
t.Setenv("DATA_DIR", dir)
|
||||
|
||||
app := fxtest.New(t,
|
||||
fx.Provide(
|
||||
globals.New,
|
||||
logger.New,
|
||||
config.New,
|
||||
reportbuf.New,
|
||||
),
|
||||
fx.Invoke(func(*reportbuf.Buffer) {}),
|
||||
)
|
||||
|
||||
err := app.Start(t.Context())
|
||||
if err == nil {
|
||||
t.Fatal("start succeeded with DATA_DIR /proc")
|
||||
}
|
||||
|
||||
want := fmt.Sprintf("DATA_DIR %q: ", dir)
|
||||
if !strings.Contains(err.Error(), want) {
|
||||
t.Fatalf("start error = %q, want it to contain %q", err, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestStopWaitsForPeriodicWrite holds open the write the periodic flush
|
||||
// starts, then stops the app. The stop must wait until the write is
|
||||
// released, and the report must then be in its file. The test runs in
|
||||
|
||||
Reference in New Issue
Block a user