readysite / pkg / application / app_test.go
4.6 KB
app_test.go
package application

import (
	"net/http"
	"net/http/httptest"
	"testing"
)

func TestAppServeCreatesView(t *testing.T) {
	app := &App{
		funcs:       make(map[string]any),
		viewsFS:     testViews,
		controllers: make(map[string]Controller),
	}
	app.base = app.parseBaseTemplates(testViews)

	view := app.Serve("pages/home.html", nil)

	if view == nil {
		t.Fatal("expected view to be created")
	}
	if view.App != app {
		t.Error("expected view to reference app")
	}
	if view.template != "pages/home.html" {
		t.Errorf("expected template 'pages/home.html', got %q", view.template)
	}
}

func TestAppMethod(t *testing.T) {
	app := &App{}

	ctrl := &methodTestController{}
	handler := app.Method(ctrl, "DoSomething", nil)

	req := httptest.NewRequest(http.MethodPost, "/", nil)
	rec := httptest.NewRecorder()

	handler.ServeHTTP(rec, req)

	if !ctrl.called {
		t.Error("expected method to be called")
	}
	if rec.Code != http.StatusOK {
		t.Errorf("expected status 200, got %d", rec.Code)
	}
}

func TestAppMethodWithBouncer(t *testing.T) {
	app := &App{}

	ctrl := &methodTestController{}
	bouncer := func(app *App, w http.ResponseWriter, r *http.Request) bool {
		http.Error(w, "Forbidden", http.StatusForbidden)
		return false
	}

	handler := app.Method(ctrl, "DoSomething", bouncer)

	req := httptest.NewRequest(http.MethodPost, "/", nil)
	rec := httptest.NewRecorder()

	handler.ServeHTTP(rec, req)

	if ctrl.called {
		t.Error("expected method NOT to be called when bouncer blocks")
	}
	if rec.Code != http.StatusForbidden {
		t.Errorf("expected status 403, got %d", rec.Code)
	}
}

func TestAppMethodNotFound(t *testing.T) {
	app := &App{}

	ctrl := &methodTestController{}
	handler := app.Method(ctrl, "NonexistentMethod", nil)

	req := httptest.NewRequest(http.MethodPost, "/", nil)
	rec := httptest.NewRecorder()

	handler.ServeHTTP(rec, req)

	if rec.Code != http.StatusInternalServerError {
		t.Errorf("expected status 500, got %d", rec.Code)
	}
}

// TestBuildTemplateData removed - controllers are now injected as template functions

func TestTemplateFuncs(t *testing.T) {
	app := &App{
		funcs: map[string]any{
			"custom": func() string { return "custom" },
		},
	}

	funcs := app.templateFuncs()

	// Should have built-in dict function
	if _, ok := funcs["dict"]; !ok {
		t.Error("expected 'dict' function")
	}

	// Should have custom function
	if _, ok := funcs["custom"]; !ok {
		t.Error("expected 'custom' function")
	}
}

func TestTemplateFuncsDict(t *testing.T) {
	app := &App{funcs: make(map[string]any)}
	funcs := app.templateFuncs()

	dictFn := funcs["dict"].(func(...any) map[string]any)

	// Valid dict
	result := dictFn("a", 1, "b", 2)
	if result["a"] != 1 || result["b"] != 2 {
		t.Errorf("expected dict with a=1, b=2, got %v", result)
	}

	// Odd number of args returns nil
	result = dictFn("a", 1, "b")
	if result != nil {
		t.Error("expected nil for odd number of args")
	}

	// Non-string key is skipped
	result = dictFn(123, "value", "valid", "ok")
	if _, ok := result[""]; ok {
		t.Error("expected non-string key to be skipped")
	}
	if result["valid"] != "ok" {
		t.Error("expected valid key to work")
	}
}

// Test helpers

type methodTestController struct {
	called bool
}

func (c *methodTestController) DoSomething(w http.ResponseWriter, r *http.Request) {
	c.called = true
	w.WriteHeader(http.StatusOK)
}

type templateDataTestController struct {
	BaseController
	name string
}

func (c *templateDataTestController) Handle(r *http.Request) Controller {
	return c
}

// Emailer tests

type testEmailer struct {
	lastTo       string
	lastSubject  string
	lastTemplate string
	lastData     map[string]any
}

func (e *testEmailer) Send(to, subject, templateName string, data map[string]any) error {
	e.lastTo = to
	e.lastSubject = subject
	e.lastTemplate = templateName
	e.lastData = data
	return nil
}

func TestEmailerInterface(t *testing.T) {
	emailer := &testEmailer{}

	err := emailer.Send("user@example.com", "Hello", "welcome.html", map[string]any{"name": "Alice"})
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	if emailer.lastTo != "user@example.com" {
		t.Errorf("expected to 'user@example.com', got %q", emailer.lastTo)
	}
	if emailer.lastSubject != "Hello" {
		t.Errorf("expected subject 'Hello', got %q", emailer.lastSubject)
	}
	if emailer.lastTemplate != "welcome.html" {
		t.Errorf("expected template 'welcome.html', got %q", emailer.lastTemplate)
	}
	if emailer.lastData["name"] != "Alice" {
		t.Errorf("expected data name 'Alice', got %v", emailer.lastData["name"])
	}
}

func TestAppWithEmailer(t *testing.T) {
	emailer := &testEmailer{}
	app := &App{}

	opt := WithEmailer(emailer)
	opt(app)

	if app.Emailer != emailer {
		t.Error("expected emailer to be set on app")
	}
}
← Back