The problem
I rewrote my tests to use an fstest.MapFS
, defined like this
contentRoot := fstest.MapFS{
"/parentDir/index.html": &fstest.MapFile{Data: []byte("content"), Mode: 0o755},
"/parentDir/childDir/1-2-3.html": &fstest.MapFile{Data: []byte("other content"`), Mode: 0o755},
}
Simple, and yet when I ran a test against it, I received this error:
2024/03/27 19:42:59 http: panic serving [::1]:57135: open parentDir: file does not exist
The setup
I wrote this comment a few days ago and haven't thought much about it since:
// /index.html becomes index.html
// /articles/page.html becomes articles/page.html
// without this the paths aren't found properly inside the fs.
pagePath = strings.TrimPrefix(pagePath, "/")
pageContent, err := fs.ReadFile(a.SiteFiles, pagePath)
pagePath is retrieved from an http GET, and always starts with a "/"; I don't want to be seeking for an absolute
path inside the os.DirFS because the file won't be found.
ReadFile dispatches to the a.SiteFiles implementation of the Open function, so the os.DirFS
controls lookup of the file. You
can see that in the official source:
func ReadFile(fsys FS, name string) ([]byte, error) {
if fsys, ok := fsys.(ReadFileFS); ok {
return fsys.ReadFile(name)
}
file, err := fsys.Open(name)
What's the misunderstanding?
This one took me a couple of hours to finally understand.
An fstest.MapFS
is a hash map. Its implementation of Open()
is very simple:
file := fsys[name]
- reference in the go source code.
It simply looks up the key in the hashmap, and I was manipulating that key to remove a leading slash.
What's the fix?
Once I understood that the path in the fstest.MapFS
is only a map key, I realised it does not need to be an absolute path.
I redefined it like this and got my tests passing:
contentRoot := fstest.MapFS{
"parentDir/index.html": &fstest.MapFile{Data: []byte("content"), Mode: 0o755},
"parentDir/childDir/1-2-3.html": &fstest.MapFile{Data: []byte("other content"`), Mode: 0o755},
}