go

Go embed package getting a file from a directory outside package?


I am attempting to use the https://pkg.go.dev/embed package to acquire .sql file contents for queries. However it seems its not possible to use .or .. in the embed syntax and that makes me wonder if its possible at all to access files outside the current package in testing?

Simple reproduction of issue:

mkdir test && cd test

go mod init test

mkdir queries && cd queries

echo 'SELECT $1 as result;' > test.sql && cd ../

mkdir pg && cd pg

make a pg.go

package pg

func DoesSomething() string {
    return ""
}

make a pg_test.go

package pg_test

import (
    "fmt"
    "testing"
    _ "embed"

)

//go:embed ../queries/test.sql
var testQuery string

func TestDoesSomething(t *testing.T) {
    fmt.Println(testQuery)
}

test it: go test

Error received:

# test/pg
pg_test.go:9:12: pattern ../queries/test.sql: invalid pattern syntax
FAIL    test/pg [setup failed]

This does not work and im wondering if there is a way to make it work with embedding or if I need to write a work around.


Solution

  • You cannot use .. https://pkg.go.dev/embed:

    Patterns may not contain ‘.’ or ‘..’ or empty path elements, nor may they begin or end with a slash.

    Creating a symlink will also fail, for example if you do this:

    ln -s ../queries/test.sql test.sql
    

    and embed it like that:

    //go:embed test.sql
    var testQuery string
    

    You'll get:

    $ go test
    # test/pg
    pg_test.go:9:12: pattern test.sql: cannot embed irregular file test.sql
    FAIL    test/pg [setup failed]
    

    if there is a way to make it work with embedding

    None that I know of. You have to move test.sql to test/pg or create an extra package that will embed test.sql and use it in other packages. For example, create internal/assets/embed.go:

    // internal/assets/embed.go
    package assets
    
    import _ "embed"
    
    //go:embed test.sql
    var TestQuery string
    

    Move test.sql to test/internal/assets and change pg_test.go to:

    package pg_test
    
    import (
            _ "embed"
            "fmt"
            "test/internal/assets"
            "testing"
    )
    
    func TestDoesSomething(t *testing.T) {
            fmt.Println(assets.TestQuery)
    }
    

    Run test;

    $ go test -v .
    === RUN   TestDoesSomething
    SELECT $1 as result;
    
    --- PASS: TestDoesSomething (0.00s)
    PASS
    ok      test/pg 0.002s