...

Source file src/oss.terrastruct.com/d2/d2ast/d2ast_test.go

Documentation: oss.terrastruct.com/d2/d2ast

     1  package d2ast_test
     2  
     3  import (
     4  	"encoding/json"
     5  	"math/big"
     6  	math_rand "math/rand"
     7  	"reflect"
     8  	"strconv"
     9  	"strings"
    10  	"testing"
    11  
    12  	"oss.terrastruct.com/util-go/assert"
    13  	"oss.terrastruct.com/util-go/xrand"
    14  
    15  	"oss.terrastruct.com/util-go/diff"
    16  
    17  	"oss.terrastruct.com/util-go/go2"
    18  
    19  	"oss.terrastruct.com/d2/d2ast"
    20  	"oss.terrastruct.com/d2/d2format"
    21  	"oss.terrastruct.com/d2/d2parser"
    22  )
    23  
    24  func TestRange(t *testing.T) {
    25  	t.Parallel()
    26  
    27  	t.Run("String", func(t *testing.T) {
    28  		t.Parallel()
    29  
    30  		testCases := []struct {
    31  			name string
    32  			r    d2ast.Range
    33  			exp  string
    34  		}{
    35  			{
    36  				name: "one_byte",
    37  				r: d2ast.Range{
    38  					Path: "/src/example.go",
    39  					Start: d2ast.Position{
    40  						Line:   10,
    41  						Column: 5,
    42  						Byte:   100,
    43  					},
    44  					End: d2ast.Position{
    45  						Line:   10,
    46  						Column: 6,
    47  						Byte:   100,
    48  					},
    49  				},
    50  				exp: "/src/example.go:11:6",
    51  			},
    52  			{
    53  				name: "more_than_one_byte",
    54  				r: d2ast.Range{
    55  					Path: "/src/example.go",
    56  					Start: d2ast.Position{
    57  						Line:   10,
    58  						Column: 5,
    59  						Byte:   100,
    60  					},
    61  					End: d2ast.Position{
    62  						Line:   10,
    63  						Column: 7,
    64  						Byte:   101,
    65  					},
    66  				},
    67  				exp: "/src/example.go:11:6",
    68  			},
    69  			{
    70  				name: "empty_path",
    71  				r: d2ast.Range{
    72  					Start: d2ast.Position{
    73  						Line:   10,
    74  						Column: 5,
    75  						Byte:   100,
    76  					},
    77  					End: d2ast.Position{
    78  						Line:   10,
    79  						Column: 7,
    80  						Byte:   101,
    81  					},
    82  				},
    83  				exp: "11:6",
    84  			},
    85  			{
    86  				name: "start_equal_end",
    87  				r: d2ast.Range{
    88  					Start: d2ast.Position{
    89  						Line:   10,
    90  						Column: 5,
    91  						Byte:   100,
    92  					},
    93  					End: d2ast.Position{
    94  						Line:   10,
    95  						Column: 5,
    96  						Byte:   100,
    97  					},
    98  				},
    99  				exp: "11:6",
   100  			},
   101  		}
   102  
   103  		for _, tc := range testCases {
   104  			tc := tc
   105  			t.Run(tc.name, func(t *testing.T) {
   106  				t.Parallel()
   107  
   108  				if tc.exp != tc.r.String() {
   109  					t.Fatalf("expected %q but got %q", tc.exp, tc.r.String())
   110  				}
   111  			})
   112  		}
   113  	})
   114  
   115  	t.Run("UnmarshalText", func(t *testing.T) {
   116  		t.Parallel()
   117  
   118  		testCases := []struct {
   119  			name string
   120  			in   string
   121  
   122  			exp d2ast.Range
   123  
   124  			errmsg string
   125  		}{
   126  			{
   127  				name: "success",
   128  				in:   `"json_test.d2,1:1:0-5:1:50"`,
   129  				exp:  d2ast.Range{Path: "json_test.d2", Start: d2ast.Position{Line: 1, Column: 1, Byte: 0}, End: d2ast.Position{Line: 5, Column: 1, Byte: 50}},
   130  			},
   131  			{
   132  				name:   "err1",
   133  				in:     `"json_test.d2-5:1:50"`,
   134  				errmsg: "missing Start field",
   135  			},
   136  			{
   137  				name:   "err2",
   138  				in:     `"json_test.d2"`,
   139  				errmsg: "missing End field",
   140  			},
   141  			{
   142  				name:   "err3",
   143  				in:     `"json_test.d2,1:1:0-5:150"`,
   144  				errmsg: "expected three fields",
   145  			},
   146  			{
   147  				name:   "err4",
   148  				in:     `"json_test.d2,1:10-5:1:50"`,
   149  				errmsg: "expected three fields",
   150  			},
   151  			{
   152  				name:   "err5",
   153  				in:     `"json_test.d2,a:1:0-5:1:50"`,
   154  				errmsg: `strconv.Atoi: parsing "a": invalid syntax`,
   155  			},
   156  			{
   157  				name:   "err6",
   158  				in:     `"json_test.d2,1:c:0-5:1:50"`,
   159  				errmsg: `strconv.Atoi: parsing "c": invalid syntax`,
   160  			},
   161  		}
   162  
   163  		for _, tc := range testCases {
   164  			tc := tc
   165  			t.Run(tc.name, func(t *testing.T) {
   166  				t.Parallel()
   167  
   168  				var r d2ast.Range
   169  				err := json.Unmarshal([]byte(tc.in), &r)
   170  
   171  				if tc.errmsg != "" {
   172  					if err == nil {
   173  						t.Fatalf("expected error: %#v", err)
   174  					}
   175  					if !strings.Contains(err.Error(), tc.errmsg) {
   176  						t.Fatalf("error message does not contain %q: %q", tc.errmsg, err.Error())
   177  					}
   178  				} else {
   179  					if err != nil {
   180  						t.Fatal(err)
   181  					}
   182  					if !reflect.DeepEqual(tc.exp, r) {
   183  						t.Fatalf("expected %#v but got %#v", tc.exp, r)
   184  					}
   185  				}
   186  			})
   187  		}
   188  	})
   189  
   190  	t.Run("Advance", func(t *testing.T) {
   191  		t.Parallel()
   192  
   193  		t.Run("UTF-8", func(t *testing.T) {
   194  			t.Parallel()
   195  
   196  			var p d2ast.Position
   197  			p = p.Advance('a', false)
   198  			assert.StringJSON(t, `"0:1:1"`, p)
   199  			p = p.Advance('\n', false)
   200  			assert.StringJSON(t, `"1:0:2"`, p)
   201  			p = p.Advance('è', false)
   202  			assert.StringJSON(t, `"1:2:4"`, p)
   203  			p = p.Advance('𐀀', false)
   204  			assert.StringJSON(t, `"1:6:8"`, p)
   205  
   206  			p = p.Subtract('𐀀', false)
   207  			assert.StringJSON(t, `"1:2:4"`, p)
   208  			p = p.Subtract('è', false)
   209  			assert.StringJSON(t, `"1:0:2"`, p)
   210  		})
   211  
   212  		t.Run("UTF-16", func(t *testing.T) {
   213  			t.Parallel()
   214  
   215  			var p d2ast.Position
   216  			p = p.Advance('a', true)
   217  			assert.StringJSON(t, `"0:1:1"`, p)
   218  			p = p.Advance('\n', true)
   219  			assert.StringJSON(t, `"1:0:2"`, p)
   220  			p = p.Advance('è', true)
   221  			assert.StringJSON(t, `"1:1:3"`, p)
   222  			p = p.Advance('𐀀', true)
   223  			assert.StringJSON(t, `"1:3:5"`, p)
   224  
   225  			p = p.Subtract('𐀀', true)
   226  			assert.StringJSON(t, `"1:1:3"`, p)
   227  			p = p.Subtract('è', true)
   228  			assert.StringJSON(t, `"1:0:2"`, p)
   229  		})
   230  	})
   231  }
   232  
   233  func TestJSON(t *testing.T) {
   234  	t.Parallel()
   235  
   236  	m := &d2ast.Map{
   237  		Range: d2ast.Range{Path: "json_test.d2", Start: d2ast.Position{Line: 0, Column: 0, Byte: 0}, End: d2ast.Position{Line: 5, Column: 1, Byte: 50}},
   238  
   239  		Nodes: []d2ast.MapNodeBox{
   240  			{
   241  				Comment: &d2ast.Comment{
   242  					Value: `America was discovered by Amerigo Vespucci and was named after him, until
   243  people got tired of living in a place called "Vespuccia" and changed its
   244  name to "America".
   245  		-- Mike Harding, "The Armchair Anarchist's Almanac"`,
   246  				},
   247  			},
   248  			{
   249  				BlockComment: &d2ast.BlockComment{
   250  					Value: `America was discovered by Amerigo Vespucci and was named after him, until
   251  people got tired of living in a place called "Vespuccia" and changed its
   252  name to "America".
   253  		-- Mike Harding, "The Armchair Anarchist's Almanac"`,
   254  				},
   255  			},
   256  			{
   257  				Substitution: &d2ast.Substitution{
   258  					Spread: true,
   259  					Path: []*d2ast.StringBox{
   260  						{
   261  							BlockString: &d2ast.BlockString{
   262  								Quote: "|",
   263  								Tag:   "text",
   264  								Value: `America was discovered by Amerigo Vespucci and was named after him, until
   265  	people got tired of living in a place called "Vespuccia" and changed its
   266  	name to "America".
   267  	-- Mike Harding, "The Armchair Anarchist's Almanac"`,
   268  							},
   269  						},
   270  					},
   271  				},
   272  			},
   273  			{
   274  				MapKey: &d2ast.Key{
   275  					Ampersand: true,
   276  
   277  					Key: &d2ast.KeyPath{
   278  						Path: []*d2ast.StringBox{
   279  							{
   280  								SingleQuotedString: &d2ast.SingleQuotedString{
   281  									Value: "before edges",
   282  								},
   283  							},
   284  						},
   285  					},
   286  
   287  					Edges: []*d2ast.Edge{
   288  						{
   289  							Src: &d2ast.KeyPath{
   290  								Path: []*d2ast.StringBox{
   291  									{
   292  										SingleQuotedString: &d2ast.SingleQuotedString{
   293  											Value: "src",
   294  										},
   295  									},
   296  								},
   297  							},
   298  							SrcArrow: "*",
   299  
   300  							Dst: &d2ast.KeyPath{
   301  								Path: []*d2ast.StringBox{
   302  									{
   303  										SingleQuotedString: &d2ast.SingleQuotedString{
   304  											Value: "dst",
   305  										},
   306  									},
   307  								},
   308  							},
   309  							DstArrow: ">",
   310  						},
   311  						{
   312  							Src: &d2ast.KeyPath{
   313  								Path: []*d2ast.StringBox{
   314  									{
   315  										SingleQuotedString: &d2ast.SingleQuotedString{
   316  											Value: "dst",
   317  										},
   318  									},
   319  								},
   320  							},
   321  
   322  							Dst: &d2ast.KeyPath{
   323  								Path: []*d2ast.StringBox{
   324  									{
   325  										SingleQuotedString: &d2ast.SingleQuotedString{
   326  											Value: "dst2",
   327  										},
   328  									},
   329  								},
   330  							},
   331  						},
   332  					},
   333  
   334  					EdgeIndex: &d2ast.EdgeIndex{
   335  						Glob: true,
   336  					},
   337  
   338  					EdgeKey: &d2ast.KeyPath{
   339  						Path: []*d2ast.StringBox{
   340  							{
   341  								SingleQuotedString: &d2ast.SingleQuotedString{
   342  									Value: "after edges",
   343  								},
   344  							},
   345  						},
   346  					},
   347  
   348  					Primary: d2ast.ScalarBox{
   349  						Null: &d2ast.Null{},
   350  					},
   351  
   352  					Value: d2ast.ValueBox{
   353  						Array: &d2ast.Array{
   354  							Nodes: []d2ast.ArrayNodeBox{
   355  								{
   356  									Boolean: &d2ast.Boolean{
   357  										Value: true,
   358  									},
   359  								},
   360  								{
   361  									Number: &d2ast.Number{
   362  										Raw:   "0xFF",
   363  										Value: big.NewRat(15, 1),
   364  									},
   365  								},
   366  								{
   367  									UnquotedString: &d2ast.UnquotedString{
   368  										Value: []d2ast.InterpolationBox{
   369  											{
   370  												String: go2.Pointer("no quotes needed"),
   371  											},
   372  										},
   373  									},
   374  								},
   375  								{
   376  									UnquotedString: &d2ast.UnquotedString{
   377  										Value: []d2ast.InterpolationBox{
   378  											{
   379  												Substitution: &d2ast.Substitution{},
   380  											},
   381  										},
   382  									},
   383  								},
   384  								{
   385  									DoubleQuotedString: &d2ast.DoubleQuotedString{
   386  										Value: []d2ast.InterpolationBox{
   387  											{
   388  												String: go2.Pointer("no quotes needed"),
   389  											},
   390  										},
   391  									},
   392  								},
   393  								{
   394  									SingleQuotedString: &d2ast.SingleQuotedString{
   395  										Value: "rawr",
   396  									},
   397  								},
   398  								{
   399  									BlockString: &d2ast.BlockString{
   400  										Quote: "|",
   401  										Tag:   "text",
   402  										Value: `America was discovered by Amerigo Vespucci and was named after him, until
   403  			people got tired of living in a place called "Vespuccia" and changed its
   404  			name to "America".
   405  			-- Mike Harding, "The Armchair Anarchist's Almanac"`,
   406  									},
   407  								},
   408  							},
   409  						},
   410  					},
   411  				},
   412  			},
   413  		},
   414  	}
   415  
   416  	assert.StringJSON(t, `{
   417    "range": "json_test.d2,0:0:0-5:1:50",
   418    "nodes": [
   419      {
   420        "comment": {
   421          "range": ",0:0:0-0:0:0",
   422          "value": "America was discovered by Amerigo Vespucci and was named after him, until\npeople got tired of living in a place called \"Vespuccia\" and changed its\nname to \"America\".\n\t\t-- Mike Harding, \"The Armchair Anarchist's Almanac\""
   423        }
   424      },
   425      {
   426        "block_comment": {
   427          "range": ",0:0:0-0:0:0",
   428          "value": "America was discovered by Amerigo Vespucci and was named after him, until\npeople got tired of living in a place called \"Vespuccia\" and changed its\nname to \"America\".\n\t\t-- Mike Harding, \"The Armchair Anarchist's Almanac\""
   429        }
   430      },
   431      {
   432        "substitution": {
   433          "range": ",0:0:0-0:0:0",
   434          "spread": true,
   435          "path": [
   436            {
   437              "block_string": {
   438                "range": ",0:0:0-0:0:0",
   439                "quote": "|",
   440                "tag": "text",
   441                "value": "America was discovered by Amerigo Vespucci and was named after him, until\n\tpeople got tired of living in a place called \"Vespuccia\" and changed its\n\tname to \"America\".\n\t-- Mike Harding, \"The Armchair Anarchist's Almanac\""
   442              }
   443            }
   444          ]
   445        }
   446      },
   447      {
   448        "map_key": {
   449          "range": ",0:0:0-0:0:0",
   450          "ampersand": true,
   451          "key": {
   452            "range": ",0:0:0-0:0:0",
   453            "path": [
   454              {
   455                "single_quoted_string": {
   456                  "range": ",0:0:0-0:0:0",
   457                  "raw": "",
   458                  "value": "before edges"
   459                }
   460              }
   461            ]
   462          },
   463          "edges": [
   464            {
   465              "range": ",0:0:0-0:0:0",
   466              "src": {
   467                "range": ",0:0:0-0:0:0",
   468                "path": [
   469                  {
   470                    "single_quoted_string": {
   471                      "range": ",0:0:0-0:0:0",
   472                      "raw": "",
   473                      "value": "src"
   474                    }
   475                  }
   476                ]
   477              },
   478              "src_arrow": "*",
   479              "dst": {
   480                "range": ",0:0:0-0:0:0",
   481                "path": [
   482                  {
   483                    "single_quoted_string": {
   484                      "range": ",0:0:0-0:0:0",
   485                      "raw": "",
   486                      "value": "dst"
   487                    }
   488                  }
   489                ]
   490              },
   491              "dst_arrow": ">"
   492            },
   493            {
   494              "range": ",0:0:0-0:0:0",
   495              "src": {
   496                "range": ",0:0:0-0:0:0",
   497                "path": [
   498                  {
   499                    "single_quoted_string": {
   500                      "range": ",0:0:0-0:0:0",
   501                      "raw": "",
   502                      "value": "dst"
   503                    }
   504                  }
   505                ]
   506              },
   507              "src_arrow": "",
   508              "dst": {
   509                "range": ",0:0:0-0:0:0",
   510                "path": [
   511                  {
   512                    "single_quoted_string": {
   513                      "range": ",0:0:0-0:0:0",
   514                      "raw": "",
   515                      "value": "dst2"
   516                    }
   517                  }
   518                ]
   519              },
   520              "dst_arrow": ""
   521            }
   522          ],
   523          "edge_index": {
   524            "range": ",0:0:0-0:0:0",
   525            "int": null,
   526            "glob": true
   527          },
   528          "edge_key": {
   529            "range": ",0:0:0-0:0:0",
   530            "path": [
   531              {
   532                "single_quoted_string": {
   533                  "range": ",0:0:0-0:0:0",
   534                  "raw": "",
   535                  "value": "after edges"
   536                }
   537              }
   538            ]
   539          },
   540          "primary": {
   541            "null": {
   542              "range": ",0:0:0-0:0:0"
   543            }
   544          },
   545          "value": {
   546            "array": {
   547              "range": ",0:0:0-0:0:0",
   548              "nodes": [
   549                {
   550                  "boolean": {
   551                    "range": ",0:0:0-0:0:0",
   552                    "value": true
   553                  }
   554                },
   555                {
   556                  "number": {
   557                    "range": ",0:0:0-0:0:0",
   558                    "raw": "0xFF",
   559                    "value": "15"
   560                  }
   561                },
   562                {
   563                  "unquoted_string": {
   564                    "range": ",0:0:0-0:0:0",
   565                    "value": [
   566                      {
   567                        "string": "no quotes needed"
   568                      }
   569                    ]
   570                  }
   571                },
   572                {
   573                  "unquoted_string": {
   574                    "range": ",0:0:0-0:0:0",
   575                    "value": [
   576                      {
   577                        "substitution": {
   578                          "range": ",0:0:0-0:0:0",
   579                          "spread": false,
   580                          "path": null
   581                        }
   582                      }
   583                    ]
   584                  }
   585                },
   586                {
   587                  "double_quoted_string": {
   588                    "range": ",0:0:0-0:0:0",
   589                    "value": [
   590                      {
   591                        "string": "no quotes needed"
   592                      }
   593                    ]
   594                  }
   595                },
   596                {
   597                  "single_quoted_string": {
   598                    "range": ",0:0:0-0:0:0",
   599                    "raw": "",
   600                    "value": "rawr"
   601                  }
   602                },
   603                {
   604                  "block_string": {
   605                    "range": ",0:0:0-0:0:0",
   606                    "quote": "|",
   607                    "tag": "text",
   608                    "value": "America was discovered by Amerigo Vespucci and was named after him, until\n\t\t\tpeople got tired of living in a place called \"Vespuccia\" and changed its\n\t\t\tname to \"America\".\n\t\t\t-- Mike Harding, \"The Armchair Anarchist's Almanac\""
   609                  }
   610                }
   611              ]
   612            }
   613          }
   614        }
   615      }
   616    ]
   617  }`, m)
   618  }
   619  
   620  func testRawStringKey(t *testing.T, key string) {
   621  	ast := d2ast.RawString(key, true)
   622  	enc := d2format.Format(ast)
   623  	k, err := d2parser.ParseKey(enc)
   624  	if err != nil {
   625  		t.Fatal(err)
   626  	}
   627  	if len(k.Path) != 1 {
   628  		t.Fatalf("unexpected key length: %#v", k.Path)
   629  	}
   630  	err = diff.Runes(key, k.Path[0].Unbox().ScalarString())
   631  	if err != nil {
   632  		t.Fatal(err)
   633  	}
   634  }
   635  
   636  func testRawStringValue(t *testing.T, value string) {
   637  	ast := d2ast.RawString(value, false)
   638  	enc := d2format.Format(ast)
   639  	v, err := d2parser.ParseValue(enc)
   640  	if err != nil {
   641  		t.Fatal(err)
   642  	}
   643  	ps, ok := v.(d2ast.Scalar)
   644  	if !ok {
   645  		t.Fatalf("unexpected value type: %#v", v)
   646  	}
   647  	err = diff.Runes(value, ps.ScalarString())
   648  	if err != nil {
   649  		t.Fatal(err)
   650  	}
   651  }
   652  
   653  func TestRawString(t *testing.T) {
   654  	t.Parallel()
   655  
   656  	t.Run("chaos", func(t *testing.T) {
   657  		t.Parallel()
   658  
   659  		t.Run("pinned", func(t *testing.T) {
   660  			t.Parallel()
   661  
   662  			pinnedTestCases := []struct {
   663  				name string
   664  				str  string
   665  			}{
   666  				{
   667  					name: "1",
   668  					str:  "\U000b64cd\U0008b732\U0009632c\U000983f8\U000f42d4\U000c4749\U00041723\uf584蝉\U00100cd5\U0003325d\U0003e4d2\U0007ff0e\U000e03d8\U000b0431\U00042053\U0001b3ea𠒹\U0006d9cf\U000c5b1c\U00019a3c\U000f3c3d\U0004acedଶ\U0009da18\U0001a0bb\U000b6bfd\U00015ebd\U00088c5a녈\U00078277\U000eaa58\U0009266b\U000d85ae\U000d6ce8譊𣱡\U0008ac84\U000a722f\U000d3d35\U00072581\U000c3423\U000a1753\U00082014\U0001bde6\U0010bf47炏\U000423fa\U0007df70\U00088aaf\U00074e5e\U000ee80b\U000e3d53\U0003f542\U0001ad9f\U00031408\U000cce7e\U00082172\u202f",
   669  				},
   670  				{
   671  					name: "2",
   672  					str:  "'\"Tc\U000d148d\U000dd61a\U0007cf68OO\U000b87a9\U000c073a\U000e7828n\U00068a9fc\U0004fbf5\x041\\'''",
   673  				},
   674  				{
   675  					name: "3",
   676  					str:  "\r\U00057d53\x01'\U00042e5a\U0007be73T\U000fb916\x01\U000e0e4afL]\U000474d1\x15\U00083bc0\fbT\ue09bs{vP\U000b3d33\x0f\U0007ad13\x10\U00098b38\x1d\U000cf9da\n ",
   677  				},
   678  			}
   679  			for _, tc := range pinnedTestCases {
   680  				tc := tc
   681  				t.Run(tc.name, func(t *testing.T) {
   682  					t.Parallel()
   683  
   684  					t.Run("key", func(t *testing.T) {
   685  						t.Parallel()
   686  						testRawStringKey(t, tc.str)
   687  					})
   688  
   689  					t.Run("value", func(t *testing.T) {
   690  						t.Parallel()
   691  						testRawStringValue(t, tc.str)
   692  					})
   693  				})
   694  			}
   695  		})
   696  
   697  		for i := 0; i < 1000; i++ {
   698  			i := i
   699  			t.Run(strconv.Itoa(i), func(t *testing.T) {
   700  				t.Parallel()
   701  
   702  				s := xrand.String(math_rand.Intn(99), nil)
   703  				t.Logf("testing: %q", s)
   704  
   705  				t.Run("key", func(t *testing.T) {
   706  					t.Parallel()
   707  					testRawStringKey(t, s)
   708  				})
   709  
   710  				t.Run("value", func(t *testing.T) {
   711  					t.Parallel()
   712  					testRawStringValue(t, s)
   713  				})
   714  			})
   715  		}
   716  	})
   717  
   718  	testCases := []struct {
   719  		name  string
   720  		str   string
   721  		exp   string
   722  		inKey bool
   723  	}{
   724  		{
   725  			name: "empty",
   726  			str:  ``,
   727  			exp:  `""`,
   728  		},
   729  		{
   730  			name: "null",
   731  			str:  `null`,
   732  			exp:  `"null"`,
   733  		},
   734  		{
   735  			name: "simple",
   736  			str:  `wearisome_condition_of_humanity`,
   737  			exp:  `wearisome_condition_of_humanity`,
   738  		},
   739  		{
   740  			name: "specials_double",
   741  			str:  `'#;#;#'`,
   742  			exp:  `"'#;#;#'"`,
   743  		},
   744  		{
   745  			name: "specials_single_quote",
   746  			str:  `"cambridge"`,
   747  			exp:  `'"cambridge"'`,
   748  		},
   749  		{
   750  			name: "specials_single_dollar",
   751  			str:  `$bingo`,
   752  			exp:  `'$bingo'`,
   753  		},
   754  		{
   755  			name: "not_key_specials",
   756  			str:  `------`,
   757  			exp:  `------`,
   758  		},
   759  		{
   760  			name:  "key_specials_double",
   761  			str:   `-----`,
   762  			exp:   `"-----"`,
   763  			inKey: true,
   764  		},
   765  		{
   766  			name:  "key_specials_single",
   767  			str:   `"cambridge"`,
   768  			exp:   `'"cambridge"'`,
   769  			inKey: true,
   770  		},
   771  		{
   772  			name:  "key_specials_unquoted",
   773  			str:   `square-2`,
   774  			exp:   `square-2`,
   775  			inKey: true,
   776  		},
   777  		{
   778  			name: "multiline",
   779  			str: `||||yes
   780  yes
   781  yes
   782  yes
   783  ||||`,
   784  			exp:   `"||||yes\nyes\nyes\nyes\n||||"`,
   785  			inKey: true,
   786  		},
   787  		{
   788  			name: "leading_whitespace",
   789  			str:  `  yoho_park   `,
   790  			exp:  `"  yoho_park   "`,
   791  		},
   792  		{
   793  			name: "leading_whitespace_newlines",
   794  			str: `  yoho
   795  _park   `,
   796  			exp: `"  yoho\n_park   "`,
   797  		},
   798  		{
   799  			name: "leading_space_double_quotes_and_newlines",
   800  			str: `   "yoho"
   801  _park   `,
   802  			exp: `"   \"yoho\"\n_park   "`,
   803  		},
   804  	}
   805  
   806  	for _, tc := range testCases {
   807  		tc := tc
   808  		t.Run(tc.name, func(t *testing.T) {
   809  			t.Parallel()
   810  
   811  			ast := d2ast.RawString(tc.str, tc.inKey)
   812  			assert.String(t, tc.exp, d2format.Format(ast))
   813  		})
   814  	}
   815  }
   816  

View as plain text