fixed processFrontmatter and added test

This commit is contained in:
Daniel Fichtinger 2025-01-01 23:55:16 -05:00
parent 35c14f09c0
commit af81617db5
No known key found for this signature in database
GPG key ID: D1B0947B25420214
7 changed files with 162 additions and 183 deletions

View file

@ -42,7 +42,8 @@ func main() {
} }
settings := builder.GetSettings(*rootPath, "foobar") settings := builder.GetSettings(*rootPath, "foobar")
err := builder.Traverse(*rootPath, "foobar", settings) // err := builder.Traverse(*rootPath, "foobar", settings)
err := builder.ProcessTraverse(*rootPath, "foobar", settings)
if err != nil { if err != nil {
fmt.Printf("Error: %s\n", err.Error()) fmt.Printf("Error: %s\n", err.Error())
} }

View file

@ -51,8 +51,8 @@ func processWithYaml(f []byte) (Metadata, []byte, error) {
} }
func processFrontmatter(p string) (Metadata, error) { func processFrontmatter(p string) (Metadata, error) {
// read only the first three lines // TODO: Also save index of the line where the actual document starts?
f, err := util.ReadNLines(p, 3) f, err := util.ReadFile(p)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -60,11 +60,13 @@ func processFrontmatter(p string) (Metadata, error) {
trimmed := bytes.TrimSpace(f) trimmed := bytes.TrimSpace(f)
normalized := strings.ReplaceAll(string(trimmed), "\r\n", "\n") normalized := strings.ReplaceAll(string(trimmed), "\r\n", "\n")
if !strings.HasPrefix(normalized, ("---\n")) { if !strings.HasPrefix(normalized, ("---\n")) {
// No valid yaml, so return the entire content // No frontmatter, return nil -- handled by caller
return nil, nil return nil, nil
} }
// Separate YAML from rest of document // Separate YAML from rest of document
split := strings.SplitN(normalized, "---\n", 3) split := strings.SplitN(normalized, "---\n", 3)
// __AUTO_GENERATED_PRINT_VAR_START__
fmt.Println(fmt.Sprintf("processFrontmatter split: %v", split)) // __AUTO_GENERATED_PRINT_VAR_END__
if len(split) < 3 { if len(split) < 3 {
return nil, fmt.Errorf("Invalid frontmatter format.") return nil, fmt.Errorf("Invalid frontmatter format.")
} }
@ -127,7 +129,7 @@ func buildPageData(m Metadata, in string, out string, settings *Settings) *PageD
return p return p
} }
func ConvertFile(in string, out string, settings *Settings) error { func BuildHtmlFile(in string, out string, settings *Settings) error {
mdPre, err := util.ReadFile(in) mdPre, err := util.ReadFile(in)
if err != nil { if err != nil {
return err return err

View file

@ -1,37 +1,64 @@
// FILE: internal/builder/build_page_test.go
package builder package builder
import "testing" import (
"os"
"testing"
)
func Test_processWithYaml(t *testing.T) { func TestProcessFrontmatter(t *testing.T) {
tests := []struct { // Create a temporary file with valid frontmatter
name string // description of this test case validContent := `---
// Named input parameters for target function. title: "Test Title"
f []byte description: "Test Description"
want Metadata ---
want2 []byte This is the body of the document.`
wantErr bool
}{ tmpfile, err := os.CreateTemp("", "testfile")
// TODO: Add test cases. if err != nil {
t.Fatal(err)
} }
for _, tt := range tests { defer os.Remove(tmpfile.Name()) // clean up
t.Run(tt.name, func(t *testing.T) {
got, got2, gotErr := processWithYaml(tt.f) if _, err := tmpfile.Write([]byte(validContent)); err != nil {
if gotErr != nil { t.Fatal(err)
if !tt.wantErr {
t.Errorf("processWithYaml() failed: %v", gotErr)
} }
return if err := tmpfile.Close(); err != nil {
t.Fatal(err)
} }
if tt.wantErr {
t.Fatal("processWithYaml() succeeded unexpectedly") // Test the processFrontmatter function with valid frontmatter
meta, err := processFrontmatter(tmpfile.Name())
if err != nil {
t.Fatalf("processFrontmatter failed: %v", err)
} }
// TODO: update the condition below to compare got with tt.want.
if true { if meta["title"] != "Test Title" || meta["description"] != "Test Description" {
t.Errorf("processWithYaml() = %v, want %v", got, tt.want) t.Errorf("Expected title 'Test Title' and description 'Test Description', got title '%s' and description '%s'", meta["title"], meta["description"])
} }
if true {
t.Errorf("processWithYaml() = %v, want %v", got2, tt.want2) // Create a temporary file with invalid frontmatter
invalidContent := `---
title: "Test Title"
description: "Test Description"
This is the body of the document.`
tmpfile, err = os.CreateTemp("", "testfile")
if err != nil {
t.Fatal(err)
} }
}) defer os.Remove(tmpfile.Name()) // clean up
if _, err := tmpfile.Write([]byte(invalidContent)); err != nil {
t.Fatal(err)
}
if err := tmpfile.Close(); err != nil {
t.Fatal(err)
}
// Test the processFrontmatter function with invalid frontmatter
_, err = processFrontmatter(tmpfile.Name())
if err == nil {
t.Fatalf("Expected error for invalid frontmatter, got nil")
} }
} }

View file

@ -1,122 +0,0 @@
package builder_test
import (
"os"
"path/filepath"
"testing"
"github.com/ficcdaf/zona/internal/builder"
"github.com/ficcdaf/zona/internal/util"
)
func TestMdToHTML(t *testing.T) {
md := []byte("# Hello World\n\nThis is a test.")
expectedHTML := "<h1 id=\"hello-world\">Hello World</h1>\n<p>This is a test.</p>\n"
nExpectedHTML := util.NormalizeContent(expectedHTML)
html, err := builder.MdToHTML(md)
nHtml := util.NormalizeContent(string(html))
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if nHtml != nExpectedHTML {
t.Errorf("Expected:\n%s\nGot:\n%s", expectedHTML, html)
}
}
func TestWriteFile(t *testing.T) {
path := filepath.Join(t.TempDir(), "test.txt")
content := []byte("Hello, World!")
err := builder.WriteFile(content, path)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
// Verify file content
data, err := os.ReadFile(path)
if err != nil {
t.Fatalf("Error reading file: %v", err)
}
if string(data) != string(content) {
t.Errorf("Expected:\n%s\nGot:\n%s", content, data)
}
}
func TestReadFile(t *testing.T) {
path := filepath.Join(t.TempDir(), "test.txt")
content := []byte("Hello, World!")
err := os.WriteFile(path, content, 0644)
if err != nil {
t.Fatalf("Error writing file: %v", err)
}
data, err := builder.ReadFile(path)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if string(data) != string(content) {
t.Errorf("Expected:\n%s\nGot:\n%s", content, data)
}
}
func TestCopyFile(t *testing.T) {
src := filepath.Join(t.TempDir(), "source.txt")
dst := filepath.Join(t.TempDir(), "dest.txt")
content := []byte("File content for testing.")
err := os.WriteFile(src, content, 0644)
if err != nil {
t.Fatalf("Error writing source file: %v", err)
}
err = builder.CopyFile(src, dst)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
// Verify destination file content
data, err := os.ReadFile(dst)
if err != nil {
t.Fatalf("Error reading destination file: %v", err)
}
if string(data) != string(content) {
t.Errorf("Expected:\n%s\nGot:\n%s", content, data)
}
}
func TestConvertFile(t *testing.T) {
src := filepath.Join(t.TempDir(), "test.md")
dst := filepath.Join(t.TempDir(), "test.html")
mdContent := []byte("# Test Title\n\nThis is Markdown content.")
nExpectedHTML := util.NormalizeContent("<h1 id=\"test-title\">Test Title</h1>\n<p>This is Markdown content.</p>\n")
err := os.WriteFile(src, mdContent, 0644)
if err != nil {
t.Fatalf("Error writing source Markdown file: %v", err)
}
err = builder.ConvertFile(src, dst)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
// Verify destination HTML content
data, err := os.ReadFile(dst)
if err != nil {
t.Fatalf("Error reading HTML file: %v", err)
}
if util.NormalizeContent(string(data)) != nExpectedHTML {
t.Errorf("Expected:\n%s\nGot:\n%s", nExpectedHTML, data)
}
}
func TestChangeExtension(t *testing.T) {
input := "test.md"
output := builder.ChangeExtension(input, ".html")
expected := "test.html"
if output != expected {
t.Errorf("Expected %s, got %s", expected, output)
}
}

View file

@ -8,22 +8,39 @@ import (
) )
type ProcessMemory struct { type ProcessMemory struct {
// Pages holds all page data that may be // Files holds all page data that may be
// needed while building *other* pages. // needed while building *other* pages.
Pages []*Page Files []*File
// Queue is a FIFO queue of Pages indexes to be built. // Queue is a FIFO queue of Pages indexes to be built.
// queue should be constructed after all the Pages have been parsed // queue should be constructed after all the Pages have been parsed
Queue []int Queue []int
// Posts is an array of pointers to post pages // Posts is an array of pointers to post pages
Posts []*Page // This list is ONLY referenced for generating
// the archive, NOT by the build process!
Posts []*File
} }
type Page struct { type File struct {
Data *PageData Data *PageData
Ext string Ext string
InPath string InPath string
OutPath string OutPath string
Copy bool ShouldCopy bool
HasFrontmatter bool
}
// NewProcessMemory initializes an empty
// process memory structure
func NewProcessMemory() *ProcessMemory {
f := make([]*File, 0)
q := make([]int, 0)
p := make([]*File, 0)
pm := &ProcessMemory{
f,
q,
p,
}
return pm
} }
// processFile processes the metadata only // processFile processes the metadata only
@ -38,36 +55,44 @@ func processFile(inPath string, entry fs.DirEntry, err error, outRoot string, se
if !entry.IsDir() { if !entry.IsDir() {
ext = filepath.Ext(inPath) ext = filepath.Ext(inPath)
outPath = util.ReplaceRoot(inPath, outRoot) outPath = util.ReplaceRoot(inPath, outRoot)
// NOTE: This could be an if statement, but keeping
// the switch makes it easy to extend the logic here later
switch ext { switch ext {
case ".md": case ".md":
// fmt.Println("Processing markdown...")
toProcess = true toProcess = true
outPath = util.ChangeExtension(outPath, ".html") outPath = util.ChangeExtension(outPath, ".html")
// If it's not a file we need to process,
// we simply copy it to the destination path.
default: default:
toProcess = false toProcess = false
} }
} }
page := &Page{
nil, var pd *PageData
ext, hasFrontmatter := false
inPath,
outPath,
!toProcess,
}
if toProcess { if toProcess {
// process its frontmatter here // process its frontmatter here
m, err := processFrontmatter(inPath) m, err := processFrontmatter(inPath)
if err != nil { if err != nil {
return err return err
} }
pd := buildPageData(m, inPath, outPath, settings) if m != nil {
if pd.Type == "post" { hasFrontmatter = true
pm.Posts = append(pm.Posts, page)
} }
page.Data = pd pd = buildPageData(m, inPath, outPath, settings)
} else {
pd = nil
} }
pm.Pages = append(pm.Pages, page) file := &File{
pd,
ext,
inPath,
outPath,
!toProcess,
hasFrontmatter,
}
if pd != nil && pd.Type == "post" {
pm.Posts = append(pm.Posts, file)
}
pm.Files = append(pm.Files, file)
return nil return nil
} }

View file

@ -23,7 +23,7 @@ func buildFile(inPath string, entry fs.DirEntry, err error, outRoot string, sett
if err := util.CreateParents(outPath); err != nil { if err := util.CreateParents(outPath); err != nil {
return err return err
} }
if err := ConvertFile(inPath, outPath, settings); err != nil { if err := BuildHtmlFile(inPath, outPath, settings); err != nil {
return errors.Join(errors.New("Error processing file "+inPath), err) return errors.Join(errors.New("Error processing file "+inPath), err)
} else { } else {
return nil return nil
@ -52,3 +52,12 @@ func Traverse(root string, outRoot string, settings *Settings) error {
err := filepath.WalkDir(root, walkFunc) err := filepath.WalkDir(root, walkFunc)
return err return err
} }
func ProcessTraverse(root string, outRoot string, settings *Settings) error {
pm := NewProcessMemory()
walkFunc := func(path string, entry fs.DirEntry, err error) error {
return processFile(path, entry, err, outRoot, settings, pm)
}
err := filepath.WalkDir(root, walkFunc)
return err
}

View file

@ -0,0 +1,37 @@
// FILE: internal/util/file_test.go
package util
import (
"bytes"
"os"
"testing"
)
func TestReadNLines(t *testing.T) {
// Create a temporary file
tmpfile, err := os.CreateTemp("", "testfile")
if err != nil {
t.Fatal(err)
}
defer os.Remove(tmpfile.Name()) // clean up
// Write some lines to the temporary file
content := []byte("line1\nline2\nline3\nline4\nline5\n")
if _, err := tmpfile.Write(content); err != nil {
t.Fatal(err)
}
if err := tmpfile.Close(); err != nil {
t.Fatal(err)
}
// Test the ReadNLines function
lines, err := ReadNLines(tmpfile.Name(), 3)
if err != nil {
t.Fatalf("ReadNLines failed: %v", err)
}
expected := []byte("line1\nline2\nline3\n")
if !bytes.Equal(lines, expected) {
t.Errorf("Expected %q, got %q", expected, lines)
}
}