Update tests.

This commit is contained in:
2025-12-11 09:45:49 +00:00
parent 7ff6df70ee
commit 73ca73f9fc
4 changed files with 827 additions and 16 deletions
+495
View File
@@ -381,3 +381,498 @@ func TestParseRepoName(t *testing.T) {
assert.Equal(t, tt.expectedName, name, "name mismatch for %s", tt.fullName)
}
}
func TestSetUserProfiles(t *testing.T) {
t.Parallel()
cfg := config.DefaultConfig()
agg := New(cfg)
profiles := map[string]UserProfile{
"user1": {Login: "user1", Email: "user1@example.com", Name: "User One", ID: 12345},
"user2": {Login: "user2", Email: "user2@example.com", Name: "User Two", ID: 67890},
}
agg.SetUserProfiles(profiles)
assert.Equal(t, profiles, agg.userProfiles)
}
func TestNormalizeForComparison(t *testing.T) {
t.Parallel()
tests := []struct {
input string
expected string
}{
{"John Doe", "johndoe"},
{"john-doe", "johndoe"},
{"john_doe", "johndoe"},
{"john.doe", "johndoe"},
{"JOHN DOE", "johndoe"},
{"John123Doe", "johndoe"},
{"123", ""},
{"", ""},
{"ABC xyz 123", "abcxyz"},
}
for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
result := normalizeForComparison(tt.input)
assert.Equal(t, tt.expected, result)
})
}
}
func TestBuildEmailToLoginMapping_NoReplyEmails(t *testing.T) {
t.Parallel()
data := &models.RawData{
Commits: []models.Commit{
{
SHA: "abc123",
Author: models.Author{Login: "", Email: "12345+johndoe@users.noreply.github.com", Name: "John Doe"},
Repository: "owner/repo",
},
},
PullRequests: []models.PullRequest{
{
Number: 1,
Author: models.Author{Login: "johndoe", ID: 12345},
},
},
}
mapping := buildEmailToLoginMapping(data, nil)
// Should map via the ID
assert.Equal(t, "johndoe", mapping["12345+johndoe@users.noreply.github.com"])
}
func TestBuildEmailToLoginMapping_ProfileEmails(t *testing.T) {
t.Parallel()
data := &models.RawData{
Commits: []models.Commit{
{
SHA: "abc123",
Author: models.Author{Login: "", Email: "john@company.com", Name: "John Doe"},
Repository: "owner/repo",
},
},
}
profiles := map[string]UserProfile{
"johndoe": {Login: "johndoe", Email: "john@company.com", Name: "John Doe", ID: 12345},
}
mapping := buildEmailToLoginMapping(data, profiles)
// Should map via profile email
assert.Equal(t, "johndoe", mapping["john@company.com"])
}
func TestBuildEmailToLoginMapping_NameMatching(t *testing.T) {
t.Parallel()
data := &models.RawData{
Commits: []models.Commit{
{
SHA: "abc123",
Author: models.Author{Login: "", Email: "john@somewhere.com", Name: "John Doe"},
Repository: "owner/repo",
},
},
PullRequests: []models.PullRequest{
{
Number: 1,
Author: models.Author{Login: "johndoe", Name: "John Doe"},
},
},
}
mapping := buildEmailToLoginMapping(data, nil)
// Should map via name matching
assert.Equal(t, "johndoe", mapping["john@somewhere.com"])
}
func TestCalculateWorkWeekStreak(t *testing.T) {
t.Parallel()
tests := []struct {
name string
dates map[string]bool
expectedStreak int
}{
{
name: "empty dates",
dates: map[string]bool{},
expectedStreak: 0,
},
{
name: "single weekday",
dates: map[string]bool{
"2024-01-08": true, // Monday
},
expectedStreak: 1,
},
{
name: "consecutive weekdays",
dates: map[string]bool{
"2024-01-08": true, // Monday
"2024-01-09": true, // Tuesday
"2024-01-10": true, // Wednesday
},
expectedStreak: 3,
},
{
name: "weekdays with weekend gap",
dates: map[string]bool{
"2024-01-12": true, // Friday
"2024-01-15": true, // Monday
"2024-01-16": true, // Tuesday
},
expectedStreak: 3, // Weekend doesn't break streak
},
{
name: "broken streak on weekday",
dates: map[string]bool{
"2024-01-08": true, // Monday
"2024-01-10": true, // Wednesday (skipped Tuesday)
},
expectedStreak: 1,
},
{
name: "weekend only",
dates: map[string]bool{
"2024-01-13": true, // Saturday
"2024-01-14": true, // Sunday
},
expectedStreak: 0, // Weekends don't count
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := calculateWorkWeekStreak(tt.dates)
assert.Equal(t, tt.expectedStreak, result)
})
}
}
func TestCalculateWorkWeekStreak_LongestStreak(t *testing.T) {
t.Parallel()
// Multiple streaks - should return longest
dates := map[string]bool{
"2024-01-08": true, // Monday
"2024-01-09": true, // Tuesday
"2024-01-15": true, // Monday (gap - breaks streak)
"2024-01-16": true, // Tuesday
"2024-01-17": true, // Wednesday
"2024-01-18": true, // Thursday
"2024-01-19": true, // Friday
"2024-01-22": true, // Monday (weekend doesn't break)
}
result := calculateWorkWeekStreak(dates)
assert.Equal(t, 6, result) // Mon-Fri + Mon = 6 weekdays in a row
}
func TestAggregator_OutOfHoursTracking(t *testing.T) {
t.Parallel()
cfg := config.DefaultConfig()
agg := New(cfg)
data := &models.RawData{
Commits: []models.Commit{
{
SHA: "abc123",
Author: models.Author{Login: "user1"},
Date: time.Date(2024, 1, 15, 7, 0, 0, 0, time.UTC), // 7am - before 9am
Repository: "owner/repo",
},
{
SHA: "def456",
Author: models.Author{Login: "user1"},
Date: time.Date(2024, 1, 15, 10, 0, 0, 0, time.UTC), // 10am - work hours
Repository: "owner/repo",
},
{
SHA: "ghi789",
Author: models.Author{Login: "user1"},
Date: time.Date(2024, 1, 15, 18, 0, 0, 0, time.UTC), // 6pm - after 5pm
Repository: "owner/repo",
},
},
}
dateRange := &config.ParsedDateRange{}
metrics, err := agg.Aggregate(data, dateRange)
require.NoError(t, err)
require.Len(t, metrics.Repositories, 1)
require.Len(t, metrics.Repositories[0].Contributors, 1)
contrib := metrics.Repositories[0].Contributors[0]
assert.Equal(t, 2, contrib.OutOfHoursCount) // 7am and 6pm are out of hours
}
func TestAggregator_WorkWeekStreakTracking(t *testing.T) {
t.Parallel()
cfg := config.DefaultConfig()
agg := New(cfg)
data := &models.RawData{
Commits: []models.Commit{
{
SHA: "abc123",
Author: models.Author{Login: "user1"},
Date: time.Date(2024, 1, 8, 10, 0, 0, 0, time.UTC), // Monday
Repository: "owner/repo",
},
{
SHA: "def456",
Author: models.Author{Login: "user1"},
Date: time.Date(2024, 1, 9, 10, 0, 0, 0, time.UTC), // Tuesday
Repository: "owner/repo",
},
{
SHA: "ghi789",
Author: models.Author{Login: "user1"},
Date: time.Date(2024, 1, 10, 10, 0, 0, 0, time.UTC), // Wednesday
Repository: "owner/repo",
},
},
}
dateRange := &config.ParsedDateRange{}
metrics, err := agg.Aggregate(data, dateRange)
require.NoError(t, err)
require.Len(t, metrics.Repositories, 1)
require.Len(t, metrics.Repositories[0].Contributors, 1)
contrib := metrics.Repositories[0].Contributors[0]
assert.Equal(t, 3, contrib.WorkWeekStreak)
}
// Note: Bot filtering tests removed - bot filtering happens in app.go before data reaches aggregator
// The aggregator receives already filtered data
func TestAggregator_EarlyBirdTracking(t *testing.T) {
t.Parallel()
cfg := config.DefaultConfig()
agg := New(cfg)
data := &models.RawData{
Commits: []models.Commit{
{
SHA: "abc123",
Author: models.Author{Login: "user1"},
Date: time.Date(2024, 1, 15, 6, 0, 0, 0, time.UTC), // 6am
Repository: "owner/repo",
},
{
SHA: "def456",
Author: models.Author{Login: "user1"},
Date: time.Date(2024, 1, 16, 8, 30, 0, 0, time.UTC), // 8:30am
Repository: "owner/repo",
},
},
}
dateRange := &config.ParsedDateRange{}
metrics, err := agg.Aggregate(data, dateRange)
require.NoError(t, err)
require.Len(t, metrics.Repositories, 1)
require.Len(t, metrics.Repositories[0].Contributors, 1)
contrib := metrics.Repositories[0].Contributors[0]
assert.Equal(t, 2, contrib.EarlyBirdCount) // Both before 9am
}
func TestAggregator_NightOwlTracking(t *testing.T) {
t.Parallel()
cfg := config.DefaultConfig()
agg := New(cfg)
data := &models.RawData{
Commits: []models.Commit{
{
SHA: "abc123",
Author: models.Author{Login: "user1"},
Date: time.Date(2024, 1, 15, 21, 0, 0, 0, time.UTC), // 9pm
Repository: "owner/repo",
},
{
SHA: "def456",
Author: models.Author{Login: "user1"},
Date: time.Date(2024, 1, 16, 23, 30, 0, 0, time.UTC), // 11:30pm
Repository: "owner/repo",
},
},
}
dateRange := &config.ParsedDateRange{}
metrics, err := agg.Aggregate(data, dateRange)
require.NoError(t, err)
require.Len(t, metrics.Repositories, 1)
require.Len(t, metrics.Repositories[0].Contributors, 1)
contrib := metrics.Repositories[0].Contributors[0]
assert.Equal(t, 2, contrib.NightOwlCount) // Both after 9pm
}
func TestAggregator_MidnightTracking(t *testing.T) {
t.Parallel()
cfg := config.DefaultConfig()
agg := New(cfg)
data := &models.RawData{
Commits: []models.Commit{
{
SHA: "abc123",
Author: models.Author{Login: "user1"},
Date: time.Date(2024, 1, 15, 0, 30, 0, 0, time.UTC), // 12:30am
Repository: "owner/repo",
},
{
SHA: "def456",
Author: models.Author{Login: "user1"},
Date: time.Date(2024, 1, 16, 3, 0, 0, 0, time.UTC), // 3am
Repository: "owner/repo",
},
},
}
dateRange := &config.ParsedDateRange{}
metrics, err := agg.Aggregate(data, dateRange)
require.NoError(t, err)
require.Len(t, metrics.Repositories, 1)
require.Len(t, metrics.Repositories[0].Contributors, 1)
contrib := metrics.Repositories[0].Contributors[0]
assert.Equal(t, 2, contrib.MidnightCount) // Both between 0-4am
}
func TestAggregator_WeekendWarriorTracking(t *testing.T) {
t.Parallel()
cfg := config.DefaultConfig()
agg := New(cfg)
data := &models.RawData{
Commits: []models.Commit{
{
SHA: "abc123",
Author: models.Author{Login: "user1"},
Date: time.Date(2024, 1, 13, 10, 0, 0, 0, time.UTC), // Saturday
Repository: "owner/repo",
},
{
SHA: "def456",
Author: models.Author{Login: "user1"},
Date: time.Date(2024, 1, 14, 15, 0, 0, 0, time.UTC), // Sunday
Repository: "owner/repo",
},
{
SHA: "ghi789",
Author: models.Author{Login: "user1"},
Date: time.Date(2024, 1, 15, 10, 0, 0, 0, time.UTC), // Monday (not weekend)
Repository: "owner/repo",
},
},
}
dateRange := &config.ParsedDateRange{}
metrics, err := agg.Aggregate(data, dateRange)
require.NoError(t, err)
require.Len(t, metrics.Repositories, 1)
require.Len(t, metrics.Repositories[0].Contributors, 1)
contrib := metrics.Repositories[0].Contributors[0]
assert.Equal(t, 2, contrib.WeekendWarrior) // Saturday and Sunday only
}
func TestAggregator_MultiRepoContributions(t *testing.T) {
t.Parallel()
cfg := config.DefaultConfig()
agg := New(cfg)
data := &models.RawData{
Commits: []models.Commit{
{
SHA: "abc123",
Author: models.Author{Login: "user1"},
Repository: "owner/repo1",
},
{
SHA: "def456",
Author: models.Author{Login: "user1"},
Repository: "owner/repo2",
},
{
SHA: "ghi789",
Author: models.Author{Login: "user1"},
Repository: "owner/repo3",
},
},
}
dateRange := &config.ParsedDateRange{}
metrics, err := agg.Aggregate(data, dateRange)
require.NoError(t, err)
// MultiRepoCount is tracked in the global leaderboard entries, not repo contributors
// The leaderboard entry should show 3 repos for user1
require.Len(t, metrics.Repositories, 3)
assert.Equal(t, 1, metrics.TotalContributors)
}
func TestBuildEmailToLoginMapping_EmptyData(t *testing.T) {
t.Parallel()
data := &models.RawData{}
mapping := buildEmailToLoginMapping(data, nil)
assert.Empty(t, mapping)
}
func TestBuildEmailToLoginMapping_NoReplyEmailWithoutID(t *testing.T) {
t.Parallel()
// When the email is just "username@users.noreply.github.com" (without ID+),
// the mapping only happens if there's a matching PR author (via name matching later)
// The direct extraction only works for "ID+username@" format
data := &models.RawData{
Commits: []models.Commit{
{
SHA: "abc123",
Author: models.Author{Login: "", Email: "johndoe@users.noreply.github.com", Name: "John Doe"},
Repository: "owner/repo",
},
},
// Add a PR to enable name matching
PullRequests: []models.PullRequest{
{
Number: 1,
Author: models.Author{Login: "johndoe", Name: "John Doe"},
},
},
}
mapping := buildEmailToLoginMapping(data, nil)
// Should map via name matching since there's a PR author with the same name
assert.Equal(t, "johndoe", mapping["johndoe@users.noreply.github.com"])
}
@@ -667,6 +667,76 @@ func TestCalculator_NoReviewsNoBonus(t *testing.T) {
assert.Equal(t, 0, contributor.Score.Breakdown.ResponseBonus)
}
func TestCalculator_OutOfHoursScoring(t *testing.T) {
t.Parallel()
cfg := config.DefaultConfig()
cfg.Scoring.Enabled = true
cfg.Scoring.Points = config.PointsConfig{
Commit: 10,
OutOfHours: 5, // 5 points per out-of-hours commit
}
calc := NewCalculator(cfg)
metrics := &models.GlobalMetrics{
Repositories: []models.RepositoryMetrics{
{
FullName: "owner/repo",
Contributors: []models.ContributorMetrics{
{
Login: "night-owl",
CommitCount: 10,
OutOfHoursCount: 8, // 8 commits outside 9am-5pm
RepositoriesContributed: []string{"owner/repo"},
},
},
},
},
}
result := calc.Calculate(metrics)
contributor := result.Repositories[0].Contributors[0]
// Commits: 10 * 10 = 100
// OutOfHours: 8 * 5 = 40
// Total: 140
assert.Equal(t, 100, contributor.Score.Breakdown.Commits)
assert.Equal(t, 40, contributor.Score.Breakdown.OutOfHours)
assert.Equal(t, 140, contributor.Score.Total)
}
func TestCalculator_WorkWeekStreakAchievement(t *testing.T) {
t.Parallel()
cfg := config.DefaultConfig()
cfg.Scoring.Enabled = true
// Achievements are now hardcoded
calc := NewCalculator(cfg)
metrics := &models.GlobalMetrics{
Repositories: []models.RepositoryMetrics{
{
FullName: "owner/repo",
Contributors: []models.ContributorMetrics{
{
Login: "consistent-worker",
CommitCount: 20,
WorkWeekStreak: 5, // 5-day work week streak
RepositoriesContributed: []string{"owner/repo"},
},
},
},
},
}
result := calc.Calculate(metrics)
contributor := result.Repositories[0].Contributors[0]
// Should have earned work week streak achievements for 3 and 5 days
assert.Contains(t, contributor.Achievements, "workweek-3")
assert.Contains(t, contributor.Achievements, "workweek-5")
}
func TestContains(t *testing.T) {
t.Parallel()
+34 -16
View File
@@ -24,26 +24,13 @@ func New(directory, port string) *Server {
// Start starts the HTTP server
func (s *Server) Start() error {
// Check if directory exists
if _, err := os.Stat(s.directory); os.IsNotExist(err) {
return fmt.Errorf("directory does not exist: %s", s.directory)
}
// Get absolute path
absPath, err := filepath.Abs(s.directory)
handler, err := s.CreateHandler()
if err != nil {
return fmt.Errorf("failed to get absolute path: %w", err)
return err
}
// Create file server with directory listing disabled for security
fs := http.FileServer(http.Dir(absPath))
// Wrap with middleware
handler := s.loggingMiddleware(s.cacheMiddleware(fs))
addr := fmt.Sprintf(":%s", s.port)
srv := &http.Server{
Addr: addr,
Addr: s.GetAddress(),
Handler: handler,
ReadTimeout: 15 * time.Second,
ReadHeaderTimeout: 15 * time.Second,
@@ -75,3 +62,34 @@ func (s *Server) cacheMiddleware(next http.Handler) http.Handler {
next.ServeHTTP(w, r)
})
}
// CreateHandler creates and returns the HTTP handler without starting the server.
// This is useful for testing and for embedding the server in other applications.
func (s *Server) CreateHandler() (http.Handler, error) {
// Check if directory exists
if _, err := os.Stat(s.directory); os.IsNotExist(err) {
return nil, fmt.Errorf("directory does not exist: %s", s.directory)
}
// Get absolute path
absPath, err := filepath.Abs(s.directory)
if err != nil {
return nil, fmt.Errorf("failed to get absolute path: %w", err)
}
// Create file server with directory listing disabled for security
fs := http.FileServer(http.Dir(absPath))
// Wrap with middleware
return s.loggingMiddleware(s.cacheMiddleware(fs)), nil
}
// GetAddress returns the server address in the format :port
func (s *Server) GetAddress() string {
return fmt.Sprintf(":%s", s.port)
}
// GetDirectory returns the directory being served
func (s *Server) GetDirectory() string {
return s.directory
}
+228
View File
@@ -207,3 +207,231 @@ func TestServer_ServesIndexHtml(t *testing.T) {
body, _ := io.ReadAll(resp.Body)
assert.Contains(t, string(body), "Test Page")
}
func TestServer_CreateHandler(t *testing.T) {
tempDir := t.TempDir()
// Create an index.html
indexFile := filepath.Join(tempDir, "index.html")
err := os.WriteFile(indexFile, []byte("<html><body>Handler Test</body></html>"), 0644)
require.NoError(t, err)
s := New(tempDir, "8080")
handler, err := s.CreateHandler()
require.NoError(t, err)
ts := httptest.NewServer(handler)
defer ts.Close()
resp, err := http.Get(ts.URL + "/")
require.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
body, _ := io.ReadAll(resp.Body)
assert.Contains(t, string(body), "Handler Test")
// Check middleware headers are applied
assert.Equal(t, "no-cache, no-store, must-revalidate", resp.Header.Get("Cache-Control"))
assert.Equal(t, "*", resp.Header.Get("Access-Control-Allow-Origin"))
}
func TestServer_CreateHandlerWithNonExistentDirectory(t *testing.T) {
t.Parallel()
s := New("/this/directory/does/not/exist", "8080")
handler, err := s.CreateHandler()
assert.Error(t, err)
assert.Nil(t, handler)
assert.Contains(t, err.Error(), "directory does not exist")
}
func TestServer_GetAddress(t *testing.T) {
t.Parallel()
tests := []struct {
name string
port string
expected string
}{
{"standard port", "8080", ":8080"},
{"different port", "3000", ":3000"},
{"port 0 for random", "0", ":0"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := New(".", tt.port)
assert.Equal(t, tt.expected, s.GetAddress())
})
}
}
func TestServer_GetDirectory(t *testing.T) {
t.Parallel()
s := New("/some/path", "8080")
assert.Equal(t, "/some/path", s.GetDirectory())
}
func TestServer_ServesJSONWithCorrectContentType(t *testing.T) {
tempDir := t.TempDir()
// Create a JSON file
jsonFile := filepath.Join(tempDir, "data.json")
err := os.WriteFile(jsonFile, []byte(`{"status": "ok"}`), 0644)
require.NoError(t, err)
s := New(tempDir, "0")
handler, err := s.CreateHandler()
require.NoError(t, err)
ts := httptest.NewServer(handler)
defer ts.Close()
resp, err := http.Get(ts.URL + "/data.json")
require.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
// Check content type is JSON
contentType := resp.Header.Get("Content-Type")
assert.Contains(t, contentType, "application/json")
}
func TestServer_ServesHTMLWithCorrectContentType(t *testing.T) {
tempDir := t.TempDir()
// Create an HTML file
htmlFile := filepath.Join(tempDir, "page.html")
err := os.WriteFile(htmlFile, []byte("<html><body>HTML Page</body></html>"), 0644)
require.NoError(t, err)
s := New(tempDir, "0")
handler, err := s.CreateHandler()
require.NoError(t, err)
ts := httptest.NewServer(handler)
defer ts.Close()
resp, err := http.Get(ts.URL + "/page.html")
require.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
// Check content type is HTML
contentType := resp.Header.Get("Content-Type")
assert.Contains(t, contentType, "text/html")
}
func TestServer_CORSHeaders(t *testing.T) {
tempDir := t.TempDir()
// Create a test file
testFile := filepath.Join(tempDir, "test.txt")
err := os.WriteFile(testFile, []byte("test content"), 0644)
require.NoError(t, err)
s := New(tempDir, "0")
handler, err := s.CreateHandler()
require.NoError(t, err)
ts := httptest.NewServer(handler)
defer ts.Close()
resp, err := http.Get(ts.URL + "/test.txt")
require.NoError(t, err)
defer resp.Body.Close()
// Check CORS header
assert.Equal(t, "*", resp.Header.Get("Access-Control-Allow-Origin"))
}
func TestServer_CacheDisabledHeaders(t *testing.T) {
tempDir := t.TempDir()
// Create a test file
testFile := filepath.Join(tempDir, "test.txt")
err := os.WriteFile(testFile, []byte("test content"), 0644)
require.NoError(t, err)
s := New(tempDir, "0")
handler, err := s.CreateHandler()
require.NoError(t, err)
ts := httptest.NewServer(handler)
defer ts.Close()
resp, err := http.Get(ts.URL + "/test.txt")
require.NoError(t, err)
defer resp.Body.Close()
// Check cache headers are disabled for development
assert.Equal(t, "no-cache, no-store, must-revalidate", resp.Header.Get("Cache-Control"))
assert.Equal(t, "no-cache", resp.Header.Get("Pragma"))
assert.Equal(t, "0", resp.Header.Get("Expires"))
}
func TestServer_LoggingMiddlewareWithDifferentMethods(t *testing.T) {
t.Parallel()
s := New(".", "8080")
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
wrapped := s.loggingMiddleware(handler)
methods := []string{"GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS"}
for _, method := range methods {
t.Run(method, func(t *testing.T) {
req := httptest.NewRequest(method, "/test-path", nil)
rr := httptest.NewRecorder()
wrapped.ServeHTTP(rr, req)
assert.Equal(t, http.StatusOK, rr.Code)
})
}
}
func TestServer_CacheMiddlewarePreservesResponseBody(t *testing.T) {
t.Parallel()
s := New(".", "8080")
expectedBody := "This is the response body content"
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte(expectedBody))
})
wrapped := s.cacheMiddleware(handler)
req := httptest.NewRequest("GET", "/test", nil)
rr := httptest.NewRecorder()
wrapped.ServeHTTP(rr, req)
body, _ := io.ReadAll(rr.Body)
assert.Equal(t, expectedBody, string(body))
}
func TestNew_WithEmptyValues(t *testing.T) {
t.Parallel()
s := New("", "")
assert.Equal(t, "", s.directory)
assert.Equal(t, "", s.port)
}
func TestNew_WithSpecialCharactersInPath(t *testing.T) {
t.Parallel()
path := "/path/with spaces/and-dashes/and_underscores"
s := New(path, "8080")
assert.Equal(t, path, s.directory)
}