fixed processFrontmatter and added test
This commit is contained in:
parent
35c14f09c0
commit
af81617db5
7 changed files with 162 additions and 183 deletions
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
37
internal/util/file_test.go
Normal file
37
internal/util/file_test.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue