feat: BreakPilot PWA - Full codebase (clean push without large binaries)
Some checks failed
Tests / Go Tests (push) Has been cancelled
Tests / Python Tests (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / Go Lint (push) Has been cancelled
Tests / Python Lint (push) Has been cancelled
Tests / Security Scan (push) Has been cancelled
Tests / All Checks Passed (push) Has been cancelled
Security Scanning / Secret Scanning (push) Has been cancelled
Security Scanning / Dependency Vulnerability Scan (push) Has been cancelled
Security Scanning / Go Security Scan (push) Has been cancelled
Security Scanning / Python Security Scan (push) Has been cancelled
Security Scanning / Node.js Security Scan (push) Has been cancelled
Security Scanning / Docker Image Security (push) Has been cancelled
Security Scanning / Security Summary (push) Has been cancelled
CI/CD Pipeline / Go Tests (push) Has been cancelled
CI/CD Pipeline / Python Tests (push) Has been cancelled
CI/CD Pipeline / Website Tests (push) Has been cancelled
CI/CD Pipeline / Linting (push) Has been cancelled
CI/CD Pipeline / Security Scan (push) Has been cancelled
CI/CD Pipeline / Docker Build & Push (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / CI Summary (push) Has been cancelled
ci/woodpecker/manual/build-ci-image Pipeline was successful
ci/woodpecker/manual/main Pipeline failed
Some checks failed
Tests / Go Tests (push) Has been cancelled
Tests / Python Tests (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / Go Lint (push) Has been cancelled
Tests / Python Lint (push) Has been cancelled
Tests / Security Scan (push) Has been cancelled
Tests / All Checks Passed (push) Has been cancelled
Security Scanning / Secret Scanning (push) Has been cancelled
Security Scanning / Dependency Vulnerability Scan (push) Has been cancelled
Security Scanning / Go Security Scan (push) Has been cancelled
Security Scanning / Python Security Scan (push) Has been cancelled
Security Scanning / Node.js Security Scan (push) Has been cancelled
Security Scanning / Docker Image Security (push) Has been cancelled
Security Scanning / Security Summary (push) Has been cancelled
CI/CD Pipeline / Go Tests (push) Has been cancelled
CI/CD Pipeline / Python Tests (push) Has been cancelled
CI/CD Pipeline / Website Tests (push) Has been cancelled
CI/CD Pipeline / Linting (push) Has been cancelled
CI/CD Pipeline / Security Scan (push) Has been cancelled
CI/CD Pipeline / Docker Build & Push (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / CI Summary (push) Has been cancelled
ci/woodpecker/manual/build-ci-image Pipeline was successful
ci/woodpecker/manual/main Pipeline failed
All services: admin-v2, studio-v2, website, ai-compliance-sdk, consent-service, klausur-service, voice-service, and infrastructure. Large PDFs and compiled binaries excluded via .gitignore.
This commit is contained in:
348
edu-search-service/internal/staff/staff_crawler_test.go
Normal file
348
edu-search-service/internal/staff/staff_crawler_test.go
Normal file
@@ -0,0 +1,348 @@
|
||||
package staff
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/breakpilot/edu-search-service/internal/database"
|
||||
)
|
||||
|
||||
func TestParseName_FullName_WithTitle(t *testing.T) {
|
||||
crawler := &StaffCrawler{}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
fullName string
|
||||
expectedFirst string
|
||||
expectedLast string
|
||||
expectedTitle bool
|
||||
}{
|
||||
{
|
||||
name: "Prof. Dr. with first and last name",
|
||||
fullName: "Prof. Dr. Hans Müller",
|
||||
expectedFirst: "Hans",
|
||||
expectedLast: "Müller",
|
||||
expectedTitle: true,
|
||||
},
|
||||
{
|
||||
name: "Dr. with first and last name",
|
||||
fullName: "Dr. Maria Schmidt",
|
||||
expectedFirst: "Maria",
|
||||
expectedLast: "Schmidt",
|
||||
expectedTitle: true,
|
||||
},
|
||||
{
|
||||
name: "Simple name without title",
|
||||
fullName: "Thomas Weber",
|
||||
expectedFirst: "Thomas",
|
||||
expectedLast: "Weber",
|
||||
expectedTitle: false,
|
||||
},
|
||||
{
|
||||
name: "Multiple first names",
|
||||
fullName: "Prof. Dr. Hans-Peter Meier",
|
||||
expectedFirst: "Hans-Peter",
|
||||
expectedLast: "Meier",
|
||||
expectedTitle: true,
|
||||
},
|
||||
{
|
||||
name: "Single name",
|
||||
fullName: "Müller",
|
||||
expectedFirst: "",
|
||||
expectedLast: "Müller",
|
||||
expectedTitle: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
person := &database.UniversityStaff{}
|
||||
crawler.parseName(tt.fullName, person)
|
||||
|
||||
firstName := ""
|
||||
if person.FirstName != nil {
|
||||
firstName = *person.FirstName
|
||||
}
|
||||
|
||||
if firstName != tt.expectedFirst {
|
||||
t.Errorf("First name: expected %q, got %q", tt.expectedFirst, firstName)
|
||||
}
|
||||
if person.LastName != tt.expectedLast {
|
||||
t.Errorf("Last name: expected %q, got %q", tt.expectedLast, person.LastName)
|
||||
}
|
||||
hasTitle := person.Title != nil && *person.Title != ""
|
||||
if hasTitle != tt.expectedTitle {
|
||||
t.Errorf("Has title: expected %v, got %v", tt.expectedTitle, hasTitle)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClassifyPosition_Professor(t *testing.T) {
|
||||
crawler := &StaffCrawler{}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
position string
|
||||
expected string
|
||||
}{
|
||||
{"Full Professor", "Professor für Informatik", "professor"},
|
||||
{"Prof abbreviation", "Prof. Dr. Müller", "professor"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := crawler.classifyPosition(tt.position)
|
||||
if result == nil {
|
||||
t.Errorf("Expected %q, got nil for position %q", tt.expected, tt.position)
|
||||
return
|
||||
}
|
||||
if *result != tt.expected {
|
||||
t.Errorf("Expected %q, got %q for position %q", tt.expected, *result, tt.position)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClassifyPosition_Postdoc(t *testing.T) {
|
||||
crawler := &StaffCrawler{}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
position string
|
||||
expected string
|
||||
}{
|
||||
{"Postdoc", "Postdoc in Machine Learning", "postdoc"},
|
||||
{"Post-Doc hyphenated", "Post-Doc", "postdoc"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := crawler.classifyPosition(tt.position)
|
||||
if result == nil {
|
||||
t.Errorf("Expected %q, got nil for position %q", tt.expected, tt.position)
|
||||
return
|
||||
}
|
||||
if *result != tt.expected {
|
||||
t.Errorf("Expected %q, got %q for position %q", tt.expected, *result, tt.position)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClassifyPosition_PhDStudent(t *testing.T) {
|
||||
crawler := &StaffCrawler{}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
position string
|
||||
expected string
|
||||
}{
|
||||
{"Doktorand", "Doktorand", "phd_student"},
|
||||
{"PhD Student", "PhD Student", "phd_student"},
|
||||
{"Promovend", "Promovend", "phd_student"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := crawler.classifyPosition(tt.position)
|
||||
if result == nil {
|
||||
t.Errorf("Expected %q, got nil for position %q", tt.expected, tt.position)
|
||||
return
|
||||
}
|
||||
if *result != tt.expected {
|
||||
t.Errorf("Expected %q, got %q for position %q", tt.expected, *result, tt.position)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClassifyPosition_Admin(t *testing.T) {
|
||||
crawler := &StaffCrawler{}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
position string
|
||||
expected string
|
||||
}{
|
||||
{"Sekretariat", "Sekretärin", "admin"},
|
||||
{"Verwaltung", "Verwaltung", "admin"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := crawler.classifyPosition(tt.position)
|
||||
if result == nil {
|
||||
t.Errorf("Expected %q, got nil for position %q", tt.expected, tt.position)
|
||||
return
|
||||
}
|
||||
if *result != tt.expected {
|
||||
t.Errorf("Expected %q, got %q for position %q", tt.expected, *result, tt.position)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClassifyPosition_Researcher(t *testing.T) {
|
||||
crawler := &StaffCrawler{}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
position string
|
||||
expected string
|
||||
}{
|
||||
{"Wissenschaftlicher Mitarbeiter", "Wissenschaftlicher Mitarbeiter", "researcher"},
|
||||
{"Researcher", "Senior Researcher", "researcher"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := crawler.classifyPosition(tt.position)
|
||||
if result == nil {
|
||||
t.Errorf("Expected %q, got nil for position %q", tt.expected, tt.position)
|
||||
return
|
||||
}
|
||||
if *result != tt.expected {
|
||||
t.Errorf("Expected %q, got %q for position %q", tt.expected, *result, tt.position)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClassifyPosition_Student(t *testing.T) {
|
||||
crawler := &StaffCrawler{}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
position string
|
||||
expected string
|
||||
}{
|
||||
{"Studentische Hilfskraft", "Studentische Hilfskraft", "student"},
|
||||
{"HiWi", "Student (HiWi)", "student"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := crawler.classifyPosition(tt.position)
|
||||
if result == nil {
|
||||
t.Errorf("Expected %q, got nil for position %q", tt.expected, tt.position)
|
||||
return
|
||||
}
|
||||
if *result != tt.expected {
|
||||
t.Errorf("Expected %q, got %q for position %q", tt.expected, *result, tt.position)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsProfessor_True(t *testing.T) {
|
||||
crawler := &StaffCrawler{}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
position string
|
||||
}{
|
||||
{"Professor keyword", "Professor für Mathematik"},
|
||||
{"Prof. abbreviation", "Prof. Dr. Müller"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := crawler.isProfessor(tt.position)
|
||||
if !result {
|
||||
t.Errorf("Expected true for position=%q", tt.position)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsProfessor_False(t *testing.T) {
|
||||
crawler := &StaffCrawler{}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
position string
|
||||
}{
|
||||
{"Dr. only", "Dr. Wissenschaftlicher Mitarbeiter"},
|
||||
{"Doktorand", "Doktorand"},
|
||||
{"Technical staff", "Laboringenieur"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := crawler.isProfessor(tt.position)
|
||||
if result {
|
||||
t.Errorf("Expected false for position=%q", tt.position)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLooksLikePosition_True(t *testing.T) {
|
||||
crawler := &StaffCrawler{}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
text string
|
||||
}{
|
||||
{"Professor", "Professor für Informatik"},
|
||||
{"Wissenschaftlicher Mitarbeiter", "Wissenschaftlicher Mitarbeiter"},
|
||||
{"Doktorand", "Doktorand"},
|
||||
{"Sekretär", "Sekretärin"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := crawler.looksLikePosition(tt.text)
|
||||
if !result {
|
||||
t.Errorf("Expected true for text=%q", tt.text)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLooksLikePosition_False(t *testing.T) {
|
||||
crawler := &StaffCrawler{}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
text string
|
||||
}{
|
||||
{"Name", "Hans Müller"},
|
||||
{"Email", "test@example.com"},
|
||||
{"Random text", "Room 123"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := crawler.looksLikePosition(tt.text)
|
||||
if result {
|
||||
t.Errorf("Expected false for text=%q", tt.text)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveURL(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
baseURL string
|
||||
href string
|
||||
expected string
|
||||
}{
|
||||
{"Absolute URL", "https://example.com", "https://other.com/page", "https://other.com/page"},
|
||||
{"Relative path", "https://example.com/team", "/person/123", "https://example.com/person/123"},
|
||||
{"Relative no slash", "https://example.com/team/", "member", "https://example.com/team/member"},
|
||||
{"Empty href", "https://example.com", "", ""},
|
||||
{"Root relative", "https://example.com/a/b/c", "/root", "https://example.com/root"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := resolveURL(tt.baseURL, tt.href)
|
||||
if result != tt.expected {
|
||||
t.Errorf("resolveURL(%q, %q) = %q, expected %q",
|
||||
tt.baseURL, tt.href, result, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user