github.com/ipld/go-ipld-prime@v0.21.0/printer/printer_test.go (about) 1 package printer 2 3 import ( 4 "testing" 5 6 qt "github.com/frankban/quicktest" 7 8 "github.com/ipfs/go-cid" 9 "github.com/ipld/go-ipld-prime/datamodel" 10 "github.com/ipld/go-ipld-prime/fluent/qp" 11 cidlink "github.com/ipld/go-ipld-prime/linking/cid" 12 "github.com/ipld/go-ipld-prime/node/basicnode" 13 "github.com/ipld/go-ipld-prime/node/bindnode" 14 "github.com/ipld/go-ipld-prime/schema" 15 "github.com/ipld/go-ipld-prime/testutil" 16 ) 17 18 var testLink = func() datamodel.Link { 19 someCid, _ := cid.Cast([]byte{1, 85, 0, 5, 0, 1, 2, 3, 4}) 20 return cidlink.Link{Cid: someCid} 21 }() 22 23 func TestSimpleData(t *testing.T) { 24 t.Run("nested-maps", func(t *testing.T) { 25 n, _ := qp.BuildMap(basicnode.Prototype.Any, -1, func(ma datamodel.MapAssembler) { 26 qp.MapEntry(ma, "some key", qp.String("some value")) 27 qp.MapEntry(ma, "another key", qp.String("another value")) 28 qp.MapEntry(ma, "nested map", qp.Map(2, func(ma datamodel.MapAssembler) { 29 qp.MapEntry(ma, "deeper entries", qp.String("deeper values")) 30 qp.MapEntry(ma, "more deeper entries", qp.String("more deeper values")) 31 })) 32 qp.MapEntry(ma, "nested list", qp.List(2, func(la datamodel.ListAssembler) { 33 qp.ListEntry(la, qp.Int(1)) 34 qp.ListEntry(la, qp.Int(2)) 35 })) 36 qp.MapEntry(ma, "list with float", qp.List(1, func(la datamodel.ListAssembler) { 37 qp.ListEntry(la, qp.Float(3.4)) 38 })) 39 }) 40 qt.Check(t, Sprint(n), qt.CmpEquals(), testutil.Dedent(` 41 map{ 42 string{"some key"}: string{"some value"} 43 string{"another key"}: string{"another value"} 44 string{"nested map"}: map{ 45 string{"deeper entries"}: string{"deeper values"} 46 string{"more deeper entries"}: string{"more deeper values"} 47 } 48 string{"nested list"}: list{ 49 0: int{1} 50 1: int{2} 51 } 52 string{"list with float"}: list{ 53 0: float{3.4} 54 } 55 }`, 56 )) 57 }) 58 59 t.Run("map-with-link-and-bytes", func(t *testing.T) { 60 n, _ := qp.BuildMap(basicnode.Prototype.Any, -1, func(ma datamodel.MapAssembler) { 61 qp.MapEntry(ma, "some key", qp.Link(testLink)) 62 qp.MapEntry(ma, "another key", qp.String("another value")) 63 qp.MapEntry(ma, "nested map", qp.Map(2, func(ma datamodel.MapAssembler) { 64 qp.MapEntry(ma, "deeper entries", qp.String("deeper values")) 65 qp.MapEntry(ma, "more deeper entries", qp.Link(testLink)) 66 qp.MapEntry(ma, "yet another deeper entries", qp.Bytes([]byte("fish"))) 67 })) 68 qp.MapEntry(ma, "nested list", qp.List(2, func(la datamodel.ListAssembler) { 69 qp.ListEntry(la, qp.Bytes([]byte("ghoti"))) 70 qp.ListEntry(la, qp.Int(1)) 71 qp.ListEntry(la, qp.Link(testLink)) 72 })) 73 }) 74 qt.Check(t, Sprint(n), qt.CmpEquals(), testutil.Dedent(` 75 map{ 76 string{"some key"}: link{bafkqabiaaebagba} 77 string{"another key"}: string{"another value"} 78 string{"nested map"}: map{ 79 string{"deeper entries"}: string{"deeper values"} 80 string{"more deeper entries"}: link{bafkqabiaaebagba} 81 string{"yet another deeper entries"}: bytes{66697368} 82 } 83 string{"nested list"}: list{ 84 0: bytes{67686f7469} 85 1: int{1} 86 2: link{bafkqabiaaebagba} 87 } 88 }`, 89 )) 90 }) 91 } 92 93 func TestTypedData(t *testing.T) { 94 t.Run("structs", func(t *testing.T) { 95 type FooBar struct { 96 Foo string 97 Bar string 98 Baz []byte 99 Jazz datamodel.Link 100 } 101 ts := schema.MustTypeSystem( 102 schema.SpawnString("String"), 103 schema.SpawnBytes("Bytes"), 104 schema.SpawnLink("Link"), 105 schema.SpawnStruct("FooBar", []schema.StructField{ 106 schema.SpawnStructField("foo", "String", false, false), 107 schema.SpawnStructField("bar", "String", false, false), 108 schema.SpawnStructField("baz", "Bytes", false, false), 109 schema.SpawnStructField("jazz", "Link", false, false), 110 }, nil), 111 ) 112 n := bindnode.Wrap(&FooBar{"x", "y", []byte("zed"), testLink}, ts.TypeByName("FooBar")) 113 qt.Check(t, Sprint(n), qt.CmpEquals(), testutil.Dedent(` 114 struct<FooBar>{ 115 foo: string<String>{"x"} 116 bar: string<String>{"y"} 117 baz: bytes<Bytes>{7a6564} 118 jazz: link<Link>{bafkqabiaaebagba} 119 }`, 120 )) 121 }) 122 t.Run("map-with-struct-keys", func(t *testing.T) { 123 type FooBar struct { 124 Foo string 125 Bar string 126 } 127 type WowMap struct { 128 Keys []FooBar 129 Values map[FooBar]string 130 } 131 ts := schema.MustTypeSystem( 132 schema.SpawnString("String"), 133 schema.SpawnStruct("FooBar", []schema.StructField{ 134 schema.SpawnStructField("foo", "String", false, false), 135 schema.SpawnStructField("bar", "String", false, false), 136 }, schema.SpawnStructRepresentationStringjoin(":")), 137 schema.SpawnMap("WowMap", "FooBar", "String", false), 138 ) 139 n := bindnode.Wrap(&WowMap{ 140 Keys: []FooBar{{"x", "y"}, {"z", "z"}}, 141 Values: map[FooBar]string{ 142 {"x", "y"}: "a", 143 {"z", "z"}: "b", 144 }, 145 }, ts.TypeByName("WowMap")) 146 qt.Check(t, Sprint(n), qt.CmpEquals(), testutil.Dedent(` 147 map<WowMap>{ 148 struct<FooBar>{foo: string<String>{"x"}, bar: string<String>{"y"}}: string<String>{"a"} 149 struct<FooBar>{foo: string<String>{"z"}, bar: string<String>{"z"}}: string<String>{"b"} 150 }`, 151 )) 152 }) 153 t.Run("map-with-nested-struct-keys", func(t *testing.T) { 154 type Baz struct { 155 Baz string 156 } 157 type FooBar struct { 158 Foo string 159 Bar Baz 160 Baz Baz 161 } 162 type WowMap struct { 163 Keys []FooBar 164 Values map[FooBar]Baz 165 } 166 ts := schema.MustTypeSystem( 167 schema.SpawnString("String"), 168 schema.SpawnStruct("FooBar", []schema.StructField{ 169 schema.SpawnStructField("foo", "String", false, false), 170 schema.SpawnStructField("bar", "Baz", false, false), 171 schema.SpawnStructField("baz", "Baz", false, false), 172 }, schema.SpawnStructRepresentationStringjoin(":")), 173 schema.SpawnStruct("Baz", []schema.StructField{ 174 schema.SpawnStructField("baz", "String", false, false), 175 }, schema.SpawnStructRepresentationStringjoin(":")), 176 schema.SpawnMap("WowMap", "FooBar", "Baz", false), 177 ) 178 n := bindnode.Wrap(&WowMap{ 179 Keys: []FooBar{{"x", Baz{"y"}, Baz{"y"}}, {"z", Baz{"z"}, Baz{"z"}}}, 180 Values: map[FooBar]Baz{ 181 {"x", Baz{"y"}, Baz{"y"}}: {"a"}, 182 {"z", Baz{"z"}, Baz{"z"}}: {"b"}, 183 }, 184 }, ts.TypeByName("WowMap")) 185 t.Run("complex-keys-in-effect", func(t *testing.T) { 186 cfg := Config{ 187 UseMapComplexStyleAlways: true, 188 } 189 qt.Check(t, cfg.Sprint(n), qt.CmpEquals(), testutil.Dedent(` 190 map<WowMap>{ 191 struct<FooBar>{ 192 foo: string<String>{"x"} 193 bar: struct<Baz>{ 194 baz: string<String>{"y"} 195 } 196 baz: struct<Baz>{ 197 baz: string<String>{"y"} 198 } 199 }: struct<Baz>{ 200 baz: string<String>{"a"} 201 } 202 struct<FooBar>{ 203 foo: string<String>{"z"} 204 bar: struct<Baz>{ 205 baz: string<String>{"z"} 206 } 207 baz: struct<Baz>{ 208 baz: string<String>{"z"} 209 } 210 }: struct<Baz>{ 211 baz: string<String>{"b"} 212 } 213 }`, 214 )) 215 }) 216 t.Run("complex-keys-in-disabled", func(t *testing.T) { 217 cfg := Config{ 218 UseMapComplexStyleOnType: map[schema.TypeName]bool{ 219 "WowMap": false, 220 }, 221 } 222 qt.Check(t, cfg.Sprint(n), qt.CmpEquals(), testutil.Dedent(` 223 map<WowMap>{ 224 struct<FooBar>{foo: string<String>{"x"}, bar: struct<Baz>{baz: string<String>{"y"}}, baz: struct<Baz>{baz: string<String>{"y"}}}: struct<Baz>{ 225 baz: string<String>{"a"} 226 } 227 struct<FooBar>{foo: string<String>{"z"}, bar: struct<Baz>{baz: string<String>{"z"}}, baz: struct<Baz>{baz: string<String>{"z"}}}: struct<Baz>{ 228 baz: string<String>{"b"} 229 } 230 }`, 231 )) 232 }) 233 }) 234 t.Run("invalid-nil-typed-node", func(t *testing.T) { 235 qt.Check(t, Sprint(&nilTypedNode{datamodel.Kind_Invalid}), qt.CmpEquals(), "invalid<?!nil>{?!}") 236 }) 237 t.Run("invalid-nil-typed-node-with-map-kind", func(t *testing.T) { 238 qt.Check(t, Sprint(&nilTypedNode{datamodel.Kind_Map}), qt.CmpEquals(), "invalid<?!nil>{?!}{}") 239 }) 240 } 241 242 var _ schema.TypedNode = (*nilTypedNode)(nil) 243 244 type nilTypedNode struct { 245 kind datamodel.Kind 246 } 247 248 func (n *nilTypedNode) Kind() datamodel.Kind { 249 return n.kind 250 } 251 252 func (n nilTypedNode) LookupByString(key string) (datamodel.Node, error) { 253 return nil, nil 254 } 255 256 func (n nilTypedNode) LookupByNode(key datamodel.Node) (datamodel.Node, error) { 257 return nil, nil 258 } 259 260 func (n nilTypedNode) LookupByIndex(idx int64) (datamodel.Node, error) { 261 return nil, nil 262 } 263 264 func (n nilTypedNode) LookupBySegment(seg datamodel.PathSegment) (datamodel.Node, error) { 265 return nil, nil 266 } 267 268 func (n nilTypedNode) MapIterator() datamodel.MapIterator { 269 return nil 270 } 271 272 func (n nilTypedNode) ListIterator() datamodel.ListIterator { 273 return nil 274 } 275 276 func (n nilTypedNode) Length() int64 { 277 return 0 278 } 279 280 func (n nilTypedNode) IsAbsent() bool { 281 return false 282 } 283 284 func (n nilTypedNode) IsNull() bool { 285 return false 286 } 287 288 func (n nilTypedNode) AsBool() (bool, error) { 289 panic("nil-typed-node") 290 } 291 292 func (n nilTypedNode) AsInt() (int64, error) { 293 panic("nil-typed-node") 294 } 295 296 func (n nilTypedNode) AsFloat() (float64, error) { 297 panic("nil-typed-node") 298 } 299 300 func (n nilTypedNode) AsString() (string, error) { 301 panic("nil-typed-node") 302 } 303 304 func (n nilTypedNode) AsBytes() ([]byte, error) { 305 panic("nil-typed-node") 306 } 307 308 func (n nilTypedNode) AsLink() (datamodel.Link, error) { 309 panic("nil-typed-node") 310 } 311 312 func (n nilTypedNode) Prototype() datamodel.NodePrototype { 313 return nil 314 } 315 316 func (n nilTypedNode) Type() schema.Type { 317 return nil 318 } 319 320 func (n nilTypedNode) Representation() datamodel.Node { 321 return nil 322 }