package notes import ( "testing" "time" "github.com/glebarez/sqlite" "github.com/google/uuid" "gorm.io/gorm" ) func setupTestDB(t *testing.T) *gorm.DB { db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{ SkipDefaultTransaction: true, }) if err != nil { t.Fatalf("Failed to open test database: %v", err) } if err := db.AutoMigrate(&Note{}, &NoteLink{}); err != nil { t.Fatalf("Failed to migrate test database: %v", err) } return db } func createTestNote(t *testing.T, db *gorm.DB, title, content string) *Note { note := &Note{ ID: uuid.New().String(), Title: title, Content: content, CreatedAt: time.Now(), UpdatedAt: time.Now(), } if err := db.Create(note).Error; err != nil { t.Fatalf("Failed to create test note: %v", err) } return note } func TestExtractLinks(t *testing.T) { tests := []struct { name string content string expected []string }{ { name: "no links", content: "Just some text without links", expected: []string{}, }, { name: "single link", content: "Text with [[another note]] link", expected: []string{"another note"}, }, { name: "multiple links", content: "Text with [[first]] and [[second]] links", expected: []string{"first", "second"}, }, { name: "repeated links", content: "[[same]] link [[same]] twice", expected: []string{"same", "same"}, }, } note := &Note{} for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := note.ExtractLinks(tt.content) if len(got) != len(tt.expected) { t.Errorf("ExtractLinks() got %v links, want %v", len(got), len(tt.expected)) } for i, link := range got { if link != tt.expected[i] { t.Errorf("ExtractLinks() got %v, want %v", link, tt.expected[i]) } } }) } } func TestUpdateLinks(t *testing.T) { db := setupTestDB(t) // Create some test notes note1 := createTestNote(t, db, "First Note", "Content with [[Second Note]] and [[Third Note]]") note2 := createTestNote(t, db, "Second Note", "Some content") createTestNote(t, db, "Third Note", "More content") // Create but don't need to track // Test creating links t.Run("create links", func(t *testing.T) { if err := note1.UpdateLinks(db); err != nil { t.Fatalf("UpdateLinks() error = %v", err) } // Verify links were created var links []NoteLink if err := db.Find(&links).Error; err != nil { t.Fatalf("Failed to query links: %v", err) } if len(links) != 2 { t.Errorf("Expected 2 links, got %d", len(links)) } // Load note with relationships var loadedNote Note if err := db.Preload("LinksTo").First(&loadedNote, "id = ?", note1.ID).Error; err != nil { t.Fatalf("Failed to load note: %v", err) } if len(loadedNote.LinksTo) != 2 { t.Errorf("Expected 2 LinksTo relationships, got %d", len(loadedNote.LinksTo)) } }) // Test updating links t.Run("update links", func(t *testing.T) { note1.Content = "Content with [[Second Note]] only" // Remove Third Note link if err := note1.UpdateLinks(db); err != nil { t.Fatalf("UpdateLinks() error = %v", err) } // Verify links were updated var links []NoteLink if err := db.Find(&links).Error; err != nil { t.Fatalf("Failed to query links: %v", err) } if len(links) != 1 { t.Errorf("Expected 1 link, got %d", len(links)) } // Load note with relationships var loadedNote Note if err := db.Preload("LinksTo").First(&loadedNote, "id = ?", note1.ID).Error; err != nil { t.Fatalf("Failed to load note: %v", err) } if len(loadedNote.LinksTo) != 1 { t.Errorf("Expected 1 LinksTo relationship, got %d", len(loadedNote.LinksTo)) } }) // Test bidirectional relationships t.Run("bidirectional relationships", func(t *testing.T) { // Add a link back to First Note note2.Content = "Content with [[First Note]]" if err := note2.UpdateLinks(db); err != nil { t.Fatalf("UpdateLinks() error = %v", err) } // Check First Note's LinkedBy var note1Updated Note if err := db.Preload("LinkedBy").First(¬e1Updated, "id = ?", note1.ID).Error; err != nil { t.Fatalf("Failed to load note: %v", err) } if len(note1Updated.LinkedBy) != 1 { t.Errorf("Expected 1 LinkedBy relationship, got %d", len(note1Updated.LinkedBy)) } }) // Test non-existent links t.Run("non-existent links", func(t *testing.T) { note1.Content = "Content with [[Non-existent Note]]" if err := note1.UpdateLinks(db); err != nil { t.Fatalf("UpdateLinks() error = %v", err) } // Verify no links were created var links []NoteLink if err := db.Find(&links).Error; err != nil { t.Fatalf("Failed to query links: %v", err) } if len(links) != 1 { // Should still have the link from Second Note to First Note t.Errorf("Expected 1 link, got %d", len(links)) } }) }