Trying to mock the following function. It basically takes an object from S3 (io.ReadCloser
) and writes it to a file that execution os.Open()
d earlier (io.WriteCloser
).
package main
import (
"io"
log "github.com/sirupsen/logrus"
)
func writeFile(destination io.WriteCloser, source io.ReadCloser) error {
defer destination.Close()
defer source.Close()
_, err := io.Copy(destination, source)
if err != nil {
log.WithFields(log.Fields{"desc": "unable to copy contents from s3 to blahblah"}).Error(err)
return err
}
return nil
}
I think I'm pretty close, but at the moment my test hangs and never errors / succeeds... I also realized that I can pass os.Stdout
to my destination, but still ran into the same issue. Something is going on within io.Copy
. I imagine it's because I'm trying to copy empty data to nothing?
package main
import (
"errors"
"io"
"reflect"
"testing"
)
type mockReadCloser struct {}
func (m mockReadCloser) Read(p []byte) (int, error) { return 0, nil }
func (m mockReadCloser) Close() error { return nil }
type mockWriteCloser struct{}
func (m mockWriteCloser) Close() error { return nil }
func (m mockWriteCloser) Write(b []byte) (n int, err error) { return 0, nil }
func Test_writeFile(t *testing.T) {
type args struct {
destination io.WriteCloser
source io.ReadCloser
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "",
args: args{
destination: &mockWriteCloser{},
source: &mockReadCloser{},
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := writeFile(tt.args.destination, tt.args.source); (err != nil) != tt.wantErr {
t.Errorf("writeFile() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
CodePudding user response:
It's because io.Copy doesn't return until EOF or error.
Copy copies from src to dst until either EOF is reached on src or an error occurs. It returns the number of bytes copied and the first error encountered while copying, if any.
A successful Copy returns err == nil, not err == EOF. Because Copy is defined to read from src until EOF, it does not treat an EOF from Read as an error to be reported.
So, if you return EOF from your mockReadCloser.Read
, it shouldn't hang any more.
func (m mockReadCloser) Read(p []byte) (int, error) { return 0, io.EOF }
This is because Read is called repeatedly until there is nothing more to read (EOF).
for {
nr, er := src.Read(buf)
...
}