internal/devserver/server_test.go

package devserver

import (
	"context"
	"io"
	"net/http"
	"net/http/httptest"
	"os"
	"path/filepath"
	"testing"
	"time"
)

func writeFile(t *testing.T, path, body string) {
	t.Helper()
	if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
		t.Fatal(err)
	}
	if err := os.WriteFile(path, []byte(body), 0o644); err != nil {
		t.Fatal(err)
	}
}

func TestServeStatic(t *testing.T) {
	t.Parallel()
	dir := t.TempDir()
	writeFile(t, filepath.Join(dir, "index.html"), "<h1>home</h1>")
	writeFile(t, filepath.Join(dir, "posts", "a.html"), "<h1>post a</h1>")

	cfg := DefaultConfig(dir)
	rr := httptest.NewRecorder()
	r := httptest.NewRequest(http.MethodGet, "/", nil)
	serveStatic(rr, r, cfg)
	if rr.Code != http.StatusOK {
		t.Errorf("code = %d", rr.Code)
	}
	body, _ := io.ReadAll(rr.Body)
	if string(body) != "<h1>home</h1>" {
		t.Errorf("body = %q", body)
	}
}

func TestServeStaticNotFound(t *testing.T) {
	t.Parallel()
	cfg := DefaultConfig(t.TempDir())
	rr := httptest.NewRecorder()
	r := httptest.NewRequest(http.MethodGet, "/missing.html", nil)
	serveStatic(rr, r, cfg)
	if rr.Code != http.StatusNotFound {
		t.Errorf("code = %d", rr.Code)
	}
}

func TestServerRunAndShutdown(t *testing.T) {
	t.Parallel()
	dir := t.TempDir()
	writeFile(t, filepath.Join(dir, "index.html"), "ok")
	cfg := DefaultConfig(dir)
	cfg.Addr = "127.0.0.1:0"
	s := New(cfg)
	ctx, cancel := context.WithCancel(context.Background())
	done := make(chan error, 1)
	go func() { done <- s.Run(ctx) }()
	time.Sleep(10 * time.Millisecond)
	cancel()
	select {
	case err := <-done:
		if err != nil {
			t.Errorf("Run returned: %v", err)
		}
	case <-time.After(2 * time.Second):
		t.Fatal("server did not shut down in time")
	}
}

func TestMimeTypeMapping(t *testing.T) {
	t.Parallel()
	t.Helper()
	if got := MimeType(".html"); got != "text/html; charset=utf-8" {
		t.Errorf(".html = %q", got)
	}
}