|
package main |
|
|
|
import ( |
|
"database/sql" |
|
"encoding/json" |
|
"fmt" |
|
"net/http" |
|
"net/http/httptest" |
|
"net/url" |
|
"os" |
|
"testing" |
|
"time" |
|
) |
|
|
|
// setupTestDB creates an in-memory SQLite database for testing. |
|
func setupTestDB(t *testing.T) *sql.DB { |
|
t.Helper() |
|
// Using in-memory database for tests |
|
db, err := sql.Open("sqlite3", ":memory:") |
|
if err != nil { |
|
t.Fatalf("Failed to open in-memory database: %v", err) |
|
} |
|
|
|
createTableSQL := `CREATE TABLE holidays ( |
|
id INTEGER PRIMARY KEY AUTOINCREMENT, |
|
date TEXT NOT NULL UNIQUE, |
|
description TEXT NOT NULL |
|
);` |
|
if _, err := db.Exec(createTableSQL); err != nil { |
|
t.Fatalf("Failed to create holidays table: %v", err) |
|
} |
|
|
|
return db |
|
} |
|
|
|
func TestScrapeHolidays(t *testing.T) { |
|
// 1. Setup mock HTTP server |
|
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
|
w.Header().Set("Content-Type", "application/json") |
|
var holidays []FebrabanHoliday |
|
// Respond based on the year requested |
|
year := r.URL.Query().Get("ano") |
|
if year == "2026" { |
|
holidays = []FebrabanHoliday{ |
|
{DiaMes: "01 de janeiro", NomeFeriado: "Confraternização Universal"}, |
|
{DiaMes: "25 de dezembro", NomeFeriado: "Natal"}, |
|
} |
|
} else if year == "2027" { |
|
holidays = []FebrabanHoliday{ |
|
{DiaMes: "01 de janeiro", NomeFeriado: "Confraternização Universal 2027"}, |
|
} |
|
} |
|
json.NewEncoder(w).Encode(holidays) |
|
})) |
|
defer mockServer.Close() |
|
|
|
// 2. Setup test database |
|
tempDB := setupTestDB(t) |
|
defer tempDB.Close() |
|
|
|
// Set the global db variable to the test database and restore it after the test |
|
originalDB := db |
|
db = tempDB |
|
defer func() { db = originalDB }() |
|
|
|
// 3. Override doHTTPRequest and timeNow to point to the mock server and mock time |
|
originalDoHTTPRequest := doHTTPRequest |
|
defer func() { doHTTPRequest = originalDoHTTPRequest }() |
|
doHTTPRequest = func(requestURL string) ([]byte, error) { |
|
// Parse the requestURL to extract the 'ano' parameter |
|
parsedURL, err := url.Parse(requestURL) |
|
if err != nil { |
|
return nil, fmt.Errorf("failed to parse URL: %w", err) |
|
} |
|
year := parsedURL.Query().Get("ano") |
|
|
|
// Construct a mock request to our httptest server |
|
mockReq := httptest.NewRequest("GET", mockServer.URL+"?ano="+year, nil) |
|
// Since httptest.NewServer creates a simple handler, we need to manually call it |
|
// And capture the response |
|
rr := httptest.NewRecorder() |
|
mockServer.Config.Handler.ServeHTTP(rr, mockReq) |
|
|
|
if rr.Code != http.StatusOK { |
|
return nil, fmt.Errorf("mock server returned non-OK status: %d", rr.Code) |
|
} |
|
return rr.Body.Bytes(), nil |
|
} |
|
|
|
originalTimeNow := timeNow |
|
defer func() { timeNow = originalTimeNow }() |
|
timeNow = func() time.Time { return time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC) } |
|
|
|
// 4. Run the scraper |
|
scrapeHolidays() |
|
|
|
// 5. Verify the results in the database |
|
rows, err := db.Query("SELECT date, description FROM holidays") |
|
if err != nil { |
|
t.Fatalf("Failed to query test database: %v", err) |
|
} |
|
defer rows.Close() |
|
|
|
var holidays []Holiday |
|
for rows.Next() { |
|
var h Holiday |
|
if err := rows.Scan(&h.Date, &h.Description); err != nil { |
|
t.Fatalf("Failed to scan row: %v", err) |
|
} |
|
holidays = append(holidays, h) |
|
} |
|
|
|
// We expect 3 holidays: two from 2026 (Confraternização Universal, Natal) and one from 2027. |
|
expectedCount := 3 |
|
if len(holidays) != expectedCount { |
|
t.Errorf("Expected %d holidays to be inserted, but got %d", expectedCount, len(holidays)) |
|
} |
|
|
|
// Check for specific holidays |
|
expectedHolidays := map[string]string{ |
|
"2026-01-01": "Confraternização Universal", |
|
"2026-12-25": "Natal", |
|
"2027-01-01": "Confraternização Universal 2027", |
|
} |
|
|
|
for _, h := range holidays { |
|
expectedDesc, found := expectedHolidays[h.Date] |
|
if !found { |
|
t.Errorf("Found unexpected holiday: %v", h) |
|
} |
|
if expectedDesc != h.Description { |
|
t.Errorf("For date %s, expected description '%s', got '%s'", h.Date, expectedDesc, h.Description) |
|
} |
|
delete(expectedHolidays, h.Date) |
|
} |
|
|
|
if len(expectedHolidays) > 0 { |
|
t.Errorf("Missing expected holidays: %v", expectedHolidays) |
|
} |
|
} |
|
|
|
func TestGetHolidaysHandler(t *testing.T) { |
|
// 1. Setup test database and data |
|
tempDB := setupTestDB(t) |
|
defer tempDB.Close() |
|
|
|
// Set the global db variable for the handler and restore it after the test |
|
originalDB := db |
|
db = tempDB |
|
defer func() { db = originalDB }() |
|
|
|
// Insert test data |
|
testHolidays := []Holiday{ |
|
{Date: "2026-01-01", Description: "New Year"}, |
|
{Date: "2026-07-04", Description: "Independence Day"}, |
|
} |
|
for _, h := range testHolidays { |
|
_, err := db.Exec("INSERT INTO holidays (date, description) VALUES (?, ?)", h.Date, h.Description) |
|
if err != nil { |
|
t.Fatalf("Failed to insert test data: %v", err) |
|
} |
|
} |
|
|
|
// 2. Create a request and response recorder |
|
req := httptest.NewRequest("GET", "/holidays", nil) |
|
rr := httptest.NewRecorder() |
|
|
|
// 3. Call the handler |
|
getHolidaysHandler(rr, req) |
|
|
|
// 4. Check the results |
|
if status := rr.Code; status != http.StatusOK { |
|
t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusOK) |
|
} |
|
|
|
// Check the response body |
|
var returnedHolidays []Holiday |
|
if err := json.NewDecoder(rr.Body).Decode(&returnedHolidays); err != nil { |
|
t.Fatalf("Failed to decode response body: %v", err) |
|
} |
|
|
|
if len(returnedHolidays) != len(testHolidays) { |
|
t.Errorf("Expected %d holidays, but got %d", len(testHolidays), len(returnedHolidays)) |
|
} |
|
|
|
// Sort for consistent comparison, as database order isn't guaranteed without ORDER BY |
|
// In getHolidaysHandler we use ORDER BY, so it should be consistent |
|
if returnedHolidays[0].Description != "New Year" { |
|
t.Errorf("Expected first holiday to be 'New Year', but got '%s'", returnedHolidays[0].Description) |
|
} |
|
} |
|
|
|
// Global variable to allow mocking time.Now() - not needed in main_test.go as it's defined in main.go |
|
// var timeNow = time.Now |
|
|
|
func TestMain(m *testing.M) { |
|
// This is a good place for setup/teardown if needed, |
|
// but our tests are self-contained. |
|
os.Exit(m.Run()) |
|
} |