diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..6e4bd87 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,26 @@ +# This workflow will build a golang project +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go + +name: Go + +on: + pull_request: + branches: [ "main" ] + +jobs: + + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: '1.24' + + - name: Build + run: go build -v ./... + + - name: Test + run: go test -v ./... \ No newline at end of file diff --git a/examples/main.go b/examples/main.go new file mode 100644 index 0000000..053867b --- /dev/null +++ b/examples/main.go @@ -0,0 +1,15 @@ +package main + +import ( + statix "github.com/devchris123/statix/pkg/statix" +) + +func main() { + // Create a new server instance with the specified address + srv := statix.NewServer(":8080") + + // Start the server + if err := srv.Start(); err != nil { + panic(err) + } +} diff --git a/examples/static/hello-world.html b/examples/static/hello-world.html new file mode 100644 index 0000000..fcca1a4 --- /dev/null +++ b/examples/static/hello-world.html @@ -0,0 +1,11 @@ + + + + + + Hello World + + +

Hello, World!

+ + diff --git a/pkg/server/server.go b/pkg/server/server.go deleted file mode 100644 index ccb4768..0000000 --- a/pkg/server/server.go +++ /dev/null @@ -1,22 +0,0 @@ -package server - -import ( - "net/http" -) - -// Server represents a simple HTTP server. -type server struct { - addr string - server http.Server -} - -// NewServer creates a new server instance with the specified address. -func NewServer(addr string) *server { - return &server{addr: addr} -} - -// Start starts the HTTP server. -func (s *server) Start() error { - s.server.Addr = s.addr - return s.server.ListenAndServe() -} diff --git a/pkg/statix/server.go b/pkg/statix/server.go new file mode 100644 index 0000000..45faf29 --- /dev/null +++ b/pkg/statix/server.go @@ -0,0 +1,40 @@ +// Package server provides a simple HTTP server to serve static files. +package server + +import ( + "log" + "net/http" + "path/filepath" +) + +const defaultStaticDir = "./static" + +type server struct { + server http.Server +} + +// NewServer creates a new server instance with the specified address. +func NewServer(addr string) *server { + handler := http.DefaultServeMux + + handler.HandleFunc("/", staticFileHandler(defaultStaticDir)) + + return &server{server: http.Server{ + Addr: addr, + Handler: handler, + }} +} + +func staticFileHandler(dir string) http.HandlerFunc { + log.Printf("statix: Serving static files from %s", dir) + return func(w http.ResponseWriter, r *http.Request) { + log.Printf("statix: Received request for %s", r.URL.Path) + http.ServeFile(w, r, filepath.Join(dir, r.URL.Path)) + } +} + +// Start starts the HTTP server. +func (s *server) Start() error { + log.Printf("statix: Starting server on %s", s.server.Addr) + return s.server.ListenAndServe() +} diff --git a/pkg/statix/server_integration_test.go b/pkg/statix/server_integration_test.go new file mode 100644 index 0000000..dabe0e9 --- /dev/null +++ b/pkg/statix/server_integration_test.go @@ -0,0 +1,59 @@ +package server + +import ( + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "testing" +) + +// TestStaticFileHandler tests the staticFileHandler function. +// The test creates a dummy directory with dummy files and checks if the handler serves them correctly. +func TestIntegrationStaticFileHandler(t *testing.T) { + dummyFile0 := "testfile0.txt" + dummyFile1 := "testfile1.txt" + dummyFile0Content := "This is a test file." + dummyFile1Content := "This is another test file." + readPerm := os.FileMode(0644) + + // Create a temporary directory for testing + staticDir, err := os.MkdirTemp(".", "static_") + if err != nil { + t.Fatalf("could not create temp dir: %v", err) + } + t.Cleanup(func() { + // Remove the temporary directory after the test + if err := os.RemoveAll(staticDir); err != nil { + t.Fatalf("could not remove temp dir: %v", err) + } + }) + // Create a dummy file in the temporary directory + if err := os.WriteFile(filepath.Join(staticDir, dummyFile0), []byte(dummyFile0Content), readPerm); err != nil { + t.Fatalf("could not create dummy file: %v", err) + } + // Create another dummy file + if err := os.WriteFile(filepath.Join(staticDir, dummyFile1), []byte(dummyFile1Content), readPerm); err != nil { + t.Fatalf("could not create another dummy file: %v", err) + } + + handler := staticFileHandler(staticDir) + + if handler == nil { + t.Error("expected handler to be non-nil") + } + + // Simulate a request to the handler + req, err := http.NewRequest("GET", dummyFile0, nil) + if err != nil { + t.Fatalf("could not create request: %v", err) + } + rr := httptest.NewRecorder() + handler.ServeHTTP(rr, req) + if rr.Code != http.StatusOK { + t.Errorf("expected status OK, got %d", rr.Code) + } + if rr.Body.String() != dummyFile0Content { + t.Errorf("expected body %s, got %s", dummyFile0Content, rr.Body.String()) + } +} diff --git a/pkg/statix/server_test.go b/pkg/statix/server_test.go new file mode 100644 index 0000000..f8672cc --- /dev/null +++ b/pkg/statix/server_test.go @@ -0,0 +1,65 @@ +package server + +import ( + "net/http" + "net/http/httptest" + "testing" +) + +// TestNewServer tests the NewServer function. +func TestNewServer(t *testing.T) { + addr := ":8080" + srv := NewServer(addr) + + if srv.server.Addr != addr { + t.Errorf("expected server Addr %s, got %s", addr, srv.server.Addr) + } +} + +// Test that request.URL.path is always the relative path +// regardless of whether the request is made with a full URL or a relative path. +func TestRequestPath(t *testing.T) { + path := "/testfile.txt" + + // Test with a relative path + r, err := http.NewRequest("GET", path, nil) + if err != nil { + t.Fatalf("could not create request: %v", err) + } + if r.URL.Path != path { + t.Errorf("expected request URL path %s, got %s", path, r.URL.Path) + } + + // Test with a full URL + r, err = http.NewRequest("GET", "http://example.com"+path, nil) + if err != nil { + t.Fatalf("could not create request: %v", err) + } + if r.URL.Path != path { + t.Errorf("expected request URL path %s, got %s", path, r.URL.Path) + } +} + +// TestStaticFileHandler tests the staticFileHandler function. +func TestStaticFileHandler(t *testing.T) { + dir := "/static" + handler := staticFileHandler(dir) + + if handler == nil { + t.Error("expected handler to be non-nil") + } + + // Simulate a request to the handler + req, err := http.NewRequest("GET", dir+"/testfile.txt", nil) + if err != nil { + t.Fatalf("could not create request: %v", err) + } + + // Create a ResponseRecorder to capture the response + rr := httptest.NewRecorder() + handler.ServeHTTP(rr, req) + + if rr.Code != http.StatusNotFound { + t.Errorf("expected status code 404, got %d", rr.Code) + } +}