mirror of
https://github.com/therootcompany/golib.git
synced 2026-03-29 03:24:07 +00:00
Tests cover: - 3-column input with header skip (--no-header flag) - Extra columns ignored (columns beyond 3rd are discarded) - 2-column input (no comment column) - Empty comment column handling - Comment preservation - Multi-line values and comments - Single quote escaping - Invalid key error - Export prefix optional - Empty key produces blank line
214 lines
4.6 KiB
Go
214 lines
4.6 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/therootcompany/golib/io/transform/gsheet2csv"
|
|
)
|
|
|
|
func TestConvert(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input string
|
|
noHeader bool
|
|
noExport bool
|
|
want string
|
|
wantErr bool
|
|
errSubstr string
|
|
}{
|
|
{
|
|
name: "basic 3-column with header",
|
|
noHeader: false,
|
|
input: "KEY,VALUE,COMMENT\n" +
|
|
"FOO,bar,comment here\n" +
|
|
"BAZ,qux,another comment\n",
|
|
want: "# comment here\nexport FOO='bar'\n# another comment\nexport BAZ='qux'\n",
|
|
},
|
|
{
|
|
name: "3-column with --no-header",
|
|
noHeader: true,
|
|
input: "KEY,VALUE,COMMENT\n" +
|
|
"FOO,bar,comment here\n",
|
|
want: "# COMMENT\nexport KEY='VALUE'\n# comment here\nexport FOO='bar'\n",
|
|
},
|
|
{
|
|
name: "extra columns ignored",
|
|
noHeader: false,
|
|
input: "KEY,VALUE,COMMENT,URL,NOTES\n" +
|
|
"FOO,bar,comment,https://example.com,extra notes\n",
|
|
want: "# comment\nexport FOO='bar'\n",
|
|
},
|
|
{
|
|
name: "2-column no comment",
|
|
noHeader: false,
|
|
input: "KEY,VALUE\n" +
|
|
"FOO,bar\n" +
|
|
"BAZ,qux\n",
|
|
want: "export FOO='bar'\nexport BAZ='qux'\n",
|
|
},
|
|
{
|
|
name: "empty comment column",
|
|
noHeader: false,
|
|
input: "KEY,VALUE,COMMENT\n" +
|
|
"FOO,bar,\n" +
|
|
"BAZ,qux,has comment\n",
|
|
want: "export FOO='bar'\n# has comment\nexport BAZ='qux'\n",
|
|
},
|
|
{
|
|
name: "comment preserved",
|
|
noHeader: false,
|
|
input: "# This is a comment\n" +
|
|
"KEY,VALUE,COMMENT\n" +
|
|
"FOO,bar,note\n",
|
|
want: "# This is a comment\n# note\nexport FOO='bar'\n",
|
|
},
|
|
{
|
|
name: "multi-line comment",
|
|
noHeader: false,
|
|
input: "KEY,VALUE,COMMENT\n" +
|
|
"FOO,bar,\"line1\nline2\"\n",
|
|
want: "# line1\n# line2\nexport FOO='bar'\n",
|
|
},
|
|
{
|
|
name: "multi-line value",
|
|
noHeader: false,
|
|
input: "KEY,VALUE,COMMENT\n" +
|
|
"FOO,\"val1\nval2\",note\n",
|
|
want: "# note\nexport FOO='val1\nval2'\n",
|
|
},
|
|
{
|
|
name: "single quotes in value escaped",
|
|
noHeader: false,
|
|
input: "KEY,VALUE,COMMENT\n" +
|
|
"FOO,it's value,note\n",
|
|
want: "# note\nexport FOO='it'\"'\"'s value'\n",
|
|
},
|
|
{
|
|
name: "invalid key errors",
|
|
noHeader: false,
|
|
input: "KEY,VALUE,COMMENT\ninvalid-key,val,note\n",
|
|
wantErr: true,
|
|
errSubstr: "invalid key",
|
|
},
|
|
{
|
|
name: "export prefix optional",
|
|
noHeader: false,
|
|
noExport: true,
|
|
input: "KEY,VALUE,COMMENT\n" +
|
|
"FOO,bar,note\n",
|
|
want: "# note\nFOO='bar'\n",
|
|
},
|
|
{
|
|
name: "empty key produces blank line",
|
|
noHeader: false,
|
|
input: "KEY,VALUE,COMMENT\n" +
|
|
",value,note\n" +
|
|
"FOO,bar,note\n",
|
|
want: "\n# note\nexport FOO='bar'\n",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
gsr := gsheet2csv.NewReader(strings.NewReader(tt.input))
|
|
gsr.Comment = 0
|
|
gsr.FieldsPerRecord = -1
|
|
|
|
var out bytes.Buffer
|
|
err := convert(gsr, &out, tt.noHeader, tt.noExport)
|
|
|
|
if tt.wantErr {
|
|
if err == nil {
|
|
t.Errorf("expected error containing %q, got nil", tt.errSubstr)
|
|
} else if !strings.Contains(err.Error(), tt.errSubstr) {
|
|
t.Errorf("error %q does not contain %q", err.Error(), tt.errSubstr)
|
|
}
|
|
return
|
|
}
|
|
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
return
|
|
}
|
|
|
|
got := out.String()
|
|
if got != tt.want {
|
|
t.Errorf("output mismatch:\ngot:\n%s\nwant:\n%s", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIsValidKey(t *testing.T) {
|
|
tests := []struct {
|
|
key string
|
|
want bool
|
|
}{
|
|
{"FOO", true},
|
|
{"FOO_BAR", true},
|
|
{"FOO123", true},
|
|
{"A1B2C3", true},
|
|
{"", true},
|
|
{"foo", false},
|
|
{"FOO-BAR", false},
|
|
{"FOO.BAR", false},
|
|
{"FOO BAR", false},
|
|
{"${FOO}", false},
|
|
{"123ABC", true},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.key, func(t *testing.T) {
|
|
got := isValidKey(tt.key)
|
|
if got != tt.want {
|
|
t.Errorf("isValidKey(%q) = %v, want %v", tt.key, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSanitizeComment(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input string
|
|
want string
|
|
}{
|
|
{
|
|
name: "simple comment",
|
|
input: "note",
|
|
want: "# note\n",
|
|
},
|
|
{
|
|
name: "comment with leading/trailing space",
|
|
input: " note ",
|
|
want: "# note\n",
|
|
},
|
|
{
|
|
name: "multi-line comment",
|
|
input: "line1\nline2",
|
|
want: "# line1\n# line2\n",
|
|
},
|
|
{
|
|
name: "multi-line with CRLF",
|
|
input: "line1\r\nline2",
|
|
want: "# line1\n# line2\n",
|
|
},
|
|
{
|
|
name: "empty comment",
|
|
input: "",
|
|
want: "",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := sanitizeComment(tt.input)
|
|
if got != tt.want {
|
|
t.Errorf("sanitizeComment(%q) = %q, want %q", tt.input, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|