github.com/wfusion/gofusion@v1.1.14/test/common/utils/cases/encode_test.go (about)

     1  package cases
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"math/rand"
     8  	"strings"
     9  	"sync"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/stretchr/testify/suite"
    14  
    15  	"github.com/wfusion/gofusion/common/utils"
    16  	"github.com/wfusion/gofusion/common/utils/cipher"
    17  	"github.com/wfusion/gofusion/common/utils/compress"
    18  	"github.com/wfusion/gofusion/common/utils/encode"
    19  	"github.com/wfusion/gofusion/log"
    20  
    21  	testUtl "github.com/wfusion/gofusion/test/common/utils"
    22  )
    23  
    24  func TestEncode(t *testing.T) {
    25  	t.Parallel()
    26  	testingSuite := &Encode{Test: new(testUtl.Test)}
    27  	suite.Run(t, testingSuite)
    28  }
    29  
    30  type Encode struct {
    31  	*testUtl.Test
    32  }
    33  
    34  func (t *Encode) BeforeTest(suiteName, testName string) {
    35  	t.Catch(func() {
    36  		log.Info(context.Background(), "right before %s %s", suiteName, testName)
    37  	})
    38  }
    39  
    40  func (t *Encode) AfterTest(suiteName, testName string) {
    41  	t.Catch(func() {
    42  		log.Info(context.Background(), "right after %s %s", suiteName, testName)
    43  	})
    44  }
    45  
    46  func (t *Encode) TestCodecSingleCombination() {
    47  	cases := [][]encode.EncodedType{
    48  		{encode.EncodedTypeCipher},
    49  		{encode.EncodedTypeCompress},
    50  		{encode.EncodedTypeEncode},
    51  	}
    52  	t.runCodecCases(cases)
    53  }
    54  
    55  func (t *Encode) TestCodecDoubleCombinations() {
    56  	cases := [][]encode.EncodedType{
    57  		{encode.EncodedTypeCipher, encode.EncodedTypeCompress},
    58  		{encode.EncodedTypeCompress, encode.EncodedTypeCipher},
    59  		{encode.EncodedTypeCipher, encode.EncodedTypeEncode},
    60  		{encode.EncodedTypeEncode, encode.EncodedTypeCipher},
    61  		{encode.EncodedTypeCompress, encode.EncodedTypeEncode},
    62  		{encode.EncodedTypeEncode, encode.EncodedTypeCompress},
    63  	}
    64  
    65  	t.runCodecCases(cases)
    66  }
    67  
    68  func (t *Encode) TestCodecTripleCombinations() {
    69  	cases := [][]encode.EncodedType{
    70  		{encode.EncodedTypeCipher, encode.EncodedTypeCompress, encode.EncodedTypeEncode},
    71  		{encode.EncodedTypeCipher, encode.EncodedTypeEncode, encode.EncodedTypeCompress},
    72  		{encode.EncodedTypeCompress, encode.EncodedTypeCipher, encode.EncodedTypeEncode},
    73  		{encode.EncodedTypeCompress, encode.EncodedTypeEncode, encode.EncodedTypeCipher},
    74  		{encode.EncodedTypeEncode, encode.EncodedTypeCompress, encode.EncodedTypeCipher},
    75  		{encode.EncodedTypeEncode, encode.EncodedTypeCipher, encode.EncodedTypeCompress},
    76  	}
    77  
    78  	t.runCodecCases(cases)
    79  }
    80  
    81  func (t *Encode) TestStreamSingleCombination() {
    82  	cases := [][]encode.EncodedType{
    83  		{encode.EncodedTypeCipher},
    84  		{encode.EncodedTypeCompress},
    85  		{encode.EncodedTypeEncode},
    86  	}
    87  
    88  	t.runStreamCases(cases)
    89  	t.runStreamParallelCases(cases)
    90  }
    91  
    92  func (t *Encode) TestStreamDoubleCombinations() {
    93  	cases := [][]encode.EncodedType{
    94  		{encode.EncodedTypeCipher, encode.EncodedTypeCompress},
    95  		{encode.EncodedTypeCompress, encode.EncodedTypeCipher},
    96  		{encode.EncodedTypeCipher, encode.EncodedTypeEncode},
    97  		{encode.EncodedTypeEncode, encode.EncodedTypeCipher},
    98  		{encode.EncodedTypeCompress, encode.EncodedTypeEncode},
    99  		{encode.EncodedTypeEncode, encode.EncodedTypeCompress},
   100  	}
   101  
   102  	t.runStreamCases(cases)
   103  	t.runStreamParallelCases(cases)
   104  }
   105  
   106  func (t *Encode) TestStreamTripleCombinations() {
   107  	cases := [][]encode.EncodedType{
   108  		{encode.EncodedTypeCipher, encode.EncodedTypeCompress, encode.EncodedTypeEncode},
   109  		{encode.EncodedTypeCipher, encode.EncodedTypeEncode, encode.EncodedTypeCompress},
   110  		{encode.EncodedTypeCompress, encode.EncodedTypeCipher, encode.EncodedTypeEncode},
   111  		{encode.EncodedTypeCompress, encode.EncodedTypeEncode, encode.EncodedTypeCipher},
   112  		{encode.EncodedTypeEncode, encode.EncodedTypeCompress, encode.EncodedTypeCipher},
   113  		{encode.EncodedTypeEncode, encode.EncodedTypeCipher, encode.EncodedTypeCompress},
   114  	}
   115  
   116  	t.runStreamCases(cases)
   117  	t.runStreamParallelCases(cases)
   118  }
   119  
   120  func (t *Encode) runCodecCases(cases [][]encode.EncodedType) {
   121  	t.Catch(func() {
   122  		data := t.smallRandomData()
   123  		for _, cs := range cases {
   124  			for _, testCase := range t.generateCodecCases(cs...) {
   125  				t.Run(strings.Join(testCase.names, "->"), func() {
   126  					options := testCase.extenders
   127  					reversed := make([]utils.OptionExtender, len(options))
   128  					for i, v := range options {
   129  						reversed[len(options)-1-i] = v
   130  					}
   131  
   132  					// case: encode then decode
   133  					encoded, err := encode.From(data).Encode(options...).ToBytes()
   134  					t.NoError(err)
   135  					t.NotEqualValues(data, encoded)
   136  
   137  					codec := encode.From(encoded)
   138  					for i := 0; i < len(reversed); i++ {
   139  						codec = codec.Decode(reversed[i])
   140  					}
   141  					decoded, err := codec.ToString()
   142  					t.NoError(err)
   143  					t.EqualValues(data, []byte(decoded))
   144  
   145  					// case: encoded and decode together
   146  					actual, err := encode.
   147  						From(data).
   148  						Encode(options...).
   149  						Decode(reversed...).
   150  						ToBytes()
   151  					t.NoError(err)
   152  					t.EqualValues(data, actual)
   153  				})
   154  			}
   155  		}
   156  	})
   157  }
   158  
   159  func (t *Encode) runStreamCases(cases [][]encode.EncodedType) {
   160  	t.Catch(func() {
   161  		data := t.randomData()
   162  		for _, cs := range cases {
   163  			for _, testCase := range t.generateStreamCases(cs...) {
   164  				t.Run(strings.Join(testCase.names, "->"), func() {
   165  					codecStream := encode.NewCodecStream(testCase.extenders...)
   166  					dataBuffer := bytes.NewReader(data)
   167  
   168  					encodedBuffer := bytes.NewBuffer(nil)
   169  					_, err := codecStream.Encode(encodedBuffer, dataBuffer)
   170  					t.NoError(err)
   171  					t.NotZero(encodedBuffer.Len())
   172  					t.NotEqualValues(data, encodedBuffer.Bytes())
   173  
   174  					decodedBuffer := bytes.NewBuffer(nil)
   175  					_, err = codecStream.Decode(decodedBuffer, encodedBuffer)
   176  					t.NoError(err)
   177  
   178  					t.EqualValues(data, decodedBuffer.Bytes())
   179  				})
   180  			}
   181  		}
   182  	})
   183  }
   184  
   185  func (t *Encode) runStreamParallelCases(cases [][]encode.EncodedType) {
   186  	t.Catch(func() {
   187  		t.Run(fmt.Sprintf("stream-combination-%v-parallel", len(cases[0])), func() {
   188  			data := t.randomData()
   189  			wg := new(sync.WaitGroup)
   190  			defer wg.Wait()
   191  			for _, cs := range cases {
   192  				for _, item := range t.generateStreamCases(cs...) {
   193  					wg.Add(1)
   194  
   195  					testCase := item
   196  					go func() {
   197  						defer wg.Done()
   198  
   199  						codecStream := encode.NewCodecStream(testCase.extenders...)
   200  						dataBuffer := bytes.NewReader(data)
   201  
   202  						encodedBuffer := bytes.NewBuffer(nil)
   203  						_, err := codecStream.Encode(encodedBuffer, dataBuffer)
   204  						t.NoError(err)
   205  						t.NotZero(encodedBuffer.Len())
   206  						t.NotEqualValues(data, encodedBuffer.Bytes())
   207  
   208  						decodedBuffer := bytes.NewBuffer(nil)
   209  						_, err = codecStream.Decode(decodedBuffer, encodedBuffer)
   210  						t.NoError(err)
   211  
   212  						t.EqualValues(data, decodedBuffer.Bytes())
   213  					}()
   214  				}
   215  			}
   216  		})
   217  	})
   218  }
   219  
   220  func (t *Encode) randomData() (data []byte) {
   221  	const (
   222  		jitterLength = 4 * 1024                   // 4kb
   223  		largeLength  = 1024*1024 - jitterLength/2 // 1m - 2kb
   224  	)
   225  
   226  	// 1m ± 2kb
   227  	data = make([]byte, largeLength+rand.Int()%(jitterLength/2))
   228  	//data = make([]byte, 10)
   229  	_, err := utils.Random(data, utils.GetTimeStamp(time.Now()))
   230  	t.NoError(err)
   231  	return
   232  }
   233  
   234  func (t *Encode) smallRandomData() (data []byte) {
   235  	const (
   236  		jitterLength = 4 * 1024                 // 4kb
   237  		smallLength  = 32*1024 - jitterLength/2 // 32kb - 2kb
   238  	)
   239  
   240  	// 32kb ± 2kb
   241  	data = make([]byte, smallLength+rand.Int()%(jitterLength/2))
   242  	_, err := utils.Random(data, utils.GetTimeStamp(time.Now()))
   243  	t.NoError(err)
   244  	return
   245  }
   246  
   247  func (t *Encode) generateCodecCases(types ...encode.EncodedType) (options []*option) {
   248  	opts := make(optionList, 0, len(types))
   249  	for _, typ := range types {
   250  		switch typ {
   251  		case encode.EncodedTypeCipher:
   252  			opts = append(opts, t.cipherOptions())
   253  		case encode.EncodedTypeCompress:
   254  			opts = append(opts, t.compressOptions())
   255  		case encode.EncodedTypeEncode:
   256  			opts = append(opts, t.printableOptions())
   257  		}
   258  	}
   259  
   260  	return opts.combine()
   261  }
   262  
   263  func (t *Encode) generateStreamCases(types ...encode.EncodedType) (options []*option) {
   264  	opts := make(optionList, 0, len(types))
   265  	for _, typ := range types {
   266  		switch typ {
   267  		case encode.EncodedTypeCipher:
   268  			opts = append(opts, t.cipherStreamOptions())
   269  		case encode.EncodedTypeCompress:
   270  			opts = append(opts, t.compressOptions())
   271  		case encode.EncodedTypeEncode:
   272  			opts = append(opts, t.printableOptions())
   273  		}
   274  	}
   275  
   276  	return opts.combine()
   277  }
   278  
   279  func (t *Encode) cipherStreamOptions() (opt *option) {
   280  	opt = new(option)
   281  
   282  	cipherModes := []cipher.Mode{
   283  		cipher.ModeCFB,
   284  		cipher.ModeCTR,
   285  		cipher.ModeOFB,
   286  		cipher.ModeGCM,
   287  	}
   288  
   289  	// cipher Options
   290  	var (
   291  		k1        [1]byte
   292  		k8, iv8   [8]byte
   293  		k16, iv16 [16]byte
   294  		k24       [24]byte
   295  		k32       [32]byte
   296  		k256      [256]byte
   297  	)
   298  	_, err := utils.Random(k1[:], utils.GetTimeStamp(time.Now()))
   299  	t.NoError(err)
   300  	_, err = utils.Random(k8[:], utils.GetTimeStamp(time.Now()))
   301  	t.NoError(err)
   302  	_, err = utils.Random(iv8[:], utils.GetTimeStamp(time.Now()))
   303  	t.NoError(err)
   304  	_, err = utils.Random(k16[:], utils.GetTimeStamp(time.Now()))
   305  	t.NoError(err)
   306  	_, err = utils.Random(iv16[:], utils.GetTimeStamp(time.Now()))
   307  	t.NoError(err)
   308  	_, err = utils.Random(k24[:], utils.GetTimeStamp(time.Now()))
   309  	t.NoError(err)
   310  	_, err = utils.Random(k32[:], utils.GetTimeStamp(time.Now()))
   311  	t.NoError(err)
   312  	_, err = utils.Random(k256[:], utils.GetTimeStamp(time.Now()))
   313  	t.NoError(err)
   314  
   315  	for _, mode := range cipherModes {
   316  		if mode != cipher.ModeGCM {
   317  			opt.names = append(opt.names, cipher.AlgorithmDES.String()+"-"+mode.String())
   318  			opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmDES, mode, k8[:], iv8[:]))
   319  
   320  			opt.names = append(opt.names, cipher.Algorithm3DES.String()+"-"+mode.String())
   321  			opt.extenders = append(opt.extenders, encode.Cipher(cipher.Algorithm3DES, mode, k24[:], iv8[:]))
   322  		}
   323  
   324  		opt.names = append(opt.names, cipher.AlgorithmAES.String()+"-128-"+mode.String())
   325  		opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmAES, mode, k16[:], iv16[:]))
   326  
   327  		opt.names = append(opt.names, cipher.AlgorithmAES.String()+"-192-"+mode.String())
   328  		opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmAES, mode, k24[:], iv16[:]))
   329  
   330  		opt.names = append(opt.names, cipher.AlgorithmAES.String()+"-256-"+mode.String())
   331  		opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmAES, mode, k32[:], iv16[:]))
   332  	}
   333  
   334  	opt.names = append(opt.names, cipher.AlgorithmRC4.String()+"-8")
   335  	opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmRC4, 0, k1[:], nil))
   336  
   337  	opt.names = append(opt.names, cipher.AlgorithmRC4.String()+"-256")
   338  	opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmRC4, 0, k32[:], nil))
   339  
   340  	opt.names = append(opt.names, cipher.AlgorithmRC4.String()+"-1024")
   341  	opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmRC4, 0, k256[:], nil))
   342  
   343  	opt.names = append(opt.names, cipher.AlgorithmChaCha20poly1305.String())
   344  	opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmChaCha20poly1305, 0, k32[:], nil))
   345  
   346  	opt.names = append(opt.names, cipher.AlgorithmXChaCha20poly1305.String())
   347  	opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmXChaCha20poly1305, 0, k32[:], nil))
   348  
   349  	return
   350  }
   351  
   352  func (t *Encode) cipherOptions() (opt *option) {
   353  	opt = new(option)
   354  
   355  	cipherModes := []cipher.Mode{
   356  		cipher.ModeECB,
   357  		cipher.ModeCBC,
   358  		cipher.ModeCFB,
   359  		cipher.ModeCTR,
   360  		cipher.ModeOFB,
   361  		cipher.ModeGCM,
   362  	}
   363  
   364  	// cipher Options
   365  	var (
   366  		k1        [1]byte
   367  		k8, iv8   [8]byte
   368  		k16, iv16 [16]byte
   369  		k24       [24]byte
   370  		k32       [32]byte
   371  		k256      [256]byte
   372  	)
   373  	_, err := utils.Random(k1[:], utils.GetTimeStamp(time.Now()))
   374  	t.NoError(err)
   375  	_, err = utils.Random(k8[:], utils.GetTimeStamp(time.Now()))
   376  	t.NoError(err)
   377  	_, err = utils.Random(iv8[:], utils.GetTimeStamp(time.Now()))
   378  	t.NoError(err)
   379  	_, err = utils.Random(k16[:], utils.GetTimeStamp(time.Now()))
   380  	t.NoError(err)
   381  	_, err = utils.Random(iv16[:], utils.GetTimeStamp(time.Now()))
   382  	t.NoError(err)
   383  	_, err = utils.Random(k24[:], utils.GetTimeStamp(time.Now()))
   384  	t.NoError(err)
   385  	_, err = utils.Random(k32[:], utils.GetTimeStamp(time.Now()))
   386  	t.NoError(err)
   387  	_, err = utils.Random(k256[:], utils.GetTimeStamp(time.Now()))
   388  	t.NoError(err)
   389  
   390  	for _, mode := range cipherModes {
   391  		if mode != cipher.ModeGCM {
   392  			opt.names = append(opt.names, cipher.AlgorithmDES.String()+"-"+mode.String())
   393  			opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmDES, mode, k8[:], iv8[:]))
   394  
   395  			opt.names = append(opt.names, cipher.Algorithm3DES.String()+"-"+mode.String())
   396  			opt.extenders = append(opt.extenders, encode.Cipher(cipher.Algorithm3DES, mode, k24[:], iv8[:]))
   397  		}
   398  
   399  		opt.names = append(opt.names, cipher.AlgorithmAES.String()+"-128-"+mode.String())
   400  		opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmAES, mode, k16[:], iv16[:]))
   401  
   402  		opt.names = append(opt.names, cipher.AlgorithmAES.String()+"-192-"+mode.String())
   403  		opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmAES, mode, k24[:], iv16[:]))
   404  
   405  		opt.names = append(opt.names, cipher.AlgorithmAES.String()+"-256-"+mode.String())
   406  		opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmAES, mode, k32[:], iv16[:]))
   407  
   408  		opt.names = append(opt.names, cipher.AlgorithmSM4.String()+"-"+mode.String())
   409  		opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmSM4, mode, k16[:], iv16[:]))
   410  	}
   411  
   412  	opt.names = append(opt.names, cipher.AlgorithmRC4.String()+"-8")
   413  	opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmRC4, 0, k1[:], nil))
   414  
   415  	opt.names = append(opt.names, cipher.AlgorithmRC4.String()+"-256")
   416  	opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmRC4, 0, k32[:], nil))
   417  
   418  	opt.names = append(opt.names, cipher.AlgorithmRC4.String()+"-1024")
   419  	opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmRC4, 0, k256[:], nil))
   420  
   421  	opt.names = append(opt.names, cipher.AlgorithmChaCha20poly1305.String())
   422  	opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmChaCha20poly1305, 0, k32[:], nil))
   423  
   424  	opt.names = append(opt.names, cipher.AlgorithmXChaCha20poly1305.String())
   425  	opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmXChaCha20poly1305, 0, k32[:], nil))
   426  
   427  	return
   428  }
   429  
   430  func (t *Encode) compressOptions() (opt *option) {
   431  	opt = new(option)
   432  
   433  	algos := []compress.Algorithm{
   434  		compress.AlgorithmZSTD,
   435  		compress.AlgorithmZLib,
   436  		compress.AlgorithmS2,
   437  		compress.AlgorithmGZip,
   438  		compress.AlgorithmDeflate,
   439  	}
   440  
   441  	opt.names = make([]string, 0, len(algos))
   442  	opt.extenders = make([]utils.OptionExtender, 0, len(algos))
   443  	for _, algo := range algos {
   444  		opt.names = append(opt.names, algo.String())
   445  		opt.extenders = append(opt.extenders, encode.Compress(algo))
   446  	}
   447  	return
   448  }
   449  
   450  func (t *Encode) printableOptions() (opt *option) {
   451  	opt = new(option)
   452  
   453  	algos := []encode.Algorithm{
   454  		encode.AlgorithmHex,
   455  		encode.AlgorithmBase32Std,
   456  		encode.AlgorithmBase32Hex,
   457  		encode.AlgorithmBase64Std,
   458  		encode.AlgorithmBase64URL,
   459  		encode.AlgorithmBase64RawStd,
   460  		encode.AlgorithmBase64RawURL,
   461  	}
   462  
   463  	opt.names = make([]string, 0, len(algos))
   464  	opt.extenders = make([]utils.OptionExtender, 0, len(algos))
   465  	for _, algo := range algos {
   466  		opt.names = append(opt.names, algo.String())
   467  		opt.extenders = append(opt.extenders, encode.Encode(algo))
   468  	}
   469  	return
   470  }
   471  
   472  type option struct {
   473  	names     []string
   474  	extenders []utils.OptionExtender
   475  }
   476  
   477  type optionList []*option
   478  
   479  func (o optionList) combine() (result []*option) {
   480  	type element struct {
   481  		depth   int
   482  		current *option
   483  	}
   484  	queue := []*element{
   485  		{
   486  			depth:   0,
   487  			current: new(option),
   488  		},
   489  	}
   490  	for len(queue) > 0 {
   491  		elem := queue[0]
   492  		queue = queue[1:]
   493  
   494  		if elem.depth == len(o) {
   495  			if len(elem.current.extenders) == len(o) {
   496  				result = append(result, elem.current)
   497  			}
   498  			continue
   499  		}
   500  
   501  		opt := o[elem.depth]
   502  		if len(opt.extenders) == 0 {
   503  			queue = append(queue, &element{
   504  				depth: elem.depth + 1,
   505  				current: &option{
   506  					names:     elem.current.names,
   507  					extenders: elem.current.extenders,
   508  				},
   509  			})
   510  			continue
   511  		}
   512  
   513  		for i := 0; i < len(opt.extenders); i++ {
   514  			newElem := &element{
   515  				depth: elem.depth + 1,
   516  				current: &option{
   517  					names:     append(elem.current.names, opt.names[i]),
   518  					extenders: append(elem.current.extenders, opt.extenders[i]),
   519  				},
   520  			}
   521  			queue = append(queue, newElem)
   522  		}
   523  	}
   524  
   525  	return
   526  }