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  }