Files
2026-07-13 13:00:08 +08:00

181 lines
4.7 KiB
Go

package frontmatter
import (
"strings"
"testing"
)
func TestSplitNoFence(t *testing.T) {
fm, body := Split("just body text\nno fence")
if len(fm) != 0 {
t.Errorf("expected empty fm, got %v", fm)
}
if !strings.Contains(body, "just body text") {
t.Errorf("body = %q", body)
}
}
func TestSplitUnclosedFence(t *testing.T) {
fm, body := Split("---\nkey: val\n\nno closing fence")
if len(fm) != 0 {
t.Errorf("unclosed fence should return empty fm, got %v", fm)
}
if !strings.Contains(body, "---") {
t.Errorf("body should contain original content: %q", body)
}
}
func TestSplitEmptyBody(t *testing.T) {
fm, body := Split("---\nkey: val\n---\n")
if fm["key"] != "val" {
t.Errorf("key = %q", fm["key"])
}
if strings.TrimSpace(body) != "" {
t.Errorf("expected empty body, got %q", body)
}
}
func TestSplitNestedMetadata(t *testing.T) {
fm, body := Split("---\nname: test\ndescription: desc\nmetadata:\n type: user\n---\n\nbody here")
if fm["name"] != "test" {
t.Errorf("name = %q", fm["name"])
}
if fm["description"] != "desc" {
t.Errorf("description = %q", fm["description"])
}
if fm["type"] != "user" {
t.Errorf("type = %q, expected flattened from metadata", fm["type"])
}
if !strings.Contains(body, "body here") {
t.Errorf("body = %q", body)
}
}
func TestSplitCRLF(t *testing.T) {
fm, body := Split("---\r\nname: test\r\n---\r\nbody\r\n")
if fm["name"] != "test" {
t.Errorf("name = %q", fm["name"])
}
if !strings.Contains(body, "body") {
t.Errorf("body = %q", body)
}
}
func TestSplitQuotedValues(t *testing.T) {
fm, _ := Split("---\nname: test\ndescription: \"quoted desc\"\n---\n")
if fm["description"] != "quoted desc" {
t.Errorf("description should be unquoted: %q", fm["description"])
}
}
func TestSplitSingleQuotes(t *testing.T) {
fm, _ := Split("---\nname: test\ndescription: 'single quoted'\n---\n")
if fm["description"] != "single quoted" {
t.Errorf("description should be unquoted: %q", fm["description"])
}
}
func TestSplitEmptyInput(t *testing.T) {
fm, body := Split("")
if len(fm) != 0 {
t.Errorf("empty input should return empty fm, got %v", fm)
}
if body != "" {
t.Errorf("body = %q", body)
}
}
func TestSplitOnlyFence(t *testing.T) {
fm, body := Split("---\n---\n")
if len(fm) != 0 {
t.Errorf("empty fence should return empty fm, got %v", fm)
}
if strings.TrimSpace(body) != "" {
t.Errorf("body = %q", body)
}
}
func TestSplitMultipleKeys(t *testing.T) {
fm, _ := Split("---\na: 1\nb: 2\nc: 3\n---\n")
if fm["a"] != "1" || fm["b"] != "2" || fm["c"] != "3" {
t.Errorf("fm = %v", fm)
}
}
func TestSplitCaseInsensitive(t *testing.T) {
fm, _ := Split("---\nName: Test\nDESCRIPTION: desc\n---\n")
if fm["name"] != "Test" {
t.Errorf("name = %q", fm["name"])
}
if fm["description"] != "desc" {
t.Errorf("description = %q", fm["description"])
}
}
func TestSplitYAMLScalarsWithColonAndMultiline(t *testing.T) {
fm, body := Split("---\n" +
"name: test\n" +
"description: \"run: with colon\"\n" +
"notes: |\n" +
" first line\n" +
" second line\n" +
"---\n" +
"body")
if fm["description"] != "run: with colon" {
t.Fatalf("description = %q", fm["description"])
}
if fm["notes"] != "first line\nsecond line" {
t.Fatalf("notes = %q", fm["notes"])
}
if body != "body" {
t.Fatalf("body = %q", body)
}
}
func TestDecodeTypedFrontmatter(t *testing.T) {
var meta struct {
Name string `yaml:"name"`
Description string `yaml:"description"`
AllowedTools []string `yaml:"allowed-tools"`
}
body, err := Decode("---\nname: review\ndescription: \"run: checks\"\nallowed-tools:\n - read_file\n - grep\n---\nbody", &meta, DecodeOptions{KnownFields: true})
if err != nil {
t.Fatalf("Decode: %v", err)
}
if meta.Name != "review" || meta.Description != "run: checks" {
t.Fatalf("meta = %+v", meta)
}
if strings.Join(meta.AllowedTools, ",") != "read_file,grep" {
t.Fatalf("AllowedTools = %#v", meta.AllowedTools)
}
if body != "body" {
t.Fatalf("body = %q", body)
}
}
func TestDecodeKnownFieldsReportsUnknownKey(t *testing.T) {
var meta struct {
Name string `yaml:"name"`
}
_, err := Decode("---\nname: ok\nextra: nope\n---\nbody", &meta, DecodeOptions{KnownFields: true})
if err == nil {
t.Fatal("Decode accepted unknown frontmatter key")
}
if !strings.Contains(err.Error(), "extra") {
t.Fatalf("error = %v, want unknown key name", err)
}
}
func TestDecodeReportsMalformedYAML(t *testing.T) {
var meta struct {
Name string `yaml:"name"`
}
_, err := Decode("---\nname: [unterminated\n---\nbody", &meta, DecodeOptions{})
if err == nil {
t.Fatal("Decode accepted malformed YAML")
}
if !strings.Contains(strings.ToLower(err.Error()), "line") {
t.Fatalf("error = %v, want YAML location detail", err)
}
}