github.com/GuanceCloud/cliutils@v1.1.21/point/point_test.go (about)

     1  // Unless explicitly stated otherwise all files in this repository are licensed
     2  // under the MIT License.
     3  // This product includes software developed at Guance Cloud (https://www.guance.com/).
     4  // Copyright 2021-present Guance, Inc.
     5  
     6  package point
     7  
     8  import (
     9  	"encoding/json"
    10  	"fmt"
    11  	"math"
    12  	"reflect"
    13  	"sort"
    14  	"strings"
    15  	T "testing"
    16  	"time"
    17  
    18  	"github.com/GuanceCloud/cliutils"
    19  	influxm "github.com/influxdata/influxdb1-client/models"
    20  	"github.com/stretchr/testify/assert"
    21  )
    22  
    23  func TestInfluxFields(t *T.T) {
    24  	t.Run("bytes-array", func(t *T.T) {
    25  		var kvs KVs
    26  		kvs = kvs.Add("f1", MustNewAnyArray([]byte("hello"), []byte("world")), false, false)
    27  		pt := NewPointV2("m1", kvs)
    28  		fields := pt.InfluxFields()
    29  		t.Logf("fields: %+#v", fields)
    30  	})
    31  }
    32  
    33  func TestSizeofPoint(t *T.T) {
    34  	t.Run("small-pt", func(t *T.T) {
    35  		var kvs KVs
    36  		kvs = kvs.Add("f1", 123, false, true)
    37  		kvs = kvs.Add("f2", 3.14, false, true)
    38  		kvs = kvs.MustAddTag("t1", "v1")
    39  		kvs = kvs.MustAddTag("t2", "v2")
    40  
    41  		pbpt := NewPointV2("some", kvs)
    42  		t.Logf("type  size(pbpt): %d", reflect.TypeOf(*pbpt).Size())
    43  		t.Logf("value size(pbpt): %d", pbpt.Size())
    44  	})
    45  
    46  	t.Run("rand-large-pt", func(t *T.T) {
    47  		r := NewRander(WithFixedTags(true), WithRandText(3))
    48  		pts := r.Rand(1)
    49  		t.Logf("type  size(pbpt): %d", reflect.TypeOf(*pts[0]).Size())
    50  		t.Logf("value size(pbpt): %d", pts[0].Size())
    51  	})
    52  }
    53  
    54  func BenchmarkLPPoint(b *T.B) {
    55  	now := time.Now()
    56  	b.Run("pt-lppt", func(b *T.B) {
    57  		fields := map[string]any{
    58  			"f1": 123,
    59  			"f2": 3.14,
    60  		}
    61  		tags := influxm.Tags{
    62  			influxm.Tag{Key: []byte("t1"), Value: []byte("v1")},
    63  			influxm.Tag{Key: []byte("t2"), Value: []byte("v2")},
    64  		}
    65  
    66  		b.ResetTimer()
    67  		for i := 0; i < b.N; i++ {
    68  			influxm.NewPoint("some", tags, fields, now)
    69  		}
    70  	})
    71  
    72  	b.Run("pt-pbpt", func(b *T.B) {
    73  		b.ResetTimer()
    74  		for i := 0; i < b.N; i++ {
    75  			var kvs KVs
    76  			kvs = kvs.Add("f1", 123, false, true)
    77  			kvs = kvs.Add("f2", 3.14, false, true)
    78  			kvs = kvs.MustAddTag("t1", "v1")
    79  			kvs = kvs.MustAddTag("t2", "v2")
    80  
    81  			NewPointV2("some", kvs, WithPrecheck(false), WithTime(now))
    82  		}
    83  	})
    84  
    85  	b.Run("pt-pbpt-with-check", func(b *T.B) {
    86  		b.ResetTimer()
    87  		for i := 0; i < b.N; i++ {
    88  			var kvs KVs
    89  			kvs = kvs.Add("f1", 123, false, true)
    90  			kvs = kvs.Add("f2", 3.14, false, true)
    91  			kvs = kvs.MustAddTag("t1", "v1")
    92  			kvs = kvs.MustAddTag("t2", "v2")
    93  
    94  			NewPointV2("some", kvs, WithTime(now))
    95  		}
    96  	})
    97  }
    98  
    99  func BenchmarkFromModelsLP(b *T.B) {
   100  	r := NewRander(WithFixedTags(true), WithRandText(3))
   101  	pts := r.Rand(1)
   102  
   103  	enc := GetEncoder()
   104  	defer PutEncoder(enc)
   105  
   106  	data, err := enc.Encode(pts)
   107  	assert.NoError(b, err)
   108  	assert.Len(b, data, 1)
   109  
   110  	b.Run("basic", func(b *T.B) {
   111  		lppts, err := influxm.ParsePointsWithPrecision(data[0], time.Now(), "n")
   112  		assert.NoError(b, err)
   113  		assert.Len(b, lppts, 1)
   114  
   115  		b.ResetTimer()
   116  		for i := 0; i < b.N; i++ {
   117  			FromModelsLP(lppts[0])
   118  		}
   119  	})
   120  
   121  	b.Run("with-check", func(b *T.B) {
   122  		lppts, err := influxm.ParsePointsWithPrecision(data[0], time.Now(), "n")
   123  		assert.NoError(b, err)
   124  		assert.Len(b, lppts, 1)
   125  
   126  		c := GetCfg()
   127  		chk := checker{cfg: c}
   128  
   129  		b.ResetTimer()
   130  		for i := 0; i < b.N; i++ {
   131  			pt := FromModelsLP(lppts[0])
   132  
   133  			pt = chk.check(pt)
   134  			pt.pt.Warns = chk.warns
   135  			chk.reset()
   136  
   137  			// re-sort again: check may update pt.kvs
   138  			if c.keySorted {
   139  				kvs := KVs(pt.pt.Fields)
   140  				sort.Sort(kvs)
   141  				pt.pt.Fields = kvs
   142  			}
   143  		}
   144  	})
   145  }
   146  
   147  func TestGet(t *T.T) {
   148  	t.Run(`get-tag`, func(t *T.T) {
   149  		pt := NewPointV2(`abc`, NewKVs(nil).MustAddTag(`t1`, `v1`))
   150  
   151  		assert.Equal(t, `v1`, pt.GetTag(`t1`))
   152  		assert.Equal(t, "", pt.GetTag(`not-exist`))
   153  
   154  		// get non-tag key
   155  		pt.pt.Fields = KVs(pt.pt.Fields).MustAddKV(NewKV(`f1`, 1.23,
   156  			WithKVUnit("bytes"),
   157  			WithKVTagSet(true), // set failed
   158  			WithKVType(COUNT)))
   159  		assert.Equal(t, "", pt.GetTag(`f1`))
   160  
   161  		pt.AddTag(`empty-tag`, ``)
   162  		assert.Equal(t, ``, pt.GetTag(`empty-tag`))
   163  
   164  		t.Logf("kvs:\n%s", KVs(pt.pt.Fields).Pretty())
   165  	})
   166  
   167  	t.Run("get", func(t *T.T) {
   168  		var kvs KVs
   169  
   170  		EnableDictField = true
   171  		EnableMixedArrayField = true
   172  		defer func() {
   173  			EnableDictField = false
   174  			EnableMixedArrayField = false
   175  		}()
   176  
   177  		kvs = kvs.Add("si1", int8(1), false, true)
   178  		kvs = kvs.Add("si2", int16(1), false, true)
   179  		kvs = kvs.Add("si3", int32(1), false, true)
   180  		kvs = kvs.Add("si4", int(1), false, true)
   181  		kvs = kvs.Add("si5", int64(1), false, true)
   182  
   183  		kvs = kvs.Add("ui1", uint8(1), false, true)
   184  		kvs = kvs.Add("ui2", uint16(1), false, true)
   185  		kvs = kvs.Add("ui3", uint32(1), false, true)
   186  		kvs = kvs.Add("ui4", uint(1), false, true)
   187  		kvs = kvs.Add("ui5", uint64(1), false, true)
   188  
   189  		kvs = kvs.Add("b1", false, false, true)
   190  		kvs = kvs.Add("b2", true, false, true)
   191  
   192  		kvs = kvs.Add("d", []byte(`hello`), false, true)
   193  		kvs = kvs.Add("s", `hello`, false, true)
   194  
   195  		kvs = kvs.Add("arr", MustNewAnyArray(1, 2.0, false), false, true)
   196  		kvs = kvs.Add("map", MustNewAny(MustNewMap(map[string]any{"i": 1, "f": 3.14, "s": "world"})), false, true)
   197  
   198  		pt := NewPointV2("get", kvs)
   199  
   200  		t.Logf("pt: %s", pt.Pretty())
   201  
   202  		assert.Equal(t, int64(1), pt.Get("si1"))
   203  		assert.Equal(t, int64(1), pt.Get("si2"))
   204  		assert.Equal(t, int64(1), pt.Get("si3"))
   205  		assert.Equal(t, int64(1), pt.Get("si4"))
   206  		assert.Equal(t, int64(1), pt.Get("si5"))
   207  
   208  		assert.Equal(t, uint64(1), pt.Get("ui1"))
   209  		assert.Equal(t, uint64(1), pt.Get("ui2"))
   210  		assert.Equal(t, uint64(1), pt.Get("ui3"))
   211  		assert.Equal(t, uint64(1), pt.Get("ui4"))
   212  		assert.Equal(t, uint64(1), pt.Get("ui5"))
   213  
   214  		assert.Equal(t, false, pt.Get("b1"))
   215  		assert.Equal(t, true, pt.Get("b2"))
   216  		assert.Equal(t, []byte(`hello`), pt.Get("d"))
   217  		assert.Equal(t, `hello`, pt.Get("s"))
   218  
   219  		assert.Equal(t, []any{int64(1), 2.0, false}, pt.Get("arr"))
   220  		assert.Equal(t, map[string]any{"i": int64(1), "f": 3.14, "s": "world"}, pt.Get("map"))
   221  	})
   222  
   223  	t.Run("get", func(t *T.T) {
   224  		var kvs KVs
   225  
   226  		EnableDictField = true
   227  		EnableMixedArrayField = true
   228  		defer func() {
   229  			EnableDictField = false
   230  			EnableMixedArrayField = false
   231  		}()
   232  
   233  		kvs = kvs.Add("si1", int8(1), false, true)
   234  		kvs = kvs.Add("si2", int16(1), false, true)
   235  		kvs = kvs.Add("si3", int32(1), false, true)
   236  		kvs = kvs.Add("si4", int(1), false, true)
   237  		kvs = kvs.Add("si5", int64(1), false, true)
   238  
   239  		kvs = kvs.Add("ui1", uint8(1), false, true)
   240  		kvs = kvs.Add("ui2", uint16(1), false, true)
   241  		kvs = kvs.Add("ui3", uint32(1), false, true)
   242  		kvs = kvs.Add("ui4", uint(1), false, true)
   243  		kvs = kvs.Add("ui5", uint64(1), false, true)
   244  
   245  		kvs = kvs.Add("b1", false, false, true)
   246  		kvs = kvs.Add("b2", true, false, true)
   247  
   248  		kvs = kvs.Add("d", []byte(`hello`), false, true)
   249  		kvs = kvs.Add("s", `hello`, false, true)
   250  
   251  		kvs = kvs.Add("arr", MustNewAnyArray(1, 2.0, false), false, true)
   252  		kvs = kvs.Add("map", MustNewAny(MustNewMap(map[string]any{"i": 1, "f": 3.14, "s": "world"})), false, true)
   253  
   254  		pt := NewPointV2("get", kvs)
   255  
   256  		t.Logf("pt: %s", pt.Pretty())
   257  
   258  		assert.Equal(t, int64(1), pt.Get("si1"))
   259  		assert.Equal(t, int64(1), pt.Get("si2"))
   260  		assert.Equal(t, int64(1), pt.Get("si3"))
   261  		assert.Equal(t, int64(1), pt.Get("si4"))
   262  		assert.Equal(t, int64(1), pt.Get("si5"))
   263  
   264  		assert.Equal(t, uint64(1), pt.Get("ui1"))
   265  		assert.Equal(t, uint64(1), pt.Get("ui2"))
   266  		assert.Equal(t, uint64(1), pt.Get("ui3"))
   267  		assert.Equal(t, uint64(1), pt.Get("ui4"))
   268  		assert.Equal(t, uint64(1), pt.Get("ui5"))
   269  
   270  		assert.Equal(t, false, pt.Get("b1"))
   271  		assert.Equal(t, true, pt.Get("b2"))
   272  		assert.Equal(t, []byte(`hello`), pt.Get("d"))
   273  		assert.Equal(t, `hello`, pt.Get("s"))
   274  
   275  		assert.Equal(t, []any{int64(1), 2.0, false}, pt.Get("arr"))
   276  		assert.Equal(t, map[string]any{"i": int64(1), "f": 3.14, "s": "world"}, pt.Get("map"))
   277  	})
   278  }
   279  
   280  func TestFlags(t *T.T) {
   281  	t.Run("test-flag-value", func(t *T.T) {
   282  		t.Logf("Psent: %d", Psent)
   283  		t.Logf("Ppb: %d", Ppb)
   284  	})
   285  
   286  	t.Run("test-flag-set-clear", func(t *T.T) {
   287  		pt := &Point{}
   288  		pt.SetFlag(Psent)
   289  
   290  		assert.True(t, pt.HasFlag(Psent))
   291  
   292  		pt.SetFlag(Ppb)
   293  		assert.True(t, pt.HasFlag(Ppb))
   294  
   295  		pt.ClearFlag(Ppb)
   296  		assert.False(t, pt.HasFlag(Ppb))
   297  
   298  		pt.ClearFlag(Psent)
   299  		assert.False(t, pt.HasFlag(Psent))
   300  
   301  		pt.SetFlag(Ppooled)
   302  		assert.True(t, pt.HasFlag(Ppooled))
   303  		pt.ClearFlag(Ppooled)
   304  		assert.False(t, pt.HasFlag(Ppooled))
   305  	})
   306  }
   307  
   308  func TestPrettyPoint(t *T.T) {
   309  	t.Run(`basic`, func(t *T.T) {
   310  		pt := NewPointV2(`abc`, NewKVs(map[string]any{"f1": 123}).AddTag("t1", "v1"))
   311  		t.Logf("%s", pt.Pretty())
   312  	})
   313  
   314  	t.Run(`with-warns`, func(t *T.T) {
   315  		pt := NewPointV2(`abc`, NewKVs(map[string]any{"f1": 123}).
   316  			AddTag("t1", "v1").
   317  			AddTag("t2", "v1"),
   318  			WithDisabledKeys(NewTagKey(`t2`, "")))
   319  
   320  		t.Logf("%s", pt.Pretty())
   321  	})
   322  
   323  	t.Run(`with-all-types`, func(t *T.T) {
   324  		pt := NewPointV2(`abc`, NewKVs(map[string]any{
   325  			"f1": 123,
   326  			"f2": uint64(321),
   327  			"f3": 3.14,
   328  			"f4": false,
   329  			"f5": []byte("hello"),
   330  		}).AddTag("t1", "v1").AddTag("t2", "v1"), WithDisabledKeys(NewTagKey(`t2`, "")))
   331  
   332  		t.Logf("%s", pt.Pretty())
   333  	})
   334  }
   335  
   336  func TestPointString(t *T.T) {
   337  	cases := []struct {
   338  		name   string
   339  		pt     *Point
   340  		expect string
   341  	}{
   342  		{
   343  			name: "normal-lppt",
   344  			pt: func() *Point {
   345  				pt, err := NewPoint("abc",
   346  					map[string]string{"tag1": "v1"},
   347  					map[string]interface{}{
   348  						"f1": 123, "f2": true,
   349  					},
   350  					WithTime(time.Unix(0, 123)))
   351  				assert.NoError(t, err)
   352  				return pt
   353  			}(),
   354  			expect: `abc,tag1=v1 f1=123i,f2=true 123`,
   355  		},
   356  
   357  		{
   358  			name: "normal-pbpt",
   359  			pt: func() *Point {
   360  				pt, err := NewPoint("abc",
   361  					map[string]string{
   362  						"tag1": "v1",
   363  						"tag2": "v2",
   364  						"xtag": "vx",
   365  					}, map[string]interface{}{
   366  						"f1": 123,
   367  						"f2": true,
   368  						"f3": uint64(123),
   369  						"f4": 123.4,
   370  						"f5": "foobar",
   371  						"f6": []byte("hello, 屈原"),
   372  						"f7": struct{ a int }{a: 123},
   373  					},
   374  					WithTime(time.Unix(0, 123)), WithEncoding(Protobuf))
   375  				assert.NoError(t, err)
   376  				return pt
   377  			}(),
   378  		},
   379  	}
   380  
   381  	for _, tc := range cases {
   382  		t.Run(tc.name, func(t *T.T) {
   383  			if tc.pt.HasFlag(Ppb) {
   384  				j, err := json.Marshal(tc.pt) // json protobuf point
   385  				assert.NoError(t, err)
   386  
   387  				var marshalPt Point
   388  				assert.NoError(t, json.Unmarshal(j, &marshalPt))
   389  
   390  				t.Logf("pb.JSON string: %s", j)
   391  
   392  				assert.True(t, tc.pt.Equal(&marshalPt))
   393  
   394  				t.Logf("pb.String: %s", marshalPt.Pretty())
   395  			} else {
   396  				assert.Equal(t, tc.expect, tc.pt.LineProto())
   397  			}
   398  		})
   399  	}
   400  }
   401  
   402  func TestInfluxTags(t *T.T) {
   403  	t.Run("get-tags", func(t *T.T) {
   404  		pt := NewPointV2(`abc`, NewKVs(map[string]any{"f1": 123}).AddTag(`t1`, `v1`))
   405  		tags := pt.InfluxTags()
   406  		assert.Equal(t,
   407  			influxm.Tags{influxm.Tag{Key: []byte("t1"), Value: []byte("v1")}},
   408  			tags)
   409  
   410  		t.Log(pt.Pretty())
   411  	})
   412  
   413  	t.Run("no-tags", func(t *T.T) {
   414  		pt := NewPointV2(`abc`,
   415  			NewKVs(map[string]any{"v1": 123}).
   416  				AddTag(`v1`, `foo`)) // tag key exist, skipped
   417  
   418  		tags := pt.InfluxTags()
   419  		assert.Equal(t, 0, len(tags), "pt: %s", pt.Pretty())
   420  
   421  		t.Log(pt.Pretty())
   422  	})
   423  }
   424  
   425  func TestPointLineProtocol(t *T.T) {
   426  	EnableDictField = true
   427  	EnableMixedArrayField = true
   428  	defer func() {
   429  		EnableDictField = false
   430  		EnableMixedArrayField = false
   431  	}()
   432  
   433  	cases := []struct {
   434  		name string
   435  		pt   *Point
   436  
   437  		prec   Precision
   438  		expect string
   439  	}{
   440  		{
   441  			name: "lp-point-ns-prec",
   442  			prec: PrecNS,
   443  			pt: func() *Point {
   444  				pt, err := NewPoint("abc", nil, map[string]interface{}{"f1": 1},
   445  					append(DefaultLoggingOptions(), WithTime(time.Unix(0, 123)))...)
   446  
   447  				assert.NoError(t, err)
   448  
   449  				t.Logf("pt: %s", pt.Pretty())
   450  				return pt
   451  			}(),
   452  			expect: `abc f1=1i,status="unknown" 123`,
   453  		},
   454  
   455  		{
   456  			name: "lp-point-ms-prec",
   457  			prec: PrecMS,
   458  			pt: func() *Point {
   459  				pt, err := NewPoint("abc", nil, map[string]interface{}{"f1": 1},
   460  					append(DefaultLoggingOptions(), WithTime(time.Unix(0, 12345678)))...)
   461  
   462  				assert.NoError(t, err)
   463  				return pt
   464  			}(),
   465  			expect: `abc f1=1i,status="unknown" 12`,
   466  		},
   467  
   468  		{
   469  			name: "lp-point-us-prec",
   470  			prec: PrecUS, // only accept u
   471  			pt: func() *Point {
   472  				pt, err := NewPoint("abc", nil, map[string]interface{}{"f1": 1},
   473  					append(DefaultLoggingOptions(), WithTime(time.Unix(0, 12345678)))...)
   474  
   475  				assert.NoError(t, err)
   476  				return pt
   477  			}(),
   478  			expect: `abc f1=1i,status="unknown" 12345`,
   479  		},
   480  
   481  		{
   482  			name: "lp-point-ns-prec",
   483  			prec: PrecNS, // only accept u
   484  			pt: func() *Point {
   485  				pt, err := NewPoint("abc", nil, map[string]interface{}{"f1": 1},
   486  					append(DefaultLoggingOptions(), WithTime(time.Unix(0, 12345678)))...)
   487  				assert.NoError(t, err)
   488  				return pt
   489  			}(),
   490  			expect: `abc f1=1i,status="unknown" 12345678`,
   491  		},
   492  
   493  		{
   494  			name: "lp-point-invalid-prec",
   495  			prec: -1,
   496  			pt: func() *Point {
   497  				pt, err := NewPoint("abc", nil, map[string]interface{}{"f1": 1},
   498  					append(DefaultLoggingOptions(), WithTime(time.Unix(0, 12345678)))...)
   499  				assert.NoError(t, err)
   500  				return pt
   501  			}(),
   502  			expect: `abc f1=1i,status="unknown" 12345678`,
   503  		},
   504  
   505  		{
   506  			name: "lp-point-second-prec",
   507  			prec: PrecS,
   508  			pt: func() *Point {
   509  				pt, err := NewPoint("abc", nil, map[string]interface{}{"f1": 1},
   510  					append(DefaultLoggingOptions(), WithTime(time.Unix(1, 123456789)))...)
   511  				assert.NoError(t, err)
   512  				return pt
   513  			}(),
   514  			expect: `abc f1=1i,status="unknown" 1`,
   515  		},
   516  
   517  		{
   518  			name: "lp-point-minute-prec",
   519  			prec: PrecM,
   520  			pt: func() *Point {
   521  				pt, err := NewPoint("abc", nil, map[string]interface{}{"f1": 1},
   522  					append(DefaultLoggingOptions(), WithTime(time.Unix(120, 123456789)))...)
   523  				assert.NoError(t, err)
   524  				return pt
   525  			}(),
   526  			expect: `abc f1=1i,status="unknown" 2`,
   527  		},
   528  
   529  		{
   530  			name: "lp-point-hour-prec",
   531  			prec: PrecH,
   532  			pt: func() *Point {
   533  				pt, err := NewPoint("abc", nil, map[string]interface{}{"f1": 1},
   534  					append(DefaultLoggingOptions(), WithTime(time.Unix(7199, 123456789)))...)
   535  				assert.NoError(t, err)
   536  				return pt
   537  			}(),
   538  			expect: `abc f1=1i,status="unknown" 1`, // 7199 not reached 2hour
   539  		},
   540  
   541  		// pb point
   542  		{
   543  			name: "pb-point",
   544  			prec: PrecNS,
   545  			pt: func() *Point {
   546  				pt, err := NewPoint("abc",
   547  					nil,
   548  					map[string]interface{}{"f1": int64(1)},
   549  					WithTime(time.Unix(0, 123)), WithEncoding(Protobuf))
   550  
   551  				assert.NoError(t, err)
   552  
   553  				t.Logf("pb point: %s", pt.Pretty())
   554  				return pt
   555  			}(),
   556  			expect: `abc f1=1i 123`,
   557  		},
   558  
   559  		{
   560  			name: "pb-point-with-binary-data",
   561  			prec: PrecNS,
   562  			pt: func() *Point {
   563  				pt, err := NewPoint("abc",
   564  					map[string]string{"t1": "v1"},
   565  					map[string]interface{}{"f1": []byte("abc123")},
   566  					WithTime(time.Unix(0, 1)), WithEncoding(Protobuf))
   567  
   568  				assert.NoError(t, err)
   569  
   570  				t.Logf("pt: %s", pt.Pretty())
   571  
   572  				return pt
   573  			}(),
   574  			expect: `abc,t1=v1 f1="YWJjMTIz"b 1`,
   575  		},
   576  
   577  		{
   578  			name: `string-field-with-newline`,
   579  			prec: PrecNS,
   580  			pt: NewPointV2(`abc`, append(NewTags(map[string]string{"tag1": "v1"}),
   581  				NewKVs(map[string]any{"f1": `message
   582  with
   583  new
   584  line`})...), WithTime(time.Unix(0, 123))),
   585  			expect: `abc,tag1=v1 f1="message
   586  with
   587  new
   588  line" 123`,
   589  		},
   590  
   591  		{
   592  			name: "lp-point-with-array-field",
   593  			prec: PrecNS,
   594  			pt: func() *Point {
   595  				var kvs KVs
   596  				kvs = kvs.Add("arr",
   597  					MustNewAnyArray(1, 3.14, 1.414, "hello"),
   598  					false, true)
   599  				pt := NewPointV2("abc", kvs, WithTime(time.Unix(0, 123)))
   600  
   601  				return pt
   602  			}(),
   603  			expect: `abc arr=[1i,3.14,1.414,"hello"] 123`,
   604  		},
   605  	}
   606  
   607  	for _, tc := range cases {
   608  		t.Run(tc.name, func(t *T.T) {
   609  			t.Logf("pt: %s", tc.pt.Pretty())
   610  
   611  			assert.Equal(t, tc.expect, tc.pt.LineProto(tc.prec))
   612  
   613  			// _, err := influxm.ParsePointsWithPrecision([]byte(tc.expect), time.Now(), "n")
   614  		})
   615  	}
   616  }
   617  
   618  func TestPBJSON(t *T.T) {
   619  	t.Run("pbjson", func(t *T.T) {
   620  		EnableDictField = true
   621  		EnableMixedArrayField = true
   622  		defer func() {
   623  			EnableDictField = false
   624  			EnableMixedArrayField = false
   625  		}()
   626  
   627  		pt := NewPointV2(`abc`, NewKVs(map[string]any{
   628  			"i":         1234567890,
   629  			"u":         uint64(1234567890),
   630  			"f":         3.14,
   631  			"s":         "hello",
   632  			"bool":      false,
   633  			"d":         []byte("hello world"),
   634  			"int-arr":   MustNewIntArray([]int{1, 2, 3}...),
   635  			"mixed-arr": MustNewAnyArray(1, 2.0, "hello", false),
   636  		}))
   637  
   638  		kvs := KVs(pt.pt.Fields)
   639  		kvs = kvs.MustAddTag(`t1`, `v1`).
   640  			MustAddKV(NewKV(`f2`, 3.14, WithKVUnit("kb"), WithKVType(COUNT)))
   641  		pt.pt.Fields = kvs
   642  
   643  		j, _ := pt.PBJson()
   644  		t.Logf("raw: %s", string(j))
   645  
   646  		jpt := MustFromPBJson(j)
   647  
   648  		assert.True(t, pt.Equal(jpt))
   649  
   650  		j, _ = pt.PBJsonPretty()
   651  		t.Logf("pretty:\n%s", string(j))
   652  
   653  		jpt = MustFromPBJson(j)
   654  		assert.True(t, pt.Equal(jpt))
   655  	})
   656  }
   657  
   658  func TestPointPB(t *T.T) {
   659  	t.Run(`valid-types`, func(t *T.T) {
   660  		pt := NewPointV2(`abc`, NewKVs(map[string]any{
   661  			"f1":  uint64(123),
   662  			"f2":  uint64(math.MaxUint64),
   663  			"f3":  int64(123),
   664  			"f4":  false,
   665  			"f5":  true,
   666  			"f6":  "hello",
   667  			"f7":  []byte("world"),
   668  			"f8":  struct{}{}, // user-defined
   669  			"f9":  nil,
   670  			"f10": 3.14,
   671  		}), WithTime(time.Unix(0, 123)), WithKeySorted(true))
   672  
   673  		kvs := KVs(pt.pt.Fields)
   674  		sort.Sort(kvs)
   675  		pt.pt.Fields = kvs
   676  
   677  		j := fmt.Sprintf(`{
   678  	"name": "%s",
   679  	"fields": [
   680  		{ "key": "%s", "u": "123" },
   681  		{ "key": "%s", "u": "%d" },
   682  		{ "key": "%s", "i": "123" },
   683  		{ "key": "%s", "b": false },
   684  		{ "key": "%s", "b": true },
   685  		{ "key": "%s", "s": "%s" },
   686  		{ "key": "%s", "d": "%s" },
   687  		{ "key": "%s" },
   688  		{ "key": "%s" },
   689  		{ "key": "%s", "f": "%f" }
   690  	], "time":"123"}`,
   691  			`abc`,
   692  			`f1`,
   693  			`f2`, uint64(math.MaxUint64),
   694  			`f3`,
   695  			`f4`,
   696  			`f5`,
   697  			`f6`, `hello`,
   698  			`f7`, b64([]byte(`world`)),
   699  			`f8`,
   700  			`f9`,
   701  			`f10`, float64(3.14))
   702  
   703  		expect := MustFromPBJson([]byte(j))
   704  		kvs = KVs(expect.pt.Fields)
   705  		sort.Sort(kvs)
   706  		expect.pt.Fields = kvs
   707  
   708  		cfg := GetCfg()
   709  		defer PutCfg(cfg)
   710  		chk := checker{cfg: cfg}
   711  		expect = chk.check(expect)
   712  		expect.SetFlag(Pcheck)
   713  		expect.pt.Warns = chk.warns
   714  
   715  		assert.Equal(t, expect.Pretty(), pt.Pretty(), "got\n%s\nexpect\n%s", expect.Pretty(), pt.Pretty())
   716  
   717  		t.Logf("pt: %s", pt.Pretty())
   718  	})
   719  }
   720  
   721  func TestLPPoint(t *T.T) {
   722  	t.Run(`uint`, func(t *T.T) {
   723  		pt := NewPointV2(`abc`, NewKVs(map[string]any{"f1": uint64(123)}), WithTime(time.Unix(0, 123)))
   724  		assert.Equal(t, `abc f1=123u 123`, pt.MustLPPoint().String())
   725  
   726  		// max-int64 is ok
   727  		pt = NewPointV2(`abc`, NewKVs(map[string]any{"f1": uint64(math.MaxInt64)}), WithTime(time.Unix(0, 123)))
   728  		assert.Equal(t, fmt.Sprintf(`abc f1=%du 123`, math.MaxInt64), pt.MustLPPoint().String())
   729  
   730  		// max-int64 + 1 not ok
   731  		pt = NewPointV2(`abc`, NewKVs(map[string]any{
   732  			"f1": uint64(math.MaxInt64 + 1),
   733  			"f2": "foo",
   734  		}), WithTime(time.Unix(0, 123)))
   735  		assert.Equal(t, `abc f1=9223372036854775808u,f2="foo" 123`, pt.MustLPPoint().String())
   736  
   737  		t.Logf("lp: %s", pt.MustLPPoint().String())
   738  	})
   739  
   740  	t.Run(`nil`, func(t *T.T) {
   741  		// max-int64 + 1 not ok
   742  		pt := NewPointV2(`abc`, NewKVs(map[string]any{"f1": 123, "f2": nil}), WithTime(time.Unix(0, 123)))
   743  		assert.Equal(t, `abc f1=123i 123`, pt.MustLPPoint().String())
   744  
   745  		t.Logf("lp: %s", pt.MustLPPoint().String())
   746  	})
   747  
   748  	t.Run(`struct`, func(t *T.T) {
   749  		// max-int64 + 1 not ok
   750  		pt := NewPointV2(`abc`, NewKVs(map[string]any{"f1": 123, "f2": struct{}{}}), WithTime(time.Unix(0, 123)))
   751  		assert.Equal(t, `abc f1=123i 123`, pt.MustLPPoint().String())
   752  
   753  		t.Logf("lp: %s", pt.MustLPPoint().String())
   754  	})
   755  }
   756  
   757  func TestFields(t *T.T) {
   758  	cases := []struct {
   759  		name   string
   760  		pt     *Point
   761  		expect map[string]interface{}
   762  	}{
   763  		{
   764  			name: "basic-lp-point",
   765  
   766  			pt: func() *Point {
   767  				x, err := NewPoint("abc", nil,
   768  					map[string]interface{}{
   769  						"i8":     int8(1),
   770  						"u8":     uint8(1),
   771  						"i16":    int16(1),
   772  						"u16":    uint16(1),
   773  						"i32":    int32(1),
   774  						"u32":    uint32(1),
   775  						"i64":    int64(1),
   776  						"u64":    uint64(1),
   777  						"f32":    float32(1.0),
   778  						"f64":    float64(1.0),
   779  						"bool_1": false,
   780  						"bool_2": true,
   781  						"str":    "hello",
   782  						"data":   []byte("abc123"),
   783  						"nil":    nil,
   784  						"udf":    struct{}{},
   785  					})
   786  				assert.NoError(t, err)
   787  				return x
   788  			}(),
   789  
   790  			expect: map[string]interface{}{
   791  				"i8":     int64(1),
   792  				"u8":     uint64(1),
   793  				"i16":    int64(1),
   794  				"u16":    uint64(1),
   795  				"i32":    int64(1),
   796  				"u32":    uint64(1),
   797  				"i64":    int64(1),
   798  				"u64":    uint64(1),
   799  				"f32":    float32(1.0),
   800  				"f64":    float64(1.0),
   801  				"bool_1": false,
   802  				"nil":    nil,
   803  				"udf":    nil,
   804  				"data":   []byte("abc123"),
   805  				"bool_2": true,
   806  				"str":    "hello",
   807  			},
   808  		},
   809  
   810  		{
   811  			name: "basic-pb-point",
   812  
   813  			pt: func() *Point {
   814  				x, err := NewPoint("abc", nil,
   815  					map[string]interface{}{
   816  						"bool_1": false,
   817  						"bool_2": true,
   818  						"data":   []byte("abc123"),
   819  						"f32":    float32(1.0),
   820  						"f64":    float64(1.0),
   821  						"i16":    int16(1),
   822  						"i32":    int32(1),
   823  						"i64":    int64(1),
   824  						"i8":     int8(1),
   825  						"nil":    nil,
   826  						"str":    "hello",
   827  						"u16":    uint16(1),
   828  						"u32":    uint32(1),
   829  						"u64":    uint64(1),
   830  						"u8":     uint8(1),
   831  						"udf":    struct{}{},
   832  					}, WithEncoding(Protobuf))
   833  				assert.NoError(t, err)
   834  				return x
   835  			}(),
   836  
   837  			expect: map[string]interface{}{
   838  				// "any":    someAny,
   839  				"bool_1": false,
   840  				"bool_2": true,
   841  				"data":   []byte("abc123"),
   842  				"f32":    float64(1.0),
   843  				"f64":    float64(1.0),
   844  				"i16":    int64(1),
   845  				"i32":    int64(1),
   846  				"i64":    int64(1),
   847  				"i8":     int64(1),
   848  				"str":    "hello",
   849  				"u16":    uint64(1),
   850  				"u32":    uint64(1),
   851  				"u64":    uint64(1),
   852  				"u8":     uint64(1),
   853  				"nil":    nil,
   854  				"udf":    nil,
   855  			},
   856  		},
   857  	}
   858  
   859  	eopt := eqopt{}
   860  	for _, tc := range cases {
   861  		t.Run(tc.name, func(t *T.T) {
   862  			fs := tc.pt.Fields()
   863  			assert.True(t, len(fs) > 0)
   864  
   865  			eq, reason := eopt.kvsEq(fs, NewKVs(tc.expect))
   866  			assert.True(t, eq, "not equal, reason: %s, pt: %s", reason, tc.pt.Pretty())
   867  
   868  			assert.NotNil(t, tc.pt.PBPoint())
   869  			assert.NotNil(t, tc.pt.MustLPPoint())
   870  
   871  			eq, reason = eopt.kvsEq(fs, NewKVs(tc.expect))
   872  			assert.True(t, eq, "not equal, reason: %s, pt: %s", reason, KVs(tc.pt.pt.Fields).Pretty())
   873  		})
   874  	}
   875  }
   876  
   877  func FuzzPBPointString(f *T.F) {
   878  	cases := []struct {
   879  		measurement string
   880  		tagk        string
   881  		tagv        string
   882  		fieldk      string
   883  
   884  		i64  int64
   885  		u64  uint64
   886  		str  string
   887  		b    bool
   888  		f    float64
   889  		d    []byte
   890  		time int64
   891  	}{
   892  		{
   893  			measurement: "fuzz",
   894  			tagk:        "tag",
   895  			tagv:        "tval",
   896  			fieldk:      "field",
   897  
   898  			i64:  int64(1),
   899  			u64:  uint64(123),
   900  			str:  "hello world",
   901  			b:    false,
   902  			f:    3.14,
   903  			d:    []byte("hello, world"),
   904  			time: 123,
   905  		},
   906  	}
   907  
   908  	for _, tc := range cases {
   909  		f.Add(
   910  			tc.measurement, tc.tagk, tc.tagv, tc.fieldk,
   911  			tc.i64, tc.u64, tc.str, tc.b, tc.f, tc.d, tc.time)
   912  	}
   913  
   914  	f.Fuzz(func(t *T.T,
   915  		measurement string,
   916  		tagk string,
   917  		tagv string,
   918  		fieldk string,
   919  
   920  		i64 int64,
   921  		u64 uint64,
   922  		str string,
   923  		b bool,
   924  		f float64,
   925  		d []byte,
   926  		ts int64,
   927  	) {
   928  		pt, err := NewPoint(measurement,
   929  			map[string]string{tagk: tagv},
   930  			map[string]interface{}{
   931  				"i64": i64,
   932  				"u64": u64,
   933  				"str": str,
   934  				"b":   b,
   935  			}, WithTime(time.Unix(0, 123)), WithDotInKey(true), WithEncoding(Protobuf))
   936  
   937  		assert.NoError(t, err)
   938  		if pt != nil {
   939  			t.Logf(pt.Pretty())
   940  		}
   941  	})
   942  }
   943  
   944  func TestKey(t *T.T) {
   945  	cases := []struct {
   946  		name, key string
   947  		pt        *Point
   948  		expect    any
   949  	}{
   950  		{
   951  			"basic",
   952  			`f1`,
   953  			NewPointV2("abc", NewKVs(map[string]any{"f1": 123})),
   954  			int64(123),
   955  		},
   956  
   957  		{
   958  			"query-tag-no-field",
   959  			`t1`,
   960  			NewPointV2("abc", nil, WithExtraTags(map[string]string{"t1": "v1"})),
   961  			"v1",
   962  		},
   963  
   964  		{
   965  			"no-field-query-field-not-found",
   966  			`f1`,
   967  			NewPointV2("abc", nil, nil),
   968  			nil,
   969  		},
   970  
   971  		{
   972  			"query-field-not-found",
   973  			`f1`,
   974  			NewPointV2("abc", NewKVs(map[string]any{"f2": 123})),
   975  			nil,
   976  		},
   977  
   978  		{
   979  			"query-f32",
   980  			`f1`,
   981  			NewPointV2("abc", NewKVs(map[string]any{"f1": float32(3.0)})),
   982  			float64(3.0),
   983  		},
   984  
   985  		{
   986  			"query-f64",
   987  			`f1`,
   988  			NewPointV2("abc", NewKVs(map[string]any{"f1": float64(3.14)})),
   989  			float64(3.14),
   990  		},
   991  
   992  		{
   993  			"query-u64",
   994  			`f1`,
   995  			NewPointV2("abc", NewKVs(map[string]any{"f1": uint64(3)})),
   996  			uint64(3),
   997  		},
   998  
   999  		{
  1000  			"query-data",
  1001  			`f1`,
  1002  			NewPointV2("abc", NewKVs(map[string]any{"f1": []byte("hello")}), WithEncoding(Protobuf)),
  1003  			[]byte("hello"),
  1004  		},
  1005  
  1006  		{
  1007  			"query-bool",
  1008  			`f1`,
  1009  			NewPointV2("abc", NewKVs(map[string]any{"f1": false})),
  1010  			false,
  1011  		},
  1012  	}
  1013  
  1014  	for _, tc := range cases {
  1015  		t.Run(tc.name, func(t *T.T) {
  1016  			t.Logf("%s", tc.pt.Pretty())
  1017  			assert.Equal(t, tc.expect, tc.pt.Get(tc.key))
  1018  		})
  1019  	}
  1020  }
  1021  
  1022  func TestPointKeys(t *T.T) {
  1023  	t.Run("point-keys", func(t *T.T) {
  1024  		p := NewPointV2("abc",
  1025  			NewKVs(map[string]any{"f1": "123", "f2": false, "f3": float32(3.14)}),
  1026  			WithExtraTags(map[string]string{"t1": "t2"}))
  1027  		keys := p.Keys()
  1028  
  1029  		t.Logf("keys:\n%s", keys.Pretty())
  1030  
  1031  		hash1 := keys.Hash()
  1032  
  1033  		keys.Add(NewKey(`hello`, D))
  1034  
  1035  		hash2 := keys.Hash()
  1036  		assert.NotEqual(t, hash1, hash2, "keys:\n%s", keys.Pretty())
  1037  
  1038  		keys.Del(NewKey(`hello`, D))
  1039  
  1040  		hash3 := keys.Hash()
  1041  		assert.Equal(t, hash1, hash3, "keys: \n%s", keys.Pretty())
  1042  
  1043  		keys.Del(NewKey(`t1`, D))
  1044  
  1045  		hash4 := keys.Hash()
  1046  		assert.NotEqual(t, hash3, hash4, "keys: \n%s", keys.Pretty())
  1047  
  1048  		t.Logf("keys:\n%s", keys.Pretty())
  1049  	})
  1050  
  1051  	t.Run("exist", func(t *T.T) {
  1052  		p := NewPointV2("abc", NewKVs(map[string]any{"x1": "123"}))
  1053  		keys := p.Keys()
  1054  
  1055  		assert.True(t, keys.Has(NewKey(`x1`, D)), "keys:\n%s", keys.Pretty())
  1056  	})
  1057  
  1058  	t.Run("add", func(t *T.T) {
  1059  		p := NewPointV2("abc", NewKVs(map[string]any{"f1": "123"}))
  1060  		keys := p.Keys()
  1061  
  1062  		h1 := keys.Hash()
  1063  
  1064  		// add exist key
  1065  		keys.Add(NewKey(`f1`, D))
  1066  
  1067  		h2 := keys.Hash()
  1068  		assert.Equal(t, h1, h2, "keys:\n%s", keys.Pretty())
  1069  	})
  1070  
  1071  	t.Run("no-kvs", func(t *T.T) {
  1072  		p := NewPointV2("abc", nil)
  1073  		keys := p.Keys()
  1074  
  1075  		t.Logf("keys:\n%s", keys.Pretty())
  1076  
  1077  		hash1 := keys.Hash()
  1078  
  1079  		keys.Add(NewKey("hello", D))
  1080  
  1081  		hash2 := keys.Hash()
  1082  		assert.NotEqual(t, hash1, hash2, "keys:\n%s", keys.Pretty())
  1083  
  1084  		keys.Del(NewKey("hello", D))
  1085  
  1086  		hash3 := keys.Hash()
  1087  		assert.Equal(t, hash1, hash3, "keys: \n%s", keys.Pretty())
  1088  
  1089  		// delete not-exist-key
  1090  		keys.Del(NewKey("t1", D))
  1091  		hash4 := keys.Hash()
  1092  		assert.Equal(t, hash3, hash4, "keys: \n%s", keys.Pretty())
  1093  		assert.True(t, keys.hashed)
  1094  
  1095  		t.Logf("keys:\n%s", keys.Pretty())
  1096  	})
  1097  }
  1098  
  1099  func TestPointAddKey(t *T.T) {
  1100  	t.Run("add", func(t *T.T) {
  1101  		pt := NewPointV2("abc", NewKVs(map[string]any{"f1": 123}))
  1102  		pt.Add("new-key", "hello")
  1103  		kvs := KVs(pt.pt.Fields)
  1104  		assert.True(t, kvs.Has(`new-key`), "fields: %s", kvs.Pretty())
  1105  	})
  1106  }
  1107  
  1108  func BenchmarkPointSize(b *T.B) {
  1109  	b.Run(`basic-pt-size`, func(b *T.B) {
  1110  		r := NewRander(WithRandText(3))
  1111  		pts := r.Rand(1)
  1112  
  1113  		for i := 0; i < b.N; i++ {
  1114  			pts[0].Size()
  1115  		}
  1116  	})
  1117  
  1118  	b.Run(`basic-pb-size`, func(b *T.B) {
  1119  		r := NewRander(WithRandText(3))
  1120  		pts := r.Rand(1)
  1121  
  1122  		for i := 0; i < b.N; i++ {
  1123  			pts[0].PBSize()
  1124  		}
  1125  	})
  1126  }
  1127  
  1128  func TestPayloadSize(t *T.T) {
  1129  	t.Run("sizes", func(t *T.T) {
  1130  		// empty point
  1131  		pt := NewPointV2(`abc`, nil)
  1132  		t.Logf("pt size: %d, pb size: %d, lp size: %d", pt.Size(), pt.PBSize(), pt.LPSize())
  1133  
  1134  		// basic point
  1135  		pt = NewPointV2(`abc`, NewKVs(map[string]any{
  1136  			"f1": 123,
  1137  			"f2": uint64(123),
  1138  			"f3": false,
  1139  			"f4": 3.14,
  1140  			"f5": []byte(`hello`),
  1141  		}))
  1142  		t.Logf("pt size: %d, pb size: %d, lp size: %d", pt.Size(), pt.PBSize(), pt.LPSize())
  1143  
  1144  		// large numbers
  1145  		pt = NewPointV2(`abc`, NewKVs(map[string]any{
  1146  			"f1": math.MaxInt64,
  1147  			"f3": false,
  1148  			"f5": []byte(strings.Repeat(`hello`, 100)),
  1149  			"f4": float64(math.MaxFloat64),
  1150  			"f6": float32(math.MaxFloat32),
  1151  			"f7": 3.14,
  1152  			"f9": 3.14159265359,
  1153  		}))
  1154  		t.Logf("pt size: %d, pb size: %d, lp size: %d", pt.Size(), pt.PBSize(), pt.LPSize())
  1155  
  1156  		// with kv unit/type
  1157  		pt = NewPointV2(`abc`, NewKVs(nil).
  1158  			MustAddKV(NewKV(`f1`, 123, WithKVUnit("MB"), WithKVType(COUNT))).
  1159  			MustAddTag(`t1`, `v1`))
  1160  		t.Logf("pt size: %d, pb size: %d, lp size: %d", pt.Size(), pt.PBSize(), pt.LPSize())
  1161  
  1162  		// rand point
  1163  		r := NewRander(WithRandText(3))
  1164  		pts := r.Rand(10)
  1165  		for idx, pt := range pts {
  1166  			t.Logf("[%d] pt size: % 5d, pb size: % 5d, lp size: % 5d", idx, pt.Size(), pt.PBSize(), pt.LPSize())
  1167  		}
  1168  	})
  1169  
  1170  	t.Run("size-of-anypb", func(t *T.T) {
  1171  		pt := NewPointV2("some", nil)
  1172  
  1173  		pt.MustAdd("arr", []string{
  1174  			cliutils.CreateRandomString(100),
  1175  			cliutils.CreateRandomString(100),
  1176  		})
  1177  		t.Logf("s-arr pt size: %d, pbsize: %d\npt: %s", pt.Size(), pt.PBSize(), pt.Pretty())
  1178  
  1179  		pt = NewPointV2("some", nil)
  1180  		pt.MustAdd("arr", []int{(1), (1)})
  1181  		t.Logf("i-arr pt size: %d, pbsize: %d\npt: %s", pt.Size(), pt.PBSize(), pt.Pretty())
  1182  
  1183  		pt = NewPointV2("some", nil)
  1184  		pt.MustAdd("arr", []bool{false, true})
  1185  		t.Logf("b-arr pt size: %d, pbsize: %d\npt: %s", pt.Size(), pt.PBSize(), pt.Pretty())
  1186  	})
  1187  
  1188  	t.Run("size-of-large-string-point", func(t *T.T) {
  1189  		pt := NewPointV2("some", nil)
  1190  
  1191  		_32mstr := cliutils.CreateRandomString(1024 * 1024 * 32)
  1192  		pt.MustAdd("large-string", _32mstr)
  1193  
  1194  		enc := GetEncoder(WithEncEncoding(Protobuf))
  1195  		defer PutEncoder(enc)
  1196  
  1197  		datas, err := enc.Encode([]*Point{pt})
  1198  		assert.NoError(t, err)
  1199  
  1200  		gz := cliutils.MustGZip(datas[0])
  1201  		gz32mb := cliutils.MustGZip([]byte(_32mstr))
  1202  
  1203  		t.Logf("pt size: %d, pbsize: %d/gz: %d, gzraw: %d", pt.Size(), pt.PBSize(), len(gz), len(gz32mb))
  1204  	})
  1205  }