github.com/hairyhenderson/gomplate/v4@v4.0.0-pre-2.0.20240520121557-362f058f0c93/coll/jq_test.go (about)

     1  package coll
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"testing"
     7  
     8  	"github.com/stretchr/testify/assert"
     9  	"github.com/stretchr/testify/require"
    10  )
    11  
    12  func TestJQ(t *testing.T) {
    13  	ctx := context.Background()
    14  	in := map[string]interface{}{
    15  		"store": map[string]interface{}{
    16  			"book": []interface{}{
    17  				map[string]interface{}{
    18  					"category": "reference",
    19  					"author":   "Nigel Rees",
    20  					"title":    "Sayings of the Century",
    21  					"price":    8.95,
    22  				},
    23  				map[string]interface{}{
    24  					"category": "fiction",
    25  					"author":   "Evelyn Waugh",
    26  					"title":    "Sword of Honour",
    27  					"price":    12.99,
    28  				},
    29  				map[string]interface{}{
    30  					"category": "fiction",
    31  					"author":   "Herman Melville",
    32  					"title":    "Moby Dick",
    33  					"isbn":     "0-553-21311-3",
    34  					"price":    8.99,
    35  				},
    36  				map[string]interface{}{
    37  					"category": "fiction",
    38  					"author":   "J. R. R. Tolkien",
    39  					"title":    "The Lord of the Rings",
    40  					"isbn":     "0-395-19395-8",
    41  					"price":    22.99,
    42  				},
    43  			},
    44  			"bicycle": map[string]interface{}{
    45  				"color": "red",
    46  				"price": 19.95,
    47  			},
    48  		},
    49  	}
    50  	out, err := JQ(ctx, ".store.bicycle.color", in)
    51  	require.NoError(t, err)
    52  	assert.Equal(t, "red", out)
    53  
    54  	out, err = JQ(ctx, ".store.bicycle.price", in)
    55  	require.NoError(t, err)
    56  	assert.InEpsilon(t, 19.95, out, 1e-12)
    57  
    58  	out, err = JQ(ctx, ".store.bogus", in)
    59  	require.NoError(t, err)
    60  	assert.Nil(t, out)
    61  
    62  	_, err = JQ(ctx, "{.store.unclosed", in)
    63  	require.Error(t, err)
    64  
    65  	out, err = JQ(ctx, ".store", in)
    66  	require.NoError(t, err)
    67  	assert.EqualValues(t, in["store"], out)
    68  
    69  	out, err = JQ(ctx, ".store.book[].author", in)
    70  	require.NoError(t, err)
    71  	assert.Len(t, out, 4)
    72  	assert.Contains(t, out, "Nigel Rees")
    73  	assert.Contains(t, out, "Evelyn Waugh")
    74  	assert.Contains(t, out, "Herman Melville")
    75  	assert.Contains(t, out, "J. R. R. Tolkien")
    76  
    77  	out, err = JQ(ctx, ".store.book[]|select(.price < 10.0 )", in)
    78  	require.NoError(t, err)
    79  	expected := []interface{}{
    80  		map[string]interface{}{
    81  			"category": "reference",
    82  			"author":   "Nigel Rees",
    83  			"title":    "Sayings of the Century",
    84  			"price":    8.95,
    85  		},
    86  		map[string]interface{}{
    87  			"category": "fiction",
    88  			"author":   "Herman Melville",
    89  			"title":    "Moby Dick",
    90  			"isbn":     "0-553-21311-3",
    91  			"price":    8.99,
    92  		},
    93  	}
    94  	assert.EqualValues(t, expected, out)
    95  
    96  	in = map[string]interface{}{
    97  		"a": map[string]interface{}{
    98  			"aa": map[string]interface{}{
    99  				"foo": map[string]interface{}{
   100  					"aaa": map[string]interface{}{
   101  						"aaaa": map[string]interface{}{
   102  							"bar": 1234,
   103  						},
   104  					},
   105  				},
   106  			},
   107  			"ab": map[string]interface{}{
   108  				"aba": map[string]interface{}{
   109  					"foo": map[string]interface{}{
   110  						"abaa": true,
   111  						"abab": "baz",
   112  					},
   113  				},
   114  			},
   115  		},
   116  	}
   117  	out, err = JQ(ctx, `tostream|select((.[0]|index("foo")) and (.[0][-1]!="foo") and (.[1])) as $s|($s[0]|index("foo")+1) as $ind|($ind|truncate_stream($s)) as $newstream|$newstream|reduce . as [$p,$v] ({};setpath($p;$v))|add`, in)
   118  	require.NoError(t, err)
   119  	assert.Len(t, out, 3)
   120  	assert.Contains(t, out, map[string]interface{}{"aaaa": map[string]interface{}{"bar": 1234}})
   121  	assert.Contains(t, out, true)
   122  	assert.Contains(t, out, "baz")
   123  }
   124  
   125  func TestJQ_typeConversions(t *testing.T) {
   126  	ctx := context.Background()
   127  
   128  	type bicycleType struct {
   129  		Color string
   130  	}
   131  	type storeType struct {
   132  		Bicycle *bicycleType
   133  		safe    interface{}
   134  	}
   135  
   136  	structIn := &storeType{
   137  		Bicycle: &bicycleType{
   138  			Color: "red",
   139  		},
   140  		safe: "hidden",
   141  	}
   142  
   143  	out, err := JQ(ctx, ".Bicycle.Color", structIn)
   144  	require.NoError(t, err)
   145  	assert.Equal(t, "red", out)
   146  
   147  	out, err = JQ(ctx, ".safe", structIn)
   148  	require.NoError(t, err)
   149  	assert.Nil(t, out)
   150  
   151  	_, err = JQ(ctx, ".*", structIn)
   152  	require.Error(t, err)
   153  
   154  	// a type with an underlying type of map[string]interface{}, just like
   155  	// gomplate.tmplctx
   156  	type mapType map[string]interface{}
   157  
   158  	out, err = JQ(ctx, ".foo", mapType{"foo": "bar"})
   159  	require.NoError(t, err)
   160  	assert.Equal(t, "bar", out)
   161  
   162  	// sometimes it'll be a pointer...
   163  	out, err = JQ(ctx, ".foo", &mapType{"foo": "bar"})
   164  	require.NoError(t, err)
   165  	assert.Equal(t, "bar", out)
   166  
   167  	// underlying slice type
   168  	type sliceType []interface{}
   169  
   170  	out, err = JQ(ctx, ".[1]", sliceType{"foo", "bar"})
   171  	require.NoError(t, err)
   172  	assert.Equal(t, "bar", out)
   173  
   174  	out, err = JQ(ctx, ".[2]", &sliceType{"foo", "bar", "baz"})
   175  	require.NoError(t, err)
   176  	assert.Equal(t, "baz", out)
   177  
   178  	// other basic types
   179  	out, err = JQ(ctx, ".", []byte("hello"))
   180  	require.NoError(t, err)
   181  	assert.EqualValues(t, "hello", out)
   182  
   183  	out, err = JQ(ctx, ".", "hello")
   184  	require.NoError(t, err)
   185  	assert.EqualValues(t, "hello", out)
   186  
   187  	out, err = JQ(ctx, ".", 1234)
   188  	require.NoError(t, err)
   189  	assert.EqualValues(t, 1234, out)
   190  
   191  	out, err = JQ(ctx, ".", true)
   192  	require.NoError(t, err)
   193  	assert.EqualValues(t, true, out)
   194  
   195  	out, err = JQ(ctx, ".", nil)
   196  	require.NoError(t, err)
   197  	assert.Nil(t, out)
   198  
   199  	// underlying basic types
   200  	type intType int
   201  	out, err = JQ(ctx, ".", intType(1234))
   202  	require.NoError(t, err)
   203  	assert.EqualValues(t, 1234, out)
   204  
   205  	type byteArrayType []byte
   206  	out, err = JQ(ctx, ".", byteArrayType("hello"))
   207  	require.NoError(t, err)
   208  	assert.EqualValues(t, "hello", out)
   209  }
   210  
   211  func TestJQConvertType_passthroughTypes(t *testing.T) {
   212  	// non-marshalable values, like recursive structs, can't be used
   213  	type recursive struct{ Self *recursive }
   214  	v := &recursive{}
   215  	v.Self = v
   216  	_, err := jqConvertType(v)
   217  	require.Error(t, err)
   218  
   219  	testdata := []interface{}{
   220  		map[string]interface{}{"foo": 1234},
   221  		[]interface{}{"foo", "bar", "baz", 1, 2, 3},
   222  		"foo",
   223  		[]byte("foo"),
   224  		json.RawMessage(`{"foo": "bar"}`),
   225  		true,
   226  		nil,
   227  		int(1234), int8(123), int16(123), int32(123), int64(123),
   228  		uint(123), uint8(123), uint16(123), uint32(123), uint64(123),
   229  		float32(123.45), float64(123.45),
   230  	}
   231  
   232  	for _, d := range testdata {
   233  		out, err := jqConvertType(d)
   234  		require.NoError(t, err)
   235  		assert.Equal(t, d, out)
   236  	}
   237  }