github.com/aavshr/aws-sdk-go@v1.41.3/service/s3/cust_integ_eventstream_test.go (about) 1 //go:build integration 2 // +build integration 3 4 package s3_test 5 6 import ( 7 "bytes" 8 "encoding/csv" 9 "io" 10 "strings" 11 "testing" 12 13 "github.com/aavshr/aws-sdk-go/aws" 14 "github.com/aavshr/aws-sdk-go/aws/awserr" 15 "github.com/aavshr/aws-sdk-go/internal/sdkio" 16 "github.com/aavshr/aws-sdk-go/service/s3" 17 ) 18 19 func TestInteg_SelectObjectContent(t *testing.T) { 20 keyName := "selectObject.csv" 21 22 var header = []byte("A,B,C,D,E,F,G,H,I,J\n") 23 var recordRow = []byte("0,0,0.5,217.371,217.658,218.002,269.445,487.447,2.106,489.554\n") 24 25 buf := make([]byte, 0, 6*sdkio.MebiByte) 26 buf = append(buf, []byte(header)...) 27 for i := 0; i < (cap(buf)/len(recordRow))-1; i++ { 28 buf = append(buf, recordRow...) 29 } 30 31 // Put a mock CSV file to the S3 bucket so that its contents can be 32 // selected. 33 putTestContent(t, bytes.NewReader(buf), keyName) 34 35 resp, err := s3Svc.SelectObjectContent(&s3.SelectObjectContentInput{ 36 Bucket: &integMetadata.Buckets.Source.Name, 37 Key: &keyName, 38 Expression: aws.String("Select * from S3Object"), 39 ExpressionType: aws.String(s3.ExpressionTypeSql), 40 InputSerialization: &s3.InputSerialization{ 41 CSV: &s3.CSVInput{ 42 FieldDelimiter: aws.String(","), 43 FileHeaderInfo: aws.String(s3.FileHeaderInfoIgnore), 44 }, 45 }, 46 OutputSerialization: &s3.OutputSerialization{ 47 CSV: &s3.CSVOutput{ 48 FieldDelimiter: aws.String(","), 49 }, 50 }, 51 }) 52 if err != nil { 53 t.Fatalf("expect no error, %v", err) 54 } 55 defer resp.EventStream.Close() 56 57 recReader, recWriter := io.Pipe() 58 59 var sum int64 60 var processed int64 61 62 var gotEndEvent bool 63 go func(w *io.PipeWriter, resp *s3.SelectObjectContentOutput) { 64 defer recWriter.Close() 65 var numRecordEvents int64 66 for event := range resp.EventStream.Events() { 67 switch tv := event.(type) { 68 case *s3.RecordsEvent: 69 n, err := recWriter.Write(tv.Payload) 70 if err != nil { 71 t.Logf("failed to write to record writer, %v, %v", n, err) 72 } 73 sum += int64(n) 74 numRecordEvents++ 75 case *s3.StatsEvent: 76 processed = *tv.Details.BytesProcessed 77 case *s3.EndEvent: 78 gotEndEvent = true 79 t.Logf("s3.EndEvent received") 80 } 81 } 82 t.Logf("received %d record events", numRecordEvents) 83 }(recWriter, resp) 84 85 type Record []string 86 87 records := make(chan []Record) 88 go func(r io.Reader, records chan<- []Record, batchSize int) { 89 defer close(records) 90 91 csvReader := csv.NewReader(r) 92 var count int64 93 94 batch := make([]Record, 0, batchSize) 95 for { 96 count++ 97 record, err := csvReader.Read() 98 if err != nil { 99 if _, ok := err.(*csv.ParseError); ok { 100 t.Logf("failed to decode record row, %v, %v", count, err) 101 continue 102 } 103 if err != io.EOF { 104 t.Logf("csv decode failed, %v", err) 105 } 106 err = nil 107 break 108 } 109 batch = append(batch, record) 110 if len(batch) >= batchSize { 111 records <- batch 112 batch = batch[0:0] 113 } 114 } 115 if len(batch) != 0 { 116 records <- batch 117 } 118 }(recReader, records, 10) 119 120 var count int64 121 for batch := range records { 122 // To simulate processing of a batch, add sleep delay. 123 count += int64(len(batch)) 124 125 if err := resp.EventStream.Err(); err != nil { 126 t.Errorf("exect no error, got %v", err) 127 } 128 } 129 130 if !gotEndEvent { 131 t.Errorf("expected EndEvent, did not receive") 132 } 133 134 if e, a := int64(101474), count; e != a { 135 t.Errorf("expect %d records, got %d", e, a) 136 } 137 138 if sum == 0 { 139 t.Errorf("expect selected content, got none") 140 } 141 142 if processed == 0 { 143 t.Errorf("expect selected status bytes processed, got none") 144 } 145 146 if err := resp.EventStream.Err(); err != nil { 147 t.Fatalf("expect no error, got %v", err) 148 } 149 } 150 151 func TestInteg_SelectObjectContent_Error(t *testing.T) { 152 keyName := "negativeSelect.csv" 153 154 buf := make([]byte, 0, 6*sdkio.MebiByte) 155 buf = append(buf, []byte("name,number\n")...) 156 line := []byte("jj,0\n") 157 for i := 0; i < (cap(buf)/len(line))-2; i++ { 158 buf = append(buf, line...) 159 } 160 buf = append(buf, []byte("gg,NaN\n")...) 161 162 putTestContent(t, bytes.NewReader(buf), keyName) 163 164 resp, err := s3Svc.SelectObjectContent(&s3.SelectObjectContentInput{ 165 Bucket: &integMetadata.Buckets.Source.Name, 166 Key: &keyName, 167 Expression: aws.String("SELECT name FROM S3Object WHERE cast(number as int) < 1"), 168 ExpressionType: aws.String(s3.ExpressionTypeSql), 169 InputSerialization: &s3.InputSerialization{ 170 CSV: &s3.CSVInput{ 171 FileHeaderInfo: aws.String(s3.FileHeaderInfoUse), 172 }, 173 }, 174 OutputSerialization: &s3.OutputSerialization{ 175 CSV: &s3.CSVOutput{ 176 FieldDelimiter: aws.String(","), 177 }, 178 }, 179 }) 180 if err != nil { 181 t.Fatalf("expect no error, %v", err) 182 } 183 defer resp.EventStream.Close() 184 185 var sum int64 186 for event := range resp.EventStream.Events() { 187 switch tv := event.(type) { 188 case *s3.RecordsEvent: 189 sum += int64(len(tv.Payload)) 190 } 191 } 192 193 if sum == 0 { 194 t.Errorf("expect selected content") 195 } 196 197 err = resp.EventStream.Err() 198 if err == nil { 199 t.Fatalf("exepct error") 200 } 201 202 aerr := err.(awserr.Error) 203 if a := aerr.Code(); len(a) == 0 { 204 t.Errorf("expect, error code") 205 } 206 if a := aerr.Message(); len(a) == 0 { 207 t.Errorf("expect, error message") 208 } 209 } 210 211 func TestInteg_SelectObjectContent_Stream(t *testing.T) { 212 keyName := "selectGopher.csv" 213 214 buf := `name,number 215 gopher,0 216 ᵷodɥǝɹ,1 217 ` 218 // Put a mock CSV file to the S3 bucket so that its contents can be 219 // selected. 220 putTestContent(t, strings.NewReader(buf), keyName) 221 222 // Make the Select Object Content API request using the object uploaded. 223 resp, err := s3Svc.SelectObjectContent(&s3.SelectObjectContentInput{ 224 Bucket: &integMetadata.Buckets.Source.Name, 225 Key: &keyName, 226 Expression: aws.String("SELECT name FROM S3Object WHERE cast(number as int) < 1"), 227 ExpressionType: aws.String(s3.ExpressionTypeSql), 228 InputSerialization: &s3.InputSerialization{ 229 CSV: &s3.CSVInput{ 230 FileHeaderInfo: aws.String(s3.FileHeaderInfoUse), 231 }, 232 }, 233 OutputSerialization: &s3.OutputSerialization{ 234 CSV: &s3.CSVOutput{}, 235 }, 236 }) 237 if err != nil { 238 t.Fatalf("failed making API request, %v\n", err) 239 } 240 defer resp.EventStream.Close() 241 242 results, resultWriter := io.Pipe() 243 go func() { 244 defer resultWriter.Close() 245 for event := range resp.EventStream.Events() { 246 switch e := event.(type) { 247 case *s3.RecordsEvent: 248 resultWriter.Write(e.Payload) 249 case *s3.StatsEvent: 250 t.Logf("Processed %d bytes\n", *e.Details.BytesProcessed) 251 } 252 } 253 }() 254 255 // Printout the results 256 resReader := csv.NewReader(results) 257 for { 258 record, err := resReader.Read() 259 if err == io.EOF { 260 break 261 } 262 t.Log(record) 263 } 264 265 if err := resp.EventStream.Err(); err != nil { 266 t.Fatalf("reading from event stream failed, %v\n", err) 267 } 268 }