add tests (ai gen)
This commit is contained in:
@@ -1 +1,5 @@
|
|||||||
my take on how notes and srs should be combind and simplified
|
my take on how notes and srs should be combind and simplified
|
||||||
|
|
||||||
|
# Tests
|
||||||
|
|
||||||
|
Run tests with `go test ./...`
|
||||||
|
|||||||
+109
@@ -0,0 +1,109 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// tests creating a file in a temp dir
|
||||||
|
func TestCreateFile(t *testing.T) {
|
||||||
|
tmpDir, err := os.MkdirTemp("", "mend_fs_test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
|
// test valid creation
|
||||||
|
filePath := filepath.Join(tmpDir, "test.md")
|
||||||
|
err = createFile(filePath, []byte("content"))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("expected no error, got %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stat(filePath); os.IsNotExist(err) {
|
||||||
|
t.Error("file was not created")
|
||||||
|
}
|
||||||
|
|
||||||
|
// test creation failure (exists)
|
||||||
|
err = createFile(filePath, []byte("content"))
|
||||||
|
if err == nil {
|
||||||
|
t.Error("expected error for existing file, got nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
// test empty path
|
||||||
|
err = createFile("", []byte{})
|
||||||
|
if err == nil {
|
||||||
|
t.Error("expected error for empty path, got nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// tests creating a folder
|
||||||
|
func TestCreateFolder(t *testing.T) {
|
||||||
|
tmpDir, err := os.MkdirTemp("", "mend_fs_test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
|
folderPath := filepath.Join(tmpDir, "subfolder")
|
||||||
|
err = createFolder(folderPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("expected no error, got %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := os.Stat(folderPath)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
t.Error("folder was not created")
|
||||||
|
}
|
||||||
|
if !info.IsDir() {
|
||||||
|
t.Error("created path is not a directory")
|
||||||
|
}
|
||||||
|
|
||||||
|
// test creation failure (exists)
|
||||||
|
err = createFolder(folderPath)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("expected error for existing folder, got nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
// test empty path
|
||||||
|
err = createFolder("")
|
||||||
|
if err == nil {
|
||||||
|
t.Error("expected error for empty path, got nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// tests recursive deletion
|
||||||
|
func TestDeletePath(t *testing.T) {
|
||||||
|
tmpDir, err := os.MkdirTemp("", "mend_fs_test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
|
subDir := filepath.Join(tmpDir, "subdir")
|
||||||
|
os.Mkdir(subDir, 0755)
|
||||||
|
fileInSubDir := filepath.Join(subDir, "file.txt")
|
||||||
|
os.WriteFile(fileInSubDir, []byte("data"), 0644)
|
||||||
|
|
||||||
|
err = deletePath(subDir)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("expected no error, got %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stat(subDir); !os.IsNotExist(err) {
|
||||||
|
t.Error("directory was not deleted")
|
||||||
|
}
|
||||||
|
|
||||||
|
// test delete non-existent
|
||||||
|
err = deletePath(filepath.Join(tmpDir, "nonexistent"))
|
||||||
|
if err == nil {
|
||||||
|
t.Error("expected error for nonexistent path, got nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
// test empty path
|
||||||
|
err = deletePath("")
|
||||||
|
if err == nil {
|
||||||
|
t.Error("expected error for empty path, got nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
+147
@@ -0,0 +1,147 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// tests creating a new fstree
|
||||||
|
func TestNewFsTree(t *testing.T) {
|
||||||
|
tmpDir, err := os.MkdirTemp("", "mend_fstree_test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
|
// setup structure:
|
||||||
|
// root/
|
||||||
|
// folder1/
|
||||||
|
// file1.md
|
||||||
|
// file2.md
|
||||||
|
|
||||||
|
os.Mkdir(filepath.Join(tmpDir, "folder1"), 0755)
|
||||||
|
os.WriteFile(filepath.Join(tmpDir, "folder1", "file1.md"), []byte(""), 0644)
|
||||||
|
os.WriteFile(filepath.Join(tmpDir, "file2.md"), []byte(""), 0644)
|
||||||
|
|
||||||
|
tree := NewFsTree(tmpDir, 0)
|
||||||
|
|
||||||
|
if tree == nil {
|
||||||
|
t.Fatal("expected tree to be created, got nil")
|
||||||
|
}
|
||||||
|
if tree.root.path != tmpDir {
|
||||||
|
t.Errorf("expected root path %s, got %s", tmpDir, tree.root.path)
|
||||||
|
}
|
||||||
|
// root children: folder1, file2.md
|
||||||
|
if len(tree.root.children) != 2 {
|
||||||
|
t.Errorf("expected 2 children, got %d", len(tree.root.children))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// tests creating nodes via tree
|
||||||
|
func TestTreeCreateNode(t *testing.T) {
|
||||||
|
tmpDir, err := os.MkdirTemp("", "mend_fstree_test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
|
tree := NewFsTree(tmpDir, 0)
|
||||||
|
// create folder
|
||||||
|
err = tree.CreateNode(tree.root, "new_folder", FolderNode)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("expected no error creating folder, got %v", err)
|
||||||
|
}
|
||||||
|
// check fs
|
||||||
|
if _, err := os.Stat(filepath.Join(tmpDir, "new_folder")); os.IsNotExist(err) {
|
||||||
|
t.Error("folder not created on fs")
|
||||||
|
}
|
||||||
|
// check tree
|
||||||
|
if len(tree.root.children) != 1 || tree.root.children[0].nodeType != FolderNode {
|
||||||
|
t.Error("tree block not updated with new folder")
|
||||||
|
}
|
||||||
|
|
||||||
|
// create file in new folder
|
||||||
|
newFolder := tree.root.children[0]
|
||||||
|
err = tree.CreateNode(newFolder, "new_file", FileNode)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("expected no error creating file, got %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check fs (.md appended automatically)
|
||||||
|
if _, err := os.Stat(filepath.Join(tmpDir, "new_folder", "new_file.md")); os.IsNotExist(err) {
|
||||||
|
t.Error("file not created on fs")
|
||||||
|
}
|
||||||
|
// check tree
|
||||||
|
if len(newFolder.children) != 1 || newFolder.children[0].nodeType != FileNode {
|
||||||
|
t.Error("tree node not updated with new file")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// tests deleting nodes
|
||||||
|
func TestTreeDeleteNode(t *testing.T) {
|
||||||
|
tmpDir, err := os.MkdirTemp("", "mend_fstree_test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
|
// setup: root/file.md
|
||||||
|
os.WriteFile(filepath.Join(tmpDir, "file.md"), []byte(""), 0644)
|
||||||
|
|
||||||
|
tree := NewFsTree(tmpDir, 0)
|
||||||
|
targetNode := tree.root.children[0]
|
||||||
|
|
||||||
|
err = tree.DeleteNode(targetNode)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("expected no error deleting node, got %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check fs
|
||||||
|
if _, err := os.Stat(filepath.Join(tmpDir, "file.md")); !os.IsNotExist(err) {
|
||||||
|
t.Error("file not deleted from fs")
|
||||||
|
}
|
||||||
|
// check tree
|
||||||
|
if len(tree.root.children) != 0 {
|
||||||
|
t.Error("tree node not updated after deletion")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// tests toggling expand
|
||||||
|
func TestTreeToggleExpand(t *testing.T) {
|
||||||
|
root := &FsNode{
|
||||||
|
nodeType: FolderNode,
|
||||||
|
expanded: true,
|
||||||
|
children: []*FsNode{}, // Initialize children
|
||||||
|
}
|
||||||
|
|
||||||
|
node := &FsNode{
|
||||||
|
nodeType: FolderNode,
|
||||||
|
expanded: true,
|
||||||
|
parent: root,
|
||||||
|
}
|
||||||
|
root.children = append(root.children, node)
|
||||||
|
|
||||||
|
// mocked tree for this test
|
||||||
|
tree := &FsTree{
|
||||||
|
root: root,
|
||||||
|
}
|
||||||
|
// Initial buildLines to setup state
|
||||||
|
tree.buildLines()
|
||||||
|
|
||||||
|
err := tree.ToggleExpand(node)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
if node.expanded {
|
||||||
|
t.Error("expected node to be collapsed")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tree.ToggleExpand(node)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
if !node.expanded {
|
||||||
|
t.Error("expected node to be expanded")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ require (
|
|||||||
github.com/charmbracelet/glamour v0.10.0
|
github.com/charmbracelet/glamour v0.10.0
|
||||||
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834
|
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834
|
||||||
github.com/mattn/go-runewidth v0.0.16
|
github.com/mattn/go-runewidth v0.0.16
|
||||||
|
github.com/yuin/goldmark v1.7.8
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -33,7 +34,6 @@ require (
|
|||||||
github.com/muesli/termenv v0.16.0 // indirect
|
github.com/muesli/termenv v0.16.0 // indirect
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||||
github.com/yuin/goldmark v1.7.8 // indirect
|
|
||||||
github.com/yuin/goldmark-emoji v1.0.5 // indirect
|
github.com/yuin/goldmark-emoji v1.0.5 // indirect
|
||||||
golang.org/x/net v0.33.0 // indirect
|
golang.org/x/net v0.33.0 // indirect
|
||||||
golang.org/x/sys v0.36.0 // indirect
|
golang.org/x/sys v0.36.0 // indirect
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
|
||||||
|
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
|
||||||
github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE=
|
github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE=
|
||||||
github.com/alecthomas/assert/v2 v2.7.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
github.com/alecthomas/assert/v2 v2.7.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
||||||
github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E=
|
github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E=
|
||||||
|
|||||||
+107
@@ -0,0 +1,107 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// tests hint extraction from text
|
||||||
|
func TestExtractHints(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
content string
|
||||||
|
expected []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "single bold hint",
|
||||||
|
content: "This is a **hint**.",
|
||||||
|
expected: []string{"hint"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple bold hints",
|
||||||
|
content: "**one** and **two**",
|
||||||
|
expected: []string{"one", "two"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "underscore hints",
|
||||||
|
content: "__one__ and __two__",
|
||||||
|
expected: []string{"one", "two"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mixed hints",
|
||||||
|
content: "**one** and __two__",
|
||||||
|
expected: []string{"one", "two"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no hints",
|
||||||
|
content: "just plain text",
|
||||||
|
expected: []string{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := extractHints(tt.content)
|
||||||
|
if !reflect.DeepEqual(got, tt.expected) {
|
||||||
|
t.Errorf("extractHints() = %v, want %v", got, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// tests section parsing from markdown
|
||||||
|
func TestParseSections(t *testing.T) {
|
||||||
|
content := []byte(`# Title 1
|
||||||
|
Content 1 with **hint1**.
|
||||||
|
|
||||||
|
# Title 2
|
||||||
|
Content 2 with __hint2__.
|
||||||
|
`)
|
||||||
|
|
||||||
|
sections := parseSections(content)
|
||||||
|
|
||||||
|
if len(sections) != 2 {
|
||||||
|
t.Fatalf("expected 2 sections, got %d", len(sections))
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify first section
|
||||||
|
if sections[0].Title != "# Title 1" {
|
||||||
|
t.Errorf("expected title '# Title 1', got '%s'", sections[0].Title)
|
||||||
|
}
|
||||||
|
if sections[0].Content != "Content 1 with **hint1**." {
|
||||||
|
t.Errorf("expected content match for section 1, got '%s'", sections[0].Content)
|
||||||
|
}
|
||||||
|
if len(sections[0].Hints) != 1 || sections[0].Hints[0] != "hint1" {
|
||||||
|
t.Error("failed to extract hint from section 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify second section
|
||||||
|
if sections[1].Title != "# Title 2" {
|
||||||
|
t.Errorf("expected title '# Title 2', got '%s'", sections[1].Title)
|
||||||
|
}
|
||||||
|
if sections[1].Content != "Content 2 with __hint2__." {
|
||||||
|
t.Errorf("expected content match for section 2, got '%s'", sections[1].Content)
|
||||||
|
}
|
||||||
|
if len(sections[1].Hints) != 1 || sections[1].Hints[0] != "hint2" {
|
||||||
|
t.Error("failed to extract hint from section 2")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// tests parsing with no headers
|
||||||
|
func TestParseSectionsNoHeaders(t *testing.T) {
|
||||||
|
content := []byte(`Just some content without any headers.
|
||||||
|
It has **one hint**.`)
|
||||||
|
|
||||||
|
sections := parseSections(content)
|
||||||
|
|
||||||
|
if len(sections) != 1 {
|
||||||
|
t.Fatalf("expected 1 section, got %d", len(sections))
|
||||||
|
}
|
||||||
|
|
||||||
|
if sections[0].Title != "no title" {
|
||||||
|
t.Errorf("expected 'no title', got '%s'", sections[0].Title)
|
||||||
|
}
|
||||||
|
if len(sections[0].Hints) != 1 || sections[0].Hints[0] != "one hint" {
|
||||||
|
t.Error("failed to extract hint")
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user