Skip to content

Test filesystem walks in memory

workspace never opens the real filesystem itself — it walks an afero.Fs you hand it. In production that is afero.NewOsFs(); in tests it is afero.NewMemMapFs(), so you can build an entire project layout in memory and assert on the walk with no temp directories and no cleanup.

Build a layout and detect

func TestDetectsModuleRoot(t *testing.T) {
    fs := afero.NewMemMapFs()
    require.NoError(t, afero.WriteFile(fs, "/proj/go.mod", []byte("module x"), 0o644))
    require.NoError(t, fs.MkdirAll("/proj/internal/pkg", 0o755))

    ws, err := workspace.Detect(fs, "/proj/internal/pkg", workspace.DefaultMarkers)
    require.NoError(t, err)

    assert.Equal(t, "/proj", ws.Root)
    assert.Equal(t, "go.mod", ws.Marker)
}

The marker is created two levels above the start directory; the walk climbs to it and reports the root and the marker that matched.

Assert the "not found" path

Because the memory filesystem contains only what you put there, the failure path is just as easy to exercise:

fs := afero.NewMemMapFs()
require.NoError(t, fs.MkdirAll("/nowhere/deep", 0o755))

_, err := workspace.Detect(fs, "/nowhere/deep", workspace.DefaultMarkers)
assert.ErrorIs(t, err, workspace.ErrNotFound)

Keep tests parallel-safe

An afero.NewMemMapFs() is independent per test, so detection tests can run with t.Parallel() freely. DetectFromCWD reads the process working directory with os.Getwd(), but the filesystem it queries is still the one you inject — a memory filesystem seeded at that working directory keeps even the CWD variant hermetic:

cwd, _ := os.Getwd()
fs := afero.NewMemMapFs()
require.NoError(t, afero.WriteFile(fs, cwd+"/go.mod", []byte("module x"), 0o644))

ws, err := workspace.DetectFromCWD(fs, workspace.DefaultMarkers)
require.NoError(t, err)
assert.Equal(t, cwd, ws.Root)

Prefer Detect with an explicit start directory when you want a test that does not depend on the working directory at all.